Linux C/C++ 從內存轉儲中恢復64位ELF可執行文件

ELF(Executable and Linking Format)是一種對象文件的格式,它主要用于定義ELF(Executable and Linking Format)是一種對象文件的格式,它主要用于定義不同類型的對象文件中的內容以及它們的存儲方式。一個ELF文件主要包含三個部分:文本段、數據段和堆棧段。

在程序運行時,每個線程都有一個獨立的棧空間。這個棧空間用于存儲函數調用時的局部變量和返回地址。當一個新的函數被調用時,處理器會將當前的棧指針移動到棧頂,為新的函數調用分配足夠的空間來存儲其局部變量和返回地址。當函數調用結束時,處理器會將棧指針恢復到原來的值,從而完成函數調用的上下文切換。

ELF文件格式解析

ELF(Executable and Linkable Format)是一種可執行文件和可鏈接文件的格式,它被廣泛用于類Unix系統。ELF文件格式由以下幾個部分組成:

  1. ELF頭(ELF Header):包含了文件的基本屬性和結構信息,如文件類型、入口地址、程序頭表位置等。

  2. 程序頭表(Program Header Table):包含了文件中各個段的信息,如代碼段、數據段、堆棧段等。每個段都有一個對應的程序頭,其中包含了該段在文件中的位置、大小、讀寫權限等信息。

  3. 節頭表(Section Header Table):包含了文件中各個節的信息,如符號表、字符串表等。每個節都有一個對應的節頭,其中包含了該節在文件中的位置、大小、類型等信息。

  4. 符號表(Symbol Table):包含了文件中定義的全局變量、函數等符號信息。符號表中的每個符號都有一個對應的符號條目,其中包含了該符號的名稱、類型、值等信息。

  5. 字符串表(String Table):包含了文件中所有的字符串常量。字符串表中的每個字符串都有一個對應的字符串條目,其中包含了該字符串的內容和長度等信息。

ELF節區信息概述

  1. .shstrtab:這是一個字符串表,用于存儲程序中的符號名。

  2. .interp:這是一個指向解釋器段的指針,用于支持動態鏈接。

  3. .dynamic:這是一個動態段,包含了程序運行時所需的一些信息,如動態鏈接庫、符號表等。

  4. .note:這是一個注釋段,用于存儲程序中的各種注釋信息。

  5. .tbss:這是一個未初始化的數據段,用于存儲程序中未初始化的全局變量和靜態變量。

  6. eh_frname:這是一個異常處理程序的名稱段,用于存儲異常處理程序的名稱。

  7. .bss:這是一個未初始化的數據段,用于存儲程序中未初始化的全局變量和靜態變量。

  8. .init_array:這是一個初始化數組段,用于存儲程序的初始化函數地址。

  9. .fini_array:這是一個終止函數數組段,用于存儲程序的終止函數地址。

  10. .dynstr:這是一個動態字符串表段,用于存儲動態鏈接庫中的字符串。

  11. .dynsym:這是一個動態符號表段,用于存儲動態鏈接庫中的符號信息。

  12. .got.plt:這是一個全局偏移表(GOT)和過程鏈接表(PLT)段,用于存儲動態鏈接庫中的全局偏移表和過程鏈接表的地址。

  13. .data:這是一個數據段,用于存儲程序中的常量和靜態變量。

  14. .plt:這是一個過程鏈接表段,用于存儲程序中需要調用的外部函數的地址。

  15. .init:這是一個初始化函數段,用于存儲程序的初始化函數地址。

  16. .rela.dyn:這是一個重定位動態段,用于存儲動態鏈接庫中的重定位信息。

  17. .rel.dyn:這是一個重定位動態段,用于存儲動態鏈接庫中的重定位信息。

  18. .rela.plt:這是一個重定位過程鏈接表段,用于存儲程序中需要調用的外部函數的重定位信息。

  19. .rel.plt:這是一個重定位過程鏈接表段,用于存儲程序中需要調用的外部函數的重定位信息。

  20. .text:這是一個代碼段,用于存儲程序的可執行代碼。

  21. .fini:這是一個終止函數段,用于存儲程序的終止函數地址。

  22. .gun.version:這是一個版本信息段,用于存儲程序的版本信息。

  23. .gun.version_r:這是一個版本信息段,用于存儲程序的版本信息。

  24. .gun.hash:這是一個哈希值段,用于存儲程序的哈希值。

