在 android 上要反組譯 c/c++ 的執行檔是相當麻煩的,不過常用的 Linux 工具可以提供我們一個思考方向:nm, objdump, 甚至是 gdb。
寫在前面:其實是有你看這篇時心中想要的工具,不過好用的是要錢的,不要錢的我自己都編譯不了,但是還是提供一下資訊供大家參考: boomerang
objdump 是很常見的,網路上也有相當多的文件,有興趣的人可以自行找找看,或是參考這篇
我舉個簡單的例子來說明 nm, objdump
#includeint sum(int a, int b){ return a+b; } int main(int argc, char* argv[]) { int i, j; i = 10; j = i*2; printf ("Hello, %d + %d == %d\n", i, j, sum(i, j)); return 0; }
編譯: 通常拿到執行檔的時候,它的編譯選項是不可能加 -g 的(gcc -g hello.c -o hello),如果有的話,算是你幸運,可以直接下 objdump -S hello 就可以得到下面的結果(大部份都略過):
00000000004004f4: #include int sum(int a, int b){ 4004f4: 55 push %rbp 4004f5: 48 89 e5 mov %rsp,%rbp 4004f8: 89 7d fc mov %edi,-0x4(%rbp) 4004fb: 89 75 f8 mov %esi,-0x8(%rbp) return a+b; 4004fe: 8b 45 f8 mov -0x8(%rbp),%eax 400501: 8b 55 fc mov -0x4(%rbp),%edx 400504: 8d 04 02 lea (%rdx,%rax,1),%eax } 400507: c9 leaveq 400508: c3 retq
上面可以見到 sum() 的程式碼......其實 main() 的也有,往下找....
0000000000400509不過,問題是,我們拿到的執行檔應該是沒有加 -g 這個 debug 選項的,事實上,在沒有更好的工具時,有一個方法可以「觀察」,就是 nm -s hello (PS: 假設 gcc hello.c -o hello): int main(int argc, char* argv[]) { 400509: 55 push %rbp 40050a: 48 89 e5 mov %rsp,%rbp 40050d: 48 83 ec 20 sub $0x20,%rsp 400511: 89 7d ec mov %edi,-0x14(%rbp) 400514: 48 89 75 e0 mov %rsi,-0x20(%rbp) int i, j; i = 10; 400518: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) j = i*2; 40051f: 8b 45 fc mov -0x4(%rbp),%eax 400522: 01 c0 add %eax,%eax 400524: 89 45 f8 mov %eax,-0x8(%rbp) printf ("Hello, %d + %d == %d\n", i, j, sum(i, j)); 400527: 8b 55 f8 mov -0x8(%rbp),%edx 40052a: 8b 45 fc mov -0x4(%rbp),%eax 40052d: 89 d6 mov %edx,%esi 40052f: 89 c7 mov %eax,%edi 400531: e8 be ff ff ff callq 4004f4 400536: 89 c1 mov %eax,%ecx 400538: b8 4c 06 40 00 mov $0x40064c,%eax 40053d: 8b 55 f8 mov -0x8(%rbp),%edx 400540: 8b 75 fc mov -0x4(%rbp),%esi 400543: 48 89 c7 mov %rax,%rdi 400546: b8 00 00 00 00 mov $0x0,%eax 40054b: e8 a0 fe ff ff callq 4003f0 return 0; 400550: b8 00 00 00 00 mov $0x0,%eax }
... 0000000000600e24 d __init_array_end 0000000000600e24 d __init_array_start 00000000004005f0 T __libc_csu_fini 0000000000400560 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 0000000000601020 A _edata 0000000000601030 A _end 0000000000400638 T _fini 00000000004003c8 T _init 0000000000400410 T _start 000000000040043c t call_gmon_start 0000000000601020 b completed.6557 0000000000000000 a crtstuff.c 0000000000000000 a crtstuff.c 0000000000601010 W data_start 0000000000601028 b dtor_idx.6559 00000000004004d0 t frame_dummy 0000000000000000 a hello.c 0000000000400509 T main U printf@@GLIBC_2.2.5 00000000004004f4 T sum
此時可以見到程式中定義了 main, 使用(呼叫)了 printf(), 及 sum()
PS: 上面有很多底線開頭的符號,都可以直接略過,因為那都是「內建」符號
講到這邊,有人可能要大聲抗議,這怎麼叫反組譯啊!!!!我可沒說反組譯回 C/C++ source code.....若你能讀懂組合語言的話.....當然,正常程式寫作上,光函式名稱, 也就是符號表就常常暗示了很大的一部份程式運作了。
還有一個命令叫 strings, 上面無 -g 的 hello 也可以用 strings hello 得到下面結果:
/lib64/ld-linux-x86-64.so.2 __gmon_start__ libc.so.6 printf __libc_start_main GLIBC_2.2.5 fff. =p l$ L t$(L |$0H Hello, %d + %d == %d看到最後一行的 hello, %d + %d == %d 了嗎?那就是出現在程式中的字串
最後最後,提供一個 debug 的工具叫 strace,我會提它是因為它也是 busybox 有提供的工具。
strace 的功能是「追蹤命令執行時所呼叫到的系統函式」,這一段說明恐怕你不容易了解,但是能追蹤系統呼叫通常也是解密的最重要線索,用法如下:
strace ./hello
PS: strace 後面要接的是執行的命令,因此是 ./hello, 而不是 hello
PS2: strace 還有一些參數,例如把輸入導到檔案之類的,可以自行上網查。
0 意見:
張貼留言