請轉到以下連結食用 🐷
代碼1(部分情況下有修複記憶體對齊的 bug):
#include <windows.h>
#include <iostream>
#include <exception>
#include <string>
using namespace std;
ULONG32 PEAlign(ULONG32 dwNumber, ULONG32 dwAlign)
{
return(((dwNumber + dwAlign - 1) / dwAlign) * dwAlign);
}
BOOL AddNewSection(const string& strTargetFile, ULONG ulNewSectionSize)
{
BOOL bOk = FALSE;
HANDLE TargetFileHandle = nullptr;
HANDLE MappingHandle = nullptr;
PVOID FileData = nullptr;
ULONG ulFileSize = 0;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_SECTION_HEADER pNewSectionHeader = NULL;
PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;
DWORD FileSize = 0;
DWORD FileOffset = 0;
DWORD VirtualSize = 0;
DWORD VirtualOffset = 0;
PCHAR pNewSectionContent = NULL;
DWORD dwWrittenLength = 0;
// 打開檔案
TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (TargetFileHandle == INVALID_HANDLE_VALUE)
{
goto EXIT;
}
ulFileSize = GetFileSize(TargetFileHandle, NULL);
if (INVALID_FILE_SIZE == ulFileSize)
{
goto EXIT;
}
// 映射檔案
MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
if (MappingHandle == NULL)
{
goto EXIT;
}
// 得到緩存頭
FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
if (FileData == NULL)
{
goto EXIT;
}
// 判斷是否是PE檔案
if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
{
goto EXIT;
}
pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
goto EXIT;
}
// 判斷是否可以增加一個新節
if ((pNtHeaders->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) > pNtHeaders->OptionalHeader.SizeOfHeaders)
{
goto EXIT;
}
// 得到新節的起始位址, 最後的起始位址
pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections;
pLastSectionHeader = pNewSectionHeader - 1;
// 對齊RVA和偏移
FileSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.FileAlignment);
FileOffset = PEAlign(pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData, pNtHeaders->OptionalHeader.FileAlignment);
VirtualSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.SectionAlignment);
VirtualOffset = PEAlign(pLastSectionHeader->VirtualAddress + pLastSectionHeader->Misc.VirtualSize, pNtHeaders->OptionalHeader.SectionAlignment);
// 填充新節表
memcpy(pNewSectionHeader->Name, "Inject", strlen("Inject"));
pNewSectionHeader->VirtualAddress = VirtualOffset;
pNewSectionHeader->Misc.VirtualSize = VirtualSize;
pNewSectionHeader->PointerToRawData = FileOffset;
pNewSectionHeader->SizeOfRawData = FileSize;
pNewSectionHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
// 修改IMAGE_NT_HEADERS
pNtHeaders->FileHeader.NumberOfSections++;
pNtHeaders->OptionalHeader.SizeOfImage += VirtualSize;
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
// 添加新節到檔案尾部
SetFilePointer(TargetFileHandle, 0, 0, FILE_END);
pNewSectionContent = new CHAR[FileSize];
RtlZeroMemory(pNewSectionContent, FileSize);
dwWrittenLength = 0;
if (!WriteFile(TargetFileHandle, pNewSectionContent, FileSize, &dwWrittenLength, nullptr))
{
goto EXIT;
}
bOk = TRUE;
EXIT:
if (TargetFileHandle != NULL)
{
CloseHandle(TargetFileHandle);
TargetFileHandle = nullptr;
}
if (FileData != NULL)
{
UnmapViewOfFile(FileData);
FileData = nullptr;
}
if (MappingHandle != NULL)
{
CloseHandle(MappingHandle);
MappingHandle = nullptr;
}
return bOk;
}
PIMAGE_SECTION_HEADER GetOwnerSection(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
int i;
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pNTHeaders + 1);
for (i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++)
{
if ((dwRVA >= (pSectionHeader + i)->VirtualAddress) && (dwRVA <= ((pSectionHeader + i)->VirtualAddress + (pSectionHeader + i)->SizeOfRawData)))
{
return ((PIMAGE_SECTION_HEADER)(pSectionHeader + i));
}
}
return PIMAGE_SECTION_HEADER(NULL);
}
DWORD RVAToFOA(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
DWORD _offset;
PIMAGE_SECTION_HEADER section;
// 找到偏移所在節
section = GetOwnerSection(pNTHeaders, dwRVA);
if (section == NULL)
{
return(0);
}
// 修正偏移
_offset = dwRVA + section->PointerToRawData - section->VirtualAddress;
return(_offset);
}
BOOL AddNewImportDescriptor(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
BOOL bOk = FALSE;
ULONG ulFileSize = 0;
HANDLE TargetFileHandle = nullptr;
HANDLE MappingHandle = nullptr;
PVOID FileData = nullptr;
PIMAGE_IMPORT_DESCRIPTOR pImportTable = nullptr;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
BOOL bBoundImport = FALSE;
PIMAGE_SECTION_HEADER pNewSectionHeader = NULL;
PBYTE pNewSectionData = NULL;
PBYTE pNewImportDescriptor = NULL;
INT i = 0;
DWORD dwDelt = 0;
PIMAGE_THUNK_DATA pNewThunkData = NULL;
PBYTE pszDllName = NULL;
PIMAGE_IMPORT_BY_NAME pImportByName = NULL;
// 打開檔案
TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (TargetFileHandle == INVALID_HANDLE_VALUE)
{
goto EXIT;
}
ulFileSize = GetFileSize(TargetFileHandle, NULL);
if (INVALID_FILE_SIZE == ulFileSize)
{
goto EXIT;
}
// 映射檔案
MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
if (MappingHandle == NULL)
{
goto EXIT;
}
// 得到緩存頭
FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
if (FileData == NULL)
{
goto EXIT;
}
// 判斷是否是PE檔案
if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
{
goto EXIT;
}
pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
goto EXIT;
}
// 得到原導入表
pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)FileData + RVAToFOA(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
// 判斷是否使用了綁定導入表
if (pImportTable->Characteristics == 0 && pImportTable->FirstThunk != 0)
{
bBoundImport = TRUE;
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
}
// 找到自己添加的新節
pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections - 1;
pNewSectionData = pNewSectionHeader->PointerToRawData + (PBYTE)FileData;
pNewImportDescriptor = pNewSectionData;
// 往新節中拷貝原導入表内容
i = 0;
while (pImportTable->FirstThunk != 0 || pImportTable->Characteristics != 0)
{
memcpy(pNewSectionData + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), pImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR));
pImportTable++;
pNewImportDescriptor += sizeof(IMAGE_IMPORT_DESCRIPTOR);
i++;
}
// 複制最後一個描述符
memcpy(pNewImportDescriptor, pNewImportDescriptor - sizeof(IMAGE_IMPORT_DESCRIPTOR), sizeof(IMAGE_IMPORT_DESCRIPTOR));
// 計算修正值
dwDelt = pNewSectionHeader->VirtualAddress - pNewSectionHeader->PointerToRawData;
// pNewImportDescriptor 目前指向要構造的新描述符 再空出一個空描述符作為導入表的結束符 是以是 2 *
pNewThunkData = PIMAGE_THUNK_DATA(pNewImportDescriptor + 2 * sizeof(IMAGE_IMPORT_DESCRIPTOR));
pszDllName = (PBYTE)(pNewThunkData + 2);
memcpy(pszDllName, strInjectDllName.c_str(), strInjectDllName.length());
// 确定 DllName 的位置
pszDllName[strInjectDllName.length() + 1] = 0;
// 确定 IMAGE_IMPORT_BY_NAM 的位置
pImportByName = (PIMAGE_IMPORT_BY_NAME)(pszDllName + strInjectDllName.length() + 1);
// 初始化 IMAGE_THUNK_DATA
pNewThunkData->u1.Ordinal = (DWORD_PTR)pImportByName - (DWORD_PTR)FileData + /*加上修正值 - 這裡應該填充在記憶體中的位址*/dwDelt;
// 初始化 IMAGE_IMPORT_BY_NAME
pImportByName->Hint = 1;
memcpy(pImportByName->Name, strFunctionName.c_str(), strFunctionName.length());
pImportByName->Name[strFunctionName.length() + 1] = 0;
// 初始化 PIMAGE_IMPORT_DESCRIPTOR
if (bBoundImport)
{
((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = 0;
}
else
{
((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
}
((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->FirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->Name = dwDelt + (DWORD_PTR)pszDllName - (DWORD_PTR)FileData;
// 修改導入表入口
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pNewSectionHeader->VirtualAddress;
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (i + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
bOk = TRUE;
EXIT:
if (TargetFileHandle != NULL)
{
CloseHandle(TargetFileHandle);
TargetFileHandle = nullptr;
}
if (FileData != NULL)
{
UnmapViewOfFile(FileData);
FileData = nullptr;
}
if (MappingHandle != NULL)
{
CloseHandle(MappingHandle);
MappingHandle = nullptr;
}
return bOk;
}
BOOL AddImportTable(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
BOOL bOk = FALSE;
if (!AddNewSection(strTargetFile, 256))
{
goto end;
}
if (!AddNewImportDescriptor(strTargetFile, strInjectDllName, strFunctionName))
{
goto end;
}
bOk = TRUE;
end:
return bOk;
}
int main()
{
getchar();
AddImportTable("被注入程序.exe", "要注入的子產品.dll", "子產品導出函數名");
system("pause");
return true;
}
代碼2(測試正常,推薦):
// 這段代碼要放在檔案頭部才能生效 (。・∀・)ノ
#ifndef
#define
#endif
BOOL iatInsertDll(CONST TCHAR dllName[MAX_PATH],CONST TCHAR funcName[MAX_PATH],CONST TCHAR sourceFile[MAX_PATH],CONST TCHAR targetFile[MAX_PATH])
{
BOOL ret = FALSE;
PTCHAR pExe = nullptr; // 原檔案二進制
UINT fileSize = 0; // 檔案大小
FILE* pfin = nullptr; // 輸入檔案
FILE* pfout = nullptr; // 輸出檔案
TCHAR injectStr[64] = { 0 }; // 導入函數字元資訊
UINT injectStrLen = 0; // 字元資訊長度
UINT injectStrThunk = 0; // 以 '\0' 結尾的 dll 名字元串
IMAGE_DOS_HEADER dos_header; // dos 頭
IMAGE_NT_HEADERS* nt_headers_ptr; // nt 頭
IMAGE_SECTION_HEADER* section_headers; // 可選頭
IMAGE_IMPORT_DESCRIPTOR* import_descriptor; // 導入表
IMAGE_IMPORT_DESCRIPTOR newImportStruct; // 新導入項(非導入表)
LONG IatOffset = 0; // 導入表所在區段 rof 、rva 內插補點
LONG finalSectionOffset = 0; // 最後一個區段 rof 、 rva 的內插補點
LONG finalSectionEnd = 0; // 最後一個區段末尾rof
UINT finalSectionIndex = 0; // 最後一個區段的下标
INT importCount = -1; // 導入函數個數
ULONG injectAddr = 0; // 要插入的導入函數位址
PTCHAR szEndPad = { 0 }; // 末尾填充字元串
UINT padLen = 0; // 填充長度
// 隻讀方式打開二進制檔案
pfin = fopen(sourceFile, "rb");
if (!pfin)
{
printf("fopen failed. \n");
goto end;
}
// 将檔案二進制讀入記憶體
fseek(pfin, 0, SEEK_END);
fileSize = ftell(pfin);
fseek(pfin, 0, SEEK_SET);
pExe = (char*)malloc(fileSize);
fread(pExe, fileSize, 1, pfin);
fclose(pfin);
// 拼接插入dll 和導入函數的字元資訊
sprintf(injectStr, "%s%c%c%c%s%c%c%c%c%c", dllName, 0, 0, 0, funcName, 0, 0, 0, 0, 0);
injectStrLen = strlen(dllName) + 3 + strlen(funcName) + 5;
injectStrThunk = strlen(dllName) + 1;
// 判斷是否為有效的 pe 檔案
dos_header = *(IMAGE_DOS_HEADER*)(pExe);
nt_headers_ptr = (IMAGE_NT_HEADERS*)(pExe + dos_header.e_lfanew);
if (dos_header.e_magic != 0x5A4D || (*nt_headers_ptr).Signature != 0x4550)
{
printf("Invalid PE file. \n");
goto end;
}
// 計算可選頭
section_headers =
(IMAGE_SECTION_HEADER*)
(pExe
+ dos_header.e_lfanew
+ sizeof((*nt_headers_ptr).Signature)
+ sizeof((*nt_headers_ptr).FileHeader)
+ (*nt_headers_ptr).FileHeader.SizeOfOptionalHeader);
// 計算最後一個區段的下标
finalSectionIndex = (*nt_headers_ptr).FileHeader.NumberOfSections - 1;
// 計算最後一個 區段的 rof 和 rva 的內插補點
finalSectionOffset = section_headers[finalSectionIndex].PointerToRawData - section_headers[finalSectionIndex].VirtualAddress;
// 計算最後一個區段的末尾 rof
finalSectionEnd = section_headers[finalSectionIndex].PointerToRawData + section_headers[finalSectionIndex].SizeOfRawData;
// 找到導入表所在區段 rof、rva 的內插補點
for (int i = finalSectionIndex; i > -1; i--)
// 因為是 i-- ,是以區段的 VirtualAddress 是越來越小的,當 VirtualAddress 首次小于導入表的 VirtualAddress 時,說明導入表在這個區段中
if (section_headers[i].VirtualAddress <= (*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
{
IatOffset = section_headers[i].PointerToRawData - section_headers[i].VirtualAddress;
break;
}
// 計算導入表 rof(=區段rof+導入表所在區段的段内偏移)
import_descriptor =
(IMAGE_IMPORT_DESCRIPTOR*)
(pExe
+ (*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
+ IatOffset);
// 計算導入函數個數
while (import_descriptor[++importCount].OriginalFirstThunk);
// 重定向導入表到最後一個區段末尾
(*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = finalSectionEnd - finalSectionOffset;
// 調整 pe 以适應新的導入表(擴充大小、記憶體對齊、可寫)
(*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);
(*nt_headers_ptr).OptionalHeader.SizeOfImage += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
section_headers[finalSectionIndex].SizeOfRawData += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
section_headers[finalSectionIndex].Misc.VirtualSize += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
section_headers[finalSectionIndex].Characteristics |= IMAGE_SCN_MEM_WRITE;
// 計算擴充後的導入函數位址
injectAddr = (importCount + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR) + finalSectionEnd - finalSectionOffset;
// 在導入字元資訊後追加導入函數位址資訊(讓新副本找到導入函數)
*(DWORD*)(injectStr + injectStrLen - 4) = injectAddr + injectStrThunk;
// 初始化導入項資訊
newImportStruct.OriginalFirstThunk = injectAddr + injectStrLen - 4; // 給 Name 留(inject_strlen - 4)長度的空位
newImportStruct.TimeDateStamp = 0;
newImportStruct.ForwarderChain = 0;
newImportStruct.Name = injectAddr; // Name 是擴充後的導入函數頭部位址
newImportStruct.FirstThunk = injectAddr + injectStrLen - 4; // 新導入函數 rva (緊随頭部位址之後)
// 寫方式打開二進制檔案,拷貝一份副本
pfout = fopen(targetFile, "wb");
if (!pfout)
{
printf("fopen failed. \n");
goto end;
}
fwrite(pExe, finalSectionEnd, 1, pfout);
// 修改新副本導入表位址
fwrite(import_descriptor, importCount * sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);
// 追加新導入項
fwrite(&newImportStruct, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);
// 填充 sizeof(IMAGE_IMPORT_DESCRIPTOR) 個 '\0' ,表示導入表結構到此結束
fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);
// 設定導入函數字元串資訊
fwrite(injectStr, injectStrLen, 1, pfout);
// 将檔案填充到正确的對齊位置
padLen = (*nt_headers_ptr).OptionalHeader.SectionAlignment - (importCount + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR) - injectStrLen;
szEndPad = (PTCHAR)calloc(1, padLen);
fwrite(szEndPad, padLen, 1, pfout);
ret = TRUE;
end:
if (pfin)
{
fclose(pfin);
}
if (pfout)
{
fclose(pfout);
}
if (pExe)
{
free(pExe);
}
if (szEndPad)
{
free(szEndPad);
}
return ret;
}
int main()
{
iatInsertDll("Insert.dll", "justExport", "before.exe", "after.exe");
return 0;
}