手工解析PE(文件注入代码实现)

手工解析PE(文件拉伸)_GNAIXGNAHZ的博客-CSDN博客 这节讲解了硬盘文件向内存载入的过程

手工解析PE(文件注入)_GNAIXGNAHZ的博客-CSDN博客这节讲节了如何手工修改硬盘文件达到代码注入的效果

本次实验的内容是将以上两节结合,在文件载入到内存后,将我们的shellcode嵌入到内存,然后将内存文件导出为硬盘文件,也就是将上面第一节手工注入的内容使用代码实现

原程序执行后,会先调用fun()函数 打印hello,再调用messagebox弹出一个窗口,我们注入的代码是在程序开头调用fun()函数,所以注入后的程序,会多调用一次fun()函数。

之前从内存复制节到硬盘的时候,是复制了整个节,本次实验,除了节本身,还需要多复制我们嵌入的代码,所以下面这个函数做了修改

//将ImageBuffer还原到FileBuffer内存
//int secno 第几个节需要多复制
//int shellcodelength 需要多复制的长度
int trans_image_buffe_to_file_buffer(char* pimagebuffer,char* pfilebuffer,int secno,int shellcodelength)
{
	//找到PE偏移
	int* p_e_lfanew=(int*)pimagebuffer+15; 
	//PE标志
	char* p_signature = pimagebuffer+(*p_e_lfanew);	
	//节数
	short* p_numberofsections=(short*)(p_signature+6);	
 
	//DOS头-节表  这段可以直接复制过去 不用做内存转换  大小在sizeofheaders 记录着
	int* p_sizeofheaders=(int*)(p_signature+84);
	memcpy(pfilebuffer,pimagebuffer,*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,pimagebuffer + (*pvirtualaddress),pimagebuffer + (*pvirtualaddress)+ *pmisc);
		//当 节为 代码追加节时  复制的内容 要多复制一些
		if( i == secno)
		{
		//								    偏移	                                
			memcpy( pfilebuffer    +  (*ppointertorawdata)      //FileBuffer中   节的位置
				   ,pimagebuffer   +  (*pvirtualaddress)	    //ImageBuffer中  节的位置 
				   ,*pmisc+shellcodelength);				    //节大小 +  shellcode大小
		}
		else
		{
			//								    偏移	                                
			memcpy( pfilebuffer    +  (*ppointertorawdata)      //FileBuffer中   节的位置
				   ,pimagebuffer   +  (*pvirtualaddress)	    //ImageBuffer中  节的位置 
				   ,*pmisc);									//节大小
		}

		image_section_header_base=image_section_header_base+40;
	}
	return 0;
}

下面就是主函数了

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "mycode.h"
 
// 文件 -> FileBuffer -> ImageBuffer -> NewFileBuffer -> 文件
//          硬盘对齐       内存对齐       硬盘对齐

char shellcode[] =
{
	0xE8,00,00,00,00,
	0xE9,00,00,00,00
};
int targetcalladdress= 0x00401020;    //要call的地址
int targetjumpaddress= 0x00;		  //要jmp的地址

