分别打开.exe .dll .sys
等文件,观察特征前2个字节,都是4D5A,变成字母是MZ,是DOS系统开发人员的一个名字,再看3C字节是什么,3C存的是一个地址,这个地址对应的5045,变为字母就是PE。
对齐是为了读写速度,找起来快,用空间换时间
早期计算机硬盘水平比较低,只有很小的几个G,节与节之间的空隙比较小,硬盘对齐为200H,后来随着硬件水平提高了,硬盘对齐与内存对齐一样了为1000H
硬盘间隔小,内存间隔大,这是老的编译器
任何一个exe程序都会有一个自己独立的4G内存空间,虚拟内存
2G是平时写应用程序用的,2G是给操作系统用的
这里注意:还有一些exe程序 当我们用winhex打开时
它在硬盘上和内存中是一样的
这个时候我们要有两个概念 就是硬盘对齐(200h字节)和内存对齐(1000h字节),它是为了增加读写速度
比如一个qq程序有两个部分,数据1(可读),数据2(可读可写) ,都是100M,我运行一个账号就需要200M,我再开一个,数据1(可读)已经有了,就没必要在复制一份丢在内存中,只需要复制一份数据2就可以了
PE磁盘文件与内存映像图
节表(块表): PE文件中所有节的属性都被定义在节表中
PE文件头/DOS头:对当前整个exe程序做概要性描述
从文件开始的地方算,过80个字节,就是PE文件真正开始的地方
中间这一部分大小是不确定的
留了一块空间,可以放一些随意的数据
TimeDateStamp
.map
文件是对.exe
文件中函数的描述,对.exe
文件的说明
当.map
文件和.exe
文件 不同步时
就是检查 时间戳是否 一致
Characteristics
打勾的 即为1
把所有值 对应起来 0 1 0 E
(第六位是标志保留位,但是没有显示出来)
程序入口 + 内存镜像基址 才是真正的地址
进行了拉伸,完成之后完全遵守操作系统,就可以执行了
程序入口 + 内存镜像基址 才是真正的入口点地址
代码有个极大的缺点,就是只能加载打印特定固定的notepad.exe文件,而且在
不同的操作系统下面会有异常,可移植性差
typedef struct IMAGE_DOS_HEADER{
WORD e_magic;
/
/
DOS头的标识,为
4Dh
和
5Ah
。分别为字母MZ
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[
4
];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[
10
];
DWORD e_lfanew;
/
/
指向IMAGE_NT_HEADERS的所在(PE头相对于文件的偏移,用于定位PE文件)
}IMAGE_DOS_HEADER,
*
PIMAGE_DOS_HEADER;
typedef struct IMAGE_DOS_HEADER{
WORD e_magic;
/
/
DOS头的标识,为
4Dh
和
5Ah
。分别为字母MZ
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[
4
];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[
10
];
DWORD e_lfanew;
/
/
指向IMAGE_NT_HEADERS的所在(PE头相对于文件的偏移,用于定位PE文件)
}IMAGE_DOS_HEADER,
*
PIMAGE_DOS_HEADER;
0x00
WORD e_magic;
/
/
5A4D
*
MZ标记用于判断是否为可执行文件
0x02
WORD e_cblp;
/
/
0090
0x04
WORD e_cp;
/
/
0003
0x06
WORD e_crlc;
/
/
0000
0x08
WORD e_cparhdr;
/
/
0004
0x0a
WORD e_minalloc;
/
/
0000
0x0c
WORD e_maxalloc;
/
/
FFFF
0x0e
WORD e_ss;
/
/
0000
0x10
WORD e_sp;
/
/
00B8
0x12
WORD e_csum;
/
/
0000
0x14
WORD e_ip;
/
/
0000
0x16
WORD e_cs;
/
/
0000
0x18
WORD e_lfarlc;
/
/
0040
0x1a
WORD e_ovno;
/
/
0000
0x1c
WORD e_res[
4
];
/
/
0000000000000000
0x24
WORD e_oemid;
/
/
0000
0x26
WORD e_oeminfo;
/
/
0000
0x28
WORD e_res2[
10
];
/
/
20
0x3c
DWORD e_lfanew;
/
/
00000080
*
PE头相对于文件的偏移,用于定位PE文件
0x00
WORD e_magic;
/
/
5A4D
*
MZ标记用于判断是否为可执行文件
0x02
WORD e_cblp;
/
/
0090
0x04
WORD e_cp;
/
/
0003
0x06
WORD e_crlc;
/
/
0000
0x08
WORD e_cparhdr;
/
/
0004
0x0a
WORD e_minalloc;
/
/
0000
0x0c
WORD e_maxalloc;
/
/
FFFF
0x0e
WORD e_ss;
/
/
0000
0x10
WORD e_sp;
/
/
00B8
0x12
WORD e_csum;
/
/
0000
0x14
WORD e_ip;
/
/
0000
0x16
WORD e_cs;
/
/
0000
0x18
WORD e_lfarlc;
/
/
0040
0x1a
WORD e_ovno;
/
/
0000
0x1c
WORD e_res[
4
];
/
/
0000000000000000
0x24
WORD e_oemid;
/
/
0000
0x26
WORD e_oeminfo;
/
/
0000
0x28
WORD e_res2[
10
];
/
/
20
0x3c
DWORD e_lfanew;
/
/
00000080
*
PE头相对于文件的偏移,用于定位PE文件
typedef struct IMAGE_NT_HEADERS{
0x00
DWORD Signature;
/
/
PE标识
0x04
IMAGE_FILE_HEADER FileHeader;
/
/
标准PE头(
20
字节)
0x18
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
/
/
扩展PE头(大小不确定)
}IMAGE_NT_HEADERS,
*
PIMAGE_NT_HEADERS;
typedef struct IMAGE_NT_HEADERS{
0x00
DWORD Signature;
/
/
PE标识
0x04
IMAGE_FILE_HEADER FileHeader;
/
/
标准PE头(
20
字节)
0x18
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
/
/
扩展PE头(大小不确定)
}IMAGE_NT_HEADERS,
*
PIMAGE_NT_HEADERS;
0x00
WORD Machine;
/
/
014C
*
程序运行的CPU型号,
0x0
任何处理器
/
0x14C
Intel
386
及后续处理器
0x02
WORD NumberOfSections;
/
/
0008
*
文件中存在的节的总数,除了头,还有几节数据,如果要新增节或者合并节就要修改这个值.
0x04
DWORD TimeDateStamp;
/
/
3E22F0DF
*
时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的
0x08
DWORD PointerToSymbolTable;
/
/
00000000
0x0c
DWORD NumberOfSymbols;
/
/
00000000
0x10
WORD SizeOfOptionalHeader;
/
/
00E0
*
可选PE头的大小,
32
位PE文件默认E0h
=
16
*
14
,
64
位PE文件默认为F0h,大小可以自定义
0x12
WORD Characteristics;
/
/
010E
*
每个位有不同的含义,可执行文件值为
10F
即
0
1
2
3
8
位置
1
0x00
WORD Machine;
/
/
014C
*
程序运行的CPU型号,
0x0
任何处理器
/
0x14C
Intel
386
及后续处理器
0x02
WORD NumberOfSections;
/
/
0008
*
文件中存在的节的总数,除了头,还有几节数据,如果要新增节或者合并节就要修改这个值.
0x04
DWORD TimeDateStamp;
/
/
3E22F0DF
*
时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的
0x08
DWORD PointerToSymbolTable;
/
/
00000000
0x0c
DWORD NumberOfSymbols;
/
/
00000000
0x10
WORD SizeOfOptionalHeader;
/
/
00E0
*
可选PE头的大小,
32
位PE文件默认E0h
=
16
*
14
,
64
位PE文件默认为F0h,大小可以自定义
0x12
WORD Characteristics;
/
/
010E
*
每个位有不同的含义,可执行文件值为
10F
即
0
1
2
3
8
位置
1
0000
0001
0000
1110
0x00
WORD Magic;
*
说明文件类型:
10B
-
>
32
位下的PE文件
20B
-
>
64
位下的PE文件
0x02
BYTE MajorLinkerVersion;
0x03
BYTE MinorLinkerVersion;
0x04
DWORD SizeOfCode;
*
代码大小
/
所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
0x08
DWORD SizeOfInitializedData;
*
已初始化数据大小的和,必须是 FileAlignment的整数倍 编译器填的 没用
0x0c
DWORD SizeOfUninitializedData;
*
未初始化数据大小的和,必须是 FileAlignment的整数倍 编译器填的 没用
0x10
DWORD AddressOfEntryPoint;
*
程序入口
0x14
DWORD BaseOfCode;
*
代码开始的基址,编译器填的 没用
0x18
DWORD BaseOfData;
*
数据开始的基址,编译器填的 没用
0x1c
DWORD ImageBase;
*
内存镜像基址
0x20
DWORD SectionAlignment;
*
内存对齐,区段对齐
0x24
DWORD FileAlignment;
*
文件对齐
0x28
WORD MajorOperatingSystemVersion;
0x2a
WORD MinorOperatingSystemVersion;
0x2c
WORD MajorImageVersion;
0x2e
WORD MinorImageVersion;
0x30
WORD MajorSubsystemVersion;
0x32
WORD MinorSubsystemVersion;
0x34
DWORD Win32VersionValue;
0x38
DWORD SizeOfImage;
*
镜像大小
/
内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍,是拉伸之后的大小,,,
0x3c
DWORD SizeOfHeaders;
*
所有头
+
节表,技照‘文件对齐 FileAlignment’后的大小,否则加载会出错
0x40
DWORD CheckSum;
*
校验和,一些系统文件有要求,用来判断文件是否被修改
0x44
WORD Subsystem;
0x46
WORD DllCharacteristics;
0x48
DWORD SizeOfStackReserve;
*
初始化时保留的堆栈大小
0x4c
DWORD SizeOfStackCommit;
*
初始化时实际提交的大小
0x50
DWORD SizeOfHeapReserve;
*
初始化时保留的堆大小
0x54
DWORD SizeOfHeapCommit;
*
初始化时实践提交的大小
0x58
DWORD LoaderFlags;
0x5c
DWORD NumberOfRvaAndSizes;
*
目录项数目,RVA数目和大小
0x60
_IMAGE_DATA_DIRECTORY DataDirectory[
16
];
16
个结构体,每个结构体是
8
个字节
0x00
WORD Magic;
*
说明文件类型:
10B
-
>
32
位下的PE文件
20B
-
>
64
位下的PE文件
0x02
BYTE MajorLinkerVersion;
0x03
BYTE MinorLinkerVersion;
0x04
DWORD SizeOfCode;
*
代码大小
/
所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
0x08
DWORD SizeOfInitializedData;
*
已初始化数据大小的和,必须是 FileAlignment的整数倍 编译器填的 没用
0x0c
DWORD SizeOfUninitializedData;
*
未初始化数据大小的和,必须是 FileAlignment的整数倍 编译器填的 没用
0x10
DWORD AddressOfEntryPoint;
*
程序入口
0x14
DWORD BaseOfCode;
*
代码开始的基址,编译器填的 没用
0x18
DWORD BaseOfData;
*
数据开始的基址,编译器填的 没用
0x1c
DWORD ImageBase;
*
内存镜像基址
0x20
DWORD SectionAlignment;
*
内存对齐,区段对齐
0x24
DWORD FileAlignment;
*
文件对齐
0x28
WORD MajorOperatingSystemVersion;
0x2a
WORD MinorOperatingSystemVersion;
0x2c
WORD MajorImageVersion;
0x2e
WORD MinorImageVersion;
0x30
WORD MajorSubsystemVersion;
0x32
WORD MinorSubsystemVersion;
0x34
DWORD Win32VersionValue;
0x38
DWORD SizeOfImage;
*
镜像大小
/
内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍,是拉伸之后的大小,,,
0x3c
DWORD SizeOfHeaders;
*
所有头
+
节表,技照‘文件对齐 FileAlignment’后的大小,否则加载会出错
0x40
DWORD CheckSum;
*
校验和,一些系统文件有要求,用来判断文件是否被修改
0x44
WORD Subsystem;
0x46
WORD DllCharacteristics;
0x48
DWORD SizeOfStackReserve;
*
初始化时保留的堆栈大小
0x4c
DWORD SizeOfStackCommit;
*
初始化时实际提交的大小
0x50
DWORD SizeOfHeapReserve;
*
初始化时保留的堆大小
0x54
DWORD SizeOfHeapCommit;
*
初始化时实践提交的大小
0x58
DWORD LoaderFlags;
0x5c
DWORD NumberOfRvaAndSizes;
*
目录项数目,RVA数目和大小
0x60
_IMAGE_DATA_DIRECTORY DataDirectory[
16
];
16
个结构体,每个结构体是
8
个字节
int
*
OpenFile()
{
FILE
*
PointToFile
=
NULL;
int
FileSize
=
0
;
int
*
StrBuffer
=
NULL;
int
Num
=
0
;
/
/
打开文件
if
((PointToFile
=
fopen(
"C:\\notepad.exe"
,
"rb"
))
=
=
NULL) {
printf(
"打开文件失败!\n"
);
exit(
1
);
}
/
/
获取文件大小
fseek(PointToFile,
0
,
2
);
FileSize
=
ftell(PointToFile);
/
/
重定位指针
fseek(PointToFile,
0
,
0
);
/
/
buffer
指向申请的堆
StrBuffer
=
(
int
*
)(malloc(FileSize));
if
(!StrBuffer)
{
printf(
"堆空间分配失败!\n"
);
free(StrBuffer);
return
0
;
}
/
/
读取文件内容
Num
=
fread(StrBuffer,FileSize,
1
,PointToFile);
if
(!Num)
{
printf(
"读取文件内容失败!\n"
);
free(StrBuffer);
return
0
;
}
/
/
关闭文件
fclose(PointToFile);
/
/
将缓冲区内的文件内容的地址返回到调用函数的地方
return
StrBuffer;
}
int
*
FileSizes
=
OpenFile();
int
PrintfNtHeaders()
{
/
/
文件指针
unsigned
int
*
PointBuffer
=
(unsigned
int
*
)FileSizes;
unsigned short
*
pBuffer
=
(unsigned short
*
)PointBuffer;
unsigned char
*
pcBuffer
=
(unsigned char
*
)PointBuffer;
/
/
判断MZ和PE的标志
unsigned short Cmp1
=
0x5A4D
;
unsigned
int
Cmp2
=
0x00004550
;
/
/
判断文件是否读取成功
if
(!PointBuffer)
{
printf(
"文件读取失败!\n"
);
free(PointBuffer);
return
0
;
}
/
/
判断是否为MZ标志
if
(
*
pBuffer !
=
Cmp1)
{
printf(
"不是有效MZ标志!\n"
);
printf(
"%X\n"
,
*
pBuffer);
free(PointBuffer);
return
0
;
}
printf(
"*********打印DOS头*********\n"
);
printf(
"e_magic:\t\t\t%X\n"
,
*
(pBuffer));
printf(
"e_ifanew:\t\t\t%08X\n\n\n"
,
*
(PointBuffer
+
15
));
/
/
判断是否为PE标志
if
(
*
(PointBuffer
+
56
) !
=
Cmp2)
{
printf(
"不是有效的PE标志!\n"
);
printf(
"%X\n"
,
*
(PointBuffer
+
56
));
free(PointBuffer);
return
0
;
}
printf(
"*********打印标准PE文件头*********\n"
);
printf(
"PE标志:\t\t\t\t%X\n"
,
*
(PointBuffer
+
56
));
printf(
"Machine:\t\t\t%04X\n"
,
*
(pBuffer
+
114
));
printf(
"NumberOfSection:\t\t%04X\n"
,
*
(pBuffer
+
115
));
printf(
"TimeDateStamp:\t\t\t%08X\n"
,
*
(PointBuffer
+
58
));
printf(
"PointerToSymbolTable:\t\t%08X\n"
,
*
(PointBuffer
+
59
));
printf(
"NumberOfSymbols:\t\t%08X\n"
,
*
(PointBuffer
+
60
));
printf(
"SizeOfOptionalHeader:\t\t%04X\n"
,
*
(pBuffer
+
122
));
printf(
"Chrarcteristics:\t\t%04X\n\n\n"
,
*
(pBuffer
+
123
));
printf(
"*********打印标准可选PE头*********\n"
);
printf(
"Magic:\t\t\t\t%04X\n"
,
*
(pBuffer
+
124
));
printf(
"MajorLinkerVersion:\t\t%02X\n"
,
*
(pcBuffer
+
250
));
printf(
"MinorLinkerVersion:\t\t%02X\n"
,
*
(pcBuffer
+
251
));
printf(
"SizeOfCode:\t\t\t%08X\n"
,
*
(PointBuffer
+
63
));
printf(
"SizeOfInitializedData:\t\t%08X\n"
,
*
(PointBuffer
+
64
));
printf(
"SizeOfUninitializedData:\t%08X\n"
,
*
(PointBuffer
+
65
));
printf(
"AddressOfEntryPoint:\t\t%08X\n"
,
*
(PointBuffer
+
66
));
printf(
"BaseOfCode:\t\t\t%08X\n"
,
*
(PointBuffer
+
67
));
printf(
"BaseOfData:\t\t\t%08X\n"
,
*
(PointBuffer
+
68
));
printf(
"ImageBase:\t\t\t%08X\n"
,
*
(PointBuffer
+
69
));
printf(
"SectionAlignment:\t\t%08X\n"
,
*
(PointBuffer
+
70
));
printf(
"FileAlignment:\t\t\t%08X\n"
,
*
(PointBuffer
+
71
));
printf(
"MajorOperatingSystemVersion:\t%04X\n"
,
*
(pBuffer
+
144
));
printf(
"MinorOperatingSystemVersion:\t%04X\n"
,
*
(pBuffer
+
145
));
printf(
"MajorImageVersion:\t\t%04X\n"
,
*
(pBuffer
+
146
));
printf(
"MinorImageVersion:\t\t%04X\n"
,
*
(pBuffer
+
147
));
printf(
"MajorSubsystemVersion:\t\t%04X\n"
,
*
(pBuffer
+
148
));
printf(
"MinorSubsystemVersion:\t\t%04X\n"
,
*
(pBuffer
+
149
));
printf(
"Win32VersionValue:\t\t%08X\n"
,
*
(PointBuffer
+
75
));
printf(
"SizeOfImage:\t\t\t%08X\n"
,
*
(PointBuffer
+
76
));
printf(
"SizeOfHeaders:\t\t\t%08X\n"
,
*
(PointBuffer
+
77
));
printf(
"CheckSum:\t\t\t%08X\n"
,
*
(PointBuffer
+
78
));
printf(
"Subsystem:\t\t\t%04X\n"
,
*
(pBuffer
+
158
));
printf(
"DllCharacteristics:\t\t%04X\n"
,
*
(pBuffer
+
159
));
printf(
"SizeOfStackReserve:\t\t%08X\n"
,
*
(PointBuffer
+
80
));
printf(
"SizeOfStackCommit:\t\t%08X\n"
,
*
(PointBuffer
+
81
));
printf(
"SizeOfHeapReserve:\t\t%08X\n"
,
*
(PointBuffer
+
82
));
printf(
"SizeOfHeapCommit:\t\t%08X\n"
,
*
(PointBuffer
+
83
));
printf(
"LoaderFlags:\t\t\t%08X\n"
,
*
(PointBuffer
+
84
));
printf(
"NumberOfRvaAndSizes:\t\t%08X\n"
,
*
(PointBuffer
+
85
));
free(PointBuffer);
return
0
;
}
int
main()
{
PrintfNtHeaders();
OpenFile();
return
0
;
}
int
*
OpenFile()
{
FILE
*
PointToFile
=
NULL;
int
FileSize
=
0
;
int
*
StrBuffer
=
NULL;
int
Num
=
0
;
/
/
打开文件
if
((PointToFile
=
fopen(
"C:\\notepad.exe"
,
"rb"
))
=
=
NULL) {
printf(
"打开文件失败!\n"
);
exit(
1
);
}
/
/
获取文件大小
fseek(PointToFile,
0
,
2
);
FileSize
=
ftell(PointToFile);
/
/
重定位指针
fseek(PointToFile,
0
,
0
);
/
/
buffer
指向申请的堆
StrBuffer
=
(
int
*
)(malloc(FileSize));
if
(!StrBuffer)
{
printf(
"堆空间分配失败!\n"
);
free(StrBuffer);
return
0
;
}
/
/
读取文件内容
Num
=
fread(StrBuffer,FileSize,
1
,PointToFile);
if
(!Num)
{
printf(
"读取文件内容失败!\n"
);
free(StrBuffer);
return
0
;
}
/
/
关闭文件
fclose(PointToFile);
/
/
将缓冲区内的文件内容的地址返回到调用函数的地方
return
StrBuffer;
}
int
*
FileSizes
=
OpenFile();
int
PrintfNtHeaders()
{
/
/
文件指针
unsigned
int
*
PointBuffer
=
(unsigned
int
*
)FileSizes;
unsigned short
*
pBuffer
=
(unsigned short
*
)PointBuffer;
unsigned char
*
pcBuffer
=
(unsigned char
*
)PointBuffer;
/
/
判断MZ和PE的标志
unsigned short Cmp1
=
0x5A4D
;
unsigned
int
Cmp2
=
0x00004550
;
/
/
判断文件是否读取成功
if
(!PointBuffer)
{
printf(
"文件读取失败!\n"
);
free(PointBuffer);
return
0
;
}
/
/
判断是否为MZ标志
if
(
*
pBuffer !
=
Cmp1)
{
printf(
"不是有效MZ标志!\n"
);
printf(
"%X\n"
,
*
pBuffer);
free(PointBuffer);
return
0
;
}
printf(
"*********打印DOS头*********\n"
);
printf(
"e_magic:\t\t\t%X\n"
,
*
(pBuffer));
printf(
"e_ifanew:\t\t\t%08X\n\n\n"
,
*
(PointBuffer
+
15
));
/
/
判断是否为PE标志
if
(
*
(PointBuffer
+
56
) !
=
Cmp2)
{
printf(
"不是有效的PE标志!\n"
);
printf(
"%X\n"
,
*
(PointBuffer
+
56
));
free(PointBuffer);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)