0%

15-pe文件

PE文件

需要认真学的地方了,我多次学这个都放弃了,这次得认真学下去

手动解析PE文件

dos头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
WORD e_magic; // Magic number *
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header *
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

总大小为60+4 = 64 = 0x3c + 4

我以吾爱破解od的exe为例进行观察

带*为必背的

  1. e_magic
  2. e_lfanew

第一个ascii为MZ ,一般用于判断是否是DOS_HEADER

第二个表示NT_HEADER的偏移地址 (计算方法 0+e_lfanew) 0代表基址

变量名变量值
e_magic5A4D
e_cblp0050
e_cp0002
e_crlc0000
e_cparhdr0004
e_minalloc000F
e_maxallocFFFF
e_ss0000
e_sp00B8
e_csum0000
e_ip0000
e_cs0000
e_lfarlc0040
e_ovno001A
e_res[4]0000 * 0x4
e_oemid0000
e_oeminfo0000
e_res2[10]0000 * 0xA
e_lfanew0000 0200

NT_HEADER

1
2
3
4
5
struct NT_HEADER {
DWORD signature; //ansi字符串 恒定为50 45 PE
struct IMAGE_FILE_HEADER fileHeader; //后续介绍
struct IMAGE_OPTIONAL_HEADER optionalHeader;//后续介绍
}

IMAGE_FILE_HEADER

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //*
WORD NumberOfSections;//*
DWORD TimeDateStamp;//*
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;//*
WORD Characteristics;//*
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
变量名变量值
Machine014C
NumberOfSections0009
TimeDateStamp40B10868
PointerToSymbolTable00000000
NumberOfSymbols00000000
SizeOfOptionalHeader00E0
Characteristics010E

总大小为: 20 = 0x14

  1. Machine 代表的是PE文件的类型,只会为三个值
    • 0x014c(x86架构)
    • 0x0200(英特尔安腾架构 已基本废弃)
    • 0x8664(64架构程序)
  2. NumberOfSections 表示节(块, 区段)的数量
  3. TimeDateStamp 文件编译的时间
  4. SizeOfOptionalHeader 是可选头的大小
  5. Characteristics 文件属性,dll跟exe标志存放

IMAGE_OPTIONAL_HEADER

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
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//

WORD Magic; //*
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //*
DWORD SizeOfInitializedData;//*
DWORD SizeOfUninitializedData;//*
DWORD AddressOfEntryPoint;//*
DWORD BaseOfCode;//*
DWORD BaseOfData;//*

//
// NT additional fields.
//

DWORD ImageBase;//*
DWORD SectionAlignment;//*
DWORD FileAlignment;//*
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;//*
DWORD SizeOfHeaders;//*
DWORD CheckSum;//*
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;//*
DWORD SizeOfStackCommit;//*
DWORD SizeOfHeapReserve;//*
DWORD SizeOfHeapCommit;//*
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

Standard fields.

变量名变量值
Magic010B
MajorLinkerVersion05
MinorLinkerVersion00
SizeOfCode000AF000
SizeOfInitializedData0016CE00
SizeOfUninitializedData00000000
AddressOfEntryPoint00001000
BaseOfCode00001000
BaseOfData000B0000

NT additional fields.

变量名变量值
ImageBase00400000
SectionAlignment00001000
FileAlignment00000200
MajorOperatingSystemVersion0004
MinorOperatingSystemVersion0000
MajorImageVersion0000
MinorImageVersion0000
MajorSubsystemVersion0004
MinorSubsystemVersion0000
Win32VersionValue00000000
SizeOfImage001AF000
SizeOfHeaders00000600
CheckSum00000000
Subsystem0002
DllCharacteristics0000
SizeOfStackReserve00100000
SizeOfStackCommit00020000
SizeOfHeapReserve01000000
SizeOfHeapCommit00001000
LoaderFlags00000000
NumberOfRvaAndSizes00000010
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]非常多暂时不管
  1. Magic: The state of the image file.
    • 0x010b IMAGE_NT_OPTIONAL_HDR32_MAGIC The file is an executable image.
    • 0x020b IMAGE_NT_OPTIONAL_HDR64_MAGIC The file is an executable image.
    • 0x0107 IMAGE_ROM_OPTIONAL_HDR_MAGIC The file is a ROM image.
  2. SizeOfCode: The size of the code section, in bytes, or the sum of all such sections if there are multiple code sections. 简单来说就是代码段有多大
  3. SizeOfInitializedData: The size of the initialized data section, in bytes, or the sum of all such sections if there are multiple initialized data sections. 初始化数据段长度
  4. SizeOfUninitializedData: The size of the uninitialized data section, in bytes, or the sum of all such sections if there are multiple uninitialized data section 未初始化数据段长度
  5. AddressOfEntryPoint: A pointer to the entry point function, relative to the image base address. For executable files, this is the starting address. For device drivers, this is the address of the initialization function. The entry point function is optional for DLLs. When no entry point is present, this member is zero. 入口点地址
  6. BaseOfCode: A pointer to the beginning of the code section, relative to the image base.
  7. BaseOfData: A pointer to the beginning of the data section, relative to the image base
  8. ImageBase: The preferred address of the first byte of the image when it is loaded in memory. This value is a multiple of 64K bytes. The default value for DLLs is 0x10000000. The default value for applications is 0x00400000, except on Windows CE where it is 0x00010000.
  9. SectionAlignment: The alignment of sections loaded in memory, in bytes. This value must be greater than or equal to the FileAlignment member. The default value is the page size for the system.
  10. FileAlignment: The alignment of the raw data of sections in the image file, in bytes. The value should be a power of 2 between 512 and 64K (inclusive). The default is 512. If the SectionAlignment member is less than the system page size, this member must be the same as SectionAlignment.
  11. SizeOfImage: The size of the image, in bytes, including all headers. Must be a multiple of SectionAlignment.
  12. SizeOfHeaders: The combined size of the following items, rounded to a multiple of the value specified in the FileAlignment member.
    • e_lfanew member of IMAGE_DOS_HEADER
    • 4 byte signature
    • size of IMAGE_FILE_HEADER
    • size of optional header
    • size of all section headers
  13. CheckSum: The image file checksum. The following files are validated at load time: all drivers, any DLL loaded at boot time, and any DLL loaded into a critical system process.
  14. SizeOfStackReserve: The number of bytes to reserve for the stack. Only the memory specified by the SizeOfStackCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.
  15. SizeOfStackCommit: The number of bytes to commit for the stack.
  16. SizeOfHeapReserve: The number of bytes to reserve for the local heap. Only the memory specified by the SizeOfHeapCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.
  17. The number of bytes to commit for the local heap.

pe文件解析

doc头

WORD e_magic*“MZ标记” 用于判断是否为可执行文件.
DWORD e_lfanew;*PE头相对于文件的偏移,用于定位PE文件

标准pe头

WORD Machine; *程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器
WORD NumberOfSections; *文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值.
DWORD TimeDateStamp; *时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的.
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader; *可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义.
WORD Characteristics; *每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1

可选PE头:

WORD Magic; *说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;*所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
DWORD SizeOfInitializedData;*已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
DWORD SizeOfUninitializedData;*未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
DWORD AddressOfEntryPoint;*程序入口
DWORD BaseOfCode;*代码开始的基址,编译器填的 没用
DWORD BaseOfData;*数据开始的基址,编译器填的 没用
DWORD ImageBase;*内存镜像基址
DWORD SectionAlignment;*内存对齐
DWORD FileAlignment;*文件对齐
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;*内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
DWORD SizeOfHeaders;*所有头+节表按照文件对齐后的大小,否则加载会出错
DWORD CheckSum;*校验和,一些系统文件有要求.用来判断文件是否被修改.
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;*初始化时保留的堆栈大小
DWORD SizeOfStackCommit;*初始化时实际提交的大小
DWORD SizeOfHeapReserve;*初始化时保留的堆大小
DWORD SizeOfHeapCommit;*初始化时实践提交的大小
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;*目录项数目

编写exe读取PE头

name.h:

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
#ifndef NAME_H_INCLUDED
#define NAME_H_INCLUDED

typedef unsigned int DWORD;
typedef short WORD;
typedef char BYTE;
typedef unsigned int LONG;
typedef char* LPRVOID;
typedef short* PWORD;
typedef long* PDWORD;


#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_NT_SIGNATURE 0x4550
#define IMAGE_SIZEOF_FILE_HEADER 20

typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number *
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header *
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //*
WORD NumberOfSections;//*
DWORD TimeDateStamp;//*
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;//*
WORD Characteristics;//*
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//

WORD Magic; //*
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //*
DWORD SizeOfInitializedData;//*
DWORD SizeOfUninitializedData;//*
DWORD AddressOfEntryPoint;//*
DWORD BaseOfCode;//*
DWORD BaseOfData;//*

//
// NT additional fields.
//

DWORD ImageBase;//*
DWORD SectionAlignment;//*
DWORD FileAlignment;//*
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;//*
DWORD SizeOfHeaders;//*
DWORD CheckSum;//*
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;//*
DWORD SizeOfStackCommit;//*
DWORD SizeOfHeapReserve;//*
DWORD SizeOfHeapCommit;//*
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
//IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _NT_HEADER {
DWORD signature; //ansi字符串 恒定为50 45 PE
IMAGE_FILE_HEADER fileHeader; //后续介绍
IMAGE_OPTIONAL_HEADER32 optionalHeader;//后续介绍
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;


#endif // NAME_H_INCLUDED

main.c

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
114
115
116
117
118
119
120
#include <stdio.h>
#include <stdlib.h>
#include "name.h"



LPRVOID ReadPEFile(LPRVOID fileName)
{
FILE *pFile = NULL;
DWORD fileSize = 0;
LPRVOID pFileBuffer = NULL;

//open file
if(!(pFile=fopen(fileName, "rb")))
{
printf("无法打开exe文件\n");
return NULL;
}
//read the size
if(fseek(pFile, 0, SEEK_END)!=0)
{
printf("fseek end error\n");
return NULL;
}
if((fileSize = ftell(pFile)) == -1)
{
printf("size error\n");
return NULL;
}
if(fseek(pFile, 0, SEEK_SET)!=0)
{
printf("fseek start error\n");
return NULL;
}
if(!(pFileBuffer = malloc(fileSize)))
{
printf("malloc error\n");
fclose(pFile);
return NULL;
}
//read into buffer
if(!fread(pFileBuffer, fileSize, 1, pFile))
{
printf("read error\n");
free(pFileBuffer);
fclose(pFile);
return NULL;
}
fclose(pFile);
return pFileBuffer;
}
void PrintHeaders(LPRVOID addr)
{
LPRVOID pFileBuffer = addr;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;

if(!pFileBuffer)
{
printf("Read file error\n");
return;
}

//judge if start with MZ
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("Not a pe file!!!\n");
free(pFileBuffer);
return;
}

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
printf("********************DOS头********************\n");
printf("MZ标志: 0x%x\n", pDosHeader->e_magic);
printf("PE偏移: 0x%x\n", pDosHeader->e_lfanew);
//judge if pe
if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
free(pFileBuffer);
return;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
//print NT_Header
printf("********************NT头********************\n");
printf("NT: 0x%x\n", pNTHeader->signature);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
printf("********************PE头********************\n");
printf("PE: 0x%x\n", pPEHeader->Machine);
printf("NumberOfSections: 0x%x\n", pPEHeader->NumberOfSections);
printf("TimeDateStamp: 0x%x\n", pPEHeader->TimeDateStamp);
printf("SizeOfOptionHeader: 0x%x\n", pPEHeader->SizeOfOptionalHeader);
printf("Characteristics: 0x%x\n", pPEHeader->Characteristics);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
printf("********************OPTIOIN_PE头********************\n");
printf("OPTION_PE: 0x%x\n", pOptionHeader->Magic);
printf("AddressOfEntryPoint: 0x%x\n", pOptionHeader->AddressOfEntryPoint);
printf("ImageBase: 0x%x\n", pOptionHeader->ImageBase);
printf("SectionAlignment: 0x%x\n", pOptionHeader->SectionAlignment);
printf("FileAlignment: 0x%x\n", pOptionHeader->FileAlignment);
printf("SizeOfImage: 0x%x\n", pOptionHeader->SizeOfImage);
printf("SizeOfHeaders: 0x%x\n", pOptionHeader->SizeOfHeaders);
printf("CheckSum: 0x%x\n", pOptionHeader->CheckSum);
printf("SizeOfStackReserve: 0x%x\n", pOptionHeader->SizeOfStackReserve);
printf("SizeOfStackCommit: 0x%x\n", pOptionHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve: 0x%x\n", pOptionHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit: 0x%x\n", pOptionHeader->SizeOfHeapCommit);

printf("The real entrypoint is: 0x%x", pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint);
free(pFileBuffer);

}
int main()
{
LPRVOID addr = ReadPEFile("C:\\吾爱破解[LCG].exe");
PrintHeaders(addr);
return 0;
}

通过StudyPE对比一致

节表学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

节表通过pPEHeader的NumberOfSection 还有 SizeOfOptionHeader两个来查找,通过SizeOfOptionHeader可以找到节表起始地址,而NumberOfSection可以当做循环的终止条件,进行遍历查找

8+4*6+4+4=40

  1. Name:8个字节,一般情况下是以”\0”结尾的ASCII吗字符串来标识的名称,内容可以自定义.
    • 注意:该名称并不遵守必须以”\0”结尾的规律,如果不是以”\0”结尾,系统会截取8个字节的长度进行处理.
  2. Misc: 双字 是该节在没有对齐前的真实尺寸,该值可以不准确。
  3. VirtualAddress 节区在内存中的偏移地址。加上ImageBase才是在内存中的真正地址.
  4. SizeOfRawData 节在文件中对齐后的尺寸.
  5. PointerToRawData 节区在文件中的偏移.
  6. PointerToRelocations 在obj文件中使用 对exe无意义 //不用背
  7. PointerToLinenumbers 行号表的位置 调试的时候使用//不用背
  8. NumberOfRelocations 在obj文件中使用 对exe无意义//不用背
  9. NumberOfLinenumbers 行号表中行号的数量 调试的时候使用//不用背
  10. Characteristics 节的属性

PE加载的过程:

  1. 根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).
  2. 根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer
  3. 根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PIMAGE_SECTION_HEADER pSectionHeader =(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

printf("********************IMAGE_SECTIONS_HEADER********************\n");
int i = 0;
for(; i<pPEHeader->NumberOfSections; i++)
{
printf("Name: %s\n", pSectionHeader->Name);
printf("Misc: 0x%x\n", pSectionHeader->Misc.PhysicalAddress);
printf("VirtualAddress: 0x%x\n", pSectionHeader->VirtualAddress);
printf("SizeOfRawData: 0x%x\n", pSectionHeader->SizeOfRawData);
printf("PointerToRawData: 0x%x\n", pSectionHeader->PointerToRawData);
printf("Characteristics: 0x%x\n", pSectionHeader->Characteristics);
pSectionHeader += 1;
}

FileBuffer和ImageBuffer

PE加载的过程:
1、根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).
2、根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer
3、根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.
4、Misc.VirtualSize 和 SizeOfRawData谁大? 看情况,VirtualSize是内存中的,而SizeOfRawData是文件中的
5、FileBuffer与ImageBuffer谁大? ImageBuffer大

练习

2、编写一个函数,能够将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
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#include "name.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//**************************************************************************
//ReadPEFile:将文件读取到缓冲区
//参数说明:
//fileName 文件路径
//pFileBuffer 缓冲区指针
//返回值说明:
//读取失败返回0 否则返回实际读取的大小
//**************************************************************************
DWORD ReadPEFile(IN LPSTR fileName,OUT LPVOID* pFileBuffer)
{
FILE *pFile = NULL;
DWORD fileSize = 0;
//open file
if(!(pFile=fopen(fileName, "rb")))
{
printf("无法打开exe文件\n");
return 0;
}
//read the size
if(fseek(pFile, 0, SEEK_END)!=0)
{
printf("fseek end error\n");
return 0;
}
if((fileSize = ftell(pFile)) == -1)
{
printf("size error\n");
return 0;
}
if(fseek(pFile, 0, SEEK_SET)!=0)
{
printf("fseek pSectionHeader error\n");
return 0;
}
if(!(*pFileBuffer = malloc(fileSize)))
{
printf("malloc error\n");
fclose(pFile);
return 0;
}
//read into buffer
if(!fread(*pFileBuffer, fileSize, 1, pFile))
{
printf("read error\n");
free(pFileBuffer);
fclose(pFile);
return 0;
}
fclose(pFile);
return fileSize;
}

//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer
//参数说明:
//pFileBuffer FileBuffer指针
//pImageBuffer ImageBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//记录复制数
DWORD copyNum = 0;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取ImageBuffer大小
DWORD ImageSize = pOptionHeader->SizeOfImage;
//申请内存
if(!(*pImageBuffer = calloc(ImageSize, 1)))
{
printf("malloc error\n");
return 0;
}
//获取SizeOfHeaders
DWORD HeadersSize = pOptionHeader->SizeOfHeaders;
//复制全部头+节表+文件对齐
memcpy(*pImageBuffer, pFileBuffer, HeadersSize);
copyNum += HeadersSize;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
int i = 0;
for(; i<NumberOfSection; i++)
{
//获取SizeOfRawData
DWORD SizeOfRawData = pSectionHeader->SizeOfRawData;
//获取VirtualAddress + ImageBuffer
memcpy((LPVOID)((DWORD)*pImageBuffer + pSectionHeader->VirtualAddress), (LPVOID)((DWORD)pFileBuffer + pSectionHeader->PointerToRawData), SizeOfRawData);
copyNum += SizeOfRawData;
pSectionHeader += 1;
}
return copyNum;
}



//CopyImageBufferToNewBuffer:将ImageBuffer中的数据复制到新的缓冲区
//参数说明:
//pImageBuffer ImageBuffer指针
//pNewBuffer NewBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//记录复制数
DWORD copyNum = 0;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取SizeOfHeaders
DWORD HeadersSize = pOptionHeader->SizeOfHeaders;
//复制全部头+节表+文件对齐
memcpy(*pNewBuffer, pImageBuffer, HeadersSize);
copyNum += HeadersSize;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
int i = 0;
for(; i<NumberOfSection; i++)
{
//获取SizeOfRawData
DWORD SizeOfRawData = pSectionHeader->SizeOfRawData;
//获取VirtualAddress + ImageBuffer
memcpy( (LPVOID)((DWORD)*pNewBuffer + (pSectionHeader->PointerToRawData)), (LPVOID)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress), SizeOfRawData);
copyNum += SizeOfRawData;
pSectionHeader += 1;
}
return copyNum;
}

//**************************************************************************
//MemeryTOFile:将内存中的数据复制到文件
//参数说明:
//pMemBuffer 内存中数据的指针
//size 要复制的大小
//lpszFile 要存储的文件路径
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile)
{
FILE *pFile = NULL;
DWORD fileSize = 0;
//open file
if(!(pFile=fopen(lpszFile, "wb")))
{
printf("无法创建文件\n");
return 0;
}
if(!fwrite(pMemBuffer, size, 1, pFile))
{
printf("write error\n");
fclose(pFile);
return 0;
}
fclose(pFile);
return fileSize;
}

//**************************************************************************
//RvaToFileOffset:将内存偏移转换为文件偏移
//参数说明:
//pFileBuffer FileBuffer指针
//dwRva RVA的值
//返回值说明:
//返回转换后的FOA的值 如果失败返回0
//**************************************************************************
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//记录结果
DWORD result = 0;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
int i = 0;
for(; i<NumberOfSection; i++)
{
//获取SizeOfRawData
DWORD VirtualAddress = pSectionHeader->VirtualAddress;
DWORD NextVirtualAddress = VirtualAddress + pSectionHeader->Misc.VirtualSize;
if(dwRva > VirtualAddress && dwRva < NextVirtualAddress)
{
result = (dwRva - VirtualAddress) + pSectionHeader->PointerToRawData;
break;
}
pSectionHeader += 1;
}
return result;
}

void PrintHeaders(LPVOID pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
if(!pFileBuffer)
{
printf("Read file error\n");
return;
}

//judge if pSectionHeader with MZ
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("Not a pe file!!!\n");
free(pFileBuffer);
return;
}

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
printf("********************DOS头********************\n");
printf("MZ标志: 0x%x\n", pDosHeader->e_magic);
printf("PE偏移: 0x%x\n", pDosHeader->e_lfanew);
//judge if pe
if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
free(pFileBuffer);
return;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
//print NT_Header
printf("********************NT头********************\n");
printf("NT: 0x%x\n", pNTHeader->signature);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
printf("********************PE头********************\n");
printf("PE: 0x%x\n", pPEHeader->Machine);
printf("NumberOfSections: 0x%x\n", pPEHeader->NumberOfSections);
printf("TimeDateStamp: 0x%x\n", pPEHeader->TimeDateStamp);
printf("SizeOfOptionHeader: 0x%x\n", pPEHeader->SizeOfOptionalHeader);
printf("Characteristics: 0x%x\n", pPEHeader->Characteristics);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
printf("********************OPTIOIN_PE头********************\n");
printf("OPTION_PE: 0x%x\n", pOptionHeader->Magic);
printf("AddressOfEntryPoint: 0x%x\n", pOptionHeader->AddressOfEntryPoint);
printf("ImageBase: 0x%x\n", pOptionHeader->ImageBase);
printf("SectionAlignment: 0x%x\n", pOptionHeader->SectionAlignment);
printf("FileAlignment: 0x%x\n", pOptionHeader->FileAlignment);
printf("SizeOfImage: 0x%x\n", pOptionHeader->SizeOfImage);
printf("SizeOfHeaders: 0x%x\n", pOptionHeader->SizeOfHeaders);
printf("CheckSum: 0x%x\n", pOptionHeader->CheckSum);
printf("SizeOfStackReserve: 0x%x\n", pOptionHeader->SizeOfStackReserve);
printf("SizeOfStackCommit: 0x%x\n", pOptionHeader->SizeOfStackCommit);
printf("SizeOfHeapReserve: 0x%x\n", pOptionHeader->SizeOfHeapReserve);
printf("SizeOfHeapCommit: 0x%x\n", pOptionHeader->SizeOfHeapCommit);
printf("The real entrypoint is: 0x%x\n", pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint);
free(pFileBuffer);

}

手动在代码区添加Shellcode

1、获取MessageBox地址,构造ShellCode代码.
2、E8 E9计算公式
3、在代码区手动添加代码
4、修改OEP,指向ShellCode.

选了PE提取这个exe,然后发觉他FileAlign跟SectionAlign都是0x1000,所以

计算笔记:

7DCBFD1E > 8BFF mov edi,edi
42409D + x = 7DCBFD1E
4240A2 + x = 40A301

6A 00 6A 00 6A 00 6A 00 E8 81 BC 89 7D E9 5F 62 FE FF

任意节添加Shellcode

编写exe实现

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 AddShellcode(LPVOID* pImageBuffer,DWORD where)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//记录复制数
DWORD copyNum = 0;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)*pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取SizeOfHeaders
DWORD HeadersSize = pOptionHeader->SizeOfHeaders;
copyNum += HeadersSize;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
int i = 0;
for(; i<NumberOfSection; i++, pSectionHeader++)
{
if(where != i)
continue;
//获取shellcode长度
DWORD length = sizeof(shellcode)/sizeof(shellcode[0])+0x10;
//判断是否还有空闲位置存放shellcode
if(pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize < length)
{
printf("Not enough space to write this shellcode");
return ;
}
//获取SectionNullBegin;
DWORD SectionNullBegin= (DWORD)*pImageBuffer+ pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
//获取shellcode开始地址
DWORD shellcodeStart = pOptionHeader->ImageBase + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
//对齐,在整数地址节开始添加
DWORD align = (0x10- (shellcodeStart & 0xf));
SectionNullBegin += align;
shellcodeStart += align;
memcpy((LPVOID)SectionNullBegin, shellcode, length);
//修正call_addr
*(PDWORD)(SectionNullBegin+0x9) = (DWORD)(MessageBoxA_addr - (shellcodeStart+0xd));
//修正jmp addr
*(PDWORD)(SectionNullBegin+0xe) = (DWORD)((DWORD)(pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - shellcodeStart-0x12);
//修改OEP
pOptionHeader->AddressOfEntryPoint = shellcodeStart - (pOptionHeader->ImageBase);
break;
}
if(i == NumberOfSection)
{
printf("没有足够多的Section");
}
return;
}

添加节

添加节,特别要注意SizeOfImage,还有VirtualAddress,这里跟视频讲解不太一样,照着视频的会出错,高能高能,节属性要设置可读可写可执行,不要随便设置复制,会出错的,我被坑惨了

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
void AddSection(LPVOID* pImageBuffer, DWORD length)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeaderEnd = NULL;
PIMAGE_SECTION_HEADER pSectionHeaderBefore = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)*pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取SizeOfHeaders
DWORD HeadersSize = pOptionHeader->SizeOfHeaders;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
//获取最后一个和最后一个的前一个节表结束的地址
pSectionHeaderEnd = pSectionHeader + NumberOfSection;
pSectionHeaderBefore = pSectionHeader + (NumberOfSection-1);
DWORD restSize = HeadersSize - pDosHeader->e_lfanew - 20 - pPEHeader->SizeOfOptionalHeader - 0x28*NumberOfSection;
if(restSize < 0x28*2)
{
printf("Not enough space to add Section");
return;
}

//复制节跟.text相同属性
*pSectionHeaderEnd = *pSectionHeader;
//修改名字
BYTE NAME[8] = {".NewSec"};
int i=0;
for(; i<8; i++)
pSectionHeaderEnd->Name[i] = NAME[i];
//填充00

memset(pSectionHeaderEnd + 1, 0, 0x28);
//修改virtualSize
pSectionHeaderEnd->Misc.VirtualSize = getAlign(length, pOptionHeader->SectionAlignment);
//修改VirtualAddress 谁大取谁
// pSectionHeaderEnd->VirtualAddress = pSectionHeaderBefore->VirtualAddress +
// (pSectionHeaderBefore->SizeOfRawData > pSectionHeaderBefore->Misc.VirtualSize
// ? pSectionHeaderBefore->SizeOfRawData:pSectionHeaderBefore->Misc.VirtualSize);
pSectionHeaderEnd->VirtualAddress = getAlign(pOptionHeader->SizeOfImage, pOptionHeader->SectionAlignment);
//修改SizeOfRawData
pSectionHeaderEnd->SizeOfRawData = getAlign(length, pOptionHeader->FileAlignment);
//修改PointerToRawData
pSectionHeaderEnd->PointerToRawData = pSectionHeaderBefore->PointerToRawData + pSectionHeaderBefore->SizeOfRawData;
printf("%x\n", pSectionHeaderEnd->PointerToRawData);
//修改节的Charact
pSectionHeaderEnd->Characteristics = 0xE00000C0;
//修改节的数量
pPEHeader->NumberOfSections += 1;

//修改SizeOfImage
pOptionHeader->SizeOfImage += getAlign(length, pOptionHeader->SectionAlignment);
//提升空间

*pImageBuffer = realloc(*pImageBuffer, pOptionHeader->SizeOfImage);
//清空空间
memset((DWORD)*pImageBuffer+pOptionHeader->SizeOfImage - length, 0, length);
}

扩展节

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
void ExtendSection(LPVOID* pImageBuffer, DWORD where, DWORD length)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeaderEnd = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)*pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
//获取指定节
if(where >= NumberOfSection)
{
printf("OverFlow the Section table\n");
return;
}
pSectionHeaderEnd = pSectionHeader + where;
//增大节空间
pSectionHeaderEnd->Misc.VirtualSize += length;
pSectionHeaderEnd->SizeOfRawData += length;
//修改ImageSize
pOptionHeader->SizeOfImage += length;
//提升空间
realloc(*pImageBuffer, pOptionHeader->SizeOfImage);
//清空空间
memset(*pImageBuffer+pOptionHeader->SizeOfImage - length, 0, length);
}

移动节

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
void MoveSection(LPVOID* pImageBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)*pImageBuffer;
if(pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER) < 0x28*2)
{
printf("No enough space to add Section\n");
return;
}
DWORD peStart = sizeof(IMAGE_DOS_HEADER);
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取SizeOfHeaders
DWORD HeadersSize = pOptionHeader->SizeOfHeaders;
DWORD copyNum = HeadersSize - pDosHeader->e_lfanew;
memcpy((LPVOID)(*pImageBuffer+peStart), pNTHeader, copyNum);
//修改起始位置
pDosHeader->e_lfanew = peStart;
//重新获取地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
}

