ELF(Executable and Linking Format)是一種對象文件的格式,它主要用于定義ELF(Executable and Linking Format)是一種對象文件的格式,它主要用于定義不同類型的對象文件中的內容以及它們的存儲方式。一個ELF文件主要包含三個部分:文本段、數據段和堆棧段。
在程序運行時,每個線程都有一個獨立的棧空間。這個棧空間用于存儲函數調用時的局部變量和返回地址。當一個新的函數被調用時,處理器會將當前的棧指針移動到棧頂,為新的函數調用分配足夠的空間來存儲其局部變量和返回地址。當函數調用結束時,處理器會將棧指針恢復到原來的值,從而完成函數調用的上下文切換。
ELF文件格式解析
ELF(Executable and Linkable Format)是一種可執行文件和可鏈接文件的格式,它被廣泛用于類Unix系統。ELF文件格式由以下幾個部分組成:
-
ELF頭(ELF Header):包含了文件的基本屬性和結構信息,如文件類型、入口地址、程序頭表位置等。
-
程序頭表(Program Header Table):包含了文件中各個段的信息,如代碼段、數據段、堆棧段等。每個段都有一個對應的程序頭,其中包含了該段在文件中的位置、大小、讀寫權限等信息。
-
節頭表(Section Header Table):包含了文件中各個節的信息,如符號表、字符串表等。每個節都有一個對應的節頭,其中包含了該節在文件中的位置、大小、類型等信息。
-
符號表(Symbol Table):包含了文件中定義的全局變量、函數等符號信息。符號表中的每個符號都有一個對應的符號條目,其中包含了該符號的名稱、類型、值等信息。
-
字符串表(String Table):包含了文件中所有的字符串常量。字符串表中的每個字符串都有一個對應的字符串條目,其中包含了該字符串的內容和長度等信息。
ELF節區信息概述
-
.shstrtab:這是一個字符串表,用于存儲程序中的符號名。
-
.interp:這是一個指向解釋器段的指針,用于支持動態鏈接。
-
.dynamic:這是一個動態段,包含了程序運行時所需的一些信息,如動態鏈接庫、符號表等。
-
.note:這是一個注釋段,用于存儲程序中的各種注釋信息。
-
.tbss:這是一個未初始化的數據段,用于存儲程序中未初始化的全局變量和靜態變量。
-
eh_frname:這是一個異常處理程序的名稱段,用于存儲異常處理程序的名稱。
-
.bss:這是一個未初始化的數據段,用于存儲程序中未初始化的全局變量和靜態變量。
-
.init_array:這是一個初始化數組段,用于存儲程序的初始化函數地址。
-
.fini_array:這是一個終止函數數組段,用于存儲程序的終止函數地址。
-
.dynstr:這是一個動態字符串表段,用于存儲動態鏈接庫中的字符串。
-
.dynsym:這是一個動態符號表段,用于存儲動態鏈接庫中的符號信息。
-
.got.plt:這是一個全局偏移表(GOT)和過程鏈接表(PLT)段,用于存儲動態鏈接庫中的全局偏移表和過程鏈接表的地址。
-
.data:這是一個數據段,用于存儲程序中的常量和靜態變量。
-
.plt:這是一個過程鏈接表段,用于存儲程序中需要調用的外部函數的地址。
-
.init:這是一個初始化函數段,用于存儲程序的初始化函數地址。
-
.rela.dyn:這是一個重定位動態段,用于存儲動態鏈接庫中的重定位信息。
-
.rel.dyn:這是一個重定位動態段,用于存儲動態鏈接庫中的重定位信息。
-
.rela.plt:這是一個重定位過程鏈接表段,用于存儲程序中需要調用的外部函數的重定位信息。
-
.rel.plt:這是一個重定位過程鏈接表段,用于存儲程序中需要調用的外部函數的重定位信息。
-
.text:這是一個代碼段,用于存儲程序的可執行代碼。
-
.fini:這是一個終止函數段,用于存儲程序的終止函數地址。
-
.gun.version:這是一個版本信息段,用于存儲程序的版本信息。
-
.gun.version_r:這是一個版本信息段,用于存儲程序的版本信息。
-
.gun.hash:這是一個哈希值段,用于存儲程序的哈希值。
深入分析ELF文件的可執行棧
ELF文件是一種常見的可執行文件格式,用于在Linux和其他類Unix系統上運行程序。ELF文件包含程序的可執行代碼和其他相關信息,如數據段、符號表、重定位表等。
可執行棧是程序運行時用于存儲局部變量、函數調用信息等的一塊內存區域。ELF文件中的可執行代碼被加載到內存中,操作系統會為程序分配一塊可執行棧空間。在程序執行過程中,將局部變量等數據保存在棧上,并通過棧來維護函數調用的執行順序。
要深入分析ELF文件的可執行棧,可以參考以下幾個方面:
-
棧幀結構:程序的每個函數調用都會在棧上創建一個棧幀。棧幀包含函數的參數、局部變量以及一些與函數調用相關的信息,如返回地址、上一級函數的棧幀指針等。了解棧幀的結構可以幫助分析函數調用過程和內存布局。
-
棧指針:程序運行時,棧指針指向當前棧頂的位置。當函數調用時,棧指針會移動到新的棧幀起始位置,分配空間給新的局部變量。了解棧指針的變化可以追蹤函數調用的過程。
-
緩沖區溢出漏洞:棧溢出是一種常見的安全漏洞,攻擊者可以通過向緩沖區輸入超出預留空間的數據來破壞程序的執行或注入惡意代碼。分析可執行棧可以幫助識別和防止棧溢出漏洞。
-
異常處理:當程序運行遇到異常情況時,如除零錯誤或訪問非法內存操作等,操作系統會通過異常處理機制來捕獲并處理這些異常。了解可執行棧的結構和異常處理機制可以幫助理解程序運行時的錯誤處理過程。
Linux內存映射
Linux內存映射是一種將文件或設備映射到進程虛擬內存地址空間的機制。通過內存映射,進程可以像訪問內存一樣訪問文件或設備的內容,而無需進行顯式的讀寫操作。
在Linux中,內存映射主要通過下面兩個系統調用來實現:
-
mmap():mmap系統調用可以將文件的一部分或整個文件映射到進程的虛擬內存地址空間中。通過mmap,進程可以直接訪問文件內容,而不需要使用常規的I/O操作。
-
munmap():munmap系統調用用于解除對內存的映射關系。通過munmap,進程可以釋放不再需要的內存映射。這樣可以及時釋放系統資源,避免內存泄漏。
使用內存映射的好處包括:
-
避免了頻繁的文件I/O操作:內存映射可以將文件內容直接映射到進程的內存中,避免了頻繁的讀寫操作,提高了讀寫效率。
-
簡化了對文件數據的訪問:通過內存映射,可以直接通過內存地址訪問文件的內容,無需使用read()和write()等系統調用。
-
允許進程間共享數據:多個進程可以通過將同一個文件映射到它們的地址空間中來共享數據,實現高效的進程間通信。
-
方便處理大文件:通過內存映射,即使是超過進程地址空間大小的大文件也可以被處理,而不需要將其一次性讀入內存。
二進制修復和重建
二進制修復和重建是指對損壞、缺失或不完整的二進制文件進行修復或重新構建的過程。這通常是在軟件開發、系統管理和計算機安全等領域中遇到的問題。
二進制修復通常包括以下幾個步驟:
-
檢測和診斷:首先需要檢測二進制文件的問題,并進行診斷。常見的問題包括缺少或損壞的代碼、數據、庫引用、符號表等。通過分析文件的結構和內容,以及使用相關工具進行靜態或動態分析,可以幫助確定問題的位置和性質。
-
修復和恢復:一旦問題被確定,就可以進行修復和恢復工作。這可能包括從備份中恢復、重新生成缺失的部分、修復損壞的數據結構等。根據問題的具體性質和復雜程度,可能需要使用特定的工具和技術進行修復。
-
驗證和測試:修復完成后,需要進行驗證和測試,確保二進制文件恢復正常并沒有引入新的問題。可以通過運行測試用例、執行靜態和動態分析、進行漏洞掃描等方式來驗證修復的效果。
二進制重建通常是指從已有的二進制文件中重建出源代碼或高層次的抽象表示。這在逆向工程、漏洞分析、惡意軟件分析等領域中非常有用。重建二進制文件的源代碼可以幫助理解程序的邏輯、進行安全審計和修復漏洞等工作。
二進制重建常用的技術包括:
-
反匯編:通過將二進制文件轉換為匯編代碼,可以獲得對程序邏輯的低級別理解。可以使用反匯編器工具來執行這個過程。
-
動態分析:通過運行二進制文件,并使用調試器和動態分析工具,可以觀察和分析程序的執行行為。這可以幫助理解程序的運行流程、數據處理機制等。
-
符號恢復:嘗試恢復丟失的符號信息,比如函數名、變量名等。這可以通過對二進制文件進行靜態分析,觀察函數調用關系、命名模式等來實現。
Linux C/C++ 從內存轉儲中恢復64位ELF可執行文件
首先,我們需要獲得一個程序的核心轉儲。為此,您可以使用gcore實用程序或提供的示例/dump.sh腳本,在調用gcore之前,它可以確保正確設置了coredump_filter。
dump.sh
#!/bin/sh
echo 0x07 > /proc/$1/coredump_filter
gcore $1
程序的核心轉儲示例:
...int main(int argc, char *argv[])
{bss_var = 0x13371337;int stack_var = 0xcafecafe;int *heap_var = (int *) malloc(sizeof(int));*heap_var = 0xabcdabcd;printf("bss_var....[%p]=0x%08x\n", &bss_var, bss_var);printf("data_var...[%p]=0x%08x\n", &data_var, data_var);printf("stack_var..[%p]=0x%08x\n", &stack_var, stack_var);printf("heap_var...[%p]=0x%08x\n", heap_var, *heap_var);data_var = 0xaf7e3; //aftergetc(stdin);return 0;
}
./test &
sh dump.sh $(pgrep test)
在獲得轉儲之后,為了恢復二進制文件,通過將核心轉儲文件作為第一參數和輸出文件的所需名稱作為第二參數來調用core_elf64程序就足夠了,Linux C/C++ 從內存轉儲中恢復64位ELF可執行文件:
int main(int argc, char **argv)
{
...if ( argc != 3 ) printf("Usage: %s <core dump> <output file>", argv[0]);in = open(argv[1], O_RDONLY);if (in < 0) die("Coudln't open file: %s\n", argv[1]);if (read(in, core_e_ident, sizeof(core_e_ident)) != sizeof(core_e_ident)) printf("Read error\n");if(strncmp(core_e_ident, ELFMAG, SELFMAG))printf("%s is not an ELF file!\n", argv[1]);if(core_e_ident[EI_CLASS] != ELFCLASS64)printf("This version supports only 64 bit core dumps!\n");if(core_e_ident[EI_DATA] != ELFDATA2LSB)printf("This version supports only Little Endian!\n");/* Read ELF header */if (lseek(in, 0, SEEK_SET) < 0) printf("Seek error\n");core_ehdr = (Elf64_Ehdr *) malloc(sizeof(Elf64_Ehdr));if (core_ehdr == NULL) printf("malloc error\n");if (read(in, core_ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) printf("Read error\n");if (core_ehdr->e_type != ET_CORE) printf("%s is not a core dump!", argv[1]);if (core_ehdr->e_machine != EM_X86_64)printf("This version supports only 64 bit core dumps!");unsigned int core_phdr_size = core_ehdr->e_phentsize*core_ehdr->e_phnum;if (lseek(in, core_ehdr->e_phoff, SEEK_SET) < 0) printf("Seek error\n");core_phdr = (Elf64_Phdr *) malloc(core_phdr_size);if (core_phdr == NULL) printf("malloc error\n");if (read(in, core_phdr, core_phdr_size) < core_phdr_size) printf("Read error\n");printf("\n[*] Core dump contains the following segments:\n\n");printf("Index %16s Virt. addr. start Virt. addr. end Flags\n", "Type");for(i=0; i<core_ehdr->e_phnum; i++){printf("[%4d] %16s 0x%016lx - 0x%016lx %c %c %c\n", i,seg_type_to_str(core_phdr[i].p_type),core_phdr[i].p_vaddr, core_phdr[i].p_vaddr+core_phdr[i].p_memsz,core_phdr[i].p_flags & PF_R ? 'R' : ' ',core_phdr[i].p_flags & PF_W ? 'W' : ' ',core_phdr[i].p_flags & PF_X ? 'X' : ' ');}printf("\n[*] Valid text segments: ");for(i=0; i<core_ehdr->e_phnum; i++){/* 讀取段的前4個字節,看看它是否為ELF */char *seg_data = xget(in, core_phdr[i].p_offset, SELFMAG);if( core_phdr[i].p_type == PT_LOAD &&core_phdr[i].p_flags == (PF_R | PF_X) &&strncmp(seg_data, ELFMAG, SELFMAG) == 0 ){printf("%d ", i);if ( (core_phdr[i].p_vaddr & (~0xfffff)) == 0x400000 )core_text_seg_index = i;} if(seg_data != NULL) free(seg_data);}printf("\n");if(core_text_seg_index == -1){printf("Unable to find a text segment near virtual address 0x400000, " "please specify a text segment index (usually 1): ");scanf("%d", &core_text_seg_index);} printf("[*] Text segment index = %d\n", core_text_seg_index);/* 檢索文本段數據 */char *text_seg_data = xget(in, core_phdr[core_text_seg_index].p_offset, core_phdr[core_text_seg_index].p_filesz);Elf64_Ehdr *ehdr;Elf64_Phdr *phdr;ehdr = (Elf64_Ehdr *) text_seg_data;phdr = (Elf64_Phdr *) &text_seg_data[ehdr->e_phoff];printf("[*] Reconstructed Program Header:\n\n");printf("Index %16s Virt. addr. start Virt. addr. end Flags\n", "Type");for(i=0; i<ehdr->e_phnum; i++){printf("[%4d] %16s 0x%016lx - 0x%016lx %c %c %c\n", i,seg_type_to_str(phdr[i].p_type),phdr[i].p_vaddr, phdr[i].p_vaddr+phdr[i].p_memsz,phdr[i].p_flags & PF_R ? 'R' : ' ',phdr[i].p_flags & PF_W ? 'W' : ' ',phdr[i].p_flags & PF_X ? 'X' : ' ');}for(i=0; i<ehdr->e_phnum; i++){if( phdr[i].p_type == PT_LOAD && phdr[i].p_flags == (PF_R | PF_X) &&phdr[i].p_vaddr < ehdr->e_entry &&phdr[i].p_vaddr + phdr[i].p_memsz > ehdr->e_entry){text_seg_index = i;}if( phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_W) ){data_seg_index = i;}if( phdr[i].p_type == PT_DYNAMIC ){dyn_seg_index = i;}}if(text_seg_index == -1)die("Unable to find text segment");elseprintf("\n[*] Text segment: 0x%lx - 0x%lx\n", phdr[text_seg_index].p_vaddr,phdr[text_seg_index].p_vaddr + phdr[text_seg_index].p_memsz);if(data_seg_index == -1)printf("[*] Unable to find data segment\n");elseprintf("[*] Data segment: 0x%lx - 0x%lx\n", phdr[data_seg_index].p_vaddr,phdr[data_seg_index].p_vaddr + phdr[data_seg_index].p_memsz);if(dyn_seg_index == -1)printf("[*] Unable to find dynamic segment\n");elseprintf("[*] Dynamic segment: 0x%lx - 0x%lx\n", phdr[dyn_seg_index].p_vaddr,phdr[dyn_seg_index].p_vaddr + phdr[dyn_seg_index].p_memsz);/* 恢復找到的分段的內容 */char **seg_data = malloc(sizeof(char *)*ehdr->e_phnum);if(seg_data == NULL) printf("Malloc error\n");for(i=0; i<ehdr->e_phnum; i++){for (j=0; j<core_ehdr->e_phnum; j++) {/* If executable is PIE */if(ehdr->e_type == ET_DYN){if( phdr[i].p_vaddr >= core_phdr[j].p_vaddr - core_phdr[core_text_seg_index].p_vaddr &&phdr[i].p_vaddr < core_phdr[j].p_vaddr + core_phdr[j].p_filesz - core_phdr[core_text_seg_index].p_vaddr){//printf("%d recover with %d\n",i,j);seg_data[i] = xget( in,core_phdr[j].p_offset + phdr[i].p_vaddr - (core_phdr[j].p_vaddr - core_phdr[core_text_seg_index].p_vaddr),phdr[i].p_filesz );break;}}else{if( phdr[i].p_vaddr >= core_phdr[j].p_vaddr &&phdr[i].p_vaddr < core_phdr[j].p_vaddr + core_phdr[j].p_filesz ){//printf("%d recover with %d\n",i,j);seg_data[i] = xget( in,core_phdr[j].p_offset + phdr[i].p_vaddr - core_phdr[j].p_vaddr,phdr[i].p_filesz );break;}}}}
...written)int out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);if (out < 0) die("Failed to create output file %s", argv[2]);for (i=0; i<ehdr->e_phnum; i++){if (lseek(out, phdr[i].p_offset, SEEK_SET) < 0)die("Error seek");if (write(out, seg_data[i], phdr[i].p_filesz) != phdr[i].p_filesz)die("Write error");if (phdr[i].p_offset + phdr[i].p_filesz > eof)eof = phdr[i].p_offset + phdr[i].p_filesz;}/* Write section strings */if (lseek(out, eof, SEEK_SET) < 0)die("Error Seek");if (write(out, shstr, sizeof(shstr)) != sizeof(shstr))die("Error writing shstr");/* Reset section header offset and number */ehdr->e_shoff = eof + sizeof(shstr);ehdr->e_shnum = 0;ehdr->e_shstrndx = 1;/*********************************//* Section header reconstruction *//*********************************/Elf64_Shdr shdr;Elf64_Word interp_index = 0; //Store interp section index needed below/* Recover simple sections (1:1 matching with segments) */printf("\n[*] Recovered sections:\n");/* NULL section */memset(&shdr, 0, sizeof(shdr));if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Write Error");ehdr->e_shnum++;/* .shstrtab */shdr.sh_name = sec_index(".shstrtab");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .shstrtab");ehdr->e_shnum++;for (i=0; i<ehdr->e_phnum; i++){switch(phdr[i].p_type){/* .interp */case PT_INTERP:shdr.sh_name = sec_index(".interp");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .interp");ehdr->e_shnum++;printf("\t.interp\n");break;/* .dynamic */case PT_DYNAMIC:shdr.sh_name = sec_index(".dynamic");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .dynamic");ehdr->e_shnum++;printf("\t.dynamic\n");break;/* .note */case PT_NOTE:shdr.sh_name = sec_index(".note");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .note");ehdr->e_shnum++;printf("\t.note\n");break;/* .tbss */case PT_TLS:shdr.sh_name = sec_index(".tbss");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .tbss");ehdr->e_shnum++;printf("\t.tbss\n");break;/* .eh_frame_hdr and .eh_frame */case PT_GNU_EH_FRAME:shdr.sh_name = sec_index(".eh_frame_hdr");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .eh_frame_hdr");ehdr->e_shnum++;printf("\t.eh_frame\n");shdr.sh_name = sec_index(".eh_frame");
... if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .eh_frame");ehdr->e_shnum++;printf("\t.eh_frame_hdr\n");break;}} if(data_seg_index != -1){shdr.sh_name = sec_index(".bss");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .bss");ehdr->e_shnum++;printf("\t.bss\n");}...for(; dyn->d_tag != DT_NULL; ++dyn){switch(dyn->d_tag){ case DT_BIND_NOW: //Full relrobindnow = 1;break;case DT_INIT:init = dyn->d_un.d_ptr;break;case DT_FINI:fini = dyn->d_un.d_ptr;break;case DT_INIT_ARRAY:init_array = dyn->d_un.d_ptr;break;case DT_INIT_ARRAYSZ:init_arraysz = dyn->d_un.d_val;break;case DT_FINI_ARRAY:fini_array = dyn->d_un.d_ptr;break;case DT_FINI_ARRAYSZ:fini_arraysz = dyn->d_un.d_val;break;case DT_GNU_HASH:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;gnu_hash = dyn->d_un.d_ptr;break;case DT_STRTAB:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;strtab = dyn->d_un.d_ptr;break;case DT_SYMTAB:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;symtab = dyn->d_un.d_ptr;break;case DT_STRSZ:strsz = dyn->d_un.d_val;break;case DT_SYMENT:syment = dyn->d_un.d_val;break;case DT_PLTGOT:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;pltgot = dyn->d_un.d_ptr;break;case DT_PLTRELSZ:pltrelsz = dyn->d_un.d_val;break;case DT_PLTREL:pltrel = dyn->d_un.d_val;break;case DT_JMPREL:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;jmprel = dyn->d_un.d_ptr;break;case DT_RELA:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;rela = dyn->d_un.d_ptr;break;case DT_RELASZ:relasz = dyn->d_un.d_val;break;case DT_RELAENT:relaent = dyn->d_un.d_val;break;case DT_REL:rel = dyn->d_un.d_ptr;break;case DT_RELSZ:relsz = dyn->d_un.d_val;break;case DT_RELENT:relent = dyn->d_un.d_val;break;case DT_VERNEED:verneed = dyn->d_un.d_ptr;break;case DT_VERNEEDNUM:verneednum = dyn->d_un.d_val;break;case DT_VERSYM:if(pie) dyn->d_un.d_ptr -= core_phdr[core_text_seg_index].p_vaddr;versym = dyn->d_un.d_ptr;break;default://printf("%ld\n", dyn->d_tag);break;}}/* 如果PIE將正確的值重寫到.dynamic部分 */if(pie){uint64_t fd_pos;if ((fd_pos = lseek(out, 0, SEEK_CUR)) < 0) die("Error seek");if (lseek(out, phdr[dyn_seg_index].p_offset, SEEK_SET) < 0)die("Error seek");if (write(out, seg_data[dyn_seg_index], phdr[dyn_seg_index].p_filesz) != phdr[dyn_seg_index].p_filesz)die("Write error");if (lseek(out, fd_pos, SEEK_SET) < 0)die("Error seek");} if(init_array != 0 && init_arraysz != 0){shdr.sh_name = sec_index(".init_array");
....if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .init_array");ehdr->e_shnum++;printf("\t.init_array\n");}/* .fini_array - In data segment, contains pointer to code to be* execute at the end */if(fini_array != 0 && fini_arraysz != 0){shdr.sh_name = sec_index(".fini_array");
....if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .fini_array");ehdr->e_shnum++;printf("\t.fini_array\n");}/* .dynstr, usually followed by versym */Elf64_Word dynstr_index = 0;if(strtab != 0 && versym != 0){shdr.sh_name = sec_index(".dynstr");
....dynstr_index = ehdr->e_shnum;if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .dynstr");ehdr->e_shnum++;printf("\t.dynstr\n");}Elf64_Word dynsym_index = 0; if(symtab !=0 && strtab !=0){shdr.sh_name = sec_index(".dynsym");shdr.sh_type = SHT_DYNSYM;....dynsym_index = ehdr->e_shnum;if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .dynsym");ehdr->e_shnum++;printf("\t.dynsym\n");}uint64_t got_entries = 0;if(pltrel != 0 && pltrelsz !=0 && (relent != 0 || relaent != 0) && pltgot != 0){if(pltrel == DT_RELA)got_entries = ((pltrelsz/relaent) + 3);else got_entries = ((pltrelsz/relent) + 3);shdr.sh_size = got_entries * sizeof(Elf64_Addr);shdr.sh_name = sec_index(".got.plt");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .got.plt");ehdr->e_shnum++;printf("\t.got.plt\n");}/* .data - resides after .got.plt */if(pltgot != 0 && got_entries != 0){shdr.sh_name = sec_index(".data");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .data");ehdr->e_shnum++;printf("\t.data\n");}/*.plt*/Elf64_Word plt_addr = 0;Elf64_Word plt_index = 0;/* 從init開始,然后搜索.plt模式 */if(init != 0 && pltgot != 0 && got_entries != 0){
...if(plt_addr == 0)goto section_rebuild_end;shdr.sh_name = sec_index(".plt");...plt_index = ehdr->e_shnum;if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .got.plt");ehdr->e_shnum++;printf("\t.plt\n");}/* .init - is just before .plt */if(init != 0 && plt_addr != 0){shdr.sh_name = sec_index(".init");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .got.plt");ehdr->e_shnum++;printf("\t.init\n");}/* .rel.dyn or .rela.dyn */if( (rela != 0 && relaent !=0) || (rel != 0 && relent != 0) ){if(rela != 0 && relaent != 0){shdr.sh_name = sec_index(".rela.dyn");shdr.sh_type = SHT_RELA;shdr.sh_addr = rela;shdr.sh_entsize = relaent; if(jmprel)shdr.sh_size = jmprel - shdr.sh_addr;elseshdr.sh_size = relasz;}else{shdr.sh_name = sec_index(".rel.dyn");shdr.sh_type = SHT_REL;shdr.sh_addr = rel;shdr.sh_entsize = relent;if(jmprel)shdr.sh_size = jmprel - shdr.sh_addr;elseshdr.sh_size = relsz;}...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr))die("Error .rel.dyn or .rela.dyn");ehdr->e_shnum++;if(rela != 0)printf("\t.rela.dyn\n");elseprintf("\t.rel.dyn\n");}if((relent != 0 || relaent != 0) && init != 0 && jmprel != 0) {if(pltrel == DT_RELA){shdr.sh_name = sec_index(".rela.plt");shdr.sh_type = SHT_RELA;shdr.sh_entsize = relaent;}else{shdr.sh_name = sec_index(".rel.plt");shdr.sh_type = SHT_REL;shdr.sh_entsize = relent;}
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr))die("Error .rel.dyn or .rela.dyn");ehdr->e_shnum++;if(pltrel == DT_RELA)printf("\t.rela.plt\n");elseprintf("\t.rel.plt\n");}if(plt_addr != 0 && got_entries != 0 && fini != 0){shdr.sh_name = sec_index(".text");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .text");ehdr->e_shnum++;printf("\t.text\n");}/* .fini *//* TODO - find size*/if(fini != 0){shdr.sh_name = sec_index(".fini");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .fini");ehdr->e_shnum++;printf("\t.fini\n");}/* .gnu.version */if(versym != 0 && strtab != 0 && symtab != 0){shdr.sh_name = sec_index(".gnu.version");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .gnu.version");ehdr->e_shnum++;printf("\t.gnu.version\n");}/* .gnu_versioni_r - before .rel or .rela */if(verneed != 0 && verneednum != 0 && pltrel != 0){shdr.sh_name = sec_index(".gnu.version_r");
...if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .gnu_version_r");ehdr->e_shnum++;printf("\t.gnu_version_r\n");}/* .gnu.hash */if(gnu_hash != 0 && symtab != 0){shdr.sh_name = sec_index(".gnu.hash");shdr.sh_type = SHT_GNU_HASH;shdr.sh_addr = gnu_hash;shdr.sh_offset = gnu_hash - phdr[text_seg_index].p_vaddr;shdr.sh_size = symtab - gnu_hash;shdr.sh_flags = SHF_ALLOC;shdr.sh_link = dynsym_index;shdr.sh_info = 0;shdr.sh_addralign = 8;shdr.sh_entsize = 0;if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) die("Error .gnu.hash");ehdr->e_shnum++;printf("\t.gnu.hash\n");}if(bindnow){printf("[*] FULL Relro binary, no GOT reconstruction is needed\n");goto section_rebuild_end;}else if(got_entries == 0){printf("[*] WARNING Can't recover GOT entries\n");goto section_rebuild_end;}printf("\n[*] %ld GOT entries found\n", got_entries);uint64_t got_off = phdr[data_seg_index].p_offset +(pltgot - phdr[data_seg_index].p_vaddr);uint64_t got_entry = 0x0;for (i = 1; i < 3; i++){if (lseek(out,got_off + sizeof(Elf64_Addr) * i,SEEK_SET) < 0)
...}/* 使用恢復的PLT地址恢復GOT部分 */for(i = 3; i < got_entries; i++){if (lseek(out,got_off + sizeof(Elf64_Addr)*i,SEEK_SET) < 0) die("Seek error");...}section_rebuild_end:/* 所有部分都恢復。現在用正確的節數重寫ELF頭 */if (lseek(out, 0 , SEEK_SET) < 0) die("Seek error");if (write(out, (char *)ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) die("Write error");/* 釋放分配的內存并關閉文件 */ for(i=0; i<ehdr->e_phnum; i++){free(seg_data[i]);}
...return 0;
}
If you need the complete source code, please add the WeChat number (c17865354792)
運行效果:
…/core_elf64 core.2171 rebuild
./rebuild
從轉儲中恢復二進制文件
從轉儲中恢復二進制文件的一般步驟如下:
-
使用適當的工具(如gcore、windbg等)生成進程的內存轉儲文件。這需要在合適的時間點進行,以確保轉儲文件包含了所需的二進制數據。
-
確定目標二進制文件的基址,即二進制文件加載到進程內存中的起始地址。可以通過查看進程的內存映射信息或使用調試工具來獲取此信息。
-
使用適當的工具打開內存轉儲文件,并根據基址將所需的二進制數據提取出來。這可以通過從內存轉儲中復制包含目標二進制代碼和數據區域的內存塊來實現。這需要根據目標平臺和文件格式進行解析和提取。
-
如果內存轉儲文件不包含相關的符號信息,則可能需要使用反匯編工具對提取的二進制數據進行反匯編。這樣可以獲得與匯編指令對應的代碼。
-
對提取的二進制數據進行適當的修復和調整,以使其成為有效的可執行文件。這可能包括修復段和節表、添加相關的符號信息、修復鏈接關系等。
-
驗證并測試恢復的二進制文件。這可以包括使用適當的調試器工具來運行和調試恢復的可執行文件,確保其功能正確。
請注意,恢復二進制文件可能涉及到操作系統、文件格式和調試方面的復雜知識和技術。因此,在執行此過程時,建議有相關的經驗和對底層系統有深入的了解。
總結
從內存轉儲中恢復64位ELF可執行文件需要以下步驟:
-
首先,獲取內存轉儲文件。這可以通過使用操作系統提供的工具(如Windows的Task Manager或Linux的GDB)或專用工具(如Volatility)來完成。
-
確定內存轉儲文件中的ELF可執行文件。這可以通過檢查文件的頭部信息來完成。ELF文件的頭部通常包括魔數、文件類型和機器架構等信息。
-
從內存轉儲文件中提取ELF可執行文件。這可以通過解析文件的節區表和程序頭表來完成。節區表描述了不同節區(如代碼段、數據段)在文件中的位置和大小,而程序頭表則描述了不同的程序段(如可加載的段、動態鏈接段)。
-
重建ELF可執行文件的結構。這包括創建文件頭、節區表、程序頭表以及各個節區的內容。通過將這些信息寫入新的文件中,就能夠得到一個完整的ELF可執行文件。
-
修復可能損壞的部分。由于從內存轉儲中恢復ELF可執行文件可能會丟失一些數據,因此可能需要進行一些修復工作。這可能包括修復損壞的符號表、重建重定位表等。
總結起來,從內存轉儲中恢復64位ELF可執行文件需要解析內存轉儲文件的結構,提取ELF文件,并通過重建文件的結構來恢復文件。
Welcome to follow WeChat official account【程序猿編碼】
參考:
https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html
https://stackoverflow.com/questions/58076539/plt-plt-got-what-is-different