PE

此文章为自查文章,部分内容没有详细解释。具体概念出自李承远《逆向工程核心原理》。

.exe .dll .sys

PE或PE+,注意没有PE64

C:\Windows\system32\notepad.exe //32位

C:\Windows\syswow64\notepad.exe //64位

PE文件的标志

image-20220905213538297

PE格式文件加载到内存中的情形

image-20220905213651291

DOS头 windows.h中有定义 _IMAGE_DOS_HEADE

NULL区域是未使用的区域用0填充的,补齐文件或内存粒度对齐

重点关注0x00 word e_magic=0x5a4d 对应MZ, 和dword(long)0x3c e_ifanew, (PIMAGE_NT_HEADERS)(BYTE)ImageDosHeader+(ImagerDosHeader->e_ifanew)就是NT_Headers的地址*

64位下的地址为8字节,32位为4字节,如果文件是64位,则4字节定位的地址必为RVA

32、64位的最大区别是NT_HEADERS后面是否跟数字64

NtHeaders除了signature签名还有file optional header两个成员,往下是section header signature里是四个字节的dword或long的PE00,即0x005040

image-20220905215743384

VA是虚拟内存的绝对地址,RVA是相对虚拟地址 RVA+ImageBase = VA

image-20220905220631629

PE头内部信息大多以RVA形式存在,因为PE文件被加载到内存空间的时候,每一次的绝对地址都是不一样的。32位WindowsOS中,各进程分配有4GB内存,即VA的值最多到FFFFFFFF

0-2^32-1 0-0xFFFFFFFF-1

0-2^64-1 0-0xFFFFFFFFFFFFFFFF-1但现在16进制还不使用前四个F。64位下的0是0xFFFF 0000 0000 0000,也就是只用到2^48-1

文件操作

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
290
291
292
293
294
295
296
297
298
299
#include <Windows.h>
#include <iostream>
#include <tchar.h>
#include<winnt.h>
#include<ImageHlp.h>
using namespace std;


void _tmain()
{
_tsetlocale(LC_ALL, _T("Chinese-simplified"));
//获得文件句柄
char* VirtualAddress = NULL;
DWORD FileSizeLow = 0;
DWORD FileSizeHigh = 0;
DWORD NumberOfBytesRead = 0;
PIMAGE_DOS_HEADER ImageDosHeader = NULL; //指针
PIMAGE_NT_HEADERS ImageNtHeaders = NULL;
PIMAGE_FILE_HEADER ImageFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader32 = NULL; //区分32与64
PIMAGE_OPTIONAL_HEADER64 ImageOptionalHeader64 = NULL;
PIMAGE_DATA_DIRECTORY ImageDataDirectory = NULL;
PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;


WORD Characteristics = 0;

_tprintf(_T("%p\r\n"), sizeof(IMAGE_NT_HEADERS));

TCHAR v2[] = _T("CFF Explorer.exe"); //二进制文件
TCHAR v1[] = _T("avfilter-6.dll");
TCHAR v3[] = _T("hidden.sys");
TCHAR v4[] = _T("PE文件格式.exe");

//根据文件路径打开文件
//文件句柄
HANDLE FileHandle = CreateFile(v1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);

if (FileHandle == INVALID_HANDLE_VALUE)
{
goto Exit;
}
//根据文件句柄获取文件大小
FileSizeLow = GetFileSize(FileHandle, &FileSizeHigh);

if (FileSizeLow == 0)
{
goto Exit;
}
//根据文件大小进程内存申请
VirtualAddress = new char[FileSizeLow];
if (VirtualAddress == NULL)
{
goto Exit;
}
//读取文件内容到我们动态申请的内存中
if (ReadFile(FileHandle, VirtualAddress, FileSizeLow, &NumberOfBytesRead, NULL) == FALSE)
{
goto Exit;
}
ImageDosHeader = (PIMAGE_DOS_HEADER)VirtualAddress;
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
goto Exit;
}
//VA RVA
ImageNtHeaders = (PIMAGE_NT_HEADERS)(VirtualAddress + (ImageDosHeader->e_lfanew));

if (ImageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
goto Exit;
}

ImageFileHeader = &(ImageNtHeaders->FileHeader); //定位到了第三个PE结构

if (ImageFileHeader == NULL)
{
goto Exit;
}

