内容纲要

学习方法基本来源于《加密与解密》第十二章

需要:PE文件知识

DLL注入

一些要点

导入表的定义:

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;               // 指向IAT(引入函数真实地址单元处的RVA)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;COPY

每个导入的DLL都会成为该数组中的一项,也就是说,一个这样的结构对应一个导入的DLL。

书上例题

干预输入表

静态修改PE输入表

vs2019创建dll的工程

初始状态如下:

image-20210620214142000

image-20210620220911834

书上的代码会报错,这里把char改为了TCHAR则通过了

关于TCHAR

并且sprintf和printf需包含头文件(需要关闭安全检查)

dll注入之合适区段检测

image-20210526135424153

导入表的RVA是0x7604,大小为0xc8

我们需要导入一个自己写的dll,而一个IID结构内有5个双字,一共0x14个字节,所以我们需要一个大小为0xc8+0x14=0xdc的空间来存放我们新的IID数组。

看一下各段

image-20210620235232234

关于VirtualSize和SizeOfRawData大小的问题:https://www.zhihu.com/question/64435651

取virtualsize和rawsize最小值看,而各段rawsize按filealignment对齐,所以要找virtualsize小于rawsize的区段,因此只有.text段

空隙大小:0x7800-0x7748=0xB8小于0xDC

此时有两种方法:①扩展最后一个区段 ②加一个区段

(选择扩展最后一个区段)

①得到输入表RawOffset

输入表的RVA为0x7604,位于.text段,.text段的VirtualOffset为0x1000,RawOffset为0x400

输入表的RawOffset=输入表的RVA-(段VirtualOffset-段RawOffset)=0x7604-(0x1000-0x400)=0x6A04

输入表大小为0xc8

image-20210621004329884

②复制这些DII到新扩展的区段

扩展RAW:0x8400+0x8000=0x10400

在0x10400处粘贴IID数据

image-20210621005939353

一个IID为0x14字节

同时最后0x14个全0标志IID的结束,所以要在0x104c8后再写入0x14个0作为结束标志,而0x104b4之后的0x14个字节为要添加的IID区域

③在原IID区域构造新IID的OriginalFirstThunk、Name、FirstThunk结构

将原有IID区域(0x6a04~0x6acb)全写0

一个IID结构的格式如下:

image-20210621010951824

在原IID区域(现为空白区)构造一个IID,如上一定要有OriginalFirstThunk,Name,FirstThunk

开始在此区域构造,

第一个写OriginalFirstThunk,指向IMPORT_BY_NAME的RVA

第二个写FirstThunk,也指向IMPORT_BY_NAME的RVA

第三个写DLL文件名

第四个写name(因为dll的name长度不固定,所以最后写)

从0x6a04开始写8字节,0x6a0c写8字节,然后从0x6a14开始是dll文件名,最后是name

先写文件名

image-20210621031938484

得到name的RAW为0x6a20,RVA为0x7620

开始填写OFT和FT(指向为RVA)

image-20210621032404607

最后写name

image-20210621032809663

④填充新输入表项的IID结构

image-20210621010951824

按顺序填写

第一个双字填写OriginalFirstThunk的RVA,就是上个步骤创造的0x6a04,RVA则为0x7604

后面的8个字节,为0(TimeDateStamp为-1表示预先绑定,此时我们未绑定,所以为0)

然后写name的RVA,指向前面创建的dll文件名的RVA,0x7614

FirstThunk同上,指向0x760c

⑤修正PE文件头的信息

image-20210621033529932

原始文件:

image-20210621033855394

要将0x7604改为0x10400(IID的RVA),把0xc8改为0xdc(Size)

image-20210621034104928

RVA=dll的VOffset+(段VOffset-段ROffset)=0x10400+(0xb000-0x8400)=0x10400+0x2c00=0x13000

修改后:image-20210621034358687

修改节区属性

image-20210621034443767

修改前:

image-20210621034509525

修改后:

image-20210621034532154

这里书上没写,但是.rsrc区段的size还是要改一下的,0x8000改为0x8200

image-20210621035609041

⑥修改绑定表

image-20210621034833846

修改前:

image-20210621034920183

修改后:

image-20210621034933374

成果:

没在路径中放dll

image-20210621040105011

放了之后

image-20210621035904639

image-20210621035913208

成功~

总结

步骤还是有点多的,包括一些计算RAWRVA转换,还有一些大小和位置的注意,加上书上其实一些地方都没有讲为什么这么做,还漏了一点修改……

静态修改输入表通过添加IID数组,由于包含IID数组的区段剩余空间不足添加一个IID(0x14字节大小),所以扩展最后一个区段,存放所有IID数组(修改的区段记得文件头改大小),此时原IID数组位置已无用,所以将其改写为新添加IID的相关数据的RVA,在新IID区域添加一个IID指向其数据(记得修改IID数组的大小和位置),最后修改绑定表为未绑定进行重新绑定。

进程创建期修改PE输入表

修改映射后的PE内存。

待更

逆向

dll内存注入

exe拖入ida

image-20210704172242975

在关键函数里,有一个7ff62c函数

image-20210704172318398

image-20210704172330134

很像dll内存注入,动调查看word和dword两个变量,

image-20210704172624864

image-20210704172642293

确定是dll

根据反编译,小于0x1e400

写dump的idc脚本

image-20210704172139192

static main(void){
    auto fp, start, size,i;
    start = 0x00007FF62C975A00;
    fp = fopen("D:\\r2.dll", "wb");
    for (i=0; i <0x1e400; i++ )
        fputc(Byte(start+i), fp);
}

总结

可以看到一些dll内存注入的函数调用,并且dll的二进制数据存放在变量里,需要dump下来