首页
社区
课程
招聘
[原创]SEH记录小工具
发表于: 2007-9-30 04:49 13085

[原创]SEH记录小工具

2007-9-30 04:49
13085

(本文章纯属虚构,如有雷同纯属巧合)

   这个SEH记录小工具是学习脱壳时的“副产品”。大多数加密壳都少不了用SEH进行反跟踪、反调试,不过前辈们说过“这些连续的SEH给调试者指明了一条通往正确目标的道路”,所以对一个壳的SEH了解应该是比较必要的。但是在OD上不断重复重复再重复地“shift+F9”毕竟是一件比较烦人的事,所以就写了这个小工具,让其自动记录SEH发生异常的地址以及其对应的Handler地址,这样一下子就能知道第几个SEH后就开始处理输入表、第几个SEH后就会跳到OEP,直达代码核心,还能根据Handler地址正确设置断点,这样“Shift+F9”就保证不会跑飞。好,废话少说,下面跟大家来分享。

一,一个脱壳实例
    看雪学院编著的《软件加密技术内幕》第三章第二节有一个Hying前辈写的tElock0.98脱壳机源码,KANXUE编著的《加密与解密(第二版)》第十一章第七节脱壳实例有两个用tElock0.98加了壳的样本,分别是Note_tElock.exe、No-Anti.exe。然后用Hying前辈写的脱壳机去脱这两个壳,Note_tElock.exe脱壳成功,No-Anti.exe脱壳失败。
    这个脱壳机会在第16次SEH时候设置断点保护输入表,第20次SEH时候DUMP文件。用SEHstat.exe(附件中)打开Note_tElock.exe及No-Anti.exe,很快就生成两份SEH数据。如下:
    ----------------------------------------------------------------------------------------------
    Note_tElock.exe
    ImageBase:00400000h  StartAddress:0040DBD6h ProcessId:1256 ThreadId:388
    ...
    ---------------------------------main start-----------------------------------
    Exception 01 Address:0040DA1Dh(-441) Handler:0040DA0E(-456) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 02 Address:0040DA74h(-354) Handler:0040DA58(-382) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 03 Address:0040C08Ch(-6986) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_BREAKPOINT
    Exception 04 Address:0040C090h(-6982) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 05 Address:0040C099h(-6973) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 06 Address:0040C09Eh(-6968) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 07 Address:0040C0A3h(-6963) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 08 Address:0040C0A7h(-6959) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_INT_DIVIDE_BY_ZERO
    Exception 09 Address:0040C6A8h(-5422) Handler:0040C68A(-5452) ExcptionCode:EXCEPTION_ILLEGAL_INSTRUCTION
    Exception 10 Address:0040CAA1h(-4405) Handler:0040CA90(-4422) ExcptionCode:EXCEPTION_INT_DIVIDE_BY_ZERO
    Exception 11 Address:0040CAE4h(-4338) Handler:0040CAC2(-4372) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 12 Address:0040CB27h(-4271) Handler:0040CB03(-4307) ExcptionCode:EXCEPTION_BREAKPOINT
    Exception 13 Address:0040CB67h(-4207) Handler:0040CB41(-4245) ExcptionCode:EXCEPTION_INT_DIVIDE_BY_ZERO
    Exception 14 Address:0040CBA6h(-4144) Handler:0040CB84(-4178) ExcptionCode:EXCEPTION_ACCESS_VIOLATION
    Exception 15 Address:0040CBF0h(-4070) Handler:0040CBC4(-4114) ExcptionCode:EXCEPTION_BREAKPOINT
    Exception 16 Address:0040CE0Dh(-3529) Handler:0040CDFE(-3544) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 17 Address:0040CE49h(-3469) Handler:0040CE2D(-3497) ExcptionCode:EXCEPTION_SINGLE_STEP
    Load Dll   7D590000h C:\WINDOWS\system32\SHELL32.dll
    Load Dll   77F40000h C:\WINDOWS\system32\SHLWAPI.dll
    Load Dll   77180000h C:\WINDOWS\WinSxS\...\comctl32.dll
    Load Dll   5D170000h C:\WINDOWS\system32\comctl32.dll
    Load Dll   76320000h C:\WINDOWS\system32\comdlg32.dll
    Exception 18 Address:0040D6F1h(-1253) Handler:0040D6FF(-1239) ExcptionCode:EXCEPTION_ILLEGAL_INSTRUCTION
    Exception 19 Address:0040D7E1h(-1013) Handler:0040D7D2(-1028) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 20 Address:0040D817h(-959) Handler:0040D7FB(-987) ExcptionCode:EXCEPTION_SINGLE_STEP
    ;这里之后程序就运行了
   
    --------------------------------------------------------------------------------------------------
    No-Anti.exe
    ImageBase:00400000h  StartAddress:0040DBD6h ProcessId:1612 ThreadId:1632
    ...
    ---------------------------------main start-----------------------------------
    Exception 01 Address:0040D9FCh(-474) Handler:0040D9ED(-489) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 02 Address:0040DA37h(-415) Handler:0040DA1B(-443) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 03 Address:0040C08Ch(-6986) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_BREAKPOINT
    Exception 04 Address:0040C090h(-6982) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 05 Address:0040C099h(-6973) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 06 Address:0040C09Eh(-6968) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 07 Address:0040C0A3h(-6963) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 08 Address:0040C0A7h(-6959) Handler:0040C0C5(-6929) ExcptionCode:EXCEPTION_INT_DIVIDE_BY_ZERO
    Exception 09 Address:0040C6A8h(-5422) Handler:0040C68A(-5452) ExcptionCode:EXCEPTION_ILLEGAL_INSTRUCTION
    Exception 10 Address:0040CAA1h(-4405) Handler:0040CA90(-4422) ExcptionCode:EXCEPTION_INT_DIVIDE_BY_ZERO
    Exception 11 Address:0040CAE4h(-4338) Handler:0040CAC2(-4372) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 12 Address:0040CDF0h(-3558) Handler:0040CDE1(-3573) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 13 Address:0040CE28h(-3502) Handler:0040CE0C(-3530) ExcptionCode:EXCEPTION_SINGLE_STEP
    Load Dll   7D590000h C:\WINDOWS\system32\SHELL32.dll
    Load Dll   77F40000h C:\WINDOWS\system32\SHLWAPI.dll
    Load Dll   77180000h C:\WINDOWS\WinSxS\...\comctl32.dll
    Load Dll   5D170000h C:\WINDOWS\system32\comctl32.dll
    Load Dll   76320000h C:\WINDOWS\system32\comdlg32.dll
    Exception 14 Address:0040D6F1h(-1253) Handler:0040D6FF(-1239) ExcptionCode:EXCEPTION_ILLEGAL_INSTRUCTION
    Exception 15 Address:0040D7C8h(-1038) Handler:0040D7B9(-1053) ExcptionCode:EXCEPTION_SINGLE_STEP
    Exception 16 Address:0040D806h(-976) Handler:0040D7EA(-1004) ExcptionCode:EXCEPTION_SINGLE_STEP
    ;这里之后程序就运行了
   
    (说明:Address表示SEH异常发生的地址,Handler表示SEH处理函数的地址,括号里面的数据表示该地址相对程序入口点的偏移,ExcptionCode就是异常类型)
    ---------------------------------------------------------------------------------------------------------
    由上面两份SEH记录很明显可以看出,Note_tElock.exe的SEH很符合Hying前辈的统计数据,所以很顺利就脱了。但是第二份SEH明显就不符合,它总共才发生16次SEH异常而不是20次,在第13次SEH之后就开始处理输入表了,所以这个脱壳机报“locked.exe可能不是TeLock 0.98的外壳。”。
    知道失败原因,我们就可以对源代码进行修改,使它“兼容性”更好一些。由上面数据看来,在本例中根据第几次SEH脱壳可能不是个好主意,我的做法是直接对LoadLibraryA、GetModuleHandleA、VirtualProtectEx下断点(不是INT3哦),当程序第一次执行到LoadLibraryA或GetModuleHandleA时,说明开始处理输入表了,然后在函数返回地址向前80个字节范围内搜索“test  esi, esi;je  ****;”两条指令(不是硬编码偏移值),将函数返回地址直接修改为je ****中的****,这样当LoadLibraryA或GetModuleHandleA执行完毕直接就跳过后面的加密处理代码了(不用修改程序代码哦),而这时的ESI正是原来的输入表地址,可以直接记录下来。当程序执行到VirtualProtectEx时就可以DUMP了(趁PE头还没有被修改),然后大功告成,两个样本程序均能成功脱壳。(附件中有这个tElock0.98_Unpacker.exe)

