Linux Driver Reverse Engineering
出自Wiki
目錄 |
FreedomHEC
FreedomHEC Taipei 2009[1] 2009/06/11CIH<Software Magician>
EMAIL:??????(AT)gmail(DOT)com
GIGA-BYTE TECHNOLOGY CO., LTD.
故事的開始
看一下,下面這個C檔案(部分擷取)。int my_init(void) { my_test(); my_dev->reg.data1 = DATA1; my_dev->reg.data2 = DATA2; my_dev->reg.data3 = DATA3; my_dev->reg.data4 = DATA4; initial_irq(IRQ_WLAN, my_isr, 0, MY_NAME, my_argv); }
開始想方法
我:老闆我們去買公板上的solution,好不好?裡面就有完整的code。老闆:拿你一整年的薪水去買,就可以。
我:==_==|||
這個driver要從無開始寫出來,不知道要寫到民國幾年?所以,那只有暴力型破解硬幹了。我叫公司買一台那個網通產品。決定,在這2 MB flash的binary裡面,去找DATA1~DATA4。
P.S 以上的故事的描述,用極誇張的方式去捏造,不用當真。
關於破解
這類的破解,一定可以破解,只是時間長短。可能不到一個小時,可能一天,可能一兩個禮拜。若是遇到軟體故意防破保護,甚至要好幾個月,或更久。在還沒有去try的情況,是很難評估所需的時間,因此,必須花一兩天去try,才能做時間的評估。破解的3個重點:
- 能力與經驗
- 耐心與耐力
- 運氣
結果呢~在破解的過程中,運氣太好了,超出我的預計,不到一天的時間,就全部找到,並且把C檔案欠缺的東西,全部填回去。
尋找Flash裡面的資料
- 方法一
- 若有Flash的燒錄器,同時有適合socket的話。就把flash拔掉,用燒入器把Flash內容讀出來。
- 方法二
- NOR Flash
- Minicom可以透過UART連上板子
- 可以下dump memory的指令
- 查SOC Memory map,把NOR flash的內容全部dump出來
- 方法三
- 透過JTAG把flash讀出來
在此Case,因為SOC沒有JTAG pin,所以只能採用方法一、二。哪種方法都可以,但是最後要注意的是,要透過任何方式(UART、USB),想辦法得到開機所有訊息。
若是運氣好,WLAN的debug訊息有印出來,那就可以找到相對映flash的位址,反組譯後,就可以查到WLAN driver的組合語言。
結果,運氣超級不好。沒有WLAN的訊息。更慘的是,幾乎沒有debug message。只能下簡單幾個command,去write memory、dump memory。因此,我唯一的訊息,就是command的說明。
最後情況:慘
在flash image裡面,找不到command說明。這代表一件事,這個image有做過處理,或是壓縮起來。
宣告失敗!!!
ARM組合語言
在這個板子,CPU是ARM7 TDMI,NO-MMU。對ARM組語完全不懂,是很難做逆向破解。所以我們很簡單介紹ARM組合語言。ARM的C function,在組合語言裡面,如何傳遞參數(參考資料:Procedure Call Standard for the ARM Architecture p.15, ARM Assembly Language Programming 的 Index)[2]
ARM的C function,在組合語言裡面,如何傳遞參數
$ cat g.cxxx(int a1, int a2, int a3, int a4, int a5, int a6) { } main() { asm("nop"); asm("nop"); asm("nop"); /****************/ /* 用夾擊法 */ xxx(1,2,3,4,5,6); /****************/ asm("nop"); asm("nop"); asm("nop"); }
- $ arm-elf-gcc g.c -S
- $ cat g.s
nop nop nop mov r3, #5 str r3, [sp, #0] ==> 參數5 stack mov r3, #6 str r3, [sp, #4] ==> 參數6 stack mov r0, #1 ==> 參數1 r0 mov r1, #2 ==> 參數2 r1 mov r2, #3 ==> 參數3 r2 mov r3, #4 ==> 參數4 r3 bl xxx nop nop nop
ARM的C function,進入、返回,的組合語言
$ cat g.cxxx() {} /******************************/ /* 用xxx和nop夾擊function進入 */ main() { asm("nop"); asm("nop"); /******************************/ asm("nop"); /* 用nop和yyy夾擊function返回 */ } yyy() {} /******************************/
- $ arm-elf-gcc g.c -o g
- $ arm-elf-objdump -d g [3]
0x00008218 <xxx>: 0x8218: e1a0c00d mov ip, sp 0x821c: e92dd800 stmdb sp!, {fp, ip, lr, pc} 0x8220: e24cb004 sub fp, ip, #4 ; 0x4 0x8224: e1a00003 mov r0, r3 0x8228: e89da800 ldmia sp, {fp, sp, pc} ---------------------------------------------------------------------- /*****************************/ 0x0000822c <main>: /* C function enter */ 0x822c: e1a0c00d mov ip, sp 0x8230: e92dd800 stmdb sp!, {fp, ip, lr, pc} ==> 這行是重點 0x8234: e24cb004 sub fp, ip, #4 ; 0x4 /*****************************/ ---------------------------------------------------------------------- 0x8238: e1a00000 nop (mov r0,r0) 0x823c: e1a00000 nop (mov r0,r0) 0x8240: e1a00000 nop (mov r0,r0) ---------------------------------------------------------------------- /*****************************/ /* C function exit */ 0x8244: e1a00003 mov r0, r3 0x8248: e89da800 ldmia sp, {fp, sp, pc} ==> 這行是重點 /*****************************/ ---------------------------------------------------------------------- 0x0000824c <yyy>: 0x824c: e1a0c00d mov ip, sp 0x8250: e92dd800 stmdb sp!, {fp, ip, lr, pc} 0x8254: e24cb004 sub fp, ip, #4 ; 0x4 0x8258: e1a00003 mov r0, r3 0x825c: e89da800 ldmia sp, {fp, sp, pc}
ARM CPU中斷向列表
在0x00000000或是0xffff00000x00000000 Reset 0x00000004 Undefined instruction 0x00000008 Software interrupt 0x0000000C Abort(prefetch) 0x00000010 Abort(data) 0x00000014 Reserved 0x00000018 IRQ 0x0000001C FIQ
決定去RAM尋找
全部8MB怎麼找?
花幾分鍾想一下,再繼續往下看...
我們面臨到的問題就是:
- 如何把裡面的RAM抓出來?
- 如何反組譯變成ARM組合語言?
最重要的一個問題就是,怎麼找?大海撈針嗎?
Dump RAM裡面的資料
> d 0x00000000 00000000 00 00 9f ef dd 00 00 ea 10 f4 9f e5 bb 00 00 ea |................| 00000010 9a 00 00 ea fa 00 00 ea 78 00 00 ea f7 00 00 ea |........x.......| 00000020 02 00 00 ea 18 28 6f 01 00 00 00 00 3c e9 07 00 |.....(o.....<...| ...... 000000d0 0d 3e b5 e8 0d 3e a1 e8 0d 3e b5 e8 0d 3e a1 e8 |.>...>...>...>..| 000000e0 0d 3e b5 e8 0d 3e a1 e8 08 00 55 e1 f5 ff ff ba |.>...>....U.....| 000000f0 00 00 a0 e3 07 10 a0 e1 04 f0 a0 e1 00 00 00 00 |................| > d 0x00001000 > d 0x00002000 ...... > d 0x007fff00 007fff00 54 ff 7f 00 10 ff 7f 00 78 1f 04 00 60 1b 04 00 |T.......x...`...| 007fff10 00 e0 7f 00 f4 01 00 00 00 00 00 00 68 ff 7f 00 |............h...| 007fff20 05 00 00 00 00 00 00 00 00 d0 6f 00 80 00 00 00 |..........o.....| ...... 007fffd0 7c fe 7b 00 04 e0 6f 00 04 00 00 00 00 00 00 00 ||.{...o.........| 007fffe0 00 e0 6f 00 34 fe 7b 00 04 e0 6f 00 24 fe 7b 00 |..o.4.{...o.$.{.| 007ffff0 44 02 79 00 18 28 7a 00 10 00 00 60 03 00 00 00 |D.y..(z....`....|
- $ arm-elf-objcopy -B arm -I binary -O elf32-littlearm xxx.bin xxx.elf
- $ arm-elf-objdump -D -z xxx.elf > xxx
- $ cat xxx
Reset 0x0: swi 0x009f0000 Undefined instruction 0x4: b 380 <_binary_xxx_bin_start+0x380> Software interrupt 0x8: ldr pc, [pc, #1040] ; 420 <_binary_xxx_bin_start+0x420> Abort(prefetch) 0xc: b 300 <_binary_xxx_bin_start+0x300> Abort(data) 0x10: b 280 <_binary_xxx_bin_start+0x280> Reserved 0x14: b 404 <_binary_xxx_bin_start+0x404> IRQ 0x18: b 200 <_binary_xxx_bin_start+0x200> FIQ 0x1c: b 400 <_binary_xxx_bin_start+0x400> 以下省略
怎麼找
在這個8MB的ARM組合語言,怎麼尋找那個C檔案欠缺的DATA1~DATA4?事實上,很簡單。利用一行組合語言,毀掉系統。
若是在WLAN的程式碼,我們硬塞這一行組合語言。當WLAN沒動作的時候,整個系統會正常。但是一但WLAN瞬間運作,若踏入這一行組語,整個系統就會死翹翹,就代表這個地方一定是WLAN的code。
那要塞什麼組合語言?
- mov pc, #0
- pc是Program Counter。寫入0,就是跳到0的位置,在ARM裡面,0是reset的進入點。
- 我沒採用這個方法。因為這是軟體reset,有可能會reset失敗,造成誤判。
- while(1)的組合語言
- 我採用這個方式。一但踏進這裡,整個系統一定會卡住。
main() { asm("nop"); asm("nop"); asm("nop"); while(1); asm("nop"); asm("nop"); asm("nop"); }
- $ arm-elf-gcc g.c -o g
- $ arm-elf-objdump -d g
0x00008218 <main>: 0x8218: e1a0c00d mov ip, sp 0x821c: e92dd800 stmdb sp!, {fp, ip, lr, pc} 0x8220: e24cb004 sub fp, ip, #4 ; 0x4 0x8224: e1a00000 nop (mov r0,r0) 0x8228: e1a00000 nop (mov r0,r0) 0x822c: e1a00000 nop (mov r0,r0) ---------------------------------------------------------------------- 0x8230: eafffffe b 8230 <main+0x18> ; while(1); ---------------------------------------------------------------------- 0x00008234 <atexit>: 0x8234: e1a0c00d mov ip, sp
問:那要從哪裡開始毀掉系統?
答:IRQ的進入點。
當WLAN的中斷產生時,一定先從IRQ進入,最後會跑到WLAN的ISR。所以我們就從IRQ進入點,追到WLAN ISR。
問:哪些點要毀掉?
答:寫入pc register、所有判斷式branch指令(例如:beq、bne、bgt等等)。
這個系統有提供Write Memory的command,讓我們把中斷向量表IRQ毀掉吧。
> e 0x00000018 0xeafffffe
關於IRQ
儘可能把所有周邊IRQ disable,最後應該只會有System timer、UART、WLAN的IRQ發生。從IRQ中斷進入點開始追
0x200: ldr sp, [pc, #540] ; 424 <_binary_xxx_bin_start+0x424> 0x204: sub lr, lr, #4 ; 0x4 0x208: str lr, [sp] 0x20c: mrs lr, SPSR 0x210: str lr, [sp, #4] 0x214: mrs sp, SPSR 0x218: bic sp, sp, #31 ; 0x1f 0x21c: orr sp, sp, #147 ; 0x93 0x220: msr SPSR_c, sp 0x224: and lr, lr, #15 ; 0xf 0x228: ldr lr, [pc, lr, lsl #2] ; lr=*(pc+lr*4)=*(0x230+lr*4); 0x22c: movs pc, lr 0x230: 0x00015840 lr=0 死翹翹 0x234: 0x000155a8 lr=1 系統正常運作 0x238: 0x000155a8 lr=2 系統正常運作 以下省略繼續追
0x15840:死翹翹 sub sp, sp, #72 ; 0x48 0x15844: stmia sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip} 0x15848: ldr r4, [pc, #-112] ; 157e0 <_binary_xxx_bin_start+0x157e0> 0x1584c: add r8, sp, #60 ; 0x3c 0x15850: ldmia r4, {r5, r6, r7} 0x15854: stmia r8, {r5, r6, r7} 0x15858: stmdb r8, {sp, lr}^ 0x1585c: mov fp, #0 ; 0x0 0x15860: mov r5, #-2080374784 ; 0x84000000 0x15864: ldr r6, [r5] 0x15868: mov r0, #0 ; 0x0 0x1586c: tst r6, #1 ; 0x1 0x15870: addeq r0, r0, #1 ; 0x1 0x15874: moveq r6, r6, lsr #1 0x15878: tsteq r0, #8 ; 0x8 0x1587c: beq 1586c <_binary_xxx_bin_start+0x1586c> 系統正常運作 0x15880:死翹翹 teq r0, #8 ; 0x8 0x15884: movne r1, sp 0x15888: subne lr, pc, #48 ; 0x30 0x1588c: bne 161d0 <_binary_xxx_bin_start+0x161d0> 死翹翹 0x15890:正常 mov r8, #0 ; 0x0 0x15894: mov r7, sp, lsr #13 0x15898: mov r7, r7, lsl #13 0x1589c: b 159e4 <_binary_xxx_bin_start+0x159e4>
在這裡開始分叉-找到WLAN的ISR
0x161d0:死翹翹 mov ip, sp 0x161d4: stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc} 0x161d8: sub fp, ip, #4 ; 0x4 0x161dc: mov r5, r0 0x161e0: cmp r5, #7 ; 0x7 0x161e4: mov sl, r1 0x161e8: bgt 16300 <_binary_xxx_bin_start+0x16300> 正常 0x161ec:死翹翹 ldr r8, [pc, #300] ; 16320 <_binary_xxx_bin_start+0x16320> 0x161f0: mov r6, r5, lsl #5 0x161f4: add r7, r6, r8 0x161f8: mov lr, pc 0x161fc: ldr pc, [r7, #4] ; pc=*(0x11f08c+r0*32+4); 0x16200: ldr r0, [pc, #284] ; 16324 <_binary_xxx_bin_start+0x16324> 0x16204: ldr r1, [pc, #284] ; 16328 <_binary_xxx_bin_start+0x16328> 0x16208: ldr r3, [r0, #4] 0x1620c: ldrb r2, [r6, r8] 0x16210: add r3, r3, #1 ; 0x1 0x16214: str r3, [r0, #4] 0x16218: orr r2, r2, #4 ; 0x4 0x1621c: strb r2, [r6, r8] 0x16220: mov r2, r5, lsl #2 0x16224: ldr r3, [r1, r2] 0x16228: mov r9, r0 0x1622c: ldr r4, [r7, #16] 0x16230: add r3, r3, #1 ; 0x1 0x16234: cmp r4, #0 ; 0x0 0x16238: str r3, [r1, r2] 0x1623c: beq 162d4 <_binary_xxx_bin_start+0x162d4> 正常 0x16240:死翹翹 ldrb r3, [r6, r8] 0x16244: mov r6, #0 ; 0x0 0x16248: tst r3, #1 ; 0x1 0x1624c: beq 1625c <_binary_xxx_bin_start+0x1625c> 正常 0x16250:死翹翹 mov r0, r5 0x16254: mov lr, pc 0x16258: ldr pc, [r7, #12] ; pc=*(0x11f08c+r0*32+12); 0x1625c: ldr r3, [r4, #4] 0x16260: tst r3, #536870912 ; 0x20000000 0x16264: bne 16274 <_binary_xxx_bin_start+0x16274> 正常 0x16268:死翹翹 mrs r3, CPSR 0x1626c: bic r3, r3, #128 ; 0x80 0x16270: msr CPSR_c, r3 0x16274: mov r0, r5 0x16278: ldr r3, [r4, #4] 0x1627c: mov r2, sl 0x16280: ldr r1, [r4, #16] 0x16284: orr r6, r6, r3 0x16288: mov lr, pc 0x1628c: ldr pc, [r4] ; pc=*( *(0x11f08c+r0*32+16) );[4] 0x16290: ldr r4, [r4, #20] 0x16294: cmp r4, #0 ; 0x0 0x16298: bne 16274 <_binary_xxx_bin_start+0x16274> 0x1629c: tst r6, #268435456 ; 0x10000000
0x16320: 0011f08c
r0=0 0 0x11f08c: 00000036 4 0x11f090: 0001ae90 8 0x11f094: 0001ae40 12 0x11f098: 0001ae68 16 0x11f09c: 00101140 20 0x11f0a0: 00000000 24 0x11f0a4: 000e8c94 28 0x11f0a8: 0004a370 r0=1 0x11f0ac: 00000030 0x11f0b0: 0001ae90 0x11f0b4: 0001ae40 0x11f0b8: 0001ae68 0x11f0bc: 00000000 0x11f0c0: 00000000 0x11f0c4: 00000000 0x11f0c8: 00000000 r0=2 0x11f0cc: 00000030 0x11f0d0: 0001ae90 0x11f0d4: 0001ae40 0x11f0d8: 0001ae68 0x11f0dc: 00000000 0x11f0e0: 00000000 0x11f0e4: 00000000 0x11f0e8: 00000000 r0=3 0x11f0ec: 00000030 0x11f0f0: 0001ae90 0x11f0f4: 0001ae40 0x11f0f8: 0001ae68 0x11f0fc: 00000000 0x11f100: 00000000 0x11f104: 00000000 0x11f108: 00000000 r0=4 0x11f10c: 00000030 0x11f110: 0001ae90 0x11f114: 0001ae40 0x11f118: 0001ae68 0x11f11c: 00000000 0x11f120: 00000000 0x11f124: 00000000 0x11f128: 00000000 r0=5 0x11f12c: 00000036 0x11f130: 0001ae90 0x11f134: 0001ae40 0x11f138: 0001ae68 0x11f13c: 0017f300 0x11f140: 00000001 0x11f144: 00070dac 0x11f148: 0004a365 r0=6 WLAN ISR 0x11f14c: 00000030 0x11f150: 0001ae90 0x11f154: 0001ae40 0x11f158: 0001ae68 0x11f15c: 00178000 0x11f160: 00000000 0x11f164: 00000000 0x11f168: 00000000 r0=7 Ethernet ISR 0x11f16c: 00000036 0x11f170: 0001ae90 0x11f174: 0001ae40 0x11f178: 0001ae68 0x11f17c: 001d6ac0 0x11f180: 00000000 0x11f184: 00016e28 0x11f188: 0004a352 r0=8 0x11f18c: 00000000 0x11f190: 00000000 0x11f194: 00000000 0x11f198: 00000000 0x11f19c: 00000000 0x11f1a0: 00000000 0x11f1a4: 00000000 0x11f1a8: 00000000
0x1ae90: mov ip, sp 0x1ae94: stmdb sp!, {fp, ip, lr, pc} 0x1ae98: sub fp, ip, #4 ; 0x4 0x1ae9c: mov r3, #-2147483636 ; 0x8000000c 0x1aea0: add r3, r3, #67108864 ; 0x4000000 0x1aea4: ldr r2, [r3] 0x1aea8: mov r1, #1 ; 0x1 0x1aeac: orr r2, r2, r1, lsl r0 0x1aeb0: str r2, [r3] 0x1aeb4: ldmdb fp, {fp, sp, pc}
0x1ae68: mov ip, sp 0x1ae6c: stmdb sp!, {fp, ip, lr, pc 0x1ae70: sub fp, ip, #4 ; 0x4 0x1ae74: mov r3, #-2147483640 ; 0x80000008 0x1ae78: add r3, r3, #67108864 ; 0x4000000 0x1ae7c: ldr r2, [r3] 0x1ae80: mov r1, #1 ; 0x1 0x1ae84: orr r2, r2, r1, lsl r0 0x1ae88: str r2, [r3] 0x1ae8c: ldmdb fp, {fp, sp, pc}
0x178000: 0007b150 WLAN ISR 0xf8b4: 0007b150 WLAN ISR 0x1d6ac0: 000756ec Ethernet ISR 0x756dc: 000756ec Ethernet ISR
0x7b150: mov ip, sp ; WLAN ISR 0x7b154: stmdb sp!, {r4, r5, r6, r7, fp, ip, lr, pc} 0x7b158: mov r6, r1 0x7b15c: ldr r3, [r6, #192] 0x7b160: sub fp, ip, #4 ; 0x4 0x7b164: ldr r5, [r3, #40]
0xf814: ldr r1, [pc, #152] ; f8b4 <_binary_123_start+0x68b4> 0xf818: mov r0, #6 ; 0x6 0xf81c: ldr r3, [pc, #148] ; f8b8 <_binary_123_start+0x68b8> 0xf820: mov r2, r6 0xf824: str r5, [sp] 0xf828: bl 16410 <_binary_123_start+0xd410> initial_irq(IRQ_WLAN, my_isr, 0, MY_NAME, my_argv); 0xf8b8: 0xfff0 0xfff0: 'wlan'
0x756ec: mov ip, sp ; Ethernet ISR 0x756f0: stmdb sp!, {r4, r5, r6, r7, fp, ip, lr, pc} 0x756f4: sub fp, ip, #4 ; 0x4 0x756f8: mov r3, #180 ; 0xb4 0x756fc: add r3, r3, #-2013265920 ; 0x88000000 0x75700: mvn r2, #-268435456 ; 0xf0000000
0x7564c: ldr r1, [pc, #136] ; 756dc <_binary_xxx_bin_start+0x756dc> 0x75650: str r2, [r3] 0x75654: ldr r3, [pc, #132] ; 756e0 <_binary_xxx_bin_start+0x756e0> 0x75658: mov r0, #7 ; 0x7 0x7565c: ldr ip, [r3] 0x75660: mov r4, #0 ; 0x0 0x75664: ldr r3, [pc, #120] ; 756e4 <_binary_xxx_bin_start+0x756e4> 0x75668: mov r2, r4 0x7566c: str ip, [sp] 0x75670: bl 16410 <_binary_xxx_bin_start+0x16410>
找到了
int my_init(void) { my_test(); my_dev->reg.data1 = DATA1; my_dev->reg.data2 = DATA2; my_dev->reg.data3 = DATA3; my_dev->reg.data4 = DATA4; initial_irq(IRQ_WLAN, my_isr, 0, MY_NAME, my_argv); }
0xf7ec: bl d7c14 <_binary_123_start+0xcec14> my_test(); 0xf7f0: mov r3, #2336 ; 0x920 0xf7f4: add r3, r3, #10 ; 0xa 0xf7f8: strh r3, [r4, #60] my_dev->reg.data1 = DATA1; my_dev->reg.data1 = 0x92a; 0xf7fc: mov r3, #2432 ; 0x980 0xf800: strh r3, [r4, #62] my_dev->reg.data2 = DATA2; my_dev->reg.data2 = 0x980; 0xf804: mov r3, #3 ; 0x3 0xf808: strh r3, [r4, #64] my_dev->reg.data3 = DATA3; my_dev->reg.data3 = 0x3; 0xf80c: mov r3, #15 ; 0xf 0xf810: strh r3, [r4, #66] my_dev->reg.data4 = DATA4; my_dev->reg.data4 = 0xf; 0xf814: ldr r1, [pc, #152] ; f8b4 <_binary_123_start+0x68b4> 0xf818: mov r0, #6 ; 0x6 0xf81c: ldr r3, [pc, #148] ; f8b8 <_binary_123_start+0x68b8> 0xf820: mov r2, r6 0xf824: str r5, [sp] 0xf828: bl 16410 <_binary_123_start+0xd410> initial_irq(IRQ_WLAN, my_isr, 0, MY_NAME, my_argv);
註解
- ↑
http://www.oss.org.tw/dokuwiki/doku.php?id=freedomhec2009
http://freedomhectaipei.pbworks.com
http://www.linuxdevices.com/news/NS8928401338.html
http://lwn.net/SubscriberLink/337314/b0d15935c07fbe81 - ↑ scott補充的
- ↑ 為了方便閱讀,這裡所有objdump的輸出內容,有做一點小修改。所以與最原始的內容,會有些許差異。
- ↑ 簡化過的結果。真實情況是,會執行r4這個List裡面所有的isr。簡單描述如下:
struct XXX { ISR isr; XXX *next; }; XXX *r4 = 0x00178000;