0x00 前言
对于脱壳后的程序往往不能直接运行,这是因为它们很多都没有重建输入表。当然用的od脱壳可能可以。重建输入表一般都是用专业软件比如ImportREC。但是对于逆向研究者来说会自己重讲输入表是必须的技能。这就需要读者对前面几篇所讲的PE文件格式非常熟悉。
0x01 寻找OEP脱壳脱壳
前面已经提到对于区段信息全都已经改的面目全非的加壳程序,用两次内存断点法是无法找到OEP地址的,比如今天我们用的这个程序,下图为它的区段信息:
上图可知我们根本无法知晓哪个区段存放数据,哪个区段存放代码。这种情况下用两次断点法难免会失效。因此我们应该采用堆栈平衡法来寻找OEP地址。这里步骤就省去了,毕竟今天的主题是构造输入表,有兴趣了解堆栈平衡法的可以去我的博客按标题查看。下图为找的OEP地址:
既然找到了OEP地址,就直接使用OD来脱壳了。点击插件的ollydump,把重建输入表去掉,毕竟这个是我们接下来的工作。如下图:
0x02 手动重建输入表
思路:找到被调用的api函数——》将DLL函数和api函数写入脱壳后的文件的空白区域——》空白区域重写IMAGE_THUNK_DATA数组——》空白区域重写IID数组——》将IID数组地址写入数据目录表第二项。
重建之前我们先来看看脱壳后的程序的输入表,如下图:
明显看出这个输入表已经紊乱了。接下来我们一步步重建它。
1)我们先用没有加壳的原程序来看看他调用了哪些函数(一定要用没有加壳的,加了壳的调用的函数会很不同)。见下图:
上图可知共调用了四个个函数KERNEL32.DLL调用了ExitProcess和CreateFileA而USER32.DLL调用了MessageBoxA。
2)将输入DLL以及被调用的函数写入脱壳后程序的空白区域
此表是DLL已经函数写入的地址(这里的地址都是高低位互换的便易在hexworkshop中读写)
DLL名及DLL地址 |
函数名及函数地址 |
||||
KERNEL32.DLL |
0016 0000 |
ExitProcess |
1a16 0000 |
CreateFileA |
2816 0000 |
USER32.DLL |
0d16 0000 |
MessageBoxA |
3616 0000 |
|
|
(PS:DLL之间应该有一个字符空隙,DLL与API函数,API函数与API函数之间应该有三个字符间隙,因为前面API函数前面个两个字节是预留给IMAGE_IMPORT_BY_NAME的Hint字段多余的一个作为间隙)
3)在空白区域构造IMAGE_THUNK_DATA数组。
构造IMAGE_THUNK_DATA数组起始位置16b0
第一个IID数组的IMAGE_THUNK_DATA数组 |
1a16 0000-2=1816(16b0) |
28160000-2=2616 (16b4) |
第二个IID数组的IAMGE_THUNK_DATA数组 |
3616 0000-2=3416(16BC) |
构造完成之后如下图:
IID数组之间预留四个字节的间隙
4)构造IID数组
另找一块空白区域,我们这里选用1760处。
构造IID数组位置:1760
DLL名称 |
OriginalFristThunk |
TimaStamp |
ForwordChian |
Name |
FristThunk |
KERNEL32.DLL |
B016 0000 |
0000 0000 |
0000 0000 |
0016 0000 |
B016 0000 |
USER32.DLL |
Bc16 0000 |
0000 0000 |
0000 0000 |
0d16 0000 |
Bc16 0000 |
结束标志 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
构造后如下图:
5)将数据目录表的第二项改成IID的地址1760,后面四个字节写入IID数组大小28,如下图:
至此手动构造完毕,保存文件。
0x03 检验构造的输入表是否起作用。
我们用LordPE打开保存后的文件,点击目录信息,再点击输入表即可查看信息,如下图:
很明显,我们手动构造的输入表已经起作用了。
0x04 总结
虽然手动构造输入表成功了,但是我发现脱壳后的程序并不能正常运行,这两天一直在思考其中的缘由,如果有幸哪位大佬看到这篇文章请留言指导一二。