吾愛破解 - LCG - LSG |安卓破解|病毒分析|huihengkj.com

 找回密碼
 注冊[Register]

QQ登錄

500彩票邀请码只需一步,快速開始

搜索
查看: 2145|回復: 22

[Android 原創] DEX文件解析--7、類及其類數據解析(完結篇)

  [復制鏈接]
windy_ll 發表于 2020-7-16 13:35
本帖最后由 windy_ll 于 2020-7-16 16:42 編輯

一、前言

   前置技能鏈接:
      DEX文件解析---1、dex文件頭解析
      DEX文件解析---2、Dex文件checksum(校驗和)解析
      DEX文件解析--3、dex文件字符串解析
      DEX文件解析--4、dex類的類型解析
      DEX文件解析--5、dex方法原型解析
      DEX文件解析--6、dex文件字段和方法定義解析

    PS:Dex文件解析到現在,終于到了最重要也是結構最復雜的部分了,不了解前面的一些必要知識的,可以看我前面幾篇文章;這篇文章分析的dex樣本來自一個復雜apk的dex文件,但是代碼運行時使用的樣本是一個在網上找的很簡單的dex樣本,原因很簡單,分析使用的dex涉及的smali指令太多了,大概有200多個,挨個解析起來工作量太大了,有時間我會寫一個通用的python解析模塊,完成了我會上傳到github倉庫,有興趣的完成后可以看一下,用簡單的dex只涉及到5個指令,代碼寫起來就沒那么麻煩了!!!(tips:Dex類數據這里解析起來有種俄羅斯套娃的感覺,多看幾篇就很容易理解了。)  

    PS:這篇文章及其之前同系列的整合版(只是所有文章匯總在一起的整合版)都發在某公眾號上面了,名字就不說了,怕被認為打廣告,所以這不是抄襲哦!!!500彩票邀请码  


二、uleb128編碼

    PS:本來關于uleb128編碼網上一大堆,沒必要寫這個,但是網上的你抄我的我抄你的,能找的的相關資料基本都一樣。。。。或者干脆貼個官方代碼,官方代碼的位運算寫的很巧妙,但是直接去看的化,反正我是沒看懂到底是怎么解碼出來的。  

    uleb128編碼,是一種可變長度的編碼,長度大小為1-5字節,uleb128通過字節的最高位來決定是否用到下一個字節,如果最高位為1,則用到下一個字節,直到某個字節最高位為0或已經讀取了5個字節為止,接下來通過一個實例來理解uleb128編碼。500彩票邀请码  

    假設有以下經過uleb128編碼的數據(都為16進制)--81 80 04,首先來看第一個字節81,他的二進制為10000001,他的最高位為1,則說明還要用到下一個字節,它存放的數據則為0000001;再來看第二個字節80,它的二進制為10000000,它的最高位為1,則說明還需要用到第三個字節,存放的數據為0000000;再來看第三個字節04,它的二進制為00000100,最高位為0,說明一共使用了三個字節,它存放的數據為0000100;通過上面的數據我們已經獲取了存放的數據,接下來就是把這些bit組合起來獲取解碼后的數據,dex文件里面的數據都是采用的小端序的方式,uleb128也不例外,在這三個字節,也不例外,第三個字節04存放的數據0000100作為解碼后的數據的高7位,第二個字節80存放的數據0000000作為解碼后的數據的中7位,第一個字節81存放的數據0000001作為解碼后的數據的低7位;那么解碼后的數據二進制則為0000100 0000000 0000001,轉換為16進制則為0x10001。其他使用5個字節、4個字節照此類推即可,下面是python讀取uleb128的代碼(ps:該代碼是最終類數據解析代碼的一共函數,無法單獨運行,僅供參考,采用的是官方提供的位運算算法):500彩票邀请码  

def readuleb128(f,addr):
    result = [-1,-1]
    n = 0
    f.seek(addr)
    data = oneByte2Int(f.read(1))
    if data > 0x7f:
        f.seek(addr + 1)
        n = 1
        tmp = oneByte2Int(f.read(1))
        data = (data & 0x7f) | ((tmp & 0x7f) << 7)
        if tmp > 0x7f:
            f.seek(addr + 2)
            n = 2
            tmp = oneByte2Int(f.read(1))
            data |= (tmp & 0x7f) << 14
            if tmp > 0x7f:
                f.seek(addr + 3)
                n = 3
                tmp = oneByte2Int(f.read(1))
                data |= (tmp & 0x7f) << 21
                if tmp > 0x7f:
                    f.seek(addr + 4)
                    n = 4
                    tmp = oneByte2Int(f.read(1))
                    data |= tmp << 28
    result[0] = data
    result[1] = addr + n + 1
    return result

三、類解析第一層結構:class_def_item

    1、在dex文件頭0x60-0x63這四個字節,指明了class的數量,在0x64-0x67這四個字節,指明的class_def_item的偏移地址。如下所示:500彩票邀请码  

1.png  

    2、通過上面的偏移地址,我們可以找到class_def_item的起始地址,class_def_item包含了一個類的類名、接口、父類、所屬java文件名等信息。一個class_def_item結構大小為32字節,分別包含8個信息,每個信息大小為4字節(小端序存儲):  

  • 第1-4字節--class_idx(該值為前面解析出來的類的類型列表的索引,也就是這個類的類名);  
  • 第5-8字節--access_flags(類的訪問標志,也就是這個類是public還是private等,這個通過官方的文檔查表得知,具體算法在最后面說明);  
  • 第9-12字節--superclass_idx(該值也為前面解析出來的類的類型列表的索引,指明了父類的類名)  
  • 第13-16字節--interfaces_off(該值指明了接口信息的偏移地址,所指向的地址結構為typelist,前面的文章有說過,這里不再多說,如果該類沒有接口,該值則為0)  
  • 第17-20字節--source_file_idx(該值為dex字符串列表的的索引,指明了該類所在的java文件名)  
  • 第21-24字節--annotations_off(該值為注釋信息的偏移地址,由于注釋信息不是我要解析的重點,要查看注釋信息具體結構的可以參考官方文檔,官方文檔地址粘貼在文末)  
  • 第25-28字節--class_data_off(該值是這個類數據第二層結構的偏移地址,在該結構中指明了該類的字段和方法)  
  • 第29-32字節--static_value_off(該值也是一個偏移地址,指向了一個結構,不是重點,感興趣的參考官方文檔,如果沒相關信息,則該值為0)  

    具體分析過程,如下圖所示:500彩票邀请码  

2.png  


四、類解析第二層結構:class_data_item

    1、通過上面class_def_item的分析,我們知道了類的基本信息,例如類名、父類等啊,接下來就是要找到類里面的字段和方法這些信息,而這些信息,在class_def_item里面的class_data_off字段給我們指明class_data_item就包含這些信息并給出了偏移地址,即現在需要解析class_data_iem結構獲取字段和方法信息。(ps:以下的數據結構不做特別說明都為uleb128編碼格式)
    2、class_data_item結構包含以下信息:500彩票邀请码  

  • 第一個uleb128編碼--static_field_size,指明了該類的靜態字段的數量
  • 第二個uleb128編碼--instance_field_size,指明了該類的實例字段的數量(實例字段不知道是啥的建議百度)  
  • 第三個uleb128編碼--direct_method_size,指明了該類的直接方法的個數
  • 第四個uleb128編碼--virtual_method_size,指明了該類的虛方法的個數(虛方法理解不清楚的建議百度一下)  
  • encoded_field--static_fields,該結構指明了具體的靜態字段信息,該結構的存在前提是static_field_size >0,該結構包含兩個uleb128編碼,第一個uleb128編碼為前面解析出來的字段列表的索引,第二個uleb128編碼指明了該字段的訪問標志  
  • encoded_field--instance_fields,跟上面類似,不再多說,值得注意的是,該結構存在的前提是instance_field_size > 0  
  • encoded_method--direct_methods,該結構指明了直接方法具體信息,該結構存在的前提同樣是direct_method_size > 0,該結構包含3個uleb128編碼,第一個uleb128為前面文章解析出來的方法原型列表的索引值,第二個uleb128編碼為該方法的訪問標志,第三個uleb128為code_off,也就是該方法具體代碼的字節碼的偏移地址,對應的結構為code_item,code_item結構里面包含了該方法內部的代碼,這里是字節碼,也就是smali(ps:如果該方法為抽象方法,例如native方法,這時code_off對應的值為0,即該方法不存在具體代碼)  
  • encoded_method--virtual_methods,該結構指明了該類的虛方法的具體信息,存在前提為virtual_method_size > 0,具體結構和上面一樣,不再多說
        具體分析過程,如下圖所示:  

3.png500彩票邀请码  


五、類解析的第三層結構:code_item

    1、在上面的class_data_item結構中的encoded_method結構的第三個uleb128編碼中,指出了一個類中的方法具體代碼的偏移地址,也就是dv虛擬機在執行該方法的具體指令的偏移地址,該值指向的地址結構為code_item,里面包含了寄存器數量、具體指令等信息,下面來分析一下該結構。500彩票邀请码  

    2、code_item結構包含以下信息:500彩票邀请码  

  • 第1-2字節--registers_size,該值指明了該方法使用的寄存器數量,對應的smali語法中的.register的值  
  • 第3-4字節--ins_size,該值指明了傳入參數的個數  
  • 第5-6字節--outs_size,該值指明了該方法內部調用其他函數用到的寄存器個數  
  • 第7-8字節--tries_size,該值指明了該方法用到的try-catch語句的個數  
  • 第9-12字節--debug_info_off,該值指明了調試信息結構的偏移地址,如果不存在調試信息,則該值為0  
  • 第13-16字節--insns_size,該值指明了指令列表的大小,可以這么理解:規定了指令所用的字節數大小--2 x insns_size  
  • ushort[insns_size]--insns,這個是指令列表,包含了該方法所用到的指令的字節,每個指令占用的字節數可以參考官方文檔,這個沒什么算法,就是一個查表的過程,例如invoke-direct指令占用6個字節,return-void指令占用2個字節  
  • 2個字節--padding,該值存在的前提是tries-size > 0,作用用來對齊代碼  
  • try_item--tries,該值存在的前提是tries-size > 0,作用是指明異常具體位置和處理方式,該結構不是解析重點,重點是解析指令,感興趣的查看官方文檔  
  • encoded_catch_handler_list--handlers,該結構存在前提為tries-size > 0,同樣不是解析重點,感興趣的查看官方文檔  

    具體分析過程,如下圖所示:  

4.png  

5.png  


六、access_flags算法

    access_flags訪問標志具體值可以去查看官方文檔,下圖只截了一部分。如果access_flags的算法為access_flags = flag1 | flag2 | ...,如果訪問標志只有一共,直接查表即可,如果是兩個,按照算法對比值即可,下面舉給=個例子來理解該算法。500彩票邀请码  

6.png  

    例如我有一個類的訪問標志為public static,經過查表得知public對應的值為0x01static對應的值為0x8,那么public static對應的訪問標志為0x01 | 0x08 = 0x9,如果讀取出來的access_flags為0x09,那么對應的訪問標志則為public static,其余的照此算法計算即可!!!  


七、解析代碼

    PS:代碼運行環境推薦3.6及其以上,需要模塊binascii,運行樣本為Hello.dex,樣本附在文末網盤鏈接中!!!500彩票邀请码  

運行截圖  

7.png500彩票邀请码  

通過腳本解析出來的和通過apktools反編譯出來的smali文件對比圖
(ps:左側為apktools反編譯出來的,右側為腳本解析出來的,可以發現基本差不多)500彩票邀请码  

8.png500彩票邀请码  

9.png500彩票邀请码  

解析代碼(ps:代碼量有點多):  

