levels 发布的文章
pe(protable executable file format)
exe,dll,sys,com为主流可执行文件格式(pe格式)
32位pe32 64位pe32+
winnt.h有pe详细定义
pe含程序运行时所有info
layout:
ms-dos header
nt header
section header
section 实体
MS-DOS Header
DOS MZ Header
DOS STUB
IMAGE NT HEADERS
Signature
IMAGE FILE HEADERS
IMAGE
_OPTIONAL HEADERS
IMAGE DATA DIRECTORY(array lists)
Section Table
IMAGE SECTION HEADER
......
IMAGE SECTION HEADER
Sections
SECTION
SECTION
......
SECTION
实现LoadLibrary(系统默认给的pe格式),PE文件加载到内存中经过四步
- 要判定输入文件是否是PE格式;
- 要将PE文件分块按照内存映像结构放在内存中;
- 在IAT(Import Address Table)中,填入其依赖的导入函数地址;
- 利用重定位表修复需要重定位的值。
PE文件加载,首先要检查的是该文件是否为PE格式,还需要检查该PE文件是否为DLL。
◎ PE的检测,需要检查的部分是MS-DOS头和NT头
◎ DLL的检测(只针对dll),检查NT头中的IMAGE_FILE_HEADER.
MS-DOS头
◎ MS-DOS头是微软为了考虑PE文件对DOS文件的兼容性而添加的。
• 大多数情况下由编译器自动生成,通常把
DOS MZ头与DOS stub合称为DOS文件头
◎ IMAGE_DOS_HEADER结构体如下图
IMAGE DOS HEADER结构体共64字节,其中两个字段比较重要,分别是e_magic和
e_lfanew.
byte 1字节 word 2字节 double word 4字节
e_magic: 4D5A=>MZ 代表DOS signature
e_lfanew: NT offset address
Signature字段必须被设置为0x00004550
其中Machine,NumberOfSections,SizeOfOptionalHeader, Characteristics如果出现错误,将导致该PE文件无法正常执行。
0#1 Machine
◎该字段说明了可执行文件的目标CPU类型。
常见的一些Machine码
Machine Value
Inteli386 14Ch
MIPS R3000 162h
MIPS R4000 166h
Alpha AXP 184h
Power PC 1F0h
2 NumberOfSections
◎该字段说明了在这个PE文件中节区(Section)的数目。
3 TimeDateStamp
◎该字段说明了该PE文件是何时被创建的。
4 PointerToSymbolTable
◎该字段说明了COFF符号表(基本用不到)的文件偏移位置。COFF符号表在PE文件中较为少见,通常其值为0
SizeOfOptionalHeader
对于32位文件,该字段通常为0x00E0
对于64位文件,该字段通常为0x00F0
Magic
当IMAGE_ OPTIONAL_HEADER为
IMAGE_OPTIONAL_HEADER32(32位)时,Magic
0x10B。当其为IMAGE_OPTIONAL_HEADER64时,
Magic)70x20B.
AddressOfEntryPoint
该字段的值相对虚拟地址(加载到内存的地址),该值表明了程序最先执行的代码的启始地址,即程序入口点。
ImageBase
该字段表明了PE文件被加载进内存时,文件将被优先装入的虚拟内存的地址。对于EXE来说,ImageBase通常为0x00400000;对于DLL来说,ImageBase通常为0x10000000。装载后,EIP = ImageBase+
AddressOfEntryPoint.
SectionAlignment,FileAlignment
PE文件的Body部分划分为若干节区,FileAlignment制定了节区在文件系统中的最小单位,SectionAlignment则指定了节区在内存中的最小单位。
磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment的整数倍。
SizeOflmage
加载PE文件时,SizeOflmage指定了PE Image在虚拟内存中所占的空间大小。
SizeOfHeaders
该字段表明了整个PE文件头部的大小,该值必须是FileAlignment的整数倍。
Subsystem
该字段用于区分系统驱动文件与普通可执行文件。
NumberOfRvaAndSizes
该字段表明了下面出现的DataDirectory数组的个数。一般来说该值为16。
DataDirectory
DataDirectory是有IMAGE_DATA_DIRECTORY结构体构成的数组,数组的每项都有不同的意义。
RVA&VA(OS referance:ostep)
relative virtual address
virtual address(va)
va=image base + rva
PE文件在存储时为了减少体积,FileAlignment通常小于SectionAlignment。
当文件被映射到内存中后,同一数据在文件中的偏移量与在内存中的偏移量是不一样的,这样就存在这从文件偏移地址(RAW)到相对虚拟地址(RVA)之间的转换。
如果需要对存储在硬盘中的PE文件进行操作,需要将RVA换为RAW。
动态调试TraceMe程序
对这个对话框进行功能分析,当我们输入用户名和密码后,系统有可能会调用GetDlgItemText()函数(常用于获取对话框控件的内容信息)获取Text里面的信息。
系统调用GetDlgItemText()函数至少2次,第1次获取用户名;第2次获取序列号。获取到用户名和序列号后,他可能会把输入的序列号和真正的序列号(也可能会比较用户名和序列号两个参数)进行比较(常用lstrcmp,代表long类型的string compare,即long类型字符串比较函数),相同则弹出成功对话框;不相同则弹出失败对话框。
options-->debugging options,选events选项卡,选择WinMain(或EP),这样每次加载就会直接到WinMain函数,按Ctrl+F2重新加载程序。
Alt+C返回到CPU窗口,在命令行输入bp GetDlgItemTextA,按Alt+B确认断点有效
就是看一看真正的序列号是多少。还记得我们之前并没有跟进的一个函数lstrcmpA吗?我们重新加载程序,再次跟进到这个函数。
OD
source code:
include "stdafx.h" //
int global = 50; //定义一个全局变量;
int PlusFunction(int x,int y) //定义一个返回值为int类型函数
//函数名PlusFunction,函数有两个int类型参数的函数;
{
int a = 10; //定义两个函数局部变量;
int b = 20;
return x+y+a+b+global; //函数返回值
}
int main(int argc, char* argv[])
{
int result = PlusFunction(30,40); //函数调用
printf("%d\n",result); //按格式打印result
return 0; //程序结束
}