寫在前面的話:今年疫情在家,纔開始在CSDN與各位大佬們分享交流技術,最近一個月返校之後,一直忙碌於學校課設和期末考試的備考(畢竟平時不怎麼聽課),所以博客幾乎一個多月沒有更新,最近只剩下一門考試了,今天小庫又回來了。
之前有關PE文件的知識好像更新到了導出表,今日來看看導入表。
我們提到導入表,最常說的就是導入表的雙橋結構
先來看一下導入表描述符結構體IMAGE_IMPORT_DESCRIPTOR
typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
union
{
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
老規矩,其中各字段的簡單介紹。。
@Characteristics:屬性
@OriginalFirstThunk:指向的是一個數組,數組中每個結構定義了一個導入函數的信息,最後以一個內容爲全0的結構最爲結束。指向的數組中每一項爲一個IMAGE_THUNK_DATA結構,這是一個結構體,32位下大小是雙字(DWORD,64位下爲ULONGLONG),最高位0時,表示一個數值RVA,爲1時,表示導入符號是一個名稱。這也是經常說的橋1。
@TimeDateStamp:時間戳,一般爲0。如果該導入表項被綁定,那麼綁定後的這個時間戳就被設置對應DLL文件的時間戳。
@ForwarderChain:鏈表的前一個結構,如果沒有,則爲-1.
@Name:指向鏈接庫名字的指針’\0’結尾的Ansi字符串。
@FirstThunk:我們所說的橋2,也是一個IMAGE_THUNK_DATA結構,指向當前導入動態庫引入的所有導入函數。
導入表中最重要的結構莫不是雙橋結構,橋1和橋2最終指向了同一個地方,是一個PIMAGE_IMPORT_BY_NAME結構體,但是這是在橋2沒有發生斷裂的前提下。
橋1指向的是INT(Import Name Table)表,其中的每一個成員都是一個RVA,指向PIMAGE_IMPORT_BY_NAME,這個結構體中兩個成員分別表示函數編號和函數名稱的字符串。直到遇到內容全0,INT表遍歷結束。
橋2指向的是IAT(Import Address Table)表,在PE文件裝入內存之前,它裏面的成員和INT中指向的是一樣的,直到PE文件被加載進虛擬內存空間之後,會發生斷裂,其中指向的是函數的VA。
有關橋1橋2,在恢復導入表鉤子的代碼中有使用舉例。
值得注意的一點是,單橋結構無法執行綁定導入操作,因爲當PE文件加載進內存,如果不存在橋1,則無法重新找到VA到底調用了哪個函數,自然無法實施綁定。
下面給一個我自己寫的枚舉導入表信息的關鍵代碼:
BOOL EnumImportTableAsDataFileInternal32(PVOID ModuleBase, list<IMPORT_DATA>& DataList,list<IMPORT>& DataList2)
{
DWORD Size = 0;
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;
PIMAGE_THUNK_DATA32 pImportProcNameList = NULL;
PIMAGE_THUNK_DATA32 pImportProcAddressList = NULL;
PIMAGE_IMPORT_BY_NAME pINTItem = NULL;
DWORD FOA = 0;
BOOL IsBind = FALSE;
int Index = 0;
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)PEGetMemberInModule((HMODULE)ModuleBase, &Size, IMAGE_DIRECTORY_ENTRY_IMPORT, FALSE, TRUE);
// 時間戳
if (ImportDescriptor->TimeDateStamp == 0)
{
IsBind = FALSE;
}
else
{
IsBind = TRUE;
}
while (TRUE)
{
IMPORT Data2 = { 0 };
Index = 0;
// 空項結尾
if (ImportDescriptor->Characteristics == 0)
{
break;
}
GetFOA32((PBYTE)ModuleBase, ImportDescriptor->FirstThunk, &FOA);
// IAT
pImportProcAddressList = (PIMAGE_THUNK_DATA32)((PBYTE)ModuleBase + FOA);
Data2.ImportProcAddress = (DWORD)pImportProcAddressList;
Data2.FOAImportProcAddress = FOA;
GetFOA32((PBYTE)ModuleBase, ImportDescriptor->OriginalFirstThunk, &FOA);
// INT
pImportProcNameList = (PIMAGE_THUNK_DATA32)((PBYTE)ModuleBase + FOA);
Data2.ImportProcName = (DWORD)pImportProcNameList;
Data2.FOAImportProcName = FOA;
while (TRUE)
{
// 遍歷到盡頭了
if (pImportProcNameList[Index].u1.Function == NULL)
{
break;
}
else
{
if (pImportProcNameList[Index].u1.Function & IMAGE_ORDINAL_FLAG)
{
// 索引導入
}
else
{
GetFOA32((PBYTE)ModuleBase, pImportProcNameList[Index].u1.AddressOfData, &FOA);
pINTItem = (PIMAGE_IMPORT_BY_NAME)(FOA + (PBYTE)ModuleBase);
}
IMPORT_DATA Data = { 0 };
// 序號,便於修正地址時查詢
WORD Hint = pINTItem->Hint;
Data.Ordinals = Hint;
// 名稱
PCHAR Name = (PCHAR)pINTItem->Name;
Data.FunctionName = Name;
// Dll名稱
GetFOA32((PBYTE)ModuleBase, ImportDescriptor->Name, &FOA);
PCHAR DllName = (PCHAR)(FOA + (PBYTE)ModuleBase);
Data.DllName = DllName;
Data2.DllName = DllName;
if (IsBind)
{
// 指向函數地址
GetFOA32((PBYTE)ModuleBase, pImportProcAddressList[Index].u1.Function, &FOA);
}
DataList.push_back(Data);
Index++;
}
}
Data2.NumberOfFunction = Index;
Data2.TimeDateStamp = ImportDescriptor->TimeDateStamp;
BOOL IsOk = FALSE;
for (list<IMPORT>::iterator i = DataList2.begin();i != DataList2.end();i++)
{
if (Data2.DllName == i->DllName)
{
IsOk = TRUE;
}
}
if (IsOk == FALSE)
{
DataList2.push_back(Data2);
}
ImportDescriptor++;
}
return TRUE;
}
BOOL EnumImportTableAsDataFileInternal64(PVOID ModuleBase, list<IMPORT_DATA>& DataList, list<IMPORT>& DataList2)
{
DWORD Size = 0;
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;
PIMAGE_THUNK_DATA64 pImportProcNameList = NULL;
PIMAGE_THUNK_DATA64 pImportProcAddressList = NULL;
PIMAGE_IMPORT_BY_NAME pINTItem = NULL;
IMPORT_DATA Data = { 0 };
DWORD FOA = 0;
BOOL IsBind = FALSE;
int Index = 0;
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)PEGetMemberInModule((HMODULE)ModuleBase, &Size, IMAGE_DIRECTORY_ENTRY_IMPORT, TRUE, TRUE);
// 時間戳
if (ImportDescriptor->TimeDateStamp == 0)
{
IsBind = FALSE;
}
else
{
IsBind = TRUE;
}
while (TRUE)
{
IMPORT Data2 = { 0 };
Index = 0;
// 空項結尾
if (ImportDescriptor->Characteristics == 0)
{
break;
}
GetFOA64((PBYTE)ModuleBase, ImportDescriptor->FirstThunk, &FOA);
// IAT
pImportProcAddressList = (PIMAGE_THUNK_DATA64)((PBYTE)ModuleBase + FOA);
Data2.ImportProcAddress = (DWORD)pImportProcAddressList;
Data2.FOAImportProcAddress = FOA;
GetFOA64((PBYTE)ModuleBase, ImportDescriptor->OriginalFirstThunk, &FOA);
// INT
pImportProcNameList = (PIMAGE_THUNK_DATA64)((PBYTE)ModuleBase + FOA);
Data2.ImportProcName = (DWORD)pImportProcNameList;
Data2.FOAImportProcName = FOA;
while (TRUE)
{
// 遍歷到盡頭了
if (pImportProcNameList[Index].u1.Function == NULL)
{
break;
}
else
{
if (pImportProcNameList[Index].u1.Function & IMAGE_ORDINAL_FLAG)
{
// 索引導入
}
else
{
GetFOA64((PBYTE)ModuleBase, pImportProcNameList[Index].u1.AddressOfData, &FOA);
pINTItem = (PIMAGE_IMPORT_BY_NAME)(FOA + (PBYTE)ModuleBase);
}
// 序號,便於修正地址時查詢
WORD Hint = pINTItem->Hint;
Data.Ordinals = Hint;
// 名稱
PCHAR Name = (PCHAR)pINTItem->Name;
Data.FunctionName = Name;
// Dll名稱
GetFOA64((PBYTE)ModuleBase, ImportDescriptor->Name, &FOA);
PCHAR DllName = (PCHAR)(FOA + (PBYTE)ModuleBase);
Data.DllName = DllName;
Data2.DllName = DllName;
if (IsBind)
{
// 指向函數地址
GetFOA64((PBYTE)ModuleBase, pImportProcAddressList[Index].u1.Function, &FOA);
}
DataList.push_back(Data);
Index++;
}
}
Data2.NumberOfFunction = Index;
Data2.TimeDateStamp = ImportDescriptor->TimeDateStamp;
BOOL IsOk = FALSE;
for (list<IMPORT>::iterator i = DataList2.begin();i != DataList2.end();i++)
{
if (Data2.DllName == i->DllName)
{
IsOk = TRUE;
}
}
if (IsOk == FALSE)
{
DataList2.push_back(Data2);
}
ImportDescriptor++;
}
return TRUE;
}
“衆裏尋他千百度,驀然回首,那人卻在,燈火闌珊處。”我回來啦。。。
參考書籍:《Windows PE權威指南》