VMP1.8的IAT分析,同时膜拜一下Nooby大牛

首先说一说VMP1.8,当然大家认为壳很老了,直接跑脚本就可以了,但本人并不是这麽认为,本人可能比较执拗的人,一定要知其然,知其所以然(所以学得慢吧,也许,呵呵。)
看了kissy的壳世界,对VMP有一定的认识了,也在网上找了一些有关VMP1.8的文章来看了。搜到UPK,一阵暗喜,UPK有一些关於VMP的文章,可惜,我注册后过几天就关了,令人唏嘘不已。
不说了,来正题,再者PYG论坛里好像没多少人写有关壳的文章,小弟不才,但对壳有兴趣,虽然破解能力一般,但本着学习的态度,把文章写出来供大家一起研究。


有错误请大家指正。
实验程序用DELPHI生成的一个默认窗体,在保护选项里只选Import protection,即只保护输入表.
经过研究和资料,VMP会先将DLL的导出表遍成进行分析,找到函数后把差值写到一个地址里,调用时再将值取出来,加上一个差值,然後就得到实际的API地址,这里VMP1.8的处理好厉害,会把e8型调用API的CALL
也用VM函数处理了,当然FF 25 型的也处理掉,所以在Nooby牛的脚本里把差值算出来,用DLL调用,运行时把差值读出来,达到跨平台的目的。(跟VM累的很,跳转多,我就不截图了,大家看看代码分析吧),
花指令比较多,有用的指令我注释出来了)
首先,来看看找导入表的地方(以Kernel32.dll为例)(代码较多,不太好看,请大家见谅):
004A17EE    8B70 3C         MOV     ESI, DWORD PTR DS:[EAX+0x3C]             ; PE标识偏移
004A17F1    F6D6            NOT     DH
004A17F3    66:0FADF2       SHRD    DX, SI, CL
004A17F7    30DA            XOR     DL, BL
004A17F9    01C6            ADD     ESI, EAX                                 ; 基址+偏移定位到PE头VA
004A17FB    0FBED1          MOVSX   EDX, CL
004A17FE    D3E2            SHL     EDX, CL
004A1800    8B56 78         MOV     EDX, DWORD PTR DS:[ESI+0x78]             ; 导出表RVA
004A1803    9C              PUSHFD
004A1804    F6C4 B7         TEST    AH, 0xB7
004A1807    84D1            TEST    CL, DL
004A1809    66:0FBAE3 06    BT      BX, 0x6
004A180E    85D2            TEST    EDX, EDX                                 ; 导出表RVA是否为0
004A1810    52              PUSH    EDX
004A1811    8D6424 28       LEA     ESP, DWORD PTR SS:[ESP+0x28]
004A1815    0F84 CB030000   JE      delphi7?004A1BE6                         ; 为0则跳走
004A181B    66:F7D1         NOT     CX
004A181E    30D1            XOR     CL, DL
004A1820    F9              STC
004A1821    01C2            ADD     EDX, EAX                                 ; 导出表VA
004A1823    68 FFF78DF6     PUSH    0xF68DF7FF
004A1828    8B4E 7C         MOV     ECX, DWORD PTR DS:[ESI+0x7C]             ; 导出表大小
004A182B    F9              STC
004A182C    80FA 79         CMP     DL, 0x79
004A182F    E9 A8570000     JMP     delphi7?004A6FDC
接着:

004A00BF    01D1            ADD     ECX, EDX                                 ; 导出表最后的位置
004A00C1  ^ E9 5A9DFFFF     JMP     delphi7?00499E20


