本次的实验内容是将重定位表移动到新增的节中,并且手工修改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的变化
修改前
修改后