二、SEH记录小工具的实现

lea ebx,DBEvent.u       
       .while TRUE
       	
       	      invoke WaitForDebugEvent,addr DBEvent,INFINITE
       	      
       	      mov dwDebugOperation,DBG_CONTINUE
       	      
       	      .if DBEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT       ;进程结束       	      	    
                    ...       	      	     
       	      .elseif DBEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT ;进程创建
       	              
       	              assume ebx:ptr CREATE_PROCESS_DEBUG_INFO
       	              
       	              mov eax,[ebx].lpStartAddress
       	              mov dwStartAddress,eax       	              
       	              ...
       	              invoke _SetBreakPoint,pi.hProcess,dwStartAddress,addr pOldStartAddressCode ;在程序入口点设置INT3断点      
       	              
       	      .elseif DBEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT    ;线程结束
       	              ...       	              
       	      .elseif DBEvent.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT  ;线程创建
       	      	      ...       	      	      
       	      .elseif DBEvent.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT       ;DLL装载
       	      	      
       	      	      assume ebx:ptr LOAD_DLL_DEBUG_INFO
       	      	      
       	      	      invoke ReadProcessMemory,pi.hProcess,[ebx].lpImageName,addr pTempBuffer,4,NULL
       	      	      mov eax,dword ptr pTempBuffer
       	      	      invoke ReadProcessMemory,pi.hProcess,eax,addr pDllName,256,NULL
       	      	      ...
       	      	      
       	      .elseif DBEvent.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT     ;DLL卸载
       	      	      ...       	      	             	      	      
       	      .elseif DBEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT       ;进程异常	     
       	      	      
       	      	      assume ebx:ptr EXCEPTION_DEBUG_INFO
       	      	      
       	      	      mov dwDebugOperation,DBG_EXCEPTION_NOT_HANDLED ;对不是预期的异常 通通都不处理
       	      	      
       	      	      invoke _GetExceptionText,[ebx].pExceptionRecord.ExceptionCode,addr szExceptionText
       	      	      
       	      	      .if [ebx].pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT
       	      	      	      
       	      	      	      .if bFirstBreakPoint == 1 ;第一次系统断点       	      	      	             
       	      	      	             invoke lstrcat,addr szExceptionText,addr szNtdllBreakPoint           	      	      	     

        
       	      	      	             mov dwDebugOperation,DBG_CONTINUE ;继续执行  	      	      	             
       	      	      	             mov bFirstBreakPoint,0       	      	      	             
       	      	      	      .endif
       	      	      	      
       	      	      	      mov eax,[ebx].pExceptionRecord.ExceptionAddress
       	      	      	      
       	      	      	      .if eax == dwStartAddress  ;在程序入口点中断 记录一下
       	      	      	      	     
       	      	      	      	     invoke _CleanBreakPoint,pi.hProcess,dwStartAddress,offset pOldStartAddressCode ;清除INT3断点
       	      	      	      	     invoke _ContinueExecute,pi.hThread,dwStartAddress ;INT3之后需要修改EIP
       	      	      	      	     mov dwDebugOperation,DBG_CONTINUE ;继续执行  
       	      	      	      	     
       	      	      	      	     invoke _Hide,pi.hProcess,pi.dwThreadId ;隐藏调试器,目前只能防止IsDebuggerPresent检测 :-)
       	      	      	      	     
       	      	      	      	     invoke _AddRecord,addr szMain
       	      	      	      	     jmp @MyBreakPoint       	      	      	      	     
       	      	      	      	     
       	      	      	      .endif
       	      	      	     
       	      	      .endif       	     	                  	      
       	      	      
                      ;除了自己设置的程序入口断点,其他的异常就开始记录
       	      	      invoke _FindSEHHandler,pi.hProcess,DBEvent.dwThreadId ;查找SEH处理函数地址
       	      	      mov edi,eax
       	      	      mov esi,edi
       	      	      sub esi,dwStartAddress ;计算相对程序入口点偏移
       	      	      
       	      	      mov eax,[ebx].pExceptionRecord.ExceptionAddress ;异常发生地址
       	      	      mov edx,eax
       	      	      sub edx,dwStartAddress ;计算相对程序入口点偏移
       	      	      
       	      	      invoke wsprintf,addr pTempBuffer,addr szExceptionInfo,nExceptionCounter,eax,edx,edi,esi,addr szExceptionText
       	      	      invoke _AddRecord,addr pTempBuffer    ;把记录写到文件中去  	      	
       	      	      
       	      	      inc nExceptionCounter    ;SEH计数器
       	      	      
       	      	      @MyBreakPoint:    
       	      	             	      	              	      	     
       	      .endif       	      
       	      
       	      invoke ContinueDebugEvent,DBEvent.dwProcessId,DBEvent.dwThreadId,dwDebugOperation      	              	
       	
       	
       .endw
