目录
合并节与新增节、扩大节有一点区别.合并节如果文件与内存对齐不同需拉伸到内存后进行合并节.
合并节对PE文件中哪些值会有影响?
- IMAGE_FILE_HEADER→NumberOfSections(当前PE文件节的数量,如果更改需要修正此值).
- 第一个节得包含其余所有节的属性.
- 修正第一个节内存中的大小以及文件中的大小.
- 如果文件对齐与内存对齐相等则不需要拉伸即可完成.如果文件对齐与内存对齐不相同必须拉伸到ImageBuffer来处理否则合并节后程序无法运行.
合并节步骤:
- 修改第一个节的内存大小以及文件大小,公式如下:
- MAX = 最后一个节内存中的大小 > 最后一个节文件中的大小 ? 最后一个节内存中的大小 : 最后一个节文件中的大小.
- DWORD dwMax = pSec[pFil->NumberOfSections - 1].Misc.VirtualSize > pSec[pFil->NumberOfSections - 1].SizeOfRawData ? pSec[pFil->NumberOfSections - 1].Misc.VirtualSize : pSec[pFil->NumberOfSections - 1].SizeOfRawData;
- 第一个节内存中的大小 = 文件中的大小 = 最后一个节内存中的起始位置 + MAX - SizeOfHeader(内存对齐后的大小).
- pSec->Misc.VirtualSize = pSec->SizeOfRawData = pSec[pFil->NumberOfSections - 1].VirtualAddress + dwMax - Align(pOpo->SectionAlignment, pOpo->SizeOfHeaders);
- 修正第一个节的属性.
- 修改IMAGE_FILE_HEADER→NumberOfSections为1.
- 抹除其余节数据
手动合并节
通过WinHex工具查看PE文件默认属性
测试文件内存与文件对齐一致(1000h)
1).修改第一个节的内存大小以及文件大小
MAX = 最后一个节内存中的大小 > 最后一个节文件中的大小 ? 最后一个节内存中的大小 : 最后一个节文件中的大小 = 0x8000
第一个节内存中的大小 = 文件中的大小 = 最后一个节内存中的起始位置 + MAX - SizeOfHeader(内存对齐后的大小) = 0x2b000 + 0x8000 - 0x1000 = 0x32000
2).修正第一个节的属性
IMAGE_SECTION_HEADER[0] -> Characteristics = 0x60000020
IMAGE_SECTION_HEADER[1] -> Characteristics = 0x40000040
IMAGE_SECTION_HEADER[2] -> Characteristics = 0xC0000040
IMAGE_SECTION_HEADER[3] -> Characteristics = 0x40000040
所有属性进行OR(或运算)得出结果 = 0xE0000060
3.修改IMAGE_FILE_HEADER→NumberOfSections为1
4.抹除其余节数据
存为文件,通过PE工具查看其数据
程序是否可以正常运行就知道合并节成功与否.
代码合并节
读取文件代码
PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "rb");
if (!pFile)
{
printf("FileToMem fopen Fail \r\n");
return NULL;
}
//获取文件长度
fseek(pFile, 0, SEEK_END); //SEEK_END文件结尾
DWORD Size = ftell(pFile);
fseek(pFile, 0, SEEK_SET); //SEEK_SET文件开头
//申请存储文件数据缓冲区
PCHAR pFileBuffer = (PCHAR)malloc(Size);
if (!pFileBuffer)
{
printf("FileToMem malloc Fail \r\n");
fclose(pFile);
return NULL;
}
//读取文件数据
fread(pFileBuffer, Size, 1, pFile);
//判断是否为可执行文件
if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
{
printf("Error: MZ \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
{
printf("Error: PE \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (dwFileSize)
{
*dwFileSize = Size;
}
fclose(pFile);
return pFileBuffer;
}
输出文件代码
VOID MemToFile(IN PCHAR szFilePath, IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "wb");
if (!pFile)
{
printf("MemToFile fopen Fail \r\n");
return;
}
//输出文件
fwrite(pFileBuffer, dwFileSize, 1, pFile);
fclose(pFile);
}
FileBuffer -> ImageBuffer
VOID FileBufferToImageBuffer(IN PVOID pFileBuffer, OUT PVOID* pImageBuffer)
{
//定位PE结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)((PUCHAR)pFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
//通过IMAGE_OPTIONAL_HEADER.SizeOfOptionalHeader确定分配内存缓存大小
DWORD dwImageSize = pOpo->SizeOfImage;
PUCHAR pTemp = (PUCHAR)malloc(dwImageSize);
if (!pTemp)
{
printf("FileBufferToImageBuffer malloc Fail \r\n");
*pImageBuffer = NULL;
return ;
}
memset(pTemp, 0, dwImageSize);
//拷贝头+节表数据
memcpy(pTemp, pFileBuffer, pOpo->SizeOfHeaders);
//拷贝节区数据
for (size_t i = 0; i < pFil->NumberOfSections; i++)
{
memcpy(
pTemp + pSec[i].VirtualAddress, //内存镜像基址 + 内存节区偏移
(PUCHAR)pFileBuffer + pSec[i].PointerToRawData, //文件镜像基址 + 文件节区偏移
pSec[i].SizeOfRawData //拷贝大小
);
}
*pImageBuffer = pTemp;
}
ImageBuffer -> FileBuffer
DWORD ImageBufferToFileBuffer(IN PVOID pImageBuffer, OUT PVOID* pFileBuffer)
{
//定位PE结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)((PUCHAR)pImageBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
//遍历获取内存镜像对应文件镜像大小
DWORD dwFileSize = pOpo->SizeOfHeaders;
for (size_t i = 0; i < pFil->NumberOfSections; i++)
{
dwFileSize += pSec[i].SizeOfRawData;
}
//也可通过最后一个节区的文件偏移+文件大小获取
//分配文件缓存
PUCHAR pTemp = (PUCHAR)malloc(dwFileSize);
if (!pTemp)
{
printf("ImageBufferToFileBuffer malloc Fail \r\n");
*pFileBuffer = NULL;
return NULL;
}
memset(pTemp, 0, dwFileSize);
//拷贝头+节表数据
memcpy(pTemp, pImageBuffer, pOpo->SizeOfHeaders);
//拷贝节区数据
for (size_t i = 0; i < pFil->NumberOfSections; i++)
{
memcpy(
pTemp + pSec[i].PointerToRawData, //文件镜像基址 + 文件节区偏移
(PUCHAR)pImageBuffer + pSec[i].VirtualAddress, //内存镜像基址 + 内存节区偏移
pSec[i].SizeOfRawData //拷贝大小
);
}
*pFileBuffer = pTemp;
return dwFileSize;
}
合并节代码
PVOID MergeSection2(PCHAR pBuffer, LPDWORD pNewFileSize)
{
//定位结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuffer;
PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
//修改最后一个节的属性
DWORD dwMax = pSec[pFil->NumberOfSections - 1].Misc.VirtualSize > pSec[pFil->NumberOfSections - 1].SizeOfRawData ? pSec[pFil->NumberOfSections - 1].Misc.VirtualSize : pSec[pFil->NumberOfSections - 1].SizeOfRawData;
pSec->Misc.VirtualSize = pSec->SizeOfRawData = pSec[pFil->NumberOfSections - 1].VirtualAddress + dwMax - Align(pOpo->SectionAlignment, pOpo->SizeOfHeaders);
for (size_t i = 1; i < pFil->NumberOfSections; i++)
{
pSec->Characteristics |= pSec[i].Characteristics;
}
//抹除其他节表数据
memset(pSec + 1, 0, IMAGE_SIZEOF_SECTION_HEADER * (pFil->NumberOfSections - 1));
//修正NumberOfSections
pFil->NumberOfSections = 1;
return NULL;
}
测试代码
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
PCHAR pImageBuffer = NULL;
FileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
MergeSection2(pImageBuffer, 0);
PUCHAR pNewBuffer = NULL;
dwFileSize = ImageBufferToFileBuffer(pImageBuffer, &pNewBuffer);
MemToFile(FILE_PATH_OUT, pNewBuffer, dwFileSize);
return 0;
}