switch (ImageFileHeader->Machine) //判断出该PE文件的位数 exe dll sys
{
case IMAGE_FILE_MACHINE_I386:
{
_tprintf(_T("x86Pe文件\r\n"));

break;

}
case IMAGE_FILE_MACHINE_IA64:
{
_tprintf(_T("Intelx64Pe文件\r\n"));
break;
}
case IMAGE_FILE_MACHINE_AMD64:
{
_tprintf(_T("Amdx64Pe文件\r\n"));
break;
}
default:
{
goto Exit;
}
}
_tprintf(_T("NumberOfSections:%d\r\n"), ImageFileHeader->NumberOfSections);

_tprintf(_T("SizeOfOptionalHeader:%p\r\n"), ImageFileHeader->SizeOfOptionalHeader);

if (ImageFileHeader->Machine == IMAGE_FILE_MACHINE_I386) //判断正在分析的PE文件的位数
{
if (ImageFileHeader->SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32))
{
goto Exit;
}
ImageOptionalHeader32 = (PIMAGE_OPTIONAL_HEADER32)&(ImageNtHeaders->OptionalHeader);
}
else if (ImageFileHeader->Machine == IMAGE_FILE_MACHINE_IA64 ||
ImageFileHeader->Machine == IMAGE_FILE_MACHINE_AMD64)
{
if (ImageFileHeader->SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64))
{
goto Exit;
}
ImageOptionalHeader64 = (PIMAGE_OPTIONAL_HEADER64)&(ImageNtHeaders->OptionalHeader);
}
else
{
goto Exit;
}

if (ImageFileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)
{
_tprintf(_T("ExecutableImage文件\r\n"));
}
if (ImageFileHeader->Characteristics & IMAGE_FILE_SYSTEM)
{
_tprintf(_T("System文件\r\n"));
}
if (ImageFileHeader->Characteristics & IMAGE_FILE_DLL)
{
_tprintf(_T("Dll文件\r\n"));
}


if (ImageOptionalHeader32 != NULL) //真正分析的Pe文件是个32位
{
if (ImageOptionalHeader32->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) //PE
{
goto Exit;
}

//4个字节的RVA
_tprintf(_T("程序最先执行的代码起始位置的RVA%p\r\n"), ImageOptionalHeader32->AddressOfEntryPoint); //Hook OEP
//4个字节的绝对地址
_tprintf(_T("优先装载地址%p\r\n"), ImageOptionalHeader32->ImageBase);

//重点概念
_tprintf(_T("文件对齐粒度%p\r\n"), ImageOptionalHeader32->FileAlignment);
_tprintf(_T("内存对齐粒度%p\r\n"), ImageOptionalHeader32->SectionAlignment);

//exe 0x400000
//dll 0x10000000
_tprintf(_T("镜像大小%p\r\n"), ImageOptionalHeader32->SizeOfImage); //内存粒度对齐后的整个PE文件的大小

_tprintf(_T("所有头部大小%p\r\n"), ImageOptionalHeader32->SizeOfHeaders);
_tprintf(_T("子系统%p\r\n"), ImageOptionalHeader32->Subsystem); //可以判断该PE文件的文件属性


//重点的重点
//设计16结构体组成的结构体数组
//每个结构体由两个成员组成
ImageDataDirectory = &(ImageOptionalHeader32->DataDirectory[0]);
for (int i = 0;i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i++)
{
_tprintf(_T("%d VirtualAddress:%p\r\n"),i, ImageDataDirectory->VirtualAddress);
_tprintf(_T("%d ViewSize:%p\r\n"), i,ImageDataDirectory->Size);

ImageDataDirectory++;
}








}
if (ImageOptionalHeader64 != NULL)
{
if (ImageOptionalHeader32->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) //PE+
{
goto Exit;
}
//4个字节
_tprintf(_T("程序最先执行的代码起始位置的:RVA%p\r\n"), ImageOptionalHeader32->AddressOfEntryPoint);
//8个字节的绝对地址
_tprintf(_T("优先装载地址%p\r\n"), ImageOptionalHeader32->ImageBase);


_tprintf(_T("文件对齐粒度%p\r\n"), ImageOptionalHeader32->FileAlignment); //0x200
_tprintf(_T("内存对齐粒度%p\r\n"), ImageOptionalHeader32->SectionAlignment);//0x1000


_tprintf(_T("镜像大小%p\r\n"), ImageOptionalHeader32->SizeOfImage);

_tprintf(_T("所有头部大小%p\r\n"), ImageOptionalHeader32->SizeOfHeaders);
_tprintf(_T("子系统%p\r\n"), ImageOptionalHeader32->Subsystem);


//重点的重点
ImageDataDirectory = &(ImageOptionalHeader32->DataDirectory[0]);
for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
{
_tprintf(_T("%d VirtualAddress:%p\r\n"), i, ImageDataDirectory->VirtualAddress);
_tprintf(_T("%d ViewSize:%p\r\n"), i, ImageDataDirectory->Size);

ImageDataDirectory++;
}

}

