那么,我们找到了某个 需要修改的绝对地址 的RVA, 将这个RVA转换成FOA后,这个绝对地址是读DWORD ,还是QWORD? 就是说,32位和64位是否有区别?
实验: 找到重定位表的数据,并观察在内存中和文件中的区别
得到某个需要修正绝对地址(就是文件中的数据)的RVA 后,
32位读取4字节 64位程序读取8字节 就是文件中这个绝对地址的数据.
上图是操作系统 修复了重定位表,如果我们自己加载,就需要按照上面的计算方式手工修复重定位表的数据
代码遍历重定位表:
以下的代码 是垃圾
//打印所有的重定位表
void PrintReloc(__in char* m_fileName)
{
char* Filebuffer = NULL;
if (!GetFileBuffer(m_fileName, &Filebuffer)) return ;
PIMAGE_DOS_HEADER LPdosHeader = NULL;
LPdosHeader = (PIMAGE_DOS_HEADER)Filebuffer;
PIMAGE_OPTIONAL_HEADER32 LPoptioinHeader32 = NULL;//暂时使用32位的,因为我只需要确定一下,是32位程序还是64位程序
LPoptioinHeader32 = (PIMAGE_OPTIONAL_HEADER32)((char*)LPdosHeader + LPdosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
PIMAGE_DATA_DIRECTORY LPdataDir = NULL;
/*判断64位程序还是32位程序*/
if (LPoptioinHeader32->Magic == 0x10b)
{
//如果是32位的程序
LPdataDir = (PIMAGE_RELOCATION)((char*)LPoptioinHeader32 + 136);
}
else
{
//如果是64位的程序
LPdataDir = (PIMAGE_RELOCATION) ((char*)LPoptioinHeader32 + 152);//借用一下可选头的指针
}
//64位和32位的程序 只是定位的时候不一样,上面的 136 和152 可以在微软的官网查询到,
//或者可以这样,但我觉得不太好理解
if (LPoptioinHeader32->Magic == 0x10b)
{
//如果是32位的程序
LPdataDir = (PIMAGE_RELOCATION)(&LPoptioinHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
}
else
{
//如果是64位的程序
PIMAGE_OPTIONAL_HEADER64 LPoptioinHeader64 = (PIMAGE_OPTIONAL_HEADER64)((char*)LPdosHeader + LPdosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
LPdataDir = (PIMAGE_RELOCATION)(&LPoptioinHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
}
/* 通过数据目录表 中的virtual address 转换成FOA ,加上文件buffer指针,就可以找到现在内存中的 第一个 重定位块*/
PIMAGE_BASE_RELOCATION LPfirstRelc = NULL;
DWORD Characteristics = 0;//获得一下重定位表 所在的节区属性
LPfirstRelc = (PIMAGE_BASE_RELOCATION)((char*)LPdosHeader + RVAToFOAEX(LPdosHeader, LPdataDir->VirtualAddress,&Characteristics));
PIMAGE_BASE_RELOCATION LPcurrentRelc = LPfirstRelc;//我希望LPfirstRelc 指针不要发生变化,后面还需要使用
int i = 0;
PWORD WPtypeAndOffset = NULL;//数据的类型 和页内偏移指针
while (LPcurrentRelc->SizeOfBlock!=0 && LPcurrentRelc->VirtualAddress!=0)//如果都为0说明重定位块的结束
{
int PageOffsetCount = (LPcurrentRelc->SizeOfBlock - sizeof(DWORD) * 2) /sizeof(WORD);//块的“页面 RVA”字段中所指定起始地址的偏移量 的数量
UINT64 FOA = (char*)LPcurrentRelc - (char*)LPdosHeader;//重定位块在文件中的位置.
printf("[%d]-FOA:0x%llx PageRVA:0x%x BlockSize:0x%x PageOffsetCount:%d \n", i, FOA, LPcurrentRelc->VirtualAddress, LPcurrentRelc->SizeOfBlock, PageOffsetCount);
WPtypeAndOffset = (PWORD)((char*)LPcurrentRelc + sizeof(DWORD) * 2);//指针越过IMAGE_BASE_RELOCATION结构,指向第一个页内偏移
for (size_t j = 0; j < PageOffsetCount; j++)
{
byte type =(byte) ((*WPtypeAndOffset) >> 12);//类型
WORD PageOffset = (*WPtypeAndOffset) & 0XFFF;//去掉高4位
printf("\t [%d]-value:0x%x type:0x%x PageOffset:0x%x \n", j,*WPtypeAndOffset, type, PageOffset);
if (type == IMAGE_REL_BASED_DIR64 )
{
//64位
DWORD RelocRVA = LPcurrentRelc->VirtualAddress + PageOffset;//需要修正的绝对地址的RVA
DWORD ModifyFOA = RVAToFOA(LPdosHeader, RelocRVA);//需要修正的绝对地址,在文件中的位置
UINT64 ObAdress = *((UINT64*)((char*)LPdosHeader + ModifyFOA));//读一下哪个绝对地址要修正
printf("\t ModifyFOA=0x%x RelocRVA=0x%x ObAdress=0x%llx\n", RelocRVA, ModifyFOA, ObAdress);
}
else if(type == IMAGE_REL_BASED_HIGHLOW)
{
//32位
DWORD RelocRVA = LPcurrentRelc->VirtualAddress + PageOffset;//需要修正的绝对地址的RVA
DWORD ModifyFOA = RVAToFOA(LPdosHeader, RelocRVA);//需要修正的绝对地址,在文件中的位置
UINT32 ObAdress = *((UINT32*)((char*)LPdosHeader + ModifyFOA));//读一下哪个绝对地址要修正
printf("\t ModifyFOA=0x%x RelocRVA=0x%x ObAdress=0x%x\n", ModifyFOA,RelocRVA, ObAdress);
}
WPtypeAndOffset++;
}
LPcurrentRelc = (PIMAGE_BASE_RELOCATION)((char*)LPcurrentRelc + LPcurrentRelc->SizeOfBlock);//指针跳到下一个表
i++;//用于统计,无意义
}
}
移动重定位表:
- 清晰划分32位与64位逻辑,避免指针混用。
- 加强内存管理,确保出错时释放已分配的资源。
- 优化对齐计算和节表处理,确保数据正确对齐。
- 增加错误处理和日志记录,便于调试和维护。
- 改进代码结构和命名,提高可读性。
我愿称以下代码为垃圾
//移动重定位表
BOOL RemoveReloc(__in char* m_fileName)
{
char* Filebuffer = NULL;
if (!GetFileBuffer(m_fileName, &Filebuffer)) return FALSE;
PIMAGE_DOS_HEADER LPdosHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 LPoptioinHeader32 = NULL;//暂时使用32位的,因为我只需要确定一下,是32位程序还是64位程序
PIMAGE_SECTION_HEADER LPfirstSectionHeader = NULL;//第一个节表的指针
PIMAGE_SECTION_HEADER LPlastSectionHeader = NULL;//最后一个节表的指针,为了计算新节区的 virtual address
PIMAGE_DATA_DIRECTORY LPdataDirOfreloc = NULL;
LPdosHeader = (PIMAGE_DOS_HEADER)Filebuffer;
PIMAGE_FILE_HEADER LPfileHeader = (PIMAGE_FILE_HEADER)((char*)LPdosHeader + LPdosHeader->e_lfanew + sizeof(DWORD));
LPoptioinHeader32 = (PIMAGE_OPTIONAL_HEADER32)((char*)LPdosHeader + LPdosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
// 1.定位到 重定位表
UINT sizeNeedRemoveOfHeader = 0;//顺便计算一下,如果需要删除dos stub , nt头和节表 的大小
//64位和32位的程序 只是定位的时候不一样
if (LPoptioinHeader32->Magic == 0x10b)
{
//如果是32位的程序
LPdataDirOfreloc = (PIMAGE_RELOCATION)(&LPoptioinHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
LPfirstSectionHeader =(PIMAGE_SECTION_HEADER)((CHAR*)LPoptioinHeader32 + sizeof(IMAGE_OPTIONAL_HEADER32));
sizeNeedRemoveOfHeader = sizeof(IMAGE_OPTIONAL_HEADER32) + sizeof(IMAGE_SECTION_HEADER) * LPfileHeader->NumberOfSections;
}
else
{
//如果是64位的程序
PIMAGE_OPTIONAL_HEADER64 LPoptioinHeader64 = (PIMAGE_OPTIONAL_HEADER64)((char*)LPdosHeader + LPdosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER));
LPdataDirOfreloc = (PIMAGE_RELOCATION)(&LPoptioinHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
LPfirstSectionHeader = (PIMAGE_SECTION_HEADER)((CHAR*)LPoptioinHeader32 + sizeof(IMAGE_OPTIONAL_HEADER64));
sizeNeedRemoveOfHeader = sizeof(IMAGE_OPTIONAL_HEADER64) + sizeof(IMAGE_SECTION_HEADER) * LPfileHeader->NumberOfSections;
}
LPlastSectionHeader = (PIMAGE_SECTION_HEADER)((CHAR*)LPfirstSectionHeader + sizeof(IMAGE_SECTION_HEADER) * (LPfileHeader->NumberOfSections - 1));
#pragma region 计算重定位表的大小
PIMAGE_BASE_RELOCATION LPfirstRelc = NULL;
DWORD Characteristics = 0;//获得一下重定位表 所在的节区属性
LPfirstRelc = (PIMAGE_BASE_RELOCATION)((char*)LPdosHeader + RVAToFOAEX(LPdosHeader, LPdataDirOfreloc->VirtualAddress, &Characteristics));
PIMAGE_BASE_RELOCATION LPcurrentRelc = LPfirstRelc;//我希望LPfirstRelc 指针不要发生变化,后面还需要使用
//2.遍历得到重定位表块的大小
UINT relocsBlockSize = 0;
while (LPcurrentRelc->SizeOfBlock != 0 && LPcurrentRelc->VirtualAddress != 0)
{
relocsBlockSize += LPcurrentRelc->SizeOfBlock;
LPcurrentRelc = (PIMAGE_BASE_RELOCATION)((char*)LPcurrentRelc + LPcurrentRelc->SizeOfBlock);//指针跳到下一个表
}
//最后加上全0结构结尾的大小
relocsBlockSize += sizeof(LPcurrentRelc->SizeOfBlock) + sizeof(LPcurrentRelc->VirtualAddress);
#pragma endregion
#pragma region 计算移动重定位表需要 的文件内存大小 ,用于填写新节区的 sizeofRawdata
DWORD newSectionSizeOfRawData = 0;
if (relocsBlockSize % LPoptioinHeader32->FileAlignment == 0)
{
//是文件对齐的整数倍
newSectionSizeOfRawData = relocsBlockSize;
}
else
{
newSectionSizeOfRawData = (relocsBlockSize / LPoptioinHeader32->FileAlignment + 1) * LPoptioinHeader32->FileAlignment;
}
#pragma endregion
#pragma region 用于计算 移动重定位表时,新增节 的内存对齐大小
DWORD newSizeOfImage = 0;
if (newSectionSizeOfRawData % LPoptioinHeader32->SectionAlignment == 0)
{
newSizeOfImage = LPoptioinHeader32->SizeOfImage + newSectionSizeOfRawData;
}
else
{
newSizeOfImage= LPoptioinHeader32->SizeOfImage+(newSectionSizeOfRawData / LPoptioinHeader32->SectionAlignment + 1) * LPoptioinHeader32->SectionAlignment ;
}
#pragma endregion
#pragma region 计算新节表的virtual Adress 和pointtorawdata
DWORD newSectionVirtualAddress = 0;
DWORD max = LPlastSectionHeader->Misc.VirtualSize > LPlastSectionHeader->SizeOfRawData ? LPlastSectionHeader->Misc.VirtualSize : LPlastSectionHeader->SizeOfRawData;
if (max % LPoptioinHeader32->SectionAlignment == 0)
{
newSectionVirtualAddress = LPlastSectionHeader->VirtualAddress +max;
}
else
{
newSectionVirtualAddress = LPlastSectionHeader->VirtualAddress+(max / LPoptioinHeader32->SectionAlignment + 1) * LPoptioinHeader32->SectionAlignment ;
}
DWORD newSectionPointToRawData = LPlastSectionHeader->PointerToRawData+LPlastSectionHeader->SizeOfRawData;
#pragma endregion
PIMAGE_SECTION_HEADER LPnewSectionHeader = (PIMAGE_SECTION_HEADER)((char*)LPlastSectionHeader+sizeof(IMAGE_SECTION_HEADER));
BOOL isok = FALSE;//用于最终确定
BOOL isZero = TRUE;//用于是否80字节为0
//循环看一下是不是可以包含2个全0 的节表结构 ,就是80个字节是不是全0
PULONG64 tempPoint = (PULONG64)LPnewSectionHeader;
//这里借用一下32位的结构体,64位这里的偏移相同
if (LPoptioinHeader32->SizeOfHeaders - ((CHAR*)LPnewSectionHeader - (CHAR*)LPdosHeader) >= 80)
{
//如果大于80字节,但是我还需要遍历这80字节是否有数据,
for (size_t i = 0; i < 80/8; i++)
{
if (*(PULONG64)tempPoint != 0)
{
isZero = FALSE;
break;
}
tempPoint++;
}
if (!isZero)
{
isok = FALSE;
}
else
{
isok = TRUE;
}
}
//先修改一个节的数量,因为下面要开始修改指针了,这样头部的指针将失效
LPfileHeader->NumberOfSections += 1;
if (!isok)
{
/*---------------------------------------------
* 说明可能塞得下,但是有数据,不能使用
* 将dosstub的数据删除,将NT头提升上来
* 按道理还要确定一下去掉dosstub后,能不能塞进来一个新的节表,我想偷懒了
----------------------------------------------*/
void* source = (VOID*)((CHAR*)LPdosHeader + LPdosHeader->e_lfanew);
void* dest = (VOID*) ((CHAR*)LPdosHeader + sizeof(IMAGE_DOS_HEADER));
memcpy(dest, source, sizeNeedRemoveOfHeader);
LPdosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
LPnewSectionHeader = (char*)LPnewSectionHeader - ((char*)dest - (char*)source);//重新定位 新节表的指针
}
//不要再使用头部的指针
RtlZeroMemory(LPnewSectionHeader, sizeof(IMAGE_SECTION_HEADER) * 2);
LPnewSectionHeader->Characteristics = Characteristics;
LPnewSectionHeader->PointerToRawData = newSectionPointToRawData;
LPnewSectionHeader->SizeOfRawData = newSectionSizeOfRawData;
LPnewSectionHeader->VirtualAddress = newSectionVirtualAddress;
LPnewSectionHeader->Misc.VirtualSize = newSectionSizeOfRawData;
CHAR name[8] = "ABC";
strcpy_s(LPnewSectionHeader->Name,8, name);
//修改目录项中重定位表的virtual address
LPdataDirOfreloc->VirtualAddress = LPnewSectionHeader->VirtualAddress;
//修改可选头中的sizeofimage
LPoptioinHeader32->SizeOfImage += newSizeOfImage;
//4.重新申请内存
ULONGLONG fileSize = 0;
fileSize = LPlastSectionHeader->PointerToRawData + LPlastSectionHeader->SizeOfRawData + newSectionSizeOfRawData;
char* newFileBuffer = malloc(fileSize);
if (newFileBuffer == NULL)
{
perror("申请新内存失败");
return FALSE;
}
RtlZeroMemory(newFileBuffer, fileSize);
memcpy(newFileBuffer, Filebuffer, fileSize- newSectionSizeOfRawData);//复制内存
//将原来的重定位表清空 .....
//将原来的重定位表复制到新节的头部
char* CPnewSectionData = newFileBuffer + newSectionPointToRawData;
memcpy(CPnewSectionData, LPfirstRelc, relocsBlockSize);
free(Filebuffer);
StoringFile("E:\\win32Api\\源代码\\基础篇代码-配套资源\\Chapter9\\DialogBoxProgram\\Debug\\测试miscxxx.exe", newFileBuffer, fileSize);
}