PE文件是指32位可执行文件,也称PE32;64位的可执行文件称为PE+或PE32+,是PE文件的一种扩展形式
种类主扩展名可执行系列EXE 或 SCR驱动程序系列SYS 或 VXD库系列DLL 或 OCX 或 CPL 或 DRV对象文件系列OBJ
1、从DOS头(DOS header)到节区头(Section header)是PE头部分,其下的节区合称PE体,文件中使用偏移(offset),内存中使用VA(Virtual Address 虚拟地址)来表示位置。
2、PE头与各节区的尾部都存在一个区域,称为NULL填充(NULL padding)。
1、VA指的是进程虚拟内存的绝对地址,RVA(Relative Virtual Address 相对虚拟地址)指从某个基准位置(ImageBase)开始的相对地址。其中VA与RVA满足:RVA + ImageBase = VA
2、PE头内部信息大都以RVA形式存在。
1、PE文件加载到内存时,每个节区都要准确完成内存地址与文件偏移间的映射,一般称为RVA to RAW,方法如下:
(1)查找RVA所在节区,即节区头的虚拟地址VirtualAddress
(2)使用简单的公式计算文件偏移(RAW)
2、根据IMAGE_SECTION_HEADER结构体,换算公式:
RAW – PointerToRawData = RVA – VirtualAddress,其中PointerToRawData是磁盘上的PE文件节区偏移地址,VirtualAddress是内存上的PE文件节区相对基准位置ImageBase偏移地址wps 的官网的下载的地方。
案例:
若RVA = 5000时,则RAW = 5000(RVA) – 1000(VirtualAddress) + 400(PointerToRawData)
IMAGE_DOS_HEADER结构体大小为64(0x3CH)字节,其中e_magic指示DOS签名,e_lfanew指示NT头的偏移。
wps的的官网最新下载的网址是多少
DOS存根(stub)在DOS头下方,是个可选项,且大小不固定,由代码与数据混合而成,灵活使用该特性可以在一个可执行文件中创建另一个文件,在DOS与Windows中都能运行(在DOS环境中运行16位DOS代码,在Windows环境中运行32Windows代码)。
IMAGE_NT_HEADERS32结构体由3个成员组成,第一个成员为签名Signature,值位50450000H,另外两个成员分别为文件头(File Header)与可选头(Option Header)结构体。
IMAGE_FILE_HEADER FileHeader文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader 可选头
1、VirtualAddress与PointerToRawData可以不带任何值,分别由(IMAGE_OPTIONAL_HEADER32)中的SectionAlignment与File Alignment确定。
2、Name成员不一定需要以NULL结束,也不一定是ASCII值
1、加载DLL的方式有两种:一种是“显式链接”(Explicit Linking),程序使用DLL是加载,使用完毕后释放内存;另一种是“隐式加载”(Implicit Linking),程序开始时一同加载DLL,程序终止时再释放占用内存。IAT提供的机制与隐式链接有关。
2、
1、IAT保存的内容与Windows操作系统的核心进程、内存、DLL结构有关。
2、程序编译时,并不知道该程序要运行在那种Windows(7、XP),哪种语言(ENG、JPN),哪种服务包(ServicePack)下,因此,内核重要的DLL(如kernel32.dll)的版本不一样,对应的DLL中API函数地址也不相同;为了确保在所有环境中都能正常调用DLL中的API函数,编译器要预先保存API函数实际地址的位置,并记下CALL DWORD PTR DS:[Address]指令形式,其中Address是IAT数组中的某一项地址,执行文件时,PE装载器将对应的API函数地址写到Address位置上。
3、DLL加载时,无法保证被加载到PE头指定的ImageBase地址处,需要DLL重定位(Windows系统的DLL文件拥有自身固定的ImageBase)。
4、EXE文件拥有独立的虚拟空间地址,因此能够准确加载到自身的ImageBase中。
5、微软制作服务包过程中重建相关系统文件,此时会硬编入准确地址(普通的DLL实际地址不会被硬编码到IAT中,通常带有与INT相同的值)。
图中,INT与IAT的各元素指向相同地址,但是实际很多情况下它们指向的地址不一致的。
IAT每个元素对应一个被导入的符号,元素的值在不同情况有不同的含义,在动态链接器刚完成映射还没有开始重定位和符号解析时,IAT中的元素值表示相对应的导入符号的序号或者符号名,当动态链接器完成该模块的链接时,元素值被动态链接器改写成该符号的真正地址。如若元素值最高位为1,则低31位值就是导入符号的序号值,如若没有,那么元素的值指向IMAGE_IMPORT__BY_NAME结构体的RVA
PE装载器把导入函数输入至IAT的顺序
读取IMAGE_IMPORT_DESCRIPTOR结构体的Name成员,获取库名称字符串(如"kernel32.dll")装载相应库–>LoadLibrary(“kernel32.dll”)读取IMAGE_IMPORT_DESCRIPTOR结构体的OriginalFirstThunk成员,获取INT地址逐一读取INT数组的值,获取相应IMAGE_IMPORT_BY_NAME地址(RVA)使用IMAGE_IMPORT_BY_NAME的Hint(Ordinal)或Name成员,获取相应函数的起始地址–>GetProcAddress(“GetCurrentThreadld”)从EAT中获取实际地址读取IMAGE_IMPORT_DESCRIPTOR结构体的FirstThunk(IAT)成员,获取IAT数组地址将第5步获得的函数地址输入相应IAT数组中重复步骤4~7,直到INT结束(遇到NULL)
1、只有通过EAT才能准确求得从相应库中导出函数的起始地址。
2、PE文件内的特定结构体(IMAGE_EXPORT_DIRECTORY)保存着导出信息,且PE文件中仅有一个用来说明EAT导出地址表的IMAGE_EXPORT_DIRECTORY结构体。
3、用来说明IAT导入地址表的IMAGE_EXPORT_DIRECTORY结构体以数组形式存在,且拥有多个成员。
4、从库中获取函数地址的API为GetProcAddress()函数,该API引用EAT来获取指定API的地址。
GetProcAddress()操作原理
9. 利用AddressOfNames成员转到“函数名称数组”
10. “函数名称数组”中存储着字符串地址,通过比较(strcmp)字符串,查找指定的函数名称(此时数组的索引称为name_index)
11. wps的免费版下载的网址是什么 利用AddressOfNameOrdinals成员,转到ordinal数组
12. 在ordinal数组中通过name_index查找相应的ordinal值
13. 利用addressOfFunctions成员转到“函数地址数组(EAT)
14. 在“函数地址数组”中将刚刚获得的ordinal用作数组索引,获得指定函数的起始地址
15. 指定函数的实际地址 = DLL的实际加载基址 +DLL指定函数的起始地址
**提示:**对于没有函数名称的导出函数,可以通过Ordinal查找到它们的地址。从Ordinal值重减去IMAGE_EXPORT_DIRECTORY.Base成员后得到一个值,使用该值作为“函数地址数组”的索引,即可查找到相应的函数地址。
1、TLS回调函数的运行要先于EP代码的执行,常用于反调试;
2、TLS是各线程的独立的数据存储空间;
3、TLS回调函数,每当创建/终止进程的线程时会自动调用执行的函数,创建/终止进程的主线程也会自动调用回调函数,且其调用执行先于EP代码;
1、TEB指线程环境块,包含进程中运行线程的各种信息,进程中的每个进程都对应一个TEB结构体;
2、在不同Window OS下,TEB结构体的成员不一样;
3、offset 0x00 是NtTib成员结构体,意为“线程信息块”
4、offset 0x30 是ProcessEnvironmentBlock成员,指向PEB(Process Environment Block进程环境块),每个进程对应一个PEB结构体。
SDT索引TEB结构体
1、PEB是存放进程信息的结构体,尺寸非常大;
2、在不同Window OS下,PEB结构体的成员不一样;
3、offset 0x02 BeingDebugged:Uchar 指示该进程是否正在调试状态
4、offset 0x08 ImageBaseAddress:Ptr32 Void 进程被加载的ImageBase
5、offset 0x0C Ldr:Ptr32 _PEB_LDR_DATA 指向_PEB_LDR_DATA结构体指针,当模块DLL加载到进程后,通过PEB.Ldr成员可以直接获取该模块的加载地址
6、offset 0x18 ProcessHeap:Ptr32 Void 应用于反调试技术
7、offset 0x68 NtGlobalFlag:Uint4B 应用于反调试技术
1、进程正常运行时,若发生异常,OS会委托进程处理,若进程代码中存在具体的异常处理(SEH异常处理)代码,则能顺利处理相关异常,程序继续运行,但如果进程内部没有实现SEH,那么相关异常就无法处理,OS就会启动默认的异常处理机制,终止进程运行。
2、进程调试运行时,若被调试进程内部发生异常,OS会首先把异常抛给调试进程处理,调试器几乎拥有被调试者的所有权限,不仅可以运行、终止被调试者,还拥有被调试进程的虚拟内存、寄存器的读写权限(也就是说被调试者内部发生的所有异常都由调试器处理),所以调试过程中发生的所wps官网最新下载入口在哪里有异常都要先交给调试器管理(被调试者的SEH依据优先顺序推给调试器)。被调试者发生异常时,调试器会暂停运行,必须处理异常,完成后继续调试,可以通过直接修改异常代码、寄存器、内存等。
3、将异常抛给被调试者处理:如果被调试者内部存在SEH(异常处理函数)能够处理异常,那么异常通知会发送给被调试者,由被调试者自行处理,与正常运行时的异常处理方式一样。
4、OS默认的异常处理机制:若调试器与被调试者都无法处理或故意不处理当前发生的异常,则OS的默认异常处理机制会处理它,终止被调试进程,同时结束调试。
5、SEH大量应用于压缩器、保护器、恶意程序(Malware),用来反调试。
异常发生时,执行异常代码的线程就会中断运行,转而运行SEH(异常处理器/异常处理函数),此时OS会把线程的CONTEXT结构体的指针传递给异常处理函数的相应参数,在CONTEXTwps office的免费版下载的地址在哪结构体的Eip成员(偏移量:B8),将会被设置为其他地址,然后返回异常处理函数,这样,被暂停的线程就会执行新设置的EIP地址处的代码。异常处理器处理异常后返回ExceptionContinueExecution(0),从发生异常的代码处继续运行,若当前异常处理器无法处理异常,则返回ExceptionContinueSearch(1),将异常派送到SEH链的下一个异常处理器。
PE重定位的基本操作原理
在应用程序中查找硬编码的地址位置,可以通过基址重定位表查找读取值后,减去ImageBase(VA –> RVA)加上实际加载地址(RVA –> VA)
PE加载器通过对一个IMAGE_BASE_RELOCATION结构体(位于节区.reloc)所有的TypeOffset重复上述处理,根据实际加载的内存地址修正后,将得到的值覆盖到同一位置。
基址重定位表地址位于PE头的DataDirectory数组的第六个元素(数组索引为5):IMAGE_OPTIONAL_HEADER32.DataDirectory[5].VirtualAddress的值就是IMAGE_BASE_RELOCATION结构体数组的起始地址
1、根据重定位表,查找程序中需要重定位的位置RVA = VirtualAddress + (TypeOffset & 0x0FFF)
2、读取硬编码地址的值后,减去ImageBase值(VR –> RVA)
3、加上实际加载地址(RVA –> VA)
4、将得到的值覆盖到重定位的位置上
5、对一个IMAGE_BASE_RELOCATION结构体的所有TypeOffset重复步骤1~4,直至TypeOffset的值为0
6、对PE文件中的所有IMAGE_BASE_RELOCATION结构体(衔接上一个结构体的TypeOffset的后面),重复步骤1~5,直至结构体为NULL(即最后一个结构体的TypeOffset为0时,后面连续有8个字节为0)
PEView.exe是一个分析PE文件的应用程序
Upack(Ultimate PE压缩器)是一款PE文件的运行时压缩器
Stud_PE是一款分析PE文件的应用软件:http://www.cgsoftlabs.ro
可以用来捕获并显示系统中运行的进程输出的所有调试字符串:https://technet.microsoft.com/en-us
一款功能强大的PE文件编辑工具,具有进程内存转储、PE文件头编辑、PE重建等功能,并wps的电脑版下载网址怎么找(wps下载电脑版怎么安装到桌面上)且支持插件,带有插件编写示例:http://petools.org
提供多样化的功能,并且支持PE32+文件格式:https://ntcore.com,还提供PE编辑器、PE重建、RVA<–>RAW转换器、反汇编等综合功能。