PE结构之 重定位表

 那么,我们找到了某个 需要修改的绝对地址 的RVA,  将这个RVA转换成FOA后,这个绝对地址是读DWORD ,还是QWORD?  就是说,32位和64位是否有区别?

实验: 找到重定位表的数据,并观察在内存中和文件中的区别

将引用dll的exe文件,设置一下基址
 
同时DLL文件的基址和EXE文件设置一样,并且关闭随机基址,目的是为了让dll文件的需要重定位的绝对地址发生改变
 

打开调试器,在内存布局中可以看到这个dll文件没有抢到 它应该在的imagebase
 

打开PE工具,随便找一个重定位表 中的数据
 

通过简单的计算,我们在调试器中的内存窗口中找到 这个位置
 

可以看到绝对地址被修改了

得到某个需要修正绝对地址(就是文件中的数据)的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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值