简单写一个DLL项目,生成LIB和DLL文件回顾基础
在进程进入和退出的时候调用自己写的函数
dllmain.cpp
xxxxxxxxxx// dllmain.cpp : 定义 DLL 应用程序的入口点。
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: Init(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: Destroy(); break; } return TRUE;}TestDLL.cpp
xxxxxxxxxx
void Init() { system("calc.exe");}
void Destroy(){ system("calc.exe");}
void ExportFunction(){ system("calc.exe");}TestDLL.h
导出函数
xxxxxxxxxxvoid Init();void Destroy();extern "C" _declspec(dllexport) void ExportFunction();编译后会生成一个LIB和DLL文件
测试LIB文件加载静态链接库,成功弹出三个计算器
(注意TestDLL.lib放在工作目录中)
xxxxxxxxxxextern "C" __declspec(dllimport) void ExportFunction();int main(){ ExportFunction();}
测试DLL动态链接库,成功弹出三个计算器
xxxxxxxxxxint main(){ typedef void (*p)(); p MyFunction; HINSTANCE hModule = LoadLibrary(L"YOUR-PATH-TO\\TestDLL.dll"); if (hModule == NULL) { return 0; } MyFunction = (p)GetProcAddress(hModule, "ExportFunction"); MyFunction(); return 0;}
当EXE被加载时,系统会根据EXE导入表信息来加载需要用到的DLL。导入表注入的原理是修改EXE导入表,将自己的DLL添加到EXE的导入表中,这样EXE运行时可以将自己的DLL加载到EXE的进程空间
第一步:拿到导入表信息(数据目录第二个)
xxxxxxxxxxPIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));第二部:计算新增节的大小
新增节存储的内容有:原来的所有导入表,新导入表,新INT和IAT表,模块名和一个_IMAGE_IMPORT_BY_NAME结构体,并且包含一个全0的结束标记
计算DLL的数量(导入表数量)
xxxxxxxxxxDWORD dwNumberOfDll = 0;while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk){ dwNumberOfDll++; pImportTable++;}大小应该增加原有的部分再加一个新导入表和一个全0结束标记
xxxxxxxxxxdwNewSectionSize += (dwNumberOfDll + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR);计算一个IAT和INT表项,以及对应的结束标记(4个DWORD)
xxxxxxxxxxdwNewSectionSize += 16;
计算DLL名称长度(末尾是0)
xxxxxxxxxxdwNewSectionSize += strlen("InjectDll.dll") + 1;_IMAGE_IMPORT_BY_NAME结构体大小为WORD Hint加字符串长度
xxxxxxxxxxtypedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; CHAR Name[1];} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;xxxxxxxxxxdwNewSectionSize += 2 + strlen("ExportFunction") + 1;增加新节,从pNewFileBuffer开始找到新节表地址,在新节的节表中设置属性为可读写含已初始化数据
xxxxxxxxxxDWORD dwNewFileSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileSize, dwNewSectionSize);
pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0xC0000040;拿到新节的PointerToRawData地址
xxxxxxxxxxLPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);将所有导出表复制到新节中
xxxxxxxxxxLPVOID pInsert = pNewSec;memcpy(pInsert, pImportTable, dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));设置新增导入表的属性,时间戳设为0表示不使用绑定导入,其中ForwarderChain值一般需要设置为-1
xxxxxxxxxxpImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pInsert + dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));pImportTable->TimeDateStamp = 0;pImportTable->ForwarderChain = -1;设置导入表的结束标记,需要一个导入表的长度全部设为0
xxxxxxxxxxpInsert = (LPVOID)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR));memset(pInsert, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));指定INT表插入点
xxxxxxxxxxpInsert = (LPVOID)((DWORD)pInsert + sizeof(IMAGE_IMPORT_DESCRIPTOR));PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)pInsert;设置INT表的结束标志
xxxxxxxxxxmemset(pINT + 1, 0, sizeof(IMAGE_THUNK_DATA));由于INT表只有一个元素,所以加8偏移得到IMPORT_BY_NAME
xxxxxxxxxxPIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pINT + 8);导出序号没有必要设置
xxxxxxxxxxpImportByName->Hint = 0;函数名设置为开头写的DLL内导出的函数
xxxxxxxxxxstrcpy((char*)(pImportByName->Name), "ExportFunction");而INT表这一项实际保存的值是IMPORT_BY_NAME的RVA
xxxxxxxxxx*((PDWORD)pINT) = FoaToImageOffset(*pNewFileBuffer, (DWORD)pImportByName - (DWORD)*pNewFileBuffer);对应偏移后得到IAT表,将INT表直接复制过来即可(都指向IMPORT_BY_NAME)
xxxxxxxxxxpInsert = (LPVOID)((DWORD)pImportByName + 2 + strlen("ExportFunction") + 1); PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)pInsert;memcpy(pIAT, pINT, 8);继续留出结束标志
xxxxxxxxxxmemset(pIAT + 1, 0, sizeof(IMAGE_THUNK_DATA));紧接着偏移IAT表加结束标志之后的部分写入字符串
xxxxxxxxxxpInsert = (LPVOID)((DWORD)pInsert + 16);strcpy((char*)pInsert, "InjectDll.dll");这个字符串是作为新导出表的Name部分的RVA
xxxxxxxxxxpImportTable->FirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pINT - (DWORD)*pNewFileBuffer);pImportTable->OriginalFirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pIAT - (DWORD)*pNewFileBuffer);pImportTable->Name = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);最后修改数据目录导入表的RVA指向最后节起始地址
xxxxxxxxxxpOptionHeader->DataDirectory[1].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, ((DWORD)pNewSec - (DWORD)*pNewFileBuffer));
整体代码
xxxxxxxxxxDWORD ImportTableInjectDemo(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileSize){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4); PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress)); DWORD dwNewSectionSize = 0; DWORD dwNumberOfDll = 0; while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk) { dwNumberOfDll++; pImportTable++; } dwNewSectionSize += (dwNumberOfDll + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR); dwNewSectionSize += 16; dwNewSectionSize += strlen("InjectDll.dll") + 1; dwNewSectionSize += 2 + strlen("ExportFunction") + 1; DWORD dwNewFileSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileSize, dwNewSectionSize); pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer; pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER)); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress)); pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0xC0000040; LPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData); LPVOID pInsert = pNewSec; memcpy(pInsert, pImportTable, dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR)); pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pInsert + dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR)); pImportTable->TimeDateStamp = 0; pImportTable->ForwarderChain = -1; pInsert = (LPVOID)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR)); memset(pInsert, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR)); pInsert = (LPVOID)((DWORD)pInsert + sizeof(IMAGE_IMPORT_DESCRIPTOR)); PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)pInsert; memset(pINT + 1, 0, sizeof(IMAGE_THUNK_DATA)); PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pINT + 8); pImportByName->Hint = 0; strcpy((char*)(pImportByName->Name), "ExportFunction"); *((PDWORD)pINT) = FoaToImageOffset(*pNewFileBuffer, (DWORD)pImportByName - (DWORD)*pNewFileBuffer); pInsert = (LPVOID)((DWORD)pImportByName + 2 + strlen("ExportFunction") + 1); PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)pInsert; memcpy(pIAT, pINT, 8); memset(pIAT + 1, 0, sizeof(IMAGE_THUNK_DATA)); pInsert = (LPVOID)((DWORD)pInsert + 16); strcpy((char*)pInsert, "InjectDll.dll"); pImportTable->FirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pINT - (DWORD)*pNewFileBuffer); pImportTable->OriginalFirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pIAT - (DWORD)*pNewFileBuffer); pImportTable->Name = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer); pOptionHeader->DataDirectory[1].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, ((DWORD)pNewSec - (DWORD)*pNewFileBuffer)); return dwNewFileSize;}
随便找一个EXE并将新的FileBuffer写入新EXE
xxxxxxxxxxLPVOID SRCBuffer = NULL;DWORD size = ReadPEFile((LPSTR)FGEXE, &SRCBuffer);LPVOID DESTBuffer = NULL;DWORD newSize = ImportTableInjectDemo(SRCBuffer,&DESTBuffer,size);MemeryToFile(DESTBuffer, newSize, (LPSTR)FGEXE_NEW);
注意将DLL重命名为InjectDll.dll然后放在EXE同级目录,双击打开EXE截图