int main()
{
	char* sourcefile="D:\\CODE\\t\\Debug\\t.exe";
	char* targetfile="D:\\CODE\\t\\Debug\\t2.exe";
	int shllcodelength=sizeof(shellcode);
	char* ptr=read_file_to_file_buffer(sourcefile);
	
	PE filePE;
	set_pe(ptr,&filePE);

	//为ImageBuffer申请空间
	char* p_imagebuffer;
	p_imagebuffer = (char*) malloc(*filePE.p_sizeofimage);
	if( p_imagebuffer == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	else
	{
		printf("ImageBuffer地址:%x \n",p_imagebuffer);
	}
	memset(p_imagebuffer,0,*filePE.p_sizeofimage);
	//FileBuffer内容 复制到 ImageBuffer
	trans_file_buffer_to_image_buffer(ptr,p_imagebuffer);
	PE imagePE;
	set_pe(p_imagebuffer,&imagePE);

	//循环判断 节的空闲空间是否能够存放shellcode
	int i=1;
	int E8addr;
	int E9addr;

	for(i;i <= *imagePE.p_numberofsections;i++)
	{
		if( (*imagePE.psizeofrawdata-*imagePE.pmisc) >= shllcodelength )
		{
			printf("第 %d 节 空闲大小为 %d  shellcode 大小为 %d \n",i,(*imagePE.psizeofrawdata-*imagePE.pmisc),shllcodelength);
			//将shellcode 追加到第i节 
			memcpy(
					(char*)p_imagebuffer + *imagePE.pvirtualaddress + *imagePE.pmisc // 目标地址为imagebuffer中的地址  p_imagebuffer+节偏移+节大小
					,shellcode
					,shllcodelength
					);
			//E8的地址 是E8在内存中的地址 所以用imagebase定位
			E8addr = *imagePE.p_imagebase + *imagePE.pvirtualaddress + *imagePE.pmisc;
			E9addr = E8addr+5;
			break;
		}
		else if( i+1 < *imagePE.p_numberofsections  ) 
		{
			imagePE.psizeofrawdata=(int*)((char*)imagePE.psizeofrawdata+40);
			imagePE.pmisc=(int*)((char*)imagePE.pmisc+40);
			imagePE.pvirtualaddress=(int*)((char*)imagePE.pvirtualaddress+40);
		}
		else 
		{
			printf("所有节 空间都不够 \n");
		}
	}
	//修改E8 calladdress  =  目标地址 - E8内存地址 - 5
	int calladdress = targetcalladdress - E8addr - 5;
	memcpy(
		(char*)p_imagebuffer + *imagePE.pvirtualaddress + *imagePE.pmisc + 1
		,&calladdress
		,4
		);
	//修改E9 jmpaddress	  =			 原始入口点								E9内存地址
	int jmpaddress = *imagePE.p_imagebase + *imagePE.p_addressofentrypoint - E9addr - 5;
		memcpy(
		(char*)p_imagebuffer + *imagePE.pvirtualaddress + *imagePE.pmisc + 1 + 5
		,&jmpaddress
		,4
		);
	//修改OEP 入口点 = 追加的代码的地址  OEP位置也就是E8的位置 如果有传参 需要重新计算
	//OEP是相对于imagebase的偏移量  不要写内存中的入口地址 要写imagebase的偏移量
	int oepaddress = E8addr - *imagePE.p_imagebase;
	*imagePE.p_addressofentrypoint=oepaddress;

	//ImageBuffer    还原到  NewFileBuffer 
	//为newfilebuffer申请空间
	char* p_newfilebuffer;
	//最后一节的偏移+大小 就是总共的文件大小
	int lastsection_no=*imagePE.p_numberofsections;
	//这里相比之前那个版本改进了一下算法  计算出文件大小 而不是按原来的文件大小
	//每个节表40个字节 N*40就是最后一个节的地址
	int lastsection_offset=*(int*)((char*)imagePE.ppointertorawdata+40*(lastsection_no-1));
	int lastsection_size=*(int*)((char*)imagePE.psizeofrawdata+40*(lastsection_no-1));
	//printf("大小%d %x %x \n",lastsection_size+lastsection_offset,lastsection_size,lastsection_offset );
	int sizeofp_newfilebuffer=lastsection_offset+lastsection_size;
	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);
	//ImageBuffer    还原到 NewFileBuffer 
	trans_image_buffe_to_file_buffer(p_imagebuffer,p_newfilebuffer,i,shllcodelength);
	//将NewFileBuffer 中的内容写入到一个新的exe文件
	trans_file_buffer_to_file(p_newfilebuffer,targetfile,sizeofp_newfilebuffer);
 
	//释放内存
	free(p_imagebuffer);
	p_imagebuffer=NULL;
	free(p_newfilebuffer);
	p_newfilebuffer=NULL;
	read_file_to_file_buffer_free(ptr);
 
	return 0;
}
 
/*

fun() :00401020

E8
00401020 - ( 35f5 + 5)
00401020 - ( 400000 + 1000+(35f5+5-400) )
00401020 - 4041FA = FFFF CE26

E9
400000+000013A0=4013A0
4013A0 - (35fa + 5 )
4013A0 - ( 400000 + 1000+(35fa+5-400) )
4013A0 - 4041FF =FFFF D1A1

OEP
13A0 ->  35f5 - >  1000 + (35f5 - 400) -> 41F5

*/

运行结果如下

 未注入的程序 只会调用一次fun()函数

注入之后的程序 会在程序开始的时候 也调用一次fun()函数 ,一共调用两次

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值