內核級木馬與病毒攻防:Linux可執行文件的ELF格式描述

要想在Linux系統上實現逆向工程,分析,設計或查殺病毒和惡意代碼,你不得不深入掌握其可執行文件的ELF格式,這樣你才能瞭解進程在內存空間的佈局和運行的基本規律,這樣你纔能有針對性的設計有效的病毒或惡意代碼入侵系統。
程序員

ELF文件主要有如下幾種類型,ET_NONE表示該文件的做用未知;ET_REL表示重定向文件或叫目標文件,它們將會被連接並加裝到某個指定的虛擬內存位置,常見的以.o結尾的二進制文件就屬於這種類型。ET_EXEC表示可執行文件,它是由多個.o文件連接起來,能夠被加載到內存進行執行的進程數據文件;ET_DYN表示動態連接庫,它裏面包含一系列向外導出的函數代碼,當進程須要調用其內部函數時會將其加載到內存;最後一種是ET_CORE,它是進程奔潰時產生的二進制信息文件,經過GDB加載該文件能夠查找進程奔潰的緣由。微信

ELF文件的頭部結構能反映出該文件不少重要信息,使用readelf -h能夠讀取指定ELF文件的文件頭,其使用示例以下:

其二進制數據結構以下:數據結構

#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
ElfN_Addr e_entry;
ElfN_Off e_phoff;
ElfN_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
}ELFN_Ehdr

掌握其二進制結構很重要,後面咱們要使用代碼來實現ELF文件的解析。ELF文件中的程序表頭是很重要的數據結構,它用來描述各個段的信息,例如哪裏是代碼段,哪裏是數據段,這些段有多長,要加載到虛擬內存哪一個位置等等。經過程序表頭中的e_phoff能夠得到該表頭在ELF文件中的偏移,其二進制數據結構以下:ide

typedef struct {
uint32_t p_type; (segment type)
Elf32_Off p_offset; (segment offset)
Elf32_Addr p_vaddr; (segment virtual address)
Elf32_Addr p_paddr; (segment physical address)
uint32_t p_filesz; (size of segment in the file)
uint32_t p_memsz; (size of segment in memory)
uint32_t p_flags; (segment flags, I.E execute|read|read)
uint32_t p_align; (segment alignment in memory)
} Elf32_Phd

程序表頭有若干種類型,其中的p_type字段就用來表示該表頭類型。若是ELF文件類型是ET_EXEC,那麼它必須包含類型爲PT_LOAD類型的表頭,這種表頭會告訴系統在如何加載器數據段或代碼段到虛擬內存空間的特定位置,表頭內各個字段的在逆向工程時會有重要做用。若是編譯成的文件是動態連接庫,那麼它的程序表頭類型爲PT_DYNAMIC,表頭中包含不少信息用來告訴系統如何對它進行加載,它一般包含這幾類信息:加載它時所須要的共享鏈接庫;全局偏離表的起始地址,這個表在後面會描述;以及有關重定向入口的信息。動態連接庫對開發病毒和惡意代碼很是重要,在後續章節咱們會深刻研究。函數

ELF格式的可執行文件在概念上由各類」段「組成,例如用於存儲代碼的文本段,用於存儲數據的數據段等,這些段的數量和相關信息就由程序表頭來描述,在加載運行ELF可執行文件時,系統會讀取程序表頭,得到各個段的信息,將段對應的內容加載到內存,這樣可執行文件才能變成能夠運行的進程,咱們能夠使用命令readelf -l 來讀取程序表頭的內容,具體狀況以下:
佈局

接下來咱們看看ELF文件中的段,該結構包含着要加載到內存的代碼和數據,其二進制數據結構以下:ui

typedef struct {
uint32_t sh_name; // offset into shdr string table for shdr name
uint32_t sh_type; // shdr type I.E SHT_PROGBITS
uint32_t sh_flags; // shdr flags I.E SHT_WRITE|SHT_ALLOC
Elf32_Addr sh_addr; // address of where section begins
Elf32_Off sh_offset; // offset of shdr from beginning of file
uint32_t sh_size; // size that section takes up on disk
uint32_t sh_link; // points to another section
uint32_t sh_info; // interpretation depends on section type
uint32_t sh_addralign; // alignment for address of section
uint32_t sh_entsize; // size of each certain entries that may be in
section
} Elf32_Shdr;

咱們審視幾個很是重要的段及其內容。首先是.text段,它包含代碼通過編譯後的可執行二進制指令,.rodata段包含只讀數據,在代碼中寫死的字符串,例如printf(「hello world」)中的」hello world」就包含在該段。.plt段也叫函數連接表,它告訴系統如何將動態連接庫裏面的函數加載到內存以便被進程調用,後面會對其進行深刻研究。.data段用於存儲初始化後的全局變量等數據;.bss段包含未經初始化的全局變量;.got.plt段也叫全局偏移表,它結合.plt段用來告知系統如何加載動態連接庫內部的函數,該段是黑客對進程進行感染或劫持的關鍵入口;、spa

.dynsym段包含了動態連接庫有關的符號信息;.rel.*段包含的信息用於告訴系統將如何變換某些段的虛擬空間地址;.hash段包含一個哈希表,用來加速對符號的查找,所謂符號是編譯器設置的用於記錄代碼中變量類型,取值等信息的數據結構,它屬於編譯原理的內容,有了符號,咱們才能使用調試器對程序進行調試。.symtab對應符號表,它用於指導調試器或系統如何查找調試程序所需的各個符號;.cotrs和.dtors兩個段各自包含一個指針,前者指針指向一段初始化代碼,在進程的main函數執行前必須先執行初始化代碼,後者指向結束代碼,當進程執行結束後,必須執行結束代碼後進程才能徹底被殺掉。由此初始化代碼就是黑客或病毒製造者的主要目標,他們會在這個地方注入一種叫PTRACE_TRACEME的代碼用於阻止調試器掛載到進程上進行調試。.net

咱們能夠經過以下方式查看各類段的信息,首先先將代碼編譯成.o類型的中間文件:gcc -c hello_world.c,而後調用命令readelf -S hello_wolrd.o,所得結果以下圖:設計

本節所描述的信息過於底層,它涉及到編譯原理,系統運行機制等絕大多數程序員在其一輩子的職業生涯中均可能沒有接觸的知識。但所謂黑客,本質上是無限制擴充本身技術能力圈的程序員,所以咱們須要掌握哪些工做上不經常使用,但能幫你探尋到技術核心真相的知識和信息,這就像黑客帝國中莫非是拉尼奧入夥時給出的兩顆藥丸,吞入紅色藥丸他就能理解世界的本質,普通程序員選擇吞下藍色藥丸,而渴望探索和把握技術世界本質的黑客固然要吞下紅色藥丸。

本節的信息枯燥難懂,你看了後會雲裏霧裏。不要緊,本文描述只是給你留個概念,在後續實戰中這些概念才容易被消化和理解。從這裏你必定能體會要想擁有黑客級技術能力的困難之處,你須要有耐心,恆心,探索真相之路永遠是艱辛,但黑客的目的就是主宰技術而不是被技術主宰,那些35歲被裁掉的程序員大多都是吞下藍藥丸並被技術所主宰,因而他們失去了對本身命運的把控力。

本文分享自微信公衆號 - Coding迪斯尼(gh_c9f933e7765d)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索