通过移动节可以增大剩余节的空间,通过扩展节,可以添加shellcode

问题:

1
2
3
4
5
//增大节空间
pSectionHeaderEnd->Misc.VirtualSize += 0x1000;
pSectionHeaderEnd->SizeOfRawData += length;
//修改ImageSize
pOptionHeader->SizeOfImage += 0x1000;

这里不能是三个相同,因为我添加shellcode代码要求SizeOfRawData-VirtualSize > 0x28*2

经过测试,SizeOfImage和VirtualSize不能乱改,改错就直接执行不了

合并节

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
DWORD MergeSection(LPVOID* pImageBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)*pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)*pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);

//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;

//修改第一个节表大小
DWORD FinalSize = pOptionHeader->SizeOfImage - pSectionHeader->VirtualAddress;
pSectionHeader->SizeOfRawData = pSectionHeader->Misc.VirtualSize = FinalSize;
//关键,修改属性
pSectionHeader->Characteristics = 0xE0000020;
memset(pSectionHeader+1, 0, 0x28);
//修改节表数量
pPEHeader->NumberOfSections = 1;
return pOptionHeader->SizeOfImage;
}

内存对齐

1
2
3
4
DWORD getAlign(DWORD x, DWORD y)
{
return x/y == x/(float)y ? x: (x/y+1)*y;
}

x为值,y为对齐的值

动态链接和静态链接

codeblock静态链接

动态链接

  1. 名称导出
  2. 序号导出

导出表

函数数量= 最大序号-最小序号+1,所以可能会不准

2,3,5,6 6-2+1 = 5,实际是4个函数

序号表存的减去base的,所以要base+序号表才是真正序号导出

手动找导出表

ReadPEFile->FileBuffer->pOptionHeader->DataDirectory->Export_Table

然后获取到三张表地址,AddressOfFunction, AddressOfName, AddressOfNameOrdinals

查表过程

注意:

  1. 三个Address都是RVA的地址
  2. AddressOfFunction 4个字节,AddressOfNameOrdinals 2个字节, AddressOfName 4个字节
  3. AddressOfName里存的是名字的RVA,AddressOfFunction里存的也是RVA

序号查:

将序号减去导出表的Base,然后直接从AddressOfFunction里找对应目录项,获取到RVA,然后在转成FOA

名称查:

将名字同AddressOfName里每个目录项对比,若相同,则去对应的AddressOfNameOrdinals里找序号,然后转到AddressOfFunction里

练习

获取导出表并打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void getExportTable(LPVOID pFileBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory= NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
printf("Export_Table: %x %x\n", pDataDirectory->VirtualAddress, pDataDirectory->Size);
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pDataDirectory->VirtualAddress));
printf("ImageBase: %x\n", pOptionHeader->ImageBase);
PrintfExportDirectory(pFileBuffer, pExportDirectory);
char Name[]="Sub";
printf("%s: %x\n", Name, GetFunctionAddrByName(pFileBuffer, Name));
printf("序号3: %x\n", GetFunctionAddrByOrdinals(pFileBuffer, 3));

}

打印重定位表

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
void PrintfExportDirectory(LPVOID pFileBuffer, PIMAGE_EXPORT_DIRECTORY pExportDirectory)
{
printf("Characteristics: %x\n", pExportDirectory->Characteristics);
printf("TimeDateStamp: %x\n", pExportDirectory->TimeDateStamp);
printf("MajorVersion: %x\n", pExportDirectory->MajorVersion);
printf("MinorVersion: %x\n", pExportDirectory->MinorVersion);
printf("Base: %x\n", pExportDirectory->Base);
printf("NumberOfFunctions: %x\n", pExportDirectory->NumberOfFunctions);
printf("NumberOfNames: %x\n", pExportDirectory->NumberOfNames);
int i = 0;
printf(" |函数地址表|函数序号表|函数名称表|\n");
DWORD max = pExportDirectory->NumberOfFunctions > pExportDirectory->NumberOfNames ? pExportDirectory->NumberOfFunctions:pExportDirectory->NumberOfNames;
for(; i< max; i++)
{
printf("%2x", i);
if(i < pExportDirectory->NumberOfFunctions)
printf("|%9x |", RvaToFileOffset(pFileBuffer, ((PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions)))[i]));
//printf("|%9x |", ((PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions)))[i]);
else
printf("|---------|");
if(i < pExportDirectory->NumberOfNames)
{
printf("%9x |", ((PWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNameOrdinals)))[i]);
printf("%9x |", RvaToFileOffset(pFileBuffer, ((PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNames)))[i]));
}
printf("\n");
}

}

通过两种方式获取地址

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
DWORD GetFunctionAddrByName(LPVOID pFileBuffer, LPVOID pName)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory= NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pDataDirectory->VirtualAddress));
PDWORD AddressName = pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNames);
DWORD i = 0;
DWORD NumberOfNames = pExportDirectory->NumberOfNames;
for(; i < NumberOfNames ; i++)
{
if(!strcmp( (PDWORD)(pFileBuffer + RvaToFileOffset(pFileBuffer, AddressName[i])), pName))
break;
}
if(i >= NumberOfNames)
{
printf("Not found");
return 0;
}
PWORD AddressOfNameOrdinals = pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNameOrdinals);
PDWORD AddressOfFunction = pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions);
DWORD result = RvaToFileOffset(pFileBuffer, AddressOfFunction[AddressOfNameOrdinals[i]]);
return result;
}

DWORD GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD num)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory= NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pDataDirectory->VirtualAddress));
PDWORD AddressOfFunction = pFileBuffer + RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions);
return RvaToFileOffset(pFileBuffer, AddressOfFunction[num - pExportDirectory->Base]);
}

重定位表

获取重定位表

重定位表就是处理地址冲突的表,假设有个dll是0x10000000,另一个dll的ImageBase也是,加载到内存中必然会冲突,所以重定位的话,假设让他放到0x20000000,然后就可以处理冲突了,但是代码的偏移都需要改变

打印重定位表

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
void getRelaction(LPVOID pFileBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_BASE_RELOCATION pBaseRelocation= NULL;
PBYTE pName = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
pBaseRelocation = (PIMAGE_BASE_RELOCATION)(pFileBuffer + RvaToFileOffset(pFileBuffer, pDataDirectory[5].VirtualAddress));
//pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer,pDataDirectory));
printf("Relocation_Table: %x %x\n", RvaToFileOffset(pFileBuffer, pDataDirectory[5].VirtualAddress), pDataDirectory[5].Size);
DWORD i = 0;
do{
DWORD VirtualAddress = pBaseRelocation->VirtualAddress;
DWORD SizeOfBlock = pBaseRelocation->SizeOfBlock;
pName = getSectionName(pFileBuffer, VirtualAddress);
DWORD NumberOfBlock = (SizeOfBlock - 8)/2;
PWORD pSection = (DWORD)pBaseRelocation + 8;
printf("%s VirtualAddress: %x Size: %x\n", pName, VirtualAddress, SizeOfBlock);
for(i=0; i<NumberOfBlock; i++)
{
printf("第%d项: Address:%x 属性:%x\n",i, VirtualAddress + (((WORD)(pSection[i]<<4))>>4), pSection[i]>>12);
}
pBaseRelocation = (DWORD)pBaseRelocation + SizeOfBlock;
}while(pBaseRelocation->VirtualAddress!=0 && pBaseRelocation->SizeOfBlock!=0);

}

移动导出表

  1. 新增一个节
  2. 移动AddressOfFunctions表
  3. 移动AddressOfNameOrdinals表
  4. 移动AddressOfName表
  5. 移动AddressOfName表中每个地址的内容,同时修复AddressOfName
  6. 移动整个表结构,同时修复AddressOfFunctions,AddressOfNameOrdinals,AddressOfName三个的RVA
  7. 修复pDataDirectory[0].VirtualAddress为新表的VirtualAddress
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
void TestAddSectionAndMoveExportTable(DWORD size, PBYTE NAME)
{
LPVOID pFileBuffer = NULL;
DWORD num = ReadPEFile(peFile, &pFileBuffer);
if(!num)
{
printf("ReadPEFile error\n");
return;
}
else
{
printf("ReadPEFile finish: %x\n", num);
}
DWORD FOA = AddSectionFromFileBuffer(&pFileBuffer, num, NAME, size);
printf("FOA is: %x\n", FOA);
MoveExportTable(pFileBuffer, FOA);
MemeryTOFile(pFileBuffer, num+size, "1.dll");
}
void MoveExportTable(LPVOID pFileBuffer, DWORD FOA)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory= NULL;
PIMAGE_EXPORT_DIRECTORY pCopyPlace = NULL;
PIMAGE_EXPORT_DIRECTORY pNewExportDirectory = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = pSectionHeader + (NumberOfSection-1);
DWORD VirtualAddress = pSectionHeader->VirtualAddress;
pDataDirectory = pOptionHeader->DataDirectory;
printf("Export_Table: %x %x\n", pDataDirectory->VirtualAddress, pDataDirectory->Size);
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + getFOA(pDataDirectory->VirtualAddress));
pCopyPlace = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + FOA);
//复制函数地址
PDWORD AddressOfFunction = pFileBuffer + getFOA(pExportDirectory->AddressOfFunctions);
DWORD NumberOfFunction = pExportDirectory->NumberOfFunctions;
DWORD length = 0;//记录已复制数目
//一个地址4个字节
memcpy((LPVOID)pCopyPlace, (LPVOID)AddressOfFunction, NumberOfFunction*4);
length += NumberOfFunction*4;
//复制函数对应表
DWORD AddressOfNameOrdinals = (DWORD)pFileBuffer + getFOA(pExportDirectory->AddressOfNames);
DWORD NumberOfNames = pExportDirectory->NumberOfNames;
printf("%x\n", AddressOfNameOrdinals);
memcpy((DWORD)pCopyPlace+length, AddressOfNameOrdinals, 2*NumberOfNames);
length += 2*NumberOfNames;
//复制函数名称表
PDWORD AddressOfName = (DWORD)pFileBuffer + getFOA(pExportDirectory->AddressOfNames);
DWORD i = 0;//循环变量
DWORD size = 0;//size记录名称长度
DWORD realAddr = 0;
memcpy((DWORD)pCopyPlace+length, AddressOfName, 4*NumberOfNames);
length += 4*NumberOfNames;
for(; i<NumberOfNames; i++)
{
realAddr = (DWORD)pFileBuffer + getFOA(AddressOfName[i]);
size = strlen((PBYTE)realAddr)+1;
memcpy((DWORD)pCopyPlace+length, realAddr, size);
//修复名称表
AddressOfName[i] = VirtualAddress+ length;
length += size;
}
//复制表结构
memcpy((DWORD)pCopyPlace+length, pExportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY));
//修复表
pNewExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(DWORD)pCopyPlace+length;
pNewExportDirectory->AddressOfFunctions = VirtualAddress;
pNewExportDirectory->AddressOfNameOrdinals = pNewExportDirectory->AddressOfFunctions + NumberOfFunction*4 ;
pNewExportDirectory->AddressOfNames = pNewExportDirectory->AddressOfNameOrdinals + NumberOfNames*2;
//printf("%x\n", getFOA(pNewExportDirectory->AddressOfNameOrdinals));

pDataDirectory->VirtualAddress = VirtualAddress + length ;
printf("%x\n", getFOA(pDataDirectory->VirtualAddress));
length += sizeof(IMAGE_EXPORT_DIRECTORY);


}

移动重定位表

  1. 新增一个节
  2. 将重定位表整体移动到新节里
  3. 修改pDataDirectory[5].VirtualAddress为新增节的VirtualAddress
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
void TestAddSectionAndMoveRelocationTable(DWORD size, PBYTE NAME)
{
LPVOID pFileBuffer = NULL;
DWORD num = ReadPEFile(peFile, &pFileBuffer);
if(!num)
{
printf("ReadPEFile error\n");
return;
}
else
{
printf("ReadPEFile finish: %x\n", num);
}
DWORD FOA = AddSectionFromFileBuffer(&pFileBuffer, num, NAME, size);
printf("FOA is: %x\n", FOA);
MoveRelocationTable(pFileBuffer, FOA);
MemeryTOFile(pFileBuffer, num+size, "1.dll");
}

void MoveRelocationTable(LPVOID pFileBuffer,DWORD FOA)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
PIMAGE_BASE_RELOCATION pCopyPlace = NULL;
PIMAGE_BASE_RELOCATION pNewExportDirectory = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = pSectionHeader + (NumberOfSection-1);
DWORD VirtualAddress = pSectionHeader->VirtualAddress;
pDataDirectory = pOptionHeader->DataDirectory;
pDataDirectory += 5;
printf("Relocation_Table: %x %x\n", pDataDirectory->VirtualAddress, pDataDirectory->Size);
pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + getFOA(pDataDirectory->VirtualAddress));
pCopyPlace = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + FOA);
DWORD i = 0;
DWORD length = 0;//记录长度
do{
DWORD SizeOfBlock = pBaseRelocation->SizeOfBlock;
DWORD realAddr = (DWORD)pFileBuffer + getFOA(pBaseRelocation->VirtualAddress);
memcpy((DWORD)pCopyPlace+length, pBaseRelocation, SizeOfBlock);
pBaseRelocation = (DWORD)pBaseRelocation + SizeOfBlock;
length += SizeOfBlock;
}while(pBaseRelocation->VirtualAddress!=0 && pBaseRelocation->SizeOfBlock!=0);
//增加结束标志
memcpy((DWORD)pCopyPlace+length,pBaseRelocation, sizeof(PIMAGE_BASE_RELOCATION));
//新增节的地址
pDataDirectory->VirtualAddress = VirtualAddress;
printf("%x\n", pDataDirectory->VirtualAddress);
printf("%x\n", getFOA(pDataDirectory->VirtualAddress));
}

