請勿轉(zhuǎn)載,謝謝S势āU!?
連續(xù)半個月重感冒和咳嗽佑吝,這個時候才倍感身體的重要坐昙。
所以身體才是革命的本錢啊。芋忿。炸客。
言歸正傳。
編碼格式uleb128
先介紹下dex中用到的一種編碼方式uleb128戈钢。
簡單來說痹仙,它是對int(4字節(jié))數(shù)據(jù)的一個可變長度編碼,使得原先固定長度(4字節(jié))的數(shù)據(jù)殉了,可以通過更少的字節(jié)表示出來开仰;而且這個可變長度不超過5。
假定有數(shù)據(jù)序列:a b c d e宣渗,我們把它們用二進制表示出來:
aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee
如果每字節(jié)的高位開頭是1抖所,那么繼續(xù)往后;如果高位開頭是0痕囱,那么終止在這里田轧;然后把所有的高位都去除,反序拼出來的二進制鞍恢,就是最后的結(jié)果傻粘。
比如這樣的:
1aaaaaaa 1bbbbbbb 1ccccccc 0ddddddd eeeeeeee
一直到第4個每窖,它是以0為高位開頭的,那么這個有效數(shù)據(jù)就是前四個字節(jié)組成弦悉,并且反序如下:
ddddddd ccccccc bbbbbbb aaaaaaa
如上窒典,第一是反序,第二是每個字節(jié)只有7位稽莉。
這個就是簡單的uleb128的示范瀑志。
解碼程序如下:
數(shù)據(jù)結(jié)構(gòu)
其中classDataOff是指向類數(shù)據(jù)信息區(qū)域的一個偏移數(shù)值。
從這里可以看到污秆,類數(shù)據(jù)其實包含了四種信息劈猪,靜態(tài)屬性,實例屬性良拼,直接方法和虛方法战得。
對于DexMethod來說,它代表一個類的方法庸推,方法中當然是包含代碼的常侦,那么代碼(字節(jié)碼)所在的位置,通過codeOff來表示贬媒。
另外加一個說明聋亡,這里的fieldIdx和methodIdx其實是一個diff,也就是說掖蛤,在field列表和method列表中杀捻,第一個Idx是真實的Idx,后面的Idx都是在這個基礎(chǔ)上做的diff蚓庭。
數(shù)據(jù)追蹤
按照上面的數(shù)據(jù)結(jié)構(gòu),我們走一遍真實的dex數(shù)據(jù)仅仆。
如上一篇所講器赞,在dex的頭部區(qū)域中,有兩個字段分別表示類信息的偏移值和類的個數(shù)墓拜;上圖中港柜,第一個四字節(jié)(07)表示類的總個數(shù),第二個四字節(jié)(2ac)就表示類信息的偏移值咳榜。
我們找到2ac處: (按照類定義夏醉,每個類信息占據(jù)4*8=32個字節(jié))
我們把這7個類的信息,按著類的結(jié)構(gòu)解析并且打印出來:
結(jié)果是這樣的:
也就是說涌韩,我寫的這個Demo程序中畔柔,在dex里包含了7個類的信息,如上所示臣樱;其中MainActivity就是主Activity靶擦。
我們現(xiàn)在只看這個MainActivity的數(shù)據(jù):
其中類數(shù)據(jù)的偏移數(shù)值腮考,是第7個四字節(jié),也就是5cc (cc05 0000)玄捕。
這里使用到了前面鎖說的uleb128格式:00 00 01 01(四個字節(jié))
按上面的解釋方法踩蔚,每個字節(jié)都是以0開頭的,所以就是最后的結(jié)果了枚粘,那么定義出來的:
static_field_size = 0
instance_field_size = 0
direct_method_size = 1
virtual_method_size = 1
也就是說馅闽,MainActivity中沒有屬性信息,但是有一個直接方法和一個虛方法馍迄。
接下來的10個字節(jié)捞蛋,就是直接方法和虛方法的信息(屬性為空忽略),它們也是用uleb128進行編碼的柬姚。
這兩個方法分別是:
A: method_idx = 5, code_off = 4f0
B: method_idx = 6, code_off = 508
根據(jù)上一篇文章中介紹的拟杉,我們查找到方法信息如下:
其中第一個是構(gòu)造函數(shù)<init>,第二個就是我們很熟悉的android方法量承,onCreate搬设。
繼續(xù),我們跟蹤onCreate方法的信息撕捍,它的偏移是508(十六進制):
對照結(jié)構(gòu)如下:
其中從6f20開始拿穴,連續(xù)的32個字節(jié),就是該方法體的字節(jié)碼信息忧风。
接下來的任務(wù)默色,就是把這32個字節(jié),翻譯成可以識別的指令信息狮腿,就是反匯編過程了腿宰。
在這之前,我們總結(jié)下類信息的打印程序:
用到的一些方法:
反匯編
前面提到缘厢,onCreate方法中的指令字節(jié)序列吃度,就是下面的32個字節(jié)數(shù)據(jù)。
我們知道指令一般是指令符+指令數(shù)組成的贴硫,第一個字節(jié)一般標識一個特定的指令椿每。
在dex格式中,這個指令既表示具體的操作(字節(jié)碼格式)英遭,又對應(yīng)一個數(shù)據(jù)解析格式(說明格式)间护。
字節(jié)碼格式文檔:
https://source.android.google.cn/devices/tech/dalvik/dalvik-bytecode
說明格式文檔:
https://source.android.google.cn/devices/tech/dalvik/instruction-formats
比如第一個字節(jié)是6f,對照文檔如下:
字節(jié)碼格式
6e..72是數(shù)字范圍挖诸,下面的35c就代表著數(shù)據(jù)說明格式汁尺。
對于上面的35c來說,3表示本次指令共計3個字(2個字節(jié))税灌,5表示寄存器數(shù)目均函,而c是一個助記符標識常量:
這3個字是怎么排列呢亿虽?
把6f開始的3個字,如上面格式依次排開:
206f 0001 0032
對照格式得出:
A = 2, G = 0, op = 6f, BBBB = 1, C = 2, D = 3, E = 0, F = 0
這里的A = 2苞也,那么對應(yīng)格式:
也就是op {v2, v3}, kind@0001洛勉。
再對照字節(jié)碼格式文檔,其實就是:
invoke-super {v2, v3}, meth@0001
根據(jù)method的信息如迟,查詢出: meth@0001是
那么前3個字的反匯編代碼就是:
invoke-super {v2, v3}, Landroid/app/Activity; --> V onCreate(Landroid/os/Bundle;)
這個不就是調(diào)用父類Activity的onCreate方法嗎收毫?
對照源代碼:
同樣的方式,可以把32個字節(jié)的數(shù)據(jù)殷勘,全部反匯編出來此再。
我們是為了學(xué)習(xí)Dex的結(jié)構(gòu),所以會這么大費周折玲销;更加方便的方式输拇,是直接用現(xiàn)有的反編譯工具,將dex反編譯成smali贤斜,可以更加直觀的看到信息策吠。
在面對一個未知的應(yīng)用時,我們沒有源碼瘩绒,如果去修改它的信息呢猴抹?(假定沒有加固)
最簡單的方式,還是反編譯出smali锁荔,通過修改smali再重新打包蟀给。
如果是,我們直接在dex上修改呢阳堕? 后面繼續(xù)研究和探討跋理。