1.修改導入表,即添加一個新的導入表描述符及其iat,int. 這樣系統加載該pe文件時將自動加載添加的dll,從而實現dll注入
2.思路:
可以手工修改,但是復雜程度一點也不比寫代碼低,而且低效. 通過代碼實現可以一勞永逸.
新增一個節來存儲新的導入表. 其實也可以在原來的pe文件找空隙插入進去,但是又很難實現通用,因為不同的pe文件空隙位置大小不同.容易出錯
新增的導入表描述符通過序號導入,這樣更簡單一些
基本步驟: 修改節區數量,在原來的節區頭中添加一個節,如果空間不夠就不行了,但絕大多數pe文件的節區空間都是足夠的 ->
填好新節頭的字段->申請節內存->復制原來的導入表到新節中->增加新的導入表描述符並填寫相應字段->增加該導入表描述符的iat和int->將新增數據
寫入到文件尾部-> 修改pe文件中重要字段
以上步驟並不是嚴格順序的,因為涉及到rva, 文件偏移,各種大小的計算.
3.代碼:
//導入表注入
DWORD rva2offset(LPVOID base, DWORD rva)
{
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)base;
IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(dosHeader->e_lfanew + (DWORD)base);
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)ntHeader + sizeof(IMAGE_NT_HEADERS32));
if (rva<ntHeader->OptionalHeader.SizeOfHeaders)
{
return rva;
}
for (DWORD i = 1; i <= ntHeader->FileHeader.NumberOfSections; i++)
{
//如果到了最后一個節時,就可以直接計算了,否則可以通過前后節頭的VirtualAddress確定在哪個節中
if (i == ntHeader->FileHeader.NumberOfSections)
{
return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData;
}
else if (rva >= sectionHeader->VirtualAddress && rva < (sectionHeader + 1)->VirtualAddress)
{
return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData;
}
sectionHeader++;
}
return 0;
}
//文件或者內存對齊
DWORD PEAlign(DWORD size, DWORD dwAlignTo)
{
return(((size + dwAlignTo - 1) / dwAlignTo)*dwAlignTo);
}
DWORD importTableInject(WCHAR* modulepath,char* dllpath)//dllpath傳入dll的名字,而不是路徑
{
//先備份源文件
WCHAR newFile[MAX_PATH];
wsprintf(newFile, L"%s.bak", modulepath);
CopyFileW(modulepath, newFile, 0);
HANDLE hFile = CreateFileW(modulepath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile==0||hFile==INVALID_HANDLE_VALUE)
{
return 0;
}
DWORD fileSize = GetFileSize(hFile, 0);
HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0 , 0);
if (hMap<=0)
{
CloseHandle(hFile);
return 0;
}
LPVOID imagebase = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (imagebase==0)
{
CloseHandle(hFile);
CloseHandle(hMap);
return 0;
}
PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)imagebase + ((PIMAGE_DOS_HEADER)(imagebase))->e_lfanew);
if ((ntHeader->FileHeader.NumberOfSections+1)*sizeof(IMAGE_SECTION_HEADER)>ntHeader->OptionalHeader.SizeOfHeaders)
{
CloseHandle(hFile);
CloseHandle(hMap);
return 0;
}
//定位到最后一個節區的最外面地址,就是nt頭最后的尾部
PIMAGE_SECTION_HEADER newSection = (PIMAGE_SECTION_HEADER)(ntHeader + 1) + ntHeader->FileHeader.NumberOfSections;
//添加節區頭
memcpy(newSection->Name, "freesec", 8); //節區頭名字最多為8個字節,包括結尾的\0
newSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
newSection->Misc.VirtualSize = //添加一個dll信息,所以在原來大小的基礎上加一個導入表描述符大小加dll名字字符串大小+4個IMAGE_THUNK_DATA32大小
ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1 + sizeof(IMAGE_THUNK_DATA32) * 4;
newSection->NumberOfLinenumbers = 0;
newSection->NumberOfRelocations = 0;
newSection->PointerToLinenumbers = 0;
newSection->PointerToRawData = (newSection-1)->PointerToRawData+(newSection-1)->SizeOfRawData;
newSection->PointerToRelocations = 0;
newSection->VirtualAddress = (newSection - 1)->VirtualAddress + PEAlign((newSection - 1)->SizeOfRawData, ntHeader->OptionalHeader.SectionAlignment);
newSection->SizeOfRawData = PEAlign(newSection->Misc.VirtualSize, ntHeader->OptionalHeader.FileAlignment);
DWORD sectionSize = newSection->SizeOfRawData;
//添加節表
SetFilePointer(hFile, 0, 0, FILE_END); //文件指針向文件尾部
LPVOID content = malloc(newSection->SizeOfRawData);
memset(content, 0, newSection->SizeOfRawData);
char* p = (char*)content;
memcpy(content, (LPVOID)((DWORD)imagebase+rva2offset(imagebase, ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress)), ntHeader->OptionalHeader.DataDirectory[1].Size-sizeof(IMAGE_IMPORT_DESCRIPTOR));
p =(char*)((DWORD)p + ntHeader->OptionalHeader.DataDirectory[1].Size - sizeof(IMAGE_IMPORT_DESCRIPTOR));
((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1;
((PIMAGE_IMPORT_DESCRIPTOR)p)->FirstThunk = ((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk + sizeof(IMAGE_THUNK_DATA32)*2;
((PIMAGE_IMPORT_DESCRIPTOR)p)->ForwarderChain = 0;
((PIMAGE_IMPORT_DESCRIPTOR)p)->Name = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR);
((PIMAGE_IMPORT_DESCRIPTOR)p)->TimeDateStamp = 0;
p += sizeof(IMAGE_IMPORT_DESCRIPTOR)*2; //越過新增節和空節尾
//導入表描述符中的name字段,注意2者之間rva的關聯
memcpy(p, dllpath, strlen(dllpath)+1);
p += strlen(dllpath) + 1;
//添加注入的dll的iat和int.4個元素,前2個是給iat的,后2個給int的均以
IMAGE_THUNK_DATA32 ixt[4] = { 0 };
ixt[0].u1.AddressOfData |= 0x80000000; //將最高位置1,表示是以序號導入
ixt[0].u1.AddressOfData += 1;
ixt[2].u1.AddressOfData |= 0x80000000; //將最高位置1,表示是以序號導入
ixt[2].u1.AddressOfData += 1;
memcpy(p, ixt, 4 * sizeof(IMAGE_THUNK_DATA32));
//修改必要字段
ntHeader->FileHeader.NumberOfSections++;
ntHeader->OptionalHeader.SizeOfImage += newSection->SizeOfRawData;
//綁定導入表改為0,保險一些
ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress = newSection->VirtualAddress;
ntHeader->OptionalHeader.DataDirectory[1].Size = newSection->Misc.VirtualSize;
UnmapViewOfFile(imagebase); //這個操作需要在writefile之前調用
if (!WriteFile(hFile, content, sectionSize, 0, 0))
{
CloseHandle(hMap);
CloseHandle(hFile);
free(content);
return 0;
}
CloseHandle(hMap);
CloseHandle(hFile);
free(content);
return 1;
}
//end 導入表注入
