01-PE头

课程安排

从今天开始,我们要学习一门新的课,大体的课程安排是这样的。

  • 第一节课是 PE头
  • 第二节课是 节表
  • 第三节课是 导入表
  • 第四节课是 loadpe
  • 第五节课是 手写
  • 第六节课是 导出表
  • 第七节课重 定位表
  • 第八节课 TLS 表 和 资源表

参考书籍

  1. 加密解密
    1. 专门讲 PE 文件的那一章
  2. PE权威指南

什么是PE?

PE概念:Portable Excutable File Formate 可移植的可执行的文件格式。
它是能够被解析的文件,里面有一个内部的格式的,比如图片、音频、视频、压缩包文件等。在 windows 上可以用来被执行的文件 比如 exe、dll,它们也有自己的格式。

PE和安全行业相关的表

PE有好多表,基本上我们只会用到跟我们安全行业相关的,比如导入表、导出表,重定位表、TLS和资源表。

PE历史

PE的一段小历史,原先操作系统是 DOS 系统, DOS系统是 16 位的系统,后来才出NT windows 系统是 32 位。 DOS 系统的文件它其实是有自己的格式的,但是它只支持 16 位的。当操作系统由DOS 系统转向windows操作系统, 或者说由 16 位转向 32 位的操作系统时候。就要面临可执行文件格式的转变,就需要设计新的文件格式。比尔盖茨就希望以后所有的操作系统上的可执行文件格式都按照他的标准来,当然这个梦想微软没有实现。那么这个新的文件格式就是要兼容 16 位的文件格式,要兼容其它所有的平台方向。

PE文件格式的作用

  • 它兼容 16 位,所以在 32 位的平台上,很多的 16 位相关的字段,对于我们来说,我们都不必再学了。
  • 它要兼容很多其他的平台,导致什么有些字段的格式是为其他平台准备的,而 windows 没有用上,操作系统也不检查。所以这些字段导致操作系统压根没用,那么我们可以为所欲为的用。

生成一个比较小的PE

生成一个比较小的PE,作为一个标准的 PE 。
程序里面就弹一个 messageBox,然后一个退出进程, 就完事了。
新建工程——》下一步——》选择win32 ——》下一步——》完成。
01-PE头_第1张图片
01-PE头_第2张图片
01-PE头_第3张图片
其余的代码都不要,就弹一个 MessageBox,如以下代码所示。

.586
.model flat,stdcall
option casemap:none

   include windows.inc
   include user32.inc
   include kernel32.inc
   
   includelib user32.lib
   includelib kernel32.lib

.data
   g_szText db "Standard pe",0
   g_szTitle  db "Tip",0
.code

start:
	invoke MessageBox,NULL,offset g_szText,offset g_szTitle,MB_OK
	xor eax,eax
	invoke ExitProcess,eax
	
end start

选择构建
01-PE头_第4张图片
构建完成,无报错。
01-PE头_第5张图片
直接查看生成PE文件有3KB
01-PE头_第6张图片
或者使用WinHex工具查看,结果是3072 bytes的大小
01-PE头_第7张图片
拿出来改下名字,标准 PE
01-PE头_第8张图片
以后我们看文件格式就看它了。

编译器极限生成最小程序

使用编译器把PE文件生成到编译器的极限。编译器能生成到多小,就让它生成到多小。
ml /c /coff PE.Asm
01-PE头_第9张图片

link /subsystem:windows user32.lib kernel32.lib PE.obj
image.png
查看程序的大小 16K? 这越变越大 ???
01-PE头_第10张图片

程序中间空有非常多的0,这些0都是为了对齐文件而准备的。
01-PE头_第11张图片
所以我们要把这些0干掉。

观察程序

能看出来这里 6A00680C是代码,代码下面一堆的0。
01-PE头_第12张图片
导入函数的名称
01-PE头_第13张图片
这个是用到的字符串
01-PE头_第14张图片
用到的字符串,编译器给我准备一个内存分页。1000
01-PE头_第15张图片
所以把数据段里的代码移进到代码里面,不用再帮我为数据准备单独的。

