二進制分析屬於信息安全業界逆向工程中的一種技術,經過利用可執行的機器代碼(二進制)來分析應用程序的控制結構和運行方式,它有助於信息安全從業人員更好地分析各類漏洞、病毒以及惡意軟件,從而找到相應的解決方案。html
在本文中,你將會更加深刻地瞭解程序如何映射到磁盤並加載到內存中。程序執行的內部邏輯比較複雜,對於有抱負的二進制黑客、逆向工程師或者普通的程序員來講,對二進制格式的理解將會是很是寶貴的知識財富。在Linux中,程序就是以ELF二進制的格式執行的。linux
像許多Linux反編譯工程師同樣,我也是先了解ELF的說明規範,而後把學到的內容以一種創造性的方式進行應用,經過這樣的方式來進行ELF的學習。程序員
一個ELF文件能夠被標記爲如下幾種類型之一。算法
ET_NONE
:未知類型。這個標記代表文件類型不肯定,或者還未定義。編程
ET_REL
:重定位文件。ELF類型標記爲relocatable意味着該文件被標記爲了一段可重定位的代碼,有時也稱爲目標文件。可重定位目標文件一般是還未被連接到可執行程序的一段位置獨立的代碼(position
independent code)。在編譯完代碼以後一般能夠看到一個.o
格式的文件,這種文件包含了建立可執行文件所須要的代碼和數據。數組
ET_EXEC
:可執行文件。ELF類型爲executable,代表這個文件被標記爲可執行文件。這種類型的文件也稱爲程序,是一個進程開始執行的入口。安全
ET_DYN
:共享目標文件。ELF類型爲dynamic,意味着該文件被標記爲了一個動態的可連接的目標文件,也稱爲共享庫。這類共享庫會在程序運行時被裝載並連接到程序的進程鏡像中。數據結構
ET_CORE
:核心文件。在程序崩潰或者進程傳遞了一個SIGSEGV信號(分段違規)時,會在覈心文件中記錄整個進程的鏡像信息。可使用GDB讀取這類文件來輔助調試並查找程序崩潰的緣由。異步
使用readelf–h
命令查看ELF文件,能夠看到原始的ELF文件頭。ELF文件頭從文件的0偏移量開始,是除了文件頭以後剩餘部分文件的一個映射。文件頭主要標記了ELF類型、結構和程序開始執行的入口地址,並提供了其餘ELF頭(節頭和程序頭)的偏移量,稍後會細講。一旦理解了節頭和程序頭的含義,就容易理解文件頭了。經過查看Linux的ELF(5)手冊,能夠了解ELF頭部的結構:ide
#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;複製代碼
在本文的後續內容中,咱們會用一個簡單的C程序來展現如何利用上面結構中的字段映射一個ELF文件。咱們先繼續介紹現存的其餘類型的ELF頭。
ELF程序頭是對二進制文件中段的描述,是程序裝載必需的一部分。段(segment)是在內核裝載時被解析的,描述了磁盤上可執行文件的內存佈局以及如何映射到內存中。能夠經過引用原始ELF頭中名爲e_phoff
(程序頭表偏移量)的偏移量來獲得程序頭表,如前面ElfN_Ehdr
結構中所示。
下面討論5種常見的程序頭類型。程序頭描述了可執行文件(包括共享庫)中的段及其類型(爲哪一種類型的數據或代碼而保留的段)。首先,咱們來看一下Elf32_Phdr
的結構,它構成了32位ELF可執行文件程序頭表的一個程序頭條目。
在本書的後續內容中有時還會引用Phdr的程序頭結構。
下面是Elf32_Phdr
結構體:
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_Phdr;複製代碼
一個可執行文件至少有一個PT_LOAD
類型的段。這類程序頭描述的是可裝載的段,也就是說,這種類型的段將被裝載或者映射到內存中。
例如,一個須要動態連接的ELF可執行文件一般包含如下兩個可裝載的段(類型爲PT_LOAD
):
存放程序代碼的text段;
存放全局變量和動態連接信息的data段。
上面的兩個段將會被映射到內存中,並根據p_align
中存放的值在內存中對齊。建議讀者閱讀一下Linux的ELF手冊,以便理解Phdr結構體中全部變量的含義,這些變量描述了段在文件和內存中的佈局。
程序頭主要描述了程序執行時在內存中的佈局。本章稍後會使用Phdr來講明什麼是程序頭,以及如何在反編譯軟件中使用程序頭。
一般將text段(也稱代碼段)的權限設置爲PF_X | PF_R
(讀和可執行)。
一般將data段的權限設置爲PF_W | PF_R
(讀和寫)。
感染了千面人病毒(polymorphic virus)文件的text段或data段的權限可能會被修改,如經過在程序頭的段標記(p_flags
)處增長PF_W
標記來修改text段的權限。
動態段是動態連接可執行文件所特有的,包含了動態連接器所必需的一些信息。在動態段中包含了一些標記值和指針,包括但不限於如下內容:
運行時須要連接的共享庫列表;
全局偏移表(GOT)的地址
——ELF動態連接部分(2.6節)會討論;
重定位條目的相關信息。
表2-1是完整的標記名列表。
表2-1
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
動態段包含了一些結構體,在這些結構體中存放着與動態連接相關的信息。d_tag
成員變量控制着d_un
的含義。
32位ELF文件的動態段結構體以下:
typedef struct{
Elf32_Sword d_tag;
union{
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
extern Elf32_Dyn _DYNAMIC[];複製代碼
本文稍後會繼續對動態連接進行更深刻的探討。
PT_NOTE
類型的段可能保存了與特定供應商或者系統相關的附加信息。下面是標準ELF規範中對PT_NOTE
的定義:
有時供應商或者系統構建者須要在目標文件上標記特定的信息,以便於其餘程序對一致性、兼容性等進行檢查。SHT_NOTE
類型的節(section)和PT_NOTE
類型的程序頭元素就能夠用於這一目的。節或者程序頭元素中的備註信息能夠有任意數量的條目,每一個條目都是一個4字節的目標處理器格式的數組。下面的標籤能夠解釋備註信息的組織結構,不過這些標籤並非規範中的內容。
比較有意思的一點:事實上,這一段只保存了操做系統的規範信息,在可執行文件運行時是不須要這個段的(由於系統會假設一個可執行文件是本地的),這個段成了很容易被病毒感染的一個地方。因爲篇幅限制,就不具體介紹了。更多NOTE段病毒感染相關的信息能夠從vxheavens.com/
瞭解到。
lib/vhe06.html
PT_INTERP段只將位置和大小信息存放在一個以null爲終止符的字符串中,是對程序解釋器位置的描述。例如,/lib/linux-ld.so.2
通常是指動態連接器的位置,也即程序解釋器的位置。
PT_PHDR段保存了程序頭表自己的位置和大小。Phdr表保存了全部的Phdr對文件(以及內存鏡像)中段的描述信息。
能夠查閱ELF(5)手冊或者ELF規範文檔來查看全部的Phdr類型。咱們已經介紹了一些最經常使用的Phdr類型,其中一些對程序執行相當重要,有一些在反編譯時會常常用到。
可使用readelf–l <filename>
命令查看文件的Phdr表:
Elf file type is EXEC (Executable file) Entry point 0x8049a30 There are 9 program headers, starting at offset 52 Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x1622c 0x1622c R E 0x1000
LOAD 0x016ef8 0x0805fef8 0x0805fef8 0x003c8 0x00fe8 RW 0x1000
DYNAMIC 0x016f0c 0x0805ff0c 0x0805ff0c 0x000e0 0x000e0 RW 0x4
NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x016104 0x0805e104 0x0805e104 0x0002c 0x0002c R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x016ef8 0x0805fef8 0x0805fef8 0x00108 0x00108 R 0x1複製代碼
從上面的片斷中,能夠看到可執行程序的入口點,還有剛剛討論的不一樣段的類型。注意看中間部分的PT_LOAD
段,從最左邊的偏移量到最右邊的權限標識和對齊標識。
text段是可讀可執行的,data段是可讀可寫的,這兩個段都有0x1000
(4096)的對齊標識,恰好是32位可執行文件一頁的大小,該標識用於在程序裝載時對齊。
本文摘自於《Linux二進制分析》
本書是目前爲止惟一一本剖析Linux ELF工做機制的圖書,共分爲9章,其內容涵蓋了Linux環境和相關工具、ELF二進制格式、Linux進程追蹤、ELF病毒技術、Linux二進制保護、Linux中的ELF二進制取證分析、進程內存取證分析、擴展核心文件快照技術、Linux/proc/kcore分析等。
本書適合具備必定的Linux操做知識,且瞭解C語言編程技巧的信息安全從業人員閱讀。
目 錄
第1章 Linux環境和相關工具1
1.1 Linux工具1
1.1.1 GDB2
1.1.2 GNU binutils中的objdump2
1.1.3 GNU binutils中的objcopy3
1.1.4 strace3
1.1.5 ltrace4
1.1.6 基本的ltrace命令4
1.1.7 ftrace4
1.1.8 readelf4
1.1.9 ERESI——ELF反編譯系統接口5
1.2 有用的設備和文件6
1.2.1 /proc//maps6
1.2.2 /proc/kcore6
1.2.3 /boot/System.map6
1.2.4 /proc/kallsyms7
1.2.5 /proc/iomem7
1.2.6 ECFS7
1.3 連接器相關環境指針7
1.3.1 LD_PRELOAD環境變量8
1.3.2 LD_SHOW_AUXV環境變量8
1.3.3 連接器腳本9
1.4 總結10
第2章 ELF二進制格式11
2.1 ELF文件類型12
2.2 ELF程序頭14
2.2.1 PT_LOAD14
2.2.2 PT_DYNAMIC——動態段的Phdr15
2.2.3 PT_NOTE17
2.2.4 PT_INTERP17
2.2.5 PT_PHDR17
2.3 ELF節頭18
2.3.1 .text節20
2.3.2 .rodata節20
2.3.3 .plt節21
2.3.4 .data節21
2.3.5 .bss節21
2.3.6 .got.plt節21
2.3.7 .dynsym節21
2.3.8 .dynstr節22
2.3.9 .rel.*節22
2.3.10 .hash節22
2.3.11 .symtab節22
2.3.12 .strtab節23
2.3.13 .shstrtab節23
2.3.14 .ctors和.dtors節23
2.4 ELF符號27
2.4.1 st_name28
2.4.2 st_value28
2.4.3 st_size28
2.4.4 st_other28
2.4.5 st_shndx29
2.4.6 st_info29
2.5 ELF重定位34
2.6 ELF動態連接43
2.6.1 輔助向量44
2.6.2 瞭解PLT/GOT46
2.6.3 重溫動態段49
2.7 編碼一個ELF解析器52
2.8 總結55
第3章 Linux進程追蹤57
3.1 ptrace的重要性57
3.2 ptrace請求58
3.3 進程寄存器狀態和標記60
3.4 基於ptrace的調試器示例61
3.5 ptrace調試器67
3.6 高級函數追蹤軟件75
3.7 ptrace和取證分析75
3.8 進程鏡像重建77
3.8.1 重建進程到可執行文件的挑戰78
3.8.2 重建可執行文件的挑戰78
3.8.3 添加節頭表79
3.8.4 重建過程算法79
3.8.5 在32位測試環境中使用Quenya重建進程81
3.9 使用ptrace進行代碼注入83
3.10 簡單的例子演示覆雜的過程91
3.11 code_inject工具演示92
3.12 ptrace反調試技巧92
3.13 總結94
第4章 ELF病毒技術——Linux/UNIX病毒95
4.1 ELF病毒技術96
4.2 設計ELF病毒面臨的挑戰97
4.2.1 寄生代碼必須是獨立的97
4.2.2 字符串存儲的複雜度99
4.2.3 尋找存放寄生代碼的合理空間100
4.2.4 將執行控制流傳給寄生代碼100
4.3 ELF病毒寄生代碼感染方法101
4.3.1 Silvio填充感染101
4.3.2 逆向text感染106
4.3.3 data段感染108
4.4 PT_NOTE到PT_LOAD轉換感染110
4.5 感染控制流112
4.5.1 直接PLT感染113
4.5.2 函數蹦牀(function trampolines)113
4.5.3 重寫.ctors/.dtors函數指針114
4.5.4 GOT感染或PLT/GOT重定向115
4.5.5 感染數據結構115
4.5.6 函數指針重寫115
4.6 進程內存病毒和rootkits——遠程代碼注入技術115
4.6.1 共享庫注入116
4.6.2 text段代碼注入120
4.6.3 可執行文件注入120
4.6.4 重定位代碼注入——ET_REL注入120
4.7 ELF反調試和封裝技術121
4.7.1 PTRACE_TRACEME技術121
4.7.2 SIGTRAP處理技術122
4.7.3 /proc/self/status技術122
4.7.4 代碼混淆技術123
4.7.5 字符串錶轉換技術124
4.8 ELF病毒檢測和殺毒124
4.9 總結126
第5章 Linux二進制保護127
5.1 ELF二進制加殼器127
5.2 存根機制和用戶層執行128
5.3 保護器存根的其餘用途133
5.4 現存的ELF二進制保護器133
5.4.1 DacryFile——Grugq於2001年發佈134
5.4.2 Burneye——Scut於2002年發佈134
5.4.3 Shiva——Neil Mehta和Shawn Clowes於2003年發佈135
5.4.4 May's Veil——Ryan O'Neill於2014年發佈136
5.5 下載Maya保護的二進制文件142
5.6 二進制保護中的反調試142
5.7 防模擬技術143
5.7.1 經過系統調用檢測模擬144
5.7.2 檢測模擬的CPU不一致144
5.7.3 檢測特定指令之間的時延144
5.8 混淆方法145
5.9 保護控制流完整性145
5.9.1 基於ptrace的攻擊145
5.9.2 基於安全漏洞的攻擊146
5.10 其餘資源147
5.11 總結147
第6章 Linux下的ELF二進制取證分析149
6.1 檢測入口點修改技術150
6.2 檢測其餘形式的控制流劫持154
6.2.1 修改.ctors/.init_array節154
6.2.2 檢測PLT/GOT鉤子155
6.2.3 檢測函數蹦牀158
6.3 識別寄生代碼特徵159
6.4 檢查動態段是否被DLL注入161
6.5 識別逆向text填充感染164
6.6 識別text段填充感染166
6.7 識別被保護的二進制文件170
6.8 IDA Pro175
6.9 總結175
第7章 進程內存取證分析177
7.1 進程內存佈局178
7.1.1 可執行文件內存映射179
7.1.2 程序堆179
7.1.3 共享庫映射180
7.1.4 棧、VDSO和vsyscall180
7.2 進程內存感染181
7.2.1 進程感染工具181
7.2.2 進程感染技術182
7.3 檢測ET_DYN注入184
7.3.1 Azazel:用戶級rootkit檢測184
7.3.2 映射出進程的地址空間184
7.3.3 查找棧中的LD_PRELOAD187
7.3.4 檢測PLT/GOT鉤子188
7.3.5 ET_DYN注入內部原理190
7.3.6 操縱VDSO194
7.3.7 共享目標文件加載195
7.3.8 檢測.so注入的啓發方法196
7.3.9 檢測PLT/GOT鉤子的工具197
7.4 Linux ELF核心文件198
7.5 總結204
第8章 ECFS——擴展核心文件快照技術205
8.1 歷史205
8.2 ECFS原理206
8.3 ECFS入門206
8.3.1 將ECFS嵌入到核心處理器中207
8.3.2 在不終止進程的狀況下使用ECFS快照208
8.4 libecfs——解析ECFS文件的庫208
8.5 readecfs工具209
8.6 使用ECFS檢測被感染的進程210
8.6.1 感染主機進程210
8.6.2 捕獲並分析ECFS快照211
8.6.3 使用readecfs提取寄生代碼215
8.6.4 Azazel用戶級rootkit分析216
8.7 ECFS參考指南224
8.7.1 ECFS符號表重建225
8.7.2 ECFS節頭226
8.7.3 使用ECFS文件做爲常規的核心文件229
8.7.4 libecfs API的使用229
8.8 使用ECFS恢復中斷的進程230
8.9 瞭解更多ECFS相關內容231
8.10 總結232
第9章 Linux/proc/kcore分析233
9.1 Linux內核取證分析和rootkit233
9.2 沒有符號的備份vmlinux234
9.3 探索/proc/kcore和GDB236
9.4 直接修改sys_call_table237
9.4.1 檢測sys_call_table修改238
9.4.2 內核函數蹦牀238
9.4.3 函數蹦牀示例239
9.4.4 檢測函數蹦牀241
9.4.5 檢測中斷處理器修復243
9.5 Kprobe rootkit243
9.6 調試寄存器rootkit——DRR244
9.7 VFS層rootkit244
9.8 其餘內核感染技術245
9.9 vmlinux和.altinstructions修補245
9.9.1 .altinstructions和.altinstr_replace246
9.9.2 arch/x86/include/asm/alternative.h代碼片斷246
9.9.3 使用textify驗證內核代碼完整性247
9.9.4 使用textify檢查sys_call_table247
9.10 使用taskverse查看隱藏進程248
9.11 感染的LKM——內核驅動249
9.11.1 方法一:感染LKM文件——符號劫持249
9.11.2 方法二:感染LKM文件——函數劫持249
9.11.3 檢測被感染的LKM250
9.12 /dev/kmem和/dev/mem250
9.12.1 /dev/mem251
9.12.2 FreeBSD /dev/kmem251
9.13 K-ecfs ——內核ECFS251
9.14 內核黑客工具252
9.14.1 通用的逆向工程和調試253
9.14.2 高級內核劫持/調試接口253
9.14.3 本章提到的論文253
9.15 總結254
本文禁止轉載,如需轉載,請聯繫公衆號異步社區