导入表是WindowsPE文件中的一组数据结构,可执行程序(即EXE文件)被加载到地址空间后,每个导入的DLL模块都有一个对应的导入表,PE加载器会根据导入表来加载进程需要的其他DLL模块。
导入表的数据结构如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk/Characteristics:指向导入表(INT)的RVA(相对虚拟地址)。INT是一个IMAGE_THUNK_DATA结构的数组,数组中的每个元素指向一个IMAGE_IMPORT_BY_NAME结构,INT以元素0结束。
TimeDateStamp:时间戳,可以忽略。
ForwarderChain:如果没有前向引用(forwarders)的话就是-1.
Name:被导入DLL的名字指针,是一个RVA。
FirstThunk:指向导入表(IAT)的RVA。IAT是一个IMAGE_THUNK_DATA结构的数组。
导入表逻辑结构如下:
下面贴出源码:
// INTHook.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<Windows.h>
#include <exception>
#include <iostream>
using namespace std;
BOOL AddImportTable(const WCHAR * wzPEFilePath, char * szInjectDllName, char *szFunctionName);
BOOL AddNewImportDescriptor(const WCHAR * wzPEFilePath, char * szInjectDllName, char *szImportFunctionName);
BOOL AddNewSection(LPCTSTR lpModulePath, DWORD dwNewSectionSize);
DWORD PEAlign(DWORD dwTarNumber, DWORD dwAlignTo);
DWORD RVAToOffset(PIMAGE_NT_HEADERS pImageNTHeader, DWORD dwRVA);
PIMAGE_SECTION_HEADER ImageRVAToSection(PIMAGE_NT_HEADERS pImageNTHeader, DWORD dwRVA);
int main()
{
WCHAR TargetPath[0x20] = {0};
char DllPath[0x20] = "InjectDll.dll";
printf("Please Input Target Full Path:\r\n");
//scanf_s(TargetPath, "%s");
wcin >> TargetPath;
AddImportTable(TargetPath,DllPath, "InjectFunction");
return 0;
}
BOOL AddImportTable(const WCHAR * wzPEFilePath, char * szInjectDllName, char *szFunctionName)
{
BOOL bOk = FALSE;
try
{
//增加一个叫"WINSUN"的节
bOk = AddNewSection(wzPEFilePath, 256);
if (!bOk)
{
MessageBox(NULL, L"Add New Section Fail", L"Error", MB_OK);
return bOk;
}
//增加一个导入表
AddNewImportDescriptor(wzPEFilePath, szInjectDllName, szFunctionName);
}
catch (exception* e)
{
return bOk;
}
return bOk;
}
//
//增加导入表项
//
BOOL AddNewSection(LPCTSTR lpModulePath, DWORD dwNewSectionSize)
{
BOOL bOk = FALSE;
LPVOID lpMemoryModule = NULL;
LPBYTE lpData = NULL;
DWORD dwNewSectionFileSize, dwNewSectionMemorySize;
HANDLE FileHandle = INVALID_HANDLE_VALUE, MappingHandle = INVALID_HANDLE_VALUE;
PIMAGE_NT_HEADERS NtHeader = NULL;
PIMAGE_SECTION_HEADER NewSection = NULL, LastSection = NULL;
printf("[!] AddNewSection Enter!\n");
//TODO:可能还涉及关闭windows文件保护
__try
{
//pe文件映射到内存
FileHandle = CreateFile(
lpModulePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE == FileHandle)
{
printf("[-] AddNewSection CreateFile Fail!\n");
goto _EXIT_;
}
DWORD dwFileLength = GetFileSize(FileHandle, NULL);
//映射PE文件
MappingHandle = CreateFileMapping(FileHandle, NULL, PAGE_READWRITE/* | SEC_IMAGE*/, 0, dwFileLength,L"WINSUN_MAPPING_FILE");
if (NULL == MappingHandle)
{
printf("[-] AddNewSection CreateFileMapping Fail!\n");
goto _EXIT_;
}
lpMemoryModule = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, dwFileLength);
if (NULL == lpMemoryModule)
{
printf("[-] AddNewSection MapViewOfFile Fail!\n");
goto _EXIT_;
}
lpData = (LPBYTE)lpMemoryModule;
//判断是否是PE文件
if (((PIMAGE_DOS_HEADER)lpData)->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("[-] AddNewSection PE Header MZ error!\n");
goto _EXIT_;
}
NtHeader = (PIMAGE_NT_HEADERS)(lpData + ((PIMAGE_DOS_HEADER)(lpData))->e_lfanew);
if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("[-] AddNewSection PE Header PE Error!\n");
goto _EXIT_;
}
//判断是否可以增加一个新节
if (((NtHeader->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER)) > (NtHeader->OptionalHeader.SizeOfHeaders))
{
printf("[-] AddNewSection Cannot Add A New Section!\n");
goto _EXIT_;
}
NewSection = (PIMAGE_SECTION_HEADER)(NtHeader + 1) + NtHeader->FileHeader.NumberOfSections;
LastSection = NewSection - 1;
DWORD rSize, vSize, rOffset, vOffset;
//对齐偏移和RVA
rSize = PEAlign(dwNewSectionSize,
NtHeader->OptionalHeader.FileAlignment);
rOffset = PEAlign(LastSection->PointerToRawData + LastSection->SizeOfRawData,
NtHeader->OptionalHeader.FileAlignment);
vSize = PEAlign(dwNewSectionSize,
NtHeader->OptionalHeader.SectionAlignment);
vOffset = PEAlign(LastSection->VirtualAddress + LastSection->Misc.VirtualSize,
NtHeader->OptionalHeader.SectionAlignment);
//填充新节表
memcpy(NewSection->Name, "WINSUN", strlen("WINSUN"));
NewSection->VirtualAddress = vOffset;
NewSection->PointerToRawData = rOffset;
NewSection->Misc.VirtualSize = vSize;
NewSection->SizeOfRawData = rSize;
NewSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
//修改IMAGE_NT_HEADERS,增加新节表
NtHeader->FileHeader.NumberOfSections++;
NtHeader->OptionalHeader.SizeOfImage += vSize;
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
//增加新节到文件尾部
DWORD dwWriteBytes;
SetFilePointer(FileHandle, 0, 0, FILE_END);
PBYTE pbNewSectionContent = new BYTE[rSize];
ZeroMemory(pbNewSectionContent, rSize);
bOk = WriteFile(FileHandle, pbNewSectionContent, rSize, &dwWriteBytes, NULL);
if (!bOk)
{
MessageBox(NULL, L"新增节失败", L"Error", MB_OK);
goto _EXIT_;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("[-] AddImportTableItem Exception!\n");
return false;
}
printf("[!] AddNewSection Exit!\n");
bOk = true;
_EXIT_:
if (FileHandle)
{
CloseHandle(FileHandle);
}
if (lpMemoryModule)
{
UnmapViewOfFile(lpMemoryModule);
}
if (MappingHandle)
{
CloseHandle(MappingHandle);
}
return true;
}
//内存对齐
DWORD PEAlign(DWORD dwTarNumber, DWORD dwAlignTo)
{
return(((dwTarNumber + dwAlignTo - 1) / dwAlignTo)*dwAlignTo);
}
//增加一个导入表
BOOL AddNewImportDescriptor(const WCHAR * wzPEFilePath, char * szInjectDllName, char *szImportFunctionName)
{
BOOL bOk = FALSE;
LPVOID lpMemoryModule = NULL;
LPBYTE lpData = NULL;
DWORD dwNewSecFileSize, dwNewSecMemSize;
HANDLE FileHandle = INVALID_HANDLE_VALUE, MappingHandle = INVALID_HANDLE_VALUE;
PIMAGE_NT_HEADERS NtHeader = NULL;
PIMAGE_IMPORT_DESCRIPTOR ImportTable = NULL;
PIMAGE_SECTION_HEADER SectionHeader = NULL;
__try
{
//pe文件映射到内存
FileHandle = CreateFile(
wzPEFilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE == FileHandle)
{
printf("[-] AddNewImportDescriptor CreateFile fail!\n");
goto _EXIT_;
}
DWORD dwFileLength = GetFileSize(FileHandle, NULL);
MappingHandle = CreateFileMapping(FileHandle, NULL, PAGE_READWRITE/* | SEC_IMAGE*/, 0, dwFileLength,L"WINSUN_MAPPING_FILE");
if (NULL == MappingHandle)
{
printf("[-] AddNewImportDescriptor CreateFileMapping fail!\n");
goto _EXIT_;
}
lpMemoryModule = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, dwFileLength);
if (NULL == lpMemoryModule)
{
printf("[-] AddNewImportDescriptor MapViewOfFile fail!\n");
goto _EXIT_;
}
lpData = (LPBYTE)lpMemoryModule;
//判断是否是PE
if (((PIMAGE_DOS_HEADER)lpData)->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("[-] AddNewImportDescriptor PE Header MZ error!\n");
goto _EXIT_;
}
NtHeader = (PIMAGE_NT_HEADERS)(lpData + ((PIMAGE_DOS_HEADER)(lpData))->e_lfanew);
if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("[-] AddNewImportDescriptor PE Header PE error!\n");
goto _EXIT_;
}
ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(lpData + RVAToOffset(NtHeader, NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
BOOL bBoundImport = FALSE;
if (ImportTable->Characteristics == 0 && ImportTable->FirstThunk != 0)
{
bBoundImport = TRUE;
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
}
SectionHeader = (PIMAGE_SECTION_HEADER)(NtHeader + 1) + NtHeader->FileHeader.NumberOfSections - 1;
PBYTE pbNewSection = SectionHeader->PointerToRawData + lpData;
int i = 0;
while (ImportTable->FirstThunk != 0)
{
memcpy(pbNewSection, ImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR));
ImportTable++;
pbNewSection += sizeof(IMAGE_IMPORT_DESCRIPTOR);
i++;
}
memcpy(pbNewSection, (pbNewSection - sizeof(IMAGE_IMPORT_DESCRIPTOR)), sizeof(IMAGE_IMPORT_DESCRIPTOR));
DWORD dwDelt = SectionHeader->VirtualAddress - SectionHeader->PointerToRawData;
//avoid import not need table
PIMAGE_THUNK_DATA pImgThunkData = (PIMAGE_THUNK_DATA)(pbNewSection + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2);
//import dll name
PBYTE pszDllNamePosition = (PBYTE)(pImgThunkData + 2);
memcpy(pszDllNamePosition, szInjectDllName, strlen(szInjectDllName));
pszDllNamePosition[strlen(szInjectDllName)] = 0;
//确定IMAGE_IMPORT_BY_NAM的位置
PIMAGE_IMPORT_BY_NAME pImgImportByName = (PIMAGE_IMPORT_BY_NAME)(pszDllNamePosition + strlen(szInjectDllName) + 1);
//init IMAGE_THUNK_DATA
pImgThunkData->u1.Ordinal = dwDelt + (DWORD)pImgImportByName - (DWORD)lpData;
//init IMAGE_IMPORT_BY_NAME
pImgImportByName->Hint = 1;
memcpy(pImgImportByName->Name, szImportFunctionName, strlen(szImportFunctionName)); //== dwDelt + (DWORD)pszFuncNamePosition - (DWORD)lpData ;
pImgImportByName->Name[strlen(szImportFunctionName)] = 0;
//init OriginalFirstThunk
if (bBoundImport)
{
((PIMAGE_IMPORT_DESCRIPTOR)pbNewSection)->OriginalFirstThunk = 0;
}
else
((PIMAGE_IMPORT_DESCRIPTOR)pbNewSection)->OriginalFirstThunk = dwDelt + (DWORD)pImgThunkData - (DWORD)lpData;
//init FirstThunk
((PIMAGE_IMPORT_DESCRIPTOR)pbNewSection)->FirstThunk = dwDelt + (DWORD)pImgThunkData - (DWORD)lpData;
//init Name
((PIMAGE_IMPORT_DESCRIPTOR)pbNewSection)->Name = dwDelt + (DWORD)pszDllNamePosition - (DWORD)lpData;
//改变导入表
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = SectionHeader->VirtualAddress;
NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (i + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("[-] AddNewImportDescriptor Exception!\n");
return false;
}
_EXIT_:
if (FileHandle)
{
CloseHandle(FileHandle);
}
if (lpMemoryModule)
{
UnmapViewOfFile(lpMemoryModule);
}
if (MappingHandle)
{
CloseHandle(MappingHandle);
}
return true;
}
//
// calulates the Offset from a RVA
// Base - base of the MMF
// dwRVA - the RVA to calculate
// returns 0 if an error occurred else the calculated Offset will be returned
DWORD RVAToOffset(PIMAGE_NT_HEADERS pImageNTHeader, DWORD dwRVA)
{
DWORD _offset;
PIMAGE_SECTION_HEADER section;
section = ImageRVAToSection(pImageNTHeader, dwRVA);//ImageRvaToSection(pimage_nt_headers,Base,dwRVA);
if (section == NULL)
{
return(0);
}
_offset = dwRVA + section->PointerToRawData - section->VirtualAddress;
return(_offset);
}
PIMAGE_SECTION_HEADER ImageRVAToSection(PIMAGE_NT_HEADERS pImageNTHeader, DWORD dwRVA)
{
int i;
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pImageNTHeader + 1);
for (i = 0; i<pImageNTHeader->FileHeader.NumberOfSections; i++)
{
if ((dwRVA >= (pSectionHeader + i)->VirtualAddress) && (dwRVA <= ((pSectionHeader + i)->VirtualAddress + (pSectionHeader + i)->SizeOfRawData)))
{
return ((PIMAGE_SECTION_HEADER)(pSectionHeader + i));
}
}
return(NULL);
}