首页
社区
课程
招聘
金山2007逆向分析挑战赛第一阶段第二题详解
发表于: 2011-12-23 12:29 8905

金山2007逆向分析挑战赛第一阶段第二题详解

2011-12-23 12:29
8905

-----------------------------------------
题目:
一、将_text,_rdata,_data合并成一个EXE文件,重建一个PE头
二、在第一步的基础上加入一个菜单
三、加入点击菜单调用MessageBox
*************************************************************
因为最终结果是一个用WIN32API(非MFC)编写窗口程序,所以建议用汇编一个差不多的程序,做为比较。
一、合成PE文件:
1、从名字上看_text,_rdata,_data分别应该是代码段,只读数据段(输入表之类)和可读写数据段。所以用WINHEX按顺序加上PE头后,复制粘贴就可以了。

WINHEX也用一个合成文件的功能。这时记做1.exe。
2、修改PE头数据。用STUD_PE打开1.exe,先修改“区段数目”,后区段的偏移和大小。这里题目没有规定文件大小,所以PE头大小最好为0X1000。分析如下:
DOS和PE头-0X0000-->┌┈┈┈┈┐
                   │        │
                   │ 0X1000 │
.text---0X1000---->├────┤
                   │        │
                   │ 0X6000 │
.rdata--0X7000---->├────┤
                   │        │
                   │ 0X1000 │
.data---0X8000---->├────┤
                   │        │
                   │ 0X3000 │
.rsrc---0XB000---->├────┤<---未加资源段时这里是文件结尾
                   │        │
                   │ 0X0200 │
文件尾--0XB200---->└────┘

根据以上图表得如下:   
No  | 名称      | 虚拟大小   | 虚拟偏移量 | 原始大小   | 原始偏移量 | 特性       |
01  | .text     | 00006000   | 00001000   | 00006000   | 00001000   | E0000020   |
02  | .rdata    | 00001000   | 00007000   | 00001000   | 00007000   | C0000040   |
03  | .data     | 00003000   | 00008000   | 00003000   | 00008000   | C0000040   |
04  | .rsrc     | 00000200   | 0000B000   | 00000200   | 0000B000   | 40000040   |
注: .rsrc段为加入菜单时才加入的,合成PE文件可以不看

根据文件的大小修改“镜像大小”。保存后,重新用STUD_PE载入,看它的提示,一般会提示“资源表”和“输入表”错误,“资源表”错误会让PE加载器无法

识别PE文件,所以先把“数据目录:IMAGE_DIR_ENTRY_RESOURCE”改成0。“输入表”错误只会至程序运行错误。

现在分析.rdata段,找到输入表的正确地址和大小。输入表是什么东西呢?
输入表的地址是:
IMAGE_OPTIONAL_HEADER.DataDirectory[1].VirtualAddress
输入表的大小是:
IMAGE_OPTIONAL_HEADER.DataDirectory[1].Size

VirtualAddress里存的是一个指向IMAGE_IMPORT_DESCRIPTOR数组的指针,该数组以一个全是0的IMAGE_IMPORT_DESCRIPTOR结构结束。

IMAGE_IMPORT_DESCRIPTOR结构的大小为0x14,所以输入DLL的个数等于输入表大小除以0x14后得的商再减1。
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
IMAGE_IMPORT_DESCRIPTOR结构主要看最后两个变量,DWORD   Name为指向DLL名字的指针,DWORD   FirstThunk指向一个数组,数组里存有该DLL函数名的指针。

分析正常EXE文件的输入表后发现,输入表一般是在调用函数名字的前面,用WINHEX打开1.exe,看地址0x77A0的地方,这里全是一些函数名,如:lstrcpyA和系统

,所以输入表应该在这0x77A0的前面(注意与正常文件对比),还有就是注意看DLL的名字,如:user32.dll的位置是0x788A,那么我们从0x7000的地方开始找0x8A

,找到后看看0x8A前面是不是0x78,这样就容易多了。在这里应该是0x7618,大小为0x50。