;查找异常处理函数所在地址
_FindSEHHandler proc uses esi edi ebx hProcess,dwThreadID
	
	LOCAL Teb:DWORD,SEHPtrAddress:DWORD
	
	invoke _FindTeb,dwThreadID ;查找该线程对应的Teb
	.if eax == FALSE
		mov eax,-1
		jmp @ExitFindSEHHandler
	.endif
	mov Teb,eax
	
	invoke ReadProcessMemory,hProcess,Teb,addr SEHPtrAddress,4,NULL ;获取当前SEH handler指针所在地址
	invoke ReadProcessMemory,hProcess,SEHPtrAddress,addr pTempBuffer,8,NULL ;获取当前EXCEPTION_REGISTRATION结构
		
	mov eax,dword ptr [pTempBuffer+4] ;得到SEH handler	
	
	
  @ExitFindSEHHandler:
	ret

_FindSEHHandler endp

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 82
活跃值: (531)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
很强大
2007-9-30 05:13
0
雪    币: 8212
活跃值: (3356)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
LZ真是牛人,支持了。。。
2007-9-30 06:46
0
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
4
呃,被某人先写了,我来支持鸟。。。。
2007-9-30 08:00
0
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
5
好东西。支持
2007-9-30 08:51
0
雪    币: 260
活跃值: (102)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
学习了,努力学习脱壳。
2007-9-30 10:07
0
雪    币: 1969
活跃值: (46)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
7
天才就是天才......
2007-9-30 10:40
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
堕落终于又看见你了
2007-9-30 11:00
0
雪    币: 1451
活跃值: (3891)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
如何修改楼主提供的源代码成一个loader啊?
运行这个小工具记录软件运行情况,得到日志文件“SEH.txt”:
Unload Dll 77B90000h
Load Dll   75E60000h C:\WINDOWS\system32\apphelp.dll
Load Dll   4DC30000h C:\WINDOWS\system32\msctfime.ime
Load Dll   73B30000h C:\WINDOWS\system32\dciman32.dll
Unload Dll 047E0000h
Load Dll   047E0000h C:\Program Files\GraphicsEditor\Flex.License.Common.dll
Load Dll   71BB0000h C:\WINDOWS\system32\WSOCK32.dll


