在之前的Android安全學(xué)習(xí)過程中溉仑,大部分都是dex部分的分析吊说,關(guān)于native只有零散的一些知識(shí)點(diǎn)吧兔,偽代碼全靠F5磷仰。但是遇到復(fù)雜的函數(shù)就看不懂了,因此往往在分析至so層面時(shí)遭遇失敗境蔼。然而elf文件的分析又是Android安全中不可避免的一塊內(nèi)容灶平,所以還得從基礎(chǔ)學(xué)起來。
自己寫了個(gè)簡單的demo箍土,定義了一個(gè)native的add方法逢享。
接下來將so文件拖入IDA中等待分析,以下的內(nèi)容還是需要一些基礎(chǔ)arm指令集吴藻、寄存器瞒爬、尋址方式、棧平衡等知識(shí)才會(huì)無障礙。但即便是零基礎(chǔ)侧但,因?yàn)樯婕暗降膬?nèi)容都很簡單矢空,邊查邊看也是能看懂的。
等待載入的過程中禀横,正好簡單地回顧一下arm匯編中的寄存器內(nèi)容屁药,節(jié)選自網(wǎng)絡(luò):
寄存器 | 說明 |
---|---|
R0-R3 | 用作傳入函數(shù)參數(shù),傳出函數(shù)返回值柏锄。在子程序調(diào)用之間酿箭,可以將 R0-R3 用于任何用途。被調(diào)用函數(shù)在返回之前不必恢復(fù) R0-R3趾娃。如果調(diào)用函數(shù)需要再次使用 R0-R3 的內(nèi)容缭嫡,則它必須保留這些內(nèi)容。 |
... | (其他請(qǐng)自行查閱) |
等到IDA載入完成后找到入口函數(shù)Java_com_johnhao_myjniapplication_JniTest_add茫舶,就可以一行一行閱讀了械巡。其中涉及到的ADD等arm指令,尋址方式等都很簡單饶氏,就像上面說的現(xiàn)學(xué)也能看的懂讥耗,下面步入正題:
一行一行閱讀然后加注釋是個(gè)不錯(cuò)的初學(xué)手段,首先是將R4,R5,R7,LR壓入堆棧疹启,然后設(shè)置R7的值古程,并作為棧幀指針。接下來是R0-R3寄存器的保存喊崖,我們知道這個(gè)函數(shù)正好有4個(gè)參數(shù)(JNIEnv 挣磨、jclass、jint和jint)荤懂,結(jié)合上面的表格知道R0-R3分別與之對(duì)應(yīng)茁裙。STR R0, [SP,#0x2C+var_c]這行表示的是一種arm的尋址方式,SP表示的棧指針节仿,后面可以根據(jù)這個(gè)指針和地址的偏移量晤锥,找到對(duì)應(yīng)的操作數(shù)。
為了后面閱讀方便廊宪,重命名了部分偏移量的名字矾瘾,用以對(duì)應(yīng)函數(shù)的四個(gè)參數(shù)(env、jclass箭启、number1壕翩、number2)。
繼續(xù)往后分析傅寡,B是跳轉(zhuǎn)指令放妈,這里跳轉(zhuǎn)到了loc_668中北救。還是先看第一行LDR R0, [SP,#0x2C+temp_var],和上面那行STR類似大猛,都是基址變址尋址扭倾。通過sp指針以及偏移地址,取出tem_var的值并賦值給R0寄存器(tem_var是上面一個(gè)初始值為0的臨時(shí)變量挽绩,所以重新命名為了tem_var),然后和2進(jìn)行比較驾中,如果大于就跳轉(zhuǎn)到loc_682唉堪,否則跳轉(zhuǎn)到loc_670。
接著往下看肩民,loc_670中把臨時(shí)變量tem_var和傳入的參數(shù)number1相加唠亚,tem_var再自相加后跳轉(zhuǎn)回loc_668。這里很明顯的就是for循環(huán)的特征了持痰,如果切換一下IDA視圖灶搜,也可看出來。
完成循環(huán)后工窍,將number1和number2相加割卖,并將棧恢復(fù)成入棧時(shí)的樣子患雏,這個(gè)函數(shù)就結(jié)束了鹏溯。最后再看一下F5的偽代碼,不得不感嘆IDA還真的是非常的強(qiáng)大淹仑。
至此丙挽,一篇簡單的分析就完成了。這個(gè)過程中匀借,了解了一些arm的基礎(chǔ)指令(ADD颜阐、SUB、LDR吓肋、CMP等)凳怨;學(xué)習(xí)了一種尋址方式--基址變址尋址;回顧了寄存器的一些知識(shí)(R0-R3傳遞前4個(gè)參數(shù)蓬坡,多余4個(gè)參數(shù)則使用堆棧方式傳遞)猿棉;脫離了F5偽代碼獨(dú)立完成了一段簡單函數(shù)的分析,為后面的學(xué)習(xí)分析提供了一種方法屑咳。