手工解析PE(将重定位表移动到新增节中)

这一节讲了如何新增节

这一节讲了如何解析重定位表

本次的实验内容是将重定位表移动到新增的节中,并且手工修改imagebase,重定位表中的RVA都是相对于原来的imagebase来说的,由于imagebase发生了变化,所以重定位表中的RVA也要做出相应的调整,保证修改imagebase后,RVA仍可以准确定位到函数地址,所以需调整重定位表中每一块的RVA

主要步骤

        1.新增节(见上文)
        2.将原来的重定位表复制到新增的节中,并修改目录中重定位表的位置,指向新增的节的RVA
        3.修改imagebase为自定义的imagebase
        4.修改新增节中每一块的RVA

代码如下

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "mycode.h"


int main()
{
	char* sourcefile="C:\\Users\\Admin\\Desktop\\DLL\\TestDllfb.dll";
	char* targetfile="C:\\Users\\Admin\\Desktop\\DLL\\TestDllfb3.dll";
	int newsecsize=0x1000;  //新增节的大小 默认0x1000字节 
	char* ptr=read_file_to_file_buffer(sourcefile);
	
	PE filePE;
	set_pe(ptr,&filePE);

	//判断 节表空闲空间是否够40(节表数据)+40(0)=80个字节
	//不够80字节 需要节表上移 占用dos stub 的空间
	//我们这里按不够来处理  将新增的节表放到dos stub中
	if( *filePE.p_e_lfanew >= 80 )
	{
		printf("dos stub 可以存放80字节 \n");
	}
	else
	{
		printf("dos stub 不可以存放80字节 \n");
		return 0;
	}

	//要复制节表的开始、结束 地址  从PE开始
	int source_sec_no=1; //节表编号
	char* sec_table_begin = (char*)filePE.image_section_header_base + (source_sec_no-1)*40  ;
	char* sec_table_end = (char*)filePE.image_section_header_base + (source_sec_no-1)*40 + 40 ;
	char* pe_sig= filePE.p_signature;
	//printf("当前复制的节表为第%d节 节表的起始地址:%x  节表的结束地址:%x \n",source_sec_no,sec_table_begin - ptr,sec_table_end - ptr);

	//计算重定位表的大小
	newsecsize = *filePE.sizeofblock ;
	//内存对齐
	int zhengshu_newsec= newsecsize / *filePE.p_sectionalignment ;
	int yushu_newsec= newsecsize - zhengshu_newsec*(*filePE.p_sectionalignment);
	int addsize_newsec=(*filePE.p_sectionalignment) - yushu_newsec;
	newsecsize = newsecsize + addsize_newsec;

	//为新文件申请空间  新文件大小为原文件大小+新增的节的大小
	int sizeofp_newfilebuffer=get_file_size(sourcefile) + newsecsize ;
	char* p_newfilebuffer = (char*) malloc(sizeofp_newfilebuffer);
	if( p_newfilebuffer == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	else
	{
		printf("Newfilebuffer地址:%x \n",p_newfilebuffer);
	}
	memset(p_newfilebuffer,0,sizeofp_newfilebuffer);
	
	//将原文件先全部复制到新节
	memcpy(p_newfilebuffer
		,ptr
		,get_file_size(sourcefile) );

	PE newfilePE;
	set_pe(p_newfilebuffer,&newfilePE);

	//新增的节默认给0
	memset( p_newfilebuffer + get_file_size(sourcefile) //目标位置为 文件的结束位置
			,0											
			,newsecsize);								//节大小

	//复制节表
	//修改e_lfanew  节表上移
	*newfilePE.p_e_lfanew = *newfilePE.p_e_lfanew - 80;
	//节表上移
	memcpy(	p_newfilebuffer + *newfilePE.p_e_lfanew
			,ptr + *filePE.p_e_lfanew
			,4+20 + *(filePE.p_sizeofoptionalheader) + *filePE.p_numberofsections*40  //PE、标准PE头、可选PE头、所有节表
		);
	//新增的节 复制到节表后面  包括节表本身40字节 和 40个0 的空字节 共80字节
	memcpy(
		// 文件开始位置       PE位置			   PE大小	标准PE头大小			 可选PE头大小					所有节大小
		 p_newfilebuffer + *newfilePE.p_e_lfanew  +  4   +    20  +     *(filePE.p_sizeofoptionalheader)  +  *filePE.p_numberofsections*40
		,sec_table_begin
		,40
		);
	memset(p_newfilebuffer + *newfilePE.p_e_lfanew  +  4   +    20  +     *(filePE.p_sizeofoptionalheader)  +  *filePE.p_numberofsections*40 + 40
		,0
		,40);

	//刷新PE结构
	set_pe(p_newfilebuffer,&newfilePE); 
	//节的数量加一
	*newfilePE.p_numberofsections = *newfilePE.p_numberofsections+1;
	
	//修改节属性
	//修改节名
	char newname[]="newsec";
	memcpy(newfilePE.image_section_header_base + *filePE.p_numberofsections*40,newname,8);
	//修改在内存中的大小
	*(int*)((char*)newfilePE.pmisc + *filePE.p_numberofsections*40)= newsecsize;

	//修改新增节的内存偏移  根据上一个节的偏移和大小计算																					
	int finalsecmemsize= *(int*)((char*)newfilePE.pmisc + (*newfilePE.p_numberofsections-2)*40 ) > *(int*)((char*)newfilePE.psizeofrawdata+(*newfilePE.p_numberofsections-2)*40)  
		? 
		*(int*)((char*)newfilePE.pmisc + (*newfilePE.p_numberofsections-2)*40 ) : *(int*)((char*)newfilePE.psizeofrawdata+(*newfilePE.p_numberofsections-2)*40);  //取内存大小和文件大小 较大的一个
	int zhengshu =finalsecmemsize/(*newfilePE.p_sectionalignment) ;
	int yushu = finalsecmemsize - zhengshu*(*newfilePE.p_sectionalignment);
	int addsize =  (*newfilePE.p_sectionalignment) - yushu;  //根据内存对齐需要增加的大小                   上一节内存偏移                                                       上一节大小(内存大小或者文件大小)     补齐内存对齐
	*(int*)((char*)newfilePE.pvirtualaddress + (*newfilePE.p_numberofsections-1)*40 ) =  *(int*)((char*)newfilePE.pvirtualaddress + (*newfilePE.p_numberofsections-2)*40) +  finalsecmemsize               +       addsize ; //上一个节的偏移+大小(这里的大小 要判断文件大小和内存大小哪个大  取较大的那个然后根据内存对齐)
	
	//修改在文件中的大小
	*(int*)((char*)newfilePE.psizeofrawdata + *filePE.p_numberofsections*40)= newsecsize;

	//修改新增节的文件偏移  根据上一个节的偏移和大小计算														上一个节的偏移																			上一个节的大小
	*(int*)((char*)newfilePE.ppointertorawdata + (*newfilePE.p_numberofsections-1)*40 ) =  *(int*)((char*)newfilePE.ppointertorawdata + (*newfilePE.p_numberofsections-2)*40 )  +  *(int*)((char*)newfilePE.psizeofrawdata + (*newfilePE.p_numberofsections-2)*40 ) ;

	//修改sizeofimage大小  增加一个节内存对齐后的大小
	zhengshu = *(filePE.pmisc + (source_sec_no-1)*40)/(*newfilePE.p_sectionalignment);
	yushu = *(filePE.pmisc + (source_sec_no-1)*40) - zhengshu * (*newfilePE.p_sectionalignment);
	int finalimgmemsize =  (*newfilePE.p_sectionalignment) - yushu;  
	*newfilePE.p_sizeofimage = *newfilePE.p_sizeofimage + *(filePE.pmisc + (source_sec_no-1)*40) + finalimgmemsize;  //这里应该算内存对齐后的字节 所以要加finalimgmemsize


	//移动重定位表
	int relocation_virtualaddress_foa = trans_RVA_to_FOA(*filePE.pdirectory_relocation_virtualaddress,sourcefile);
	printf("重定位表地址RVA:%x \t FOA:%x \n",*filePE.pdirectory_relocation_virtualaddress,relocation_virtualaddress_foa);
	printf("重定位表大小:%x \n",*filePE.sizeofblock);

	memcpy(p_newfilebuffer + get_file_size(sourcefile)
		,ptr + relocation_virtualaddress_foa  
		,*newfilePE.sizeofblock);

	//更新重定位表的地址
	//最后一个节的RVA
	int lastsec_rva = *(int*)((char*)newfilePE.pvirtualaddress + (*newfilePE.p_numberofsections-1)*40 );
	*newfilePE.pdirectory_relocation_virtualaddress=lastsec_rva;

	//自定义imagebase 并修改移动重定位表每一块的RVA
	//原来的 imagebase=0x10000000
	//修改后 imagebase=0x20000000
	int new_imagebase=*newfilePE.p_imagebase + 0x1000;
	int diff=new_imagebase - *newfilePE.p_imagebase ;  //新旧imagebase的差值 ,后面修改项的RVA时  每一项地址需要加上 diff
	*newfilePE.p_imagebase = new_imagebase;

	//计算新增节 重定位表的FOV
	trans_file_buffer_to_file(p_newfilebuffer,targetfile,sizeofp_newfilebuffer);

	relocation_virtualaddress_foa =  trans_RVA_to_FOA(*newfilePE.pdirectory_relocation_virtualaddress,targetfile);
	int block_base= (int)(p_newfilebuffer+relocation_virtualaddress_foa);	//所有块的起始地址
	int no_block_addr = *(int*)block_base;						//第n块的起始地址
	int no_block_size = *(int*)((char*)block_base+4);			//第n块的大小

	int next_block_addr=0;			//下一块的地址
	int next_block_size=0x9999;		//下一块的大小
	int all_block=0;				//块数
	int all_item=0;					//每一块的项数
	int items=0;					//第几项
	int i,j;
	short block_item;				//每一项的地址
	int block_item_addr;			//地址的低12位
	int attribute;					//地址的高4位
	int new_block_item_addr;		//自定义imagebase 项的RVA
	//修改每一块的RVA
	for( i=1; next_block_size != 0; i++ ) //下一块大小为0时 结束
	{
		items=(no_block_size-8)/2;
		printf("第%d块:原RVA %x 大小%x  \n",i,no_block_addr,no_block_size);
		//项是相对于块的偏移 这里不用+diff了
		no_block_addr=no_block_addr+diff;
		*(int*)((char*)block_base+all_block) = *(int*)((char*)block_base+all_block) + diff;
		printf("第%d块:新RVA %x 大小%x  \n",i,no_block_addr,no_block_size);
	
		all_block=all_block+no_block_size;								//总块数增加
		next_block_addr = *(int*)((char*)block_base+all_block);			//下一块地址
		next_block_size = *(int*)((char*)block_base+all_block+4);		//下一块大小
 
		no_block_size=next_block_size;	//下一块改为当前块
		no_block_addr=next_block_addr;	//
	}

	//将NewFileBuffer 中的内容写入到一个新的exe文件
	trans_file_buffer_to_file(p_newfilebuffer,targetfile,sizeofp_newfilebuffer);

	//释放内存  这里有点bug 先不释放了
	free(p_newfilebuffer);
	p_newfilebuffer=NULL;
	read_file_to_file_buffer_free(ptr);
 
	return 0;
}
 

运行结果

使用PE工具对比修改imagebase前后 RVA的变化

修改前

修改后

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值