看到密技 Binary Hacks--駭客秘傳技巧一百招 #72 時, 讓我訝異萬分, 載入/執行一個 object file, 這念頭我想都沒想過, 真的做得到嗎?object file 可以執行?太神奇了。不過在看完整篇文章後, 我恍然大悟, 原來是這樣阿, 厲害厲害!
這篇文章所需要的知識:
看書單就知道很硬, 所以得花點時間才能搞懂。
本程式和 #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 應該是相同原理。
在 gcc 4.4 並不會產生 .rel.eh_frame section, gcc 4.7.2 才會有這個 section, 可以參考:
Exception Frames, 本範例只有用到 .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。
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。