深入分析ELF文件的可執行棧

ELF文件是一種常見的可執行文件格式,用于在Linux和其他類Unix系統上運行程序。ELF文件包含程序的可執行代碼和其他相關信息,如數據段、符號表、重定位表等。

可執行棧是程序運行時用于存儲局部變量、函數調用信息等的一塊內存區域。ELF文件中的可執行代碼被加載到內存中,操作系統會為程序分配一塊可執行棧空間。在程序執行過程中,將局部變量等數據保存在棧上,并通過棧來維護函數調用的執行順序。

要深入分析ELF文件的可執行棧,可以參考以下幾個方面:

  1. 棧幀結構:程序的每個函數調用都會在棧上創建一個棧幀。棧幀包含函數的參數、局部變量以及一些與函數調用相關的信息,如返回地址、上一級函數的棧幀指針等。了解棧幀的結構可以幫助分析函數調用過程和內存布局。

  2. 棧指針:程序運行時,棧指針指向當前棧頂的位置。當函數調用時,棧指針會移動到新的棧幀起始位置,分配空間給新的局部變量。了解棧指針的變化可以追蹤函數調用的過程。

  3. 緩沖區溢出漏洞:棧溢出是一種常見的安全漏洞,攻擊者可以通過向緩沖區輸入超出預留空間的數據來破壞程序的執行或注入惡意代碼。分析可執行棧可以幫助識別和防止棧溢出漏洞。

  4. 異常處理:當程序運行遇到異常情況時,如除零錯誤或訪問非法內存操作等,操作系統會通過異常處理機制來捕獲并處理這些異常。了解可執行棧的結構和異常處理機制可以幫助理解程序運行時的錯誤處理過程。

Linux內存映射

Linux內存映射是一種將文件或設備映射到進程虛擬內存地址空間的機制。通過內存映射,進程可以像訪問內存一樣訪問文件或設備的內容,而無需進行顯式的讀寫操作。

在Linux中,內存映射主要通過下面兩個系統調用來實現:

  1. mmap():mmap系統調用可以將文件的一部分或整個文件映射到進程的虛擬內存地址空間中。通過mmap,進程可以直接訪問文件內容,而不需要使用常規的I/O操作。

  2. munmap():munmap系統調用用于解除對內存的映射關系。通過munmap,進程可以釋放不再需要的內存映射。這樣可以及時釋放系統資源,避免內存泄漏。

使用內存映射的好處包括:

  1. 避免了頻繁的文件I/O操作:內存映射可以將文件內容直接映射到進程的內存中,避免了頻繁的讀寫操作,提高了讀寫效率。

  2. 簡化了對文件數據的訪問:通過內存映射,可以直接通過內存地址訪問文件的內容,無需使用read()和write()等系統調用。

  3. 允許進程間共享數據:多個進程可以通過將同一個文件映射到它們的地址空間中來共享數據,實現高效的進程間通信。

  4. 方便處理大文件:通過內存映射,即使是超過進程地址空間大小的大文件也可以被處理,而不需要將其一次性讀入內存。

二進制修復和重建

二進制修復和重建是指對損壞、缺失或不完整的二進制文件進行修復或重新構建的過程。這通常是在軟件開發、系統管理和計算機安全等領域中遇到的問題。

二進制修復通常包括以下幾個步驟:

  1. 檢測和診斷:首先需要檢測二進制文件的問題,并進行診斷。常見的問題包括缺少或損壞的代碼、數據、庫引用、符號表等。通過分析文件的結構和內容,以及使用相關工具進行靜態或動態分析,可以幫助確定問題的位置和性質。

  2. 修復和恢復:一旦問題被確定,就可以進行修復和恢復工作。這可能包括從備份中恢復、重新生成缺失的部分、修復損壞的數據結構等。根據問題的具體性質和復雜程度,可能需要使用特定的工具和技術進行修復。

  3. 驗證和測試:修復完成后,需要進行驗證和測試,確保二進制文件恢復正常并沒有引入新的問題。可以通過運行測試用例、執行靜態和動態分析、進行漏洞掃描等方式來驗證修復的效果。

