第一阶段第二题我的笨方法+处理的文件(看雪金山2007逆向分析挑战赛)
by aker
16:29 2007-8-26
使用工具:winhex,stud_pe,w32asm,od
如果对pe文件格式一点都不知道的,可以先看看www.pediy.com的资料区的pe格式,我也是现学现卖的:P
另外对windows消息循环机制要有一定的了解。
开始下载后很快就合并成功并出现窗口了,就放下了,感觉应该很容易出来的。今天在论坛上发现提交的人较少,想是不是很难啊,而且我也上不了网了,所以自己动手做了下,发现确实挺烦得,但是我估计我的方法非常笨拙,也有可能是平时这方面作的少,就当是抛砖引玉吧;)希望看到大牛的手笔,特别是shoooo,ccfer等好快的牛,呵呵呵。我找导入表是就这样用眼睛看得,不知道是不是可以写程序判断之类。入口点是合并后用w32asm看得。另外如果文件要重新对齐和重建引入表,估计就更加麻烦了。大家有好方法都介绍下拉。
附件是我合并好并修改好的文件。
1.文件重建
先从其他文件剥下来一个pe文件头,命名为pehead.dat。方法随便,我用的是winhex从一个文件前面剥下一个4k的文件头。方法是在文件头ALT+1,在要选尾部ALT+2,然后再编辑里面另存为phead.dat.
然后按照下面的顺序。
文件大小
rav - name size
0000h - phead 1000h
1000h - text 6000h
7000h - rdata 1000h
8000h - data 3000h
同样用winhex工具菜单的合并,合并文件,得到build.exe。
打开stud_PE,修改每个区块的位置和大小。这个不要说了吧。
在text中找入口点,用w32asm打开刚刚重建的文件,发现有GetCommandLine的函数的起点1527h,在stud_pe中填入。
在rdata中找到import table,看看,刚好应该是这个段的起始位置应该7000h,所以就不用重建了。
import table,位置为7618h,大小为30,修改IMAGE_DIRECTORY_ENTRY_IMPORT ,基本完成重建。
点击看一下,说是非法可执行文件。郁闷,再看看,哈忘了文件大小,文件大小变了,修改为b000h,okay,保存。测试,出来了那个pediy的界面。
另外这个地方发现校验和好像不需要改,好像和资料上说的不一样,谁指点我一下。
还有问题就是如果我不知道到底哪个文件放导入表该怎么办呢?就是怎么判断哪个区段的文件是何种文件。
小小总结,文件添加区段后要修改区段值,文件大小,区段属性等
修改入口点的话自己要记得跳回来。
2.增加菜单
看到窗口类注册的时候:一大段代码,其中关键的有这几个:
004011F5 |. BE 94804000 mov esi,bf1.00408094 ; ASCII "pediy.com"
........
00401248 |. 8D45 F4 lea eax,dword ptr ss:[ebp-C]
0040124B |. 8945 D0 mov dword ptr ss:[ebp-30],eax
0040124E |. 8D45 B0 lea eax,dword ptr ss:[ebp-50]
00401251 |. BF A0804000 mov edi,bf1.004080A0 ; ASCII "pediy.com"
00401256 |. 50 push eax ; /pWndClass
00401257 |. 897D D4 mov dword ptr ss:[ebp-2C],edi ; |
0040125A |. FF15 0C714000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA
发现注册的窗口类已经有了一个默认的菜单名字叫pediy.com,这样添加菜单就很简单了。
手工写一个菜单资源,rc编译为.res文件,我不知道rsrc目录的格式是什么样子的,我就直接把这个和一个其他的代码一起编译了,然后剥出来,名为rsrc.dat,因为对齐是4k,所以文件大小有4k。
pediy.com MENU
{
POPUP "&Help"
{
MENUITEM "&About", 1
}
}
在stud_pe中添加一个新节rsrc,大小为1000h,填充为刚刚的文件,然后在stud_pe中手工修改数据目录中的resource table,指向B000h,这是对于我的文件而言的。文件大小我填写的90h。
好了,这样重新打开文件就发现菜单在上面了。现在的问题就是如何让程序响应我们的点击呢?
3.响应消息
在论坛混的大概都知道window窗体程序是消息驱动的。我们点击菜单,鼠标滑动等都会产生对应的windows消息,消息先存放到系统队列,然后由系统派送到对应的产生消息的窗体。(个人理解:P,不对请一定要告诉我)。写windows程序的时候很多消息都是系统自己处理的,我们只处理我们需要的消息,其他都有系统缺省处理。我们要处理的消息由wndproc指派。
好了,知道这个,我们就可以知道我们点击菜单上的about的时候,肯定会有对应的消息发送到窗体了,只不过窗体可能没有处理机制而已。菜单消息的大类是WM_COMMAND消息,然后小类可以是我们自己制定的消息号,我们在上面定义菜单的时候,把about的消息标志定义为1。我们现在要做的就是处理WM_COMMAND,也要处理里面的一号消息。
MENUITEM "&About", 1
看看窗体消息处理的汇编代码,在下面好长一段,首先看看wndproc的原形:
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
第二个参数为msg消息号,后面两个为对应消息的参数,第一个为窗体句柄。
所以代码中的[ebp+c]就是消息号。
这个代码可能是优化过吧,比较的方法和正常编写出来的不一样,比如正常的比较都是cmp eax,111之类的为对wm_command的处理,但是这个里面很奇怪,比较是用的减,然后判断标志位是否为0这样的,就是dec或者sub,然后je。所以不容易看得清楚。我手工加了几个消息注释,WM_DESTROY(2h),WM_SIZE(5h),WM_PAINT(15h).可以看到,没有WM_COMMAND,但是我们现在需要处理菜单呀,怎么办呢?
004012D5 /. 55 push ebp
004012D6 |. 8BEC mov ebp,esp
004012D8 |. 83EC 40 sub esp,40
004012DB |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
004012EC |. FF75 14 push dword ptr ss:[ebp+14] ; /lParam; Default case of switch 004012DE
004012EF |. FF75 10 push dword ptr ss:[ebp+10] ; |wParam
004012F2 |. FF75 0C push dword ptr ss:[ebp+C] ; |Message
004012F5 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004012F8 |. FF15 F4704000 call dword ptr ds:[<&USER32.DefWindowProcA>] ; \DefWindowProcA
004012FE |. EB 54 jmp short b1.00401354
00401300 |> 8D45 C0 lea eax,dword ptr ss:[ebp-40] ; Case F of switch 004012DE
00401303 |. 50 push eax ; /pPaintstruct
00401304 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
00401307 |. FF15 F8704000 call dword ptr ds:[<&USER32.BeginPaint>] ; \BeginPaint
0040130D |. FF35 B0A94000 push dword ptr ds:[40A9B0]
00401313 |. FF35 B4A94000 push dword ptr ds:[40A9B4]
00401319 |. 50 push eax
0040131A |. FF75 08 push dword ptr ss:[ebp+8]
0040131D |. E8 36000000 call b1.00401358
00401322 |. 83C4 10 add esp,10
00401325 |. 8D45 C0 lea eax,dword ptr ss:[ebp-40]
00401328 |. 50 push eax ; /pPaintstruct
00401329 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
0040132C |. FF15 FC704000 call dword ptr ds:[<&USER32.EndPaint>] ; \EndPaint
00401332 |. EB 1E jmp short b1.00401352
00401334 |> 8B45 14 mov eax,dword ptr ss:[ebp+14] ; Case 5 of switch 004012DE
00401337 |. 0FB7C8 movzx ecx,ax
0040133A |. C1E8 10 shr eax,10
0040133D |. 890D B4A94000 mov dword ptr ds:[40A9B4],ecx
00401343 |. A3 B0A94000 mov dword ptr ds:[40A9B0],eax
00401348 |. EB 08 jmp short b1.00401352
0040134A |> 6A 00 push 0 ; /ExitCode = 0; Case 2 of switch 004012DE
0040134C |. FF15 00714000 call dword ptr ds:[<&USER32.PostQuitMessage>>; \PostQuitMessage
00401352 |> 33C0 xor eax,eax
00401354 |> C9 leave
00401355 \. C2 1000 retn 10
我考虑的是自己添加消息,但是上面的空间很满,前后都没有空闲地方了。
怎么办呢,真急人啊;)
我考虑的是在下面代码处跳出去,跳到一个空闲的地方,处理了WM_COMMAND,然后再回来,为什么选择这个地方呢?
因为这个地方消息号刚传进来,自己控制的话比较清楚。反正总要在DefWindowProcA前处理掉自己的消息才是;)
004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
//////////////////////////
// 我修改的代码如下
004012DE . /E9 91580000 jmp build.00406B74
004012E3 |90 nop
004012E4 |90 nop
004012E5 . /74 4D je short build.00401334 ///////此处就一样了
可以看到,我就是简单的远跳出去了,把原来的减和一个近程跳转覆盖掉了,但是我们在后面必须恢复该操作。
我找的空闲空间是整个代码段之后的空地,地址是00406B74。下面看看代码跳出去后恢复原来操作和跳回来代码。
看到,我们跳到地头后,首先比较是不是111h,就是看是不是WM_COMMAND,看winuser.h。
如果不是jnz short build.00406BBC就跳出去处理原来的操作了。build.00406BBC处可以看到就是原来的操作,dec eax。。。。
不过该处需要自己手工把跳回去的地址整理好,有点麻烦,不过我的笨方法就这样了;)
00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
//.........省一个关键代码...........过会说
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
好了,上面才讲到如何跳转出去,再跳转回来。
下面就是要考虑怎么样跳一个消息框出来了。你肯定想,不就直接push参数,call messagebox摆,对了,你是对的。但是有两个问题要考虑,第一个就是数据的问题。压栈的时候你的数据该怎么搞,因为要符合要求才是啊,我就是手工在代码段后面添了
pediy
看雪论坛.珠海金山2007逆向分析挑战赛
http://www.pediy.com
两段小数据,当然,需要加字符串的结束符0和回车换行符。
call MessageBox的时候,你知道具体地址不?因为导入表中没有这个呢。
我采用的是LoadLibraryA,GetProcAddress做的,看代码吧,可能还清楚些。
下面是自己手工写的代码。
00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
///////////////// 下面是找函数的关键代码 /////////////////
00406B84 . 68 B8754000 push build.004075B8 ; /FileName = "user32.dll"
00406B89 . FF15 6C704000 call dword ptr ds:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA
00406B8F . 8BF8 mov edi,eax
00406B91 . 8B35 A0704000 mov esi,dword ptr ds:[<&KERNEL32.GetProcAddr>; kernel32.GetProcAddress
00406B97 . 68 AC754000 push build.004075AC ; /ProcNameOrOrdinal = "MessageBoxA"
00406B9C . 57 push edi ; |hModule
00406B9D . FFD6 call esi ; \GetProcAddress
00406B9F . 8BF8 mov edi,eax
00406BA1 . 68 40000000 push 40
00406BA6 . 68 E06B4000 push build.00406BE0 ; ASCII "pediy"
00406BAB . 68 E76B4000 push build.00406BE7
00406BB0 . 68 00000000 push 0
00406BB5 . FFD7 call edi
///////////////// 上面是找函数的关键代码 /////////////////
00406BB7 .^ E9 96A7FFFF jmp build.00401352
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
总结:以前看过一点点pe的知识,也很少写windows窗体程序,真是什么什么到用时方恨少啊,发现自己根本就是不懂,呵呵呵呵。
另外估计我的方法是特别笨的方法,希望抛砖引玉,大家说说到底有什么速成的方法咯。
说老实话,我觉得这道题目可能考基础知识点多点,考耐心,比如合并文件自己可以用工具,但是要修改里面的数据的时候,感觉很麻烦,上一道题目考思维能力,解题能力。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
上传的附件: