纯手工完美恢复IAT

2018-12-04  本文已影响22人  看雪学院

程序脱壳后都需要修复IAT,否则程序运行会出错,以前都是用工具来修复,工具一般都是新加一个节,然后将导入函数放在新的节里面(这个函数会比实际程序多一些,但不影响程序的运行,但个别定位出问题的多余函数会影响到程序运行),再修复跟函数相关的重定位项,其实有另一种方式可以完美修复IAT,今天就给大家交流一下,希望多多指正。

首先用ASPack给程序加一个壳,然后进行脱壳,脱壳过程很简单,这里就不详细叙述了。

运行到OEP用OllyDump导出当前进程。

注意:把重建输入表前面的对勾去掉,因为我们要手工恢复,所以就不需要这个选项了。

用工具查看输入表信息:

------------------------------------DataDirectory

No.  VirtualAddress  Size

0   00000000    00000000    EXPORT

1   00067FAC    00000240    IMPORT

2   00061000    00005400    RESOURCE

3   00000000    00000000    EXCEPTION

4   00000000    00000000    SECURITY

5   00067F54    00000008    BASERELOC

6   00000000    00000000    DEBUG

7   00000000    00000000    ARCHITECTURE

8   00000000    00000000    GLOBALPTR

9   00067F3C    00000018    TLS

10  00000000    00000000    LOAD_CONFIG

11  00000000    00000000    BOUND_IMPORT

12  00000000    00000000    IAT

13  00000000    00000000    DELAY_IMPORT

14  00000000    00000000    COM_DESCRIPTOR

15  00000000    00100000    ...

输入表的地址是:67FAC,查看一下这个地址的数据。可以看出下面的数据是壳的导入表,并不是程序自身的。

下面我们查找程序自己的导入表,很简单,搜索Kernel32.dll这个字符串,会找到几个,搜索字符串后面是乱码的跳过,最后我们定位的信息如下:

这是程序自己的导入表,但有些数据被覆盖了,所以程序运行会出错。

从这个地址向前查找会找到完整导入表信息,这里的界限很明显,非全零的那一行就是导入信息的开始,地址是:56000,但数据已经乱了,我们需要的就是重建这部分结构。

如何重建,这里我们要先介绍一下导入表的结构:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD   Characteristics;// 0 for terminating null import descriptor

DWORD   OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

} DUMMYUNIONNAME;

DWORD   TimeDateStamp;// 0 if not bound,

// -1 if bound, and real date\time stamp

//  in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)

// O.W. date/time stamp of DLL bound to (Old BIND)

DWORD   ForwarderChain; // -1 if no forwarders

DWORD   Name;

DWORD   FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)

} IMAGE_IMPORT_DESCRIPTOR;

typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

上面结构中的的FirstThunk指向下一个结构:

typedef struct _IMAGE_THUNK_DATA32 {

union {

DWORD ForwarderString;// PBYTE 

DWORD Function; // PDWORD

DWORD Ordinal;

DWORD AddressOfData;// PIMAGE_IMPORT_BY_NAME

} u1;

} IMAGE_THUNK_DATA32;

typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

上面结构中的数据指向了我们刚才查找是内容,那里的内容结构如下:

typedef struct _IMAGE_IMPORT_BY_NAME {  

WORD    Hint; 

CHAR   Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

我们现在要修复的就是前两个结构的数据,第三组结构的数据是正常的,没有被打乱,我们就是根据第三组结构的数据来还原前两组结构,这个修复是完美的修复,修复后倒入表信息和加壳之前的完全一样。

下面我们就先来看一组数据:

其中地址A中的数据指向DLL的字符串,地址B中的数据指向函数名的地址,地址C中的数据指向函数名,可以看到地址A和B中的数据都已经乱了,指向不正确。

地址C中的数据已经被函数的实际地址替代,C中的地址是程序加载的时候由系统填写的,但现在因为有外壳加密,所以这部分数据是由壳程序来填写的。

我们只要恢复A、B、C中的数据即可恢复导入表的信息。第一个红框中的数据除了A和B其他都可以用00来填充。

知道了原理手工修复就简单了,但是这个工作量有些大,因此就写了一个程序恢复这些信息。

代码如下:

procedure IAT;

var

Txt:File of Char;

Buf:Array of Char;

Len:integer;

PDosHead:PImageDosHeader;

PNtHead:PImageNtHeaders;

PSectionHead:PImageSectionHeader;

PSec:PImageSectionHeader;

ls:String;

i,j,n:integer;

p:PChar;

ImpAddr,ExpAddr,vAddr,pAddr:Cardinal;

pImp:PIMAGE_IMPORT_DESCRIPTOR;

pImpname:PIMAGE_IMPORT_BY_NAME;

pExp:PImageExportDirectory;

pNameAddr:Cardinal;

pd:PDWORD;

begin

Memo1.SetFocus;

FileMode:=fmOpenRead;

AssignFile(Txt,Edit1.Text);

Reset(Txt);

Len:=Filesize(Txt);

Setlength(Buf,Len);

BlockRead(Txt,Buf[0],Len);

CloseFile(Txt);

n:=Integer(@buf[0]);

pImp:=@Buf[$56000];

p:=@Buf[$56724];

pImp.Name:=integer(p)-n;

pd:=@Buf[$56118];

pImp.FirstThunk:=$56118;

pImp.OriginalFirstThunk:=0;

pImp.TimeDateStamp:=0;

pImp.ForwarderChain:=0;

While True do

begin

Inc(p,Length(p));

for i:=1 to 10 do

begin

Inc(p);

if Pos('.dll',p)<>0 then

begin

ShowMessage('--'+p);

pd^:=0;

inc(pd);

Inc(pImp);

pImp.Name:=integer(p)-n;

pImp.FirstThunk:=integer(pd)-n;

pImp.OriginalFirstThunk:=0;

pImp.TimeDateStamp:=0;

pImp.ForwarderChain:=0;

Inc(p,Length(p));

Continue;

//Exit;

end;

if p[0]<>#0 then

begin

//ShowMessage(#9+p);

break;

end;

end;

if i=11 then

break;

//dec(p,2);

pd^:=(integer(p)-n-2);

Inc(pd);

ShowMessage('  '+IntToHex(pImp.FirstThunk,8)+' '+p);

end;

AssignFile(Txt,'1.exe');

Rewrite(Txt);

BlockWrite(Txt,Buf[0],Len);

CloseFile(Txt);

end;

我是用PAS写的代码,大家也可以用其他语言来写,修改完IAT表以后,还有最后一步,就是要把导入表的地址改为:56000,以前的导入表地址是:067FAC,查找二进制的字符串:AC 7F 06改为新地址即可。

附件中是这次演示的文件,大家可以尝试手工修复一下。

(点击文末“原文链接”即可获取附件~)

- End -

原文作者:lrtlrt

原文链接:https://bbs.pediy.com/thread-248111.htm

转载请注明转自看雪学院

更多阅读:

1、[原创] CVE-2014-0322 IE与Flash结合利用 绕过ASLR+DEP

2、[原创]2018bctf three

3、[原创]一种通用DLL劫持技术研究

4、[原创]我的二进制漏洞挖掘方法思路(不仅是fuzz)希望有缘人指点

上一篇下一篇

猜你喜欢

热点阅读