手工解析PE(RVA转FOA)

RVA是程序加载到内存中的地址

FOA是程序在硬盘中的地址

我们知道 程序运行时是加载到内存中的,比如程序执行了一个函数,此时 我们可以通过调试知道函数所在内存中的地址,但是 如果想要修改这个函数,那就需要知道他对应在物理文件中的哪个位置,就是将exe文件加载到内存的逆过程,为了实现通过RVA找FOA的功能,写出了以下代码

思路就是有了RVA的地址,根据内存的节表,找出RVA所在的节 n 和 节的偏移量 x,然后再根据文件的节表,找出 n 所对应的物理 节位置,再加上偏移量x  就是FOA

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "mycode.h"
 
int main()
{
    //用于存放硬盘中 和 内存中的PE结构
	PE PE_file;
	PE PE_memory;
	//找出文件中的PE结构
	//char* ptr=read_file_to_file_buffer("D:\\T\\zx.exe");
	char* ptr=read_file_to_file_buffer("D:\\CODE\\DLL\\TestDll.dll");
	set_pe(ptr,&PE_file);
	//为ImageBuffer申请空间
	char* p_imagebuffer;
	p_imagebuffer = (char*) malloc(*PE_file.p_sizeofimage);
	//找出内存中的PE结构
	if( p_imagebuffer == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	else
	{	
		memset(p_imagebuffer,0,*PE_file.p_sizeofimage);
		//这里一定要注意 p_imagebuffer里面有数据之后 才能set_pe 
		//FileBuffer内容 复制到 ImageBuffer
		trans_file_buffer_to_image_buffer(ptr,p_imagebuffer);
		set_pe(p_imagebuffer,&PE_memory);
		printf("ImageBuffer范围:%x --> %x \n",p_imagebuffer,p_imagebuffer+(*PE_file.p_sizeofimage));
	}
	//节表大小
	int sectablesize = *PE_file.p_numberofsections*40;
	//节表结束位置
	int sectableend = (int)(PE_memory.image_section_header_base + sectablesize);
	printf("节表范围:%x --> %x \n",PE_memory.image_section_header_base,sectableend);

	int RVA;
	int FOA;
	int section_no;			//RVA属于第几节
	int section_offset;		//RVA节偏移
	printf("请输入RVA值\n");
	scanf("%p",&RVA);
	printf("RVA = %x \n",RVA);
	
	//如果RVA在节表结束之前 那么不用转换 直接减去基址就是FOA
	if( RVA < sectableend )
	{
		FOA = RVA - (int)p_imagebuffer;
		printf("节表之前 无需转化 FOA = %x \n",FOA);
	}
	else
	{
	//找出 RVA在内存中节数 和 节偏移量
	int rva_offset = (char*)RVA - p_imagebuffer;
	//判断是第几个节
	int i=1;
	for(i;i<=*PE_memory.p_numberofsections;i++)
	{
		// RVA的偏移量  在 两点之间 此时的节数才是我们要的
		if(     rva_offset >=    *PE_memory.pvirtualaddress 
			&&  rva_offset <   ( *PE_memory.pvirtualaddress + (*PE_memory.pmisc) ) )
		{ 
			section_no=i;
			section_offset = rva_offset - *PE_memory.pvirtualaddress ;
			printf("RVA是第%d个节   节偏移量为%d \n",section_no,section_offset);
			break;
		}
		//image_section_header_base=image_section_header_base+40;
		//这里注意 pvirtualaddress 要往下走40个字节(一个节表的大小),
		//因为pvirtualaddress 是int 所以指针需要加10 即可 
		PE_memory.pvirtualaddress=PE_memory.pvirtualaddress+10;	
        PE_memory.pmisc=PE_memory.pmisc+10;
	}
	//根据 节数  找出文件中节的位置  第n节 需要遍历n-1次
	for(i=1;i<section_no;i++)
	{
		PE_file.ppointertorawdata=PE_file.ppointertorawdata+10;	
	}
	//加上 节偏移量 得出最终在文件中的位置
	printf("FOA = %x \n",*PE_file.ppointertorawdata+section_offset);
	}
	
	//释放内存
	free(p_imagebuffer);
	p_imagebuffer=NULL;
	read_file_to_file_buffer_free(ptr);
	return 0;
}
 

<mycode.h>

typedef struct _PE
{
	//DOS头
	short* p_e_magic;	//MZ标记 用于判断是否为可执行文件
	short* p_e_cblp;
	short* p_e_cp;
	short* p_e_crlc;
	short* p_e_cparhdr;
	short* p_e_minalloc;
	short* p_e_maxalloc;
	short* p_e_ss;
	short* p_e_sp;
	short* p_e_csum;
	short* p_e_ip;
	short* p_e_cs;
	short* p_e_lfarlc;
	short* p_e_ovno;
	short* p_e_res;
	short* p_e_oemid;
	short* p_e_oeminfo;
	short* p_e_res2;
	int* p_e_lfanew;  //第60个字节  一行16个字节 第四行的最后四个字节就是 e_lfanew,从文件开始偏移 *p_e_lfanew 就是PE
	//PE标记
	char* p_signature;
	//标准PE头
	short* p_machine;	//程序运行的CPU型号   0x0任何处理器 0x14C 386及后续处理器
	short* p_numberofsections;	//节的总数
	int* p_timedatestamp;
	int* p_pointertosymboltable;
	int* p_numberofsymbols;
	short* p_sizeofoptionalheader;	//可选PE头的大小  32位默认E0 64位默认F0 可以自定义
	short* p_characteristics;
	//可选PE头
	short* p_magic;	//程序运行的CPU型号   0x0任何处理器 0x14C 386及后续处理器
	char* p_majorlinkerversion;
	char* p_minorlinkerversion;
	int* p_sizeofcode; //所有代码节的和 内存对齐字节数的整数倍
	int* p_sizeofinitializeddata;	//初始化的数据大小
	int* p_sizeofuninitializeddata;	//未初始化的数据的大小
	int* p_addressofentrypoint;		//程序入口
	int* p_baseofcode;
	int* p_baseofdata;
	int* p_imagebase;				//内存镜像基址
	int* p_sectionalignment;	//内存对齐字节数
	int* p_filealignment;	//文件对齐字节数
	short* p_majoroperatingsystemversion;
	short* p_minoroperatingsystemversion;
	short* p_majorimageversion;
	short* p_minorimageversion;
	short* p_majorsubsystemversion;
	short* p_minorsubsystemversion;
	int* p_win32versionvalue;
	int* p_sizeofimage;
	int* p_sizeofheaders;
	int* p_checksum;
	short* p_subsystem;
	short* p_dllcharacteristics;
	int* p_sizeofstackreserve;
	int* p_sizeofstackcommit;
	int* p_sizeofheapreserve;
	int* p_sizeofheapcommit;
	int* p_loaderflags;
	int* p_numberofrvaandsizes;
	int* p_datadirectory;
	//节表
	char* image_section_header_base;
	char name[9];
	int *pmisc;
	int *pvirtualaddress;
	int *psizeofrawdata;
	int *ppointertorawdata;
	int *ppointertorelocations;
	int *ppointertolinenumbers;
	short *pnumberofrelocations;
	short *pnumberoflinenumbers;
	int *pcharacteristics;
}PE;


//获取文件大小
int get_file_size(char* filename);
//将文件读取到内存中
char* read_file_to_file_buffer(char* filename);
//释放 将文件读取到内存
void  read_file_to_file_buffer_free(char* ptr);
//FileBuffer内容 复制到 ImageBuffer
int trans_file_buffer_to_image_buffer(char* pfilebuffer,char* pimagebuffer);
//设置PE
void set_pe(char* ptr,PE* p_PE );

<mycode.cpp>

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

//获取文件大小
int get_file_size(char* filename)
{
	FILE* fp = fopen(filename,"r");
	int size;
	if( fp == NULL )
	{
		printf("open fail \n");
		return -1;
	}
	fseek(fp,0,SEEK_END);
	size=ftell(fp);
	fclose(fp);
	return size;
}

//将文件读取到内存中 返回读取到哪个内存地址
char* read_file_to_file_buffer(char* filename)
{
	//char* filename="D:\\T\\zx.exe"; 
	int file_size=get_file_size(filename);

	//申请空间
	char* ptr;
	ptr = (char*) malloc(file_size);
	if( ptr == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	memset(ptr,0,file_size);
	printf("文件读取到:%x \n",ptr);
	//将文件内容 读取到内存  
    //注意 这里读取文件 一定要 rb+  否则 读取的文件会缺少字节
	FILE* fp = fopen(filename,"rb+");
	fread(ptr,file_size,1,fp);
	fclose(fp);
	
	return ptr;
}
//释放内存
void read_file_to_file_buffer_free(char* ptr)
{
	free(ptr);
	ptr=NULL;
}

//FileBuffer 转化到 ImageBuffer
int trans_file_buffer_to_image_buffer(char* pfilebuffer,char* pimagebuffer)
{
	//所有节的范围
	char* section_begin=NULL;
	char* section_end=NULL;

	//找到PE偏移
	int* p_e_lfanew=(int*)pfilebuffer+15; 
	//PE标志
	char* p_signature = pfilebuffer+(*p_e_lfanew);	
	//节数
	short* p_numberofsections=(short*)(p_signature+6);	

	//DOS头-节表  这段可以直接复制过去 不用做内存转换  大小在sizeofheaders 记录着
	int* p_sizeofheaders=(int*)(p_signature+84);
	memcpy(pimagebuffer,pfilebuffer,*p_sizeofheaders);
	
	//节区 复制到 ImageBuffer  每节大小为 sizeofrawdata
	//遍历节表  复制每一节
	short* p_sizeofoptionalheader=(short*)(p_signature+20);	
	char* image_section_header_base=p_signature+4+20+(*p_sizeofoptionalheader);

	int i=1;
	for(i;i<=*p_numberofsections;i++)
	{
		int *pmisc=(int*)(image_section_header_base+8);					//节内存中的大小
		int *pvirtualaddress=(int*)(image_section_header_base+12);		//节内存中的偏移
		int *psizeofrawdata=(int*)(image_section_header_base+16);		//节文件中的大小
		int *ppointertorawdata=(int*)(image_section_header_base+20);	//节文件中的偏移

		printf("复制第 %d 个节	起始地址:%x \t 结束地址:%x \n",i,pfilebuffer + (*ppointertorawdata),pfilebuffer + (*ppointertorawdata)+ *psizeofrawdata);
		printf("到第 %d 个节	起始地址:%x \t 结束地址:%x \n",i,pimagebuffer + (*pvirtualaddress),pimagebuffer + (*pvirtualaddress)+ *pmisc);
		
	//								    偏移	                                
		memcpy( pimagebuffer  +  (*pvirtualaddress)      //Image中 节的位置
			   ,pfilebuffer   +  (*ppointertorawdata)    //文件中  节的位置 
			   ,*psizeofrawdata);						 //节大小
	
	//获取所有节的范围
		if( i == 1 )
		{
			section_begin=pimagebuffer + (*pvirtualaddress);
		}
		if( i == *p_numberofsections )
		{
			section_end=pimagebuffer + (*pvirtualaddress) + *pmisc;
		}

		image_section_header_base=image_section_header_base+40;
	}

	printf("RVA值 范围: %x - %x \n",section_begin,section_end);
	return 0;
}

void set_pe(char* ptr,PE* p_PE )
{
	//DOS头
	p_PE->p_e_magic=(short*)ptr;	//MZ标记 用于判断是否为可执行文件
	p_PE->p_e_cblp=(short*)ptr+1;
	p_PE->p_e_cp=(short*)ptr+2;
	p_PE->p_e_crlc=(short*)ptr+3;
	p_PE->p_e_cparhdr=(short*)ptr+4;
	p_PE->p_e_minalloc=(short*)ptr+5;
	p_PE->p_e_maxalloc=(short*)ptr+6;
	p_PE->p_e_ss=(short*)ptr+7;
	p_PE->p_e_sp=(short*)ptr+8;
	p_PE->p_e_csum=(short*)ptr+9;
	p_PE->p_e_ip=(short*)ptr+10;
	p_PE->p_e_cs=(short*)ptr+11;
	p_PE->p_e_lfarlc=(short*)ptr+12;
	p_PE->p_e_ovno=(short*)ptr+13;
	p_PE->p_e_res=(short*)ptr+17;
	p_PE->p_e_oemid=(short*)ptr+18;
	p_PE->p_e_oeminfo=(short*)ptr+19;
	p_PE->p_e_res2=(short*)ptr+20;
	p_PE->p_e_lfanew=(int*)ptr+15;  //第60个字节  一行16个字节 第四行的最后四个字节就是 e_lfanew,从文件开始偏移 *p_e_lfanew 就是PE
	//PE标记
	p_PE->p_signature = ptr+(*(p_PE->p_e_lfanew));
	//标准PE头
	p_PE->p_machine=(short*)(p_PE->p_signature+4);	//程序运行的CPU型号   0x0任何处理器 0x14C 386及后续处理器
	p_PE->p_numberofsections=(short*)(p_PE->p_signature+6);	//节的总数
	p_PE->p_timedatestamp=(int*)(p_PE->p_signature+8);
	p_PE->p_pointertosymboltable=(int*)(p_PE->p_signature+12);
	p_PE->p_numberofsymbols=(int*)(p_PE->p_signature+16);
	p_PE->p_sizeofoptionalheader=(short*)(p_PE->p_signature+20);	//可选PE头的大小  32位默认E0 64位默认F0 可以自定义
	p_PE->p_characteristics=(short*)(p_PE->p_signature+22);
	//可选PE头
	p_PE->p_magic=(short*)(p_PE->p_signature+24);	//程序运行的CPU型号   0x0任何处理器 0x14C 386及后续处理器
	p_PE->p_majorlinkerversion=(char*)(p_PE->p_signature+26);
	p_PE->p_minorlinkerversion=(char*)(p_PE->p_signature+27);
	p_PE->p_sizeofcode=(int*)(p_PE->p_signature+28); //所有代码节的和 内存对齐字节数的整数倍
	p_PE->p_sizeofinitializeddata=(int*)(p_PE->p_signature+32);	//初始化的数据大小
	p_PE->p_sizeofuninitializeddata=(int*)(p_PE->p_signature+36);	//未初始化的数据的大小
	p_PE->p_addressofentrypoint=(int*)(p_PE->p_signature+40);		//程序入口
	p_PE->p_baseofcode=(int*)(p_PE->p_signature+44);
	p_PE->p_baseofdata=(int*)(p_PE->p_signature+48);
	p_PE->p_imagebase=(int*)(p_PE->p_signature+52);				//内存镜像基址
	p_PE->p_sectionalignment=(int*)(p_PE->p_signature+56);	//内存对齐字节数
	p_PE->p_filealignment=(int*)(p_PE->p_signature+60);	//文件对齐字节数
	p_PE->p_majoroperatingsystemversion=(short*)(p_PE->p_signature+64);
	p_PE->p_minoroperatingsystemversion=(short*)(p_PE->p_signature+66);
	p_PE->p_majorimageversion=(short*)(p_PE->p_signature+68);
	p_PE->p_minorimageversion=(short*)(p_PE->p_signature+70);
	p_PE->p_majorsubsystemversion=(short*)(p_PE->p_signature+72);
	p_PE->p_minorsubsystemversion=(short*)(p_PE->p_signature+74);
	p_PE->p_win32versionvalue=(int*)(p_PE->p_signature+76);
	p_PE->p_sizeofimage=(int*)(p_PE->p_signature+80);
	p_PE->p_sizeofheaders=(int*)(p_PE->p_signature+84);
	p_PE->p_checksum=(int*)(p_PE->p_signature+88);
	p_PE->p_subsystem=(short*)(p_PE->p_signature+92);
	p_PE->p_dllcharacteristics=(short*)(p_PE->p_signature+94);
	p_PE->p_sizeofstackreserve=(int*)(p_PE->p_signature+96);
	p_PE->p_sizeofstackcommit=(int*)(p_PE->p_signature+100);
	p_PE->p_sizeofheapreserve=(int*)(p_PE->p_signature+104);
	p_PE->p_sizeofheapcommit=(int*)(p_PE->p_signature+108);
	p_PE->p_loaderflags=(int*)(p_PE->p_signature+112);
	p_PE->p_numberofrvaandsizes=(int*)(p_PE->p_signature+116);
	p_PE->p_datadirectory=(int*)(p_PE->p_signature+120);
	//节表
	p_PE->image_section_header_base=(p_PE->p_signature)+4+20+ *(p_PE->p_sizeofoptionalheader);
	memcpy(p_PE->name,p_PE->image_section_header_base,8);
	p_PE->name[8]='\0';
	p_PE->pmisc=(int*)(p_PE->image_section_header_base+8);
	p_PE->pvirtualaddress=(int*)(p_PE->image_section_header_base+12);
	p_PE->psizeofrawdata=(int*)(p_PE->image_section_header_base+16);
	p_PE->ppointertorawdata=(int*)(p_PE->image_section_header_base+20);
	p_PE->ppointertorelocations=(int*)(p_PE->image_section_header_base+24);
	p_PE->ppointertolinenumbers=(int*)(p_PE->image_section_header_base+28);
	p_PE->pnumberofrelocations=(short*)(p_PE->image_section_header_base+32);
	p_PE->pnumberoflinenumbers=(short*)(p_PE->image_section_header_base+34);
	p_PE->pcharacteristics=(int*)(p_PE->image_section_header_base+36);
}

运行结果如下

用第二节地址验证    结果与PE软件分析一致

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值