2014年3月17日 星期一

使用python struct實作Dex file parser

最近因為學校作業的關係,開始碰一些android的相關內容;有一個作業要我們寫一個程式去改android dex file的opCode,不過我實力不足,最後用smali/baksmali+shell來實作,一整個就不是熟練的程式人該作的事O_O。
不過,為了補一下實力不足,還是用python 寫了一個Dex file parser:

因為Dex file已經包成binary的形式,要去parse其中的內容,最方便的套件就是python struct了,在這裡以Dex為例,來介紹python struct的使用:

要使用python struct,首先要寫出format string,struct 會依照format string對資料進行處理。
基本的格式是“nX”,其中n為重複數字,X為格式化代碼,例如I代表unsigned int_32。 前面還有endian的符號,這部分請自行查閱參考資料。
有了format string就可以呼叫struct的function: pack, unpack,它們會照format string,把資料寫到binary或從binary讀成一個tuple。

 例如:我們可以看到dex 35的header為:
typedefstructDexHeader {
u1 magic[8]; /*includes version number */
u4 checksum; /*adler32 checksum */
u1 signature[kSHA1DigestLen]; /*SHA-1 hash */
u4 fileSize; /*length of entire file */
u4 headerSize; /*offset to start of next section */
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
}DexHeader;

對這個我們可以寫出v35 format string為:"8sI20s20I",就這麼簡單。 接著我們可以呼叫unpack來取得header的內容。
infile = self.open(“yay.dex”, “rb”)
header = struct.unpack(self.v35fmt, infile.read(struct.calcsize(self.v35fmt)))
比較麻煩的一點是,unpack的資料長度必須和format string會處理到的長度一樣,這裡struct提供了calcsize來處理這個問題,它會回傳format string代表的長度。
print(header) (b'dex\n035\x00', 3615126987, b'A\x89\xd9Y\xd8mm\xe4\xfe\x9d8\x0c\xc25\xbc\xcc\x9b\x86\xbd)', 912, 112, 305419896, 0, 0, 752, 16, 112, 8, 176, 4, 208, 1, 256, 5, 264, 1, 304, 576, 336)

可以看見資料已經寫入tuple中了,之後再進行轉出即可。