现在就差程序的“入口点”了,用PEID打开1.exe应该显示为VC6写的,这样我们就找一个VC6写的程序来看看它的入口,这是一个正常VC6程序的入口:
math.<Mod>/$  55            push    ebp
00409657  |.  8BEC          mov     ebp, esp
00409659  |.  6A FF         push    -1
0040965B  |.  68 F8474200   push    004247F8
00409660  |.  68 20CD4000   push    0040CD20                         ;  SE 处理程序安装
00409665  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
0040966B  |.  50            push    eax
0040966C  |.  64:8925 00000>mov     dword ptr fs:[0], esp
00409673  |.  83EC 58       sub     esp, 58
00409676  |.  53            push    ebx
00409677  |.  56            push    esi
00409678  |.  57            push    edi
00409679  |.  8965 E8       mov     dword ptr ss:[ebp-18], esp
0040967C  |.  FF15 14224200 call    dword ptr ds:[<&KERNEL32.GetVers>;  kernel32.GetVersion

math.<Mod>/$  55            push    ebp
00409657  |.  8BEC          mov     ebp, esp
这句是每个函数都有的,不看它,看这里
00409659  |.  6A FF         push    -1
0040965B  |.  68 F8474200   push    004247F8
00409660  |.  68 20CD4000   push    0040CD20                         ;  SE 处理程序安装
三个PUSH在函数里可不多呀,所在用WINHEX打开1.EXE,从0X1000开始找0X6A FF 68,会停在地址0X152A,看看前0X1527的地方,就是0X55 8B EC,所以这里应

该就是入口。修改完后一个PE格式的可执行文件就合成成功了。

二、加入菜单
刚才合成的程序是没有资源段的,所以要给它加入一个资源,里面只要有一个菜单就行了。资源文件这样写

#include                <resource.h>
#define pediy.com       0x30
#define        IDM_ABOUT        0x20
pediy.com        MENU
BEGIN
        popup        "HELP"
        BEGIN
                menuitem "ABOUT",IDM_ABOUT
        END
END

但程序是怎样把菜单加载到窗口里的呢?这里有几种方法:
1、用LoadMenu加载菜单并取得句柄后,设置CreateWindow.hMenu等于该句柄,但看看输入表,没有LoadMenu函数,所以用这种方法的话又要改输入表,太麻烦。
2、在写资源文件(.RC)的时候菜单名设置一个编号,在RegisterClass之前设置WNDCLASS.lpszMenuName等于这个编号或菜单名。

这里可先看看代码,看看它是怎样设置WNDCLASS.lpszMenuName的,用OD打开1.EXE,下断点在40125A,运行程序,停下后看ESP(我这是12FEAC),它是一个指

向WNDCLASS的指针,在内存窗口找到这个地址:

0012FEAC  03 00 00 00 D5 12 40 00 00 00 00 00 00 00 00 00  .ዕ@....
0012FEBC  00 00 40 00 27 00 01 00 03 00 01 00 10 00 90 01  .@'Ɛ
0012FECC  F0 FE 12 00 A0 80 40 00                          ﻰ肠@

WNDCLASS结构有10个成员,第9个就是菜单,所以12FEAC+(4*(9-1))=12FECC=》0012FECC  F0 FE 12 00。看看12FEF0是什么,原来是一个字串"pediy.com"

,所以我用第2种方法,写资源时把菜单名写成"pediy.com"就不用改代码了。把资源写好后,生成EXE,用WINHEX剥离.RSRC段,粘贴到1.EXE后,再用STUD_PE改

一改段数据和“镜像大小”就可以了。

三、加入提示窗口
先看看正常的菜单是怎样响应单击事件的。单击菜单后,会由WM_COMMAND(0X111)消息处理,它的wParam参数就是被单击控件的ID,如果这个ID下有处理函数

就处理,没有就返回,我们要做的就是增加这个处理函数。

用OD打开1.EXE,找到消息循环:
0040120B  |.  57            push    edi                              ; /RsrcName => IDI_APPLICATION
0040120C  |.  56            push    esi                              ; |hInst => NULL
0040120D  |.  891D ACAB4000 mov     dword ptr ds:[40ABAC], ebx       ; |
00401213  |.  C745 B0 03000>mov     dword ptr ss:[ebp-50], 3         ; |
0040121A  |.  C745 B4 D5124>mov     dword ptr ss:[ebp-4C], 004012D5  ; |<========================这里面就是设置消息循环的地方
00401221  |.  8975 B8       mov     dword ptr ss:[ebp-48], esi       ; |
00401224  |.  8975 BC       mov     dword ptr ss:[ebp-44], esi       ; |
00401227  |.  895D C0       mov     dword ptr ss:[ebp-40], ebx       ; |
0040122A  |.  FF15 04714000 call    dword ptr ds:[<&USER32.LoadIconA>; \LoadIconA
00401230  |.  57            push    edi                              ; /RsrcName => IDC_ARROW
00401231  |.  56            push    esi                              ; |hInst => NULL
00401232  |.  8945 C4       mov     dword ptr ss:[ebp-3C], eax       ; |
00401235  |.  FF15 08714000 call    dword ptr ds:[<&USER32.LoadCurso>; \LoadCursorA
0040123B  |.  56            push    esi                              ; /ObjType => WHITE_BRUSH
0040123C  |.  8945 C8       mov     dword ptr ss:[ebp-38], eax       ; |
0040123F  |.  FF15 18704000 call    dword ptr ds:[<&GDI32.GetStockOb>; \GetStockObject
00401245  |.  8945 CC       mov     dword ptr ss:[ebp-34], eax
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, 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.RegisterC>; \RegisterClassA

来到消息循环,发现没有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 0040134A
004012E2  |.  83E8 03       sub     eax, 3
004012E5  |.  74 4D         je      short 00401334
004012E7  |.  83E8 0A       sub     eax, 0A
004012EA  |.  74 14         je      short 00401300
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.DefWindow>; \DefWindowProcA
004012FE  |.  EB 54         jmp     short 00401354

注意看有三个JE,JE前面是SUB和DEC命令,不是平时的CMP命令,有猫腻。CMP是不保存结果的,但SUB和DEC是保存结果的,所以可以写成这样:
CMP EAX,2
je      short 0040134A
CMP EAX,2+3
je      short 00401334
CMP EAX,2+3+A
je      short 00401300

所以就要在这些比较之前用"跳出跳回法”写入WM_COMMAND消息处理。问题又来了,输入表里面没有MessageBox怎么办。我是用风险比较大的方法,就是绝对地

址,因为大多数程序在加载USER32.DLL等函数的时候,它的分配的空间90%都是一样的,所以直接使用USER32.DLL的空间加上MessageBox函数的偏移就可以了。

至于字串,可以在数据段加入,注意换行符为0X0A。最终如下:
004012D5   .  55            push    ebp
004012D6   .  8BEC          mov     ebp, esp
004012D8   .  83EC 40       sub     esp, 40
004012DB   .  E9 A0580000   jmp     00406B80        《=====跳出

00406B80   > \8B45 0C       mov     eax, dword ptr ss:[ebp+C]
00406B83   .  3D 11010000   cmp     eax, 111                         ;  Switch (cases 2..111)
00406B88   .  74 0D         je      short 00406B97
00406B8A   .  48            dec     eax
00406B8B   .  48            dec     eax
00406B8C   .^ 0F84 B8A7FFFF je      0040134A
00406B92   .^ E9 4BA7FFFF   jmp     004012E2         《=====跳回
00406B97   >  6A 40         push    40                               ;  Case 111 of switch 00406B83
00406B99   .  68 8C804000   push    0040808C                         ;  ASCII "pediy"
00406B9E   .  68 50804000   push    00408050
00406BA3   .  6A 00         push    0
00406BA5   .  E8 06000000   call    00406BB0
00406BAA   .^ E9 A3A7FFFF   jmp     00401352
00406BAF      00            db      00
00406BB0   $- FF25 B86B4000 jmp     dword ptr ds:[406BB8]            ;  user32.7696EA11
00406BB6      00            db      00
00406BB7      00            db      00
00406BB8   .  11EA9676      dd      user32.7696EA11


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 278
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
做好这个要对PE头相当的熟悉才OK!
2011-12-26 22:43
0
雪    币: 292
活跃值: (153)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
喜欢 看雪的精华~~~~~  Feel  so good .
2011-12-27 10:20
0
雪    币: 35
活跃值: (139)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
4
我就是这样做的还是不对啊?
2013-8-7 15:38
0
游客
登录 | 注册 方可回帖
返回
//