/*
#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \
((ULONG_PTR)(ntheader) + \
FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
((ntheader))->FileHeader.SizeOfOptionalHeader \
))

*/

ImageSectionHeader = IMAGE_FIRST_SECTION(ImageNtHeaders);
for (int i = 0; i < ImageFileHeader->NumberOfSections; i++)
{
printf("%s\r\n", ImageSectionHeader->Name); //8个字节构成的节的名字

_tprintf(_T("节区大小(真实大小)%p\r\n"), ImageSectionHeader->Misc.VirtualSize);
_tprintf(_T("节区起始位置(内存粒度RVA)%p\r\n"), ImageSectionHeader->VirtualAddress);
_tprintf(_T("节区大小(文件粒度)%p\r\n"), ImageSectionHeader->SizeOfRawData);
_tprintf(_T("节区起始位置(文件粒度)%p\r\n"), ImageSectionHeader->PointerToRawData);
_tprintf(_T("节区属性%p\r\n"), ImageSectionHeader->Characteristics); //获取该节的属性
ImageSectionHeader++;

}



Exit:
if (VirtualAddress != NULL)
{
delete VirtualAddress;
VirtualAddress = NULL;
}
if (FileHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(FileHandle);
FileHandle = INVALID_HANDLE_VALUE;

}
return;
}


//内存粒度转换文件粒度
DWORD rva_to_offset(IMAGE_NT_HEADERS* ImageNtHeaders, DWORD Rva)
{
DWORD Offset = 0;
DWORD Limit;
IMAGE_SECTION_HEADER* ImageSectionHeader = NULL;
WORD i;//两个字节

ImageSectionHeader = IMAGE_FIRST_SECTION(ImageNtHeaders);

if (Rva < ImageSectionHeader->PointerToRawData)
return Rva;

for (i = 0; i < ImageNtHeaders->FileHeader.NumberOfSections; i++)
{
if (ImageSectionHeader[i].SizeOfRawData)
Limit = ImageSectionHeader[i].SizeOfRawData; //1.90 文件粒度
else
Limit = ImageSectionHeader[i].Misc.VirtualSize;//1.70

if (Rva >= ImageSectionHeader[i].VirtualAddress && //内存粒度开始位置
Rva < (ImageSectionHeader[i].VirtualAddress + Limit))
{
if (ImageSectionHeader[i].PointerToRawData != 0)//文件粒度开始位置
{
Offset = Rva - ImageSectionHeader[i].VirtualAddress;
Offset += ImageSectionHeader[i].PointerToRawData;
}

return Offset;
}
}

return 0;
}

文件头成员

mechine 可以判断位数。IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_IA64, IMAGE_FILE_MACHINE_AMD64。

image-20220909212322047

因此32位文件,必须在文件头中的sizeofoptional成员,等于32位的结构体。

可以通过判断characteristics的值,判断该文件的具体属性,0x2(IMAGE_FILE_EXECUTABLE_IMAGE)是exe,0x2000(IMAGE_FILE_DLL)是dll,0x1000(IMAGE_FILE_SYSTEM)是sys。文件可能有多属性,判断时用与运算。这个数字可能有多个属性组成,随便写一个按f12进去看定义可以寻找

32和64位的区别就在于选项头ImageOptionalHeader。

image-20220909221218017

image-20220919221914062

image-20220919211254300

OEP,程序执行的代码起始地址,是一个RVA,加上基地址ImageBase是程序的执行入口

ImageBase是一个绝对地址VA,预设加载值,当PE文件真正开始执行时,就优先放到ImageBase,否则就向下顺延。

image-20220919221512162

读取文件到内存里,节区对齐力度是按百对齐 ImageOptionalHeader->FileAlignment==200,节区在磁盘文件中的最小单位

PE加载器加载、exe加载dll文件等,驱使PE文件变为运行状态,节区对齐粒度是按千对齐,ImageOpetionalHeader->SectionAlignment==1000 节区在内存中的最小单位

所有在PE文件中定义成RVA的成员,都必须以内存力度(按千)对齐结构为基础进行VA值得获取

image-20220919222939288

Subsystem可以判断文件类型。0是unknown gui文件有define为IMAGE_SUBSYSTEM_WINDOWS_GUI,按f12进入看其他的

NumberOfRvaAndSizes一般是16,但也有可能不是

有#define IMAGE_NUMBER_DIRECTOY_NUMS 16

重点:

DataDirectory 设计16成员组成的数组,每个结构体内有两个成员:VirtualAddress(RVA), ViewSize(Size),0-15

image-20230214020404518

image-20220921212701408

在NtHeaders内有一个Signature成员,使用Signature+OptionalHeaders(选项头之前的大小)+SizeOfOptionalHeaders(选项头自身的大小)就可以定位到SectionHeaders(IMAGE_FIRST_SECTION(ImageNtHeaders);)

image-20221127212121731

微软自带的计算偏移的宏FIELD_OFFSET(type, v1);返回值为long型,可以得到v1在type类型中的偏移。

ImageFileHeader中有一个成员是NumberOfSections,代表有多少个节。ImageSectionHeader的Name成员是节的名字,由8个字节构成,如.text

image-20220921214700402

image-20221127213230589

image-20220921214759832

节区头中的重要成员,分别是真实大小(没有填充过的),内存粒度,文件粒度,文件粒度

image-20220921222121985

节头中有个联合体misc,其中virtualSize是真实节大小

VirtualAddress RVA 按千对齐 节的起始位置

SizeOfRawData 节的大小,按200对齐

PointerToRawData 磁盘上节的开始位置,按200对齐

VA绝对地址 RVA相对地址

RVA+ImageBase = VA

内存粒度RVA计算文件粒度公式

如果传入的rva比第一个ImageSectionHeader的pointer to raw data还要小,说明这是一个头部的rva,两边一样,直接返回。否则开始扫描传入的rva属于第几个节。如果size of raw data有值,则把它放进limit变量中(按文件粒度对齐后的节大小),若没有就放入virtual size。如果rva在section header中的virtual address与virtual address+limit之间,则就是在这个区域中。

rva-VirtualAddress+PointToRawData=offset

image-20220926213113330

image-20220926214809108

IAT 导入表

image-20220926220732761

image-20220926222012185

导入函数的汇编,32位下是绝对地址,64位下是相对地址,当前地址加偏移加字节数(6)。所有API的调用均采用这种方式。

image-20221002193052700

image-20221002193042925

image-20221002193334312

动态库的基地址在不同的区域中,不一定在同一个地方

编译生成exe的时候,系统就会生成一个ImageBase,32位下一般是0x400000,dll一般是0x1000000

exe文件有导入表IAT,一般在txt节中。在DataDirectory的16成员中,第二成员指向导入表的位置。即定位到

IMAGE_IMPORT_DESCRIPTOR 导入表描述

image-20221002201505020

结构体中的Name是单字string,存放的是RVA。RVA在32、64下都是四个字节。通过转化为文件粒度,加上VirtualAddress可以定位到导入库的名字。需要强转为char*

OriginalFirstThunk INT库的地址,RVA 每次都要判断所指向的任意内容是否为空,如ul.AddressOfData。转化为offset,加上VirtualAddress,定位到新的结构体,函数名字导入。指向的结构为IMAGE_THUNK_DATA有64位和32位之分,且是一个联合体ul,64位8字节32位4字节。需要强转为DWORD。如果判断为不是名字,则是索引导入。直接用这个值转化为IMAGE_ORDINAL。文件状态下的文件粒度对齐

image-20221204232839654

FirstThunk IAT的地址,RVA 每次都要判断所指向的任意内容是否为空。进入运行状态时其中内容已经更新为地址。运行状态下内存粒度对齐。通过修改FirstThunk中的内容,可以进行IAT注入。

image-20221002202630963

image-20230119000531876

image-20230119001557338

image-20221002202822313

INT与IAT是联合体,32位下是四个字节,64位下是8个字节。

