前面两篇讲到了输出表的内容以及涉及如何在hexWorkShop中找到输出表及输入DLL,感觉有几个地方还是没有理解好,比如由数据目录表DataDirectory[16]找到输出表表后以为找到输入DLL就完了,其实这一流程的最终功能是通过输入DLL找到输入DLL调用的函数,这一步骤是通过输出表结构中的OriginalFristThunk或者OriginalFristThunk所指向的INT或者IAT结构来找到的。这里要说明的是,虽然一般情况通过OriginalFristThunk也行,但是有些情况下它的值被设置为0了,这样就无法利用了,最委托的的方式是通过FristThunk所指向的IAT表来找寻。下面来通过实例实践这一过程。
0x01 找寻输入DLL以及输入DLL调用的函数
材料及工具:名为PE.exe的可执行文件,工具hexWrokshop,LordPE
思路:找到PE文件头——》找到数据目录表第二项——》通过地址转换找到输出表数组——》逐个读出输出表数组的OriginalFristThunk,FristThunk值——》通过INT或者IAT逐个读出被调用函数的名字地址——》通过名字地址找到函数名。
1)将目标文件拖入hexWrokShop,快捷键ctrl+g跳往载入地址的3ch处,这里即PE文件头地址,如下图:
2)跳往40h处,该处即为文件头,如下图:
3)跳往PE文件头+80h处,该处存储了输入数据表的地址如下图:
4)将RVA=2040h转化位FileOffset地址,这里我们利用lordpe协助转化,转化后值为440h。
5)跳转至440h处,该处即为输出表IID数组的数据所在,每项为五对双字组成,结尾以五对双字0。我这个实例一共两组,如下图:
将以上根据字段数据统计如下表(PS:由于hex中是由低位到高位的故统计时应该注意高低位的换位):
OriginalFristThunk |
TimeStamp |
ForwardChain |
Name |
FristThunk |
0000208C |
00000000 |
00000000 |
00002174 |
00002010 |
0000207C |
00000000 |
00000000 |
000021B4 |
00002000 |
利用表中的Name字段我们可以直接推出输入DLL的名字,第一项Name的RVA为2174h,转化为FileOffset值为:574h,跳往574h,我们可以看到第一个DLL为USER32.DLL,如下图:
第二项的Name值为RVA值为21B4h,转化位FileOffset值为5B4h,跳往574h,我们知道第二项DLL为KERNEL32.DLL,如下图:
6)知道了输入DLL不是我们目的终点,我们还要知道DLL所调用的所有函数名字地址,这里有两个字段可用,第一个是OriginalFristThunk,它所指向的是一个名为输入名称表(INT)的结构,这个结构是由多个IMAGE_THUNK_DATA结构所组成的数组。第二个是FristThunk,它所指向的是一个名称为输入地址表的(IAT)的结构,这个结构也是有多个IMAGE_THUNK_DATA结构所组成的数组。IMAGE_THUNK_DATA双字数组的每项指向另一个结构——IMAGE_IMPORT_BY_NAME。最终通过IMAGE_IMPORT_BY_NAME找到被DLL调用的函数。一般而言,这两个被指向的数组值是相等的。我们接下来分别用两个字段来查找。我们先用第一项的OriginalFristThunk来试试,将208C转化为FileOffset得48ch。掉跳往48ch,的如下图结果,共十一项,以双字0结尾。
我们用FristThunk来试试,将2010h转化为FileOffset得410h,跳往410h,可得下图:
它们的值都为:
102100001C210000F4200000E0200000502100006421000002210000CE200000BC2000002E21000042210000,将数据按八个字节拆分与翻转的下表:
第一项指向的IAMGE_THUNK_DATA数组
00002110 |
0000211C |
000020F4 |
000020E0 |
00002150 |
00002164 |
00002102 |
000020CE |
000020BC |
0000212E |
00002142 |
|
|
|
|
|
接下来进行地址转换,的下表:
510 |
51c |
4f4 |
4e0 |
550 |
564 |
502 |
4ce |
4bc |
52e |
542 |
|
7)由上表逐个查询出被调用函数名,如下图:
重复以上操作的下表:
RVA |
FileOffset |
Hint |
函数名 |
00002110 |
510 |
019B |
LoadIconA |
0000211C |
51c |
01DD |
PostQuitMessage |
000020F4 |
4f4 |
0128 |
GetMessageA |
000020E0 |
4e0 |
0094 |
DispatchMessageA |
00002150 |
550 |
027D |
TranslateMessage |
00002164 |
564 |
028B |
UpdateWindow |
00002102 |
502 |
0197 |
LoadCursorA |
000020CE |
4ce |
0083 |
DefWindowProcA |
000020BC |
4bc |
0058 |
CreateWindowExA |
0000212E |
52e |
01EF |
RegisterClassExA |
00002142 |
542 |
0265 |
ShowWindow |
|
|
|
|
8)当然你可能会觉得这样找输入表实在太麻烦了,确实是,但是只有经过这样找你才能弄明白输出表到底是如何存放的转换的,从而对PE文件格式有更为透彻理解。接下来我们通过强大的lordPE来轻松查找到输入表和输入函数。如下图: