【Andriod學習記錄】使用python解析smali

1 概述

比如,進行漏洞掃描的時候,經常需要對APK進行反編譯,然後使用python對每個反編譯進行解析,得到結果

但是:在反編譯android應用的時候,使用現有的工具,比如jadx某些時候會存在解析不出來的時候,這時候就不得不使用jeb或者apktools了,
1、使用jeb反編譯並導出java文件,這個過程使用自動化是特別慢的,估計一個稍微大的應用就會差不多兩個小時,速度很慢
2、最後還是回到了apktools,解析smali代碼了

2 smali 代碼的格式

參考文章smali語法

2.1 常用類型

smali語法 Java語法 類型
B byte 原始類型
C char 原始類型
D double (64 bits) 原始類型
F float 原始類型
I int 原始類型
J long (64 bits) 原始類型
S short 原始類型
V void 只能用於返回值類型 原始類型
Z boolean 原始類型
[ [] 數組類型
Lxx/yy/zz; xx.yy.zz 對象類型

2.2 方法

形式:Lxxx/yyy/zzz;->methodName(Lxxx/yyy/zzz;Lxxx/yyy/zzz;I)Z

Lxxx/yyy/zzz;(類名)
->methodName(方法名)
(Lxxx/yyy/zzz;Lxxx/yyy/zzz;I)(參數)
Z(返回值)

ex:如 int Log.i(String str2, String str2)轉換後是 Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

2.3 變量

形式:Lxxx/yyy/zzz;->FieldName:Lxxx/yyy/zzz;

Lxxx/yyy/zzz;(類名)
->FieldName:(變量)
Lxxx/yyy/zzz;(類型)

ex:如:ff = "aa"; 轉換後是 Lcom/example/reforceapk/MyLog;->ff:Ljava/lang/String

2.4 常用語法指令

smali指令 含義
.field private isFlag:z 定義變量
.method 方法
.prologue 方法開始
.end method 方法結束
.parameter 方法參數
.line 12 此方法位於第12行
invoke-super 調用父函數
invoke-direct 調用函數
invoke-static 調用靜態函數
const/high16 v0, 0x7f03 把0x7f03賦值給v0
return-void 函數返回void
new-instance 創建實例
move-result v0 將上一個invoke類型的指令操作的非對象結果賦值給v0寄存器
move-result-object v0 將上一個invoke類型指令操作的對象賦值給v0寄存器

3 使用Python對函數解析

3.1 總體思路

不知道其他的大神是怎麼做的,搜了半天也沒有找到,我琢磨了半天,總結了以下的思路

總體思路:將函數中的類型轉換爲標準的java類型,然後考慮一下例外情況,

  1. 常用類型:比如 I 直接替換爲int
  2. 如果存在數組:需要做1個順序的調整
  3. 如果存在對象:需要將對象替換爲標準的java函數

總結下來就是這樣了:
在這裏插入圖片描述

3.2 實現算法

案例:在之前的反編譯代碼中找到一個稍微複雜點的方法(含有對象數組,含有二維數組,對象中含有常見大寫字符,對象中可能疑似函數對象“Lx/xL/xx;”)

".method private static decodeStream(Ljava/io/InputStream;Ljava/iL/InputStream;[Ljava/iLo/InputStream;[BI[[IIF)Landroid/graphics/Bitmap;\n"

通過手動轉換應該是:

private static decodeStream android.graphics.Bitmap(java.io.InputStream,java.iL.InputStream,java.iLo.InputStream[],byte[],int,int[][],int,float)

感覺寫的有點亂,最終的代碼如下:

import re
class SmaliUtil:
    def __init__(self):
        self.method_pattern = ".method(.*?)\((.*?)\)(.*?)\n"
        self.smali_type_disc = {
            "B": "byte,",
            "C": "char,",
            "D": "double,",
            "F": "float,",
            "I": "int,",
            "J": "long,",
            "S": "short,",
            "V": "void,",
            "Z": "boolean,",
        }
        pass
    def smali_type_convert_to_java_type(self,input_type_str):
        #首先將對象替換爲不會出現重複的字符
        input_obj_disc = {}
        input_obj_list = re.findall(r"L.*?;",input_type_str)
        flag = 0
        for obj in input_obj_list:
            index = "obj%s"%flag
            input_obj_disc[index] = obj.lstrip("L").rstrip(";").replace("/",".")
            input_type_str = input_type_str.replace(obj,index + ",")
            flag = flag + 1;
        #經過上面處理之後,字符串中的對象被替換,剩下的只留了標準smali類型
        for key in self.smali_type_disc:
            input_type_str = input_type_str.replace(key,self.smali_type_disc[key])
        #然後處理數組的順序問題
        input_type_str = re.sub(r"\[.*?,",self.smali_array_type_pattern,input_type_str)
        #然後將還原對象
        for key in input_obj_disc:
            input_type_str = input_type_str.replace(key,input_obj_disc[key])
        return input_type_str.strip(",")

    def smali_array_type_pattern(self,matched):
        str_array = matched.group(0)
        if str_array.count("[") == 1:
            str_array = "%s[]," % str_array.split("[")[1].strip(",")
        elif str_array.count("[") == 2:
            str_array = "%s[][]," % str_array.split("[")[2].strip(",")
        return str_array

    def get_smali_method_params(self,method_str):
        method_content_disc = {}
        method_content = re.findall(self.method_pattern,method_str)
        if len(method_content):
            content_list = method_content[0]
            method_content_disc["method_name"] = content_list[0]
            method_content_disc["method_param"] = self.smali_type_convert_to_java_type(content_list[1])
            method_content_disc["method_ret"] = self.smali_type_convert_to_java_type(content_list[2])
        else:
            print("method type error:%s" % method_content)

        method_content = "%s %s(%s)" % (method_content_disc["method_name"].strip(), method_content_disc["method_ret"].strip(), method_content_disc["method_param"].strip())
        return method_content

if __name__ == '__main__':
    test_method = ".method private static decodeStream(Ljava/io/InputStream;Ljava/iL/InputStream;[Ljava/iLo/InputStream;[BI[[IIF)Landroid/graphics/Bitmap;\n"
    smali_uitl = SmaliUtil()
    print(smali_uitl.get_smali_method_params(test_method))
    pass
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章