导出表中最重要的是三张表AddressOfFunctions
、AddressOfNames
、AddressOfNameOrdinals
,分别存储了函数地址,函数名称,函数序号,前文实现了按照函数名称查找以及按照序号查找
导出函数名称表的移动是一个难点,导出函数名称表中存储的导出函数名称偏移地址RVA,需要根据地址再进行访问函数名称。整体步骤如下:
AddressOfFunction(size:NumberOfFunctions * 4)
到新增的节AddressOfNameOrdinals(size:NumberOfNames * 2)
到新增的节中AddressOfNames(szie:NumberOfNames * 4)
到新增的节中AddressOfNames
表中对应的所有地址,及其函数名,移动字符串时也需要注意大小\0结尾(每复制一个函数名就要计算偏移RVA添加到函数名称表的地址中)AddressOfFunctions
、AddressOfNameOrdinals
、AddressOfNames
地址,其他的参数不影响运行VirtualAddress
指向新的导出表的地址
自己编写一个最简单的DLL文件
xxxxxxxxxx
int __stdcall Plus(int x, int y)
{
return x + y;
}
xxxxxxxxxx
extern "C" _declspec(dllexport) int __stdcall Plus(int x,int y);
生成DLL后进行测试
xxxxxxxxxx
int main()
{
LPSTR SRC = (LPSTR)"TestDLL.dll";
LPSTR DEST = (LPSTR)"TestDLL1.dll";
LPVOID SRCBuffer = NULL;
DWORD size = ReadPEFile(SRC, &SRCBuffer);
LPVOID DESTBuffer = NULL;
DWORD newSize = MoveExportTableToNewSection(SRCBuffer,&DESTBuffer,size);
MemeryToFile(DESTBuffer, newSize, DEST);
typedef int(__stdcall* lpPlus)(int, int);
lpPlus myPlus = NULL;
// 测试两个DLL
HINSTANCE hModule = LoadLibrary(L"TestDLL1.dll");
myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8");
if (!myPlus)
{
printf("出现错误\n");
return 0;
}
int a = 0;
a = myPlus(10, 2);
printf("10+2=%d\n", a);
printf("导出函数可以使用,说明移动导出表成功\n");
getchar();
return 0;
}
发现新DLL可以正常运行
之前的文章中提到过新增节的内容,这里重新写一下相关的内容
定义一个新增节的函数,第一个参数是原文件指针;第二个参数是修改后的文件指针,一个LPVOID
指针;第三个参数是原文件的大小,最后一个参数是新增节需要的大小
xxxxxxxxxx
DWORD AddSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize);
复制一份原来的文件
xxxxxxxxxx
LPVOID pTempFileBuffer = malloc(dwFileBufferSize);
if (!pTempFileBuffer)
{
return 0, 0, 0, 0;
}
memcpy(pTempFileBuffer, pFileBuffer, dwFileBufferSize);
pFileBuffer = pTempFileBuffer;
解析头部
xxxxxxxxxx
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
拿到最后一节和新节的节表结构体指针
xxxxxxxxxx
DWORD pNumberOfSections = pPEHeader->NumberOfSections;
PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;
PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + pNumberOfSections;
之前提到过只有节表剩余大小大于80字节才可以新增,因为一个节表大小为40,需要流出零一份全0节表来表示节表完全结束,因此一共需要80字节大小,当不足80字节可以整体上提PE头部
xxxxxxxxxx
if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader)
{
printf("没有足够的80字节插入新节表\n");
free(pTempFileBuffer);
return 0;
}
构造新节表的结构体,节表的Name
要求是8个字节长度
xxxxxxxxxx
IMAGE_SECTION_HEADER newSectionHeader;
memcpy(newSectionHeader.Name, ".newsec", 8);
新节的对齐前大小按照内存对齐来对齐,认为所有部分都是有效内存
xxxxxxxxxx
newSectionHeader.Misc.VirtualSize = AlignLength(dwNewSectionSize, pOptionHeader->SectionAlignment);
新节的VA
是上一节的VA
加上一节自身的大小(确保对齐)
xxxxxxxxxx
newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress +
AlignLength(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);
新节的SizeOFRawData
按照文件对齐来对齐
xxxxxxxxxx
newSectionHeader.SizeOfRawData = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);
新节的PointerToRawData
按照上一节的PointerToRawData
加SizeOfRawData
对齐
xxxxxxxxxx
newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
其他属性不重要
xxxxxxxxxx
newSectionHeader.PointerToRelocations = 0;
newSectionHeader.PointerToLinenumbers = 0;
newSectionHeader.NumberOfRelocations = 0;
newSectionHeader.NumberOfLinenumbers = 0;
newSectionHeader.Characteristics = 0x60000020;
给新增节分配内存并初始化
xxxxxxxxxx
*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);
memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);
memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData);
刷新指针
xxxxxxxxxx
pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
pNumberOfSections = pPEHeader->NumberOfSections;
pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;
pNewSectionHeader = pSectionHeader + pNumberOfSections;
节的数量加一,且SizeOfImage
参数增加新节按照内存对齐的大小
xxxxxxxxxx
pPEHeader->NumberOfSections += 1;
pOptionHeader->SizeOfImage += AlignLength(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);
拷贝新节,返回新节大小
xxxxxxxxxx
memcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));
free(pTempFileBuffer);
return dwFileBufferSize + newSectionHeader.SizeOfRawData;
解析头部并拿到导出表的三个主要表
xxxxxxxxxx
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
PDWORD pAddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfFunctions));
PWORD pAddressOfNameOrdinals = (PWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNameOrdinals));
PDWORD pAddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNames));
需要计算新增节的大小,并传递到新增节函数进行新增
首先是函数地址表四字节,函数名和序号表分别是两和四字节
xxxxxxxxxx
DWORD dwNewSectionSize = 0;
dwNewSectionSize += pExportTable->NumberOfFunctions * 4;
dwNewSectionSize += pExportTable->NumberOfNames * (2 + 4);
统计所有函数名字符串大小,注意需要多加1因为字符串以00
结尾
xxxxxxxxxx
for (i = 0; i < pExportTable->NumberOfNames; i++)
{
LPCSTR lpszFuncName = (LPCSTR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pAddressOfNames[i]));
dwNewSectionSize += strlen(lpszFuncName) + 1;
}
计算并加入导出表结构的大小,最后按照文件对齐来对齐,得到大小作为新增节参数
xxxxxxxxxx
dwNewSectionSize += sizeof(_IMAGE_EXPORT_DIRECTORY);
dwNewSectionSize = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);
DWORD dwNewBufferSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileBufferSize, dwNewSectionSize);
修改新增节属性为可读,含已初始化数据
xxxxxxxxxx
pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0x40000040;
计算得到新文件Buffer
的导出表地址
xxxxxxxxxx
pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)*pNewFileBuffer +
RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
pAddressOfFunctions = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfFunctions));
pAddressOfNameOrdinals = (PWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNameOrdinals));
pAddressOfNames = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNames));
复制三张子表信息到新节,并保留新子表的指针
xxxxxxxxxx
LPVOID pInsert = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);
memcpy(pInsert, pAddressOfFunctions, 4 * pExportTable->NumberOfFunctions);
pAddressOfFunctions = (PDWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfFunctions);
memcpy(pInsert, pAddressOfNameOrdinals, 2 * pExportTable->NumberOfNames);
pAddressOfNameOrdinals = (PWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 2 * pExportTable->NumberOfNames);
memcpy(pInsert, pAddressOfNames, 4 * pExportTable->NumberOfNames);
pAddressOfNames = (PDWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfNames);
遍历函数名称表,字符串地址转为FOA
后获得,复制字符串长度+1 (00) 到新节中,新节指针偏移增加继续遍历
xxxxxxxxxx
for (i = 0; i < pExportTable->NumberOfNames; i++)
{
LPCSTR lpszFuncName = (LPCSTR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pAddressOfNames[i]));
memcpy(pInsert, lpszFuncName, strlen(lpszFuncName) + 1);
pAddressOfNames[i] = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);
pInsert = (LPVOID)((DWORD)pInsert + strlen(lpszFuncName) + 1);
}
复制节表,修改三张子表的RVA
xxxxxxxxxx
memcpy(pInsert, pExportTable, sizeof(_IMAGE_EXPORT_DIRECTORY));
pExportTable = (PIMAGE_EXPORT_DIRECTORY)pInsert;
pExportTable->AddressOfFunctions = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfFunctions - (DWORD)*pNewFileBuffer);
pExportTable->AddressOfNameOrdinals = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNameOrdinals - (DWORD)*pNewFileBuffer);
pExportTable->AddressOfNames = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNames - (DWORD)*pNewFileBuffer);
修改数据目录的导出表指向新节的导出表
xxxxxxxxxx
pOptionHeader->DataDirectory[0].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, (DWORD)pExportTable - (DWORD)*pNewFileBuffer);
xxxxxxxxxx
DWORD AddSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize)
{
LPVOID pTempFileBuffer = malloc(dwFileBufferSize);
if (!pTempFileBuffer)
{
return 0, 0, 0, 0;
}
memcpy(pTempFileBuffer, pFileBuffer, dwFileBufferSize);
pFileBuffer = pTempFileBuffer;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
DWORD pNumberOfSections = pPEHeader->NumberOfSections;
PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;
PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + pNumberOfSections;
DWORD newFileBufferSize = 0;
if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader)
{
free(pTempFileBuffer);
return 0;
}
IMAGE_SECTION_HEADER newSectionHeader;
memcpy(newSectionHeader.Name, ".newsec", 8);
newSectionHeader.Misc.VirtualSize = AlignLength(dwNewSectionSize, pOptionHeader->SectionAlignment);
newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress +
AlignLength(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);
newSectionHeader.SizeOfRawData = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);
newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
newSectionHeader.PointerToRelocations = 0;
newSectionHeader.PointerToLinenumbers = 0;
newSectionHeader.NumberOfRelocations = 0;
newSectionHeader.NumberOfLinenumbers = 0;
newSectionHeader.Characteristics = 0x60000020;
*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);
memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);
memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData);
pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
pNumberOfSections = pPEHeader->NumberOfSections;
pLastSectionHeader = pSectionHeader + pNumberOfSections - 1;
pNewSectionHeader = pSectionHeader + pNumberOfSections;
pPEHeader->NumberOfSections += 1;
pOptionHeader->SizeOfImage += AlignLength(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);
memcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));
free(pTempFileBuffer);
return dwFileBufferSize + newSectionHeader.SizeOfRawData;
}
DWORD MoveExportTableToNewSection(LPVOID pFileBuffer, LPVOID* pNewFileBuffer, DWORD dwFileBufferSize)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer +
RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
PDWORD pAddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfFunctions));
PWORD pAddressOfNameOrdinals = (PWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNameOrdinals));
PDWORD pAddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportTable->AddressOfNames));
DWORD dwNewSectionSize = 0;
dwNewSectionSize += pExportTable->NumberOfFunctions * 4;
dwNewSectionSize += pExportTable->NumberOfNames * (2 + 4);
for (DWORD i = 0; i < pExportTable->NumberOfNames; i++)
{
LPCSTR lpszFuncName = (LPCSTR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pAddressOfNames[i]));
dwNewSectionSize += strlen(lpszFuncName) + 1;
}
dwNewSectionSize += sizeof(_IMAGE_EXPORT_DIRECTORY);
dwNewSectionSize = AlignLength(dwNewSectionSize, pOptionHeader->FileAlignment);
DWORD dwNewBufferSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileBufferSize, dwNewSectionSize);
pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0x40000040;
pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)*pNewFileBuffer +
RvaToFileOffset(*pNewFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
pAddressOfFunctions = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfFunctions));
pAddressOfNameOrdinals = (PWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNameOrdinals));
pAddressOfNames = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pExportTable->AddressOfNames));
LPVOID pInsert = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);
memcpy(pInsert, pAddressOfFunctions, 4 * pExportTable->NumberOfFunctions);
pAddressOfFunctions = (PDWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfFunctions);
memcpy(pInsert, pAddressOfNameOrdinals, 2 * pExportTable->NumberOfNames);
pAddressOfNameOrdinals = (PWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 2 * pExportTable->NumberOfNames);
memcpy(pInsert, pAddressOfNames, 4 * pExportTable->NumberOfNames);
pAddressOfNames = (PDWORD)pInsert;
pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfNames);
for (DWORD i = 0; i < pExportTable->NumberOfNames; i++)
{
LPCSTR lpszFuncName = (LPCSTR)((DWORD)*pNewFileBuffer + RvaToFileOffset(*pNewFileBuffer, pAddressOfNames[i]));
memcpy(pInsert, lpszFuncName, strlen(lpszFuncName) + 1);
pAddressOfNames[i] = FoaToImageOffset(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);
pInsert = (LPVOID)((DWORD)pInsert + strlen(lpszFuncName) + 1);
}
memcpy(pInsert, pExportTable, sizeof(_IMAGE_EXPORT_DIRECTORY));
pExportTable = (PIMAGE_EXPORT_DIRECTORY)pInsert;
pExportTable->AddressOfFunctions = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfFunctions - (DWORD)*pNewFileBuffer);
pExportTable->AddressOfNameOrdinals = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNameOrdinals - (DWORD)*pNewFileBuffer);
pExportTable->AddressOfNames = FoaToImageOffset(*pNewFileBuffer, (DWORD)pAddressOfNames - (DWORD)*pNewFileBuffer);
pOptionHeader->DataDirectory[0].VirtualAddress = FoaToImageOffset(*pNewFileBuffer, (DWORD)pExportTable - (DWORD)*pNewFileBuffer);
return dwNewBufferSize;
}