PE文件结构学习-2

Rva转Foa的过程

1.判断Rva减去imagebase对比sizeofheader的大小,如果比这个小,那说明这个Rva减去imagebase对比sizeofheader就是foa

2.如果上述不成立,则遍历节表的virtualaddress,看位于哪2个节表之间,判断成功后用节表的pointtoraw+rva-virtualaddress,就是得到的foa

foa转rva

反过来就行,先看这个foa在那个节,计算与这个节pointtoraw地址的偏移,然后加上这个节的virtualaddress

代码待补充

动态链接库测试

隐式链接

vs创建dll项目,pch.cpp和pch.h如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// pch.cpp: 与预编译标头对应的源文件

#include "pch.h"

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
int __stdcall Plus(int x, int y)
{
return x + y;
}
int __stdcall Sub(int x, int y)
{
return x - y;
}
int __stdcall Mul(int x, int y)
{
return x * y;
}
int __stdcall Div(int x, int y)
{
return x / y;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。

extern "C" int _declspec(dllexport) __stdcall Plus(int x, int y);
extern "C" int _declspec(dllexport) __stdcall Sub(int x, int y);
extern "C" int _declspec(dllexport) __stdcall Mul(int x, int y);
extern "C" int _declspec(dllexport) __stdcall Div(int x, int y);


#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#include "framework.h"

#endif //PCH_H

把生成的dll,pch.h,framkwork.h,lib拷贝到测试项目里

image-20220310194805992

测试项目加入

1
2
3
4
5
6
7
#pragma comment(lib, "Dll1.lib")


extern "C" __declspec(dllimport) int __stdcall Plus(int x, int y);
extern "C" __declspec(dllimport) int __stdcall Sub(int x, int y);
extern "C" __declspec(dllimport) int __stdcall Mul(int x, int y);
extern "C" __declspec(dllimport) int __stdcall Div(int x, int y);

编译即可

函数无名字导出

新建立一个模块

image-20220316102255477

输入如下

1
2
3
4
5
6
7
8
LIBRARY
EXPORTS

Plus @12
Sub @15 NONAME //要不使用名字导出的函数
Mul @13
Div @16

导出表

image-20220308231541901
1
2
3
4
5
6
7
   DWORD   NumberOfFunctions; 
根据导出函数的序号最大的减最小+1的得出,所以不一定是真实的个数
DWORD AddressOfFunctions; // 导出函数地址表RVA
有可能是空的->指这个地方无对应序号的函数
DWORD Base;
此数据加上(DWORD AddressOfNameOrdinals; // 导出函数序号表RVA)里的序号 才是导出时函数的序号

按名字找函数

DWORD AddressOfNames; ->找到名字,然后根据找到的函数对应的下标->DWORD AddressOfNameOrdinals;对应的下标找到序号->到DWORD AddressOfFunctions;表里找到对应此下标的函数

序号查找

给出的序号减去DWORD Base;,根据下标到AddressOfNames;寻找地址即可

给的图例:

image-20220308234327903 image-20220308234515094

名字表的索引是用来定位序号表的序号:名字表[2]->序号表[2]->取出序号

!!序号对应地址表的索引,如果是无名的函数,序号应该是地址表的下标加base

写的程序

我觉得我写的有点乱,看网上是最好根据函数地址表来查另外两个表然后定位和输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void showEXPORT_DIRECTORY(LPVOID pFileBuffer) {

PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_EXPORT_DIRECTORY exportDIRECTORY = NULL;

pNtHeaders = FileToNtHeader(pFileBuffer);
exportDIRECTORY = (PIMAGE_EXPORT_DIRECTORY) pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress;
exportDIRECTORY = (PIMAGE_EXPORT_DIRECTORY) RVAtoFOA((DWORD)exportDIRECTORY, pFileBuffer);
printf("******************************\n");
printf("导出表文件地址:%x\n", exportDIRECTORY);

PIMAGE_EXPORT_DIRECTORY exportDIRECTORYfilebuffer = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)exportDIRECTORY + (DWORD)pFileBuffer);

printf("导出表函数个数:%x\n", exportDIRECTORYfilebuffer->NumberOfFunctions);
printf("导出表有名字的函数个数:%x\n", exportDIRECTORYfilebuffer->NumberOfNames);
printf("******************************\n");
printf("导出表函数地址表文件地址:%x\n", RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfFunctions,pFileBuffer));

for (int i = 0; i < exportDIRECTORYfilebuffer->NumberOfFunctions; i++) {

DWORD* tureAddressOfFunctions = (DWORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfFunctions, pFileBuffer)) + i;
printf("导出表函数地址表文件地址%x:%x\n", i,(DWORD)RVAtoFOA((DWORD)tureAddressOfFunctions- (DWORD)pFileBuffer,pFileBuffer));
}
printf("******************************\n");
printf("导出表函数名字表文件地址:%x\n", RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNames, pFileBuffer));

for (int i = 0; i < exportDIRECTORYfilebuffer->NumberOfNames; i++) {
DWORD* tureAddressOfNames = (DWORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNames, pFileBuffer))+i;
printf("导出表函数名字表文件中地址的值:%x\n", *tureAddressOfNames);

printf("导出表函数名字表文件地址:%x\n", RVAtoFOA((DWORD)*tureAddressOfNames,pFileBuffer));

PCHAR Nameaddr = (PCHAR)((BYTE*)RVAtoFOA((DWORD)*tureAddressOfNames, pFileBuffer) + (DWORD)pFileBuffer);
printf("导出表函数%x名字:%s\n",i,Nameaddr );
}
printf("******************************\n");
printf("导出表函数序号表base:%x\n", exportDIRECTORYfilebuffer->Base);
printf("导出表函数序号表文件地址:%x\n", RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNameOrdinals, pFileBuffer));

for (int i = 0; i < exportDIRECTORYfilebuffer->NumberOfNames; i++) {
WORD* tureAddressOfxvhao = (WORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNameOrdinals, pFileBuffer)) + i;
printf("导出表函数序号表序号[%x]的值:%x\n", i,*tureAddressOfxvhao);

///printf("导出表函数名字表文件地址:%x\n", RVAtoFOA((DWORD)*tureAddressOfxvhao, pFileBuffer));

//PWORD idaddr = (PWORD)((BYTE*)RVAtoFOA((DWORD)*tureAddressOfxvhao, pFileBuffer) + (DWORD)pFileBuffer);
//printf("导出表函数名字表文件地址:%d\n", idaddr);
}
}

image-20220312164228866

重定位表

数据目录的第六个结构,用来定位需要修改的地址位置(程序加载dll的时候,如果dll加载的基址不是imagebase,则根据此表偏移依次修改此表中指向的地址)

image-20220312000406461

内容说明

image-20220312164009483

自己写的程序解析

写了之后,感觉清楚挺多了,就是还不知道这个表修改的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
展示重定位表块的内容
*/
void showRELOCATIONsetion(DWORD numofsetion,WORD* start,DWORD addrbase, LPVOID pFileBuffer)
{
for (DWORD i = 0;i < numofsetion;i++)
{
WORD time = *start;
DWORD addradd = (time & 0xFFF)+addrbase;

if (((time & 0xF000) >> 12) == 3)
{
printf("块需要修改,块高4位%x,后12位%x,RVA的值%x,,FOA值%x\n", ((time & 0xF000) >> 12),(time & 0xFFF),addradd,RVAtoFOA(addradd,pFileBuffer));
}
else {
printf("块不需要修改,块高4位%x,后12位%x,RVA的值%x,FOA值%x\n", ((time & 0xF000) >> 12), (time & 0xFFF),addradd, RVAtoFOA(addradd, pFileBuffer));
}


start = start + 1;
}
}/*
展示重定位表,需要文件指针
*/

void showRELOCATION(LPVOID pFileBuffer) {
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_BASE_RELOCATION pRELOCATIONDIRECTORY = NULL;
PIMAGE_BASE_RELOCATION pnextRELOCATIONDIRECTORY = NULL;
DWORD lastRELOCATIONDIRECTORYaddr = 1;
DWORD lastvirtualaddr = 1;
int kuai = 0;
pNtHeaders = FileToNtHeader(pFileBuffer);

pRELOCATIONDIRECTORY = PIMAGE_BASE_RELOCATION(RVAtoFOA(pNtHeaders->OptionalHeader.DataDirectory[5].VirtualAddress, pFileBuffer)+ (DWORD)pFileBuffer);
pnextRELOCATIONDIRECTORY = pRELOCATIONDIRECTORY;
printf("******************************\n");
printf("重定位表内容\n");
//lastvirtualaddr = pRELOCATIONDIRECTORY->VirtualAddress;
while (lastvirtualaddr > 0)
{
kuai++;
pRELOCATIONDIRECTORY = pnextRELOCATIONDIRECTORY;
DWORD numofsetion = (pRELOCATIONDIRECTORY->SizeOfBlock - 8) / 2;
printf("块%x的地址是%x,大小是%x,块的数量是%x\n", kuai,pRELOCATIONDIRECTORY->VirtualAddress,pRELOCATIONDIRECTORY->SizeOfBlock,numofsetion);
showRELOCATIONsetion(numofsetion, (WORD*)((DWORD)pRELOCATIONDIRECTORY + 8), (DWORD)pRELOCATIONDIRECTORY->VirtualAddress,pFileBuffer);



lastRELOCATIONDIRECTORYaddr = (DWORD)pRELOCATIONDIRECTORY+ pRELOCATIONDIRECTORY->SizeOfBlock;//下一个块的地址是文件中的地址加块大小
pnextRELOCATIONDIRECTORY = (PIMAGE_BASE_RELOCATION)lastRELOCATIONDIRECTORYaddr;
lastvirtualaddr = pnextRELOCATIONDIRECTORY->VirtualAddress;


}
//pRELOCATIONDIRECTORY->VirtualAddress;

}

image-20220312185341428

完全一致,说明应该没问题

image-20220312190100318

重定位表和导出表的迁移

步骤其实就是这样

image-20220313203239204

不过程序写的时候比较蛋疼,调了好几次都没成功:

1.注意rva的转换,主要是注意函数使用的是内存空间,要计算foa就得减去指针开始的地方。

2.注意rva的计算,因为表里存放的都是rva,反正是在最后一个节之后添加的的节,可以用最后一个节的VirtualAddress 加上文件地址和最后一个节文件地址PointerToRawData的相对偏移。

这个和肝了我好久

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
导出表和重定位表迁移测试
*/
LPVOID removeDIRECTORY(LPVOID pFileBuffer) {
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_SECTION_HEADER pSectionHeaderBase = NULL;
LPVOID start = NULL;
PIMAGE_EXPORT_DIRECTORY exportDIRECTORY = NULL;
pNtHeaders = FileToNtHeader(pFileBuffer);
pSectionHeaderBase = LocateSectionBase(pFileBuffer);
DWORD addrofnamervafirst = 0;

exportDIRECTORY = (PIMAGE_EXPORT_DIRECTORY)pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress;
exportDIRECTORY = (PIMAGE_EXPORT_DIRECTORY)RVAtoFOA((DWORD)exportDIRECTORY, pFileBuffer);
//导出表现在的文件地址+pfilebuffer
PIMAGE_EXPORT_DIRECTORY exportDIRECTORYfilebuffer = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)exportDIRECTORY + (DWORD)pFileBuffer);