导出表

存在dll中,只有一个

导出表目录 PIMAGE_EXPORT_DIRECTORY 像导入表目录一样定位

image-20221214212322335

有索引、函数名导出两种方式

image-20221214214327363

重定向表 relocation

exe文件加载时,image base默认地址为0x400000,此时代码地址都不需要变化。但如果image base地址不是默认值,就需要重新修复,这就需要把默认情况下地址都存放在重定向表中。

进行修复时,使用真实值-0x400000,加上默认情况下的值

主要针对绝对寻址的,如全局变量、静态变量、自己定义的函数

image-20221231224618748

重定向表不止一张。virtualaddress需要进行rva to offset转换

image-20221231225852866

image-20231213205734326

获取目标进程的dll模块的方法

  1. CreatetoolHelp32Snapshot
  2. GetMoudelHandle、Loadlibary
  3. psapi EnumHandle
  4. PEB NtWow64QueryInformationProcess64获取PROCESS_BASIC_INFORMATION64(32) pbi,NtWow64ReadVirtualMemory64(32位用 ReadProcessMemory)获取(PVOID64)pbi.PebBaseAddress。遍历PEB中的_PEB_LDR_DATA双向链表,其中_LDR_DATA_TABLE_ENTRY.dllbase

PsGetProcessPeb(EPROCESS)

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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Test_Console.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <iostream>
#include <windows.h>
#include <subauth.h>

#pragma region 依赖

#define NT_SUCCESS(x) ((x) >= 0)
#define ProcessBasicInformation 0

typedef
NTSTATUS(WINAPI *pfnNtWow64QueryInformationProcess64)
(HANDLE ProcessHandle, UINT32 ProcessInformationClass,
PVOID ProcessInformation, UINT32 ProcessInformationLength,
UINT32* ReturnLength);

typedef
NTSTATUS(WINAPI *pfnNtWow64ReadVirtualMemory64)
(HANDLE ProcessHandle, PVOID64 BaseAddress,
PVOID BufferData, UINT64 BufferLength,
PUINT64 ReturnLength);

typedef
NTSTATUS(WINAPI *pfnNtQueryInformationProcess)
(HANDLE ProcessHandle, ULONG ProcessInformationClass,
PVOID ProcessInformation, UINT32 ProcessInformationLength,
UINT32* ReturnLength);

template <typename T>
struct _UNICODE_STRING_T
{
WORD Length;
WORD MaximumLength;
T Buffer;
};

template <typename T>
struct _LIST_ENTRY_T
{
T Flink;
T Blink;
};

template <typename T, typename NGF, int A>
struct _PEB_T
{
typedef T type;

union
{
struct
{
BYTE InheritedAddressSpace;
BYTE ReadImageFileExecOptions;
BYTE BeingDebugged;
BYTE BitField;
};
T dummy01;
};
T Mutant;
T ImageBaseAddress;
T Ldr;
T ProcessParameters;
T SubSystemData;
T ProcessHeap;
T FastPebLock;
T AtlThunkSListPtr;
T IFEOKey;
T CrossProcessFlags;
T UserSharedInfoPtr;
DWORD SystemReserved;
DWORD AtlThunkSListPtr32;
T ApiSetMap;
T TlsExpansionCounter;
T TlsBitmap;
DWORD TlsBitmapBits[2];
T ReadOnlySharedMemoryBase;
T HotpatchInformation;
T ReadOnlyStaticServerData;
T AnsiCodePageData;
T OemCodePageData;
T UnicodeCaseTableData;
DWORD NumberOfProcessors;
union
{
DWORD NtGlobalFlag;
NGF dummy02;
};
LARGE_INTEGER CriticalSectionTimeout;
T HeapSegmentReserve;
T HeapSegmentCommit;
T HeapDeCommitTotalFreeThreshold;
T HeapDeCommitFreeBlockThreshold;
DWORD NumberOfHeaps;
DWORD MaximumNumberOfHeaps;
T ProcessHeaps;
T GdiSharedHandleTable;
T ProcessStarterHelper;
T GdiDCAttributeList;
T LoaderLock;
DWORD OSMajorVersion;
DWORD OSMinorVersion;
WORD OSBuildNumber;
WORD OSCSDVersion;
DWORD OSPlatformId;
DWORD ImageSubsystem;
DWORD ImageSubsystemMajorVersion;
T ImageSubsystemMinorVersion;
T ActiveProcessAffinityMask;
T GdiHandleBuffer[A];
T PostProcessInitRoutine;
T TlsExpansionBitmap;
DWORD TlsExpansionBitmapBits[32];
T SessionId;
ULARGE_INTEGER AppCompatFlags;
ULARGE_INTEGER AppCompatFlagsUser;
T pShimData;
T AppCompatInfo;
_UNICODE_STRING_T<T> CSDVersion;
T ActivationContextData;
T ProcessAssemblyStorageMap;
T SystemDefaultActivationContextData;
T SystemAssemblyStorageMap;
T MinimumStackCommit;
T FlsCallback;
_LIST_ENTRY_T<T> FlsListHead;
T FlsBitmap;
DWORD FlsBitmapBits[4];
T FlsHighIndex;
T WerRegistrationData;
T WerShipAssertPtr;
T pContextData;
T pImageHeaderHash;
T TracingFlags;
T CsrServerReadOnlySharedMemoryBase;
};

typedef _PEB_T<DWORD, DWORD64, 34> _PEB32;
typedef _PEB_T<DWORD64, DWORD, 30> _PEB64;

typedef struct _STRING_32
{
WORD Length;
WORD MaximumLength;
UINT32 Buffer;
} STRING32, *PSTRING32;

typedef struct _STRING_64
{
WORD Length;
WORD MaximumLength;
UINT64 Buffer;
} STRING64, *PSTRING64;

typedef struct _RTL_DRIVE_LETTER_CURDIR_32
{
WORD Flags;
WORD Length;
ULONG TimeStamp;
STRING32 DosPath;
} RTL_DRIVE_LETTER_CURDIR32, *PRTL_DRIVE_LETTER_CURDIR32;

typedef struct _RTL_DRIVE_LETTER_CURDIR_64
{
WORD Flags;
WORD Length;
ULONG TimeStamp;
STRING64 DosPath;
} RTL_DRIVE_LETTER_CURDIR64, *PRTL_DRIVE_LETTER_CURDIR64;

typedef struct _UNICODE_STRING_32
{
WORD Length;
WORD MaximumLength;
UINT32 Buffer;
} UNICODE_STRING32, *PUNICODE_STRING32;

typedef struct _UNICODE_STRING_64
{
WORD Length;
WORD MaximumLength;
UINT64 Buffer;
} UNICODE_STRING64, *PUNICODE_STRING64;


typedef struct _CURDIR_32
{
UNICODE_STRING32 DosPath;
UINT32 Handle;
} CURDIR32, *PCURDIR32;

typedef struct _RTL_USER_PROCESS_PARAMETERS_32
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
UINT32 ConsoleHandle;
ULONG ConsoleFlags;
UINT32 StandardInput;
UINT32 StandardOutput;
UINT32 StandardError;
CURDIR32 CurrentDirectory;
UNICODE_STRING32 DllPath;
UNICODE_STRING32 ImagePathName;
UNICODE_STRING32 CommandLine;
UINT32 Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING32 WindowTitle;
UNICODE_STRING32 DesktopInfo;
UNICODE_STRING32 ShellInfo;
UNICODE_STRING32 RuntimeData;
RTL_DRIVE_LETTER_CURDIR32 CurrentDirectores[32];
ULONG EnvironmentSize;
} RTL_USER_PROCESS_PARAMETERS32, *PRTL_USER_PROCESS_PARAMETERS32;


typedef struct _CURDIR_64
{
UNICODE_STRING64 DosPath;
UINT64 Handle;
} CURDIR64, *PCURDIR64;

typedef struct _RTL_USER_PROCESS_PARAMETERS_64
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
UINT64 ConsoleHandle;
ULONG ConsoleFlags;
UINT64 StandardInput;
UINT64 StandardOutput;
UINT64 StandardError;
CURDIR64 CurrentDirectory;
UNICODE_STRING64 DllPath;
UNICODE_STRING64 ImagePathName;
UNICODE_STRING64 CommandLine;
UINT64 Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING64 WindowTitle;
UNICODE_STRING64 DesktopInfo;
UNICODE_STRING64 ShellInfo;
UNICODE_STRING64 RuntimeData;
RTL_DRIVE_LETTER_CURDIR64 CurrentDirectores[32];
ULONG EnvironmentSize;
} RTL_USER_PROCESS_PARAMETERS64, *PRTL_USER_PROCESS_PARAMETERS64;