00499E20    F9              STC
00499E21    894C24 0C       MOV     DWORD PTR SS:[ESP+0xC], ECX              ; 导入表最后的位置
00499E25    66:0FA3F7       BT      DI, SI
00499E29    F5              CMC
00499E2A    66:0FC9         BSWAP   CX
00499E2D    8B4D 04         MOV     ECX, DWORD PTR SS:[EBP+0x4]              ; API字符串
00499E30    84E0            TEST    AL, AH
00499E32    0FA3DE          BT      ESI, EBX
00499E35    81F9 FFFF0000   CMP     ECX, 0xFFFF
00499E3B    9C              PUSHFD
00499E3C    50              PUSH    EAX
00499E3D    8D6424 0C       LEA     ESP, DWORD PTR SS:[ESP+0xC]
00499E41    0F86 CABF0000   JBE     delphi7?004A5E11
00499E47    66:0FA4C1 07    SHLD    CX, AX, 0x7
00499E4C    8B7A 24         MOV     EDI, DWORD PTR DS:[EDX+0x24]             ; AddreesOfNumberOrdinal的RVA
00499E4F    D0DB            RCR     BL, 1
00499E51    0FBCDA          BSF     EBX, EDX
00499E54    66:87D9         XCHG    CX, BX
00499E57    01C7            ADD     EDI, EAX                                 ; AddressOfNumberOrdinal的VA
00499E59    66:0FADF1       SHRD    CX, SI, CL
00499E5D    8B5A 20         MOV     EBX, DWORD PTR DS:[EDX+0x20]             ; AddressOfNames的RVA
00499E60    0FC9            BSWAP   ECX
00499E62    66:0FA4D1 0A    SHLD    CX, DX, 0xA
00499E67    D2C5            ROL     CH, CL
00499E69    01C3            ADD     EBX, EAX                                 ; AddressOfNames的VA
00499E6B    18D5            SBB     CH, DL
00499E6D    0F99C5          SETNS   CH
00499E70    FEC1            INC     CL
00499E72    C70424 00000000 MOV     DWORD PTR SS:[ESP], 0x0
00499E79    66:D3C9         ROR     CX, CL
00499E7C    8B4A 18         MOV     ECX, DWORD PTR DS:[EDX+0x18]             ; NumberOfNames,以名称导出函数数量
00499E7F  ^ E9 11D5FFFF     JMP     delphi7?00497395



//这里说明一下,在遍历时这里是用二分查找法来查找的,提高查找速度。
00497395   /0F8F 0A3B0000   JG      delphi7?0049AEA5
0049739B   |66:0FBAE5 01    BT      BP, 0x1
004973A0   |F8              CLC
004973A1   |85C9            TEST    ECX, ECX                                 ; 最大数量是否为0
004973A3   |9C              PUSHFD
004973A4   |8D6424 04       LEA     ESP, DWORD PTR SS:[ESP+0x4]
004973A8   |0F8A 76D70000   JPE     delphi7?004A4B24
004973AE   |9C              PUSHFD
004973AF   |8D6424 04       LEA     ESP, DWORD PTR SS:[ESP+0x4]
004973B3   |0F84 2DA80000   JE      delphi7?004A1BE6                         ; 为0则跳走
004973B9   |38FE            CMP     DH, BH
004973BB   |60              PUSHAD
004973BC   |9C              PUSHFD
004973BD   |83E9 01         SUB     ECX, 0x1                                ; 最大数量减1
004973C0   |9C              PUSHFD
004973C1   |38F7            CMP     BH, DH
004973C3   |FF7424 08       PUSH    DWORD PTR SS:[ESP+0x8]
004973C7   |C64424 04 01    MOV     BYTE PTR SS:[ESP+0x4], 0x1
004973CC   |894C24 30       MOV     DWORD PTR SS:[ESP+0x30], ECX             ; 将新的最大数量存到栈
004973D0   |83C4 2C         ADD     ESP, 0x2C
004973D3   |66:0FBDCA       BSR     CX, DX
004973D7   |8B0C24          MOV     ECX, DWORD PTR SS:[ESP]                  ; 二分结果
004973DA   |F7C6 A9869ACD   TEST    ESI, 0xCD9A86A9
004973E0   |84D4            TEST    AH, DL
004973E2   |F8              CLC
004973E3   |38C7            CMP     BH, AL
004973E5   |3B4C24 04       CMP     ECX, DWORD PTR SS:[ESP+0x4]              ; 二分结果与最大数量比较,这个会随着计算改变
004973E9   |E9 B4790000     JMP     delphi7?0049EDA2


