[原创]OdScript+VC 练习还原重定位表
用VC2010编译出来的EXE程序跟DLL很像,在PE文件里的基址一般写着0X400000,但是一般不会被加载到这个位置。这就意味着,如果加壳工具去除了EXE程序的原始重定位表,我们似乎只能用两种方法来处理壳:1.在壳代码中强制它以0X400000为重定位基址,然后DUMP 2.像处理DLL一样修复它的重定位表。本文以UPX加EXE为例试验了第二种方法,修复EXE的重定位与修复DLL的重定位原理差不多,坛上帖子多,我就不废话了。
大致操作过程如下:
1.使用的ESP定律迅速找到OEP 2.找到OEP以后进入原程序的代码,找几个需要重定位的地址,下内存断点,中断两三次后会发现类似下面这样的代码,拦截ebx,保存起来后处理一下即可得到重定位表。
add eax,esi
mov [ebx],eax
详细操作见OD代码:
var bp1rva=10546 var pcurrmem_ori=0 var pcurrmem var membase=0 var memsize=2000 var hdaddr=0 msgyn "是否要执行本脚本?" cmp 0,$RESULT jnz next next: mov memsize,2000 alloc memsize //分配内存用作重定位表的修复 mov pcurrmem,$RESULT mov pcurrmem_ori,pcurrmem log pcurrmem gmemi eip,MEMORYOWNER mov membase,$RESULT //获取当前加载基址 mov bp1rva,10546 add bp1rva,membase bp bp1rva //在这个地址下断 //ESP定律 sti mov hdaddr,esp log hdaddr bphws hdaddr,"r" run eob manytimes manytimes: //会先停在bp1 cmp eip,bp1rva jnz maybetheend mov [pcurrmem],ebx //保存即可 add pcurrmem,4//指针加4 run maybetheend: dm pcurrmem_ori,memsize,"d:\desktop\reloc.hex" bphwc hdaddr
脚本运行完后的文件,有一个明显的跳转,那儿就是OEP了。
DUMP下修复一下引入表:

处理过后的文件:

如果是以前,处理到这儿已经OK。但是由于它可能加载到别的基址上去,这肯定会导致程序崩溃的。
现在可以使用一些小工具处理一下,让EXE在固定的基址启动,(这是上述提到的方法1)如:

看看dump下来的程序的重定位表:

看这数量基本就可以猜出这不是原程序的引入表了。这是upx外壳的重定位表。
我们需要重建这个。
上述代码处理完后的重定位文件的内容大致如下(其实可以在就在OD脚本里处理的,只是个人感觉比较麻烦,于是转到VC里处理):

写个VC小程序处理一下:(需要我自己的开发库,看看代码即可无法编译的,嘿嘿,除非你愿意写把所有函数和结构都补全)
char* pefile="133_.exe";
char* relocfile="reloc.hex";
char* deal_relocfile="dealed_reloc.hex";
DWORD currbase=0x1330000;
//将数值存储到对应的数组中
void log(preloc_tbl arr[],PDWORD pval)
{
dwst dwval;
memset(&dwval,0,sizeof(dwval));
memcpy(&dwval,pval,sizeof(dwval));
if(dwval.high4>NUM)
{
showdbg("并不是只有1000-f000这16种副基址");
return;
}
if(dwval.high4==0)
{
showdbg("文件读入了异常数据,请确认");
}
wst wstitem;
memset(&wstitem,0,sizeof(wstitem));
wstitem.low12=dwval.low12;
wstitem.high4=RELOC_SIGN;
preloc_tbl ptmp=arr[dwval.high4-1];
memcpy((ptmp->witem)+ptmp->dwblock,&wstitem,sizeof(wstitem));
ptmp->dwblock++;
}
void makenewreloc(preloc_tbl arr[],PBYTE& finalmem,DWORD& finalmemsize)
{
struct
{
PBYTE pmem;
DWORD memsize;
} finalpart[NUM]={0};
DWORD dwTotalSize=0;
DWORD dwtmpsize;
for(int i=0;i<NUM;i++)
{
if(arr[i]==NULL)
break;//可以确定这已经是最后的一组了
if(arr[i]->dwblock==0)
continue;//没有纪录的组
dwtmpsize=(arr[i]->dwblock)*2+8+2;//项的数目(word)*2+头8个字符+2个结束符
dwTotalSize+=dwtmpsize;
finalpart[i].memsize=dwtmpsize;
finalpart[i].pmem=BMD_AllocMem(dwtmpsize);
DWORD dwtmptmp=(i+1)*0x1000;
memcpy(finalpart[i].pmem+0,&dwtmptmp,sizeof(dwtmptmp));
memcpy(finalpart[i].pmem+4,&dwtmpsize,sizeof(dwtmpsize));
memcpy(finalpart[i].pmem+8,arr[i]->witem,dwtmpsize-8);
}
finalmemsize=dwTotalSize;
finalmem=BMD_AllocMem(finalmemsize);
DWORD pcurr=0;
for(int i=0;i<NUM;i++)
{
if(finalpart[i].pmem==NULL)
continue;
memcpy(finalmem+pcurr,finalpart[i].pmem,finalpart[i].memsize);
pcurr+=finalpart[i].memsize;
}
//清理内存
for(int i=0;i<NUM;i++)
{
if(finalpart[i].pmem!=NULL)
BMD_FreeMem(finalpart[i].pmem);
}
}
void main()
{
DWORD filesize;
PBYTE mem;
if(BMD_FAIL==BMD_ReadFile(relocfile,&mem,&filesize))
{
showdbg("read fail");
goto fail;
}
//建立分别用于存储各个基址的重定位数组
preloc_tbl arr_reloc_tbl[NUM]={0};
for(int i=0;i<NUM;i++)
{
arr_reloc_tbl[i]=(preloc_tbl)BMD_AllocMem(sizeof(reloc_tbl));
arr_reloc_tbl[i]->dwmembase=(i+1)*0x1000;
arr_reloc_tbl[i]->dwblock=0;
arr_reloc_tbl[i]->witem=(WORD*)BMD_AllocMem(filesize);
}
//处理文件在外部大循环指针
PDWORD pdw=(PDWORD)mem;
while(*pdw!=0)
{
//主循环处理
log(arr_reloc_tbl,pdw);
pdw++;
}
//最终汇总
PBYTE finalmem;
DWORD finalmemsize;
makenewreloc(arr_reloc_tbl,finalmem,finalmemsize);
if(BMD_SUCC==BMD_WriteFile(deal_relocfile,finalmem,finalmemsize))
{
MessageBoxA(0,deal_relocfile,"成功重建输入表",0);
}
//异常处理
fail:
if(mem!=NULL)
BMD_FreeMem(mem);
for(int i=0;i<NUM;i++)
{
if(arr_reloc_tbl[i]!=NULL)
BMD_FreeMem(arr_reloc_tbl[i]);
if(arr_reloc_tbl[i]->witem!=NULL)
BMD_FreeMem(arr_reloc_tbl[i]->witem);
}
if(finalmem!=NULL)
BMD_FreeMem(finalmem);
return;
}最后一步就是手动修改数据目录了:(添加个新节后,导入数据)

OK,收工~
