DLL注入_修改导入表(1)

PE文件分析
(一) 替换DLL函数

通过修改PE文件导入导出表来更改函数。单纯分析PE文件是一件比较无聊的事,通过修改可以更加灵活的利用和理解PE文件。这就像生活一样,总要有一些特别的事,才能更加有灵感。学习本身就是生活的一部分,如何让程序更加灵活,是一个比较值得去实践的事,可以更加接近计算机的一些思想。

(二) 环境

VC++ 6.0

为了减少复杂度,用VC++ 6.0编译sum.cpp sub.cpp生成sum.dll sub.dll,用dll_main.cpp静态链接sum.dll,要把dll_main程序中的sum.dll的函数换成sub.dll的函数,改成两数想减,代码如下:
程序: https://github.com/sv4us/binary/blob/master/dll分析.zip

sub.cpp

extern "C" _declspec(dllexport) int sub(int a,int b);
int sub(int a,int b)
{
	return a-b;
}

sum.cpp

extern "C" _declspec(dllexport) int sum(int a,int b);
int sum(int a,int b)
{
	return a+b;
}

主函数dll_main.cpp

#include
#include

using namespace std;
extern "C" _declspec(dllimport) int sum(int a,int b);

int main(int argc,char *argv[])
{
	int a;
	a = sum(8,10);
	cout << "a=" <<a <<endl;
	system("pause");
	return 0;
}
(三) 分析PE文件

环境

010 Editor , CFF Explorer,PE结构图和说明文档

010 Editor打开dll_main.exe
这边只关心IMAGE_NT_HEADERS,010 Ediotr的插件看出(直接搜索PE字符串也可以看出,实际上是(IMAGE_DOS_HEADER->AddressOfNewExeHeader)处的内容,括号为取出地址上的内容,DOS头的最后一个成员),由于IMAGERVA均指的是PE加载到内存时的用语,文件中用RAW,但PE结构一样,以下的IMAGERVA均指文件未加载到内存时的情况。

  • NT头的起始地址 : E8h ,内容如下
 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

000000E0  00 00 00 00 00 00 00 00 50 45 00 00 4C 01 03 00  ........PE..L.

函数与导入导出表有关,查看EXE的数据目录结构 IMAGE_DATA_DIRECTORY距IMAGE_NT_HEADERS偏移量+78H处,

  • IMAGE_DATA_DIRECTORY地址 = E8H + 78H = 160H ,内容如下
 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00000160  00 00 00 00 00 00 00 00 68 53 01 00 3C 00 00 00  ........hS.<...

NtHeader下面是(Signature, IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER32), FileHeader距NtHeader偏移量+4H处, OptionalHeader距NtHeader偏移量+18H

IMAGE_DATA_DIRECTORYOptionalHeader的最后一个字段(IMAGE_OPTIONAL_HEADER32->IMAGE_DATA_DIRECTORY),距NtHeader偏移量+78H处,是一个结构数组(16个)。

typedef struct IMAGE_DATA_DIRECTORY{
    DWORD VirtualAddress ;//数据起始地址RVA
    DWORD Size ;//数据块长度 
}

IMAGE_DATA_DIRECTORY结构数组第一项 :IMAGE_DATA_DIRECTORY_Export输出表,上面EXE没有输出表,为8Byte的0x00
IMAGE_DATA_DIRECTORY结构数组第二项 :IMAGE_DATA_DIRECTORY_Import输入表,RVA = 0X0001 5368H Size = 0x0000 003CH

DWORD,双字4字节32位(与机器字长不同,x86_64机器字长64位,64/8 = 8字节,1字8字节)

  • IMAGE_DATA_DIRECTORY_Import地址 = (NtHeader + 80H) VA = 0X0001 5368H ,查看此处地址,15368H ~ 153A3H
 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00015360  FF FF FF FF F8 20 41 00 A4 54 01 00 00 00 00 00  ?A......
00015370  00 00 00 00 B2 54 01 00 00 31 01 00 A4 53 01 00  ....睺..1..
00015380  00 00 00 00 00 00 00 00 24 59 01 00 00 30 01 00  ........$Y..0.
00015390  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000153A0  00 00 00 00 0E 57 01 00 12 59 01 00 BA 54 01 00  ....W.Y.篢.
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD Characteristics;
        DWORD OriginalFirstThunk; //指向INT(输入名称表),RVA
    };
    DWORD TimeDateStamp;
    DWORD ForwarderChain;
    DWORD Name;  //指向DLL名称,RVA
    DWORD FirstThunk; //指向IAT(输入地址表),RVA
},
//IMAGE_IMPORT_DESCRIPTOR为一个数组,没有字段指出项数,但最后一个单元为NULL,IID结构长度5个字,20Byte,14H。15390H ~ 153A3H 20个0,共2项输入,sum.dll,KERNEL32.dll
Name的RVA = 0x0001 54B2H 距IMAGE_IMPORT_DESCRIPTO偏移3个字(union算1字),+CH
FirstThunk的RVA = 0x0001 3100H

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

