// 滴水三期3.31导入表注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
using namespace std;
//向上取值函数
size_t AlignUp(size_t value, size_t alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
//得到PE文件大小
long GetSize(char* fileName) {
FILE* file = NULL;
errno_t err = fopen_s(&file, fileName, "rb");
if (err != 0) {
cout << "文件打开失败!" << endl;
fclose(file);
return 0;
}
long fileSize = 0;
fseek(file, 0, SEEK_END);
fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
fclose(file);
return fileSize;
}
//RVA转FOA
DWORD RVA_TO_FOA(char* fileBuffer, long long RVAaddress) {
PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew);
if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) {
cout << "PE的NT头错误!" << endl;
return 0;
}
PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader);
PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader);
PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader);
int sectionNum = 0;
for (int i = 0; i < fileFileHeader->NumberOfSections; i++) {
if (RVAaddress >= (fileSectionHeader + i)->VirtualAddress && RVAaddress < (fileSectionHeader + i)->VirtualAddress + (fileSectionHeader + i)->SizeOfRawData) {
sectionNum = i;
break;
}
}
long long offsetSize = RVAaddress - (fileSectionHeader + sectionNum)->VirtualAddress;
DWORD fileAdderss = (DWORD)((fileSectionHeader + sectionNum)->PointerToRawData + offsetSize);
//cout << hex <<"RVAaddress:"<< RVAaddress<<"转换====》" << "文件地址:" << fileAdderss << endl;
return fileAdderss;
}
//FOA转RVA
//这里的FOA要减去fileBuffer
DWORD FOA_TO_RVA(char* fileBuffer, long long FOAaddress) {
PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew);
if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) {
cout << "PE的NT头错误X!" << endl;
return 0;
}
PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader);
PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader);
PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader);
int sectionNum = 0;
for (int i = 0; i < fileFileHeader->NumberOfSections; i++) {
if (FOAaddress >= (fileSectionHeader + i)->PointerToRawData && FOAaddress < (fileSectionHeader + i)->PointerToRawData + (fileSectionHeader + i)->SizeOfRawData) {
sectionNum = i;
break;
}
}
long long offsetSize = FOAaddress - (fileSectionHeader + sectionNum)->PointerToRawData;
DWORD fileAdderss = (DWORD)((fileSectionHeader + sectionNum)->VirtualAddress + offsetSize);
//cout << hex <<"RVAaddress:"<< RVAaddress<<"转换====》" << "文件地址:" << fileAdderss << endl;
return fileAdderss;
}
//导入表注入,我们为了方便和一劳永逸,直接移动导入表到新节
void ImportTableInjectDll(char* fileName) {
//先得到大小
long fileSize = GetSize(fileName);
//得到大小后,再申请内存 这里为了简单,我就不重新申请了内存了,所以,这里就直接将后面需要扩大的节的大小写进来
char* fileBuffer = (char*)malloc(fileSize + 0x1000);
if (!fileBuffer) {
cout << "RVA_TO_FOA---->fileBuffer内存申请失败!" << endl;
free(fileBuffer);
return;
}
//如果内存申请成功,那么就要读fileBuffer,读之前,先打开文件
FILE* file = NULL;
errno_t err = fopen_s(&file, fileName, "rb");
if (err != 0) {
cout << "文件打开失败!" << endl;
free(fileBuffer);
fclose(file);
return;
}
//如果打开也没问题,那么就需要真正的开始读了
fread(fileBuffer, 1, fileSize, file);
if ((*(PWORD)fileBuffer) != IMAGE_DOS_SIGNATURE) {
cout << "PE文件格式错误!" << endl;;
free(fileBuffer);
fclose(file);
return;
}
PIMAGE_DOS_HEADER fileDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS fileNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileBuffer + fileDosHeader->e_lfanew);
if (fileNTHeader->Signature != IMAGE_NT_SIGNATURE) {
cout << "PE的NT头错误!" << endl;
free(fileBuffer);
fclose(file);
return;
}
PIMAGE_FILE_HEADER fileFileHeader = &(fileNTHeader->FileHeader);
PIMAGE_OPTIONAL_HEADER fileOptionHeader = &(fileNTHeader->OptionalHeader);
PIMAGE_SECTION_HEADER fileSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)fileOptionHeader + fileFileHeader->SizeOfOptionalHeader);
//添加一个节,在这里,我们添加节就不复制添加了,根据实际大小来添加
int importTableSize = fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
char* lastSection = (((char*)(fileSectionHeader + (fileFileHeader->NumberOfSections - 1))) + 40);
bool isZero = true;
for (int i = 0; i < 80; i++) {
if (*(lastSection + i) != 0x00) {
isZero = false;
break;
}
}
//isZero还为真时,就说明有两个节表80个字节的空间留给我们
if (isZero) {
cout << "可以添加一个节表!" << endl;
//这里解释一下,因为是指针,所以根据指针的特性,只需要从第一个节复制到最后一个节,大小是40
memcpy(lastSection, fileSectionHeader, 40);
//复制成功后,修改节表的属性
//为了方便理解,这里直接设置一个指针变量指向添加的节表,也可以不设置,都以fileSectionHeader+fileFileHeader->NumberOfSections这种解释
PIMAGE_SECTION_HEADER llastSectionHeader = (PIMAGE_SECTION_HEADER)((char*)(fileSectionHeader + (fileFileHeader->NumberOfSections - 1)) + 40);
PIMAGE_SECTION_HEADER lastSectionHeader = fileSectionHeader + (fileFileHeader->NumberOfSections - 1);
//修改内存大小为适合的大小
llastSectionHeader->Misc.VirtualSize = importTableSize;
//修改对齐后的大小为适当的大小,需要满足对齐后的大小
llastSectionHeader->SizeOfRawData = AlignUp(importTableSize, fileOptionHeader->SectionAlignment);
llastSectionHeader->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
cout << "lastSectionHeader->SizeOfRawData:" << hex << lastSectionHeader->SizeOfRawData << "===" << AlignUp(lastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment) << endl;
//修改VirtualAddress的大小,要满足内存对齐
llastSectionHeader->VirtualAddress = AlignUp(lastSectionHeader->VirtualAddress + lastSectionHeader->SizeOfRawData,fileOptionHeader->SectionAlignment);
//修改VirtualAddress的大小,要满足磁盘对齐
llastSectionHeader->PointerToRawData = lastSectionHeader->PointerToRawData + lastSectionHeader->SizeOfRawData;
//节表的其他属性不需要修改,现在再修改节表数量
fileFileHeader->NumberOfSections = fileFileHeader->NumberOfSections + 1;
//修改SizeOfImage,SizeOfImage等于原来的sizeOfImage加上新添加的节的对齐后的大小,满足内存对齐
fileOptionHeader->SizeOfImage = AlignUp(fileOptionHeader->SizeOfImage + llastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment);
//节表修改完毕,可以开始注入
//在之前,先把添加的节内容设置为0
memset(llastSectionHeader->PointerToRawData + fileBuffer, 0, 0x1000);
//设置为0之后,开始移动导出表,这里需要注意一下,导出表
PDWORD copyBeginDST = (PDWORD)(llastSectionHeader->PointerToRawData + (DWORD_PTR)fileBuffer);
DWORD exportFOA = RVA_TO_FOA(fileBuffer,fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
cout << "exportFOA:" << exportFOA << endl;
PDWORD copyBeginSRC =(PDWORD) ((DWORD_PTR)fileBuffer + exportFOA);
//复制
memcpy(copyBeginDST, copyBeginSRC, importTableSize);
//因为都是0,直接在后面添加一个就行
PIMAGE_IMPORT_DESCRIPTOR exportTable = (PIMAGE_IMPORT_DESCRIPTOR)copyBeginSRC;
memcpy((char*)copyBeginDST + importTableSize - sizeof(IMAGE_IMPORT_DESCRIPTOR), copyBeginSRC, sizeof(IMAGE_IMPORT_DESCRIPTOR));
//添加完后,在修改里面的值
fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = llastSectionHeader->VirtualAddress;
PIMAGE_IMPORT_DESCRIPTOR newLastExport = (PIMAGE_IMPORT_DESCRIPTOR)((char*)copyBeginDST + importTableSize - sizeof(IMAGE_IMPORT_DESCRIPTOR));
newLastExport->OriginalFirstThunk = llastSectionHeader->VirtualAddress + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR);
PDWORD newINT = (PDWORD)(fileBuffer + llastSectionHeader->PointerToRawData + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR));
*newINT = llastSectionHeader->VirtualAddress + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR);
auto tmp = RVA_TO_FOA(fileBuffer, llastSectionHeader->VirtualAddress);
PIMAGE_IMPORT_BY_NAME newINTName =(PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)fileBuffer+ RVA_TO_FOA(fileBuffer, *newINT));
char functionName[] = "ExportFunction";
size_t functionNameLength = strlen(functionName) + 1;
cout << "functionNameLength:" << hex<<functionNameLength << endl;
memcpy(newINTName->Name, functionName, functionNameLength);
newLastExport->FirstThunk = llastSectionHeader->VirtualAddress + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR);
PDWORD newFirstThunk = (PDWORD)(fileBuffer + llastSectionHeader->PointerToRawData + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR));
*newFirstThunk = *newINT;
//修改名称
char name[] = "InjectDll.dll";
size_t nameLength = strlen(name) + 1;
memcpy(fileBuffer + llastSectionHeader->PointerToRawData + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR)+ sizeof(IMAGE_IMPORT_DESCRIPTOR), name, nameLength);
newLastExport->Name = llastSectionHeader->VirtualAddress + importTableSize + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR) + sizeof(IMAGE_IMPORT_DESCRIPTOR)+ sizeof(IMAGE_IMPORT_DESCRIPTOR);
fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = importTableSize + 20;
}
//好了之后,开始存盘
FILE* newFile = NULL;
errno_t newErr = fopen_s(&newFile, "C:\Users\zyjsuper\Desktop\test\IPMsgdll.exe", "wb");
if (newErr != 0) {
std::cout << "newFile创建失败!" << std::endl;
fclose(newFile);
fclose(file);
free(fileBuffer);
return;
}
fwrite(fileBuffer, 1, fileSize + 0x1000, newFile);
std::cout << "写入成功!" << std::endl;
fclose(newFile);
free(fileBuffer);
fclose(file);
}
int main()
{
char fileName[] = "C:\Users\zyjsuper\Desktop\test\IPMsg.exe";
//PrintImportTable(fileName);
ImportTableInjectDll(fileName);
cout<<hex<<sizeof(IMAGE_IMPORT_DESCRIPTOR);
}
原文始发于微信公众号(loochSec):PE-导入表注入
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论