.586
.model flat,stdcall
option casemap:none

   include windows.inc
   include user32.inc
   include kernel32.inc
   
   includelib user32.lib
   includelib kernel32.lib

.code
	g_szText db "Standard pe",0
   	g_szTitle  db "Tip",0
start:
	invoke MessageBox,NULL,offset g_szText,offset g_szTitle,MB_OK
	xor eax,eax
	invoke ExitProcess,eax
	
end start

编译——>链接
01-PE头_第16张图片
再来看一眼,发现程序大小少了一点点,12KB
01-PE头_第17张图片
这是我用到的字符串,这个是代码,这字符串和代码放一块去了。
01-PE头_第18张图片

那么接下来把导入函数跟代码也放一块
01-PE头_第19张图片
这个时候你在源码里面就改不了,只能通过链接器。
01-PE头_第20张图片

合并.rdata和.text

把这两个东西合成一块。链接器有一个选项,** /MERGE:from=to**,把两块区域合到一块。

E:\CR41\第三阶段\04 PE\01 PE头\PE>link
Microsoft ® Incremental Linker Version 6.00.8447
Copyright © Microsoft Corp 1992-1998. All rights reserved.
usage: LINK [options] [files] [@commandfile]
options:
/ALIGN:#
/BASE:{address|@filename,key}
/COMMENT:comment
/DEBUG
/DEBUGTYPE:{CV|COFF}
/DEF:filename
/DEFAULTLIB:library
/DELAY:{NOBIND|UNLOAD}
/DELAYLOAD:dll
/DLL
/DRIVER[:{UPONLY|WDM}]
/ENTRY:symbol
/EXETYPE:DYNAMIC
/EXPORT:symbol
/FIXED[:NO]
/FORCE[:{MULTIPLE|UNRESOLVED}]
/GPSIZE:#
/HEAP:reserve[,commit]
/IMPLIB:filename
/INCLUDE:symbol
/INCREMENTAL:{YES|NO}
/LARGEADDRESSAWARE[:NO]
/LIBPATH:dir
/LINK50COMPAT
/MACHINE:{ALPHA|ARM|IX86|MIPS|MIPS16|MIPSR41XX|PPC|SH3|SH4}
/MAP[:filename]
/MAPINFO:{EXPORTS|FIXUPS|LINES}
** /MERGE:from=to**
/NODEFAULTLIB[:library]
/NOENTRY
/NOLOGO
/OPT:{ICF[,iterations]|NOICF|NOREF|NOWIN98|REF|WIN98}
/ORDER:@filename
/OUT:filename
/PDB:{filename|NONE}
/PDBTYPE:{CON[SOLIDATE]|SEPT[YPES]}
/PROFILE
/RELEASE
/SECTION:name,[E][R][W][S][D][K][L][P][X]
/STACK:reserve[,commit]
/STUB:filename
/SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|WINDOWSCE|POSIX}[,#[.##]]
/SWAPRUN:{CD|NET}
/TSAWARE[:NO]
/VERBOSE[:LIB]
/VERSION:#[.#]
/VXD
/WARN[:warninglevel]
/WINDOWSCE:{CONVERT|EMULATION}
/WS:AGGRESSIVE

link /MERGE:.rdata=.text /subsystem:windows user32.lib kernel32.lib PE.obj
把.rdata 放到.text 里面
01-PE头_第21张图片
合并完大小为8KB
01-PE头_第22张图片

修改对齐值

多出来的 0 是为了对齐,说明它有个对齐值,把对齐值改小了,用来对齐的数据自然就少了。通过链接器 **/ALIGN:# **去修改对齐值。此选项指定程序的线性地址空间中的每个部分的对齐方式。number参数以字节为单位,必须是2的幂。默认值是4K。如果对齐产生无效图像,链接器发出警告。
link /ALIGN:16 /MERGE:.rdata=.text /subsystem:windows user32.lib kernel32.lib PE.obj
fatal error LNK1164: section 0x1 alignment (16) greater than /ALIGN value
致命错误LNK1164: section 0x1对齐(16)大于/ALIGN值
image.png
2B0的大小,688 KB
image.png

PE结构

创建VS工程——> 控制台
01-PE头_第23张图片
包含include
:::tips
IMAGE_DOS_HEADER
IMAGE_NT_HEADERS
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER
IMAGE_DATA_DIRECTORY[1]
IMAGE_SECTION_HEADER[1]
:::
IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER { IMAGE_DATA_DIRECTORY[1] }
都是IMAGE_NT_HEADERS 的成员。
IMAGE_DOS_HEADER 叫做DOS头
01-PE头_第24张图片
IMAGE_NT_HEADERS NT头
01-PE头_第25张图片
大概是C0从这里到1B7这里,这是 NT部分。

IMAGE_FILE_HEADER FILE头
01-PE头_第26张图片
IMAGE_OPTIONAL_HEADER
01-PE头_第27张图片
IMAGE_DATA_DIRECTORY[1]
它是一个柔性数组
01-PE头_第28张图片
常见的是 16 个,但实际上它不是固定的,它可以更加少。
IMAGE_SECTION_HEADER[1]
它也是一个柔性数组
01-PE头_第29张图片

DOS头成员

DOS头成员有很多,这里面的成员基本上给对我们来说有用的只有两个。
:::success
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;
:::
一个是 WORD e_magic; 另一个是LONG e_lfanew; 中间的这些都是都是 16 位时代的字段,跟 16 位的可执行文件是有关系的,但是到了 32 位操作系统,这个东西都是随便改的。
第一个WORD e_magic 这个是文件标识。PNG 图片的开头、还有 ZIP 文件的开头、这些文件都有着独自己独特的标识。
PDF文件
01-PE头_第30张图片
ZIP文件
01-PE头_第31张图片
文件的标识,独特的标识。这个东西能不能改。
01-PE头_第32张图片
01-PE头_第33张图片
这不是程序崩溃了,这是 windows 在加载可执行文件的时候,检查文件格式的的阶段就出了问题。它认为你不是一个可执行文件,所以直接给你报错。说明它你的格式。 检查不过关,所以这里不能改。
那么中间的字段偏移是29个WORD,(58)3A的大小。
WORD e_magic 和 LONG e_lfanew 中间的字段都是可以改的。e_lfanew是一个LONG 也就是DWORD值。
01-PE头_第34张图片
中间这些位置都是可以改的。
01-PE头_第35张图片
e_lfanew 这个是32 位T真正的NT头在文件中的偏移的位置
01-PE头_第36张图片
在这个位置到中间,你会发现是不是有一大块的残留代码。残留代码英文名指的是stub code
01-PE头_第37张图片
这一段代码其实是提示 DOS 系统的使用者的。当你把一个 32 位的程序放到 DOS 系统里面跑的时候,它就会输出字符串This program cannot be run in DOS mode
01-PE头_第38张图片

这个程序是 32 位的,不能够在DOS环境下跑。这些代码清掉。
01-PE头_第39张图片

NT头成员

IMAGE_NT_HEADERS32和IMAGE_NT_HEADERS64
:::success
#ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif
:::
这两者有区别的是指针的长度。
IMAGE_NT_HEADERS32
01-PE头_第40张图片
IMAGE_NT_HEADERS64
01-PE头_第41张图片
字段意义都是一样的,只不过是长度不一样了。

32 位的 NT 头
:::success
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
:::
有 3 个成员,第一个成员是一个DWORD,叫做 Signature 签名,
01-PE头_第42张图片
这个其实是真正的 PE 文件的文件标识,这玩意儿不能随便改,这 4 个字节是不能随便动的。
IMAGE_FILE_HEADER 文件头 描述文件信息的
:::success
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;
:::
大小是是一行半行,一般记 PE 行数,一行出 4 个DWORD 。 一行半一行出 12 个WORD。
01-PE头_第43张图片
第一个WORD Machine 这个是可执行文件所运行的 CPU 的平台或者机器平台。 Machine 取值有这些:
:::success
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
#define **IMAGE_FILE_MACHINE_I386 ** 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE

:::

常见的有IMAGE_FILE_MACHINE_I386 和 IMAGE_FILE_MACHINE_AMD64
在 windows 上基本上常见的就这两个。
01-PE头_第44张图片
14C,所以通过这里可以去判断一个 PE 文件,它是一个 32 位的 PE 文件,还是一个 64 位的 PE 文件。
NumberOfSections 解表项的个数
就是保存着 IMAGE_SECTION_HEADER[1] 数组中元素的个数。
01-PE头_第45张图片这是个数组中元素的个数,将来我们遍历解表的时候来用的
01-PE头_第46张图片
所以遍历解表是个数是由文件头的第二个字段来决定的,也不能随便改。
解表决定了如何从 PE 文件拷数据到内存,你少一个解表就少一块数据。
:::success
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
:::
这三个都是没啥用的. TimeDateStamp 时间戳, PE文件生成的时间由link 打上去的。接着 PointerToSymbolTable和NumberOfSymbols, 跟符号相关的。但是 windows 的符号现在都放到.pdb文件里面了,所以这两个也啥用,直接给它干掉。
01-PE头_第47张图片
接着这个 WORD SizeOfOptionalHeader 这个是选项头的大小
01-PE头_第48张图片IMAGE_OPTIONAL_HEADER32 它叫做选项头,这个头的大小放到这个SizeOfOptionalHeader 字段,选项图的大小来用来帮助我们定位解表的位置。

定位解表的位置

怎么定位?比如 E0大小,选项头的地址加上选项头的大小就是解表的位置。
01-PE头_第49张图片
大小不能随便改。
WORD Characteristics 属性
属性的取值有这些,这是它的属性的取值。
:::success
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Aggressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
:::
01-PE头_第50张图片01F :
:::success
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
:::
8421 按位运算。

选项头

IMAGE_OPTIONAL_HEADER32 OptionalHeader 选项头

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;

第一个 WORD Magic
01-PE头_第51张图片
01-PE头_第52张图片
它的取值有 3 个

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x107
BYTE    MajorLinkerVersion;
BYTE    MinorLinkerVersion;

主链接器版本号,副链接器版本号。这个是链接器版本号,这也是链接器打上去的东西,一般也没啥用。
01-PE头_第53张图片
直接干掉
01-PE头_第54张图片
接着

  • DWORD SizeOfCode 代码的大小
  • DWORD SizeOfInitializedData; 初始化数据的大小
  • DWORD SizeOfUninitializedData; 未初始化数据的大小

分别对应的汇编
.code :代码大小
.data :初始化数据大小
.data? :未初始化数据大小
01-PE头_第55张图片
这三个东西系统都没有依赖。
DWORD AddressOfEntryPoint;这个是程序的入口点。
01-PE头_第56张图片
这个值其实是一个与模块首地址的偏移,使用OD 打开。
01-PE头_第57张图片
这个是入口点。这玩意能随便改吗?

修改入口点

假如我要在代码段0000 0500的位置写入跳转代码 那对应的机器码是多少?代码段的起始位距离
要修改的位置偏移是100
01-PE头_第58张图片
所以在OD中偏移也是100
01-PE头_第59张图片
OD中代码起始位置是00401000,加上偏移地址100就是00401100。所以我们在00401100的位置添加修改代码。
01-PE头_第60张图片
将机器码改到0000 0500的位置中
01-PE头_第61张图片

怎么快速定位入口点?

PE 往后数两行半,修改程序入口点的偏移地址
01-PE头_第62张图片
DWORD BaseOfCode;代码的地址
DWORD BaseOfData;数据的地址
01-PE头_第63张图片
这两个东西 windows 也是操作系统是不依赖的。这种东西也是可以改。

DWORD ImageBase;模块基址 它其实就是 PE 映射进内存里面的首地址
01-PE头_第64张图片

00400000就是OD一加载,就停到这个入口点了
01-PE头_第65张图片
如果要改模块机制,你就要改很多东西,把这些原先,资源的位置,字符串、全局变量涉及到的地址都要去改掉。把模块基址改为0050 0000
01-PE头_第66张图片
01-PE头_第67张图片
01-PE头_第68张图片
这个地址,操作系统一定会满足可执行文件吗
不一定。主模块是可以满足的,是第一个被加载的模块,但是 dll 基本上是不会被满足的。比如大家都填0100000 ,dll都是这个地址。 但你肯定有一个占了,剩下占不到了,所以dll 一般情况下是满足不了的。

所以这个东西我们也叫做建议模块机制,就是可执行文件或者链接器。建议操作系统把可执行文件加载到模块地址,但是不一定就能够加载到这个地址,系统不一定能够满足这些文件。 尤其是程序,如果开了随机地址,基本上它都不会加载到00401000这个地址。 这个东西还跟实例句柄有关系,实例句柄就是可执行文件在内存中的首地址。
DWORD SectionAlignment; 内存中的对齐值
DWORD FileAlignment; 文件中的对齐值

01-PE头_第69张图片

这两个值改的时候要非常的小心。因为很多其他的值都跟这两个值息息相关,比如 模块基址它要跟谁跟这个内存对齐值对齐。

    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;

01-PE头_第70张图片

DWORD   Win32VersionValue;

01-PE头_第71张图片
Win 7 以上是可以改的。但是这个值在 XP 里面是不能随便改的。

DWORD SizeOfImage; 是可执行文件在内存中所占内存的总大小。
01-PE头_第72张图片
5000的大小
使用OD查看
01-PE头_第73张图片
它占内存 5000

它是一个计算得来的值,它是由各节的内存大小计算得来的值。
DWORD SizeOfHeaders; 这个是 PE头的总大小 400
01-PE头_第74张图片
01-PE头_第75张图片
这个值的要求它是跟文件对齐值.
DWORD CheckSum; 校验值,对于我们三环程序。 你可以随便改的.
01-PE头_第76张图片
但是对于驱动来说,这个值是不能改的,驱动会严格检查这个值,因为驱动这个玩意一旦出了问题,要比什么带来的灾难,要比我们三环程序出问题带来的灾难要大得多。
WORD Subsystem;
子系统这玩意不能随便改。哪些取值?

// Subsystem Values

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    17

改成 03CUI 控制台
01-PE头_第77张图片
01-PE头_第78张图片
这玩意不能随便改
WORD DllCharacteristics;

// DllCharacteristics Entries

//      IMAGE_LIBRARY_PROCESS_INIT            0x0001     // Reserved.
//      IMAGE_LIBRARY_PROCESS_TERM            0x0002     // Reserved.
//      IMAGE_LIBRARY_THREAD_INIT             0x0004     // Reserved.
//      IMAGE_LIBRARY_THREAD_TERM             0x0008     // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA    0x0020  // Image can handle a high entropy 64-bit virtual address space.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040     // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND      0x0800     // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000     // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   0x2000     // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF     0x4000     // Image supports Control Flow Guard.
#de

#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // DLL can move可以检测是否开启随机基址
01-PE头_第79张图片
OD查看
01-PE头_第80张图片

01-PE头_第81张图片
01-PE头_第82张图片

ULONGLONG   SizeOfStackReserve;
ULONGLONG   SizeOfStackCommit;
ULONGLONG   SizeOfHeapReserve;
ULONGLONG   SizeOfHeapCommit;

这几个值可以改,但不能改的太离谱。
01-PE头_第83张图片
DWORD LoaderFlags;
系统没用
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
这是个柔性数组这里面的元素的个数要看NumberOfRvaAndSizes ,DataDirectory 我们叫做数据目录。它其实是 PE 里面各种各样的表的位置 比如导入表\重导出表,重定位表, T2S 表,资源表.

你可能感兴趣的:(PE,windows,PE,PE文件头)