'''
                                                    __----~~~~~~~~~~~------___
                                .  .   ~~//====......          __--~ ~~
                -.            \_|//     |||\\  ~~~~~~::::... /~
                ___-==_       _-~o~  \/    |||  \\            _/~~-
        __---~~~.==~||\=_    -_--~/_-~|-   |\\   \\        _/~
    _-~~     .=~    |  \\-_    '-~7  /-   /  ||    \      /
.~       .~       |   \\ -_    /  /-   /   ||      \   /
/  ____  /         |     \\ ~-_/  /|- _/   .||       \ /
|~~    ~~|--~~~~--_ \     ~==-/   | \~--===~~        .\
        '         ~-|      /|    |-~\~~       __--~~
                    |-~~-_/ |    |   ~\_   _-~            /\
                        /  \     \__   \/~                \__
                    _--~ _/ | .-~~____--~-/                  ~~==.
                    ((->/~   '.|||' -_|    ~~-/ ,              . _||
                                -_     ~\      ~~---l__i__i__i--~~_/
                                _-~-__   ~)  \--______________--~~
                            //.-~~~-~_--~- |-------~~~~~~~~
                                    //.-~~~--\
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                            神獸保佑            永無BUG

@Author: windy_ll
@Date: 2020-07-08 16:21:27
@LastEditors: windy_ll
@LastEditTime: 2020-07-14 23:45:28
@Description: file content
'''
import binascii
import re
import os
import sys

def byte2int(bs):
    tmp = bytearray(bs)
    tmp.reverse()
    rl = bytes(tmp)
    rl = str(binascii.b2a_hex(rl),encoding='UTF-8')
    rl = int(rl,16)
    return rl

def oneByte2Int(bs):
    num = str(binascii.b2a_hex(bs),encoding='UTF-8')
    num = int(num,16)
    return num

def getSmaliName(oldname):
    newname = ''
    tmpname = oldname.split('.')
    newname = str(os.path.join(sys.path[0])) + '\\smali\\' + str(tmpname[0]) + '.smali'
    return newname

def readuleb128(f,addr):
    result = [-1,-1]
    n = 0
    f.seek(addr)
    data = oneByte2Int(f.read(1))
    if data > 0x7f:
        f.seek(addr + 1)
        n = 1
        tmp = oneByte2Int(f.read(1))
        data = (data & 0x7f) | ((tmp & 0x7f) << 7)
        if tmp > 0x7f:
            f.seek(addr + 2)
            n = 2
            tmp = oneByte2Int(f.read(1))
            data |= (tmp & 0x7f) << 14
            if tmp > 0x7f:
                f.seek(addr + 3)
                n = 3
                tmp = oneByte2Int(f.read(1))
                data |= (tmp & 0x7f) << 21
                if tmp > 0x7f:
                    f.seek(addr + 4)
                    n = 4
                    tmp = oneByte2Int(f.read(1))
                    data |= tmp << 28
    result[0] = data
    result[1] = addr + n + 1
    return result

def getAccessFlags(flag):
    accessFlag = ''
    flagList = [0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x2000,0x4000,0x10000]
    flagdict = {0x01:'public',0x02:'private',0x04:'protected',0x08:'static',0x10:'final',0x20:'synchronized',0x40:'volatile',0x80:'transient',0x100:'native',\
        0x200:'interface',0x400:'abstract',0x800:'strictfp',0x2000:'annotayion',0x4000:'enum',0x10000:'constructor'}
    if flag == 0x1:
        accessFlag = 'public'
    elif flag == 0x2:
        accessFlag = 'private'
    elif flag == 0x4:
        accessFlag = 'protected'
    elif flag == 0x8:
        accessFlag = 'static'
    elif flag == 0x10:
        accessFlag = 'final'
    elif flag == 0x20:
        accessFlag = 'synchronized'
    elif flag == 0x40:
        accessFlag = 'volatile'
    elif flag == 0x80:
        accessFlag = 'transient'
    elif flag == 0x100:
        accessFlag = 'native'
    elif flag == 0x200:
        accessFlag = 'interface'
    elif flag == 0x400:
        accessFlag = 'abstract'
    elif flag == 0x800:
        accessFlag = 'strictfp'
    elif flag == 0x2000:
        accessFlag = flagdict[0x2000]
    elif flag == 0x4000:
        accessFlag = flagdict[0x4000]
    elif flag == 0x10000:
        accessFlag = flagdict[0x10000]
    else:
        mark = 0
        for k in range(14):
            if mark == 1:
                break
            for item in flagList[(k + 1):]:
                if flag == (flagList[k] | item):
                    idx1 = flagList[k]
                    idx2 = item
                    accessFlag = flagdict[idx1] + ' ' + flagdict[idx2]
                    mark = 1
                    break
    return accessFlag

