tft每日頭條

 > 科技

 > 如何删除windows的pe選擇

如何删除windows的pe選擇

科技 更新时间:2025-02-24 08:47:18
起因

五一(2020年,遷移文章)假期沒有回家,是因為在四月份已經向公司提了離職.估計五月中旬應該就能辦理離職手續,在公司呆了6年多,合同由3(2次)年換成無固定期限勞動合同.因為家庭原因,最終還是要回去的.不知不覺間北漂了8年,在公司工作期間經曆我人生的大事(結婚和生子).

關于PE相關的内容,在前面也寫過 <<學習PE文件結構>> 和 <<在解析PE遇到的問題>>,這裡也不多的介紹了.

在看正文之前,先看看圖,對下邊具體要做的事情,有一個大概的認知.

關系圖(省略節表部分)

如何删除windows的pe選擇(PE數據目錄表和導出表解析)1

從PE中解析導出表,根據導出表的地址,進行獲取函數調用的地址

數據目錄表

在 <<學習PE文件結構>> 這邊博文中,已經對可選PE頭進行了解析. 在IMAGE_OPTIONAL_HEADER結構體中這個字段DataDirectory,就是我們通常所說的數據目錄表.重新看看這個可選PE頭結構.

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 //數據目标表的長度 //可選PE頭 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 //數據目錄表結構 typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //在内存中的相對地址 DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

先将數據目錄表信息打印出來,在用PETool進行對比,看看是否是有問題.

//打印出 數據目錄表 信息 void print_data_dir(char* base) { PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)base dos_header->e_lfanew); PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header 4); //4為nt頭中Signature DWORD PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header IMAGE_SIZEOF_FILE_HEADER); for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i ) { IMAGE_DATA_DIRECTORY data_dir = optional_header->DataDirectory[i]; printf("%d data:x size:%x\n", i, data_dir.VirtualAddress, data_dir.Size); } } int main(int argc, char* argv[]) { char* filename = "DllExportTable.dll"; //以動态庫為例 int size = file_size(filename); if (size > 0) { char* buf; file_to_memory(filename, size, &buf); print_data_dir(buf); } else { printf("file not found!\n"); } return 0; }

如何删除windows的pe選擇(PE數據目錄表和導出表解析)2

PE中的數據目錄表

導出表

1

導入表

2

資源表

3

異常表

4

安全證書表

5

重定位表

6

調試信息表

7

版權所有表

8

全局指針表

9

TLS(線程本地存儲表)

10

加載配置表

11

綁定導入表

12

IAT(導入地址表)

13

延遲導入表

14

COM表

15

保留(這個暫時沒用)

正常我們隻需要關注重點表: 導出表/導入表/重定位/IAT

導出表

從數據目錄表中,知道了第一個表是導出表,并且導出表的VirtualAddress.這個時候要了解這兩個RVA和FOA知識點.

RVA(相對虛拟地址):VirtualAddress就是RVA.

FOA(文件偏移地址)

如果解析PE時候,如果不進行内存拉伸,VirtualAddress就需要用RVA轉FOA的轉換,這個轉換是拿導出表的VirtualAddress在節表中查找每個節的VirtualAddress的區域中.然後根據節中的PointerToRelocation(文件中的偏移)

//相對虛拟地址轉換為文件偏移地址 int rva_to_foa(char* base, unsigned long* va) { unsigned long rva = *va; PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)base dos_header->e_lfanew); PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header 4); PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header IMAGE_SIZEOF_FILE_HEADER); //1. 獲取節表 PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header file_header->SizeOfOptionalHeader); DWORD alignment = optional_header->SectionAlignment; //内存對齊大小 4096 //2. 獲取節表的個數 int sections = nt_header->FileHeader.NumberOfSections; int offset = -1; for (int i = 0; i < sections; i ) { PIMAGE_SECTION_HEADER psection = section_headersection_header i; //3. 獲取節的RVA DWORD section_start = psection->VirtualAddress; if (rva < section_start) { offset = rva; return offset; } int block_count = psection->SizeOfRawData / alignment; block_count = psection->SizeOfRawData % alignment ? 1 : 0; //4. 判斷導出表的相對虛拟地址rva 是否在這個節中 if (rva >= section_start && rva < section_start block_count * alignment) { //5. 獲取節的文件中偏移加上導出表的rva減去節的rva (真正在文件中的偏移地址) offset = psection->PointerToRawData rva - section_start; return offset; } } return offset; }

//打印出 數據目錄表 void print_data_dir(char* base) { PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)base dos_header->e_lfanew); PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header 4); //4為nt頭中Signature DWORD PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header IMAGE_SIZEOF_FILE_HEADER); for (int i=0;i< IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i ) { IMAGE_DATA_DIRECTORY data_dir = optional_header->DataDirectory[i]; printf("%d data:x size:%x\n", i, data_dir.VirtualAddress, data_dir.Size); } IMAGE_DATA_DIRECTORY export_dir = optional_header->DataDirectory[0]; int offset = rva_to_foa(base, &export_dir.VirtualAddress); //根據導出表的rva,進行foa轉換 //獲取導出表 PIMAGE_EXPORT_DIRECTORY export_table = (PIMAGE_EXPORT_DIRECTORY)(base offset); }

