课上海东说尽量把常用函数放进一个头文件中,附带实现.cpp
这里作一个 Currency.h 和 Currency.cpp 函数基本是前面出现过的,包含功能和输入输出说明。
后面的文章也会使用到大部分。
首先是 Currency.h
#pragma once
#include "windows.h"
//函数声明
//**************************************************************************
//ReadPEFile:将文件读取到缓冲区
//参数说明:
//lpszFile 文件路径
//pFileBuffer 缓冲区指针
//返回值说明:
//读取失败返回0 否则返回实际读取的大小
//**************************************************************************
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer);
//**************************************************************************
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer
//参数说明:
//pFileBuffer FileBuffer指针
//ppImageBuffer ImageBuffer二重指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* ppImageBuffer);
//**************************************************************************
//CopyImageBufferToFileBuffer:将ImageBuffer中的数据复制到新的缓冲区
//参数说明:
//pImageBuffer ImageBuffer指针
//ppFileBuffer FileBuffer二重指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyImageBufferToFileBuffer(IN LPVOID pImageBuffer, OUT LPVOID* ppFileBuffer);
//**************************************************************************
//MemeryToFile:将内存中的数据复制到文件
//参数说明:
//pMemBuffer 内存中数据的指针
//size 要复制的大小
//lpszFile 要存储的文件路径
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
BOOL MemeryToFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile);
//**************************************************************************
//RVA2FOA:将内存偏移转换为文件偏移
//参数说明:
//pFileBuffer FileBuffer指针
//dwRva RVA的值
//返回值说明:
//返回转换后的FOA的值 如果失败返回0
//**************************************************************************
DWORD RVA2FOA(IN LPVOID pFileBuffer, IN DWORD dwRva);
//**************************************************************************
//Align:计算对齐后的值
//参数说明:
//x 需要进行对齐的值
//Alignment 对齐大小
//返回值说明:
//返回x进行Alignment值对齐后的值
//**************************************************************************
int Align(int x, int Alignment);
//**************************************************************************
//GetFunctionAddrByName:根据名字找到导出表中的函数地址
//参数说明:
//pFileBuffer:FileBuffer指针
//str: 函数名指针
//返回值说明:
//返回导出表中的函数地址
//**************************************************************************
LPVOID GetFunctionAddrByName(LPVOID pFileBuffer, char* str);
//**************************************************************************
//GetFunctionAddrByOrdinals:根据序号找到导出表中的函数地址
//参数说明:
//pFileBuffer:FileBuffer指针
//ord:函数序号
//返回值说明:
//返回导出表中的函数地址
//**************************************************************************
LPVOID GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD ord);
//输出内存buffer中的头
void PrintHeader(LPVOID pFileBuffer);
//验证FileBuffer和ImageBuffer映射是否正确
bool VerifyFileBufferAndImageBuffer(LPVOID pFileBuffer, LPVOID pImageBuffer);
//测试 内存偏移转换为文件偏移 是否正确
bool TestRvaToFile(LPVOID pFileBuffer, DWORD Offset);
//**************************************************************************
//AddNewSection:新增节,可能含有头抬升。函数将保存新增节后的文件
//参数说明:
//pFileBuffer:FileBuffer指针
//SizeOfFileBuffer:pFileBuffer指向的大小,也即FileBuffer大小
//SizeOfNewSectio:新增节的大小
//pNewFileBuffer:返回指向新FileBuffer的指针
//返回值说明:
//新FileBuffer的大小,NewFileBuffer的大小
//**************************************************************************
int AddNewSection(IN LPVOID pFileBuffer, IN int SizeOfFileBuffer, IN DWORD SizeOfNewSection, OUT LPVOID* ppNewFileBuffer);
其次是对应的函数实现, Currency.cpp 内容:
#include "Currency.h"
#include"windows.h"
#include"stdio.h"
//Currency::Currency(){}
//Currency::~Currency(){}
//函数声明
//**************************************************************************
//ReadPEFile:将文件读取到缓冲区
//参数说明:
//lpszFile 文件路径
//ppFileBuffer 缓冲区指针
//返回值说明:
//读取失败返回0 否则返回实际读取的大小
//**************************************************************************
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* ppFileBuffer)
{
FILE *pFile = NULL;
DWORD fileSize = 0;
//LPVOID pFileBuffer = NULL; //LPVOID 即 void *
//打开文件
pFile = fopen(lpszFile, "rb");
if (!pFile)
{
printf(" 无法打开 EXE 文件! ");
*ppFileBuffer = NULL;
return 0;
}
//读取文件大小
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
*ppFileBuffer = malloc(fileSize); //pFileBuffer指向一段 分配的exe文件这么大的内存
if (! *ppFileBuffer)
{
printf(" 分配空间失败! ");
fclose(pFile);
*ppFileBuffer = NULL;
return 0;
}
//将文件数据读取到缓冲区
size_t n = fread(*ppFileBuffer, fileSize, 1, pFile); //size_t 即 unsigned int
if (!n)
{
printf(" 读取数据失败! ");
free(*ppFileBuffer);
fclose(pFile);
*ppFileBuffer = NULL;
return 0;
}
//printf(" 成功! ");
//关闭文件
fclose(pFile);
return fileSize; //最后返回指向 已填充exe文件内容 的新开辟空间 的指针
}
//**************************************************************************
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer
//参数说明:
//pFileBuffer FileBuffer指针
//ppImageBuffer ImageBuffer二重指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* ppImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (!pFileBuffer)
{
printf("pFileBuffer空指针\n");
return 0;
}
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//判断是否是有效的MZ标志
if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE) //pFileBuffer强转 WORD* 后 取指针指向的内容
{
printf("不是有效的MZ标志\n");
return 0;
}
//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pDosHeader + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) //基址pFileBuffer + lfanew 为 NTHeader首址
{
printf("不是有效的PE标志\n");
return 0;
}
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//分配ImageBuffer的空间
*ppImageBuffer = malloc(pOptionalHeader->SizeOfImage);
if (!*ppImageBuffer)
{
printf("分配空间失败\n");
return 0;
}
printf("SizeOfImage:%x\n", pOptionalHeader->SizeOfImage);
memset(*ppImageBuffer, 0, pOptionalHeader->SizeOfImage); //初始化全为0
//拷贝头
printf("SizeOfHeaders:%x\n", pOptionalHeader->SizeOfHeaders);
memcpy(*ppImageBuffer, pDosHeader, pOptionalHeader->SizeOfHeaders);
//循环遍历节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
{
//将FileBuffer中从 其基址+PointerToRawData 的地方开始,拷贝SizeOfRawData大小的内存,到ImageBuffer 其基址+VirtualAddress 处
memcpy( (LPVOID)((DWORD)(*ppImageBuffer)+ pSectionHeader->VirtualAddress), (LPVOID)((DWORD)pDosHeader + pSectionHeader->PointerToRawData),pSectionHeader->SizeOfRawData);
} //出循环后pSectionHeader指向不存在的节表,多了一节
pSectionHeader = NULL; //所以pSectionHeader指针不可再使用
printf("拷贝完成\n");
return pOptionalHeader->SizeOfImage;
}
//**************************************************************************
//CopyImageBufferToFileBuffer:将ImageBuffer中的数据复制到新的缓冲区
//参数说明:
//pImageBuffer ImageBuffer指针
//ppFileBuffer FileBuffer二重指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyImageBufferToFileBuffer(IN LPVOID pImageBuffer, OUT LPVOID* ppFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (!pImageBuffer)
{
printf("pImageBuffer空指针\n");
return 0;
}
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; // 强转 DOS_HEADER 结构体指针
//判断是否是有效的MZ标志
if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE) //pFileBuffer强转 WORD* 后 取指针指向的内容
{
printf("不是有效的MZ标志\n");
return 0;
}
//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pDosHeader + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) //基址pFileBuffer + lfanew 为 NTHeader首址
{
printf("不是有效的PE标志\n");
return 0;
}
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//循环节表找到最后一节
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++) //注意这里i从1开始 i < NumberOfSections
{} //出循环后pSectionHeader指向最后一个节表
//分配FileBuffer的空间 这里头信息读取是基于ImageBuffer的,所以(最后一节的地址 + 最后一节的长度)相当与文件最末尾,而用 文件最末尾 - 基址 得到原本文件的长度
DWORD SizeOfFile = (DWORD)pSectionHeader->PointerToRawData + (DWORD)pSectionHeader->SizeOfRawData ;
printf("大小:%x\n", SizeOfFile);
*ppFileBuffer = malloc(SizeOfFile);
printf("SizeOfFile:%x\n", SizeOfFile);
memset(*ppFileBuffer, 0, SizeOfFile); //初始化全为0
//拷贝头
printf("SizeOfHeaders:%x\n", pOptionalHeader->SizeOfHeaders);
memcpy(*ppFileBuffer, pImageBuffer, pOptionalHeader->SizeOfHeaders);
//循环遍历节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
{
//将ImageBuffer中从 其基址+VirtualAddress 的地方开始,拷贝SizeOfRawData大小的内存,到FileBuffer 其基址+PointerToRawData 处
printf("%d次", i);
memcpy((LPVOID)((DWORD)(*ppFileBuffer) + pSectionHeader->PointerToRawData), (LPVOID)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress), pSectionHeader->SizeOfRawData);
} //出循环后pSectionHeader指向不存在的节表,多了一节
pSectionHeader = NULL; //所以pSectionHeader指针不可再使用
printf("拷贝完成,大小:%x\n", SizeOfFile);
return SizeOfFile;
}
//**************************************************************************
//MemeryToFile:将内存中的数据复制到文件
//参数说明:
//pMemBuffer 内存中数据的指针
//size 要复制的大小
//lpszFile 要存储的文件路径
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
BOOL MemeryToFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile)
{
FILE * fpw = fopen(lpszFile, "wb");
if (fpw == NULL)
{
printf("fpw fopen fail");
return false;
}
if (fwrite(pMemBuffer, 1, size, fpw) == 0)
{
printf("fpw fwrite fail");
return false;
}
fclose(fpw); //关闭写文件流
fpw = NULL;
printf("success\n");
return true;
}
//**************************************************************************
//RVA2FOA:将内存偏移转换为文件偏移
//参数说明:
//pFileBuffer FileBuffer指针
//dwRva RVA的值
//返回值说明:
//返回转换后的FOA的值 如果失败返回0
//**************************************************************************
DWORD RVA2FOA(IN LPVOID pFileBuffer, IN DWORD dwRva)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pNextSectionHeader = NULL;
//DOS头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
if (dwRva < pOptionalHeader->SizeOfHeaders) //偏移小于头的大小,内存偏移则为文件偏移
{
return dwRva;
}
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
//下一个节表
pNextSectionHeader = pSectionHeader + 1;
//循环遍历节表
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++, pNextSectionHeader++)//注意这里i从1开始 i < NumberOfSections
{ //注意这里的pSectionHeader已经是加了基址的,不是偏移, 是绝对地址。而dwRva是偏移地址
if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pNextSectionHeader->VirtualAddress)//大于当前节的内存偏移而小于下一节的内存偏移
{ //则dwRva属于当前节,则dwRva - VirtualAddress为dwRva基于当前节的偏移。此偏移加上当前节的文件起始偏移地址 则为dwRva在文件中的偏移
DWORD PointerToRawData = pSectionHeader->PointerToRawData;
DWORD VirtualAddress = pSectionHeader->VirtualAddress;
DWORD aa = pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress ;
}
} //出循环后pSectionHeader指向最后一个节表
//大于当前节(最后一节)的内存偏移且小于内存映射大小
if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pOptionalHeader->SizeOfImage)
{ //同上return
return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
}
else //大于内存映射大小
{
printf("dwRva大于内存映射大小\n");
return -1;
}
}
//**************************************************************************
//Align:计算对齐后的值
//参数说明:
//x 需要进行对齐的值
//Alignment 对齐大小
//返回值说明:
//返回x进行Alignment值对齐后的值
//**************************************************************************
int Align(int x, int Alignment)
{
if (x%Alignment==0)
{
return x;
}
else
{
return (1 + (x / Alignment)) * Alignment;
}
}
//**************************************************************************
//GetFunctionAddrByName:根据名字找到导出表中的函数地址
//参数说明:
//pFileBuffer:FileBuffer指针
//str: 函数名指针
//返回值说明:
//返回导出表中的函数地址
//**************************************************************************
LPVOID GetFunctionAddrByName(LPVOID pFileBuffer, char* str)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
DWORD nameFOA = NULL;
DWORD AddressOfNamesFOA = NULL;
DWORD AddressOfNameOrdinalsFOA = NULL;
DWORD AddressOfFunctionsFOA = NULL;
WORD Ordinal = NULL;
char * name = NULL;
int i = 0;
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//可选PE头 简化后的处理
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
//导出表
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
//printf("NumberOfNames:%d\n", pExportDirectory->NumberOfNames);
//导出表中的AddressOfNames为Rva,将其转换为FOA得到AddressOfNamesFOA
AddressOfNamesFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfNames);
for (i = 0; i < pExportDirectory->NumberOfNames; i++)
{ //AddressOfNamesFOA只是Names表的FOA地址,需加上pFileBuffer构成的绝对地址才能取出其中的值。
//取出的值即Names地址表第i个name的Rva地址,转成FOA得到name的FOA地址
nameFOA = RVA2FOA(pFileBuffer, *(PDWORD)((DWORD)pFileBuffer + AddressOfNamesFOA));
name = (char *)(nameFOA + (DWORD)pFileBuffer);//name的FOA加上pFileBuffer构成绝对地址,该地址才真正指向字符串
if (!strcmp(str, name))
{
break;
}
AddressOfNamesFOA += 4; //往前走4字节,指向Names地址表下一个元素,即下一个name地址
}
if (i == pExportDirectory->NumberOfNames)
{
printf("找不到名为%s的函数!\n", str);
return 0;
}
AddressOfNameOrdinalsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfNameOrdinals); //同Names表找法
Ordinal = *(PWORD)((DWORD)pFileBuffer + AddressOfNameOrdinalsFOA + i*2); //因为Ordinal表元素为2字节,绝对地址加上i*2直接取第i个元素
printf("i:%d,Ordinal:%d\n", i,Ordinal);
AddressOfFunctionsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfFunctions);
return (LPVOID)*(PDWORD)((DWORD)pFileBuffer + AddressOfFunctionsFOA + Ordinal * 4);//同上
}
//**************************************************************************
//GetFunctionAddrByOrdinals:根据序号找到导出表中的函数地址
//参数说明:
//pFileBuffer:FileBuffer指针
//ord:函数序号
//返回值说明:
//返回导出表中的函数地址
//**************************************************************************
LPVOID GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD ord)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
DWORD nameFOA = NULL;
DWORD AddressOfNameOrdinalsFOA = NULL;
DWORD AddressOfFunctionsFOA = NULL;
WORD Ordinal = NULL;
int i = 0;
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//可选PE头 简化后的处理
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
//导出表
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
if (ord - pExportDirectory->Base >= pExportDirectory->NumberOfNames || ord - pExportDirectory->Base < 0)
{
printf("找不到序号为%d的函数!\n", ord);
return 0;
}
AddressOfFunctionsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfFunctions);//导入表函数表地址转FOA
return (LPVOID)*(PDWORD)((DWORD)pFileBuffer + AddressOfFunctionsFOA + (ord - pExportDirectory->Base) * 4);//绝对地址 加上(ord-base)*4 直接取第ord-base个元素
}
//测试 内存偏移转换为文件偏移 是否正确
bool TestRvaToFile(LPVOID pFileBuffer ,DWORD Offset)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pNextSectionHeader = NULL;
//DOS头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//循环遍历节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
{
//通过函数由Rva得出的FOA
DWORD FuncFOA = RVA2FOA(pFileBuffer, Offset + pSectionHeader->VirtualAddress);
//查节表得出的FOA
DWORD RealFOA = Offset + pSectionHeader->PointerToRawData;
if (FuncFOA != RealFOA)
{
printf("第%d个节表测试错误!\n", i);
return false;
}
} //出循环后pSectionHeader指向不存在的节表,多了一节
pSectionHeader = NULL; //所以pSectionHeader指针不可再使用
printf("测试成功!\n");
return true;
}
//验证FileBuffer和ImageBuffer映射是否正确
bool VerifyFileBufferAndImageBuffer(LPVOID pFileBuffer, LPVOID pImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//DOS头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//头验证
PBYTE PBFileBuffer = (PBYTE)pFileBuffer;
PBYTE PBImageBuffer = (PBYTE)pImageBuffer;
//printf("开始FB:%x IB:%x\n", FileBuffer, ImageBuffer);
for (int i = 0; i < pOptionalHeader->SizeOfHeaders; i++)
{
if (*PBImageBuffer != *PBFileBuffer) //pFileBuffer强转 WORD* 后 取指针指向的内容
{
printf("头验证失败\n");
return false;
}
PBImageBuffer++;
PBFileBuffer++;
}
//printf("结束FB:%x IB:%x\n", FileBuffer, ImageBuffer);
printf("头验证通过\n");
//循环遍历节表进行节验证
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; i++)
{
PBFileBuffer = (PBYTE)((DWORD)pFileBuffer + pSectionHeader->PointerToRawData);
PBImageBuffer = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress);
for (int j = 0; j < pSectionHeader->SizeOfRawData; j++)
{
if (*PBImageBuffer != *PBFileBuffer) //pFileBuffer强转 WORD* 后 取指针指向的内容
{
printf("第%x节第%x个字节验证失败\n",i,j);
return false;
}
PBImageBuffer++;
PBFileBuffer++;
}
//节表++
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
} //出循环后pSectionHeader指向不存在的节表,多了一节
pSectionHeader = NULL; //所以pSectionHeader指针不可再使用
printf("节验证通过\n");
return true;
}
//输出内存buffer中的头
void PrintHeader(LPVOID pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
printf("************PrintHeader开始************\n");
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) //pFileBuffer强转 WORD* 后 取指针指向的内容
{
printf("不是有效的MZ标志\n");
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//打印DOS头
printf("**********DOS头**********\n");
printf("MZ标志:%x\n", pDosHeader->e_magic);
printf("PE偏移:%x\n", pDosHeader->e_lfanew);
//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) //基址pFileBuffer + lfanew 为 NTHeader首址
{
printf("不是有效的PE标志\n");
free(pFileBuffer);
return;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//打印NT头
printf("*********NT HEADERS*********\n");
printf("NT:%x\n", pNTHeader->Signature);
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
printf("********FILE HEADER********\n");
printf("PE:%x\n", pFileHeader->Machine);
printf("节的数量:%x\n", pFileHeader->NumberOfSections);
printf("SizeOfOptionalHeader:%x\n", pFileHeader->SizeOfOptionalHeader);
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
printf("*********OPTIOINAL HEADER********\n");
printf("Magic:%x\n", pOptionalHeader->Magic);
//循环遍历节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; i++)
{
char Name[IMAGE_SIZEOF_SHORT_NAME + 1];
//printf("size:%x\n", sizeof(Name));
memset(Name, 0, sizeof(Name));
printf("******第%d个节表******\n", i);
memcpy(Name, pSectionHeader->Name, IMAGE_SIZEOF_SHORT_NAME);
printf("Name:%s\n", Name);
//节表++
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
} //出循环后pSectionHeader指向不存在的节表,多了一节
pSectionHeader = NULL; //所以pSectionHeader指针不可再使用
printf("************PrintHeader结束************\n");
}
//**************************************************************************
//AddNewSection:新增节,可能含有头抬升。函数将保存新增节后的文件
//参数说明:
//pFileBuffer:FileBuffer指针
//SizeOfFileBuffer:pFileBuffer指向的大小,也即FileBuffer大小
//SizeOfNewSectio:新增节的大小
//pNewFileBuffer:返回指向新FileBuffer的指针
//返回值说明:
//新FileBuffer的大小,NewFileBuffer的大小
//**************************************************************************
int AddNewSection(IN LPVOID pFileBuffer,IN int SizeOfFileBuffer,IN DWORD SizeOfNewSection, OUT LPVOID* ppNewFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pLastSectionHeader = NULL; //指向最后一个节表
PIMAGE_SECTION_HEADER pNewSectionHeader = NULL; //指向最后一个节表的下一个节表,即不存在的节表作为新开辟的节表
bool isUplift = false;
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++) //注意这里i从1开始 i < NumberOfSections
{
} //出循环后pSectionHeader指向最后一个节表
pLastSectionHeader = pSectionHeader;
pNewSectionHeader = pLastSectionHeader + 1;
//节表结束的位置+两个节表的大小 仍然≤ 头大小,才可继续。插一个留两个为了插入一个新节表后仍有一个节表的位置填充0
if ((DWORD)pNewSectionHeader + IMAGE_SIZEOF_SECTION_HEADER * 2 <= (DWORD)pFileBuffer + pOptionalHeader->SizeOfHeaders)
{ //要节表后留出两个节表的空位且全为0,保证其中没有可能使用的数据,才允许插入新节表。
PBYTE pTemp = (PBYTE)pNewSectionHeader;
for (int i = 0; i < IMAGE_SIZEOF_SECTION_HEADER * 2; i++, pTemp++)
{
if (*pTemp)
{
printf("节表插入空位存在数据,需进行头抬升\n");
isUplift = true;
break;
}
}
}
else
{
printf("无节表插入空位,需进行头抬升\n");
isUplift = true;
}
//isUplift = true; //头抬升测试
if (isUplift)
{
if ((DWORD)pFileBuffer + sizeof(IMAGE_DOS_HEADER) - (DWORD)pNTHeader >= IMAGE_SIZEOF_SECTION_HEADER * 2)
{
printf("可抬升NT头\n");
//开始拷贝,将NT头拷贝到DOS头结束之后,长度为NT头开始到最后一个节表结束时的长度,即pNewSectionHeader
memcpy((void*)((DWORD)pFileBuffer + sizeof(IMAGE_DOS_HEADER)), pNTHeader, (DWORD)pNewSectionHeader - (DWORD)pNTHeader);
//拷贝后重置e_lfanew位置
//printf(" e_lfanew: %x\n", pDosHeader->e_lfanew);
pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
//printf("e_lfanew: %x\n", pDosHeader->e_lfanew);
//抬升后更新所有被抬升的头指针
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++) //注意这里i从1开始 i < NumberOfSections
{
} //出循环后pSectionHeader指向最后一个节表
pLastSectionHeader = pSectionHeader;
pNewSectionHeader = pLastSectionHeader + 1;
//验证代码,判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) //基址pFileBuffer + lfanew 为 NTHeader首址
{
printf("抬升后验证失败,不是有效的PE标志\n");
return 0;
}
printf("抬升成功!\n");
//抬升成功后将最后一个节表后两个节表位置的空间置零
memset(pNewSectionHeader, 0, IMAGE_SIZEOF_SECTION_HEADER * 2);
}
else
{
printf("不可抬升NT头,不可插入节表\n");
free(pFileBuffer);
return 0;
}
}
//开始构造新节表
printf("可插入节表\n");
strcpy((char*)pNewSectionHeader->Name, (char*)".NewSec");
//printf("Name:%s\n", pNewSectionHeader->Name);
pNewSectionHeader->Misc.VirtualSize = SizeOfNewSection;
//节区在内存中的偏移 = 内存中整个PE文件映射的大小
pNewSectionHeader->VirtualAddress = pOptionalHeader->SizeOfImage;
//节区在文件对齐中的大小 以VirtualSize内存对齐向上取整
printf("FileAlignment:%x\n", pOptionalHeader->FileAlignment);
pNewSectionHeader->SizeOfRawData = Align(SizeOfNewSection, pOptionalHeader->FileAlignment);
printf("SizeOfRawData:%x\n", pNewSectionHeader->SizeOfRawData);
//节区在文件中的偏移 = 文件大小 (也可以是最后一个节区所在文件中位置 + 最后一个节区在文件对齐中的大小)二者一致
pNewSectionHeader->PointerToRawData = SizeOfFileBuffer;//pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
//printf("PointerToRawData:%x\n",pNewSectionHeader->PointerToRawData);
pNewSectionHeader->PointerToRelocations = 0;
//printf("PointerToRelocations:%x\n", pNewSectionHeader->PointerToRelocations);
pNewSectionHeader->PointerToLinenumbers = 0;
pNewSectionHeader->NumberOfRelocations = 0;
pNewSectionHeader->NumberOfLinenumbers = 0;
pNewSectionHeader->Characteristics = 0xe0000060;
//新节表构造完毕, 修改节的数量
pFileHeader->NumberOfSections++;
//修改sizeOfImage的大小
//节区在内存对齐中的大小 以VirtualSize内存对齐向上取整,对齐后加到SizeOfImage中
printf("SectionAlignment: %x\nSizeOfImage:%x\n", pOptionalHeader->SectionAlignment, pOptionalHeader->SizeOfImage);
pOptionalHeader->SizeOfImage += Align(SizeOfNewSection, pOptionalHeader->SectionAlignment);
printf("SizeOfImage:%x\n", pOptionalHeader->SizeOfImage);
//printf("Characteristics:%x\n", pNewSectionHeader->Characteristics);
//开辟节区新空间
int SizeOfNewFileBuffer = SizeOfFileBuffer + pNewSectionHeader->SizeOfRawData;
*ppNewFileBuffer = malloc(SizeOfNewFileBuffer);
//新节区之前的值全部复制,新节表部分已改变
memcpy(*ppNewFileBuffer, pFileBuffer, SizeOfFileBuffer);
//新节区全部置零
memset((void*)((DWORD)*ppNewFileBuffer + pNewSectionHeader->PointerToRawData), 0, pNewSectionHeader->SizeOfRawData);
return SizeOfNewFileBuffer;
}