def parseTypeList(f,addr,tList):
    paramList = []
    f.seek(addr)
    size = byte2int(f.read(4))
    if size == 0:
        return paramList
    else:
        addr = addr + 4
        for k in range(size):
            f.seek(addr + (k * 2))
            paramString = typeList[byte2int(f.read(2))]
            paramList.append(paramString)
    return paramList

def getStringByteArr(f,addr):
    byteArr = bytearray()
    f.seek(addr + 1)
    b = f.read(1)
    b = str(binascii.b2a_hex(b),encoding='UTF-8')
    b = int(b,16)
    index = 2
    while b != 0:
        byteArr.append(b)
        f.seek(addr + index)
        b = f.read(1)
        b = str(binascii.b2a_hex(b),encoding='UTF-8')
        b = int(b,16)
        index = index + 1
    return byteArr

def BytesToString(byteArr):
    try:
        bs = bytes(byteArr)
        stringItem = str(bs,encoding='UTF-8')
        return stringItem
    except:
        pass

def getTypeAmount(f):
    f.seek(0x40)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getclassCount(f):
    f.seek(0x60)
    class_num = f.read(4)
    class_size = byte2int(class_num)
    return class_size

def getStringsCount(f):
    f.seek(0x38)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getStrings(f,stringAmount):
    stringsList = []
    f.seek(0x3c)
    stringOff = f.read(4)
    Off = byte2int(stringOff)
    f.seek(Off)
    for i in range(stringAmount):
        addr = f.read(4)
        address = byte2int(addr)
        byteArr = getStringByteArr(f,address)
        stringItem = BytesToString(byteArr)
        stringsList.append(stringItem)
        Off = Off + 4
        f.seek(Off)
    return stringsList

def getTypeItem(f,count,strLists):
    typeList = []
    f.seek(0x44)
    type_ids_off = f.read(4)
    type_off = byte2int(type_ids_off)
    f.seek(type_off)
    for i in range(count):
        typeIndex = f.read(4)
        typeIndex = byte2int(typeIndex)
        typeList.append(strLists[typeIndex])
        type_off = type_off + 0x04
        f.seek(type_off)
    return typeList

def parserField(f,stringList,typelist):
    fieldList = []
    f.seek(0x50)
    fieldSize = byte2int(f.read(4))
    fieldAddr = byte2int(f.read(4))
    for i in range(fieldSize):
        fieldStr = ''
        f.seek(fieldAddr)
        classIdx = typelist[byte2int(f.read(2))]
        f.seek(fieldAddr + 2)
        tyPEIDx = typelist[byte2int(f.read(2))]
        f.seek(fieldAddr + 4)
        nameIdx = stringList[byte2int(f.read(4))]
        fieldAddr += 8
        fieldStr = nameIdx + ':' + typeIdx
        fieldList.append(fieldStr)
    return fieldList

def parseProtold(f,typeList,stringList):
    pList = []
    f.seek(0x48)
    protoldSizeTmp = f.read(4)
    protoldSize = byte2int(protoldSizeTmp)
    f.seek(0x4c)
    protoldAddr = byte2int(f.read(4))
    for i in range(protoldSize):
        f.seek(protoldAddr)
        AllString = stringList[byte2int(f.read(4))]
        protoldAddr += 4
        f.seek(protoldAddr)
        returnString = typeList[byte2int(f.read(4))]
        protoldAddr += 4
        f.seek(protoldAddr)
        paramAddr = byte2int(f.read(4))
        if paramAddr == 0:
            protoldAddr += 4
            pList.append(returnString + '()')
            continue
        f.seek(paramAddr)
        paramSize = byte2int(f.read(4))
        paramList = []
        if paramSize == 0:
            pass
        else:
            paramAddr = paramAddr + 4
            for k in range(paramSize):
                f.seek(paramAddr + (k * 2))
                paramString = typeList[byte2int(f.read(2))]
                paramList.append(paramString)
        protoldAddr += 4
        paramTmp = []
        for paramItem in paramList:
            paramTmp.append(paramItem)
        param = returnString + '(' + ','.join(paramTmp) + ')'
        pList.append(param)
    return pList

