利用Windows异常处理和RDTSC指令反调试学习
2012-05-15 17:10
281 查看
利用Windows异常处理和RDTSC指令反调试学习
最近在学习反调试,刚好学到用rdtsc指令反调试。学习加密解密这么久了,感觉自己对Windows的异常处理还是了解得不透彻,所以决心自己好好学习跟踪一把。
于是自己写了个小对话框程序,添加了一个按钮,按钮单击代码如下:
先解释一下RDTSC指令:
RDTSC(Read Time Stamp Count),将计算机启动以来的CPU运行周期数放到EDX:EAX里面,EDX是高位,EAX是低位。这样我们就可以两次获取CPU执行周期数,相减得到中间执行花费了多少时间。在debug等待我们处理的时候,CPU采用多任务处理的机制,将这个线程挂起,把CPU的时间片分给了其他线程,因为实在是太快了导致就象多个线程同时运行一样,而停在push
eax的时候虽然时间很短0.0几秒,但是CPU的时间片却已经轮回了很多次。所以这条指令可以用来反调试。但是该指令对于多CPU可能存在误差,详细请参考:http://blog.csdn.net/Solstice/article/details/5196544
用Windbg加载源码调试,很方便,运行,捕获了一个异常:
(744.6c4):Access violation - code c0000005 (first chance)
First chanceexceptions are reported before any exception handling.
Thisexception may be expected and handled.
eax=00000000ebx=00000000 ecx=0012fe74 edx=00000a40 esi=00143948 edi=0012f6a0
eip=00401cb6esp=0012f620 ebp=0012f6a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
***WARNING: Unable to verify checksum for anti_debug.exe
anti_debug!CAnti_debugDlg::OnTest+0x36:
00401cb63118 xor dword ptr [eax],ebx ds:0023:00000000=????????
单步进入异常处理:
首先进入的函数是ntdll!KiUserExceptionDispatcher,整个函数代码如下:
ntdll!KiUserExceptionDispatcher:
7c92eaec8b4c2404 mov ecx,dword ptr [esp+4]
7c92eaf08b1c24 mov ebx,dword ptr [esp]
7c92eaf351 push ecx
7c92eaf453 push ebx
7c92eaf5 e8c78c0200 call ntdll!LdrAddRefDll+0x1a8 (7c9577c1)//本例这个函数调用返回1
7c92eafa0ac0 or al,al
7c92eafc740c je ntdll!KiUserExceptionDispatcher+0x1e(7c92eb0a)
7c92eafe5b pop ebx
7c92eaff59 pop ecx
7c92eb006a00 push 0
7c92eb0251 push ecx
7c92eb03 e811ebffff call ntdll!ZwContinue (7c92d619)//跳过这个函数的话则直接回到异常发生的下一个指令了
7c92eb08eb0b jmp ntdll!KiUserExceptionDispatcher+0x29(7c92eb15)
7c92eb0a5b pop ebx
7c92eb0b59 pop ecx
7c92eb0c6a00 push 0
7c92eb0e51 push ecx
7c92eb0f53 push ebx
7c92eb10e83df7ffff call ntdll!NtRaiseException (7c92e252)
7c92eb1583c4ec add esp,0FFFFFFECh
7c92eb18890424 mov dword ptr [esp],eax
7c92eb1bc744240401000000 mov dword ptr[esp+4],1
7c92eb23895c2408 mov dword ptr [esp+8],ebx
7c92eb27c744241000000000 mov dword ptr[esp+10h],0
7c92eb2f54 push esp
7c92eb30e877000000 call ntdll!RtlRaiseException (7c92ebac)
7c92eb35c20800 ret 8
ntdll!ZwContinue: //此函数的功能应该就是回到原程序异常处,继续执行
7c92d619b820000000 mov eax,20h
7c92d61eba0003fe7f mov edx,offset SharedUserData!SystemCallStub(7ffe0300)
7c92d623ff12 call dword ptr [edx] ds:0023:7ffe0300={ntdll!KiFastSystemCall(7c92eb8b)}
7c92d625c20800 ret 8
在异常函数那里下一个断点,发现在步过call ntdll!LdrAddRefDll+0x1a8函数的时候会中断在异常处理函数,说明异常处理函数是在该函数里调用的。展开该函数:
7c9577c18bff mov edi,edi
7c9577c355 push ebp
7c9577c48bec mov ebp,esp
7c9577c683ec64 sub esp,64h
7c9577c956 push esi
7c9577caff750c push dword ptr [ebp+0Ch]
7c9577cd8b7508 mov esi,dword ptr [ebp+8]
7c9577d056 push esi
7c9577d1c645ff00 mov byte ptr [ebp-1],0
7c9577d5 e8c2ffffff call ntdll!LdrAddRefDll+0x183 (7c95779c) //eax = 0
7c9577da84c0 test al,al
7c9577dc0f8584720100 jne ntdll!RtlInitializeSListHead+0x15a56(7c96ea66)
7c9577e253 push ebx
7c9577e38d45f4 lea eax,[ebp-0Ch]
7c9577e650 push eax
7c9577e78d45f8 lea eax,[ebp-8]
7c9577ea50 push eax
7c9577eb e81cc1fcff call ntdll!RtlCaptureContext+0xc7 (7c92390c)
7c9577f0 e838c1fcff call ntdll!RtlCaptureContext+0xe8 (7c92392d)
7c9577f583650800 and dword ptr [ebp+8],0
7c9577f98bd8 mov ebx,eax
7c9577fb83fbff cmp ebx,0FFFFFFFFh
7c9577fe0f848f000000 je ntdll!LdrAddRefDll+0x27a (7c957893) [br=0]//不跳
7c95780457 push edi
7c9578053b5df8 cmp ebx,dword ptr [ebp-8]
7c9578080f821d32ffff jb ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95780e 8d4308 lea eax,[ebx+8] //此处应该是异常处理函eax = 0x12f62c
7c9578113b45f4 cmp eax,dword ptr [ebp-0Ch] //和栈底地址比较
7c9578140f871132ffff ja ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95781af6c303 test bl,3 //ebx=0x12f624是不是4的倍数
7c95781d0f850832ffff jne ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c9578238b4304 mov eax,dword ptr [ebx+4] //异常处理函数地址
7c9578263b45f8 cmp eax,dword ptr [ebp-8] //0x12d0000
7c9578297209 jb ntdll!LdrAddRefDll+0x21b (7c957834)
7c95782b3b45f4 cmp eax,dword ptr [ebp-0Ch] //0x130000
7c95782e0f82f731ffff jb ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95783450 push eax
7c957835 e867000000 call ntdll!LdrAddRefDll+0x288 (7c9578a1) // eax=0012f201
7c95783a84c0 test al,al
7c95783c0f84e931ffff je ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c957842f6055ac3997c80 test byte ptr [ntdll!NlsMbOemCodePageTag+0x342(7c99c35a)],80h
7c957849 0f8520720100 jne ntdll!RtlInitializeSListHead+0x15a5f (7c96ea6f)
7c95784fff7304 push dword ptr [ebx+4] //异常函数地址
7c9578528d45ec lea eax,[ebp-14h]
7c95785550 push eax
7c957856ff750c push dword ptr [ebp+0Ch]
7c95785953 push ebx
7c95785a56 push esi
7c95785b e8f3befcff call ntdll!RtlConvertUlongToLargeInteger+0xe (7c923753)//此函数调用了异常处理过程,返回后一直执行至末尾
7c957860f6055ac3997c80 test byte ptr [ntdll!NlsMbOemCodePageTag+0x342(7c99c35a)],80h
7c9578678bf8 mov edi,eax
7c9578690f8516720100 jne ntdll!RtlInitializeSListHead+0x15a75(7c96ea85)
7c95786f395d08 cmp dword ptr [ebp+8],ebx
7c9578720f841b720100 je ntdll!RtlInitializeSListHead+0x15a83(7c96ea93)
7c9578788bc7 mov eax,edi
7c95787a33c9 xor ecx,ecx
7c95787c2bc1 sub eax,ecx
7c95787e0f858631ffff jne ntdll!RtlIdentifierAuthoritySid+0x26(7c94aa0a)
7c957884f6460401 test byte ptr [esi+4],1
7c9578880f854f720100 jne ntdll!RtlInitializeSListHead+0x15acd(7c96eadd)
7c95788ec645ff01 mov byte ptr [ebp-1],1
7c9578925f pop edi
7c9578935b pop ebx
7c957894 8a45ff mov al,byte ptr [ebp-1]
7c9578975e pop esi
7c957898c9 leave
7c957899c20800 ret 8
call ntdll!RtlConvertUlongToLargeInteger+0xe(函数里边又嵌套了2层调用,下面仅列出最里边的最关键的地方):
7c92379955 push ebp
7c92379a8bec mov ebp,esp
7c92379cff750c push dword ptr [ebp+0Ch]
7c92379f 52 push edx
7c9237a0 64ff3500000000 push dword ptr fs:[0]
7c9237a7 64892500000000 mov dword ptr fs:[0],esp //注册一个异常处理
7c9237ae ff7514 push dword ptr [ebp+14h]// void * DispatcherContext
7c9237b1 ff7510 push dword ptr [ebp+10h]// struct _CONTEXT *ContextRecord,
7c9237b4 ff750c push dword ptr [ebp+0Ch]// void * EstablisherFrame,
7c9237b7 ff7508 push dword ptr [ebp+8] // struct _EXCEPTION_RECORD *ExceptionRecord
7c9237ba 8b4d18 mov ecx,dword ptr [ebp+18h]
7c9237bd ffd1 call ecx {anti_debug!CAnti_debugDlg::OnTest+0x66 (00401ce6)} //异常处理回调函数
7c9237bf648b2500000000 mov esp,dword ptr fs:[0]
7c9237c6648f0500000000 pop dword ptr fs:[0]
7c9237cd8be5 mov esp,ebp
7c9237cf5d pop ebp
7c9237d0c21400 ret 14h
进入异常处理后,看看相应的参数:
0:000>dd esp l8
0012f250 7c9237bf
0012f338 0012f624 0012f354
0012f260 0012f30c 0012f624 7c9237d8 0012f624
0x0012f338指向_EXCEPTION_RECORD,0x0012f354指向_CONTEXT
先加载一下符号:srv*c:\symbolslocal*http://msdl.microsoft.com/download/symbols
让我们来看下windbg的解释:
0:000>dt _EXCEPTION_RECORD 0x12f338
MSVCRTD!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0xc0000005 //违反访问
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x00401cb6 //异常发生时的地址
+0x010 NumberParameters : 2
+0x014ExceptionInformation : [15] 1
0:000>dt _CONTEXT 0x12f354
MSVCRTD!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_S***E_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x12f6a0
+0x0a0 Esi : 0x143948
+0x0a4 Ebx : 0
+0x0a8 Edx : 0x2901
+0x0ac Ecx : 0x12fe74
+0x0b0 Eax : 0
+0x0b4 Ebp : 0x12f6a0
+0x0b8 Eip : 0x401cb6 //异常发生时的指令地址
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x10246
+0x0c4 Esp : 0x12f620
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
现在回过头去看看handler
handler: //异常处理函数
mov ecx, [esp+0Ch] //取回调函数的第三个参数,也就是context
add dword ptr[ecx+0B8h], 2 //修改EIP,忽略异常 ,如果不加2的话,将导致循环执行异常指令
xor eax, eax
ret
参考资料
//-------------------------------------------------------------TIPS-------------------------------------------------------------------//
摘自于:《Windows系统异常处理机制的研究及应用》,作者:张明,徐万里
回调函数的原型:
EXCEPTION_DISPOSITION__cdecl _except_handler(
struct_EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame,
struct_CONTEXT *ContextRecord,
void *DispatcherContext
)
该函数的最重要的2
个参数是指向_EXCEPTION_RECORD结构的ExceptionRecord参数和指向_CONTEXT结
构的ContextRecord参数[2,4,5],前者主要包括异常类别编码、异常发生地址等重要信息;后者主要包括异常发生时的通用寄存器、调试寄存器和指令寄存器的值等重要的线程执行环境。而用于注册异常处理函数的典型汇编代码可表示如下:
PUSHhandler ; handler是新的异常处理函数地址
PUSHFS:[0] ; 指向原来的处理函数的地址压入栈内
MOVFS:[0],ESP ; 注册新的异常处理结构
//--------------------------------------------------------------------------------------------------------------------------------------------//
摘自于:http://www.mouseos.com/windows/SEH4.html
EXCEPTION_RECORD 是用来记录线程发生异常时的记录信息,在WinNT.h 定义为:
这些信息重要的有:异常码,标志,地址等。异常码在
WinNT.h 中定义了一部分:
上现在非常常见的:ACCESS_VIOLATION 访问违例异常,它的值是0xC0000005
CONTEXT 结构
这个结构很简单但较长,我还是打算在这里贴出来,好有个直观的认识,它在WinNT.h 定义为:
用来保存当线程发生异常时的 CPU 上下文环境。
最近在学习反调试,刚好学到用rdtsc指令反调试。学习加密解密这么久了,感觉自己对Windows的异常处理还是了解得不透彻,所以决心自己好好学习跟踪一把。
于是自己写了个小对话框程序,添加了一个按钮,按钮单击代码如下:
voidCAnti_debugDlg::OnTest() { // TODO: Add your control notificationhandler code here int i_Debug = -1; unsigned int delay = 0; __asm { pushoffset handler pushdword ptr fs:[0] movfs:[0],esp ;注册一个异常处理 rdtsc push eax ;时间1入栈 xor eax, eax xor [eax], ebx ;触发异常 rdtsc sub eax, [esp] ;时间间隔 add esp, 4 ;时间1出栈 pop dword ptrfs:[0] add esp, 4 ;注销异常处理 cmp eax, 50000h ;若是大于说明存在调试 jb not_debugged debugged: mov i_Debug, 1 jmp end not_debugged: mov i_Debug, 0 jmp end handler: mov ecx,[esp+0Ch] ;_CONTEXT结构 add dword ptr[ecx+0B8h], 2 ;修改_CONTEXT结构中的EIP xor eax, eax ret End: mov delay, eax } SetDlgItemInt(IDC_EDIT1, delay, FALSE); if (i_Debug) { MessageBox("The programis being debuged!", ">_<!!", MB_ICONWARNING); } else { MessageBox("The programis NOT being debuged!", ">_<!!", MB_OK); } }
先解释一下RDTSC指令:
RDTSC(Read Time Stamp Count),将计算机启动以来的CPU运行周期数放到EDX:EAX里面,EDX是高位,EAX是低位。这样我们就可以两次获取CPU执行周期数,相减得到中间执行花费了多少时间。在debug等待我们处理的时候,CPU采用多任务处理的机制,将这个线程挂起,把CPU的时间片分给了其他线程,因为实在是太快了导致就象多个线程同时运行一样,而停在push
eax的时候虽然时间很短0.0几秒,但是CPU的时间片却已经轮回了很多次。所以这条指令可以用来反调试。但是该指令对于多CPU可能存在误差,详细请参考:http://blog.csdn.net/Solstice/article/details/5196544
用Windbg加载源码调试,很方便,运行,捕获了一个异常:
(744.6c4):Access violation - code c0000005 (first chance)
First chanceexceptions are reported before any exception handling.
Thisexception may be expected and handled.
eax=00000000ebx=00000000 ecx=0012fe74 edx=00000a40 esi=00143948 edi=0012f6a0
eip=00401cb6esp=0012f620 ebp=0012f6a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
***WARNING: Unable to verify checksum for anti_debug.exe
anti_debug!CAnti_debugDlg::OnTest+0x36:
00401cb63118 xor dword ptr [eax],ebx ds:0023:00000000=????????
单步进入异常处理:
首先进入的函数是ntdll!KiUserExceptionDispatcher,整个函数代码如下:
ntdll!KiUserExceptionDispatcher:
7c92eaec8b4c2404 mov ecx,dword ptr [esp+4]
7c92eaf08b1c24 mov ebx,dword ptr [esp]
7c92eaf351 push ecx
7c92eaf453 push ebx
7c92eaf5 e8c78c0200 call ntdll!LdrAddRefDll+0x1a8 (7c9577c1)//本例这个函数调用返回1
7c92eafa0ac0 or al,al
7c92eafc740c je ntdll!KiUserExceptionDispatcher+0x1e(7c92eb0a)
7c92eafe5b pop ebx
7c92eaff59 pop ecx
7c92eb006a00 push 0
7c92eb0251 push ecx
7c92eb03 e811ebffff call ntdll!ZwContinue (7c92d619)//跳过这个函数的话则直接回到异常发生的下一个指令了
7c92eb08eb0b jmp ntdll!KiUserExceptionDispatcher+0x29(7c92eb15)
7c92eb0a5b pop ebx
7c92eb0b59 pop ecx
7c92eb0c6a00 push 0
7c92eb0e51 push ecx
7c92eb0f53 push ebx
7c92eb10e83df7ffff call ntdll!NtRaiseException (7c92e252)
7c92eb1583c4ec add esp,0FFFFFFECh
7c92eb18890424 mov dword ptr [esp],eax
7c92eb1bc744240401000000 mov dword ptr[esp+4],1
7c92eb23895c2408 mov dword ptr [esp+8],ebx
7c92eb27c744241000000000 mov dword ptr[esp+10h],0
7c92eb2f54 push esp
7c92eb30e877000000 call ntdll!RtlRaiseException (7c92ebac)
7c92eb35c20800 ret 8
ntdll!ZwContinue: //此函数的功能应该就是回到原程序异常处,继续执行
7c92d619b820000000 mov eax,20h
7c92d61eba0003fe7f mov edx,offset SharedUserData!SystemCallStub(7ffe0300)
7c92d623ff12 call dword ptr [edx] ds:0023:7ffe0300={ntdll!KiFastSystemCall(7c92eb8b)}
7c92d625c20800 ret 8
在异常函数那里下一个断点,发现在步过call ntdll!LdrAddRefDll+0x1a8函数的时候会中断在异常处理函数,说明异常处理函数是在该函数里调用的。展开该函数:
7c9577c18bff mov edi,edi
7c9577c355 push ebp
7c9577c48bec mov ebp,esp
7c9577c683ec64 sub esp,64h
7c9577c956 push esi
7c9577caff750c push dword ptr [ebp+0Ch]
7c9577cd8b7508 mov esi,dword ptr [ebp+8]
7c9577d056 push esi
7c9577d1c645ff00 mov byte ptr [ebp-1],0
7c9577d5 e8c2ffffff call ntdll!LdrAddRefDll+0x183 (7c95779c) //eax = 0
7c9577da84c0 test al,al
7c9577dc0f8584720100 jne ntdll!RtlInitializeSListHead+0x15a56(7c96ea66)
7c9577e253 push ebx
7c9577e38d45f4 lea eax,[ebp-0Ch]
7c9577e650 push eax
7c9577e78d45f8 lea eax,[ebp-8]
7c9577ea50 push eax
7c9577eb e81cc1fcff call ntdll!RtlCaptureContext+0xc7 (7c92390c)
7c9577f0 e838c1fcff call ntdll!RtlCaptureContext+0xe8 (7c92392d)
7c9577f583650800 and dword ptr [ebp+8],0
7c9577f98bd8 mov ebx,eax
7c9577fb83fbff cmp ebx,0FFFFFFFFh
7c9577fe0f848f000000 je ntdll!LdrAddRefDll+0x27a (7c957893) [br=0]//不跳
7c95780457 push edi
7c9578053b5df8 cmp ebx,dword ptr [ebp-8]
7c9578080f821d32ffff jb ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95780e 8d4308 lea eax,[ebx+8] //此处应该是异常处理函eax = 0x12f62c
7c9578113b45f4 cmp eax,dword ptr [ebp-0Ch] //和栈底地址比较
7c9578140f871132ffff ja ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95781af6c303 test bl,3 //ebx=0x12f624是不是4的倍数
7c95781d0f850832ffff jne ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c9578238b4304 mov eax,dword ptr [ebx+4] //异常处理函数地址
7c9578263b45f8 cmp eax,dword ptr [ebp-8] //0x12d0000
7c9578297209 jb ntdll!LdrAddRefDll+0x21b (7c957834)
7c95782b3b45f4 cmp eax,dword ptr [ebp-0Ch] //0x130000
7c95782e0f82f731ffff jb ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c95783450 push eax
7c957835 e867000000 call ntdll!LdrAddRefDll+0x288 (7c9578a1) // eax=0012f201
7c95783a84c0 test al,al
7c95783c0f84e931ffff je ntdll!RtlIdentifierAuthoritySid+0x47(7c94aa2b)
7c957842f6055ac3997c80 test byte ptr [ntdll!NlsMbOemCodePageTag+0x342(7c99c35a)],80h
7c957849 0f8520720100 jne ntdll!RtlInitializeSListHead+0x15a5f (7c96ea6f)
7c95784fff7304 push dword ptr [ebx+4] //异常函数地址
7c9578528d45ec lea eax,[ebp-14h]
7c95785550 push eax
7c957856ff750c push dword ptr [ebp+0Ch]
7c95785953 push ebx
7c95785a56 push esi
7c95785b e8f3befcff call ntdll!RtlConvertUlongToLargeInteger+0xe (7c923753)//此函数调用了异常处理过程,返回后一直执行至末尾
7c957860f6055ac3997c80 test byte ptr [ntdll!NlsMbOemCodePageTag+0x342(7c99c35a)],80h
7c9578678bf8 mov edi,eax
7c9578690f8516720100 jne ntdll!RtlInitializeSListHead+0x15a75(7c96ea85)
7c95786f395d08 cmp dword ptr [ebp+8],ebx
7c9578720f841b720100 je ntdll!RtlInitializeSListHead+0x15a83(7c96ea93)
7c9578788bc7 mov eax,edi
7c95787a33c9 xor ecx,ecx
7c95787c2bc1 sub eax,ecx
7c95787e0f858631ffff jne ntdll!RtlIdentifierAuthoritySid+0x26(7c94aa0a)
7c957884f6460401 test byte ptr [esi+4],1
7c9578880f854f720100 jne ntdll!RtlInitializeSListHead+0x15acd(7c96eadd)
7c95788ec645ff01 mov byte ptr [ebp-1],1
7c9578925f pop edi
7c9578935b pop ebx
7c957894 8a45ff mov al,byte ptr [ebp-1]
7c9578975e pop esi
7c957898c9 leave
7c957899c20800 ret 8
call ntdll!RtlConvertUlongToLargeInteger+0xe(函数里边又嵌套了2层调用,下面仅列出最里边的最关键的地方):
7c92379955 push ebp
7c92379a8bec mov ebp,esp
7c92379cff750c push dword ptr [ebp+0Ch]
7c92379f 52 push edx
7c9237a0 64ff3500000000 push dword ptr fs:[0]
7c9237a7 64892500000000 mov dword ptr fs:[0],esp //注册一个异常处理
7c9237ae ff7514 push dword ptr [ebp+14h]// void * DispatcherContext
7c9237b1 ff7510 push dword ptr [ebp+10h]// struct _CONTEXT *ContextRecord,
7c9237b4 ff750c push dword ptr [ebp+0Ch]// void * EstablisherFrame,
7c9237b7 ff7508 push dword ptr [ebp+8] // struct _EXCEPTION_RECORD *ExceptionRecord
7c9237ba 8b4d18 mov ecx,dword ptr [ebp+18h]
7c9237bd ffd1 call ecx {anti_debug!CAnti_debugDlg::OnTest+0x66 (00401ce6)} //异常处理回调函数
7c9237bf648b2500000000 mov esp,dword ptr fs:[0]
7c9237c6648f0500000000 pop dword ptr fs:[0]
7c9237cd8be5 mov esp,ebp
7c9237cf5d pop ebp
7c9237d0c21400 ret 14h
进入异常处理后,看看相应的参数:
0:000>dd esp l8
0012f250 7c9237bf
0012f338 0012f624 0012f354
0012f260 0012f30c 0012f624 7c9237d8 0012f624
0x0012f338指向_EXCEPTION_RECORD,0x0012f354指向_CONTEXT
先加载一下符号:srv*c:\symbolslocal*http://msdl.microsoft.com/download/symbols
让我们来看下windbg的解释:
0:000>dt _EXCEPTION_RECORD 0x12f338
MSVCRTD!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0xc0000005 //违反访问
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x00401cb6 //异常发生时的地址
+0x010 NumberParameters : 2
+0x014ExceptionInformation : [15] 1
0:000>dt _CONTEXT 0x12f354
MSVCRTD!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_S***E_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x12f6a0
+0x0a0 Esi : 0x143948
+0x0a4 Ebx : 0
+0x0a8 Edx : 0x2901
+0x0ac Ecx : 0x12fe74
+0x0b0 Eax : 0
+0x0b4 Ebp : 0x12f6a0
+0x0b8 Eip : 0x401cb6 //异常发生时的指令地址
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x10246
+0x0c4 Esp : 0x12f620
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
现在回过头去看看handler
handler: //异常处理函数
mov ecx, [esp+0Ch] //取回调函数的第三个参数,也就是context
add dword ptr[ecx+0B8h], 2 //修改EIP,忽略异常 ,如果不加2的话,将导致循环执行异常指令
xor eax, eax
ret
参考资料
//-------------------------------------------------------------TIPS-------------------------------------------------------------------//
摘自于:《Windows系统异常处理机制的研究及应用》,作者:张明,徐万里
回调函数的原型:
EXCEPTION_DISPOSITION__cdecl _except_handler(
struct_EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame,
struct_CONTEXT *ContextRecord,
void *DispatcherContext
)
该函数的最重要的2
个参数是指向_EXCEPTION_RECORD结构的ExceptionRecord参数和指向_CONTEXT结
构的ContextRecord参数[2,4,5],前者主要包括异常类别编码、异常发生地址等重要信息;后者主要包括异常发生时的通用寄存器、调试寄存器和指令寄存器的值等重要的线程执行环境。而用于注册异常处理函数的典型汇编代码可表示如下:
PUSHhandler ; handler是新的异常处理函数地址
PUSHFS:[0] ; 指向原来的处理函数的地址压入栈内
MOVFS:[0],ESP ; 注册新的异常处理结构
//--------------------------------------------------------------------------------------------------------------------------------------------//
摘自于:http://www.mouseos.com/windows/SEH4.html
EXCEPTION_RECORD 是用来记录线程发生异常时的记录信息,在WinNT.h 定义为:
typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; |
WinNT.h 中定义了一部分:
#define STATUS_WAIT_0 ((DWORD )0x00000000L) #define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) #define STATUS_USER_APC ((DWORD )0x000000C0L) #define STATUS_TIMEOUT ((DWORD )0x00000102L) #define STATUS_PENDING ((DWORD )0x00000103L) #define DBG_EXCEPTION_HANDLED ((DWORD )0x00010001L) #define DBG_CONTINUE ((DWORD )0x00010002L) #define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L) #define DBG_TERMINATE_THREAD ((DWORD )0x40010003L) #define DBG_TERMINATE_PROCESS ((DWORD )0x40010004L) #define DBG_CONTROL_C ((DWORD )0x40010005L) #define DBG_PRINTEXCEPTION_C ((DWORD )0x40010006L) #define DBG_RIPEXCEPTION ((DWORD )0x40010007L) #define DBG_CONTROL_BREAK ((DWORD )0x40010008L) #define DBG_COMMAND_EXCEPTION ((DWORD )0x40010009L) #define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L) #define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L) #define STATUS_BREAKPOINT ((DWORD )0x80000003L) #define STATUS_SINGLE_STEP ((DWORD )0x80000004L) #define STATUS_LONGJUMP ((DWORD )0x80000026L) #define STATUS_UNWIND_CONSOLIDATE ((DWORD )0x80000029L) #define DBG_EXCEPTION_NOT_HANDLED ((DWORD )0x80010001L) #define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L) #define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L) #define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L) #define STATUS_INVALID_PARAMETER ((DWORD )0xC000000DL) #define STATUS_NO_MEMORY ((DWORD )0xC0000017L) #define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL) #define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L) #define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L) #define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL) #define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL) #define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL) #define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL) #define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L) #define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L) #define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L) #define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L) #define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L) #define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L) #define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L) #define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL) #define STATUS_DLL_NOT_FOUND ((DWORD )0xC0000135L) #define STATUS_ORDINAL_NOT_FOUND ((DWORD )0xC0000138L) #define STATUS_ENTRYPOINT_NOT_FOUND ((DWORD )0xC0000139L) #define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL) #define STATUS_DLL_INIT_FAILED ((DWORD )0xC0000142L) #define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L) #define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L) #define STATUS_REG_NAT_CONSUMPTION ((DWORD )0xC00002C9L) #define STATUS_STACK_BUFFER_OVERRUN ((DWORD )0xC0000409L) #define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD )0xC0000417L) #define STATUS_ASSERTION_FAILURE ((DWORD )0xC0000420L) #if defined(STATUS_SUCCESS) || (_WIN32_WINNT > 0x0500) || (_WIN32_FUSION >= 0x0100) #define STATUS_SXS_EARLY_DEACTIVATION ((DWORD )0xC015000FL) #define STATUS_SXS_INVALID_DEACTIVATION ((DWORD )0xC0150010L) |
CONTEXT 结构
这个结构很简单但较长,我还是打算在这里贴出来,好有个直观的认识,它在WinNT.h 定义为:
typedef struct _CONTEXT { // // The flags values within this flag control the contents of // a CONTEXT record. // // If the context record is used as an input parameter, then // for each portion of the context record controlled by a flag // whose value is set, it is assumed that that portion of the // context record contains valid context. If the context record // is being used to modify a threads context, then only that // portion of the threads context will be modified. // // If the context record is used as an IN OUT parameter to capture // the context of a thread, then only those portions of the thread's // context corresponding to set flags will be returned. // // The context record is never used as an OUT only parameter. // DWORD ContextFlags; // // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. // DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. // FLOATING_S***E_AREA FloatSave; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_SEGMENTS. // DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_INTEGER. // DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_CONTROL. // DWORD Ebp; DWORD Eip; DWORD SegCs; // MUST BE SANITIZED DWORD EFlags; // MUST BE SANITIZED DWORD Esp; DWORD SegSs; // // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific // BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT; typedef CONTEXT *PCONTEXT; |
相关文章推荐
- Python学习--12 异常处理、调试
- Python学习--12 异常处理、调试
- [Python学习笔记][第八章Python异常处理结构与程序调试]
- POCO C++库学习和分析 -- 异常、错误处理、调试
- [Python学习笔记][第八章Python异常处理结构与程序调试]
- 学习python的第四十七天-第九章 异常处理与程序调试
- 异常处理与调试 - 零基础入门学习Delphi50
- C#学习 - vs调试(采用异常处理来实现两个整数的和)
- 异常处理与调试 - 零基础入门学习Delphi50
- Windows平台异常处理 开发中何时利用异常处理模块
- 异常处理与调试3 - 零基础入门学习Delphi52
- 未处理异常不能调试
- 错误处理与异常抛出_Swift基础知识学习
- Java Web 初级程序员 -第5天学习内容:异常处理
- python学习之调试 错误捕捉及处理
- j2se学习笔记-异常处理
- 异常处理[学习]
- Windows异常处理流程
- 使用sxstrace.exe调试Windows应用程序运行时动态链接库的加载异常
- Python学习进程(14)异常处理