一、SEH 創建代碼
這裡我沒有繼續使用上面的SimpleSeh 進行分析,而是新寫了一個簡單的SehTest 函數。
代碼:
VOID SehTest()
{
ULONG ulVal = 0;
__try // 第一個 __try 域
{
ulVal = 0x11111111; // 最後一位為1表示“在__try 代碼塊中”
}
__except(Filter_0())
{
ulVal = 0x11111110; // 最後一位為0表示“在__except/__finally 代碼塊中”
}
__try // 第二個 __try 域
{
ulVal = 0x22222222;
__try // 第三個 __try 域
{
ulVal = 0x33333333;
*((ULONG*)NULL) = ulVal; // 觸發異常
}
__finally
{
ulVal = 0x33333330;
}
}
__except(Filter_2())
{
ulVal = 0x22222220;
}
return;
}
反彙編代碼如下:
(需要說明一下我的命名習慣是,"l_"前綴的變量是函數的局部變量,沒有任何前綴的變量是傳入的參數)
引用:
kd> uf passthrough!SehTest
;PassThrough!SehTest [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 604]:
f8720040 mov edi,edi
f8720042 push ebp ; l_ExceptionRegistration->_ebp
f8720043 mov ebp,esp
f8720045 push 0FFFFFFFEh ; l_ExceptionRegistration->trylevel = TRYLEVEL_INVALID (-2)
f8720047 push offset PassThrough!__safe_se_handler_table+0x8 (f8721468) ; l_ExceptionRegistration->scopetable
f872004c push offset PassThrough!_except_handler4 (f8720390) ; l_ExceptionRegistration->handler
f8720051 mov eax,dword ptr fs:[00000000h]
f8720057 push eax ; _EXCEPTION_REGISTRATION::prev
f8720058 add esp,0FFFFFFF4h ; 這里分配了0xc 字節的棧空間,其中緊貼著l_ExceptionRegistration->prev
; 的4個字節存放著l_ExceptionRegistration->xpointers
f872005b push ebx
f872005c push esi
f872005d push edi
f872005e mov eax,dword ptr [PassThrough!__security_cookie (f87220b0)]
f8720063 xor dword ptr [ebp-8],eax ; 對scopetable 進行異或加密
f8720066 xor eax,ebp ; 對__security_cookie 進行加密
f8720068 push eax ; 把加密了的__security_cookie 也壓入棧中,後面用來對scopetable 進行解密
f8720069 lea eax,[ebp-10h]
f872006c mov dword ptr fs:[00000000h],eax ; 將l_ExceptionRegistration 掛入線程異常鍊錶中
f8720072 mov dword ptr [ebp-18h],esp
f8720075 mov dword ptr [ebp-1Ch],0
f872007c mov dword ptr [ebp-4],0 ; 進入第一個__try 域,l_ExceptionRegistration->trylevel = 0
f8720083 mov dword ptr [ebp-1Ch],11111111h
f872008a mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第一個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
f8720091 jmp PassThrough!SehTest+0x6a (f87200aa)
; ------------------------------------------------- --------------------------------
; 這裡有個空洞,用uf 命令是不會顯示的。範圍是 f8720093 到 f87200a9,彙編碼如下
;
; PassThrough!SehTest+0x53 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 611]:
; f8720093 call PassThrough!Filter_0 (f8720010)
; f8720098 ret
;
; f8720099 mov esp,dword ptr [ebp-18h] ; 第一個__except 處理域
; f872009c mov dword ptr [ebp-1Ch],11111110h
; f87200a3 mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第一個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
; ------------------------------------------------- --------------------------------
;PassThrough!SehTest+0x6a [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 616]:
f87200aa mov dword ptr [ebp-4],1 ; 進入第二個__try 域,l_ExceptionRegistration->trylevel = 1
f87200b1 mov dword ptr [ebp-1Ch],22222222h
f87200b8 mov dword ptr [ebp-4],2 ; 進入第三個__try 域,l_ExceptionRegistration->trylevel = 2
f87200bf mov dword ptr [ebp-1Ch],33333333h
f87200c6 mov eax,dword ptr [ebp-1Ch]
f87200c9 mov dword ptr ds:[00000000h],eax ; 觸發異常
f87200ce mov dword ptr [ebp-4],1 ; 離開第三個__try 域,l_ExceptionRegistration->trylevel = 1
f87200d5 call PassThrough!SehTest+0x9c (f87200dc)
f87200da jmp PassThrough!SehTest+0xa4 (f87200e4)
; ------------------------------------------------- --------------------------------
; 空洞,範圍是f87200dc 到f87200e3,彙編碼如下
;
; PassThrough!SehTest+0x9c [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 628]:
; f87200dc c745e430333333 mov dword ptr [ebp-1Ch],33333330h ; __finally 域
; ------------------------------------------------- --------------------------------
;PassThrough!SehTest+0xa4 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 630]:
f87200e4 mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第二個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
f87200eb jmp PassThrough!SehTest+0xc4 (f8720104)
; ------------------------------------------------- --------------------------------
; 空洞,範圍是f87200ed 到f8720103,彙編碼如下
;
; PassThrough!SehTest+0xad [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 631]:
; f87200ed e83effffff call PassThrough!Filter_2 (f8720030)
; f87200f2 c3 ret
;
; f87200f3 8b65e8 mov esp,dword ptr [ebp-18h] ; 第二個__except 處理域
; f87200f6 c745e420222222 mov dword ptr [ebp-1Ch],22222220h
; f87200fd c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第三個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
; ------------------------------------------------- --------------------------------
;PassThrough!SehTest+0xc4 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 637]:
f8720104 mov ecx,dword ptr [ebp-10h]
f8720107 mov dword ptr fs:[0],ecx ; 恢復舊的EXCEPTION_REGISTRATION。即從線程異常鍊錶中摘除l_ExceptionRegistration
f872010e pop ecx
f872010f pop edi
f8720110 pop esi
f8720111 pop ebx
f8720112 mov esp,ebp
f8720114 pop ebp
f8720115 ret
來看看 scopetable 的內容:
kd> dd f8721468
f8721468 fffffffe 00000000 ffffffd4 00000000 < 16個字節的坑
f8721478 [fffffffe f8720093 f8720099] [fffffffe
f8721488 f87200ed f87200f3] [00000001 00000000
f8721498 f87200dc] 00000000 00000000 00000000
f87214a8 00000000 00000000 00000000 00000000
f87214b8 00000000 00000000 00000000 00000000
f87214c8 00000000 00000000 00000000 00000000
f87214d8 00000000 00000000 00000000 00000000
前16個字節是坑,坑的作用晚點再說。之後就是三個scopetable_entry,被我用大括號擴起來了。對照前面的彙編碼可以發現,scopetable_entry::lpfnFilter 和scopetable_entry::lpfnHandler 就是彙編碼中的空洞處的代碼。其中,第三個scopetable_entry::lpfnFilter 是NULL,對照代碼可以發現,是因為這是一個__try & __finally 塊,沒有lpfnFilter。
彙編代碼很簡單,註釋裡也有詳細的分析過程了,我不再多囉嗦。只提兩點:
1. EXCEPTION_REGISTRATION::scopetable 指針被用__security_cookie 進行了異或加密。
2. EXCEPTION_REGISTRATION::scopetable 並不直接指向scopetable_entry 數組,在第一個scopetable_entry 之前有16 個字節的坑。後續分析中會看到,它的主要作用是幫助驗證scopetable 是否被破壞。第三個DWORD,即上文中的ffffffd4 是一個偏移量,後續的分析過程中會看得很清楚。
没有评论:
发表评论