def parserMethod(f,stringlist,typelist,protoldlist):
    methodlist = []
    f.seek(0x58)
    methodSize = byte2int(f.read(4))
    f.seek(0x5c)
    methodAddr = byte2int(f.read(4))
    for i in range(methodSize):
        f.seek(methodAddr)
        classIdx = typelist[byte2int(f.read(2))]
        f.seek(methodAddr + 2)
        protoldIdx = protoldlist[byte2int(f.read(2))]
        f.seek(methodAddr + 4)
        nameIdx = stringlist[byte2int(f.read(4))]
        tmp = protoldIdx.split('(',1)
        methodItem = nameIdx + '(' + str(tmp[1]) + str(tmp[0])
        methodlist.append(methodItem)
        methodAddr += 8
    return methodlist

def parseBytecode(f,addr,bytecount,stringsList,fieldsList,methodsList):
    codestr = ''
    n = 0
    while True:
        f.seek(addr)
        op = byte2int(f.read(1))
        if op == 0x0e:
            codestr += '\treturn-void\r\n'
            addr = addr + 2
            n += 2
        elif op == 0x1a:
            f.seek(addr + 1)
            register = oneByte2Int(f.read(1))
            f.seek(addr + 2)
            idx = byte2int(f.read(2))
            stringIdx = stringsList[idx]
            re.sub("[\n]","",stringIdx)
            re.sub("[\r]","",stringIdx)
            codestr += '\tconst-string v' + str(register) + ', "' + stringIdx + '"\r\n'
            addr = addr + 4
            n += 4
        elif op == 0x62:
            f.seek(addr + 1)
            register = oneByte2Int(f.read(1))
            f.seek(addr + 2)
            idx = byte2int(f.read(2))
            codestr += '\tset-object v' + str(register) + ', ' + fieldsList[idx] + '\r\n'
            addr = addr + 4
            n += 4
        elif op == 0x70 or op == 0x6e:
            f.seek(addr + 1)
            data = oneByte2Int(f.read(1))
            f.seek(addr + 4)
            data1 = oneByte2Int(f.read(1))
            f.seek(addr + 5)
            data2 = oneByte2Int(f.read(1))
            f.seek(addr + 2)
            idx = byte2int(f.read(2))
            registerNum = (data & 0xf0) >> 4
            register = ''
            if registerNum == 1:
                register_1 = data & 0xf
                register = '{v' + str(register_1) + '}, '
            elif registerNum == 2:
                register_1 = data & 0xf
                register_2 = (data1 & 0xf0) >> 4
                register = '{v' + str(register_1) + ', v' + str(register_2) + '}, '
            elif registerNum == 3:
                register_1 = data & 0xf
                register_2 = (data1 & 0xf0) >> 4
                register_3 = data1 & 0xf
                register = '{v' + str(register_1) + ', v' + str(register_2) + ', v' + str(register_3) + '}, '
            elif registerNum == 4:
                register_1 = data & 0xf
                register_2 = (data1 & 0xf0) >> 4
                register_3 = data1 & 0xf
                register_4 = (data2 & 0xf0) >> 4
                register = '{v' + str(register_1) + ', v' + str(register_2) + ', v' + str(register_3) + ', v' + str(register_4) + '}, '
            else:
                register_1 = data & 0xf
                register_2 = (data1 & 0xf0) >> 4
                register_3 = data1 & 0xf
                register_4 = (data2 & 0xf0) >> 4
                register_5 = data2 & 0xf
                register = '{v' + str(register_1) + ', v' + str(register_2) + ', v' + str(register_3) + ', v' + str(register_4) + ', v' + str(register_5) + '}, '
            if op == 0x70:
                codestr += '\tinvoke-direct ' + register + methodsList[idx] + '\r\n'
            else:
                codestr += '\tinvoke-virtual ' + register + methodsList[idx] + '\r\n'
            addr = addr + 6
            n += 6
        else:
            pass
        if n == bytecount:
            break
    return codestr