typedef struct _PROCESS_BASIC_INFORMATION64 {
NTSTATUS ExitStatus;
UINT32 Reserved0;
UINT64 PebBaseAddress;
UINT64 AffinityMask;
UINT32 BasePriority;
UINT32 Reserved1;
UINT64 UniqueProcessId;
UINT64 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION64;

typedef struct _PROCESS_BASIC_INFORMATION32 {
NTSTATUS ExitStatus;
UINT32 PebBaseAddress;
UINT32 AffinityMask;
UINT32 BasePriority;
UINT32 UniqueProcessId;
UINT32 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION32;

#pragma endregion

int _tmain(int argc, _TCHAR* argv[])
{
HANDLE m_ProcessHandle =
OpenProcess(
PROCESS_ALL_ACCESS, // 所有权限
FALSE, // 不继承句柄
8016 // 进程ID,此处为了方便直接写死
);

BOOL bSource = FALSE; // 判断自身进程是否为 64位
BOOL bTarget = FALSE; // 判断目标进程是否为 64位
IsWow64Process(
GetCurrentProcess(), // 进程句柄
&bSource // 用来接收返回值的变量,64位 FLASE | 32位 TRUE
);
IsWow64Process(
m_ProcessHandle, // 进程句柄
&bTarget // 用来接收返回值的变量,64位 FLASE | 32位 TRUE
);

// 目标 64位,自身 32位
if(bTarget == FALSE && bSource == TRUE)
{
// 获取 ntdll.dll 模块句柄
HMODULE NtdllModule = GetModuleHandle("ntdll.dll");

pfnNtWow64QueryInformationProcess64 NtWow64QueryInformationProcess64 = (pfnNtWow64QueryInformationProcess64)GetProcAddress(NtdllModule,"NtWow64QueryInformationProcess64");
pfnNtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = (pfnNtWow64ReadVirtualMemory64)GetProcAddress(NtdllModule,"NtWow64ReadVirtualMemory64");
PROCESS_BASIC_INFORMATION64 pbi = {0};
UINT64 ReturnLength = 0;
NTSTATUS Status = NtWow64QueryInformationProcess64(m_ProcessHandle,ProcessBasicInformation,&pbi,(UINT32)sizeof(pbi),(UINT32*)&ReturnLength);

if (NT_SUCCESS(Status)){
_PEB64* Peb = (_PEB64*)malloc(sizeof(_PEB64));
RTL_USER_PROCESS_PARAMETERS64* ProcessParameters = (RTL_USER_PROCESS_PARAMETERS64*)malloc(sizeof(RTL_USER_PROCESS_PARAMETERS64));
Status = NtWow64ReadVirtualMemory64(m_ProcessHandle,(PVOID64)pbi.PebBaseAddress,(_PEB64*)Peb,sizeof(_PEB64),&ReturnLength);

std::cout << "PEB地址:" << std::hex << pbi.PebBaseAddress << std::endl;
//cout << "Ldr:" << hex << Peb->Ldr << endl;
//cout << "ImageBaseAddress:" << hex << Peb->ImageBaseAddress << endl;
}
}

// 目标 32位,自身 32位
else if (bTarget == TRUE && bSource == TRUE)
{
HMODULE NtdllModule = GetModuleHandle("ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(NtdllModule,"NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION32 pbi = {0};
UINT32 ReturnLength = 0;
NTSTATUS Status = NtQueryInformationProcess(m_ProcessHandle,ProcessBasicInformation,&pbi,(UINT32)sizeof(pbi),(UINT32*)&ReturnLength);

if (NT_SUCCESS(Status)){
_PEB32* Peb = (_PEB32*)malloc(sizeof(_PEB32));
ReadProcessMemory(m_ProcessHandle, (PVOID)pbi.PebBaseAddress,(_PEB32*)Peb,sizeof(_PEB32),NULL);

std::cout << "PEB地址:" << std::hex << pbi.PebBaseAddress << std::endl;
//printf("LdrAddress:%x\r\n", ((_PEB32*)Peb)->Ldr);
//printf("ImageBaseAddress:%x\r\n", ((_PEB32*)Peb)->ImageBaseAddress);
}
}

getchar();
return 0;
}