什么是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结构解析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论