看板 C_and_CPP 關於我們 聯絡資訊
到密技 Binary Hacks--駭客秘傳技巧一百招 #72 時, 讓我訝異萬分, 載入/執行一個 object file, 這念頭我想都沒想過, 真的做得到嗎?object file 可以執行?太神奇了 。不過在看完整篇文章後, 我恍然大悟, 原來是這樣阿, 厲害厲害! 這篇文章所需要的知識: 程式設計師的自我修養 - 連結、載入、程式庫 ( http://goo.gl/sqUf81 ) chapter 3, 4, 6, 7。 Binary Hacks--駭客秘傳技巧一百招 #72 看書單就知道很硬, 所以得花點時間才能搞懂。 本程式和 #72 範例使用 bfd library 不同, 我自行 parse elf object 得到所有需要 relocate symbol 的資訊, 並完成 relocation 的動作。 為什麼會有這樣的念頭?其實我原本只想照著範例打完程式碼就好, 不過由於我略懂 elf, 看完文章後, 覺得應該可以透過 parse elf 來處理, 就這樣完成了這個程式。 程式要做的事情就是把 hello.c compile 成 hello.o (gcc -c hello.c), 然後寫另外一 支程式, 載入 hello.o 並執行 hello() 這個 function。在看完 #72 之前, 我根本沒想 過原來可以用這樣的方式去執行 object file, 想出這招的人也太有想像力了。 這招有什麼用呢?你有沒有發現, linux kernel module a.ko 可以用 insmod 載入, 然 後呼叫其中的 function, 我苦思良久, 毫無頭緒, 我認為 linux kernel 的方法和 #72 應該是相同原理。 hello.c 1 #include <stdio.h> 2 3 int p=0x9876; 4 int i=0x1234; 5 6 void func(int *j) 7 { 8 *j = 0x56ef; 9 #ifdef _MSC_VER 10 printf("vc i: %x\n", i); 11 #else 12 printf("gcc/linux i: %x\n", i); 13 #endif 14 return; 15 } 16 17 void hello() 18 { 19 //puts("hello"); 20 func(&i); 21 } 在 gcc 4.4 並不會產生 .rel.eh_frame section, gcc 4.7.2 才會有這個 section, 可 以參考: Exception Frames ( http://goo.gl/jivOjq ), 本範例只有用到 .rel.text。 流程大概是這樣: load elf/coff object to memory. do the relocation. call the object hello function, hello will call func, func will call printf. back to linux shell prompt. 事前準備工作: dump hello.o elf object by readelf disassembly hello.o by objdump dump hex content by hexdump 這就是最下方的三個表的內容, 在這個過程中, 時常需要對照這三個表, 你應該可想像在 我寫這程式時, 終端機有好幾個畫面切來切去, 若你有雙螢幕的話, 會輕鬆不少。 要怎麼開始呢?先來找出要 relocation 的地方: 先看 hello.o.dis 紅色部份, 這五個地方就是需要重定位的部份, 需要填入正確的數值 才能使這兩個 function (func, printf) 正常執行, 其它的就是 function 的參數, 沒 做好 relocation 就會把錯誤的參數傳給 function 了。 對照 table 1 L46~50 就提供了五個資訊來讓我們做這樣的動作。 relocate func: hello.o.dis L26 是 call func, 所以得先要知道 func 的位址, 把 e8 fc ff ff ff 的 值改成 func 的位址才行。 那來找出 func 的位址吧!table 1 L54 ~ 68 可以用來找到 func 位於哪裡, value:0, 它是在 .text section, 而 .text section 的 offset: 0x34 (ref table 1 L25), func offset: 0x34+0 搞定, 只要在加上 hello.o 被載入的記憶體位址, 就是 func address 了。 ex: 若 hello.o 被載入 0x1000, 那 func address: 0x1000+0x34+0 = 0x1034 那把 fc ff ff ff 改成 0x1034 就對了嗎?不是這樣的, 實際上要複雜一點, 它是這樣 的: 假設要 call 0x100 的位址, 實際上的指令是 (0x100, 0x110, 0x115 為記憶體位址) 0x100 ... ... 0x110 call ??? 0x115 nop ??? 是 0x100 - 0x115 = -0x15 -> call -0x15 才會去 call 0x100 的位址。 所以先找出下一個指令的位址: 0x3a 0x0 - 0x3a = 0xffffffc6 (-58) 將 fc ff ff ff 改成 ff ff ff c6 就會正確呼叫 func()。 relocate printf: printf 比較特別, 我直接用 printf_addr = &printf; 將 printf_add 找出來, 這不是 正規作法, 有點 hard code, 其他和 relocate func 一樣。 relocate i: table 1 L46 可以找到 i 位於 symbol table 9th index, i value: 0x4, 屬於 .data section (section index 3), 所以 i offset: .data section offset + 0x4 = 0x70+0x4 = 0x74 (ref: hello.o.hex), 再來就是有兩個地方需要把這個值填入, .text section offset + 0x11, .text section offset + 0x31, 所以在 0x34 + 0x11 = 0x45, 0x34 + 0x31 = 0x65 (ref hello.o.hex), 要填上 i 的位址 (hello.o 的位址 + 0x74)。ex: 假如 hello.o 被載入0x1000, 0x1000+0x45, 0x1000+0x65 的 4 byte 要 換成 0x1000+0x74。 relocate .rodata: 和 i 一樣的作法。 我相信你應該有點混亂了, 這需要點毅力和耐心來對付, 當初我也花了不少心力看, 希望 這篇文章有幫到忙。文章有些沒寫的很清楚的地方可以參考程式設計師的自我修養 chapter 4。 table 1 readelf -a hello.o 1 ELF Header: 2 Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 3 Class: ELF32 4 Data: 2's complement, little endian 5 Version: 1 (current) 6 OS/ABI: UNIX - System V 7 ABI Version: 0 8 Type: REL (Relocatable file) 9 Machine: Intel 80386 10 Version: 0x1 11 Entry point address: 0x0 12 Start of program headers: 0 (bytes into file) 13 Start of section headers: 256 (bytes into file) 14 Flags: 0x0 15 Size of this header: 52 (bytes) 16 Size of program headers: 0 (bytes) 17 Number of program headers: 0 18 Size of section headers: 40 (bytes) 19 Number of section headers: 11 20 Section header string table index: 8 21 22 Section Headers: 23 [Nr] Name Type Addr Off Size ES Flg Lk Inf Al 24 [ 0] NULL 00000000 000000 000000 00 0 0 0 25 [ 1] .text PROGBITS 00000000 000034 00003c 00 AX 0 0 4 26 [ 2] .rel.text REL 00000000 0003a8 000028 08 9 1 4 27 [ 3] .data PROGBITS 00000000 000070 000008 00 WA 0 0 4 28 [ 4] .bss NOBITS 00000000 000078 000000 00 WA 0 0 4 29 [ 5] .rodata PROGBITS 00000000 000078 000011 00 A 0 0 1 30 [ 6] .comment PROGBITS 00000000 000089 000026 01 MS 0 0 1 31 [ 7] .note.GNU-stack PROGBITS 00000000 0000af 000000 00 0 0 1 32 [ 8] .shstrtab STRTAB 00000000 0000af 000051 00 0 0 1 33 [ 9] .symtab SYMTAB 00000000 0002b8 0000d0 10 10 8 4 34 [10] .strtab STRTAB 00000000 000388 00001f 00 0 0 1 35 Key to Flags: 36 W (write), A (alloc), X (execute), M (merge), S (strings) 37 I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) 38 O (extra OS processing required) o (OS specific), p (processor specific) 39 40 There are no section groups in this file. 41 42 There are no program headers in this file. 43 44 Relocation section '.rel.text' at offset 0x3a8 contains 5 entries: 45 Offset Info Type Sym.Value Sym. Name 46 00000011 00000901 R_386_32 00000004 i 47 00000016 00000501 R_386_32 00000000 .rodata 48 00000022 00000b02 R_386_PC32 00000000 printf 49 00000031 00000901 R_386_32 00000004 i 50 00000036 00000a02 R_386_PC32 00000000 func 51 52 There are no unwind sections in this file. 53 54 Symbol table '.symtab' contains 13 entries: 55 Num: Value Size Type Bind Vis Ndx Name 56 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 57 1: 00000000 0 FILE LOCAL DEFAULT ABS hello.c 58 2: 00000000 0 SECTION LOCAL DEFAULT 1 59 3: 00000000 0 SECTION LOCAL DEFAULT 3 60 4: 00000000 0 SECTION LOCAL DEFAULT 4 61 5: 00000000 0 SECTION LOCAL DEFAULT 5 62 6: 00000000 0 SECTION LOCAL DEFAULT 7 63 7: 00000000 0 SECTION LOCAL DEFAULT 6 64 8: 00000000 4 OBJECT GLOBAL DEFAULT 3 p 65 9: 00000004 4 OBJECT GLOBAL DEFAULT 3 i 66 10: 00000000 40 FUNC GLOBAL DEFAULT 1 func 67 11: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf 68 12: 00000028 20 FUNC GLOBAL DEFAULT 1 hello 69 70 No version information found in this file. hello.o.dis 1 2 hello.o: file format elf32-i386 3 4 5 Disassembly of section .text: 6 7 00000000 <func>: 8 0: 55 push %ebp 9 1: 89 e5 mov %esp,%ebp 10 3: 83 ec 18 sub $0x18,%esp 11 6: 8b 45 08 mov 0x8(%ebp),%eax 12 9: c7 00 ef 56 00 00 movl $0x56ef,(%eax) 13 f: 8b 15 00 00 00 00 mov 0x0,%edx 14 15: b8 00 00 00 00 mov $0x0,%eax 15 1a: 89 54 24 04 mov %edx,0x4(%esp) 16 1e: 89 04 24 mov %eax,(%esp) 17 21: e8 fc ff ff ff call 22 <func+0x22> 18 26: c9 leave 19 27: c3 ret 20 21 00000028 <hello>: 22 28: 55 push %ebp 23 29: 89 e5 mov %esp,%ebp 24 2b: 83 ec 18 sub $0x18,%esp 25 2e: c7 04 24 00 00 00 00 movl $0x0,(%esp) 26 35: e8 fc ff ff ff call 36 <hello+0xe> 27 3a: c9 leave 28 3b: c3 ret hello.o.hex 00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............| 00000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 00 01 00 00 00 00 00 00 34 00 00 00 00 00 28 00 |........4.....(.| 00000030 0 // 本文使用 Blog2BBS 自動將Blog文章轉成縮址的BBS純文字 http://goo.gl/TZ4E17 // blog 版本: http://descent-incoming.blogspot.tw/2013/07/dynamic-loaderlinker-0-linuxelf-object.html -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 58.114.136.108 ※ 文章網址: http://www.ptt.cc/bbs/C_and_CPP/M.1396364837.A.B76.html
LPH66:幫縮址 http://ppt.cc/b9wE 04/01 23:45
damody:這招 msvc 也可以? 04/02 08:43
damody:可以的話一堆白爛code都編成 elf 在winodws下面跑XD 04/02 08:44
dirkc:理解。請問通常用途在哪裡? 04/02 11:18
dirkc:在win也可,其實文字檔或影音檔亦可執行,用途是他們有偽裝 04/02 11:29
dirkc:想請教的是這物件檔的方式,通常可以怎麼運用? 04/02 11:30
sunneo:太棒了,弄成像dlopen一樣的話很好用 04/02 14:20
michael0728n:有點威阿!! 04/02 14:31
sunneo:那我覺得你可以join到wine project了 04/02 14:36
suhorng:太酷炫了 04/02 14:50