IAT注入

来源:互联网 发布:无线云台网络摄像机 编辑:程序博客网 时间:2024/06/03 16:29

IAT 注入 的主要思路就是在PE 中增加一个节,然后将导入表拷贝到当前的节中,在导入表后面增加一个新的导入项,并将导入表重定向到当前的表中。

一般情况下,我们的节头到段的真正的开始位置是有一定的空闲区域的。

下图为我们的示例程序SimpleEXE.exe的节头,可以看出来,我们的第一个段的起始文件偏移为0x400

虽然IMAGE_OPTIONAL_HEADER32. SizeOfHeaders 总是与第一个段的大小相同,但是我们知道,其真实大小应该为:sizeof(_IMAGE_NT_HEADERS)+sizeof(IMAGE_SECTION_HEADER)*(IMAGE_FILE_HEADER. NumberOfSections+1),其中包括最后一个表示结束的IMPORT_BY_NAME结构。

如果加上pDosHeader->e_lfanew其大小就是第一个段之前的实际数据的大小,如图所示:

我们可以看到,其大小为0x310 有 0x90 字节的数据没有使用。



这样,我们在IAT 中添加一个IMAGE_SECTION_HEADER结构是完全没有问题的。

添加了一个节头结构之后,就是添加一个节的操作了。

定位到最后一个节的结构,查看其结构如下:

 

因此我们的新节的RawAddress为:0x6E00+0x600 对 0x200 对齐,为:0x7400

虚拟地址为:0x1A000+0x469 对 0x1000 对齐,为:0x1B000

然后我们来看新节保护属性的选择,因为我们新添加的段将来不仅仅会有导入表(可读,包含初始化数据),在我们添加了一个导入DLL 之后还会添加一个导入函数,而该导入函数对应的IAT 需要被修改,因此我们应该添加可写属性,因此其最终的保护属性为可读可写包含初始化的数据-----0xC0000040


有了新节之后,我们考虑节中的数据:

1.      原来的导入表结构

2.      新添加的PIMAGE_IMPORT_DESCRIPTOR结构

3.      新添加的两个IMAGE_THUNK_DATA(分别对应于OriginalFirstThunk和FirstThunk结构)

4.      新添加的两个IMAGE_THUNK_DATA结构末尾的两个为0 的IMAGE_THUNK_DATA结构,用于表示导入表的结束

解决了这些基本问题之后就是具体的编程过程了。

下面是核心代码以及一些核心注释

// 导入表定义

typedef struct _INJECT_STRUCT_

{

    IMAGE_THUNK_DATA OriginalThunk;

    IMAGE_THUNK_DATA OriginalThunk_ZERO;

    IMAGE_THUNK_DATA FirstThunk;

    IMAGE_THUNK_DATA FirstThunk_ZERO;

    IMAGE_IMPORT_BY_NAME ImportByName;

    CHAR szFuncName[16];

    CHAR szDllName[MAX_PATH];

}INJECT_STRUCT, *PINJECT_STRUCT;

 

 