二進制重建通常是指從已有的二進制文件中重建出源代碼或高層次的抽象表示。這在逆向工程、漏洞分析、惡意軟件分析等領域中非常有用。重建二進制文件的源代碼可以幫助理解程序的邏輯、進行安全審計和修復漏洞等工作。

二進制重建常用的技術包括:

  1. 反匯編:通過將二進制文件轉換為匯編代碼,可以獲得對程序邏輯的低級別理解。可以使用反匯編器工具來執行這個過程。

  2. 動態分析:通過運行二進制文件,并使用調試器和動態分析工具,可以觀察和分析程序的執行行為。這可以幫助理解程序的運行流程、數據處理機制等。

  3. 符號恢復:嘗試恢復丟失的符號信息,比如函數名、變量名等。這可以通過對二進制文件進行靜態分析,觀察函數調用關系、命名模式等來實現。

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

從轉儲中恢復二進制文件

從轉儲中恢復二進制文件的一般步驟如下:

  1. 使用適當的工具(如gcore、windbg等)生成進程的內存轉儲文件。這需要在合適的時間點進行,以確保轉儲文件包含了所需的二進制數據。

  2. 確定目標二進制文件的基址,即二進制文件加載到進程內存中的起始地址。可以通過查看進程的內存映射信息或使用調試工具來獲取此信息。

  3. 使用適當的工具打開內存轉儲文件,并根據基址將所需的二進制數據提取出來。這可以通過從內存轉儲中復制包含目標二進制代碼和數據區域的內存塊來實現。這需要根據目標平臺和文件格式進行解析和提取。

  4. 如果內存轉儲文件不包含相關的符號信息,則可能需要使用反匯編工具對提取的二進制數據進行反匯編。這樣可以獲得與匯編指令對應的代碼。

  5. 對提取的二進制數據進行適當的修復和調整,以使其成為有效的可執行文件。這可能包括修復段和節表、添加相關的符號信息、修復鏈接關系等。

  6. 驗證并測試恢復的二進制文件。這可以包括使用適當的調試器工具來運行和調試恢復的可執行文件,確保其功能正確。

請注意,恢復二進制文件可能涉及到操作系統、文件格式和調試方面的復雜知識和技術。因此,在執行此過程時,建議有相關的經驗和對底層系統有深入的了解。

總結

從內存轉儲中恢復64位ELF可執行文件需要以下步驟:

  1. 首先,獲取內存轉儲文件。這可以通過使用操作系統提供的工具(如Windows的Task Manager或Linux的GDB)或專用工具(如Volatility)來完成。

  2. 確定內存轉儲文件中的ELF可執行文件。這可以通過檢查文件的頭部信息來完成。ELF文件的頭部通常包括魔數、文件類型和機器架構等信息。

  3. 從內存轉儲文件中提取ELF可執行文件。這可以通過解析文件的節區表和程序頭表來完成。節區表描述了不同節區(如代碼段、數據段)在文件中的位置和大小,而程序頭表則描述了不同的程序段(如可加載的段、動態鏈接段)。

  4. 重建ELF可執行文件的結構。這包括創建文件頭、節區表、程序頭表以及各個節區的內容。通過將這些信息寫入新的文件中,就能夠得到一個完整的ELF可執行文件。

  5. 修復可能損壞的部分。由于從內存轉儲中恢復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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212899.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212899.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212899.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

作業調度算法(含詳細計算過程)和進程調度算法淺析

一.作業調度 作業調度算法需要知道以下公式 周轉時間完成時間 - 到達時間 帶權周轉時間周轉時間/運行時間 注&#xff1a;帶權周轉時間越大&#xff0c;作業&#xff08;或進程&#xff09;越短&#xff1b;帶權周轉時間越小&#xff0c;作業&#xff08;或進程&#xff09;越…