004A2371  ^\0F87 6FF8FFFF   JA      delphi7?004A1BE6                         ; 大于则跳走


0049A081    034C24 08       ADD     ECX, DWORD PTR SS:[ESP+0x8]              ; 二分结果与这个最大数量相加
0049A085    83EC FC         SUB     ESP, -0x4
0049A088    0F8E 6E170000   JLE     delphi7?0049B7FC
0049A08E    66:FFCE         DEC     SI
0049A091    66:D3D7         RCL     DI, CL
0049A094    66:09DE         OR      SI, BX
0049A097    D1E9            SHR     ECX, 1                                   ; 再除以2,得到新的函数数组下标
0049A099    0FACEF 10       SHRD    EDI, EBP, 0x10
0049A09D    21CE            AND     ESI, ECX
0049A09F    66:0FACC6 0B    SHRD    SI, AX, 0xB
0049A0A4    8B3C8B          MOV     EDI, DWORD PTR DS:[EBX+ECX*4]            ; 第ecx个NumberOfNames的RVA
0049A0A7    68 B3CED963     PUSH    0x63D9CEB3
0049A0AC    01C7            ADD     EDI, EAX                                 ; 第ecx个NumberOfNames的VA
0049A0AE    F8              CLC
0049A0AF    C1E6 0C         SHL     ESI, 0xC
0049A0B2    8B75 04         MOV     ESI, DWORD PTR SS:[EBP+0x4]              ; API字符串
0049A0B5    83EC FC         SUB     ESP, -0x4
0049A0B8    0FBAE2 0F       BT      EDX, 0xF
0049A0BC    66:0FA3FE       BT      SI, DI
0049A0C0    66:0FA3CF       BT      DI, CX
0049A0C4    60              PUSHAD
0049A0C5    A6              CMPS    BYTE PTR DS:[ESI], BYTE PTR ES:[EDI]     ; 逐字节比较
0049A0C6    C64424 04 7E    MOV     BYTE PTR SS:[ESP+0x4], 0x7E
0049A0CB    8D6424 20       LEA     ESP, DWORD PTR SS:[ESP+0x20]
0049A0CF  ^ 0F87 15E3FFFF   JA      delphi7?004983EA                         ; 大于则找后面的
0049A0D5    55              PUSH    EBP                                      ; 小于等于则来到这里
0049A0D6    9C              PUSHFD
0049A0D7    51              PUSH    ECX
0049A0D8    E9 DCD60000     JMP     delphi7?004A77B9




首先看看找到的API字符大於目的API字符的地方,大於的话,这个ecx会加1,然後和二分结果相加,再除以2找到新的中值位置。
004983EA    F9              STC
004983EB    9C              PUSHFD
004983EC    83C1 01         ADD     ECX, 0x1                                ; 二分结果加1
004983EF    E9 E66E0000     JMP     delphi7?0049F2DA

0049F2DA    894C24 04       MOV     DWORD PTR SS:[ESP+0x4], ECX             ; 二分结果
0049F2DE    882424          MOV     BYTE PTR SS:[ESP], AH
0049F2E1    60              PUSHAD
0049F2E2    60              PUSHAD
0049F2E3    8D6424 44       LEA     ESP, DWORD PTR SS:[ESP+0x44]
0049F2E7  ^ E9 E780FFFF     JMP     delphi7?004973D3




004973D3    66:0FBDCA       BSR     CX, DX
004973D7    8B0C24          MOV     ECX, DWORD PTR SS:[ESP]                  ; 二分结果
004973DA    F7C6 A9869ACD   TEST    ESI, 0xCD9A86A9
004973E0    84D4            TEST    AH, DL
004973E2    F8              CLC
004973E3    38C7            CMP     BH, AL
004973E5    3B4C24 04       CMP     ECX, DWORD PTR SS:[ESP+0x4]              ; 二分结果与最大数量比较,这个会随着计算改变
004973E9    E9 B4790000     JMP     delphi7?0049EDA2


004A2371  ^\0F87 6FF8FFFF   JA      delphi7?004A1BE6                         ; 大于则跳走
004A2377    66:09DF         OR      DI, BX
004A237A    66:87F7         XCHG    DI, SI
004A237D    D3DF            RCR     EDI, CL
004A237F    E8 FD7CFFFF     CALL    delphi7?0049A081


0049A081    034C24 08       ADD     ECX, DWORD PTR SS:[ESP+0x8]              ; 二分结果与这个最大数量相加
0049A085    83EC FC         SUB     ESP, -0x4
0049A088    0F8E 6E170000   JLE     delphi7?0049B7FC
0049A08E    66:FFCE         DEC     SI
0049A091    66:D3D7         RCL     DI, CL
0049A094    66:09DE         OR      SI, BX
0049A097    D1E9            SHR     ECX, 1                                   ; 再除以2,得到新的函数数组下标
0049A099    0FACEF 10       SHRD    EDI, EBP, 0x10
0049A09D    21CE            AND     ESI, ECX
0049A09F    66:0FACC6 0B    SHRD    SI, AX, 0xB
0049A0A4    8B3C8B          MOV     EDI, DWORD PTR DS:[EBX+ECX*4]           ; 第ecx个NumberOfNames的RVA
0049A0A7    68 B3CED963     PUSH    0x63D9CEB3
0049A0AC    01C7            ADD     EDI, EAX                                 ; 第ecx个NumberOfNames的VA
0049A0AE    F8              CLC
0049A0AF    C1E6 0C         SHL     ESI, 0xC
0049A0B2    8B75 04         MOV     ESI, DWORD PTR SS:[EBP+0x4]              ; API字符串
0049A0B5    83EC FC         SUB     ESP, -0x4
0049A0B8    0FBAE2 0F       BT      EDX, 0xF
0049A0BC    66:0FA3FE       BT      SI, DI
0049A0C0    66:0FA3CF       BT      DI, CX
0049A0C4    60              PUSHAD
0049A0C5    A6              CMPS    BYTE PTR DS:[ESI], BYTE PTR ES:[EDI]     ; 逐字节比较
0049A0C6    C64424 04 7E    MOV     BYTE PTR SS:[ESP+0x4], 0x7E
0049A0CB    8D6424 20       LEA     ESP, DWORD PTR SS:[ESP+0x20]
0049A0CF  ^ 0F87 15E3FFFF   JA      delphi7?004983EA                         ; 大于则找后面的
小於来到这里:

004973BD    83E9 01         SUB     ECX, 0x1                                ; 最大数量减1
004973C0    9C              PUSHFD
004973C1    38F7            CMP     BH, DH
004973C3    FF7424 08       PUSH    DWORD PTR SS:[ESP+0x8]
004973C7    C64424 04 01    MOV     BYTE PTR SS:[ESP+0x4], 0x1
004973CC    894C24 30       MOV     DWORD PTR SS:[ESP+0x30], ECX             ; 将新的最大数量存到栈
004973D0    83C4 2C         ADD     ESP, 0x2C
004973D3    66:0FBDCA       BSR     CX, DX
004973D7    8B0C24          MOV     ECX, DWORD PTR SS:[ESP]                 ; 二分结果
004973DA    F7C6 A9869ACD   TEST    ESI, 0xCD9A86A9
004973E0    84D4            TEST    AH, DL
004973E2    F8              CLC
004973E3    38C7            CMP     BH, AL
004973E5    3B4C24 04       CMP     ECX, DWORD PTR SS:[ESP+0x4]              ; 二分结果与最大数量比较,这个会随着计算改变
004973E9    E9 B4790000     JMP     delphi7?0049EDA2


这里总结一下,首先,设要找到这个目的API下标为i,初值i=1,导出函数最大数量为t,则找法是从i=(i+t)/2开始,如果找到的字符串是大於(cmps比较),这个下标i,i=i+1,再重新算中值i=(i+t)/2,
如果是小於,则t=i-1,然後,i=(i+t)/2,再重新找,直到函数名完全一样。

