简单写一个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
导出函数
xxxxxxxxxx
void Init();
void Destroy();
extern "C" _declspec(dllexport) void ExportFunction();
编译后会生成一个LIB和DLL文件
测试LIB文件加载静态链接库,成功弹出三个计算器
(注意TestDLL.lib放在工作目录中)
xxxxxxxxxx
extern "C" __declspec(dllimport) void ExportFunction();
int main()
{
ExportFunction();
}
测试DLL动态链接库,成功弹出三个计算器
xxxxxxxxxx
int 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的进程空间
第一步:拿到导入表信息(数据目录第二个)
xxxxxxxxxx
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
第二部:计算新增节的大小
新增节存储的内容有:原来的所有导入表,新导入表,新INT和IAT表,模块名和一个_IMAGE_IMPORT_BY_NAME
结构体,并且包含一个全0的结束标记
计算DLL
的数量(导入表数量)
xxxxxxxxxx
DWORD dwNumberOfDll = 0;
while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk)
{
dwNumberOfDll++;
pImportTable++;
}
大小应该增加原有的部分再加一个新导入表和一个全0结束标记
xxxxxxxxxx
dwNewSectionSize += (dwNumberOfDll + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
计算一个IAT和INT表项,以及对应的结束标记(4个DWORD)
xxxxxxxxxx
dwNewSectionSize += 16;
计算DLL名称长度(末尾是0)
xxxxxxxxxx
dwNewSectionSize += strlen("InjectDll.dll") + 1;
_IMAGE_IMPORT_BY_NAME
结构体大小为WORD Hint
加字符串长度
xxxxxxxxxx
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
xxxxxxxxxx
dwNewSectionSize += 2 + strlen("ExportFunction") + 1;
增加新节,从pNewFileBuffer
开始找到新节表地址,在新节的节表中设置属性为可读写含已初始化数据
xxxxxxxxxx
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;
拿到新节的PointerToRawData
地址
xxxxxxxxxx
LPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);
将所有导出表复制到新节中
xxxxxxxxxx
LPVOID pInsert = pNewSec;
memcpy(pInsert, pImportTable, dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));
设置新增导入表的属性,时间戳设为0表示不使用绑定导入,其中ForwarderChain
值一般需要设置为-1
xxxxxxxxxx
pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pInsert + dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));
pImportTable->TimeDateStamp = 0;
pImportTable->ForwarderChain = -1;
设置导入表的结束标记,需要一个导入表的长度全部设为0
xxxxxxxxxx
pInsert = (LPVOID)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR));
memset(pInsert, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));
指定INT表插入点
xxxxxxxxxx
pInsert = (LPVOID)((DWORD)pInsert + sizeof(IMAGE_IMPORT_DESCRIPTOR));
PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)pInsert;
设置INT表的结束标志
xxxxxxxxxx
memset(pINT + 1, 0, sizeof(IMAGE_THUNK_DATA));
由于INT表只有一个元素,所以加8偏移得到IMPORT_BY_NAME
xxxxxxxxxx
PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pINT + 8);
导出序号没有必要设置
xxxxxxxxxx
pImportByName->Hint = 0;
函数名设置为开头写的DLL内导出的函数
xxxxxxxxxx
strcpy((char*)(pImportByName->Name), "ExportFunction");
而INT表这一项实际保存的值是IMPORT_BY_NAME
的RVA
xxxxxxxxxx
*((PDWORD)pINT) = FoaToImageOffset(*pNewFileBuffer, (DWORD)pImportByName - (DWORD)*pNewFileBuffer);
对应偏移后得到IAT表,将INT表直接复制过来即可(都指向IMPORT_BY_NAME
)
xxxxxxxxxx
pInsert = (LPVOID)((DWORD)pImportByName + 2 + strlen("ExportFunction") + 1);
PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)pInsert;
memcpy(pIAT, pINT, 8);
继续留出结束标志
xxxxxxxxxx
memset(pIAT + 1, 0, sizeof(IMAGE_THUNK_DATA));
紧接着偏移IAT表加结束标志之后的部分写入字符串
xxxxxxxxxx
pInsert = (LPVOID)((DWORD)pInsert + 16);
strcpy((char*)pInsert, "InjectDll.dll");
这个字符串是作为新导出表的Name
部分的RVA
xxxxxxxxxx
pImportTable->FirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pINT - (DWORD)*pNewFileBuffer);
pImportTable->OriginalFirstThunk = FoaToImageOffset(*pNewFileBuffer, (DWORD)pIAT - (DWORD)*pNewFileBuffer);
pImportTable->Name = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);
最后修改数据目录导入表的RVA
指向最后节起始地址
xxxxxxxxxx
pOptionHeader->DataDirectory[1].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, ((DWORD)pNewSec - (DWORD)*pNewFileBuffer));
整体代码
xxxxxxxxxx
DWORD 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
xxxxxxxxxx
LPVOID 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截图