pe格式代码,分析PE header
#include <stdio.h>
#include <Windows.h>
//#include <winnt.h> // 定义了pe结构体的所有成员
int main()
{
/******************************* Dos header ************************************/
IMAGE_DOS_HEADER Header; // Dos头
/******************************* PE header ************************************/
//IMAGE_FILE_HEADER peHeader; // 新的文件格式从这里开始,并开始区分32和64位程序,但是文件头被NT头包含
IMAGE_NT_HEADERS32 NTHeader; // IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS64 NT头(PE)
// NT头包含:
{
IMAGE_FILE_HEADER FileHeader; // 文件头,保存文件信息
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 选项头,存放程序信息
// 选项头包含:
{
IMAGE_DATA_DIRECTORY DataDirectory[1]; // 数据目录,存放各种表,放1个表就增加1项
}
}
/******************************* Section header ************************************/
IMAGE_SECTION_HEADER SectionHeader[1]; //节表,存放段信息,描述映射信息,描述可执行文件在内存中怎么放
// 节数据 ... // 节表和节数据 描述文件中的所有数据如何映射到内存
/******************************* Debug information and certificates ************************************/
// ....
printf("IMAGE_DOS_HEADER: size %d\n", sizeof(IMAGE_DOS_HEADER)); //dos的大小
//IMAGE_FILE_MACHINE_ALPHA64
return 0;
}
IMAGE_NT_HEADERS
IMAGE_NT_HEADERS32 接下来这个结构体如何区分32或64位并解析?
发现这两个结构体,只有最后一结构体成员个区分32和64
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
第一个成员:
DWORD Signature; 这个字段是固定的;
操作系统识别PE格式,就是开头的"MZ"和这里的字段;
宏定义: #define IMAGE_NT_SIGNATURE 0x50450000 // PE00
第二个成员:
IMAGE_FILE_HEADER:描述磁盘上PE文件的相关信息
// 文件头结构体 20B: _IMAGE_FILE_HEADER
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // ***** 表示CPU平台,不可修改:
// 32位IMAGE_FILE_MACHINE_I386, 0x014c
// 64位IMAGE_FILE_MACHINE_AMD64, 0x8664
WORD NumberOfSections; // ***** 表示段的个数
//IMAGE_SECTION_HEADER SectionHeader[]; 可以决定这里有几项
// 遍历节表经验:根据此处的个数拿对应的节表数据
DWORD TimeDateStamp; // 时间戳:链接器填写的文件生成的时间,作用不大(可修改)
DWORD PointerToSymbolTable; // 调试信息,微软调试用pdb,这里给别的操作系统用的
DWORD NumberOfSymbols; // 符号表个数:windows的符号表信息一般由PDB放置在文件后端(无用)
WORD SizeOfOptionalHeader; // ***** 这是选项头大小:用于定位节表位置=选项头地址+选项头大小(不可随便修改)
WORD Characteristics; // ***** 决定文件属性,指应用程序是一个什么程序(不可随便修改),如:dll、console....
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
第一个字段: Machine , 这里支持的真多,014c表示32位程序
小知识:
64位cpu 个人用户电脑是AMD
服务器上用的是inter IA 安腾,因为inter的64位cpu是全新架构,不兼容旧32位程序,所以没有发展起来,提起64位,用的都是AMD的;
如果是32位程序,我想把这个标志位改成64位程序会怎么样?
欺骗,不能运行, 那怎么做呢?
方式:卖出去就说是64位,运行的时候偷偷改成32位,运行退出再改回64位; 自古套路得人心啊;这样在反汇编也具有欺诈性
对抗: 有的三线产品是直接拿C8的偏移的,这里可以删掉一部分,把C8偏移改改,三线产品直接崩溃
第二个字段: NumberOfSections
IMAGE_SECTION_HEADER SectionHeader[]; 这个节表的最后一项是0
有的软件遍历数组,遍历到0结束,把PE中这个位置随便改一下,其他软件打开就蹦
这个数量很重要,遍历程序有几个段,一定要先拿这个段数量,调试器就是遍历节表来获取几个段的;
可以给软件增加一个段,软件运行的时候,把自己的代码也运行了;
最后一个字段:Characteristics
这个是组合使用的
反调试:****
如果只使用IMAGE_OPTIONAL_HEADER32结构体来获取节的数量,会导致一调试就崩溃,应该从文件头里获取选项头的大小,OD犯了这个错误;
欺骗的最高艺术,让一切看起来和真的一样;