BOOLModifyPEImportTableInject(ULONG_PTR ulBaseAddress, char* szDllName){PIMAGE_DOS_HEADERpDosHeader = (PIMAGE_DOS_HEADER)ulBaseAddress;PIMAGE_NT_HEADERSpNTHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)pDosHeader + pDosHeader->e_lfanew);PIMAGE_SECTION_HEADERsection = IMAGE_FIRST_SECTION(pNTHeader);PIMAGE_SECTION_HEADERpLastSection = (PIMAGE_SECTION_HEADER)(section + pNTHeader->FileHeader.NumberOfSections - 1);BYTE*pDelta = new BYTE[pNTHeader->OptionalHeader.FileAlignment];// 用于填充0数据memset(pDelta, 0, pNTHeader->OptionalHeader.FileAlignment);DWORD dwWriteSize = 0;DWORD dwReturnSize = 0;DWORD dwRet = 0;PVOID pWriteBuffer = NULL;DWORD dwFileAlignment = pNTHeader->OptionalHeader.FileAlignment;DWORD dwMemAlignment = pNTHeader->OptionalHeader.SectionAlignment;DWORD dwSectionHeaderSize = (pNTHeader->FileHeader.NumberOfSections + 2) * sizeof(IMAGE_SECTION_HEADER);PIMAGE_IMPORT_DESCRIPTOR  pImportDesc = 0;PIMAGE_SECTION_HEADERpPreImportSectionHeader = (PIMAGE_SECTION_HEADER)RvaToSection(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,pNTHeader);PIMAGE_SECTION_HEADER     pSection = 0;PIMAGE_THUNK_DATA         pThunk, pThunkIAT = 0;DWORD dwTemp = 0;// 这里仅仅创建新文件HANDLE hTargetFile = CreateFileA("Injected.exe", FILE_ALL_ACCESS,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hTargetFile == INVALID_HANDLE_VALUE){int a = GetLastError();printf("Create Virus Failed\r\n");return FALSE;}///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////写入Dos HeaderdwRet = WriteFile(hTargetFile, pDosHeader, sizeof(IMAGE_DOS_HEADER), &dwReturnSize, NULL);if (!dwRet){printf("Write Virus DosHeader Failed\r\n");delete[]pDelta;CloseHandle(hTargetFile);return FALSE;}///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////写入Dos StubdwWriteSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);dwRet = WriteFile(hTargetFile, (LPVOID)((DWORD)pDosHeader + sizeof(IMAGE_DOS_HEADER)), dwWriteSize, &dwReturnSize, NULL);if (!dwRet){printf("Write Virus DosStub Failed\r\n");delete[]pDelta;CloseHandle(hTargetFile);return FALSE;}///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////写入Nt HeaderdwWriteSize = sizeof(IMAGE_NT_HEADERS);pNTHeader->FileHeader.NumberOfSections ++;pNTHeader->OptionalHeader.SizeOfImage += 0x1000;// 增加的一个节的大小为0x1000dwTemp = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = Aligment(pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize, dwMemAlignment);dwRet = WriteFile(hTargetFile, pNTHeader, dwWriteSize, &dwReturnSize, NULL);if (!dwRet){printf("Write Virus NtHeader Failed\r\n");delete[]pDelta;CloseHandle(hTargetFile);return FALSE;}pNTHeader->FileHeader.NumberOfSections --;// 保持后面的逻辑易于理解pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = dwTemp;pNTHeader->OptionalHeader.SizeOfImage -= 0x1000;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 得到导入表所在的节表,以及导入表个数pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress \- pPreImportSectionHeader->VirtualAddress + pPreImportSectionHeader->PointerToRawData + ulBaseAddress);// 取出导入的DLL的个数int nImportDllCount = 0;while (1){if ((pImportDesc->TimeDateStamp == 0) && (pImportDesc->Name == 0))break;pThunk = (PIMAGE_THUNK_DATA)(pImportDesc->Characteristics);pThunkIAT = (PIMAGE_THUNK_DATA)(pImportDesc->FirstThunk);if (pThunk == 0 && pThunkIAT == 0)return -1;nImportDllCount++;pImportDesc++;}// 恢复pImportDesc的值,方便下面的复制当前导入表的操作.pImportDesc -= nImportDllCount;// 原来的导入表,加上一个新结构体和一个0结束结构体。就是要写入文件的导入表的大小DWORD dwIATDESCSize = (nImportDllCount + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR);/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 复制旧的数据,并填充新添加的节的数据,将所有的节表头写入文件PIMAGE_SECTION_HEADER pUpdatedSection = (PIMAGE_SECTION_HEADER)new char[dwSectionHeaderSize];memset(pUpdatedSection, 0, dwSectionHeaderSize);CopyMemory(pUpdatedSection, section, sizeof(IMAGE_SECTION_HEADER)*pNTHeader->FileHeader.NumberOfSections);// 定位新节头的位置PIMAGE_SECTION_HEADER pNewSection = pUpdatedSection + pNTHeader->FileHeader.NumberOfSections;// 初始化为0,包括后面的那个为空的节头结构memset(pNewSection, 0, 2 * sizeof(IMAGE_SECTION_HEADER));char szNewSectionName[] = ".NewSec";StringCchCopyA((char*)(pNewSection->Name), 12, szNewSectionName);pNewSection->PointerToRelocations = 0;pNewSection->PointerToLinenumbers = 0;pNewSection->NumberOfRelocations = 0;pNewSection->NumberOfLinenumbers = 0;// 读写包含初始化数据pNewSection->Characteristics = 0xC0000040;// RVA 计算 内存粒度对齐pNewSection->VirtualAddress = Aligment(pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize, dwMemAlignment);// RAW 计算 文件粒度对齐pNewSection->PointerToRawData = Aligment(pLastSection->PointerToRawData + pLastSection->SizeOfRawData, dwFileAlignment);// 下面的操作为写新节头表以及填充节表到第一个段的空白区域DWORD dwMiniPointer = 0;DWORD dwSizeDelta = 0;BOOL  bNeedModify = FALSE;for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++){if (pUpdatedSection[i].Name){if (0 == dwMiniPointer)dwMiniPointer = (DWORD)(pUpdatedSection[i].PointerToRawData);if ((pUpdatedSection[i].PointerToRawData) < dwMiniPointer)dwMiniPointer = (DWORD)pUpdatedSection[i].PointerToRawData;}}DWORD dwTmp = (pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) + dwSectionHeaderSize);// 写入文件后,没有超过第一个段的开始if (dwMiniPointer >= dwTmp){dwSizeDelta = dwMiniPointer - dwTmp;}// 超过了else{// 因为dwSizeDelta为需要填充的零的字节数,如果刚好与文件对齐粒度对齐,不需要填充dwSizeDelta = dwTmp % (pNTHeader->OptionalHeader.FileAlignment);if (dwSizeDelta != 0)dwSizeDelta = pNTHeader->OptionalHeader.FileAlignment - dwSizeDelta;else dwSizeDelta = 0;// 指示每个节表的文件偏移需要重新计算bNeedModify = TRUE;}dwMiniPointer = dwTmp;dwMiniPointer += dwSizeDelta;if (bNeedModify){for (int i = 1; i < (UINT)(pNTHeader->FileHeader.NumberOfSections+1); i++){if (1 != i){pUpdatedSection[i].PointerToRawData =pUpdatedSection[i - 1].PointerToRawData +pUpdatedSection[i - 1].SizeOfRawData;}else{pUpdatedSection[i].PointerToRawData = dwMiniPointer;}}}// 实际数据大小pNewSection->Misc.VirtualSize = sizeof(INJECT_STRUCT) + dwIATDESCSize;// 实际数据大小 文件粒度对齐pNewSection->SizeOfRawData = Aligment(sizeof(INJECT_STRUCT) + dwIATDESCSize,dwFileAlignment);// 写入新节表 (旧的+新的+{0}结构结尾)WriteFile(hTargetFile, pUpdatedSection, dwSectionHeaderSize, &dwReturnSize, NULL);// 写入空白数据 (满足文件粒度)WriteFile(hTargetFile, pDelta, dwSizeDelta, &dwReturnSize, NULL);////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////写入每个节的内容for (int i = 0; i < (UINT)(pNTHeader->FileHeader.NumberOfSections); i++){//SizeOfRawData 文件是按照这个值对齐的 VirtualSize反而是真实大小。这个一定要理解//就算是内存映射PE之后,整个节的大小也是SizeOfRawData,但是实际有数据的大小是VirtualSize。此处不理解一定要停。WriteFile(hTargetFile, (LPVOID)(ulBaseAddress + section[i].PointerToRawData), section[i].SizeOfRawData, &dwReturnSize, NULL);}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 写入最后一个节的内容PIMAGE_IMPORT_DESCRIPTOR pImportDescVector =(PIMAGE_IMPORT_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwIATDESCSize);if (pImportDescVector == NULL){printf("HeapAlloc() failed. --err: %d/n", GetLastError());return -1;}memset(pImportDescVector, 0, dwIATDESCSize);CopyMemory(pImportDescVector + 1, pImportDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nImportDllCount + 1));// 新导入的DLL 的顺序比较靠前// OriginalThunk {0}// FirstThunk {0}INJECT_STRUCT iatTableContent = {0};StringCchCopyA(iatTableContent.szDllName, MAX_PATH, szDllName);StringCchCopyA((char*)(iatTableContent.ImportByName.Name), 64, "HelloShine");//Dll中写一个按名称导出的导出函数,如果没有导出函数,检验应该是会出错的。iatTableContent.ImportByName.Hint = 0;pImportDescVector->FirstThunk = (LONG_PTR)pNewSection->VirtualAddress + dwIATDESCSize + offsetof(INJECT_STRUCT, FirstThunk);pImportDescVector->OriginalFirstThunk = (LONG_PTR)pNewSection->VirtualAddress + dwIATDESCSize + offsetof(INJECT_STRUCT, OriginalThunk);pImportDescVector->Name = (LONG_PTR)pNewSection->VirtualAddress + dwIATDESCSize + offsetof(INJECT_STRUCT, szDllName);pImportDescVector->TimeDateStamp = 0;// 没有绑定pImportDescVector->ForwarderChain = -1;// 没有转发iatTableContent.FirstThunk.u1.Function = (LONG_PTR)pNewSection->VirtualAddress + dwIATDESCSize + offsetof(INJECT_STRUCT, ImportByName);iatTableContent.OriginalThunk.u1.AddressOfData = (LONG_PTR)pNewSection->VirtualAddress + dwIATDESCSize + offsetof(INJECT_STRUCT, ImportByName);// 在新节上写入新的导入表内容DWORD dwRetSize = 0;WriteFile(hTargetFile, pImportDescVector, dwIATDESCSize, &dwRetSize, NULL);WriteFile(hTargetFile, &iatTableContent, sizeof(INJECT_STRUCT), &dwRetSize, NULL);// 填充文件最后的字节,满足文件粒度dwSizeDelta = Aligment(dwIATDESCSize + sizeof(INJECT_STRUCT), dwFileAlignment) - dwIATDESCSize + sizeof(INJECT_STRUCT);WriteFile(hTargetFile, pDelta, dwSizeDelta, NULL, NULL);///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 修正SizeOfHeadersDWORD dwBuffer = dwMiniPointer;// PE头大小重造,即段起始地址的最小值DWORD lDistanceToMove = (ULONG_PTR)(&(pNTHeader->OptionalHeader.SizeOfHeaders)) - ulBaseAddress;SetFilePointer(hTargetFile, lDistanceToMove, NULL, FILE_BEGIN);WriteFile(hTargetFile, (PVOID)&dwBuffer, 4, NULL, NULL);//释放内存,完成操作,别忘了之前的文件映射都还没释放。HeapFree(GetProcessHeap(), 0, pImportDescVector);delete []pDelta;CloseHandle(hTargetFile);return TRUE;}

0 0