FreedomHEC
FreedomHEC Taipei 2009[1]
2009/06/11
CIH<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);
}
好幾年前,在我前幾份工作。某天老闆找我說:"某個網通產品(幾乎照抄公板),已經開始賣了"。老闆希望拿這個產品的公板,改porting到Linux。其中WLAN
driver,Linux 100%不support。偏偏這個driver沒有任何sample code、也沒有任何initial
code,完全找不到,要不到,只有拿到這個C檔案(公板上的code)。但是呢、沒有.h檔案。那DATA1~DATA4這些值,根本無法得知?怎麼辦?老闆說:"景氣不好,這個工作你幹不幹?"。我只能回答:"幹"。
開始想方法
我:老闆我們去買公板上的solution,好不好?裡面就有完整的code。
老闆:拿你一整年的薪水去買,就可以。
我:==_==|||
這個driver要從無開始寫出來,不知道要寫到民國幾年?所以,那只有暴力型破解硬幹了。我叫公司買一台那個網通產品。決定,在這2 MB flash的binary裡面,去找DATA1~DATA4。
P.S 以上的故事的描述,用極誇張的方式去捏造,不用當真。
關於破解
這類的破解,一定可以破解,只是時間長短。可能不到一個小時,可能一天,可能一兩個禮拜。若是遇到軟體故意防破保護,甚至要好幾個月,或更久。在還沒有去try的情況,是很難評估所需的時間,因此,必須花一兩天去try,才能做時間的評估。
破解的3個重點:
- 能力與經驗
- 耐心與耐力
- 運氣
在我的作法,花一兩天去try,順便評估可能所需要的時間。若超出這個時間,就放棄。在這個Case,我預計花的時間,假如超過5天以上,就放棄逆向工程的破解。
結果呢~在破解的過程中,運氣太好了,超出我的預計,不到一天的時間,就全部找到,並且把C檔案欠缺的東西,全部填回去。
尋找Flash裡面的資料
- 方法一
- 若有Flash的燒錄器,同時有適合socket的話。就把flash拔掉,用燒入器把Flash內容讀出來。
- 方法二
- NOR Flash
- Minicom可以透過UART連上板子
- 可以下dump memory的指令
- 查SOC Memory map,把NOR flash的內容全部dump出來
- 方法三
在此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.c
xxx(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.c
xxx() {} /******************************/
/* 用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或是0xffff0000
0x00000000 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)的組合語言
- 我採用這個方式。一但踏進這裡,整個系統一定會卡住。
$ cat g.c
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);
註解