[git] 遠程刪除分支

[git] 遠程刪除分支 1. git刪除遠程分支 git push origin --delete [branch_name]2. 刪除本地分支區別 git branch -d 會在刪除前檢查merge狀態&#xff08;其與上游分支或者與head&#xff09;。git branch -D 是git branch --delete --force的簡寫&#xff0c;它會直接刪除…

Redis生產實戰-Redis集群故障探測以及降級方案設計

Redis 集群故障探測 在生產環境中&#xff0c;如果 Redis 集群崩潰了&#xff0c;那么會導致大量的請求打到數據庫中&#xff0c;會導致整個系統都崩潰&#xff0c;所以系統需要可以識別緩存故障&#xff0c;限流保護數據庫&#xff0c;并且啟動接口的降級機制 降級方案設計 …

《C++20設計模式》---原型模式學習筆記代碼

C20設計模式 第 4 章 原型模式學習筆記筆記代碼 第 4 章 原型模式 學習筆記 筆記代碼 #include<iostream> #include<string>// #define VALUE_OF_ADDRESS // PP_4_2_1 (no define: PP_4_2_2) namespace PP_4_2 {class Address{public:std::string street;std::st…

《C++20設計模式》學習筆記---原型模式

C20設計模式 第 4 章 原型模式4.1 對象構建4.2 普通拷貝4.3 通過拷貝構造函數進行拷貝4.4 “虛”構造函數4.5 序列化4.6 原型工廠4.7 總結4.8 代碼 第 4 章 原型模式 考慮一下我們日常使用的東西&#xff0c;比如汽車或手機。它們并不是從零開始設計的&#xff0c;相反&#x…

超過 50% 的內部攻擊使用特權提升漏洞

特權提升漏洞是企業內部人員在網絡上進行未經授權的活動時最常見的漏洞&#xff0c;無論是出于惡意目的還是以危險的方式下載有風險的工具。 Crowdstrike 根據 2021 年 1 月至 2023 年 4 月期間收集的數據發布的一份報告顯示&#xff0c;內部威脅正在上升&#xff0c;而利用權…

基于SSM的劇本殺預約系統的設計與實現

末尾獲取源碼 開發語言&#xff1a;Java Java開發工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 數據庫&#xff1a;MySQL5.7和Navicat管理工具結合 服務器&#xff1a;Tomcat8.5 開發軟件&#xff1a;IDEA / Eclipse 是否Maven項目&#xff1a;是 目錄…

【第三屆】:“玄鐵杯”RISC-V應用創新大賽(基于yolov5和OpenCv算法 — 智能警戒哨兵)

文章目錄 前言 一、智能警戒哨兵是什么&#xff1f; 二、方案流程圖 三、硬件方案 四、軟件方案 五、演示視頻鏈接 總結 前言 最近參加了第三屆“玄鐵杯”RISC-V應用創新大賽&#xff0c;我的創意題目是基于 yolov5和OpenCv算法 — 智能警戒哨兵 先介紹一下比賽&#xf…

docker容器配置MySQL與遠程連接設置(純步驟)

以下為ubuntu20.04環境&#xff0c;默認已安裝docker&#xff0c;沒安裝的網上隨便找個教程就好了 拉去mysql鏡像 docker pull mysql這樣是默認拉取最新的版本latest 這樣是指定版本拉取 docker pull mysql:5.7查看已安裝的mysql鏡像 docker images通過鏡像生成容器 docke…

大數據HCIE成神之路之數據預處理(1)——缺失值處理

缺失值處理 1.1 刪除1.1.1 實驗任務1.1.1.1 實驗背景1.1.1.2 實驗目標1.1.1.3 實驗數據解析 1.1.2 實驗思路1.1.3 實驗操作步驟1.1.4 結果驗證 1.2 填充1.2.1 實驗任務1.2.1.1 實驗背景1.2.1.2 實驗目標1.2.1.3 實驗數據解析 1.2.2 實驗思路1.2.3 實驗操作步驟1.2.4 結果驗證 1…

