IAT 重建工具
最近,写了一个IAT重建工具,感觉还比较有实用性,为感谢看雪论坛及坛上各位前辈,大侠,兄弟们之前对我的无私帮助,将这个软件发放出来,献给大家,希望大家喜欢,同时希望得到高手的指点,让这个软件更加完善。
IATRebulid 使用说明
一、IATRebulid V1.01主要功能:
1. 对PE文件的引入表进行手工重建功能
2. 往PE文件中追加引入函数
3. 导出PE文件的引入表
4. 对PE文件增加新节
5. RVA 地址转换
6. 更改PE文件头的相关数据
二、编写原因
1. 在用ImportREC进行引入表修复时,经常出现ImportREC无法修补,甚至没有反应的现象,搞得很郁闷。
2. 在PE DIY时,用LordPE加入引入函数,每进行一次引入,都会增加一个新节,搞得很不爽。
基于以上原因,于是就了写这个软件。
三、软件界面
四、实例分析:
现以来自PYG的一个CM为例(见附件),来说明这个软件是如何重建引入表的:
1. 查壳
PEiD侦测出壳为:FSG 2.0 -> bart/xt
2. 脱壳
bp LoadLibraryA 下断,可以定位到下面:
004001C1 5E POP ESI
004001C2 AD LODS DWORD PTR DS:[ESI]
004001C3 97 XCHG EAX,EDI
004001C4 AD LODS DWORD PTR DS:[ESI]
004001C5 50 PUSH EAX
004001C6 FF53 10 CALL DWORD PTR DS:[EBX+10] ; kernel32.LoadLibraryA
004001C9 95 XCHG EAX,EBP
004001CA 8B07 MOV EAX,DWORD PTR DS:[EDI]
004001CC 40 INC EAX
004001CD ^ 78 F3 JS SHORT CrackMe0.004001C2
004001CF 75 03 JNZ SHORT CrackMe0.004001D4
004001D1 FF63 0C [COLOR="Red"]JMP DWORD PTR DS:[EBX+C][/COLOR] ;转入OEP
004001D4 50 PUSH EAX
004001D5 55 PUSH EBP
004001D6 FF53 14 CALL DWORD PTR DS:[EBX+14] ; kernel32.GetProcAddress
004001D9 AB STOS DWORD PTR ES:[EDI]
004001DA ^ EB EE JMP SHORT CrackMe0.004001CA 跟踪时,很容易就发现004001D1处是跳入的OEP的关键跳转。
于是,就来到OEP处:
004689DC ? 55 PUSH EBP
004689DD ? 8BEC MOV EBP,ESP
004689DF . 83C4 F0 ADD ESP,-10
004689E2 . B8 E4874600 MOV EAX,CrackMe0.004687E4
004689E7 . E8 74D2F9FF CALL CrackMe0.00405C60
004689EC . A1 90A94600 MOV EAX,DWORD PTR DS:[46A990]
004689F1 . 8B00 MOV EAX,DWORD PTR DS:[EAX]
004689F3 . E8 B4AAFEFF CALL CrackMe0.004534AC
004689F8 . A1 90A94600 MOV EAX,DWORD PTR DS:[46A990]
004689FD . 8B00 MOV EAX,DWORD PTR DS:[EAX]
004689FF . BA 3C8A4600 MOV EDX,CrackMe0.00468A3C
00468A04 . E8 B3A6FEFF CALL CrackMe0.004530BC
... OD 直接脱壳,运行出现异常。
运行ImportRec, 选择这个CM进程,填入OEP: 689DC,再点 [自动查找IAT] 和 [获取输入表]。 晕,ImportRec只找到一个Kernle32.dll,且里面只有3个引入函数!(不知是否我的操作方法不对???)
算了,不用它了,还是用本软件来手工修补IAT表吧!
3、手工修补IAT
OD再次运行加壳的CM,还是bp LoadLibraryA 下断,来到这里:
004001C6 FF53 10 CALL DWORD PTR DS:[EBX+10] ; kernel32.LoadLibraryA
004001C9 95 XCHG EAX,EBP
004001CA 8B07 MOV EAX,DWORD PTR DS:[EDI]
004001CC 40 INC EAX
004001CD ^ 78 F3 JS SHORT CrackMe0.004001C2
004001CF 75 03 JNZ SHORT CrackMe0.004001D4
004001D1 FF63 0C [COLOR="red"] JMP DWORD PTR DS:[EBX+C][/COLOR] ;转入OEP
004001D4 50 PUSH EAX
004001D5 55 PUSH EBP
004001D6 FF53 14 CALL DWORD PTR DS:[EBX+14] ; kernel32.GetProcAddress
004001D9 AB STOS DWORD PTR ES:[EDI]
004001DA ^ EB EE JMP SHORT CrackMe0.004001CA 注意到在调用GetProcAddress之后,将返回值传给了[EDI], 当程序运行到004001D9处时,在OD的Dump窗口中输入 d edi
此时,数据是按照16进制字节方式进行显示的,我们需要显示出API函数名称,操作如下:
在Dump窗口中,选中一个DWORD数据的第一个字节,右键,在弹出的菜单中,选择:[长型/地址],如下图所示:
然后,OD就会显示如下内容:
怎么样,兴奋了吧,API函数都在这里面。让程序运行到OEP,还好,这些信息没有被清空,少了我们不少麻烦,于是在这个窗口中,通过上翻/下翻,将所有的API函数一次性复制到一个文本文件中,内容如下:
0046C12C 7C93188A ntdll.RtlDeleteCriticalSection
0046C130 7C9210ED ntdll.RtlLeaveCriticalSection
0046C134 7C921005 ntdll.RtlEnterCriticalSection
0046C138 7C809FA1 kernel32.InitializeCriticalSection
0046C13C 7C809B14 kernel32.VirtualFree
0046C140 7C809A81 kernel32.VirtualAlloc
...
0046C1B0 7C812CA9 kernel32.GetStdHandle
0046C1B4 7FFFFFFF
0046C1B8 77D3FA46 user32.GetKeyboardType
0046C1BC 77D3EC98 user32.LoadStringA
0046C1C0 77D5050B user32.MessageBoxA
0046C1C4 77D3EC40 user32.CharNextA
...
0046C734 771950C0 comctl32.ImageList_GetImageCount
0046C738 77195084 comctl32.ImageList_Destroy
0046C73C 771992D5 comctl32.ImageList_Create 这里有很多的API函数,为了节省篇幅,省略了很多,然后编辑成类似如下格式的文本文件:
[COLOR="red"][ntdll] 0046C12C[/COLOR]
RtlDeleteCriticalSection
RtlLeaveCriticalSection
RtlEnterCriticalSection
[COLOR="red"][kernel32] 0046C138[/COLOR]
InitializeCriticalSection
... 省略一些API函数
CharNextA
[COLOR="red"][ADVAPI32] 0046C1CC[/COLOR]
RegQueryValueExA
RegOpenKeyExA
RegCloseKey
[COLOR="red"][oleaut32] 0046C1DC[/COLOR]
SysFreeString
SysReAllocStringLen
SysAllocStringLen
[COLOR="red"][kernel32] 0046C1EC[/COLOR]
TlsSetValue
TlsGetValue
LocalAlloc
GetModuleHandleA
[COLOR="red"][ADVAPI32] 0046C200[/COLOR]
RegQueryValueExA
RegOpenKeyExA
RegCloseKey
[COLOR="red"][kernel32] 0046C210[/COLOR]
lstrcpyA
... 省略一些API函数
CompareStringA
CloseHandle
[COLOR="red"][version] 0046C30C[/COLOR]
VerQueryValueA
GetFileVersionInfoSizeA
GetFileVersionInfoA
[COLOR="red"][GDI32] 0046C31C[/COLOR]
UnrealizeObject
StretchBlt
... 省略一些API函数
CreateBitmap
CopyEnhMetaFileA
BitBlt
[COLOR="red"][user32] 0046C42C[/COLOR]
CreateWindowExA
... 省略一些API函数
ActivateKeyboardLayout
[COLOR="red"][oleaut32] 0046C6C4[/COLOR]
SafeArrayPtrOfIndex
SafeArrayGetUBound
SafeArrayGetLBound
SafeArrayCreate
VariantChangeType
VariantCopy
VariantClear
VariantInit
[COLOR="red"][comctl32] 0046C6E8[/COLOR]
ImageList_SetIconSize
... 省略一些API函数
ImageList_Create
这个文件格式想必大家一看就明白,我就不作多说了。只简单地注意以下几点:
a. 文件中可以插入空行,或加入注释语句 (行首加上分号表示注释)
b. 同一组DLL中引入函数顺序千万不要弄错,不同组的DLL这间顺序可以随意,但最好还是和原来保持一致.
c. 每一组DLL的引入函数最前面加入DLL名称,用“[]”括起来,随后加上一个空格,再加上这组DLL数据的起始VA地址,注意:是VA地址,不是RVA地址。
d. 可能会存在多组相同名称的DLL,不要合并它,还是按照原来的方式进行排列。
e. 你还会发现原IAT表中有几个ntdll的函数混入到了kernel32.dll中,如下所示:
0046C1A4 7C862B8A kernel32.UnhandledExceptionFilter
0046C1A8 7C957A40 [COLOR="red"]ntdll.RtlUnwind[/COLOR]
0046C1AC 7C81EAE1 kernel32.RaiseException
......
0046C254 7C801D77 kernel32.LoadLibraryA
0046C258 7C9210ED [COLOR="red"]ntdll.RtlLeaveCriticalSection[/COLOR]
0046C25C 7C809FA1 kernel32.InitializeCriticalSection
......
0046C2B0 7C80C9C1 kernel32.GetLocalTime
0046C2B4 7C930331 [COLOR="red"]ntdll.RtlGetLastWin32Error[/COLOR]
0046C2B8 7C81367C kernel32.GetFullPathNameA
......
0046C2E8 7C83761C kernel32.EnumCalendarInfoA
0046C2EC 7C921005 [COLOR="red"]ntdll.RtlEnterCriticalSection[/COLOR]
0046C2F0 7C93188A [COLOR="red"]ntdll.RtlDeleteCriticalSection[/COLOR]
0046C2F4 7C81082F kernel32.CreateThread 这应该是壳搞的鬼,它将Kernel32.dll中的一些函数替换成了ntdll.dll中的具有相同功能的函数。这里,我们要将它恢复成kernel32.dll中的函数。也许你说了,我不知道kern32.dll中与之对应的函数名称是什么。老实说,我也不知道,但没关系,可以看出:ntdll中的函数前面全部带有一个Rtl前缀。我们先将这个前缀去掉,修被之后,运行试试看,函数名有误的话,Windows会告诉我们的。于是将上面函数改为如下所示:
...
UnhandledExceptionFilter
[COLOR="red"]Unwind[/COLOR]
RaiseException
......
LoadLibraryA
[COLOR="red"]LeaveCriticalSection[/COLOR]
InitializeCriticalSection
......
GetLocalTime
[COLOR="red"]GetLastWin32Error[/COLOR]
GetFullPathNameA
......
EnumCalendarInfoA
[COLOR="red"]EnterCriticalSection[/COLOR]
[COLOR="red"]DeleteCriticalSection[/COLOR]
CreateThread
也许,你还会说,要将IAT表中每一行函数名称前面一串字符手工删掉,这里有几百个函数,岂不累死我也。这里,我告诉你一个很好的方法:将从OD复制过来的IAT表原封不动地粘贴到VC中的一个文件中去,在VC中,先按住<Alt>键不放,再按鼠标左键拉框,应可框选方框内的文字,然后,Del键删掉选中的文字块即可。什么,你没装VC,没关系,WORD用同样的方法可以实现同样的功能。你不要再说WORD也没装哦,这样的话,我只有替你哭了,手工一行一行删吧!
现在我可以很高兴的高诉你:
V1.02版已经基本上不需要手来进行处理了,可以利用IATRebulid新增的解读数据功能,将OD中的API函数数据全部解读出API函数字符串。
然后,你只需手工稍加整理,就编辑为符合要求的IAT格式文件。
如果你还不清楚这个文件格式的话,可以看附件中的IAT文件样例,很简单,一看就明白。
五、重建引入表
引入表文件编辑好后,就可用本软件进行重建了:方法如下:
1、先确定新引入表所需要空间的大小:
用IATRebulid打开脱壳后的CM,点击 [重建IAT] 按钮,然后会提示选择IAT文件,将刚才编辑好的IAT文件选进来,软件提示如下图所示:
可以看出,这个IAT文件需要空间最少为:0x213A, 呵呵,还真不小。没关系,用IATRebulid新增一个节,大小为3000即可。先点击上面的 [取消] 按钮,放弃这次操作,因为目前还没有足够的空间来容纳新的IAT表。
2、增加一个新的区段便于容纳重建后的IAT表:
点击 [增加区段] 按钮,然后按照提示,输入要增加新节的大小,如下图:
增加新节后,新节名称为NewSec,随后也可以现将它更改为其它名称,这里就不改了。新节数据如下:
记下新节的RVA地址:000C8000, 因为随后我们要将IAT表重建在这个地方。
3、重建IAT
再在IATRebulid中,点击[重建IAT] 按钮,然后会提示选择IAT文件,将刚才编辑好的IAT文件选进来,软件提示要求输入新IAT表的RVA地址,如下图所示:
可以看出,IATRebulid 已经自动将000C8000这个值自动填进去了(IATRebulid会将最后一个节的起始RVA地址默认为新IAT表的地址),当然也可以修改为其它地址值,这里我们就不修改了,直接点击 [确定] 按钮。于是IATRebulid开始对这个CM进行IAT重建,重建完毕后,如下图所示:
可以看出,DLL和API函数都已引入,可能你已经按奈不住了,马上运行修补后的CM,想看看效果。结果弹出一个错误对话框:
哇,心顿时凉了半截! 没关系,这小意思啦:查看刚才编辑的IAT文件:
找到:InitializeCriticalSection这个函数,如下所示:
[ntdll] 0046C12C
RtlDeleteCriticalSection
RtlLeaveCriticalSection
RtlEnterCriticalSection
[kernel32] 0046C138
InitializeCriticalSection
... 有点奇怪了,InitializeCriticalSection这个函数我们明明是按排在kernel32.dll中,为什么Windows却跑到ntdll.dll中去找这个函数呢?仔细一看,原来:ntdll引入数据的起始地址为:0046C12C,从它这里引入了3个函数,那么结束地址就是:0046C12C + 3*4 = 0046C138,正是Kernel32.dll引入数据的起始地址。ntdll.dll和Kernel32.dll引入数据之间已经没有空隙了(本来,每个DLL的引入数据完毕后,应加上一个00000000表示结束),这里已经没有了,所以Windows在加载引入函数时,还认为InitializeCriticalSection是位于ntdll中。
知道原因后,就简单了,将ntdll中最后一个函数删掉,保存文件,再在IATRebulid中点击[重建IAT]按钮,IAT地址保持不变,还是:000C8000. 重建后,再运行,又弹出一个错误对话框:
这正是前面那个夹在kernel32.dll中的RtlUnwind函数,我也不知道这个函数在kernel32.dll中的函数名称是什么,再改回原来的RtlUnwind名称试试,保存文件后,再在IATRebulid中点击[重建IAT]按钮,再运行,OK,这个错误提示没有了,不过又出现了一个新的提示:
这个也是刚才的ntdll.RtlGetLastWin32Error函数改名后的函数,看来这个函数名称也改得不对,如果你经常编程的话,你肯定知道Windows有一个GetLastError函数,于是将这个函数名称换成GetLastError试试,再用IATRebulid重建IAT,再运行,OK,激动人心的时刻来了,IAT修被成功,程序界面已弹出,让我们来欣赏这个成果吧:
至此,这个CM已成功手工修补IAT,颇有一番自豪感,不是吗?
在一些加密壳,它在取得API函数地址后,会随即将API函数名称清除,这也难不倒我们,大不了,我们可以在其清空函数名称之前,将函数名称复制下来,同时记下函数地址就行下(一组DLL函数只需记一个地址值).
IATRebulid 其它功能说明:
1. 导出IAT表:可以将PE文件的IAT表导出,手工进行修改后,可以用[重建IAT]功能再导回去。
2. 删除IAT表: 清空PE头部数据目录表里第1项(IMAGE_DIRECTORY_ENTRY_IMPORT)和第12项(IMAGE_DIRECTORY_ENTRY_IAT)中的数据,删除IAT表后的PE文件相当于是一个没有引入表的PE文件。
3. 引入DLL函数:类似LordPE的增加引入函数功能,你可以将引入函数放在指定的空白地址中,而不需要新增一个节。
4. 还有一些其它的功能,就不说了,很简单,大家试试就知道了。
最后,希望大家喜欢这个软件,由于这个软件刚刚写完第一版,也没有经过测试,肯定存在很多Bug,大家在使用的过程中若碰到什么问题,有什么疑问,或者有什么更好的想法,都欢迎告诉我,我将尽力解决。
Enjoy it!
感谢xblzy0423反馈的一个Bug, 已修正(2008-03-24)
==========================================================
V1.02 版新增功能:
1. 增加 API 数据解读功能(可以从OllyDbg中复制过来,直接解读出API函数名称).
可以按照OD中的两种数据格式进行解读,如下图所示:
2. 增加了PE头部数据按字段查询功能.
3. 改变了数据处理方式,极大提高了导入和导出IAT表的速度.
4. 修正导出的IAT文件地址数据格式,使之与导入的IAT文件格式一致.
2008-03-28
==========================================================
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: