导入表
导入表是什么?
记录一个可执行文件所用到的其他模块的导出的函数
记录信息:dll列表及其所用用到的函数
在PE中的结构
struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // RVA 指向IMAGE_THUNK_DATA结构数组
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; //RVA 指向dll的名字,名字以0结尾
DWORD FirstThunk; //RVA 指向IMAGE_THUNK_DATA结构数组
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal; //序号
PIMAGE_IMPORT_BY_NAME AddressOfData; //指向IMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //可能为空,编译器决定,如果不为空,是函数在导出表中的索引
BYTE Name[1];//函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
这3个结构的关系如下图
如图可知,PE加载前和加载后是有些不同的——IAT表中的地址变为模块的RVA或绝对地址
IMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk 未绑定导入表的RVA
TimeDateStamp
0:未绑定
1:已绑定
FirstThunk IAT的RVA
绑定:绝对地址
未绑定:RVA
导入表有什么作用?
通过导入表中提供的dll所依赖的函数清单去定位函数地址。
打印导入表的步骤
1、定位导入表
目录项的第二个:
2、RVA转FOA
3、输出导入表的描述信息:以0结束
4、输出dll名字:Name字段以0结尾的字符串为dll的名字
5、遍历OriginalFirstThunk
根据最高位的值判断是名称还是序号:为1==》除去最高位的值就是函数的导出序号(&80000)
为0==》指向函数名的地址
IMAGE_IMPORT_BY_NAME : HIT 2字节
NAME 长度不确定 以‘ ’ 结尾
6、遍历FirstThunk: 同上
#include <stdio.h>
#include <windows.h>
#include <string.h>
#define SRC1 "ipmsg.exe"
#define SRC "notepad.exe"
DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);
DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA);
LPVOID ReadPEFile(LPVOID pFileBuf);
DWORD FileSize(LPVOID pFileBuf);
//移动重定位表
void ImportTbInfo(){
//定义pe头结构指针
PIMAGE_DOS_HEADER pDosHeader; //dos头指针
PIMAGE_NT_HEADERS pNtHeaders;
PIMAGE_IMPORT_DESCRIPTOR importDesc; //导入表描述
PIMAGE_IMPORT_BY_NAME pImportName; //函数地址表
PIMAGE_THUNK_DATA pThunkData; //数据表
//1.将文件读入内存
LPVOID pFileBuf = fopen(SRC,"rb");
DWORD fileSize = FileSize(pFileBuf);
LPVOID pFileBuffer = ReadPEFile(pFileBuf);
if(!pFileBuffer){
printf("读取dll文件失败
");
return;
}
//3.初始化头结构指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory;
WORD numOfSec = pNtHeaders->FileHeader.NumberOfSections;
importDesc = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)pFileBuffer + RvaToFoa(pNtHeaders, dataDir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
//4遍历模块表
while(importDesc->Name)
{
pThunkData = (PIMAGE_THUNK_DATA)(RvaToFoa(pNtHeaders,importDesc->OriginalFirstThunk)+(DWORD)pFileBuffer);
char* pName = (char*)(RvaToFoa(pNtHeaders,importDesc->Name)+(DWORD)pFileBuffer);
printf("模块名称(Name):%s
", RvaToFoa(pNtHeaders,importDesc->Name)+(DWORD)pFileBuffer ); //这个打印出来的字符串应该如何输出
while(pThunkData->u1.AddressOfData)
{
if(IMAGE_SNAP_BY_ORDINAL32((DWORD)pThunkData->u1.AddressOfData)) //如果是序号导入 最高位为1
printf("导入函数序号:%04x
", (pThunkData->u1.Ordinal) & 0xFFFF); //去掉最高位的1,后面的就是导入序号
else
{
pImportName = (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(pNtHeaders,(DWORD)pThunkData->u1.AddressOfData)+(DWORD)pFileBuffer);
printf("导入类型:%04x, 函数名:%s
", pImportName->Hint,pImportName->Name); //去掉最高位的1,后面的就是导入序号
}
pThunkData++;
}
importDesc++;
}
//4.1 遍历模块中的函数
return;
}
int main(int argc, char* argv[])
{
ImportTbInfo();
getchar();
return 0;
}
DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)
{
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
{
if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
{
return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
}
}
return 0;
}
DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA)
{
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
{
if(dwFOA >= pSection[i].PointerToRawData && dwFOA < (pSection[i].PointerToRawData + pSection[i].SizeOfRawData))
{
return pSection[i].VirtualAddress + (dwFOA - pSection[i].PointerToRawData);
}
}
return 0;
}
//将PE文件读到FileBuffer
DWORD FileSize(LPVOID pFileBuf)
{
//计算文件大小
fseek((FILE* )pFileBuf,0,SEEK_END);
long fileSize = ftell((FILE* )pFileBuf);
fseek((FILE* )pFileBuf,0,SEEK_SET);
return fileSize;
}
LPVOID ReadPEFile(LPVOID pFileBuf)
{
//打开文件
//Stream = fopen(fileName,"rb");
//计算文件大小
DWORD fileSize = FileSize(pFileBuf);
//分配堆空间
LPVOID pFileBuffer = malloc(sizeof(char)*fileSize);
//将文件拷到堆
fread(pFileBuffer,sizeof(char),fileSize,(FILE*)pFileBuf);
fclose((FILE*)pFileBuf);
return pFileBuffer;
}
绑定导入表
what?
1、起始:INT=IAT,都是程序引用的dll中的函数名或序号
2、加载完后:IAT表中替换成函数真正的地址
这个真正的地址,有两种情况
2.1 程序在加载的过程中,IAT表中的地址替换成函数的RVA。
2.2 程序在加载前,IAT表中的地址为绝对地址,如此一来,省去了修复IAT表的时间。但是有两种情况比较糟糕:
2.2.1 dll基址冲突,需要重定位
2.2.2 dll被修改,IAT表中对应的函数地址可能会被修改
why?
提高加载的效率
when: 时间戳: 0 没绑定 -1 当前dll已绑定,并且已经存在另外一张表中
how?
数据目录项的第二项
offsetmoudleName 第一个Desc的值+offsetmoudleName 才是名字的偏移
1、根据导入表结构中的 :TimeDataStamp来判断是否绑定导入
1.1 TimeDataStamp = 1;未绑定导入
1.2 TimeDataStamp = 0xFFFF FFFF ; 绑定导入
3、目录项中的位置
define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
4、绑定导入表的结构
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp; //时间戳
WORD OffsetModuleName; //dll的名字 :
WORD NumberOfModuleForwarderRefs;
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
OffsetModuleName 计算的方法: 第一个 *(_IMAGE_BOUND_IMPORT_DESCRIPTOR )+OffsetModuleName
5、绑定导入表结束标记
最后一个全0的结构说明已经没有绑定导入表
输出绑定导入表代码
#include <stdio.h>
#include <windows.h>
#include <string.h>
#define SRC1 "ipmsg.exe"
#define SRC "notepad.exe"
DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);
DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA);
LPVOID ReadPEFile(LPVOID pFileBuf);
DWORD FileSize(LPVOID pFileBuf);
//移动重定位表
void PrintBoundImport(){
//定义pe头结构指针
PIMAGE_DOS_HEADER pDosHeader; //dos头指针
PIMAGE_NT_HEADERS pNtHeaders;
PIMAGE_BOUND_IMPORT_DESCRIPTOR boundDir; //数据表
PIMAGE_BOUND_FORWARDER_REF boundDef;
//1.将文件读入内存
LPVOID pFileBuf = fopen(SRC,"rb");
DWORD fileSize = FileSize(pFileBuf);
LPVOID pFileBuffer = ReadPEFile(pFileBuf);
if(!pFileBuffer){
printf("读取dll文件失败
");
return;
}
//3.初始化头结构指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory;
DWORD boundDirRVA = dataDir[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;
boundDir = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + boundDirRVA);
PIMAGE_BOUND_IMPORT_DESCRIPTOR currentBound = boundDir;
//4遍历模块表
//3.输出绑定导入表信息
while(currentBound -> TimeDateStamp){
getchar();
LPSTR moduleName = (LPSTR) ((DWORD)(currentBound->OffsetModuleName) + (DWORD)boundDir); //boundDir 第一个descriptor的值+OffsetModuleName
printf("OffsetModuleName:%s
", moduleName);
printf("TimeDateStamp:%d
",currentBound->TimeDateStamp );
int i = currentBound->NumberOfModuleForwarderRefs;
printf("依赖dll个数:%d
", i);
if(i>0){
boundDef = (PIMAGE_BOUND_FORWARDER_REF) ((DWORD) currentBound + 8); // 8 = DWORD + DWORD len(PIMAGE_IMPORT_DESCRIPTOR)+len(PIMAGE_BOUND_IMPORT_DESCRIPTOR)
for(int j=0;j<i;j++){
LPSTR refName = LPSTR((DWORD)((boundDef + j)->OffsetModuleName) + (DWORD)boundDir); //计算ref的名称,原理同上
DWORD refTime = (boundDef + j) -> TimeDateStamp;
printf("依赖dll:%s,时间戳:%d
",refName,refTime);
}
currentBound = currentBound + (i+1);
}else{
currentBound++;
}
}
return;
}
int main(int argc, char* argv[])
{
PrintBoundImport();
getchar();
return 0;
}
DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)
{
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
{
if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
{
return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
}
}
return 0;
}
DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA)
{
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
{
if(dwFOA >= pSection[i].PointerToRawData && dwFOA < (pSection[i].PointerToRawData + pSection[i].SizeOfRawData))
{
return pSection[i].VirtualAddress + (dwFOA - pSection[i].PointerToRawData);
}
}
return 0;
}
//将PE文件读到FileBuffer
DWORD FileSize(LPVOID pFileBuf)
{
//计算文件大小
fseek((FILE* )pFileBuf,0,SEEK_END);
long fileSize = ftell((FILE* )pFileBuf);
fseek((FILE* )pFileBuf,0,SEEK_SET);
return fileSize;
}
LPVOID ReadPEFile(LPVOID pFileBuf)
{
//打开文件
//Stream = fopen(fileName,"rb");
//计算文件大小
DWORD fileSize = FileSize(pFileBuf);
//分配堆空间
LPVOID pFileBuffer = malloc(sizeof(char)*fileSize);
//将文件拷到堆
fread(pFileBuffer,sizeof(char),fileSize,(FILE*)pFileBuf);
fclose((FILE*)pFileBuf);
return pFileBuffer;
}