逆向 | 导入表注入代码示例
逆向 | 导入表注入代码示例
还是在编写书中的实验代码,冗余部分比较多可以优化,实验对象是32位pe文件。
// IID_Loader.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <stdio.h>
#include <Windows.h>
#include <string.h>
VOID hex_print(UCHAR* p, DWORD size) {
DWORD i;
printf("\n--------%p--------\n", p);
for (i = 0; i < size; i++) {
printf("0x%02X ", p[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n----------------\n");
}
// 返回新section的FOA
DWORD64 add_section(WCHAR* pefilename, DWORD section_size) {
// 映射PE文件进内存
printf("add section: [%ls] \n", pefilename);
HANDLE fp = CreateFile(pefilename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (fp == NULL) {
printf("err CreateFile \n");
return NULL;
}
DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
printf(" > file size: %x \n", dwBytesInBlock);
HANDLE hFileMapping = CreateFileMapping(fp,
NULL,
PAGE_READWRITE,
0,//(DWORD)(dwBytesInBlock >> 16),
dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
NULL);
int dwError = GetLastError();
// 偏移地址
DWORD64 qwFileOffset = 0;
// 将文件数据映射到进程的地址空间
LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset >> 32),
(DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBytesInBlock);
printf(" > map: %p \n", pbFile);
// 检索PE文件信息
if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
printf(" > IMAGE_DOS_SIGNATURE check fail \n");
hex_print((UCHAR*)pbFile, 16);
return NULL;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
printf(" > IMAGE_NT_SIGNATURE check fail \n");
hex_print((UCHAR*)pNtHeader, 16);
return NULL;
}
// 判断是否有足够的空间增加一个节表项
printf(" > NumberOfSections: %d \n", pNtHeader->FileHeader.NumberOfSections);
printf(" > SizeOfHeaders: 0x%x \n", pNtHeader->OptionalHeader.SizeOfHeaders);
if ((pNtHeader->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) >
(pNtHeader->OptionalHeader.SizeOfHeaders)) {
printf("not enough space for a new section header! \n");
return NULL;
}
// 获取新增节标项的内存位置和文件偏移
PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
pNtHeader->FileHeader.SizeOfOptionalHeader + pNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
printf("> new section position: 0x%x \n", (DWORD64)pNewSection - (DWORD64)pbFile);
PIMAGE_SECTION_HEADER pLastSection = (PIMAGE_SECTION_HEADER)((DWORD64)pNewSection - 40/*节表项的大小*/);
// 新增一个节表项
memcpy(pNewSection->Name, ".mz", strlen(".mz"));
pNewSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; // 设置节属性为可读可写
pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;// 文件中的节偏移FOA(从最后一个节标项目获取到新增节的文件偏移)
pNewSection->SizeOfRawData = section_size; // 文件中的节大小
pNewSection->Misc.VirtualSize = section_size; // 在内存中的大小
DWORD64 mem_size_last_section = 0;
if (pLastSection->Misc.VirtualSize > pLastSection->SizeOfRawData) {
if (pLastSection->Misc.VirtualSize % 0x1000 != 0) {
mem_size_last_section = (pLastSection->Misc.VirtualSize / 0x1000/*内存对齐大小*/) * 0x1000 + 0x1000;
}
mem_size_last_section = pLastSection->Misc.VirtualSize;
}
else {
mem_size_last_section = pLastSection->SizeOfRawData;
}
pNewSection->VirtualAddress = pLastSection->VirtualAddress + mem_size_last_section;
printf(" > PointerToRawData: %p \n", pNewSection->PointerToRawData);
printf(" > SizeOfRawData: %p \n", pNewSection->SizeOfRawData);
printf(" > VirtualAddress: %p \n", pNewSection->VirtualAddress);
printf(" > VirtualSize: %p \n", pNewSection->Misc.VirtualSize);
// 输出新的节表项目
hex_print((UCHAR*)pNewSection, 40);
// 初始化节数据
VOID* buf = malloc(section_size);
DWORD wsize = 0;
memset(buf, 0, section_size);
SetFilePointer(fp, pNewSection->PointerToRawData, NULL, FILE_BEGIN);
WriteFile(fp, buf, section_size, &wsize, NULL);
// 更新PE头中接的数量和整个imagesize的大小
pNtHeader->FileHeader.NumberOfSections += 1;
pNtHeader->OptionalHeader.SizeOfImage = pNewSection->VirtualAddress + pNewSection->Misc.VirtualSize;
DWORD64 ret = pNewSection->PointerToRawData;
// 释放资源
free(buf);
UnmapViewOfFile(pbFile);
CloseHandle(hFileMapping);
CloseHandle(fp);
return ret;
}
DWORD64 RVA2FOA(PIMAGE_NT_HEADERS32 pNtHeader, DWORD64 dwRva) {
DWORD64 dwFoa;
DWORD dwNumberOfSections = pNtHeader->FileHeader.NumberOfSections;
PIMAGE_SECTION_HEADER pImageSectionHeaders = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
pNtHeader->FileHeader.SizeOfOptionalHeader); // 指向节表开头
for (DWORD i = 0; i < dwNumberOfSections; i++) {
// 输出节表的rva和size
printf(" > [%s] RVA: %p - size: %p \n", (pImageSectionHeaders + i)->Name, (pImageSectionHeaders + i)->VirtualAddress, (pImageSectionHeaders + i)->Misc.VirtualSize);
// 判断这个地址属不属于这个节
if (dwRva >= (pImageSectionHeaders + i)->VirtualAddress && dwRva < (pImageSectionHeaders + i)->VirtualAddress + (pImageSectionHeaders + i)->Misc.VirtualSize) {
printf(" >>> 属于这个section! \n");
// 计算FOA
dwFoa = (pImageSectionHeaders + i)->PointerToRawData + (dwRva - (pImageSectionHeaders + i)->VirtualAddress);
break;
}
}
return dwFoa;
}
DWORD64 FOA2RVA(PIMAGE_NT_HEADERS32 pNtHeader, DWORD64 dwFoa) {
DWORD64 dwRva;
DWORD dwNumberOfSections = pNtHeader->FileHeader.NumberOfSections;
PIMAGE_SECTION_HEADER pImageSectionHeaders = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
pNtHeader->FileHeader.SizeOfOptionalHeader); // 指向节表开头
for (DWORD i = 0; i < dwNumberOfSections; i++) {
// 输出节表的foa和size
printf(" > [%s] foa: %p - size: %p \n", (pImageSectionHeaders + i)->Name, (pImageSectionHeaders + i)->PointerToRawData, (pImageSectionHeaders + i)->SizeOfRawData);
// 判断这个地址属不属于这个节
if (dwFoa >= (pImageSectionHeaders + i)->PointerToRawData && dwFoa < (pImageSectionHeaders + i)->PointerToRawData + (pImageSectionHeaders + i)->SizeOfRawData) {
printf(" >>> 属于这个section! \n");
// 计算FOA
dwRva = (pImageSectionHeaders + i)->VirtualAddress + (dwFoa - (pImageSectionHeaders + i)->PointerToRawData);
break;
}
}
return dwRva;
}
VOID cpy_iid(WCHAR* pefilename, DWORD64 new_space) {
// 映射PE文件进内存
printf("cpy_iid: [%ls] \n", pefilename);
HANDLE fp = CreateFile(pefilename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (fp == NULL) {
printf("err CreateFile \n");
return;
}
DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
printf(" > file size: %x \n", dwBytesInBlock);
HANDLE hFileMapping = CreateFileMapping(fp,
NULL,
PAGE_READWRITE,
0,//(DWORD)(dwBytesInBlock >> 16),
dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
NULL);
int dwError = GetLastError();
// 偏移地址
DWORD64 qwFileOffset = 0;
// 将文件数据映射到进程的地址空间
LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset >> 32),
(DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBytesInBlock);
printf(" > map: %p \n", pbFile);
// 检索PE文件信息
if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
printf(" > IMAGE_DOS_SIGNATURE check fail \n");
hex_print((UCHAR*)pbFile, 16);
return;
}
// 这里要根据pe文件的位数选择相应的结构体,这里例子中使用的是32位的版本
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
printf(" > IMAGE_NT_SIGNATURE check fail \n");
hex_print((UCHAR*)pNtHeader, 16);
return;
}
// 检索导入表信息
IMAGE_DATA_DIRECTORY IDEI = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
DWORD IID_size = IDEI.Size;
printf(" > IMAGE_DIRECTORY_ENTRY_IMPORT: %p - size: %p \n", IDEI.VirtualAddress, IDEI.Size);
hex_print((UCHAR*) & pNtHeader->OptionalHeader.DataDirectory, 16);
// 找到IID数组 这里需要RVA转FOA
DWORD64 dwRva = IDEI.VirtualAddress;
DWORD64 dwFoa = RVA2FOA(pNtHeader, dwRva);
printf(" > FOA: %p \n", dwFoa);
////////////////
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD64)pbFile + dwFoa);
printf("%p \n", pIID);
// 计算导入表条目数量
DWORD IID_num = IID_size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
printf(" > 有%d张导入表 \n", IID_num);
// 迁移导入表
DWORD64 new_IID = new_space + (DWORD64)pbFile;
printf(" > cpy导入表到: %p \n", new_IID);
memcpy((VOID*)(new_IID), pIID, IID_size);
// 修改导入表指针和大小
dwFoa = new_space;
dwRva = FOA2RVA(pNtHeader,dwRva);
printf(" > RVA: %p \n", dwRva);
////////////////
pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = dwRva;
// size暂时不需要修改
// 释放资源
UnmapViewOfFile(pbFile);
CloseHandle(hFileMapping);
CloseHandle(fp);
}
VOID add_iid(WCHAR* pefilename, DWORD64 new_space,CHAR* dllname, CHAR* dllexport_function) {
printf("add_iid: [%s] \n", dllname);
HANDLE fp = CreateFile(pefilename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (fp == NULL) {
printf("err CreateFile \n");
return;
}
DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
printf(" > file size: %x \n", dwBytesInBlock);
HANDLE hFileMapping = CreateFileMapping(fp,
NULL,
PAGE_READWRITE,
0,//(DWORD)(dwBytesInBlock >> 16),
dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
NULL);
int dwError = GetLastError();
// 偏移地址
DWORD64 qwFileOffset = 0;
// 将文件数据映射到进程的地址空间
LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset >> 32),
(DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBytesInBlock);
printf(" > map: %p \n", pbFile);
// 检索PE文件信息
if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
printf(" > IMAGE_DOS_SIGNATURE check fail \n");
hex_print((UCHAR*)pbFile, 16);
return;
}
// 这里要根据pe文件的位数选择相应的结构体,这里例子中使用的是32位的版本
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
printf(" > IMAGE_NT_SIGNATURE check fail \n");
hex_print((UCHAR*)pNtHeader, 16);
return;
}
//////////////
// 新增导入表项目
// 检索导入表信息
IMAGE_DATA_DIRECTORY IDEI = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
DWORD IID_size = IDEI.Size;
printf(" > IMAGE_DIRECTORY_ENTRY_IMPORT: %p - size: %p \n", IDEI.VirtualAddress, IDEI.Size);
hex_print((UCHAR*)&pNtHeader->OptionalHeader.DataDirectory, 16);
// 找到IID数组 这里需要RVA转FOA
DWORD64 dwRva = IDEI.VirtualAddress;
DWORD64 dwFoa = RVA2FOA(pNtHeader, dwRva);
printf(" > FOA: %p \n", dwFoa);
////////////////
IDEI.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR); // 增加size
// 添加条目,这里使用自己定义的结构体将这些数据结构串起来
typedef struct _MzIID {
IMAGE_IMPORT_DESCRIPTOR IID[2]; // 第一个用于存放新导入表,第二个用于置0
CHAR dllname[16];
IMAGE_THUNK_DATA32 INT_ITD[2]; // INT: 第一个指向IMAGE_IMPORT_BY_NAME 第二个置空
IMAGE_THUNK_DATA32 IAT_ITD[2]; // IAT: 第一个指向IMAGE_IMPORT_BY_NAME 第二个置空
/*IMAGE_IMPORT_BY_NAME组装成一个字节数组*/
CHAR HintName[32];
}MzIID;
DWORD64 mzIID_FOA = dwFoa +
sizeof(IMAGE_IMPORT_DESCRIPTOR)*
(IID_size/ sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1);
DWORD64 mzIID_RVA = FOA2RVA(pNtHeader, mzIID_FOA);
printf(" > 插入新导入表位置FOA: %p - RVA: %p \n", mzIID_FOA, mzIID_RVA);
MzIID mzIID;
memset(&mzIID, 0, sizeof(MzIID));
memcpy(mzIID.dllname, dllname, strlen(dllname));
mzIID.IID[0].Name = mzIID_RVA + ((DWORD)&mzIID.dllname - (DWORD)&mzIID);
printf(" > mzIID.IID[0].Name: %p \n", mzIID.IID[0].Name);
mzIID.IID[0].TimeDateStamp = 0;
mzIID.IID[0].ForwarderChain = 0;
mzIID.IID[0].FirstThunk = mzIID_RVA + ((DWORD)&mzIID.IAT_ITD -(DWORD)&mzIID);
mzIID.IID[0].OriginalFirstThunk = mzIID_RVA + ((DWORD)&mzIID.INT_ITD - (DWORD)&mzIID);
printf(" > OriginalFirstThunk: %p - FirstThunk: %p \n",
mzIID.IID[0].OriginalFirstThunk,
mzIID.IID[0].FirstThunk);
mzIID.HintName[0] = 0;
mzIID.HintName[1] = 0;
memcpy(&mzIID.HintName[2], dllexport_function, strlen(dllexport_function));
mzIID.IAT_ITD[0].u1.AddressOfData = mzIID_RVA + ((DWORD)&mzIID.HintName - (DWORD)&mzIID);
mzIID.INT_ITD[0].u1.AddressOfData = mzIID_RVA + ((DWORD)&mzIID.HintName - (DWORD)&mzIID);
printf(" > INT_ITD[0].u1.AddressOfData: %p \n", mzIID.INT_ITD[0].u1.AddressOfData);
// 将mzIID打进pe文件中
memcpy((VOID*)(mzIID_FOA + (DWORD64)pbFile), &mzIID, sizeof(MzIID));
printf("> over. \n");
// 释放资源
UnmapViewOfFile(pbFile);
CloseHandle(hFileMapping);
CloseHandle(fp);
}
int main()
{
printf("> IID Loader(会覆盖输入文件,需要自行备份) \n");
WCHAR fname[] = L"print.exe";
/*
1. 新增节
2. 迁移导入表
3. 新增导入项
*/
DWORD64 new_section_foa = add_section(fname, 0x1000);
if (new_section_foa == NULL) {
printf("err \n");
return -1;
}
cpy_iid(fname, new_section_foa);
CHAR dllname[] = "bad.dll";
CHAR dllexport_function[] = "_hi@0";
add_iid(fname, new_section_foa, dllname, dllexport_function);
return 0;
}
本文来自博客园,作者:Mz1,转载请注明原文链接:https://www.cnblogs.com/Mz1-rc/p/18306823
如果有问题可以在下方评论或者email:mzi_mzi@163.com