手工解析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()函数 ,一共调用两次