模拟操作系统重定位过程

  1. 修改ImageBase
  2. 找到重定位表
  3. 根据重定位表修改每个地址原来的值
  4. 注意单位应该是4个字节,因为ImageBase也是4个字节
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
void ChangeImageBase(LPVOID pFileBuffer,DWORD Size)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
pSectionHeader = pSectionHeader + (NumberOfSection-1);
DWORD VirtualAddress = pSectionHeader->VirtualAddress;
pDataDirectory = pOptionHeader->DataDirectory;
printf("Relocation_Table: %x %x\n", pDataDirectory[5].VirtualAddress, pDataDirectory[5].Size);
pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + getFOA(pDataDirectory[5].VirtualAddress));
//修改ImageBase
printf("%x\n", pOptionHeader->ImageBase);
pOptionHeader->ImageBase += Size;
//修复地址
DWORD i = 0;
do{
DWORD VirtualAddress = pBaseRelocation->VirtualAddress;
DWORD SizeOfBlock = pBaseRelocation->SizeOfBlock;
DWORD NumberOfBlock = (SizeOfBlock - 8)/2;
PWORD pSection = (DWORD)pBaseRelocation + 8;
printf("VirtualAddress: %x Size: %x\n", VirtualAddress, SizeOfBlock);
for(i=0; i<NumberOfBlock; i++)
{
if((pSection[i]>>12) == 0x3)
{
PDWORD Addr = (DWORD)pFileBuffer + getFOA(VirtualAddress + (((WORD)(pSection[i]<<4))>>4));
printf("%p\n", Addr);
*Addr += Size;
}
}
pBaseRelocation = (DWORD)pBaseRelocation + SizeOfBlock;
}while(pBaseRelocation->VirtualAddress!=0 && pBaseRelocation->SizeOfBlock!=0);


}
void TestChangeImageBase()
{
LPVOID pFileBuffer = NULL;
DWORD num = ReadPEFile(peFile, &pFileBuffer);
if(!num)
{
printf("ReadPEFile error\n");
return;
}
else
{
printf("ReadPEFile finish: %x\n", num);
}
ChangeImageBase(pFileBuffer, 0x10000000);
MemeryTOFile(pFileBuffer, num, "1.dll");
}

IAT表

导入表hook, 修改导入表,然后让其跳转到我们的代码

动态解析导入的函数

导入表

打印导入表,有个问题,DLL跟EXE的导入表不一样,DLL的IAT是正确的,他的INT存的是地址,而且是

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
void PrintImportTable(LPVOID pFileBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = NULL;
PIMAGE_THUNK_DATA32 pThunkData32 = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
printf("Import_Table: %x Size: %x\n", pDataDirectory[1].VirtualAddress, pDataDirectory[1].Size);
pImportDescriptor = (DWORD)pFileBuffer + getFOA(pDataDirectory[1].VirtualAddress);
DWORD count = 0;
do{
printf("Name: %s\n", (DWORD)pFileBuffer + getFOA(pImportDescriptor[count].Name));
printf("OriginalFirstThunk: %x\n", pImportDescriptor[count].OriginalFirstThunk);
printf("FirstThunk: %x\n", pImportDescriptor[count].FirstThunk);
printf("------------------OriginalFirstThunk------------------\n");
pThunkData32 = (DWORD)pFileBuffer + getFOA(pImportDescriptor[count].OriginalFirstThunk);
DWORD i = 0;
do{
//说明是导出序号
if( pThunkData32[i].u1.Ordinal & 0x80000000)
{
printf("序号导出: %d\n", pThunkData32[i].u1.Ordinal &0x7fffffff);
}
else
{
printf("名称导出: %s\n", (DWORD)pFileBuffer + getFOA(pThunkData32[i].u1.AddressOfData)+2 );
}
i++;
}while(pThunkData32[i].u1.AddressOfData!=NULL);
printf("------------------FirstThunk------------------\n");
pThunkData32 = (DWORD)pFileBuffer + getFOA(pImportDescriptor[count].FirstThunk);
i = 0;
do{
//说明是导出序号
if( pThunkData32[i].u1.Ordinal & 0x80000000 )
{
printf("序号导出: %x\n", pThunkData32[i].u1.Ordinal &0x7fffffff);
}
else
{
printf("名称导出: %s\n", (DWORD)pFileBuffer + getFOA(pThunkData32[i].u1.AddressOfData)+2 );
}
i++;
}while(pThunkData32[i].u1.AddressOfData!=NULL);
count ++;
}while(pImportDescriptor[count].Name!=NULL);

}

绑定导入表

解决了导入表输出错误的问题,根据TimeStamp来判断是否是绑定的,绑定的话,地址就写死在那里

PE加载EXE相关的DLL时,首先会根据IMAGE_IMPORT_DESCRIPTOR结构中的TimeDateStamp来判断是否要重新计算IAT表中的地址。

TimeDateStamp == 0 未绑定

TimeDateStamp == -1 已绑定 真正的绑定时间为IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp

绑定导入表位于数据目录的第12项

还有ModuleOffset是相对于第一个的偏移,不是相对每个模块的偏移

打印绑定导入表…找不到一个有绑定导入表的,dll的VirtualAddress看不懂,然后打印不出来,到后面一节视频才知道VirtualAddress在节表里是找不到的,PEheader不用转换,因为直接复制过去的所以RVA->FOA需要考虑小于sizeOfHeader,小于的默认就是那个值

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
void PrintBoundImportTable(LPVOID pFileBuffer)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
PIMAGE_BOUND_IMPORT_DESCRIPTOR pBoundImportDescriptor = NULL;
PIMAGE_BOUND_FORWARDER_REF pBoundForwarderRef = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
printf("Import_Table: %x Size: %x\n", pDataDirectory[11].VirtualAddress, pDataDirectory[11].Size);
pBoundImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + pDataDirectory[11].VirtualAddress);
DWORD i = 0;
DWORD j = 0;
do
{
printf("TimeStamp: %x\n", pBoundImportDescriptor[i].TimeDateStamp);
printf("OffsetModuleName: %x\n", pBoundImportDescriptor[i].OffsetModuleName);
printf("NumberOfModuleForwarderRefs: %x\n", pBoundImportDescriptor[i].NumberOfModuleForwarderRefs);
printf("Name: %s\n", (DWORD)pBoundImportDescriptor + pBoundImportDescriptor[i].OffsetModuleName);
i++;
pBoundForwarderRef = &pBoundImportDescriptor[i];
for(j = 0; j<pBoundImportDescriptor[i].NumberOfModuleForwarderRefs; j++)
{
printf("TimeStamp: %x\n", pBoundForwarderRef[j].TimeDateStamp);
printf("OffsetModuleName: %x\n", pBoundForwarderRef[j].OffsetModuleName);
printf("Name: %s\n", (DWORD)pBoundImportDescriptor + pBoundForwarderRef[j].OffsetModuleName);
printf("Reserved: %x\n", pBoundForwarderRef->Reserved);
}
printf("\n");
//超过需要减1
if(j >= pBoundImportDescriptor[i].NumberOfModuleForwarderRefs && j!=0 )
j--;
i = i + j ;
}while(pBoundImportDescriptor[i].OffsetModuleName!=0);
}

导入表注入

真的坑,以为自己代码写错,对着16进制数据一通对比也没发现错误,重新按一种规格写了一遍,还是不行,草,最后发觉是节属性的问题,被节属性坑了好几遍了,所以把代码改了就行了,

1
2
//修改节的Characteristics
pSectionHeaderEnd->Characteristics = 0xE00000C0;