然後,到获取API的地址的地方了:
004A2C9D    8B7A 24         MOV     EDI, DWORD PTR DS:[EDX+0x24]              ; 函数序号表的RVA
004A2CA0    52              PUSH    EDX
004A2CA1    896424 04       MOV     DWORD PTR SS:[ESP+0x4], ESP
004A2CA5    01C7            ADD     EDI, EAX                                 ; 函数序号表的VA
004A2CA7    F5              CMC
004A2CA8    FF7424 04       PUSH    DWORD PTR SS:[ESP+0x4]
004A2CAC    66:0FBAE5 03    BT      BP, 0x3
004A2CB1    81FD 90BA8BB0   CMP     EBP, 0xB08BBA90
004A2CB7    0FB70C4F        MOVZX   ECX, WORD PTR DS:[EDI+ECX*2]             ; AddressOfNameOrdinals[ecx],取出这个序号
004A2CBB    83EC F4         SUB     ESP, -0xC
004A2CBE    66:0FA4C7 01    SHLD    DI, AX, 0x1
004A2CC3    66:C1DF 02      RCR     DI, 0x2
004A2CC7    66:19C7         SBB     DI, AX
004A2CCA    0FBAFF 19       BTC     EDI, 0x19
004A2CCE    8B7A 1C         MOV     EDI, DWORD PTR DS:[EDX+0x1C]             ; AddressOfFunctions的RVA
004A2CD1    9C              PUSHFD
004A2CD2    01C7            ADD     EDI, EAX                                ; AddressOfFunctions的VA
004A2CD4    9C              PUSHFD
004A2CD5    9C              PUSHFD
004A2CD6    57              PUSH    EDI
004A2CD7  ^ E9 0657FFFF     JMP     delphi7?004983E2


004983E2    8B3C8F          MOV     EDI, DWORD PTR DS:[EDI+ECX*4]           ; AddressOfFunction[ecx],第ecx个函数的地址RVA
004983E5    E8 92890000     CALL    delphi7?004A0D7C



004A0D7C    F5              CMC
004A0D7D    85FF            TEST    EDI, EDI                                 ; 得到API的RVA是否为0
004A0D7F    60              PUSHAD
004A0D80    8D6424 34       LEA     ESP, DWORD PTR SS:[ESP+0x34]
004A0D84    0F84 5C0E0000   JE      delphi7?004A1BE6
004A0D8A    66:85E9         TEST    CX, BP
004A0D8D  ^ 0F8E FF7FFFFF   JLE     delphi7?00498D92
004A0D93    84C7            TEST    BH, AL
004A0D95    F5              CMC
004A0D96    01F8            ADD     EAX, EDI                                ; API的VA,即真实函数地址
004A0D98    0FA3CE          BT      ESI, ECX
004A0D9B    39D0            CMP     EAX, EDX                                ; 与输出表VA比较
004A0D9D    E9 35190000     JMP     delphi7?004A26D7


