【逆向基础】PE结构解析

admin 2023年6月29日23:45:45评论11 views字数 19608阅读65分21秒阅读模式

什么是PE结构:

在二进制文件下如果Windows操作系统要识别成可执行文件,那么就必须要有PE固定格式。

内存与值的查看方式:

代码中的地址或者是16进制数是按照左往右看,地址对应的值是右往左看:

地址:0x12345678
值:78 56 34 12

PE头组成部分:

#include <windows.h>
_IMAGE_DOS_HEADER;
_IMAGE_NT_HEADERS;
_IMAGE_SECTION_HEADER;


DOS头

WORD e_magic;  // Magic number 标志位(MZ 4d5a 只占2字节)

LONG e_lfanew;  // File address of new exe header 偏移位置(一般接下来就是文件签名)

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;


NT头

typedef struct _IMAGE_NT_HEADERS {
   DWORD Signature;
   IMAGE_FILE_HEADER FileHeader;
   IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

DWORD Signature; 标志(50450000 dowrd占8字节)

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;

WORD Machine; CPU识别

#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

WORD NumberOfSections; 节数(最大支持96)

区节段:

一个程序把相关的功能或者抽象的一些东西,分门别类地放在统一的地方。

常见的节:

.text 代码段
.data 数据段
.tls 变量
.reloc dll导出函数地址方便别的exe快速找到函数地址的一个修正地址表
.rsrc 资源段(图标等)

DWORD TimeDateStamp; 时间戳(创建时间 以utc做标准)

DWORD PointerToSymbolTable; 标志表指针(被废除 不用管)

DWORD NumberOfSymbols; 标志表数量(被废除 不用管)

WORD SizeOfOptionalHeader; 可选头大小(对于32位文件来说是224,对于64位文件来说是240)

WORD Characteristics; 文件特征

文件特征详细表:

#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.


可选NT头

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; 文件类型标识

0x10B表明这是一个32位镜像文件。
0x107表明这是一个ROM镜像。
0x20B表明这是一个64位镜像文件。

BYTE MajorLinkerVersion; 链接器主版本号(BYTE 2位16)

BYTE MinorLinkerVersion; 链接器副版本号

DWORD SizeOfCode; .text总大小(实际的有功能的代码占的大小)

DWORD SizeOfInitializedData; .data总大小(动态变量的)

DWORD SizeOfUninitializedData; .bss总大小(类的初始化的一些静态变量的)

bss:BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。

下面三个都是以偏移量表示:

#include <windows.h>
_IMAGE_DOS_HEADER;
_IMAGE_NT_HEADERS;
//nt头下面有2个,一个文件头一个可选头
_IMAGE_SECTION_HEADER;
//text
//data
void test() {
   int b = 1;//写的代码的第一行的位置baseofcode
}
void main() {
   int a = 1;//运行起来这个地方就是entrypoint
   test();
}

DWORD AddressOfEntryPoint; 程序的虚拟入口地址

virtual address:加载进去的第一个二进制的地址
reversc virtual address:加载进去的第一个二进制的地址-功能的地址

DWORD BaseOfCode; 代码基址

DWORD BaseOfData; 数据基址

DWORD ImageBase; 入口点,当加载进内存时镜像的第1个字节的首选地址,它必须是64K的倍数,DLL默认是10000000H,Windows CE 的EXE默认是00010000H,Windows 系列的EXE默认是00400000H。

DWORD SectionAlignment; 当加载进内存时节的对齐值(以字节计--内存)

DWORD FileAlignment; 用来对齐镜像文件的节中的原始数据的对齐因子(以字节计--磁盘)

对齐:

一个pe文件无论在磁盘和内存中存放都会进行对齐,但他们的对齐值会不相同。PE 文件头里边的 FileAligment 定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始存放。而区块的实际代码或数据的大小不一定刚好是这么多,所以在多余的地方一般以 00h 来填充,这就是区块间的间隙。PE 文件头里边的 SectionAligment 定义了内存中区块的对齐值。PE 文件被映射到内存中时,区块总是从一个页边界开始。

WORD MajorOperatingSystemVersion;

WORD MinorOperatingSystemVersion;

WORD MajorImageVersion;

WORD MinorImageVersion;

WORD MajorSubsystemVersion;

WORD MinorSubsystemVersion;

以上为6个版本号

DWORD Win32VersionValue; win32版本(必须为0)

DWORD SizeOfImage; 文件的总大小

DWORD SizeOfHeaders; 所有PE头的总大小

DWORD CheckSum; 校验和(用来检查文件是否被修改)

WORD Subsystem; 子系统

子系统表:

描述
0 未知子系统
1 设备驱动程序和Native Windows进程
2 Windows图形用户界面(GUI)子系统(一般程序)
3 Windows字符模式(CUI)子系统(从命令提示符启动的)
7 Posix字符模式子系统
9 Windows CE
10 可扩展固件接口(EFI)应用程序
11 带引导服务的EFI驱动程序
12 带运行时服务的EFI驱动程序
13 EFI ROM镜像
14 XBOX

WORD DllCharacteristics; dll特征

//      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.
//aslr的关闭
#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.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     0x8000

DWORD SizeOfStackReserve; 栈保留大小

DWORD SizeOfStackCommit; 栈申请大小

DWORD SizeOfHeapReserve; 堆保留大小

DWORD SizeOfHeapCommit; 堆申请大小

DWORD LoaderFlags; 标志位(必须为0)

DWORD NumberOfRvaAndSizes; 数据目录(十进制16 16进制0x10)

导入导出信息的总览结构:

struct IMAGE_DATA_DIRECTORY_ARRAY DataDirArray; //指向一个指针,内容指向描述表
struct IMAGE_IMPORT_DESCRIPTOR ImportDescriptor[0] //dll和函数的地址


节头

typedef struct _IMAGE_SECTION_HEADER {
   BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
   union {
           DWORD   PhysicalAddress;
           DWORD   VirtualSize;
  } Misc;
   DWORD   VirtualAddress;
   DWORD   SizeOfRawData;
   DWORD   PointerToRawData;
   DWORD   PointerToRelocations;
   DWORD   PointerToLinenumbers;
   WORD    NumberOfRelocations;
   WORD    NumberOfLinenumbers;
   DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; 本节的名字

DWORD PhysicalAddress; 本节的物理地址

DWORD VirtualSize; 本节的实际大小

DWORD VirtualAddress; 本节的RVA

DWORD SizeOfRawData; 本节在磁盘中的大小

DWORD PointerToRawData; 本节在磁盘中的偏移

DWORD PointerToRelocations; exe文件无意义

DWORD PointerToLinenumbers; 行号表的位置

WORD NumberOfRelocations; 重定位数量

WORD NumberOfLinenumbers; 行号表数量

DWORD Characteristics; 特征

特征详细表:

//
// Section characteristics.
//
//     IMAGE_SCN_TYPE_REG                   0x00000000 // Reserved.
//     IMAGE_SCN_TYPE_DSECT                 0x00000001 // Reserved.
//     IMAGE_SCN_TYPE_NOLOAD               0x00000002 // Reserved.
//     IMAGE_SCN_TYPE_GROUP                 0x00000004 // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD               0x00000008  // Reserved.
//     IMAGE_SCN_TYPE_COPY                 0x00000010 // Reserved.
#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER                 0x00000100  // Reserved.
#define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
//     IMAGE_SCN_TYPE_OVER                 0x00000400 // Reserved.
#define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.
//                                           0x00002000 // Reserved.
//     IMAGE_SCN_MEM_PROTECTED - Obsolete   0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC         0x00004000  // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL                     0x00008000  // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA               0x00008000
//     IMAGE_SCN_MEM_SYSHEAP - Obsolete   0x00010000
#define IMAGE_SCN_MEM_PURGEABLE             0x00020000
#define IMAGE_SCN_MEM_16BIT                 0x00020000
#define IMAGE_SCN_MEM_LOCKED                 0x00040000
#define IMAGE_SCN_MEM_PRELOAD               0x00080000
#define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
#define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
#define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
#define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
#define IMAGE_SCN_ALIGN_16BYTES             0x00500000  // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES             0x00600000  //
#define IMAGE_SCN_ALIGN_64BYTES             0x00700000  //
#define IMAGE_SCN_ALIGN_128BYTES             0x00800000  //
#define IMAGE_SCN_ALIGN_256BYTES             0x00900000  //
#define IMAGE_SCN_ALIGN_512BYTES             0x00A00000  //
#define IMAGE_SCN_ALIGN_1024BYTES           0x00B00000  //
#define IMAGE_SCN_ALIGN_2048BYTES           0x00C00000  //
#define IMAGE_SCN_ALIGN_4096BYTES           0x00D00000  //
#define IMAGE_SCN_ALIGN_8192BYTES           0x00E00000  //
// Unused                                   0x00F00000
#define IMAGE_SCN_ALIGN_MASK                 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL           0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE           0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED             0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE               0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                 0x80000000  // Section is writeable.
含义
IMAGE_SCN_CNT_CODE 0x00000020 包含代码,常与 0x10000000一起设置。
IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 该区块包含已初始化的数据。
IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 该区块包含未初始化的数据。
IMAGE_SCN_MEM_DISCARDABLE 0x02000000 该区块可被丢弃,因为当它一旦被装入后,进程就不在需要它了,典型的如重定位区块(如.reloc)。
IMAGE_SCN_MEM_SHARED 0x10000000 该区块为共享区块。
IMAGE_SCN_MEM_EXECUTE 0x20000000 该区块可以执行。通常当0x00000020被设置时候,该标志也被设置。
IMAGE_SCN_MEM_READ 0x40000000 该区块可读,可执行文件中的区块总是设置该标志。
IMAGE_SCN_MEM_WRITE 0x80000000 该区块可写。该标志没有设置,装载程序就会将内存映像也标记为可读或可执行


PE在内存中的加载过程

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <winnt.h>
#pragma warning(disable:4996)
void* load_PE(char* PE_data);
void fix_iat(char*, IMAGE_NT_HEADERS*);
void fix_base_reloc(char* p_image_base, IMAGE_NT_HEADERS* p_NT_headers);
int main(int argc, char const* argv[]) {
if (argc < 2) {
printf("missing path argumentn");
return 1;
}
FILE* exe_file = fopen(argv[1], "rb");
if (!exe_file) {
printf("error opening filen");
return 1;
}
// Get file size : put pointer at the end
fseek(exe_file, 0L, SEEK_END);
// and read its position
long int file_size = ftell(exe_file);
// put the pointer back at the beginning
fseek(exe_file, 0L, SEEK_SET);
// allocate memory and read the whole file
char* exe_file_data = (char*)malloc(file_size + 1);
// read whole file
size_t n_read = fread(exe_file_data, 1, file_size, exe_file);
if (n_read != file_size) {
printf("reading error (%d)n", n_read);
return 1;
}
// load the PE in memory
printf("[+] Loading PE filen");
//pe里面有个导入表
//字典 里面不仅要有内容 还要有内容对应的名称
void* entry = load_PE(exe_file_data);
if (entry != NULL) {
// call its entrypoint
((void (*)(void))entry)();
}
return 0;
}
void* load_PE(char* PE_data) {
IMAGE_DOS_HEADER* p_DOS_header = (IMAGE_DOS_HEADER*)PE_data;
IMAGE_NT_HEADERS* p_NT_headers = (IMAGE_NT_HEADERS*)(PE_data + p_DOS_header->e_lfanew);
// extract information from PE header
DWORD size_of_image = p_NT_headers->OptionalHeader.SizeOfImage;
DWORD entry_point_RVA = p_NT_headers->OptionalHeader.AddressOfEntryPoint;
DWORD size_of_headers = p_NT_headers->OptionalHeader.SizeOfHeaders;
// allocate memory
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
char* p_image_base = (char*)VirtualAlloc(NULL, size_of_image, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p_image_base == NULL) {
return NULL;
}
// copy PE headers in memory
memcpy(p_image_base, PE_data, size_of_headers);
// Section headers starts right after the IMAGE_NT_HEADERS struct, so we do some pointer arithmetic-fu here.
IMAGE_SECTION_HEADER* sections = (IMAGE_SECTION_HEADER*)(p_NT_headers + 1);
for (int i = 0; i < p_NT_headers->FileHeader.NumberOfSections; i++) {
// calculate the VA we need to copy the content, from the RVA
// section[i].VirtualAddress is a RVA, mind it
char* dest = p_image_base + sections[i].VirtualAddress;
// check if there is Raw data to copy
if (sections[i].SizeOfRawData > 0) {
// We copy SizeOfRaw data bytes, from the offset PointerToRawData in the file
memcpy(dest, PE_data + sections[i].PointerToRawData, sections[i].SizeOfRawData);
}
else {
memset(dest, 0, sections[i].Misc.VirtualSize);
}
}
fix_iat(p_image_base, p_NT_headers);
fix_base_reloc(p_image_base, p_NT_headers);
// Set permission for the PE header to read only
DWORD oldProtect;
VirtualProtect(p_image_base, p_NT_headers->OptionalHeader.SizeOfHeaders, PAGE_READONLY, &oldProtect);
for (int i = 0; i < p_NT_headers->FileHeader.NumberOfSections; ++i) {
char* dest = p_image_base + sections[i].VirtualAddress;
DWORD s_perm = sections[i].Characteristics;
DWORD v_perm = 0; // flags are not the same between virtal protect and the section header
if (s_perm & IMAGE_SCN_MEM_EXECUTE) {
v_perm = (s_perm & IMAGE_SCN_MEM_WRITE) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
}
else {
v_perm = (s_perm & IMAGE_SCN_MEM_WRITE) ? PAGE_READWRITE : PAGE_READONLY;
}
VirtualProtect(dest, sections[i].Misc.VirtualSize, v_perm, &oldProtect);
}
return (void*)(p_image_base + entry_point_RVA);
}
void fix_iat(char* p_image_base, IMAGE_NT_HEADERS* p_NT_headers) {
IMAGE_DATA_DIRECTORY* data_directory = p_NT_headers->OptionalHeader.DataDirectory;
// load the address of the import descriptors array
IMAGE_IMPORT_DESCRIPTOR* import_descriptors =
(IMAGE_IMPORT_DESCRIPTOR*)(p_image_base + data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// this array is null terminated
for (int i = 0; import_descriptors[i].OriginalFirstThunk != 0; ++i) {
// Get the name of the dll, and import it
char* module_name = p_image_base + import_descriptors[i].Name;
HMODULE import_module = LoadLibraryA(module_name);
if (import_module == NULL) {
printf("import module is null");
abort();
}
// the lookup table points to function names or ordinals => it is the IDT
IMAGE_THUNK_DATA* lookup_table = (IMAGE_THUNK_DATA*)(p_image_base + import_descriptors[i].OriginalFirstThunk);
// the address table is a copy of the lookup table at first
// but we put the addresses of the loaded function inside => that's the IAT
IMAGE_THUNK_DATA* address_table = (IMAGE_THUNK_DATA*)(p_image_base + import_descriptors[i].FirstThunk);
// null terminated array, again
for (int i = 0; lookup_table[i].u1.AddressOfData != 0; ++i) {
void* function_handle = NULL;
// Check the lookup table for the adresse of the function name to import
DWORD lookup_addr = lookup_table[i].u1.AddressOfData;
if ((lookup_addr & IMAGE_ORDINAL_FLAG) == 0) { // if first bit is not 1
// import by name : get the IMAGE_IMPORT_BY_NAME struct
IMAGE_IMPORT_BY_NAME* image_import = (IMAGE_IMPORT_BY_NAME*)(p_image_base + lookup_addr);
// this struct points to the ASCII function name
char* funct_name = (char*)&(image_import->Name);
// get that function address from it's module and name
function_handle = (void*)GetProcAddress(import_module, funct_name);
}
else {
// import by ordinal, directly
function_handle = (void*)GetProcAddress(import_module, (LPSTR)lookup_addr);
}
if (function_handle == NULL) {
printf("function handle is null");
abort();
}
// change the IAT, and put the function address inside.
address_table[i].u1.Function = (DWORD)function_handle;
}
}
}
void fix_base_reloc(char* p_image_base, IMAGE_NT_HEADERS* p_NT_headers) {
IMAGE_DATA_DIRECTORY* data_directory = p_NT_headers->OptionalHeader.DataDirectory;
// this is how much we shifted the ImageBase
DWORD delta_VA_reloc = ((DWORD)p_image_base) - p_NT_headers->OptionalHeader.ImageBase;
// if there is a relocation table, and we actually shitfted the ImageBase
if (data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0 && delta_VA_reloc != 0) {
// calculate the relocation table address
IMAGE_BASE_RELOCATION* p_reloc =
(IMAGE_BASE_RELOCATION*)(p_image_base + data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
// once again, a null terminated array
while (p_reloc->VirtualAddress != 0) {
// how any relocation in this block
// ie the total size, minus the size of the "header", divided by 2 (those are words, so 2 bytes for each)
DWORD size = (p_reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
// the first relocation element in the block, right after the header (using pointer arithmetic again)
WORD* fixups = (WORD*)(p_reloc + 1);
for (int i = 0; i < size; ++i) {
// type is the first 4 bits of the relocation word
int type = fixups[i] >> 12;
// offset is the last 12 bits
int offset = fixups[i] & 0x0fff;
// this is the address we are going to change
DWORD* change_addr = (DWORD*)(p_image_base + p_reloc->VirtualAddress + offset);
// there is only one type used that needs to make a change
switch (type) {
case IMAGE_REL_BASED_HIGHLOW:
*change_addr += delta_VA_reloc;
break;
default:
break;
}
}
// switch to the next relocation block, based on the size
p_reloc = (IMAGE_BASE_RELOCATION*)(((DWORD)p_reloc) + p_reloc->SizeOfBlock);
}
}
}



原文始发于微信公众号(ZackSecurity):【逆向基础】PE结构解析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月29日23:45:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【逆向基础】PE结构解析https://cn-sec.com/archives/1843047.html

发表评论

匿名网友 填写信息