def parseCode(f,addr,fn,slist,flist,mlist):
    f.seek(addr)
    register_size = byte2int(f.read(2))
    f.seek(addr + 2)
    ins_size = byte2int(f.read(2))
    f.seek(addr + 4)
    out_size = byte2int(f.read(2))
    f.seek(addr + 6)
    try_size = byte2int(f.read(2))
    f.seek(addr + 8)
    debug_off = byte2int(f.read(4))
    f.seek(addr + 12)
    insns_size = byte2int(f.read(4))
    address = addr + 16
    bytecount = insns_size * 2
    registerString = '\t.register ' + str(register_size) + '\r\n'
    fn.write(registerString)
    codestr = parseBytecode(f,address,bytecount,slist,flist,mlist)
    fn.write(codestr)
    endstr = '.end method\r\n'
    fn.write(endstr)

def parseClassData(f,addr,fn,fList,mList,strsList):
    re = readuleb128(f,addr)
    static_fields_size = re[0]
    address = re[1]
    re = readuleb128(f,address)
    instance_fields_size = re[0]
    address = re[1]
    re = readuleb128(f,address)
    direct_method_size = re[0]
    address = re[1]
    re = readuleb128(f,address)
    virtual_method_size = re[0]
    address = re[1]
    fieldStr = ''
    if static_fields_size != 0:
        fieldStr += '# static fields\r\n'
        for i in range(static_fields_size):
            re = readuleb128(f,address)
            fieldidx = re[0]
            address = re[1]
            re = readuleb128(f,address)
            accflag = re[0]
            address = re[1]
            fieldStr += '.field ' + getAccessFlags(accflag) + ' ' + fList[fieldidx] + '\r\n'
        fieldStr += '\r\n\r\n'
        fn.write(fieldStr)
    fieldStr = ''
    if instance_fields_size != 0:
        fieldStr += '# instance fields\r\n'
        for i in range(instance_fields_size):
            re = readuleb128(f,address)
            fieldidx = re[0]
            address = re[1]
            re = readuleb128(f,address)
            accflag = re[0]
            address = re[1]
            fieldStr += '.field ' + getAccessFlags(accflag) + ' ' + fList[fieldidx] + '\r\n'
        fieldStr += '\r\n\r\n'
        fn.write(fieldStr)
    methodStr = ''
    if direct_method_size != 0:
        methodStr += '# direct methods\r\n'
        fn.write(methodStr)
        for i in range(direct_method_size):
            re = readuleb128(f,address)
            methodidx = re[0]
            address = re[1]
            re = readuleb128(f,address)
            accflag = re[0]
            address = re[1]
            re = readuleb128(f,address)
            code_off = re[0]
            address = re[1]
            methodStr = '.method ' + getAccessFlags(accflag) + ' ' + mList[methodidx] + '\r\n'
            fn.write(methodStr)
            parseCode(f,code_off,fn,strsList,fList,mList)
        methodStr = '\r\n\r\n'
        fn.write(methodStr)
    methodStr = ''
    if virtual_method_size != 0:
        methodStr = '# virtual methods\r\n'
        fn.write(methodStr)
        for i in range(virtual_method_size):
            re = readuleb128(f,address)
            methodidx = re[0]
            address = re[1]
            re = readuleb128(f,address)
            accflag = re[0]
            address = re[1]
            re = readuleb128(f,address)
            code_off = re[0]
            address = re[1]
            methodStr = '.method ' + getAccessFlags(accflag) + ' ' + mList[methodidx] + '\r\n'
            fn.write(methodStr)
            parseCode(f,code_off,fn,strsList,fList,mList)
        methodStr = '\r\n\r\n'
        fn.write(methodStr)