【STM32】ADC模數轉換器

1 ADC簡介 ADC&#xff08;Analog-Digital Converter&#xff09;模擬-數字轉換器 ADC可以將引腳上連續變化的模擬電壓轉換為內存中存儲的數字變量&#xff0c;建立模擬電路到數字電路的橋梁 STM32是數字電路&#xff0c;只有高低電平&#xff0c;沒有幾V電壓的概念&#xff…

安裝 DevEco Studio 后不能用本地 Node.js 打開

安裝 DevEco Studio 后第一次打開時&#xff0c;不能用本地 Node.js 打開 答&#xff1a;因為本地 Node.js 文件夾名字中有空格 Node.js路徑只能包含字母、數字、“。”、“_”、“-”、“:”和“V” 解決方法&#xff1a; 1.修改文件夾名稱 2.重新下載 注意&#xff1a;找一…

Qt 通過命令行編譯程序

前言 從服務器拉代碼到編譯成可執行文件一個腳本解決問題。使用的項目文件見上一個文章 Qt生成動態鏈接庫并使用動態鏈接庫 腳本代碼 為了方便易懂這是一個很簡單的Qt編譯腳本 call E:\vs2015\VC\vcvarsall.bat x86 rmdir /s /q my-project git clone gitgitee.com:wenbai1…

【CF245H】Queries for Number of Palindromes(字符串區間dp)

Queries for Number of Palindromes - 洛谷 # Queries for Number of Palindromes ## 題面翻譯 題目描述 給你一個字符串s由小寫字母組成&#xff0c;有q組詢問&#xff0c;每組詢問給你兩個數&#xff0c;l和r&#xff0c;問在字符串區間l到r的字串中&#xff0c;包含多少…

1-3算法基礎-標準模板庫STL

1.pair pair用于存儲兩個不同類型的值&#xff08;元素&#xff09;作為一個單元。它通常用于將兩個值捆綁在一起&#xff0c;以便一起傳遞或返回。 #include <iostream> #include <utility> using namespace std; int main() {pair<int, string> person m…

TailwindCSS 多主題色配置

TailwindCSS 多主題色配置 現在大多數網站都支持主題色變換&#xff0c;比如切換深色模式。那么我們該如何進行主題色配置呢&#xff1f; tailwind dark tailwind 包含一個 dark變體&#xff0c;當啟用深色模式時&#xff0c;可以為網站設置不同樣式 <div class"bg-whi…

ThingWorx 9.2 Windows安裝

參考官方文檔安裝配置 1 PostgreSQL 13.X 2 Java, Apache Tomcat, and ThingWorx PTC Help Center 參考這里安裝 數據庫 C:\ThingworxPostgresqlStorage 設置為任何人可以full control 數據庫初始化 pgadmin4 創建用戶twadmin并記錄口令password Admin Userpostgres Thin…

漏刻有時百度地圖API實戰開發(9)Echarts使用bmap.js實現軌跡動畫效果

Bmap.js是Echarts和百度地圖相結合開發的一款JavaScript API&#xff0c;它可以幫助用戶在web應用中獲取包括地圖中心點、地圖縮放級別、地圖當前視野范圍、地圖上標注點等在內的地圖信息&#xff0c;并且支持在地圖上添加控件&#xff0c;提供包括智能路線規劃、智能導航(駕車…

C# WPF上位機開發(通訊協議的編寫)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】 作為上位機&#xff0c;它很重要的一個部分就是需要和外面的設備進行數據溝通的。很多時候&#xff0c;也就是在這個溝通的過程當中&#xff0c;上…

PyQt下使用OpenCV實現人臉檢測與識別

背景&#xff1a; 一 數字圖像處理與識別警務應用模型 基于前期所學知識&#xff0c;與公安實踐相結合&#xff0c;綜合設計數字圖像處理與識別警務應用模型,從下列4個研究課題中選擇2個進行實驗實現&#xff1a;圖像增強與復原、人臉檢測與識別、虹膜內外圓檢測與分割、車牌…