//DWORD SizeOfHeadersmem = pSectionHeaderBase->VirtualAddress;
//找最后一个节
PIMAGE_SECTION_HEADER pLaswSectionHeaderBase = PIMAGE_SECTION_HEADER((BYTE*)pSectionHeaderBase + (DWORD)(0x28 * (pNtHeaders->FileHeader.NumberOfSections - 1)));
//找最后一个节的开始地址
start = (LPVOID)(pLaswSectionHeaderBase->PointerToRawData + (DWORD)pFileBuffer);
LPVOID starttest = (LPVOID)(pLaswSectionHeaderBase->PointerToRawData + (DWORD)pFileBuffer);//记录内存空间中复制开始的地址

//复制AddressOfFunctions
DWORD AddressOfFunctionssize = exportDIRECTORYfilebuffer->NumberOfFunctions * 4;//大小
//计算rva
DWORD AddressOfFunctionsrva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
//AddressOfFunctions 起始文件地址
DWORD* tureAddressOfFunctions = (DWORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfFunctions, pFileBuffer));
memcpy(start, tureAddressOfFunctions, AddressOfFunctionssize);

//三步:复制AddressOfNameOrdinals
start = (LPVOID)((DWORD)start + AddressOfFunctionssize);
DWORD AddressOfNameOrdinalsrva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
DWORD AddressOfNameOrdinalssize = exportDIRECTORYfilebuffer->NumberOfNames * 2;
//AddressOfNameOrdinals的起始文件地址
WORD* tureAddressOfNameOrdinals = (WORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNameOrdinals, pFileBuffer));
memcpy(start, tureAddressOfNameOrdinals, AddressOfNameOrdinalssize);

//四步:复制AddressOfNames
start = (LPVOID)((DWORD)start + AddressOfNameOrdinalssize);
DWORD AddressOfNamesrva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
LPVOID namneaddrstart = start;//记录一下拷贝的名字表的起始地址
DWORD AddressOfNamessize = exportDIRECTORYfilebuffer->NumberOfNames * 4;
//AddressOfNames的起始文件地址
DWORD* tureAddressOfNames = (DWORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNames, pFileBuffer));
memcpy(start, tureAddressOfNames, AddressOfNamessize);


//复制所有的函数名字
start = (LPVOID)((DWORD)start + AddressOfNamessize);
printf("******************************拷贝名字表的名字\n");
for (int i = 0; i < exportDIRECTORYfilebuffer->NumberOfNames; i++) {
DWORD* tureAddressOfNames = (DWORD*)((DWORD)pFileBuffer + RVAtoFOA(exportDIRECTORYfilebuffer->AddressOfNames, pFileBuffer)) + i;
DWORD* tureAddressOfNames2 = (DWORD*)((DWORD)namneaddrstart) + i;
printf("导出表函数名字表文件中地址的值:%x,拷贝后的表里地址的值%x\n", *tureAddressOfNames, *tureAddressOfNames2);

printf("导出表函数名字表文件地址:%x\n", RVAtoFOA((DWORD)*tureAddressOfNames, pFileBuffer));

PCHAR Nameaddr = (PCHAR)((BYTE*)RVAtoFOA((DWORD)*tureAddressOfNames, pFileBuffer) + (DWORD)pFileBuffer);
//CHAR Name[] = { *Nameaddr };
memcpy(start, Nameaddr, strlen(Nameaddr));


//修复AddressOfNames
DWORD namestartyz = (DWORD)start - (DWORD)pFileBuffer;
//计算nameaddr的rva
DWORD addrofnamerva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
*tureAddressOfNames2 = addrofnamerva;
start = (LPVOID)((DWORD)start + strlen(Nameaddr));


if (i == 0)
{
addrofnamervafirst = addrofnamerva;
}
memset(start, 0x00, 1);//字符串结尾加\00
start = (LPVOID)((DWORD)start + 1);
printf("导出表函数大小%x的%x名字:%s,验证修改后的foa正确性%x,对比现在的foa:%x\n", strlen(Nameaddr),i, Nameaddr,RVAtoFOA(*tureAddressOfNames2,pFileBuffer), namestartyz);
}
printf("******************************\n");

//复制IMAGE_EXPORT_DIRECTORY结构
PIMAGE_EXPORT_DIRECTORY exportDIRECTORYfilebuffercopy = (PIMAGE_EXPORT_DIRECTORY) start;
memcpy(start, (LPVOID)exportDIRECTORYfilebuffer, sizeof(IMAGE_EXPORT_DIRECTORY));
start = (LPVOID)((DWORD)start + sizeof(IMAGE_EXPORT_DIRECTORY));


//
/*
第七步:修复IMAGE_EXPORT_DIRECTORY结构中的

AddressOfFunctions

AddressOfNameOrdinals

AddressOfNames
*/
exportDIRECTORYfilebuffercopy->AddressOfFunctions = AddressOfFunctionsrva;
exportDIRECTORYfilebuffercopy->AddressOfNameOrdinals = AddressOfNameOrdinalsrva;

exportDIRECTORYfilebuffercopy->AddressOfNames = AddressOfNamesrva;
//exportDIRECTORYfilebuffercopy->Name = addrofnamervafirst;


//改数据目录的表指向
pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress = (DWORD)pLaswSectionHeaderBase->VirtualAddress+(DWORD)exportDIRECTORYfilebuffercopy -(DWORD)starttest;

return pFileBuffer;
}

效果

image-20220313203816403

这个移动后是可以正常调用的

导入表和IAT表

导入表结构

1
2
3
4
5
6
7
8
9
10
11
12
	typedef struct _IMAGE_IMPORT_DESCRIPTOR {							
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //指向IMAGE_THUNK_DATA32结构的起始位置 INT表
};
DWORD TimeDateStamp; //这个值为0说明可执行文件不与输入的dll绑定(绑定后IAT表的值是函数地址)
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk; //指向IMAGE_THUNK_DATA32结构 IAT表
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
`

OriginalFirstThunk,FirstThunk是指向第一个IMAGE_THUNK_DATA32的rva,遍历到0算此dll结束

MAGE_THUNK_DATA32结构,4字节,看成一个dword就行

1
2
3
4
5
6
7
8
9
10
typedef struct _IMAGE_THUNK_DATA32 {									
union { //联合类型,高位是1则说明去除高位后记录的是函数序号,不是则是指向IMAGE_IMPORT_BY_NAME的rva
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal; //序号
PIMAGE_IMPORT_BY_NAME AddressOfData; //指向IMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

IMAGE_IMPORT_BY_NAME结构

1
2
3
4
5
6
typedef struct _IMAGE_IMPORT_BY_NAME {												
WORD Hint; //可能为空,编译器决定 如果不为空 是函数在导出表中的索引
BYTE Name[1]; //函数名称的首位,以\0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;


程序解析

流程-通过可选头的数据目录的第二个[1]找到其数据目录数据->找到Virtualaddressrva(转换foa)->找到导入表起始地址->遍历导入表->主要遍历导入表的Name,OriginalFirstThunk,FirstThunk,遍历OriginalFirstThunk,FirstThunk时,先转foa,这个foa指向的位置是_IMAGE_THUNK_DATA32结构,看成DWORD即可->判断高位,为0则是指向IMAGE_IMPORT_BY_NAME结构的rva,需要转foa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
void showDESCRIPTOR(LPVOID pFileBuffer)
{
PIMAGE_IMPORT_DESCRIPTOR pDescriptor = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_IMPORT_BY_NAME pImportbyname = NULL;
PIMAGE_IMPORT_DESCRIPTOR pDescriptorture = NULL;
PIMAGE_IMPORT_BY_NAME namefuction = NULL;




pNtHeaders = FileToNtHeader(pFileBuffer);
//定位导入表
pDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) pNtHeaders->OptionalHeader.DataDirectory[1].VirtualAddress;
//此时在内存中的地址要加pFileBuffer
pDescriptorture = PIMAGE_IMPORT_DESCRIPTOR (RVAtoFOA((DWORD)pDescriptor,pFileBuffer)+(DWORD)pFileBuffer);

//遍历导入表
while (*((DWORD*)pDescriptorture) != 0)
{
printf("******************************\n");
//输出dll名字
printf("dll名字为%s\n", (RVAtoFOA(pDescriptorture->Name, pFileBuffer) + (DWORD)pFileBuffer));

//遍历OriginalFirstThunk
DWORD* startThunk =(DWORD*) (RVAtoFOA((DWORD)pDescriptorture->OriginalFirstThunk, pFileBuffer) + (DWORD)pFileBuffer);
printf("OriginalFirstThunk内容:\n");
while (*startThunk != 0)
{
if ((* startThunk & 0x80000000) == 0)

{
namefuction = (PIMAGE_IMPORT_BY_NAME)(RVAtoFOA(*startThunk, pFileBuffer)+(DWORD)pFileBuffer);
PCHAR Namefu = (PCHAR)namefuction->Name;
printf("IMAGE_IMPORT_BY_NAME地址%x,函数名字:%s\n", *startThunk,Namefu);

}
else {
printf("函数的序号%x\n", *startThunk & 0x7FFFFFFF);
}


//startThunk = 0;
startThunk = startThunk + 1;
}

//遍历FirstThunk

DWORD* startFirstThunk = (DWORD*)(RVAtoFOA((DWORD)pDescriptorture->FirstThunk, pFileBuffer) + (DWORD)pFileBuffer);
printf("FirstThunk内容:\n");
while (*startFirstThunk != 0)
{
if ((*startFirstThunk & 0x80000000) == 0)

{
namefuction = (PIMAGE_IMPORT_BY_NAME)(RVAtoFOA(*startFirstThunk, pFileBuffer) + (DWORD)pFileBuffer);
PCHAR Namefu = (PCHAR)namefuction->Name;
printf("IMAGE_IMPORT_BY_NAME地址%x,函数名字:%s\n", *startFirstThunk, Namefu);

}
else {
printf("函数的序号%x\n", *startFirstThunk & 0x7FFFFFFF);
}


//startThunk = 0;
startFirstThunk = startFirstThunk + 1;
}
pDescriptorture = pDescriptorture + 1;
}



}
image-20220317171257695

绑定导入表

PE加载EXE相关的DLL时,首先会根据IMAGE_IMPORT_DESCRIPTOR结构中的TimeDateStamp来判断是否要重新
计算IAT表中的地址。
TimeDateStamp == 0 未绑定
TimeDateStamp == -1 已绑定 真正的绑定时间为IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp

绑定导入表内的时间戳用于判断dll是否已经更新,如果时间戳和dll的不一样,则重新计算IAT表

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {								
DWORD TimeDateStamp;
WORD OffsetModuleName;//第一个DESCRIPTOR的值+OffsetModuleName
WORD NumberOfModuleForwarderRefs;
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;


代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void showBOUND_IMPORT_DESCRIPTOR(LPVOID pFileBuffer)
{
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_BOUND_IMPORT_DESCRIPTOR pDESCRIPTOR = NULL;
PIMAGE_BOUND_FORWARDER_REF pFORWARDER_REF = NULL;

printf("******************************\n");
printf("********绑定导入表解析********\n");
pNtHeaders = FileToNtHeader(pFileBuffer);
if (pNtHeaders->OptionalHeader.DataDirectory[11].VirtualAddress == 0) {
return;
}
pDESCRIPTOR = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)(RVAtoFOA(pNtHeaders->OptionalHeader.DataDirectory[11].VirtualAddress, pFileBuffer)+(DWORD)pFileBuffer);
DWORD startoffer = (DWORD)pDESCRIPTOR;
PCHAR name = NULL;
while (*(DWORD*)pDESCRIPTOR != 0)
{

name = (PCHAR)startoffer + pDESCRIPTOR->OffsetModuleName;
printf("时间戳%x名字%s,NumberOfModule数量%x\n", pDESCRIPTOR->TimeDateStamp,name,pDESCRIPTOR->NumberOfModuleForwarderRefs);
if (pDESCRIPTOR->NumberOfModuleForwarderRefs != 0)
{
printf("存在NumberOfModuleForwarderRefs结构:\n");
pDESCRIPTOR = pDESCRIPTOR + 1;
for (int i= 0; i < pDESCRIPTOR->NumberOfModuleForwarderRefs; i++)
{

PIMAGE_BOUND_FORWARDER_REF pFORWARDER_REF = (PIMAGE_BOUND_FORWARDER_REF)pDESCRIPTOR;
name = (PCHAR)startoffer + pFORWARDER_REF->OffsetModuleName;
printf("时间戳%x名字%s\n", pFORWARDER_REF->TimeDateStamp, name);
pDESCRIPTOR = pDESCRIPTOR + 1;
}
printf("NumberOfModuleForwarderRefs结构结束\n");

}
pDESCRIPTOR = pDESCRIPTOR + 1;
}




}
image-20220316200303598

导入表注入

dll的main方法是添加和去除时调用,使用一个在加载和退出时调用的messagebox函数的dll验证注入是否成功

此处使用的dll名字InjectDll.dll,函数名字:ExportFunction,git目录中有这个dll

注意修改int表,iat表,导入表的结尾,要多一个这个结构,并且全是0,特别是导入表,注意大小是0x14h

步骤:

1.新加一个节(可以找节的空白处添加,注意计算添加数据大小,这里我直接添加新节了)

2.把所有导入表copy到这个节数据端,**(这里有坑,如果按照OptionalHeader.DataDirectory[1].Size的大小复制导入表,因为这个大小其实是最后一个导入表+0x14个00的大小,所以复制的位置应该是size-0x14的位置,复制完后再填充一个0x14的00)**然后新加一个导入表结构,

3.新加IAT,INT表结构,因为这里函数名只有一个,所以这两个结构都要加8字节就行(最后4个字节是00),记得赋值给OriginalFirstThunk和FirstThunk

4.新加IMAGE_IMPORT_BY_NAME结构,函数名字拷贝到IMAGE_IMPORT_BY_NAME->Name,这个结构的起始地址赋值给IAT,INT表。

5.复制dll名字,起始地址**(rva)**赋值给新加的导入表->Name。

6.修正修正IMAGE_DATA_DIRECTORY结构的VirtualAddressSize

程序实现,虽然好像写的很拉,调了2小时,最后还是成功了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
LPVOID injectDESCRIPTOR(LPVOID pFileBuffer)
{
PIMAGE_IMPORT_DESCRIPTOR pDescriptor = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_IMPORT_BY_NAME pImportbyname = NULL;
PIMAGE_IMPORT_DESCRIPTOR pDescriptorture = NULL;
PIMAGE_IMPORT_BY_NAME namefuction = NULL;




pNtHeaders = FileToNtHeader(pFileBuffer);


//第一步
//根据目录项得到导入表的信息,得到Size:导入表的总大小
pNtHeaders = FileToNtHeader(pFileBuffer);
DWORD sizeofDescriper = pNtHeaders->OptionalHeader.DataDirectory[1].Size;
//定位导入表
pDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)pNtHeaders->OptionalHeader.DataDirectory[1].VirtualAddress;
//此时在内存中的地址要加pFileBuffer
pDescriptorture = PIMAGE_IMPORT_DESCRIPTOR(RVAtoFOA((DWORD)pDescriptor, pFileBuffer) + (DWORD)pFileBuffer);
//得到OriginalFirstThunk,FirstThunk的地址
//OriginalFirstThunk
//DWORD* startThunk = (DWORD*)(RVAtoFOA((DWORD)pDescriptorture->OriginalFirstThunk, pFileBuffer) + (DWORD)pFileBuffer);
//FirstThunk
//DWORD* startFirstThunk = (DWORD*)(RVAtoFOA((DWORD)pDescriptorture->FirstThunk, pFileBuffer) + (DWORD)pFileBuffer);

/*
*/
PIMAGE_SECTION_HEADER pSectionHeaderBase = NULL;
LPVOID start = NULL;
PIMAGE_EXPORT_DIRECTORY exportDIRECTORY = NULL;
pNtHeaders = FileToNtHeader(pFileBuffer);
pSectionHeaderBase = LocateSectionBase(pFileBuffer);
//找最后一个节
//这里直接新创建一个节了就不判断大小了(用之前的函数创建完传入filebufer)

PIMAGE_SECTION_HEADER pLaswSectionHeaderBase = PIMAGE_SECTION_HEADER((BYTE*)pSectionHeaderBase + (DWORD)(0x28 * (pNtHeaders->FileHeader.NumberOfSections - 1)));
//找最后一个节的开始地址
start = (LPVOID)(pLaswSectionHeaderBase->PointerToRawData + (DWORD)pFileBuffer);
LPVOID starttest = (LPVOID)(pLaswSectionHeaderBase->PointerToRawData + (DWORD)pFileBuffer);//记录内存空间中复制开始的地址

//导入表copy到空白区
memcpy(start, pDescriptorture, sizeofDescriper);
start = (LPVOID)((DWORD)start + sizeofDescriper);//这个是最后一个导入表+1个空的导入表的位置
start = (LPVOID)((DWORD)start - 0x14);

//追加一个导入表
PIMAGE_IMPORT_DESCRIPTOR pNewdescripitor = (PIMAGE_IMPORT_DESCRIPTOR)start;
memcpy(start,starttest,0x14);
start = (LPVOID)((DWORD)start + 2*0x14);
//start = (DWORD*)start + 1;


//追加8个字节的INT表和IAT表
DWORD* startINT = (DWORD*)start;
pNewdescripitor->OriginalFirstThunk = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
start = startINT + 2;
//追加8字节的IAT表
DWORD* startIAT = (DWORD*)start;
pNewdescripitor->FirstThunk = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
start = startIAT + 2;

//追加IMAGE_IMPORT_BY_NAME,因为本来创建节就为0,所以跳过前2个字节存储函数名字
PIMAGE_IMPORT_BY_NAME Importbyname = (PIMAGE_IMPORT_BY_NAME)start;
DWORD ImportbynameRva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
//赋值给int和iat表
*startINT = ImportbynameRva;
*startIAT = ImportbynameRva;
//存储函数名字
memcpy(Importbyname->Name, "ExportFunction", sizeof("ExportFunction"));

start = (LPVOID)((DWORD)start + 2 + sizeof("ExportFunction") );//\0字符串结尾

const char* dllname =NULL;
dllname = "InjectDll.dll";

//复制dll名字
PCHAR dllName = (PCHAR)start;
memcpy(dllName, "InjectDll.dll", sizeof("InjectDll.dll"));
DWORD dllnameRva = (DWORD)pLaswSectionHeaderBase->VirtualAddress + (DWORD)start - (DWORD)starttest;
start = (LPVOID)((DWORD)start + sizeof("InjectDll.dll") );//+1是\0字符串结尾


//把这个rva赋值给导入表的name属性
pNewdescripitor->Name = dllnameRva;

//修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
pNtHeaders->OptionalHeader.DataDirectory[1].VirtualAddress = (DWORD)starttest-(DWORD)pFileBuffer;
pNtHeaders->OptionalHeader.DataDirectory[1].Size = sizeofDescriper + sizeof(_IMAGE_EXPORT_DIRECTORY);

//DWORD sizeofnewDescriptor = 20 + 16;


//free(pImageBuffer);
return pFileBuffer;
}
image-20220317171414111 image-20220317171346388

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!,本博客仅用于交流学习,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此站文章的修改和解释权。如欲转载此站文章,需取得作者同意,且必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。若造成严重后果,本人将依法追究法律责任。 阅读本站文章则默认遵守此规则。