移动重定向表的步骤:
* 在这里,我们要根据以下步骤移动
* 1.先复制导出表的结构
* 2.移动Functions表中的数据
* 3.移动序号表中的数据
* 4.移动name表中的数据
* 5.修复name表中的数据和指向
* 6.修复序号表和Functions的指向
移动之前:
移动之后:
代码如下:
// 滴水三期3.26.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)->Misc.VirtualSize) {
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;
}
//要移动导出表,需要新增一个节,因此,我们需要先给dll文件新增加一个节
void MoveExportToNewSection(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);
//添加一个节,在这里,我们添加节就不复制添加了,根据实际大小来添加
DWORD exportTableSize = fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
cout << "节表数量:" << fileFileHeader->NumberOfSections << endl;
//得到大小后,我们需要判断节后面的空间有没有给我们留下80个空白字节,如果没有,我们需要考虑上移NT头,或者扩大最后一个节。
//这个lastSection是我们需要添加的节,不要和文件本身最后一个节搞混了
//这里需要把转换成char*类型,因为char*类型+1才是真的加1
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 = exportTableSize;
//修改对齐后的大小为适当的大小,需要满足对齐后的大小
llastSectionHeader->SizeOfRawData = AlignUp(exportTableSize, fileOptionHeader->SectionAlignment);
cout << "lastSectionHeader->SizeOfRawData:" <<hex<< lastSectionHeader->SizeOfRawData<<"==="<<AlignUp(lastSectionHeader->SizeOfRawData, fileOptionHeader->SectionAlignment) << endl;
//修改VirtualAddress的大小,要满足内存对齐
llastSectionHeader->VirtualAddress = lastSectionHeader->VirtualAddress + AlignUp(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);
//修改节表数量
fileFileHeader->NumberOfSections = fileFileHeader->NumberOfSections + 1;
//节表修改完毕,可以开始移动
/*
* 在这里,我们要根据以下步骤移动
* 1.先复制导出表的结构
* 2.移动Functions表中的数据
* 3.移动序号表中的数据
* 4.移动name表中的数据
* 5.修复name表中的数据和指向
* 6.修复序号表和Functions的指向
*/
//在之前,先把添加的节内容设置为0
memset(llastSectionHeader->PointerToRawData + fileBuffer, 0, 0x1000);
//1.先复制导出表的结构
/*
*
*/
PDWORD copyBegin = (PDWORD)(llastSectionHeader->PointerToRawData + fileBuffer);
DWORD exportTableFOA = RVA_TO_FOA(fileBuffer, fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)(exportTableFOA + fileBuffer);
memcpy(copyBegin, exportTable, sizeof(IMAGE_EXPORT_DIRECTORY));
fileOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = llastSectionHeader->VirtualAddress;
/*
* 2.移动Functions表中的数据
*/
DWORD addressOfFunctionsFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfFunctions);
PDWORD addressOfFunctions = (PDWORD)(addressOfFunctionsFOA + fileBuffer);
PDWORD copyFunctionsBegin = copyBegin + sizeof(IMAGE_EXPORT_DIRECTORY);
memcpy(copyFunctionsBegin, addressOfFunctions, exportTable->NumberOfFunctions * 4);
/*
* 3. 移动序号表中的数据
*/
DWORD addressOfNameOrdinalsFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfNameOrdinals);
PDWORD addressOfNameOrdinals = (PDWORD)(addressOfNameOrdinalsFOA + fileBuffer);
PDWORD copyOrdinalsBegin = copyFunctionsBegin + exportTable->NumberOfFunctions * 4;
memcpy(copyOrdinalsBegin, addressOfNameOrdinals, exportTable->NumberOfFunctions * 2);
/*
* 4.移动name表中的数据
*/
DWORD addressOfNamesFOA = RVA_TO_FOA(fileBuffer, exportTable->AddressOfNames);
PDWORD addressOfNames = (PDWORD)(addressOfNamesFOA + fileBuffer);
PDWORD copyNamesBegin = copyOrdinalsBegin + exportTable->NumberOfFunctions * 2;
memcpy(copyNamesBegin, addressOfNames, exportTable->NumberOfFunctions * 4);
/*
* 5.修复name表中的数据和指向
* 先修复RVA的指向
*/
PIMAGE_EXPORT_DIRECTORY newExportTable = (PIMAGE_EXPORT_DIRECTORY)copyBegin;
newExportTable->AddressOfNames = FOA_TO_RVA(fileBuffer, (long long)copyNamesBegin - (long long)fileBuffer);
PDWORD copyNamesEnd = copyNamesBegin + exportTable->NumberOfFunctions * 4;
//修复完毕之后,开始复制名字,然后修改addressOfNames中的数据
int tmp = 0;
for (int i = 0; i < exportTable->NumberOfNames; i++) {
PDWORD newExportTalbe = (PDWORD)(FOA_TO_RVA(fileBuffer, newExportTable->AddressOfNames) + fileBuffer);
char* name = (char* )(RVA_TO_FOA(fileBuffer,*(addressOfNames + i)) + fileBuffer);
//这里不要忘了,c++字符串是结尾,所以要+1,而为了进行内存对齐,所以要看看他占几个4字节。
int result = (strlen(name) + 4) / 4;
PDWORD copyRNameEnd = copyNamesEnd + tmp;
memcpy(copyRNameEnd, name, result * 4);
tmp = result + tmp;
DWORD copyRNameEndRVA = FOA_TO_RVA(fileBuffer, (long long)copyRNameEnd - (long long)fileBuffer);
*(copyNamesBegin+i) = copyRNameEndRVA;
}
//名称复制好了之后,开始存盘
FILE* newFile = NULL;
errno_t newErr = fopen_s(&newFile, "C:\Users\zyjsuper\Desktop\test\777.dll", "wb");
if (newErr != 0) {
std::cout << "newFile创建失败!" << std::endl;
fclose(newFile);
fclose(file);
free(fileBuffer);
return;
}
fwrite(fileBuffer, 1, fileSize + 0x200, newFile);
std::cout << "写入成功!" << std::endl;
fclose(newFile);
}
free(fileBuffer);
fclose(file);
}
int main()
{
char fileName[] = "C:\Users\zyjsuper\Desktop\test\MyDll.dll";
MoveExportToNewSection(fileName);
}
原文始发于微信公众号(loochSec):PE逆向-移动重定向表
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论