手工解析PE(节表)

上一篇文章解析了PE的前两个部分DOS头和NT头,这篇文章继续解析NT头的下一个部分 节表

首先我们需要算出第一个节表的地址,可以从PE标志开始算,

1.节表地址:PE标记[4字节]+标准PE头[20字节]+可选PE头[p_sizeofoptionalheader]

其中PE标记是固定的4字节,标准PE头是固定的20字节,可选PE头的大小不确定,但是在标准PE头中sizeofoptionalheader可以取到,所以最后还要加上sizeofoptionalheader的值

2.节表的个数:在标准PE头中numberofsections记录着

3.每个节表的大小:根据PE结构图可以看到,一个节表是40个字节

有了节表个数就可以确定循环次数了,每次循环结束后,节表的地址要增加40 到下一个节表地址

#include <stdio.h>
#include <malloc.h>
#include <string.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;
}
 
int main()
{
	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("ptr:%x \n",ptr);
	//将文件内容 读取到内存
	FILE* fp = fopen(filename,"rb+");
	fread(ptr,file_size,1,fp);
	fclose(fp);
	

	//找到PE偏移
	int* p_e_lfanew=(int*)ptr+15; 
	printf("PE偏移:\t\t\t%x	\n",*p_e_lfanew);
	//PE标志
	char* p_signature = ptr+(*p_e_lfanew);				
	//标准PE头中的 节数量
	short* p_numberofsections=(short*)(p_signature+6);	
	printf("节数量:\t\t\t%x  \n",*p_numberofsections);
	//可选PE头大小
	short* p_sizeofoptionalheader=(short*)(p_signature+20);	
	printf("可选PE头大小:\t\t%x -> %d	\n",*p_sizeofoptionalheader,*p_sizeofoptionalheader);
	//内存镜像基址
	int* p_imagebase=(int*)(p_signature+52);				
	printf("内存镜像基址:\t\t%x \n",*p_imagebase);
	//内存对齐字节数
	int* p_sectionalignment=(int*)(p_signature+56);	
	printf("内存对齐字节数:\t\t%x  \n",*p_sectionalignment);
	//文件对齐字节数
	int* p_filealignment=(int*)(p_signature+60);	
	printf("文件对齐字节数:\t\t%x \n",*p_filealignment);
 
	//节表地址:DOS头[64字节]+PE标记[4字节]+标准PE头[20字节]+可选PE头[p_sizeofoptionalheader] =312=0x138
	int varsize=*p_sizeofoptionalheader;
	//char* image_section_header_base=ptr+64+4+20+(*p_sizeofoptionalheader);
	char* image_section_header_base=p_signature+4+20+(*p_sizeofoptionalheader);
	printf("节表地址:\t\t%x \n",image_section_header_base);
	
	/*
	每个节表40个字节
	char name[8]																	8字节
	int* misc 					节在文件中没有对齐时的真实大小						4字节
	int* p_virtualaddress		节在内存中的偏移(拉伸后,使用时需要+imagebase)		4字节	
	int* p_sizeofrawdata		节在文件中对齐后的大小								4字节		
	int* p_pointertorawdata		节在文件中的偏移									4字节
	int* p_pointertorelocations														4字节
	int* p_pointertolinenumbers														4字节
	short* p_numberofrelocations													2字节
	short* p_numberoflinenumbers													2字节
	int* p_characteristics		节的属性											4字节
	*/

	int i=1;
	for(i;i<=*p_numberofsections;i++)
	{
		printf("第 %d 个节:\n",i);
		//printf("image_section_header_base:%x \n",image_section_header_base);
		char name[9];
		memcpy(name,image_section_header_base,8);
		name[8]='\0';

		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);
		int *ppointertorelocations=(int*)(image_section_header_base+24);
		int *ppointertolinenumbers=(int*)(image_section_header_base+28);
		short *pnumberofrelocations=(short*)(image_section_header_base+32);
		short *pnumberoflinenumbers=(short*)(image_section_header_base+34);
		int *pcharacteristics=(int*)(image_section_header_base+36);

		printf("\t name:%s \n",name);
		printf("\t misc:%x \n",*pmisc);
		printf("\t virtualaddress:%x \n",*pvirtualaddress);
		printf("\t sizeofrawdata:%x \n",*psizeofrawdata);
		printf("\t pointertorawdata:%x \n",*ppointertorawdata);
		printf("\t pointertorelocations:%x \n",*ppointertorelocations);
		printf("\t pointertolinenumbers:%x \n",*ppointertolinenumbers);
		printf("\t numberofrelocations:%x \n",*pnumberofrelocations);
		printf("\t numberoflinenumbers:%x \n",*pnumberoflinenumbers);
		printf("\t characteristics:%x \n",*pcharacteristics);
		

		image_section_header_base=image_section_header_base+40;
	}

 
	//释放内存
	free(ptr);
	ptr=NULL;
	return 0;
}
 
 

执行结果如图

拿PE程序解析一下  结果没有差异

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值