美文网首页
纯手工完美恢复IAT

纯手工完美恢复IAT

作者: 看雪学院 | 来源:发表于2018-12-04 18:03 被阅读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)希望有缘人指点

    相关文章

      网友评论

          本文标题:纯手工完美恢复IAT

          本文链接:https://www.haomeiwen.com/subject/eojvcqtx.html