zoukankan      html  css  js  c++  java
  • 金山2007逆向分析挑战赛第一阶段第二题详解

    题目:

    一、将_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 ime 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

     
  • 相关阅读:
    2020年-测试流程学习
    Jmeter接口测试2020(1)
    elk
    redis
    RabbitMQ
    memcache集群
    mysql安装
    mysql从的配置文件
    memcache
    keepalived
  • 原文地址:https://www.cnblogs.com/milantgh/p/3947536.html
Copyright © 2011-2022 走看看