文章标题: Patch修复Armadillo的IAT乱序
目标:Fraps v2.7.2
声明:纯属无聊
作者:无聊的菜鸟
时间:2005年12月7日  17:17

近日遇见一Armadillo的IAT乱序。上看雪搜索教程,看到Fly大大的用ArmInline修复,还看到一个是写代码重排IAT。不过我太菜了,没看懂。。。。

我想如果Arm要乱序的话,那他一定要先有正确序列的IAT。

隐藏OD,并且忽略一切异常。

我们在程序运行后中断,然后对第二区段下访问中断,断下后,我们找一个IAT的调用:
00401A2B               FF15 A0327501            call dword ptr ds:[17532A0]
(我们顺变记一下从0401000开始的第一个IAT调用地址00401005)
我们对17532A0下硬件写入断点,然后重来。运行后,我们断在了这里:
01615552               890C82                   mov dword ptr ds:[edx+eax*4],ecx
01615555             ^ E9 24FFFFFF              jmp 0161547E

这是我们看看内存窗口:
01753280  016065DF  遝`
01753284  016065E6  鎒`
01753288  01606654  Tf`
0175328C  016065AC  琫`
01753290  016065C1  羍`
01753294  016064F4  鬱`
01753298  016065E4  鋏`
0175329C  016066EB  雈`
017532A0  016066BA  篺`
017532A4  00000000  ....
017532A8  00000000  ....
017532AC  00000000  ....

像什么?是不是很想准备填充IAT?我们继续运行,断在了这里:
01616A1C               8B85 14DBFFFF            mov eax,dword ptr ss:[ebp-24EC]
01616A22               83C0 04                  add eax,4
01616A25               8985 14DBFFFF            mov dword ptr ss:[ebp-24EC],eax

再看看内存窗口:
01753280  77D2DF46  F咭w   USER32.SetWindowPlacement
01753284  77D2FEC2  漫襴    USER32.MapVirtualKeyA
01753288  77D202DD  ?襴    USER32.SetWindowRgn
0175328C  77D1BDC8  冉褀    USER32.ScreenToClient
01753290  77D1B5F5  醯褀    USER32.InvalidateRect
01753294  77D1B6D4  远褀    USER32.GetWindowRect
01753298  77D1DBEC  燠褀    USER32.MoveWindow
0175329C  77D1C01B  姥w   USER32.SetWindowPos
017532A0  016065D1  裡`
017532A4  016065CA  蔱`
017532A8  01606625  %f`
017532AC  016066BE  緁`

我们向下慢慢走:
01616C7A             ^\E9 C7F6FFFF              jmp 01616346

到了这里程序开始向上跳了,我们猜测,程序已经完成了对User32.dll中需要的函数的处理。所以我们也向上跳去找处理的起始地点,找到了这里:
01616346               6A 01                    push 1
01616348               58                       pop eax
01616349               85C0                     test eax,eax
0161634B               0F84 2E090000            je 01616C7F

继续向下走:
016163DB               FFB5 38D6FFFF            push dword ptr ss:[ebp-29C8]    //GDI32.dll
016163E1               E8 2202FFFF              call 01606608   //相当于GetModuleHandleA
016163E6               8985 54D7FFFF            mov dword ptr ss:[ebp-28AC],eax
....
016167DD               FF15 84E26101            call dword ptr ds:[161E284]                  ; kernel32.GetTickCount   //又看见了这个玩意,估计快到了。
...
01616A61               FFB5 14ABFFFF            push dword ptr ss:[ebp+FFFFAB14]
01616A67               FFB5 54D7FFFF            push dword ptr ss:[ebp-28AC]
01616A6D               E8 EBEEFEFF              call 0160595D  //相当于GetProcAddress
...
比较加密与未加密的运行流程之后发现,关键跳转在这里:
01616A30               83BD 1CCDFFFF 00         cmp dword ptr ss:[ebp-32E4],0  //判断加密后地址是否存在
01616A37               75 42                    jnz short 01616A7B  //不跳的话就不加密。

我们在这里下个硬件执行断点,然后重来:
01616A30               83BD 1CCDFFFF 00         cmp dword ptr ss:[ebp-32E4],0
01616A37               75 42                    jnz short 01616A7B  //nop

然后这里:
01616B97             ^\E9 4DFCFFFF              jmp 016167E9
01616B9C               FF15 84E26101            call dword ptr ds:[161E284]                  ; kernel32.GetTickCount
01616BA2               2B85 58D7FFFF            sub eax,dword ptr ss:[ebp-28A8]
01616BA8               8B8D 5CD7FFFF            mov ecx,dword ptr ss:[ebp-28A4]
01616BAE               6BC9 32                  imul ecx,ecx,32
01616BB1               81C1 D0070000            add ecx,7D0
01616BB7               3BC1                     cmp eax,ecx
01616BB9               76 28                    jbe short 01616BE3  //这种东西怎么能让他不跳呢?修改成EB 28

01616C7A             ^\E9 C7F6FFFF              jmp 01616346
01616C7F               8B85 FCD9FFFF            mov eax,dword ptr ss:[ebp-2604]
01616C85               8985 B4AEFFFF            mov dword ptr ss:[ebp+FFFFAEB4],eax
01616C8B               FFB5 B4AEFFFF            push dword ptr ss:[ebp+FFFFAEB4]
01616C91               E8 F0620000              call 0161CF86                                ; jmp to msvcrt.operator delete
看情况来说,到1616C7F的时候事情该做的都应该完成了。F4到那里。
现在我们看看对应的数据区,完美的IAT。我们把这里记为关键点1

现在我们来思考一下,下面他肯定要乱序,乱序后一定会写入乱序后的地址。而且不出意外的话肯定是从代码段的起始开始写。那么我们对0401007下硬件写入断点。
016174B4               8908                     mov dword ptr ds:[eax],ecx
016174B6             ^ E9 4DFFFFFF              jmp 01617408
我们把这里记为关键点2。

好现在我们就来Patch:
关键点1:
nop 01616A37那里的跳转,EB 01616BB9那里的跳转。
01616C9E               60                       pushad  //保护现场
01616C9F               9C                       pushfd
01616CA0               B8 00005000              mov eax,500000  //新的IAT地址,这里大家要看实际情况,只要程序运行中用不到的地方都行。
01616CA5               BB 08307501              mov ebx,1753008  //现在IAT所处的位置
01616CAA               8B0B                     mov ecx,dword ptr ds:[ebx]
01616CAC               8908                     mov dword ptr ds:[eax],ecx  //搬家
01616CAE               83C0 04                  add eax,4
01616CB1               83C3 04                  add ebx,4
01616CB4               81FB F8327501            cmp ebx,17532F8 //现在的IAT结束地址
01616CBA             ^ 72 EE                    jb short 01616CAA
01616CBC               9D                       popfd
01616CBD               61                       popad
01616CBE             ^ EB DE                    jmp short 01616C9E  //这里跳上去之后我们使用OD的“撤销选择”功能恢复原有代码继续运行。

关键点2:
016174B4               8908                     mov dword ptr ds:[eax],ecx
016174B6               E9 05000000              jmp 016174C0
016174BB               A0 348F6201              mov al,byte ptr ds:[1628F34]  //我们在这里F4
016174C0               60                       pushad  //保护现场
016174C1               9C                       pushfd
016174C2               BB 00005000              mov ebx,500000  //IAT搬家后的地址
016174C7               8B09                     mov ecx,dword ptr ds:[ecx]  //去乱序后的API存储位置中的API
016174C9               390B                     cmp dword ptr ds:[ebx],ecx  //定位我们需要的储存位置
016174CB               74 05                    je short 016174D2
016174CD               83C3 04                  add ebx,4
016174D0             ^ EB F7                    jmp short 016174C9
016174D2               8918                     mov dword ptr ds:[eax],ebx  //写入
016174D4               9D                       popfd
016174D5               61                       popad
016174D6             ^ E9 2DFFFFFF              jmp 01617408  //回去继续

在016174BB断下后同样撤销选择,然后我们再到OEP的时候,看见的IAT就比较美好了。

不过Fraps还有代码搬迁,所以这里我只是对付了一下IAT乱序。