004A1BF2    87FA            XCHG    EDX, EDI                                ; API函数RVA与输出表互换
004A1BF4    66:0FBED1       MOVSX   DX, CL
004A1BF8    5A              POP     EDX
004A1BF9    8D2CDD 45720347 LEA     EBP, DWORD PTR DS:[EBX*8+0x47037245]
004A1C00    0F9FC2          SETG    DL
004A1C03    88E2            MOV     DL, AH
004A1C05    5A              POP     EDX
004A1C06    68 48AAD205     PUSH    0x5D2AA48
004A1C0B    68 D2A45EA1     PUSH    0xA15EA4D2
004A1C10    60              PUSHAD
004A1C11    F6D2            NOT     DL
004A1C13    8B5424 28       MOV     EDX, DWORD PTR SS:[ESP+0x28]            ; ??
004A1C17    0F9EC2          SETLE   DL
004A1C1A    9C              PUSHFD
004A1C1B    9C              PUSHFD
004A1C1C    0F9FC7          SETG    BH
004A1C1F    8B5424 34       MOV     EDX, DWORD PTR SS:[ESP+0x34]            ; ??
004A1C23    87EB            XCHG    EBX, EBP
004A1C25    5E              POP     ESI
004A1C26    8A5C24 04       MOV     BL, BYTE PTR SS:[ESP+0x4]
004A1C2A    8B5C24 34       MOV     EBX, DWORD PTR SS:[ESP+0x34]             ; dll基址
004A1C2E    9C              PUSHFD
004A1C2F    8B7C24 3C       MOV     EDI, DWORD PTR SS:[ESP+0x3C]             ; API名称
004A1C33    9C              PUSHFD
004A1C34    0FB6F2          MOVZX   ESI, DL
004A1C37    8B7424 44       MOV     ESI, DWORD PTR SS:[ESP+0x44]             ; dll名称
004A1C3B    FF7424 08       PUSH    DWORD PTR SS:[ESP+0x8]
004A1C3F    8B6C24 4C       MOV     EBP, DWORD PTR SS:[ESP+0x4C]
004A1C43    883424          MOV     BYTE PTR SS:[ESP], DH
004A1C46    66:892C24       MOV     WORD PTR SS:[ESP], BP
004A1C4A    FF7424 50       PUSH    DWORD PTR SS:[ESP+0x50]
004A1C4E    C2 5C00         RETN    0x5C                                     ; 返回API CHECK


返回这里:
0049C97A    68 84F15E4E     PUSH    0x4E5EF184                               ; 获取API名称后来到这里,API check,这里会计算一个DWORD值
0049C97F    E8 58B40000     CALL    delphi7?004A7DDC


获取了API地址後就跳到再次进入虚拟机进行操作,後面是算那个差值,这里进入的虚拟机,我也不能完全看明白,只能找到一些较关键的地方:
首先说明一下我要找的地方
kernel32.FreeResource
DS:[00466C84]=9D732DB0
减法:76D9F879 - D966CAC9 =9D732DB0
加法:76D9F879+         26993537 =9D732DB0(补码加法)


因为是先知道结果再来找过程,因此不知道他後面用加法还是减法去算,所以先列出来可能的值会出现在什麽位置,上面的值用红色标出
进入虚拟机的函数就不列出来了,跳转大多,发代码也不好说明,进这里一个主要操作是从EBP的内容和EDI这里的内容计算过後会复制来复制去。
当然後面有个最关键的地方:
1.从esi取出DWORD:
004A8DEC    8B06            MOV     EAX, DWORD PTR DS:[ESI]                  ; 解密一个DWORD
004A8DEE    66:0FBAE0 01    BT      AX, 0x1
004A8DF3    F5              CMC
004A8DF4    01D8            ADD     EAX, EBX
004A8A14    05 00323AFF     ADD     EAX, 0xFF3A3200
004A8A19    60              PUSHAD
004A8A1A    F7D0            NOT     EAX                                      ; 取反
004A8A1C    52              PUSH    EDX
004A8A1D    66:C74424 10 2B>MOV     WORD PTR SS:[ESP+0x10], 0xD02B
004A8A24    40              INC     EAX                                     ; 加1
004A8A25    E9 63080000     JMP     delphi7?004A928D

004A9518    01C3            ADD     EBX, EAX
004A951A    9C              PUSHFD
004A951B  ^ E9 77E7FFFF     JMP     delphi7?004A7C97




004A7CA5    8945 00         MOV     DWORD PTR SS:[EBP], EAX                  ; 这个DWORD写进EBP里面如果此时EAX=26993537,正是FREERESOURCE的差值
///後面省略一些,具体记录找不到了,累。。
004A90D8    8B45 00         MOV     EAX, DWORD PTR SS:[EBP]                  ; 这里会出现API地址76D9F879  kernel32.FreeResource
004A90DB    F5              CMC
004A90DC    0145 04         ADD     DWORD PTR SS:[EBP+0x4], EAX              ; 这里[ebp+4]就是那个值26993537,加上API地址,就等於那个差值了


到此,这个结果出来了,几年前大牛已经将VM看透了,再次膜拜一下。


你可能感兴趣的:(Reverse)