000154B0  6D 00 73 75 6D 2E 64 6C 6C 00 E4 01 4D 75 6C 74  m.sum.dll.?Mult


Name的RVA = 0x0001 5924H 距IMAGE_IMPORT_DESCRIPTO偏移3个字(union算1字),+CH
FirstThunk的RVA = 0x0001 3000H

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00015920  6F 57 00 00 4B 45 52 4E 45 4C 33 32 2E 64 6C 6C  oW..KERNEL32.dll
  • OriginalFirstThunk和FirstThunk都指向IMAGE_THUNK_DATA结构
typedef struct IMAGE_THUNK_DATA { //指针大小联合,长度一个DWORD
    union u1 {
        ForwarderString dd;  //指向一个转向字符的RVA
        Function dd;         //被输入函数的内存地址
        Oridinal dd;         //被输入函数API序列值
        AddressOfData dd;    //指向IMAGE_IMPORT_BY_NAME
    }
}

//IMAGE_THUNK_DATA最高位为1时,函数以序号导出,Oridinal低31位为输出函数在其DLL的导出序号,      最高位为0时,函数以字符串导出, AddressOfData指向用来导入函数名称的IMAGE_IMPORT_BY_NAME的数据结构RVA

typedef struct IMAGE_IMPORT_BY_NAME {
    HINT WORD ;    //函数序号,非必须
    NAME BYTE;    //导入函数的名称,大小可变,以0结尾的ascii字符串
}
  • sum.dll的IMAGE_THUNK_DATA结构

FirstThunk的RVA = 0x0001 3100H,查看13100处的内容

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00013100  AC 54 01 00 00 00 00 00 00 00 00 00 FF FF FF FF  琓.........

IAT(sum.dll,RVA) = 0x0001 54ACH

0x0001 54ACH处的内容

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

000154A0  00 00 00 00 AC 54 01 00 00 00 00 00 00 00 73 75  ....琓.......su
000154B0  6D 00 73 75 6D 2E 64 6C 6C 00 E4 01 4D 75 6C 74  m.sum.dll.?Mult

HINT = 0X0000      NAME = sum
  • KERNEL32.dll的IMAGE_THUNK_DATA结构

FirstThunk的RVA = 0x0001 3000H, sum.dll RVA = 0x00013100H查看13000~131F0处的内容

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00013000  0E 57 01 00 12 59 01 00 BA 54 01 00 D0 54 01 00  W.Y.篢.蠺.

000130F0  DC 58 01 00 E8 58 01 00 F8 58 01 00 00 00 00 00  躕.鑈.鳻.....

可以看出IMAGE_THUNK_DATA以0x00000000结束

第一个 IAT(KERNEL32.dll,RVA) = 0x0001 570EH
0x0001 570EH处的内容

 Offset    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   Ascii

00015700  15 01 47 65 74 46 69 6C 65 54 79 70 65 00 50 01  GetFileType.P
00015710  47 65 74 53 74 61 72 74 75 70 49 6E 66 6F 41 00  GetStartupInfoA.

HINT = 0X0150      NAME = GetStartupInfoA

通过CFF EXPLORER import directory -> 查看输入表 ,hex editor -> 查看十六进制

(四) 过程整理

NtHeader(‘PE00’) ->

IMAGE_DATA_DIRECTORY(NT+78H,第二项)) ->

DATA_DIRECTORY_Import(NT+80H,DDI->VirtualAddress,rva) ->

IMAGE_IMPORT_DESCRIPTOR(IID -> FirstThunk(IID+4字),rva) ->

IMAGE_THUNK_DATA(IAT,ITD -> AddressOfData) ->

IMAGE_IMPORT_BY_NAME(IIBN -> NAME)

DLL名称 = IMAGE_IMPORT_DESCRIPTOR -> NAME

函数名 = IMAGE_IMPORT_BY_NAME -> NAME

(五) 修改EXE中dll名和函数名

NtHeader = E8h

Import = NtHeader + 80H = 168H = (0001 5368 0000 003c)

IMAGE_IMPORT_DESCRIPTOR->name = Import(15368H) + 3字 = 15374H = (0001 54b2)

DLL名称: 154b2H = (73 73 6d 2e 64 6c 6c sum.dll) 改为 (73 73 62 2e 64 6c 6c sub.dll)

IMAGE_IMPORT_DESCRIPTOR->FirstThunk = Import(15368H) + 4字 = 15378H = (0001 3100)

IMAGE_THUNK_DATA->AddressOfData = 13100H = (0001 54ac)

函数名: 154acH = (00 00 73 75 6d) 改为 (00 00 73 75 62)

原来 8+10 = 18 改完后 8-10= -2
(可以发现函数名和DLL名在154b0h这一行,用CFF Explorer查看输出表: sub.dll和sum.dll的Oridinal Function RVA Name RVA都一样)

程序: https://github.com/sv4us/binary/blob/master/dll分析.zip

你可能感兴趣的:(Binary)