e_lfanew字段是真正PE文件头(NT头)的相对偏移,其指出真正PE头的文件偏移位置,它占用4个字节,位于文件开始偏移3Ch字节中。 3、NT头结构信息-PE可选头 (IMAGE_OPTIONAL_HEADER)是一个可选的结构,但实际上IMAGE_FILE_HEADER结构不足以定义PE文件属性,因此可选映像头中定义了更多的数据, 完全不必考虑两个结构区别在哪里,两者连起来就是一个完整的“PE文件头结构”。 (3)MinorLinkerVersion:链接程序的次版本号。 PE文件头指出了这两个值,他们可以不同。 3.恶意代码或者壳进场会在区段上下文章,所以掌握区段的一些操作知识对分析恶意代码以及脱壳会有很大的帮助。
该技术能够劫持原始PE文件的入口地址,在PE程序运行之前执行ShellCode反弹,执行后挂入后台并继续运行原始程序,实现了一种隐蔽的Shell访问。而我把这种技术叫做字节注入反弹。 // 将ShellCode写出到PE程序的特定位置 // 参数1: 指定PE路径 参数2: 指定文件中的偏移(十进制) 参数3: 指定ShellCode文件 void WritePEShellCode( 文件中我们需要手动分析寻找空余块,并在注入成功后还需要自行修正PE文件内的入口地址等,这种方式适合于对PE结构非常熟悉的人可以,但也要花费一些精力去寻找分析,如下代码则是实现了自动化注入功能,该代码中FindSpace memcpy((char *)dwAddr, shellcode, strlen(shellcode) + 3); dwAddr = dwAddr - (DWORD)(BYTE *)lpBase 当我们对特定的程序插入Shell后,则还需要对该程序增加一个标志,在PE结构中有许多地方可以写入这个标志,例如DOS头部存在一个e_cblp变量,通过向该变量写入一个标志,当需要判断是否被感染时读取此处并检查是否存在特定值即可
该技术能够劫持原始PE文件的入口地址,在PE程序运行之前执行ShellCode反弹,执行后挂入后台并继续运行原始程序,实现了一种隐蔽的Shell访问。而我把这种技术叫做字节注入反弹。 // 将ShellCode写出到PE程序的特定位置// 参数1: 指定PE路径 参数2: 指定文件中的偏移(十进制) 参数3: 指定ShellCode文件void WritePEShellCode(const 文件中我们需要手动分析寻找空余块,并在注入成功后还需要自行修正PE文件内的入口地址等,这种方式适合于对PE结构非常熟悉的人可以,但也要花费一些精力去寻找分析,如下代码则是实现了自动化注入功能,该代码中FindSpace memcpy((char *)dwAddr, shellcode, strlen(shellcode) + 3); dwAddr = dwAddr - (DWORD)(BYTE *)lpBase; 图片当我们对特定的程序插入Shell后,则还需要对该程序增加一个标志,在PE结构中有许多地方可以写入这个标志,例如DOS头部存在一个e_cblp变量,通过向该变量写入一个标志,当需要判断是否被感染时读取此处并检查是否存在特定值即可
,病毒分析,外挂技术等,在PE文件中我们最需要关注,PE结构,导入表,导出表,重定位表,下面将具体介绍PE的关键结构,并使用C语言编程获取到这些结构数据. 我们需要编程实现读取PE结构,在读取PE文件中的数据的前提下,我们先来打开文件,然后才能读取。 PE文件的重要结构,接下来将通过编程读取出PE文件的开头相关数据。 第4个函数是以序号导入的,与其对应的IMAGE_THUNK_DATA结构最高位等于1,和函数的序号0010h组合起来的数值就是80000010h,其余的3个函数采用的是以函数名方式导入,所以IMAGE_THUNK_DATA 结构的数值是一个RVA,分别指向3个IMAGE_IMPORT_BY_NAME结构,每个结构的第一个字段是函数的序号,后面就是函数的字符串名称了,一切就这么简单!
,病毒分析,外挂技术等,在PE文件中我们最需要关注,PE结构,导入表,导出表,重定位表,下面将具体介绍PE的关键结构,并使用C语言编程获取到这些结构数据. ,PE文件头被放置在节和节表的前面,上面介绍的是真正的PE文件,为了兼容以前的DOS系统,所以保留了DOS的文件格式,接下来将依次介绍这几种数据结构.我们需要编程实现读取PE结构,在读取PE文件中的数据的前提下 PE文件的重要结构,接下来将通过编程读取出PE文件的开头相关数据。 第4个函数是以序号导入的,与其对应的IMAGE_THUNK_DATA结构最高位等于1,和函数的序号0010h组合起来的数值就是80000010h,其余的3个函数采用的是以函数名方式导入,所以IMAGE_THUNK_DATA 结构的数值是一个RVA,分别指向3个IMAGE_IMPORT_BY_NAME结构,每个结构的第一个字段是函数的序号,后面就是函数的字符串名称了,一切就这么简单!
0, 0, 0); HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0); // 找到PE NumberOfSections; char Code[] = { "\x60" "\xb8\x00\x00\x00\x00" "\x80\x30\x88" "\x40" "\x3d 0, 0, 0); HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0); // 定位PE
PE结构-DOS头,本部分为参照吾爱破解论坛lyl610abc师傅PE文件笔记所整理的学习笔记。 DOS部首 DOS部首结构 其结构分为两部分: DOS ‘MZ’ HEADER 其在c中定义的结构体为_IMAGE_DOS_HEADER DOS sub DOS MZ头 DOS MZ头在C语言中所定义的结构体为 是为了往下兼容在低版本DOS系统上运行,所以该部分主要用于DOS系统 目前在32/64位Windows操作系统中还有效的成员只有两个 第一个成员:e_magic (WORD 2字节) 用于识别文件是否为PE 格式文件 值固定为4d 5a (MZ) 最后一个成员: e_lfanew (LONG 4字节) 存储PE头首地址 位于0x3c位置 DOS部首中的其他值已经在现阶段Windows系统中弃用,下面通过将对应值置换为
PAGE_READWRITE, 0, 0, 0); HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0); // 找到PE NumberOfSections; char Code[] = { "\x60" "\xb8\x00\x00\x00\x00" "\x80\x30\x88" "\x40" "\x3d PAGE_READWRITE, 0, 0, 0); HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0); // 定位PE
数据目录表的第二个成员指向导入表,该指针在PE开头位置向下偏移0x80h处,此处PE开始位置为0xF0h也就是说导入表偏移地址应该在0xf0+0x80h=170h如下图中,导入表相对偏移为0x21d4h 如上图就是导入表中的IID数组,每个IID结构包含一个装入DLL的描述信息,现在有三个导入DLL文件,则第四个是一个全部填充为0的结构,标志着IID数组的结束,每一个结构有五个四字节构成,该结构体定义如下所示 上方提到的两个字段OrignalFirstThunk和FirstThunk都可以指向导入结构,在实际装入中,当程序中的OrignalFirstThunk值为0时,则就要看FirstThunk里面的数据, 其地址为22C0,使用该值减去1000h 得到 12c0h,在偏移为12c0h处保存的就是一个IMAGE_THUNK_DATA32数组,他存储的内容就是指向 IMAGE_IMPORT_BY_NAME 结构的地址 BuildIat [脱壳前文件] [脱壳后文件] \n\n\n"); } int main(int argc, char * argv[]) { Banner(); if (argc == 3)
数据目录表的第二个成员指向导入表,该指针在PE开头位置向下偏移0x80h处,此处PE开始位置为0xF0h也就是说导入表偏移地址应该在0xf0+0x80h=170h如下图中,导入表相对偏移为0x21d4h 图片如上图就是导入表中的IID数组,每个IID结构包含一个装入DLL的描述信息,现在有三个导入DLL文件,则第四个是一个全部填充为0的结构,标志着IID数组的结束,每一个结构有五个四字节构成,该结构体定义如下所示 图片上方提到的两个字段OrignalFirstThunk和FirstThunk都可以指向导入结构,在实际装入中,当程序中的OrignalFirstThunk值为0时,则就要看FirstThunk里面的数据 其地址为22C0,使用该值减去1000h 得到 12c0h,在偏移为12c0h处保存的就是一个IMAGE_THUNK_DATA32数组,他存储的内容就是指向 IMAGE_IMPORT_BY_NAME 结构的地址 Usage: BuildIat [脱壳前文件] [脱壳后文件] \n\n\n");}int main(int argc, char * argv[]){ Banner(); if (argc == 3)
Windows系统使用PE(Portable Executable)文件格式来存储可执行程序,其中包括重定位信息。当程序被加载到内存中时,系统会解析这些重定位信息,并将程序中的各种内存地址进行重定位。 图片重定位表也是分页排列的,每一页大小都是1000字节,通过使用FixRelocPage命令即可查询到当前程序中的重定位块信息,并以第一个为例,查询一下起始地址RVA为1000的页上,有哪些重定位结构, rva - SectionTables[i].VirtualAddress + SectionTables[i].PointerToRawData; } } return -1;}// 打开PE = IMAGE_DOS_SIGNATURE) return false; // 获取 NT 头并判断是不是一个有效的PE文件 NtHeader = (PIMAGE_NT_HEADERS)(FileBase auto Offset = (TypeOffset*)(Reloc + 1); // 3.3 计算重定位项的个数 // Reloc->SizeOfBlock 保存的是整个重定位块的大小 结构体
了解其格式对恶意分析及使用高级的攻击手法有很大的帮助,很多高级的攻击手段都需要对 PE、PEB 有详细的了解。 二、PE 结构 1. 3. 获取到了 Section Headers 数组的大小为 3,那么其占用空间就是 sizeof(IMAGE_SECTION_HEADER) * 3 这么大。 PE 结构总结 整个程序从 offset=0 处即 DOS Header 到程序的最后一处 EOF 的所有块状区域经 File Alignment 对齐之后都是紧密贴合的没有任何空隙。 首先,编写一个函数读取 PE 文件: 读取到 PE 文件内容并保存到 pe_content 指针中,然后直接转换为 PIMAGE_DOS_HEADER 结构就是 DOS Header 了: 通过 DOS
Windows系统使用PE(Portable Executable)文件格式来存储可执行程序,其中包括重定位信息。 重定位表也是分页排列的,每一页大小都是1000字节,通过使用FixRelocPage命令即可查询到当前程序中的重定位块信息,并以第一个为例,查询一下起始地址RVA为1000的页上,有哪些重定位结构,如下图所示 SectionTables[i].VirtualAddress + SectionTables[i].PointerToRawData; } } return -1; } // 打开PE = IMAGE_DOS_SIGNATURE) return false; // 获取 NT 头并判断是不是一个有效的PE文件 NtHeader = (PIMAGE_NT_HEADERS auto Offset = (TypeOffset*)(Reloc + 1); // 3.3 计算重定位项的个数 // Reloc->SizeOfBlock 保存的是整个重定位块的大小 结构体
ELF文件格式,是一个开放的可执行文件和链接文件格式,其主要工作在Linux系统上,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件,ELF文件格式类似于PE格式,但比起PE结构来ELF 结构显得更加的简单,Linux文件结构相比于Windows结构来说简单一些.读取ELF头: 首先需要先来编译一个简单的ELF文件,然后将文件编译并连接. lyshark00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|00000010 02 00 3e ; /* 一个字节数组用来确认文件是否是一个ELF文件 */ Elf64_Half e_type; /* 描述文件是,可执行文件elf=2,重定位so=3 printf("\n"); } return 0;}编译并运行即可读取出文件头部的前16个字节的字节数组,我们最需要关注的就是开头前4个字节,其标志着PE
PE文件中每个资源都会被分配对应的唯一资源ID,以便在运行时能够方便地查找和调用它们。 PE文件中的资源都被组织成一个树形结构,其中最顶层为根节点(Root),下一级为资源类型(Type),再下一级为资源名称(Name),最终是实际的资源内容。 PIMAGE_RESOURCE_DIRECTORY是Windows PE可执行文件中的一个结构类型,用于描述资源(Resource)的树形结构,其中包括了每个资源的类型(Type)、名称(Name)和语言 (Language),以及指向下一级PE资源目录的地址和相关信息等。 资源的目录结构,每个资源目录包括以下字段: Characteristics:指定该目录的属性,如是否允许命名、是否允许ID等; TimeDateStamp:指定该目录的时间戳; MajorVersion
PE文件在被装入内存后JMP跳转后面的地址才会被操作系统确定并填充到指定的位置上,那么在程序没有被PE装载器加载之前0x00D22000地址处的内容是什么呢,我们使用上面的PE解析器对节表进行解析观察. 第4个函数是以序号导入的,与其对应的IMAGE_THUNK_DATA结构最高位等于1,和函数的序号0010h组合起来的数值就是80000010h,其余的3个函数采用的是以函数名方式导入,所以IMAGE_THUNK_DATA 结构的数值是一个RVA,分别指向3个IMAGE_IMPORT_BY_NAME结构,每个结构的第一个字段是函数的序号,后面就是函数的字符串名称了,一切就这么简单! 这是因为PE装载器会将其中一个结构修改为函数的地址jmp dword ptr[xxxx]其中的xxxx就是由FirstThunk字段指向的那个数组中的一员。 // 3.
节表(Section Table)是Windows PE/COFF格式的可执行文件中一个非常重要的数据结构,它记录了各个代码段、数据段、资源段、重定向表等在文件中的位置和大小信息,是操作系统加载文件时根据节表来进行各个段的映射和初始化的重要依据 在执行PE文件的时候,Windows 并不在一开始就将整个文件读入内存,PE装载器在装载的时候仅仅建立好虚拟地址和PE文件之间的映射关系,只有真正执行到某个内存页中的指令或者访问页中的数据时,这个页面才会被从磁盘提交到内存中 节区的偏移: 节的起始地址在磁盘文件中是按照IMAGE_OPTIONAL_HEADER结构的FileAhgnment字段的值对齐的,而被加载到内存中时是按照同一结构中的SectionAlignment字段的值对齐的 一般来说,当一个PE文件被编译生成时则默认会存在.text,.data等基本节表,而每一个节表都是由一个IMAGE_SECTION_HEADER结构排列而成,每个结构都用来描述一个节,节表总被存放在紧接在 PE文件头的地方,也即是从PE文件头开始偏移为00f8h的位置,针对每一个节中的定义可查看节表结构体的定义; typedef struct _IMAGE_SECTION_HEADER { BYTE
ELF文件格式,是一个开放的可执行文件和链接文件格式,其主要工作在Linux系统上,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件,ELF文件格式类似于PE格式,但比起PE结构来ELF 结构显得更加的简单,Linux文件结构相比于Windows结构来说简单一些. /* 一个字节数组用来确认文件是否是一个ELF文件 */ Elf64_Half e_type; /* 描述文件是,可执行文件elf=2,重定位so=3 printf("\n"); } return 0; } 编译并运行即可读取出文件头部的前16个字节的字节数组,我们最需要关注的就是开头前4个字节,其标志着PE /elf lyshark 文件类型: 2 运行平台: 3e 入口虚拟RVA: 0x400430 程序头文件偏移: 64(bytes) 节头表文件偏移: 6464(bytes) ELF文件头大小: 64
PE文件中每个资源都会被分配对应的唯一资源ID,以便在运行时能够方便地查找和调用它们。 PE文件中的资源都被组织成一个树形结构,其中最顶层为根节点(Root),下一级为资源类型(Type),再下一级为资源名称(Name),最终是实际的资源内容。 PIMAGE_RESOURCE_DIRECTORY是Windows PE可执行文件中的一个结构类型,用于描述资源(Resource)的树形结构,其中包括了每个资源的类型(Type)、名称(Name)和语言 (Language),以及指向下一级PE资源目录的地址和相关信息等。 资源的目录结构,每个资源目录包括以下字段:Characteristics:指定该目录的属性,如是否允许命名、是否允许ID等;TimeDateStamp:指定该目录的时间戳;MajorVersion /
PE结构是Windows系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为 是PE文件格式的一部分,它包含了PE头和可选头的信息,用于描述PE文件的结构和属性。 2.2 DOS文件头详细解析 DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为64字节(0x40)。 PE结构所使用的的结构定义略有不同,代码中已经对其进行了区分。 是PE文件格式的一部分,它包含了PE头和可选头的信息,用于描述PE文件的结构和属性。