如何在楼主的代码中加点新功能啊:
.elseif DBEvent.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT       ;DLL装载
       	      	      
     assume ebx:ptr LOAD_DLL_DEBUG_INFO
       	      	      
     invoke ReadProcessMemory,pi.hProcess,[ebx].lpImageName,addr pTempBuffer,4,NULL
     mov eax,dword ptr pTempBuffer
     invoke ReadProcessMemory,pi.hProcess,eax,addr pDllName,256,NULL
     invoke _W2A,addr pDllName ;宽字符转ANSI
       	;--->加入下面加入点新功能,判断动态装载的DLL是指定的DLL后,根据DLL的基址和偏移地址在内存中打补丁,写入新数据.附件是一个现成的内存补丁源代码,但是那个代码只能修改主程序,不能修改动态加载的DLL内存数据(主要是获取不到DLL的基址),dUP的位置偏移和搜索、替换功能做出的loader也不行。      	      
       	.if pDllName == "C:\Program Files\GraphicsEditor\Flex.License.Common.dll"    	  ;汇编不会写:(    	             
       	 
       	 
       	    invoke Sleep,500        ;sleep 500 ms to be unpacked and displayed
            invoke SuspendThread,PI.hProcess    ;suspend the process
            invoke WriteProcessMemory,PI.hProcess,42b448h,addr Bytes,5,addr NBW         ;write new bytes 

       	      	      	             
       	.endif
       	;<---到这里       	      	      
     invoke wsprintf,addr pTempBuffer,addr szLoadDllInfo,[ebx].lpBaseOfDll,addr pDllName
     invoke _AddRecord,addr pTempBuffer      	      	      
       	      	      
.elseif DBEvent.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT     ;DLL卸载


能修改主程序的loader源代码:
上传的附件:
2007-9-30 12:59
0
雪    币: 222
活跃值: (15)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
10
2007-9-30 13:24
0
雪    币: 1309
活跃值: (232)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
楼主实现的是原理就是调试事件, 其实做一个od 插件应该可以得到, 而且具有进一步扩展使用其各种资源的可能.
  如果od 脚本本身能够提供接口, 通过脚本获取就更好了. 不过这个需要脚本方的支持.
  代码中 SelectorEntry.HighWord1.Bytes 不知道哪里看到有这样的成员的, 我没有查到有
HighWord1 成员, 是有意为之?
  另外目的获取teb 地址, 其实直接通过fs:[offset] 就可以访问整个段了, 根本不需要获取地址的.
  对于 GetThreadSelectorEntry的使用, 其实不需要查看它内部或者别人代码,  这些不是密码,都是intel 体系的规范, 了解这个才是根本而且所获甚多.
2007-10-1 09:32
0
雪    币: 293
活跃值: (110)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
12
多谢你看完这篇文章,多谢你的回复,确实用OD插件甚至OD脚本都能完成,至于用哪个是个人喜欢而已。LDT_ENTRY结构我是用RadAsm里面定义的,没有错。另外 fs:[offset]是对应本进程本线程来说的,不同的进程甚至不同的线程FS基地址都不相同,你总不会认为直接fs:[offset]可以访问其他线程的TEB?
2007-10-1 18:40
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
下了东西,表示下感谢
2007-10-6 16:22
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
牛人寫的軟件啊
來學習下:D
2007-10-9 10:58
0
雪    币: 245
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
牛人!佩服之至
2007-10-9 11:02
0
雪    币: 213
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
回头用下,感谢
2007-10-9 13:56
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
下了好东西当然要有丁`
2007-10-9 22:07
0
游客
登录 | 注册 方可回帖
返回
//