過年前幾天公司無事,開始做些無關的事情,比如玩玩遊戲,但是我作爲程序員,玩遊戲的總是想找一些捷徑。比如破解下加密方式,發現一些遊戲漏洞等等。
如此,便是反編譯了遊戲的加密SO。用到的工具是IDA 和 Hopper 這倆都行。因爲Mac的IDA 不能變成C。 所以用了下Hopper 還挺好用的。
好了,開始說我遇到的問題:
在靜態反彙編一個SO的過程中,開頭得到這樣一段彙編代碼:
.text:00002770 EXPORT Java_com_happyhour_android_xt_getKey
.text:00002770
.text:00002770 var_2D8 = -0x2D8
.text:00002770 var_2D4 = -0x2D4
.text:00002770 var_2D0 = -0x2D0
.text:00002770 var_2CC = -0x2CC
.text:00002770 var_2C8 = -0x2C8
.text:00002770 var_2C4 = -0x2C4
.text:00002770 var_2C0 = -0x2C0
.text:00002770 var_2BC = -0x2BC
.text:00002770 var_2B8 = -0x2B8
.text:00002770 var_2B4 = -0x2B4
.text:00002770 var_2B0 = -0x2B0
.text:00002770 var_2AC = -0x2AC
.text:00002770 var_2A4 = -0x2A4
.text:00002770 var_294 = -0x294
.text:00002770 var_274 = -0x274
.text:00002770 dest = -0x21C
.text:00002770 var_218 = -0x218
.text:00002770 s = -0x214
.text:00002770 var_1C = -0x1C
.text:00002770
.text:00002770 PUSH {R4-R7,LR}
.text:00002772 MOVS R6, R3
.text:00002774 LDR R3, =(__stack_chk_guard_ptr - 0x277E)
.text:00002776 LDR R4, =0xFFFFFD3C
.text:00002778 MOVS R5, R0
.text:0000277A ADD R3, PC ; __stack_chk_guard_ptr
.text:0000277C LDR R3, [R3] ; __stack_chk_guard
.text:0000277E ADD SP, R4
.text:00002780 LDR R1, [R3]
.text:00002782 STR R3, [SP,#0x2D8+var_2AC]
.text:00002784 STR R1, [SP,#0x2D8+var_1C]
.text:00002786 CMP R6, #0
.text:00002788 BNE loc_278C
.text:0000278A B loc_2A64
前面都好理解啊,就是到哪個__stack_chk_guard_ptr
很難理解是什麼意思。那這個地址放的是什麼吶,在Hopper裏面看到地址放的是0。
那下面那幾條語句很難理解啊,把0存入了一個內存,然後把地址爲0的內容放入了內存中。 這時候我懷疑這個地址是在Java 加載SO的時候動態放進去的。
查了下,果然。這是個堆棧保護區。在程序開始的時候把它的值存起來,然後在程序結束的時候,再去看一下里面的值和保存起來的值是不是一致,如果一致的話。說明該程序沒有被溢出攻擊,如果不一致,拋出段錯誤,也就是segment deafult 。
爲了印證這個想法,去看下程序最後怎麼做的:
.text:00002A66 LDR R1, [SP,#0x2D8+var_2AC]
.text:00002A68 LDR R2, [SP,#0x2D8+var_1C]
.text:00002A6A MOVS R0, R6
.text:00002A6C LDR R3, [R1]
.text:00002A6E CMP R2, R3
.text:00002A70 BEQ loc_2A76
.text:00002A72 BLX __stack_chk_fail
看到沒,果然進行了一次比較。 呵呵。挺有意思的。 那意思是我如果再進行堆棧溢出的時候 更改玩EIP之後。一定要把這個值還原。
===================華麗的分割線==============================
這幾天下載了一些堆棧溢出的資料分析,補充一下
這個值常見的有幾種方式:
1、0x000aff0d
2、隨機值
如果是第一種方式的話,即使可以精準預測保護區的位置,也會對攻擊造成麻煩,因爲如果是strcpy攻擊的話,遇見/0會終止。 如果gets的話,遇見\n會終止。
隨機值就不可能預測啊,除非你要知道隨機值如何生成的。
那就不能進行攻擊了麼,參考了下 http://staff.ustc.edu.cn/~bjhua/courses/security/2014/readings/stackguard-bypass.pdf
這篇文章,發現其實還是可以攻擊的。就是要根據一些特殊情況,他舉了例子,
這種代碼可能是這樣的:
int func(char *msg) {
char buf[80];
strcpy(buf,msg);
// toupper(buf); // just to give func() "some" sense
strcpy(msg,buf);
}
int main(int argv, char** argc) {
func(argc[1]);
}
其實這種代碼挺常見的, 我剛學習C的時候經常寫,想修改msg的信息,然後直接不好修改。通過複製過來,然後修改完再複製過去。
這種就會有攻擊的漏洞,這樣通過msg填寫buff溢出把msg的地址給改了。這時候有人會說這樣也會修改了 canary 字段。別慌。這個判斷是在程序最後纔會執行。
下一個語句,把buf的內容複製到msg指向的地址空間。哈哈。這樣如果把msg指向GOT的表。很容易修改了exit()函數的函數指針,比如修改成注入的shellcode。
這樣函數執行完判斷髮現 canary修改了,執行退出函數,然後就執行了exit();然後然後然後.... 就沒有然後了。