写了两个版本的,第一种不为什么,就是知道什么填什么,然后慢慢填满的

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
void InjectDll(LPVOID pFileBuffer, DWORD FOA, char* NAME, char* FuncName)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = NULL;
PIMAGE_IMPORT_DESCRIPTOR pCopyPlace = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_THUNK_DATA32 pThunkData32 = NULL;
PIMAGE_IMPORT_DESCRIPTOR pNewImportDescriptor = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
printf("Import_Table: %x Size: %x\n", pDataDirectory[1].VirtualAddress, pDataDirectory[1].Size);
pImportDescriptor = (DWORD)pFileBuffer + getFOA(pDataDirectory[1].VirtualAddress);
pCopyPlace = (DWORD)pFileBuffer + FOA;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
DWORD length = sizeof(IMAGE_IMPORT_DESCRIPTOR);
DWORD num = 0;//记录拷贝数
do{
memcpy((DWORD)pCopyPlace+num, pImportDescriptor, length);
pImportDescriptor++;
num += length;
}while(pImportDescriptor->Name!=NULL);

//创建结构
pNewImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pCopyPlace + num);
pNewImportDescriptor->TimeDateStamp = 0;
pNewImportDescriptor->ForwarderChain = 0;

num += (length*2);//保留2个结构体
//创建名字
DWORD temp = strlen(NAME);
memcpy(((DWORD)pCopyPlace + num), NAME, temp);
pNewImportDescriptor->Name = pSectionHeader[NumberOfSection-1].VirtualAddress + num;
//printf("%x %x\n",getFOA(pSectionHeader[NumberOfSection-1].VirtualAddress), getFOA(pCopyPlace->Name));
//printf("%x\n temp", getAlign(temp, 4));
num += (getAlign(temp+1,4));//\x00结尾字符串
//创建OrginalFirst

//创建函数名
temp = strlen(FuncName);
//伪造Thunk结构体
memset((DWORD)pCopyPlace+num, 0, 2);//Hint
memcpy((DWORD)pCopyPlace+num+2, FuncName, temp);//Name
//伪造OriginalFirst的表
//第一个指向结构体
DWORD align = getAlign(3+temp, 4);

*((PDWORD)((DWORD)pCopyPlace + num + align)) = pSectionHeader[NumberOfSection-1].VirtualAddress + num;
//第二个作为结尾标记
*((PDWORD)((DWORD)pCopyPlace + num + align + 4)) = 0;
//修复OriginalFirst
pNewImportDescriptor->OriginalFirstThunk = pSectionHeader[NumberOfSection-1].VirtualAddress + num + align;
pNewImportDescriptor->FirstThunk = pNewImportDescriptor->OriginalFirstThunk;
//伪造IAT
//memset((DWORD)pCopyPlace+num, 0, 8);//两个RVA
*((PDWORD)((DWORD)pCopyPlace + num + align + 8)) = pSectionHeader[NumberOfSection-1].VirtualAddress + num;
//第二个作为结尾标记
*((PDWORD)((DWORD)pCopyPlace + num + align + 8 + 4)) = 0;
num += (align + 8);//\x00结尾字符串并且加上两个RVA
//修复表结构
pNewImportDescriptor->FirstThunk = pSectionHeader[NumberOfSection-1].VirtualAddress + num;

//修复目录项
pDataDirectory[1].VirtualAddress = pSectionHeader[NumberOfSection-1].VirtualAddress;
pDataDirectory[1].Size += length;
printf("Name: %x\n", getFOA(pNewImportDescriptor->Name));
printf("Original: %x\n", getFOA(pNewImportDescriptor->OriginalFirstThunk));
printf("First_Thunk: %x\n", getFOA(pNewImportDescriptor->FirstThunk));
}

第二个版本,先设计好在进行填充的,所以写的很快

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
void InjectDll(LPVOID pFileBuffer, DWORD FOA, char* NAME, char* FuncName)
{
//初始化部分
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = NULL;
PIMAGE_IMPORT_DESCRIPTOR pCopyPlace = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_THUNK_DATA32 pThunkData32 = NULL;
PIMAGE_IMPORT_DESCRIPTOR pNewImportDescriptor = NULL;
//初始化地址指针
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = pOptionHeader->DataDirectory;
printf("Import_Table: %x Size: %x\n", pDataDirectory[1].VirtualAddress, pDataDirectory[1].Size);
pImportDescriptor = (DWORD)pFileBuffer + getFOA(pDataDirectory[1].VirtualAddress);
pCopyPlace = (DWORD)pFileBuffer + FOA;
//获取节表起始地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//获取节表数量
DWORD NumberOfSection = pPEHeader->NumberOfSections;
DWORD length = sizeof(IMAGE_IMPORT_DESCRIPTOR);
DWORD copyNum = 0;//记录总长度
DWORD temp = 0;//临时记录长度
DWORD funcNameAddr = 0;//记录函数名偏移
DWORD IATAddr = 0;//记录IAT表偏移
DWORD INTAddr = 0;//记录INT表偏移
DWORD VirtualAddress = pSectionHeader[NumberOfSection-1].VirtualAddress;
//复制Dll名
temp = strlen(NAME);
memcpy(pCopyPlace, NAME, temp);
temp = getAlign(temp+1, 4);
copyNum += temp;//对齐后
funcNameAddr = copyNum;
//复制函数名
temp = strlen(FuncName);
memset((DWORD)pCopyPlace+copyNum, 0, 2);
memcpy((DWORD)pCopyPlace+copyNum+2, FuncName, temp);
temp = getAlign(temp+3, 4);
INTAddr = copyNum+temp;
//INT表
*((PDWORD)((DWORD)pCopyPlace +INTAddr)) = VirtualAddress + funcNameAddr;
*((PDWORD)((DWORD)pCopyPlace + INTAddr)+1) = 0 ;
//INT表
IATAddr = INTAddr + 8;
*((PDWORD)((DWORD)pCopyPlace + IATAddr)) = VirtualAddress + funcNameAddr;
*((PDWORD)((DWORD)pCopyPlace + IATAddr)+1) = 0;
copyNum = (IATAddr+8);
//复制所有导入表结构
printf("%x\n", copyNum);
printf("%x\n", (DWORD)pCopyPlace);
printf("%x\n", (DWORD)pImportDescriptor);
do{
memcpy((DWORD)pCopyPlace+copyNum, pImportDescriptor, length);
pImportDescriptor++;
copyNum += length;
}while(pImportDescriptor->Name!=NULL);
//新增导入表结构

pNewImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pCopyPlace+copyNum);
pNewImportDescriptor->Characteristics = 0;
pNewImportDescriptor->TimeDateStamp = 0;
pNewImportDescriptor->ForwarderChain = 0;
pNewImportDescriptor->Name = VirtualAddress;
pNewImportDescriptor->OriginalFirstThunk = VirtualAddress + INTAddr;
pNewImportDescriptor->FirstThunk = VirtualAddress + IATAddr;

//修改目录表
pDataDirectory[1].VirtualAddress = VirtualAddress + (IATAddr+8);
pDataDirectory[1].Size += length;

printf("Name: %x\n", getFOA(pNewImportDescriptor->Name));
printf("Original: %x\n", getFOA(pNewImportDescriptor->OriginalFirstThunk));
printf("First_Thunk: %x\n", getFOA(pNewImportDescriptor->FirstThunk));
}

启动不了有可能是节的属性出问题

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/6fd44671/
版权声明:转载请注明出处!