假的分析
源码
在学习这节的时候第一遍并没有弄懂到底怎么回事, 等遍历完了再回头去看才懂, 推荐可以先把表打印出来再体会表结构.
真的分析
软件依然是某云音乐的主程序.
按照顺序依次为
1 遍历 _IMAGE_IMPORT_DESCRIPTOR
2 遍历OriginalFirstThunk中的ThunkValue偏移
3 遍历打印ThunkValue(本身还是偏移)
4 遍历IMAGE_THUNK_DATA32(ThunkValue所指向的值)
1 打印重要基础数据
while (pImportDescriptor->OriginalFirstThunk)
{
DWORD rvaFirstThunk = pImportDescriptor->FirstThunk;
DWORD rvaOriginalFirstThunk = pImportDescriptor->OriginalFirstThunk;
printf("RVA_OriginalFirstTunk: %2x RVA_Name: %2x RVA_FirstThunk: %2x\n", pImportDescriptor->OriginalFirstThunk, pImportDescriptor->Name, pImportDescriptor->FirstThunk);
DWORD foaName = RvaDataToFoaData(pFileBuffer, pImportDescriptor->Name, FALSE);
printf("DLL's name: %s\n", (char *)((DWORD)pFileBuffer + foaName));
printf("-------------------------------------------------------------------------\n");
pImportDescriptor++;
printf("-------------------------------------------------------------------------\n");
}
2 3打印OriginalFirstThunk中的ThunkValue偏移及其本身(ThunkValue还是个偏移)
DWORD foaOriginalFirstThunk = RvaDataToFoaData(pFileBuffer, rvaOriginalFirstThunk, FALSE);
DWORD OriginalThunkValue = *(DWORD *)((DWORD)pFileBuffer + foaOriginalFirstThunk);
printf("Show the OriginalFirstThunk:\n");
while (OriginalThunkValue)
{
printf("RVA_OriginalFirstThunk: %2x FOA_OriginalFirstThunk: %2x", pImportDescriptor->OriginalFirstThunk, foaOriginalFirstThunk);
printf(" OriginalThunkValue: %2x\n", OriginalThunkValue);
}
puts("");
4 打印IMAGE_THUNK_DATA32(ThunkValue所指向的值)
如果最高位为1 那么去掉1之后的就是函数序号
否则该值指向_IMAGE_IMPORT_BY_NAME
struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
结构: 双字节的Hint 以及name的起始地址
if ((OriginalThunkValue & 0x80000000) == 0x1)
{
DWORD ordinalOfFunction = (OriginalThunkValue & 0x7FFFFFFF);
TODO;
}
else
{
DWORD RVA_OFT_HintAndNameOfFunction = OriginalThunkValue;
DWORD FOA_OFT_HintAndNameOfFunction = RvaDataToFoaData(pFileBuffer, RVA_OFT_HintAndNameOfFunction, FALSE);
WORD hint = *(WORD *)((DWORD)pFileBuffer + FOA_OFT_HintAndNameOfFunction);
char * NameOfFunction = (char *)((DWORD)pFileBuffer + FOA_OFT_HintAndNameOfFunction + 2);
printf("RVA_OriginalThunkNameOfFunction: %2x FOA_NameOfFunction: %2x\n", RVA_OFT_HintAndNameOfFunction, FOA_OFT_HintAndNameOfFunction);
printf("OriginalThunkHint: %2x\nNameOfFunction: %2s\n\n", hint, NameOfFunction);
}
5 重复2 3 4打印FirstThunk相关
整体代码
while (pImportDescriptor->OriginalFirstThunk)
{
DWORD rvaFirstThunk= pImportDescriptor->FirstThunk;
DWORD rvaOriginalFirstThunk = pImportDescriptor->OriginalFirstThunk;
printf("RVA_OriginalFirstTunk: %2x RVA_Name: %2x RVA_FirstThunk: %2x\n", pImportDescriptor->OriginalFirstThunk, pImportDescriptor->Name, pImportDescriptor->FirstThunk);
DWORD foaName = RvaDataToFoaData(pFileBuffer, pImportDescriptor->Name, FALSE);
printf("DLL's name: %s\n", (char *)((DWORD)pFileBuffer + foaName));
printf("-------------------------------------------------------------------------\n");
DWORD foaOriginalFirstThunk = RvaDataToFoaData(pFileBuffer, rvaOriginalFirstThunk, FALSE);
DWORD OriginalThunkValue = *(DWORD *)((DWORD)pFileBuffer + foaOriginalFirstThunk);
printf("Show the OriginalFirstThunk:\n");
while (OriginalThunkValue)
{
printf("RVA_OriginalFirstThunk: %2x FOA_OriginalFirstThunk: %2x", pImportDescriptor->OriginalFirstThunk, foaOriginalFirstThunk);
printf(" OriginalThunkValue: %2x\n", OriginalThunkValue);
if ((OriginalThunkValue & 0x80000000) == 0x1)
{
DWORD ordinalOfFunction = (OriginalThunkValue & 0x7FFFFFFF);
TODO;
}
else
{
DWORD RVA_OFT_HintAndNameOfFunction = OriginalThunkValue;
DWORD FOA_OFT_HintAndNameOfFunction = RvaDataToFoaData(pFileBuffer, RVA_OFT_HintAndNameOfFunction, FALSE);
WORD hint = *(WORD *)((DWORD)pFileBuffer + FOA_OFT_HintAndNameOfFunction);
char * NameOfFunction = (char *)((DWORD)pFileBuffer + FOA_OFT_HintAndNameOfFunction + 2);
printf("RVA_OriginalThunkNameOfFunction: %2x FOA_NameOfFunction: %2x\n", RVA_OFT_HintAndNameOfFunction, FOA_OFT_HintAndNameOfFunction);
printf("OriginalThunkHint: %2x\nNameOfFunction: %2s\n\n", hint, NameOfFunction);
}
pImportDescriptor->OriginalFirstThunk += 4;
foaOriginalFirstThunk = RvaDataToFoaData(pFileBuffer, pImportDescriptor->OriginalFirstThunk, FALSE);
OriginalThunkValue = *(DWORD *)((DWORD)pFileBuffer + foaOriginalFirstThunk);
}
puts("");
DWORD foaFirstThunk = RvaDataToFoaData(pFileBuffer, rvaFirstThunk, FALSE);
DWORD FirstThunkValue = *(DWORD *)((DWORD)pFileBuffer + foaFirstThunk);
printf("Show the FirstThunk: \n");
while (FirstThunkValue)
{
printf("RVA_FirstThunk: %2x FOA_FirstThunk: %2x", rvaFirstThunk, foaFirstThunk);
printf(" FirstThunkValue: %2x\n", FirstThunkValue);
if ((FirstThunkValue & 0x80000000) == 0x1)
{
DWORD ordinalOfFunction = (FirstThunkValue & 0x7FFFFFFF);
TODO;
}
else
{
DWORD RVA_FT_HintAndNameOfFunction = FirstThunkValue;
DWORD FOA_FT_HintAndNameOfFunction = RvaDataToFoaData(pFileBuffer, RVA_FT_HintAndNameOfFunction, FALSE);
WORD hint = *(WORD *)((DWORD)pFileBuffer + FOA_FT_HintAndNameOfFunction);
char * NameOfFunction = (char *)((DWORD)pFileBuffer + FOA_FT_HintAndNameOfFunction + 2);
printf("RVA_FirstThunkNameOfFunction: %2x FOA_FirstThunkNameOfFunction: %2x\n", RVA_FT_HintAndNameOfFunction, FOA_FT_HintAndNameOfFunction);
printf("FirstThunkHint: %2x\nFirstThunkNameOfFunction: %2s\n\n", hint, NameOfFunction);
}
pImportDescriptor->FirstThunk += 4;
foaFirstThunk = RvaDataToFoaData(pFileBuffer, pImportDescriptor->FirstThunk, FALSE);
FirstThunkValue = *(DWORD *)((DWORD)pFileBuffer + foaFirstThunk);
}
puts("");
pImportDescriptor++;
printf("-------------------------------------------------------------------------\n");
}
最终结果
真的分析
ps: 为了方便分析程序用的视频同款Messagebox函数…
导入表中最重要的三个数据OriginalFirstThunk Name FirstThunk, 名字没什么好说的.
OFT FT 分为PE加载前和加载后, 分别存放Immpor Name Table与 Import Address Table
但是里面在PE加载前即二进制文件是一模一样的 真正产生区别的在于PE加载后.
OD中看到 [40203C]直接指向了函数地址
这里发生改变的原因是加载PE文件后系统会遍历INT并调用GetProcAddress得到函数地址复制到IAT中.
一些问题
为什么要有两个表?
第一个原因是如果直接在IAT中写入地址没有INT那么想要获取函数名字就有困难, 相当于给api备份.
我自己想的是每个电脑的地址不一样 每次动态加载需要依靠函数名字来获取. 保证在不同的机器上运行
老师讲学了下个表才能讲第二个原因, 学完回来更新.
学完本节, 最大的感悟就是预习, OFT和FP困扰我挺久, 其实先把表遍历出来的就是在预习, 当自己能先把表遍历完, 再去理解内容就变得很清晰.
ps: 建了个学习讨论群, 欢迎新朋友加入:1094301686