def parseClassDefItem(f,class_num,tList,sList,fieldlist,methodlist):
    f.seek(0x64)
    addr = byte2int(f.read(4))
    for i in range(class_num):
        f.seek(addr)
        classIdx = tList[byte2int(f.read(4))]
        f.seek(addr + 4)
        accessFlags = getAccessFlags(byte2int(f.read(4)))
        if accessFlags != 'error':
            pass
        f.seek(addr + 8)
        superclass_idx = tList[byte2int(f.read(4))]
        f.seek(addr + 12)
        interfaces_off = byte2int(f.read(4))
        if interfaces_off == 0:
            pass
        else:
            parseTypeList(f,interfaces_off,tList)
        f.seek(addr + 16)
        sourceFileIdx = sList[byte2int(f.read(4))]
        f.seek(addr + 20)
        annotions_off = byte2int(f.read(4))
        address = 0
        f.seek(addr + 24)
        class_data_off = byte2int(f.read(4))
        f.seek(addr + 28)
        static_value_off = byte2int(f.read(4))
        fname = getSmaliName(sourceFileIdx)
        fn = open(fname,'a+',True)
        headstr = '.class ' + str(accessFlags) + ' ' + str(classIdx) + '\r\n'
        headstr += '.super ' + str(superclass_idx) + '\r\n'
        headstr += '.source ' + '"' + str(sourceFileIdx) + '"\r\n\r\n'
        fn.write(headstr)
        if class_data_off != 0:
            parseClassData(f,class_data_off,fn,fieldlist,methodlist,sList)
        fn.close()
        print(' %s文件的類%s寫入完畢!'%(fname,classIdx))
        addr += 32

if __name__ == '__main__':
    filename = str(os.path.join(sys.path[0])) + '\\Hello.dex'
    dir = str(os.path.join(sys.path[0])) + '\\smali'
    if not os.path.exists(dir):
        os.makedirs(dir)
    f = open(filename,'rb',True)
    stringsCount = getStringsCount(f)
    strList = getStrings(f,stringsCount)
    typeCount = getTypeAmount(f)
    typeList = getTypeItem(f,typeCount,strList)
    fieldList = parserField(f,strList,typeList)
    protoldList = parseProtold(f,typeList,strList)
    methodList = parserMethod(f,strList,typeList,protoldList)
    classNum = getclassCount(f)
    parseClassDefItem(f,classNum,typeList,strList,fieldList,methodList)
    f.close()

八、參考資料以及樣本下載

參考資料:
    1、Android逆向之旅—解析編譯之后的Dex文件格式:
    2、一篇文章帶你搞懂DEX文件的結構:
    3、官方文檔:500彩票邀请码  

樣本及代碼下載:
藍奏云鏈接:;密碼:chb6
github鏈接:

免費評分

參與人數 8威望 +2 吾愛幣 +109 熱心值 +7 收起 理由
15903232420 + 1 希望有傻瓜式的一鍵解密~~就牛逼了
lncyq + 1 + 1 感覺很厲害,但我是不懂的,要是年輕點就想去學學
libozi + 1 + 1 用心討論,共獲提升!
qtfreet00 + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
zhourunfav + 1 + 1 我很贊同!
笙若 + 1 + 1 謝謝@Thanks!
夜步城 + 1 + 1 nice啊,來學習學習
XhyEax + 3 + 1 熱心回復!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

 樓主| windy_ll 發表于 2020-7-24 19:33
xmhwws 發表于 2020-7-24 17:12
code_item
這張圖是不是弄錯了?

有可能吧,因為那個索引值有點大,查找的時候有可能弄錯了
xmhwws 發表于 2020-7-24 17:12
417788939 發表于 2020-7-16 14:56
abs9668 發表于 2020-7-16 14:59
支持樓主!!!!!!!!!!
yiwanyiwan 發表于 2020-7-16 15:28
學無止境,謝謝
wtmft 發表于 2020-7-16 15:58

學無止境,謝謝
XhyEax 發表于 2020-7-16 16:25
建議編輯一下之前的文章,把更新完的鏈接加上。
 樓主| windy_ll 發表于 2020-7-16 16:29
XhyEax 發表于 2020-7-16 16:25
建議編輯一下之前的文章,把更新完的鏈接加上。

ok,待會修改
WX2886 發表于 2020-7-16 23:29
看不懂,感覺好厲害
shaunkelly 發表于 2020-7-17 02:14
各種軟件大比拼,真心不錯哦
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( )

GMT+8, 2020-8-4 06:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表

500彩票邀請碼-彩經網