//IMAGE_EXPORT_DIRECTORY結構體 主要的字段 //DWORD Name; //DWORD Base; //IMAGE_EXPORT_DIRECTORY 導出表 //DWORD NumberOfFunctions; //導出函數的個數 //DWORD NumberOfNames; //函數名稱的函數個數 //DWORD AddressOfFunctions; 導出函數的地址表 rva 個數用NumberOfFunctions //DWORD AddressOfNames; // 導出函數名稱表 rva 個數用NumberOfNames //DWORD AddressOfNameOrdinals; // 導出序号表 rva 個數用NumberOfNames //函數名字 通過AddressOfNames(将地址rva到foa) 查到之後,去AddressOfNameOrdinals 獲取序号,然後在根據序号去AddressOfFunctions表找到函數的地址 //序号 通過序号 減去base 得到的下标,直接去AddressOfFunctions

上面拿到導出表,通過導出表解析,可以實現GetProcAddress功能.

動态庫頭文件:

#ifdef __cplusplus extern "C" { #endif int add(int a, int b); int sub(int a, int b); #ifdef __cplusplus } #endif

動态庫源文件:

_declspec (dllexport) int add(int a, int b) { return a b; } _declspec (dllexport) int sub(int a, int b) { return a - b; }

看看如何實現:

//獲取函數的調用地址 void* my_proc_address(char* base, char* func_name) { PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)base dos_header->e_lfanew); PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header 4); PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header file_header->SizeOfOptionalHeader); IMAGE_DATA_DIRECTORY export_table = optional_header->DataDirectory[0]; PIMAGE_EXPORT_DIRECTORY export = (PIMAGE_EXPORT_DIRECTORY)(base export_table.VirtualAddress); unsigned long names = export->NumberOfNames; unsigned long* address_names = (unsigned long*)(base export->AddressOfNames); unsigned short* address_name_ordinals = (unsigned short*)(base export->AddressOfNameOrdinals); unsigned long* address_fuctions = (unsigned long*)(base export->AddressOfFunctions); for (int i = 0; i < names; i ) { char* name = (char*)(base address_names[i]); if (strcmp(name, func_name) == 0) { int ordinal = address_name_ordinals[i]; void* func_addr = (void*)(base address_fuctions[ordinal]); return func_addr; } } return NULL; } int main(int argc, char* argv[]) { typedef int(*Add)(int a, int b); HMODULE hmodule = LoadLibraryEx("DllExportTable.dll", NULL, DONT_RESOLVE_DLL_REFERENCES); //my_proc_address 實現GetProcAddress()功能 Add add = (Add)my_proc_address(hmodule, "add"); int sum = add(100, 100); printf("sum=%d\n", sum); }

如何删除windows的pe選擇(PE數據目錄表和導出表解析)3

通過導出表獲取函數的調用地址,實現GetProAddress功能

上面實現GetProcAddress函數的代碼,為什麼沒有進行RVA到FOA的轉換呢? 因為LoadLibraryEx函數已經dll文件加載到内存上進行了拉伸操作.所以不需要進行轉換.

通過序号查找函數的調用地址

//通過序号,查找函數在内存中地址 //1. 獲取導出表在内存中的位置 //2. 序号減去導出表中起始函數序号(Base),得到的下标就是 void* my_proc_ordinal(char* base, int ordinal) { if (ordinal > 0) { PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)base; PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((unsigned long)base dos_header->e_lfanew); PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header 4); PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER section_headersection_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header file_header->SizeOfOptionalHeader); IMAGE_DATA_DIRECTORY export_table = optional_header->DataDirectory[0]; //導出表 内存中相對地址 PIMAGE_EXPORT_DIRECTORY export = (PIMAGE_EXPORT_DIRECTORY)(base export_table.VirtualAddress); //導出表 在内存的地址 //這裡獲取函數個數,要特殊處理一下 //正常情況下,用NumberOfFunctions就能獲取到 //其他情況下,如在def文件,讓個别函數沒有名稱或者指定序号 unsigned long numbers = export->Base export->NumberOfFunctions - 1; //起始序号加個數 在減一 if (ordinal > numbers) { return NULL; } unsigned long* address_fuctions = (unsigned long*)(base export->AddressOfFunctions); unsigned long base_count = export->Base; return (void*)(base address_fuctions[ordinal - base_count]); } return NULL; }

個人能力有限,如果您發現有什麼不對,請私信我

如果您覺得對您有用的話,可以點個贊或者加個關注,歡迎大家一起進行技術交流

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved