├── .gitignore ├── README.md ├── test.py ├── re_gra.py └── smali2java.py /.gitignore: -------------------------------------------------------------------------------- 1 | smali/ 2 | .* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smali2java 2 | 3 | ------------ 4 | 5 | Android SMALI 文件翻译为Java代码 6 | 7 | ## 使用方法 8 | 9 | 利用apktool先把APK转换为SMALI代码, 然后执行如下命令(注: 使用Python 3) 10 | 11 | `python smali2java.py [SMALI目录] [Java目录]` 12 | 13 | ## 使用效果 14 | 15 | ``` 16 | # virtual methods 17 | .method public addOnBackPressedCallback(Landroidx/activity/OnBackPressedCallback;)V 18 | .locals 2 19 | .param p1 # Landroidx/activity/OnBackPressedCallback; 20 | .annotation build Landroidx/annotation/NonNull; 21 | .end annotation 22 | .end param 23 | .annotation runtime Ljava/lang/Deprecated; 24 | .end annotation 25 | 26 | .line 325 27 | iget-object v0, p0, Landroidx/activity/ComponentActivity;->mOnBackPressedCallbackCancellables:Ljava/util/WeakHashMap; 28 | 29 | .line 326 30 | invoke-virtual {p0}, Landroidx/activity/ComponentActivity;->getOnBackPressedDispatcher()Landroidx/activity/OnBackPressedDispatcher; 31 | 32 | move-result-object v1 33 | 34 | .line 327 35 | invoke-virtual {v1, p0, p1}, Landroidx/activity/OnBackPressedDispatcher;->addCallback(Landroidx/lifecycle/LifecycleOwner;Landroidx/activity/OnBackPressedCallback;)Landroidx/arch/core/util/Cancellable; 36 | 37 | move-result-object v1 38 | 39 | .line 325 40 | invoke-virtual {v0, p1, v1}, Ljava/util/WeakHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 41 | 42 | return-void 43 | .end method 44 | ``` 45 | 46 | 转换后的结果如下 47 | 48 | ```P 49 | void addOnBackPressedCallback(OnBackPressedCallback ){ 50 | //line 325 51 | v0 = this.mOnBackPressedCallbackCancellables; 52 | //line 326 53 | v1 = this.getOnBackPressedDispatcher();; 54 | //line 327 55 | v1 = v1.addCallback(this, );; 56 | //line 325 57 | v0.put(, v1); 58 | return; 59 | } 60 | ``` 61 | 62 | ## 博客 63 | http://www.gcsjj.cn/tags/smali2java 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | import unittest 3 | import re_gra 4 | 5 | 6 | class ReGraTest(unittest.TestCase): 7 | @staticmethod 8 | def setUPClass(cls): 9 | pass 10 | 11 | def setUp(self): 12 | pass 13 | 14 | @staticmethod 15 | def tearDownClass(cls): 16 | pass 17 | 18 | def tearDown(self): 19 | pass 20 | 21 | def test_to_dot(self): 22 | lines = [ 23 | ['.line 84', {'NUM': '84'}], 24 | ['.class public Landroidx/activity/ComponentActivity;', {'CLS': 'Landroidx/activity/ComponentActivity;'}], 25 | ['.super Landroid/text/TextWatcher;', {'CLS': 'Landroid/text/TextWatcher;'}], 26 | ['.implements Landroid/text/TextWatcher;', {'CLS': 'Landroid/text/TextWatcher;'}], 27 | ['.field private static final COLUMN_INDEX_FIRST:I = 0x0', 28 | {'VAR': 'COLUMN_INDEX_FIRST', 'VAL': '0x0', 'ATTR': ['private', 'static', 'final']}], 29 | ['.field COLUMN_INDEX_FIRST:I', {'VAR': 'COLUMN_INDEX_FIRST', 'ATTR': []}], 30 | ['.field private static final COLUMN_INDEX_FIRST:I', {'VAR': 'COLUMN_INDEX_FIRST'}], 31 | ['.field private final mLifecycleRegistry:Landroidx/lifecycle/LifecycleRegistry;', 32 | {'VAR': 'mLifecycleRegistry', 'VT': 'Landroidx/lifecycle/LifecycleRegistry;'}], 33 | ['.end field', {'CMD': '.end field'}], 34 | ['.method public static main([Ljava/lang/String;)V', 35 | {'FUNC': 'main', 'PARAMS': '[Ljava/lang/String;', 'FT': 'V', 'ATTR': ['public', 'static']}], 36 | ['.param p0, "feedbackType" # I @baksmali', {'REG': 'p0', 'VAR': 'feedbackType'}], 37 | ['.param p1 # Landroid/os/Bundle;', {'REG': 'p1', 'VAR': None}], ['.end param', {'CMD': '.end param'}], 38 | 39 | ['.catch Landroid/os/RemoteException; {:try_start_0 .. :try_end_0} :catch_0', None], 40 | ['.catchall {:try_start_0 .. :try_end_0} :catchall_0', None], 41 | ['.catch Ljava/lang/NoSuchFieldException; {:try_start_0 .. :try_end_0} :catch_0', 42 | {'CLS': 'Ljava/lang/NoSuchFieldException;'}], ['.end packed-switch', None], 43 | ['.enum Landroidx/annotation/RestrictTo$Scope;->LIBRARY:Landroidx/annotation/RestrictTo$Scope;', None], 44 | 45 | ['invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V', None], 46 | ['invoke-direct {v0}, Ljava/lang/RuntimeException;->()V', None], 47 | ['invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z', None], 48 | ['invoke-virtual {v2, v0}, Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V', 49 | {'TP': 'virtual', 'CLS': 'Landroid/webkit/WebView;'}], 50 | 51 | ['const/high16 v2, 0x7f03', None], 52 | ['const/high16 p0, 0x3f800000 # 1.0f', {'TP': 'high16', 'NUM': '0x3f800000'}], 53 | ['const-string v2, ", "', None], 54 | 55 | ['iput-object v2, p0, Lcom/tutor/apkinstaller/ApkInstaller;->apkWeb:Landroid/webkit/WebView;', None], 56 | ['sput-object v1, Lcom/moji/mjweather/activity/AddCityActivity;->mHotCitys:[Ljava/lang/String;', None], 57 | ['aget-object v3, v3, v0', None], 58 | 59 | ['move-result-object v2', None], ['move-exception v0', None], ['move-object/from16 v0, p0', None], 60 | 61 | ['return-void', None], ['return-object v0', None], 62 | ['goto :goto_0', None], 63 | 64 | ['new-instance v1, Lcom/moji/mjweather/activity/AddCityActivity$1;', None], 65 | ['new-array v1, v1, [Ljava/lang/String;', None], 66 | 67 | ['fill-array-data v0, :array_0', None], 68 | ['if-nez v1, :cond_0', None], ['if-ge v0, v1, :cond_1', None], 69 | ['check-cast v1, Landroid/widget/ImageButton;', {'CLS': 'Landroid/widget/ImageButton;'}], 70 | 71 | ['mul-double/2addr v3, v5', None], ['add-int/lit8 v9, v8, 0x1', {'NUM': '0x1'}], 72 | ['mul-double v0, v0, v2', None], 73 | 74 | ['cmpl-float v13, v11, v7', {'REG_1': 'v13'}], 75 | ['sparse-switch v1, :sswitch_data_0', None], ['packed-switch p0, :pswitch_data_0', None], 76 | ['array-length v1, v1', None], 77 | ['int-to-double v3, v3', None], 78 | ['throw v0', None], 79 | ['instance-of v8, v7, Landroid/widget/TextView;', None], 80 | ['monitor-enter p0', {'REG': 'p0'}], 81 | 82 | ] 83 | 84 | for one_line in lines: 85 | line_smali = one_line[0] 86 | expected = one_line[1] 87 | result = re_gra.to_op(line_smali) 88 | 89 | self.assertIsNotNone(result, line_smali + '(Not Match)') 90 | if expected is not None: 91 | result2 = {} 92 | for a_key in expected.keys(): 93 | if a_key in result.keys(): 94 | result2[a_key] = result[a_key] 95 | else: 96 | result2[a_key] = None 97 | self.assertEqual(expected, result2, line_smali + '(Error)') 98 | 99 | 100 | if __name__ == '__main__': 101 | unittest.main() 102 | -------------------------------------------------------------------------------- /re_gra.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | NUM = '(\d+)' 5 | NUM_16 = '(0x[\da-fA-F]+)' 6 | ATTR = '((\w+ +)*)' 7 | CLS = '(L[\w\d/\$]+;)' 8 | VAR = '([\w\d\$]+)' 9 | VT = '([\w/;\[]+)' 10 | FUNC = '([\w\<\>]+)' 11 | PARAMS = '([\[a-zA-Z\$/;]*)' 12 | FT = '([\w/;]+)' 13 | REG = '(\w\d+)' 14 | LBL = ':([\w\d]+)' 15 | REGS = '([\w\d ,]+)' 16 | STRING = '(\".+\")' 17 | 18 | 19 | GS_DOT = [ 20 | # .line 84 21 | [['.line'], [' +', NUM], ['NUM']], 22 | # .class public Landroidx/activity/ComponentActivity; 23 | [['.class'], [' +', ATTR, CLS], ['ATTR', None, 'CLS']], 24 | # .super Landroid/text/TextWatcher; 25 | [['.super'], [' +', CLS], ['CLS']], 26 | # .implements Landroid/text/TextWatcher; 27 | [['.implements'], [' +', CLS], ['CLS']], 28 | # .field private static final COLUMN_INDEX_FIRST:I = 0x0 29 | [['.field'], [' +', ATTR, VAR, ':', VT, '( += +', NUM_16, ')*'], ['ATTR', None, 'VAR', 'VT', None, 'VAL']], 30 | [['.end field']], 31 | # .method public static main([Ljava/lang/String;)V 32 | [['.method'], [' +', ATTR, FUNC, '\(', PARAMS, '\)', FT], ['ATTR', None, 'FUNC', 'PARAMS', 'FT']], 33 | [['.end method']], 34 | # .parameter "x0" 35 | [['.parameter'], [' +"', VAR, '"'], ['VAR']], 36 | # .param p0, "feedbackType" # I @baksmali 37 | [['.param'], [' +', REG, '(, +"', VAR, '")*'], ['REG', None, 'VAR']], 38 | [['.end param']], 39 | # .locals 3 40 | [['.locals'], [' +' + NUM], ['NUM']], 41 | # .local v0, bundle:Landroid/os/Bundle; 42 | [['.local'], [' +', REG, ' +', VAR + ':' + VT], ['REG', 'VAR', 'VT']], 43 | # .end local v0 #cityName:Ljava/lang/String; 44 | [['.end local'], [' +', REG,], ['REG']], 45 | 46 | [['.array-data']], 47 | [['.end array-data']], 48 | [['.annotation']], 49 | [['.end annotation']], 50 | [['.source']], 51 | [['.registers']], 52 | 53 | # .sparse-switch 54 | [['.sparse-switch']], 55 | [['.end sparse-switch']], 56 | # .packed-switch 0x1 57 | [['.packed-switch'], [' +', NUM_16], ['NUM']], 58 | [['.end packed-switch']], 59 | # '.catchall {:try_start_0 .. :try_end_0} :catchall_0' 60 | [['.catchall'], [' +{', LBL, ' +\.\. +', LBL, '} +', LBL], ['LBL_TS', 'LBL_TE', 'LBL_C']], 61 | # .catch Ljava/lang/Exception; {:try_start_7 .. :try_end_7} :catch_7 62 | [['.catch'], [' +', CLS, ' +{', LBL, ' +\.\. +', LBL, '} +', LBL], ['CLS', 'LBL_TS', 'LBL_TE', 'LBL_C']], 63 | # .enum Landroidx/annotation/RestrictTo$Scope;->LIBRARY:Landroidx/annotation/RestrictTo$Scope; 64 | [['.enum']], 65 | ] 66 | 67 | GS_CMD = [ 68 | # 'invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V' 69 | # invoke-direct {v0}, Ljava/lang/RuntimeException;->()V 70 | # invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z 71 | # invoke-virtual {v2, v0}, Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V 72 | [['invoke'], ['\-', '(\w+)', ' +\{', REGS, '\}, +', CLS, '\-\>', FUNC], ['TP', 'REGS', 'CLS', 'FUNC']], 73 | # const/high16 v2, 0x7f03 74 | # const/high16 p0, 0x3f800000 # 1.0f 75 | [['const'], ['\/', '(high\d+) +', REG, ', +', NUM_16], ['TP', 'REG', 'NUM']], 76 | # const-string v2, ", " 77 | [['const-string'], [' +', REG, ', +', STRING], ['REG', 'STR']], 78 | 79 | # iput-object v2, p0, Lcom/tutor/apkinstaller/ApkInstaller;->apkWeb:Landroid/webkit/WebView; 80 | [['iget', 'iput'], ['\-object +', REG, ', +', REG, ', +', CLS, '\-\>', VAR, ':', CLS], 81 | ['REG_V', 'REG_O', 'CLS', 'VAR', 'VAR_C']], 82 | 83 | # sput-object v1, Lcom/moji/mjweather/activity/AddCityActivity;->mHotCitys:[Ljava/lang/String; 84 | [['sget', 'sput'], ['\-object +', REG, ', +', CLS, '\-\>', VAR, ':', VT], 85 | ['REG', 'CLS', 'VAR', 'VT']], 86 | 87 | # aget-object v3, v3, v0 88 | [['aget', 'aput'], ['\-object +', REG, ', +', REG, ', +', REG], ['REG', 'REG_AR', 'REG_ID']], 89 | 90 | # move-result-object v2 91 | # move-exception v0 92 | [['move'], ['\-([\w\-]+) +', REG], ['TP', 'REG']], 93 | # move-object/from16 v0, p0 94 | [['move-object'], ['\/(from\d+) +', REG, ', +', REG], ['TP', 'REG']], 95 | 96 | # return-void 97 | [['return-void']], 98 | # return-object v0 99 | [['return-object'], [' +', REG], ['REG']], 100 | 101 | # goto :goto_0 102 | [['goto'], [' +', LBL], ['LBL']], 103 | 104 | # new-instance v1, Lcom/moji/mjweather/activity/AddCityActivity$1; 105 | [['new-instance'], [' +', REG, ', +', CLS], ['REG', 'CLS']], 106 | # new-array v1, v1, [Ljava/lang/String; 107 | [['new-array'], [' +', REG, ', +', REG, ', +', VT], ['REG_O', 'REG_L', 'VT']], 108 | 109 | # fill-array-data v0, :array_0 110 | [['fill-array-data'], [' +', REG, ', +', LBL], ['REG', 'LBL']], 111 | 112 | # if-nez v1, :cond_0 113 | # if-ge v0, v1, :cond_1 114 | [['if'], ['\-(\w+) +', REGS, ', +', LBL], ['TP', 'REGS', 'LBL']], 115 | 116 | # check-cast v1, Landroid/widget/ImageButton; 117 | [['check-cast'], [' +', REG, ', +', CLS], ['REG', 'CLS']], 118 | 119 | 120 | 121 | # cmpl-float v13, v11, v7 122 | [['cmpl-'], ['(\w+) +', REG, ', +', REG, ', +', REG], ['TP', 'REG_1', 'REG_2', 'REG_3']], 123 | 124 | # sparse-switch v1, :sswitch_data_0 125 | # packed-switch p0, :pswitch_data_0 126 | [['sparse-switch', 'packed-switch'], [' +', REG, ', +', LBL], ['REG', 'LBL']], 127 | 128 | # array-length v1, v1 129 | [['array-length'], [' +', REG, ', +', REG], ['REG_1', 'REG_2']], 130 | 131 | # throw v0 132 | [['throw'], [' +', REG], ['REG']], 133 | 134 | # instance-of v8, v7, Landroid/widget/TextView; 135 | [['instance-of'], [' +', REG, ', +', REG, ', +', CLS], ['REG', 'CLS']], 136 | 137 | # monitor-enter p0 138 | [['monitor-enter'], [' +', REG], ['REG']], 139 | 140 | # int-to-double v3, v3 141 | [['int'], ['-to\-(\w+) +', REG, ', +', REG], ['TP', 'REG_1', 'REG_2']], 142 | 143 | # add/mul/sub/div/rem/add/or/xor/shl/shr/ushr 144 | # mul-double/2addr v3, v5 145 | # mul-double v0, v0, v2 146 | # add-int/lit8 v9, v8, 0x1 147 | [['add', 'mul', 'sub', 'div', 'rem', 'add', 'or', 'xor', 'shl', 'shr', 'ushr'], 148 | ['\-(\w+)(/([\w\d]+))* +', REG, ', +', REG, '(, +', NUM_16, ')*'], 149 | ['RV', None, 'RN', 'REG_1', 'REG_2', None, 'NUM']], 150 | 151 | # not/neg 152 | 153 | ] 154 | 155 | 156 | def make_gra(gfs): 157 | gra_s = [] 158 | for fmt in gfs: 159 | for st in fmt[0]: 160 | gra = [st] 161 | if len(fmt) >= 2: 162 | gra.append(st.replace('.', '\\.') + ''.join(fmt[1])) 163 | else: 164 | gra.append(None) 165 | if len(fmt) >= 3: 166 | gra.append(fmt[2]) 167 | else: 168 | gra.append(None) 169 | gra_s.append(gra) 170 | return gra_s 171 | 172 | 173 | GS_DOT = make_gra(GS_DOT) 174 | GS_CMD = make_gra(GS_CMD) 175 | 176 | 177 | def __get_attr(attr): 178 | ret = attr.split() 179 | return ret 180 | 181 | 182 | ATTR_T = { 183 | 'ATTR': __get_attr, 184 | } 185 | 186 | 187 | def to_op(line): 188 | if line.startswith('.'): 189 | ret = to(line, GS_DOT) 190 | else: 191 | ret = to(line, GS_CMD) 192 | return ret 193 | 194 | 195 | def to(line, gs): 196 | ret = None 197 | for g in gs: 198 | g_start = g[0] 199 | if line.startswith(g_start): 200 | g_pattern = g[1] 201 | if g_pattern is not None: 202 | mat = re.match(g_pattern, line) 203 | if mat: 204 | ret = {'CMD': g_start} 205 | g_keys = g[2] 206 | groups = mat.groups() 207 | for i in range(len(g_keys)): 208 | key = g_keys[i] 209 | value = groups[i] 210 | if value is not None and key is not None: 211 | ret[key] = value.strip() 212 | if key in ATTR_T: 213 | ret[key] = ATTR_T[key](ret[key]) 214 | break 215 | else: 216 | continue 217 | else: 218 | ret = {'CMD': g_start} 219 | break 220 | return ret 221 | 222 | -------------------------------------------------------------------------------- /smali2java.py: -------------------------------------------------------------------------------- 1 | import os 2 | import struct 3 | import sys 4 | 5 | import re 6 | import re_gra 7 | 8 | class JavaOp: 9 | def __init__(self, op): 10 | self.op = op 11 | self.output = None 12 | self.type = None 13 | self.input = [] 14 | self.label = None 15 | self.line = None 16 | self.lineEnd = False 17 | self.local = None # [reg, type, localName, restart] 18 | self.localEnd = [] 19 | 20 | def set_output(self, output): 21 | self.output = output 22 | 23 | def set_type(self, op_type): 24 | self.type = op_type 25 | 26 | def add_input(self, op_input): 27 | self.input.append(op_input) 28 | 29 | def set_input(self, op_input): 30 | self.input = op_input 31 | 32 | def set_label(self, label): 33 | self.label = label 34 | 35 | def set_line(self, line): 36 | self.line = line 37 | 38 | def set_line_end(self, end): 39 | self.lineEnd = end 40 | 41 | def is_line_end(self): 42 | return self.lineEnd 43 | 44 | def set_local(self, cls, var, mode=None): 45 | self.local = [cls, var, mode] 46 | 47 | def is_local(self): 48 | if self.local is not None: 49 | return True 50 | return False 51 | 52 | def set_local_end(self, reg): 53 | self.localEnd.append(reg) 54 | 55 | def get_local_end(self): 56 | return self.localEnd 57 | 58 | 59 | class JavaField: 60 | def __init__(self, name, f_type, value=None): 61 | self.name = name 62 | self.type = f_type 63 | self.attr = [] 64 | self.value = value 65 | 66 | def add_attr(self, attr): 67 | self.attr.append(attr) 68 | 69 | 70 | class JavaSwitch: 71 | def __init__(self): 72 | self.case = [] 73 | 74 | def add_case(self, name, label): 75 | self.case.append([name, label]) 76 | 77 | 78 | class JavaLabel: 79 | def __init__(self, name): 80 | self.name = name 81 | self.catch = None 82 | 83 | def set_start(self, start): 84 | self.start = start 85 | 86 | def set_end(self, end): 87 | self.end = end 88 | 89 | def set_catch(self, catch): 90 | self.catch = catch 91 | 92 | 93 | class JavaMethod: 94 | def __init__(self, name, ret_type=None): 95 | self.name = name 96 | self.attr = [] 97 | self.ret_type = ret_type 98 | self.paramType = [] 99 | self.param = [] 100 | self.op = [] 101 | self.label = {} 102 | self.switch = {} 103 | 104 | self.reg = JavaRegister() 105 | self.cur_line = None 106 | 107 | self.try_catch = [] 108 | 109 | self.local = [] 110 | 111 | pass 112 | 113 | def add_attr(self, attr): 114 | self.attr.append(attr) 115 | 116 | def add_param_type(self, param_type): 117 | self.paramType.append(param_type) 118 | 119 | def add_param(self, name): 120 | self.param.append(name) 121 | 122 | def add_op(self, op): 123 | self.op.append(op) 124 | 125 | def add_switch(self, name, data): 126 | self.switch[name] = data 127 | 128 | def add_label(self, name, data): 129 | self.label[name] = data 130 | 131 | def get_label(self, name): 132 | return self.label[name] 133 | 134 | def get_op_count(self): 135 | return len(self.op) 136 | 137 | def set_try_catch(self, label_start, label_end, exp_class, label_catch): 138 | self.try_catch.append([label_start, label_end, exp_class, label_catch]) 139 | 140 | def get_try_catch(self, label_end): 141 | for tryCatch in self.try_catch: 142 | if tryCatch[1] == label_end: 143 | return tryCatch 144 | return None 145 | 146 | def is_try_label(self, label): 147 | for try_catch in self.try_catch: 148 | if try_catch[0] == label: 149 | return True 150 | if try_catch[1] == label: 151 | return False 152 | return None 153 | 154 | def add_local(self, c_type, var): 155 | for local in self.local: 156 | if local[1] == var: 157 | return 158 | self.local.append([c_type, var]) 159 | 160 | 161 | class JavaClass: 162 | def __init__(self, name): 163 | self.name = name 164 | self.super = None 165 | self.attr = [] 166 | self.implement = [] 167 | self.method = [] 168 | self.field = [] 169 | pass 170 | 171 | def set_super(self, name): 172 | self.super = name 173 | 174 | def add_attr(self, attr): 175 | self.attr.append(attr) 176 | 177 | def add_implement(self, name): 178 | self.implement.append(name) 179 | 180 | def add_field(self, name, f_type, value=None): 181 | field = JavaField(name, f_type, value) 182 | self.field.append(field) 183 | return field 184 | 185 | def add_method(self, method): 186 | self.method.append(method) 187 | 188 | 189 | class JavaRegister: 190 | def __init__(self): 191 | self.register = {} 192 | self.local = {} 193 | self.const = {} 194 | 195 | def get_register(self, reg): 196 | if reg in self.register.keys() and self.register[reg] is not None: 197 | return self.register[reg] 198 | else: 199 | return reg 200 | 201 | def get_registers(self, regs): 202 | t_regs = [] 203 | for reg in regs: 204 | t_regs.append(self.get_register(reg)) 205 | return t_regs 206 | 207 | def set_register(self, reg, value, local=None, const=False): 208 | if not self.is_local(reg) or local is not None: 209 | self.register[reg] = value 210 | self.const[reg] = const 211 | 212 | if local: 213 | self.local[reg] = True 214 | elif not local: 215 | self.local[reg] = False 216 | 217 | def clear_register(self): 218 | for key in self.register.keys(): 219 | if not (self.is_local(key) or self.is_const(key)): 220 | self.register[key] = key 221 | 222 | def get_local(self, reg): 223 | if reg is None: 224 | return None 225 | if self.is_local(reg): 226 | local = self.get_register(reg) 227 | return local 228 | return reg 229 | 230 | def is_local(self, reg): 231 | if reg not in self.local.keys(): 232 | return False 233 | if not self.local[reg]: 234 | return False 235 | return True 236 | 237 | def is_const(self, reg): 238 | if reg not in self.const.keys(): 239 | return False 240 | if not self.const[reg]: 241 | return False 242 | return True 243 | 244 | 245 | class SmaliFile: 246 | PATTERN_SPLIT = '[ ,]+' 247 | 248 | def __init__(self, file_smali): 249 | self.file_smali = file_smali 250 | self.import_class = [] 251 | self.switchBase = 0 252 | 253 | # V void - Z boolean B byte S short C char I int J long F float D double 254 | self.FLAG = {'V': 'void', 'Z': 'boolean', 'B': 'byte', 'S': 'short', 'C': 'char', 'I': 'int', 'J': 'long', 'F': 'float', 'D': 'double'} 255 | 256 | self.COMPARE = {'eq': '==', 'ne': '!=', 'lt': '<', 'ge': '>=', 'gt': '>', 'le': '<='} 257 | 258 | self.RE_COMPARE = {'eq': '!=', 'ne': '==', 'lt': '>=', 'ge': '<', 'gt': '<=', 'le': '>'} 259 | 260 | self.CALCULATE = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/', 'rem': '%', 'and': '&', 'or': '|', 'xor': '^', 261 | 'shl': '<<', 'shr': '>>', 'ushr': '>>>', 'not': '!', 'neg': '-'} 262 | 263 | self.output_shift = 0 264 | 265 | self.annotation_mode = False 266 | self.array_data_mode = False 267 | 268 | self.java_op = None 269 | self.java_ops = None 270 | self.registers = None 271 | self.java_class = None 272 | self.java_method = None 273 | self.java_switch = None 274 | self.java_label = None 275 | 276 | self.cur_label = None 277 | self.cur_line = None 278 | 279 | self.line_info_enable = True 280 | self.local_info_enable = True 281 | 282 | self.access = {} 283 | self.java_access = None 284 | self.cur_access = None 285 | 286 | self.output_file = None 287 | 288 | @staticmethod 289 | def debug(msg): 290 | print(msg) 291 | pass 292 | 293 | def scan_file(self): 294 | fd_smali = open(self.file_smali) 295 | lines = fd_smali.readlines() 296 | fd_smali.close() 297 | 298 | for parent in self.get_parent_files(self.file_smali): 299 | fd_parent = open(parent, 'r') 300 | for line in fd_parent.readlines(): 301 | self.do_parent_translate(line) 302 | fd_parent.close() 303 | 304 | for line in lines: 305 | try: 306 | self.do_translate(line) 307 | except: # ValueError, IndexError: 308 | print(line) 309 | print(sys.exc_info()[0]) 310 | print(sys.exc_info()[1]) 311 | exit() 312 | 313 | def to_java(self, file_java): 314 | fd_java = open(file_java, 'w') 315 | self.output_file = fd_java 316 | 317 | classes = set(self.import_class) 318 | 319 | file_string = '' 320 | for cls in classes: 321 | file_string = 'import ' + cls 322 | # if cls != '': 323 | self.to_file(file_string) 324 | 325 | file_string = 'class ' 326 | # for attr in self.JavaClass.attr: 327 | # string = string + attr + ' ' 328 | file_string = file_string + self.java_class.name 329 | if self.java_class.super is not None: 330 | file_string = file_string + ' extends ' + self.java_class.super 331 | if len(self.java_class.implement) != 0: 332 | file_string = file_string + ' implement ' 333 | for i in range(len(self.java_class.implement)): 334 | if len(self.java_class.implement) == i + 1: 335 | file_string = file_string + self.java_class.implement[i] 336 | else: 337 | file_string = file_string + self.java_class.implement[i] + ', ' 338 | self.to_file(file_string) 339 | 340 | file_string = '{' 341 | self.to_file(file_string) 342 | 343 | self.to_file_shift(1) 344 | 345 | for field in self.java_class.field: 346 | file_string = '' 347 | for attr in field.attr: 348 | file_string = file_string + attr + ' ' 349 | file_string = file_string + field.type + ' ' + field.name 350 | if field.value is not None: 351 | file_string = file_string + ' = ' + field.value 352 | file_string = file_string + ';' 353 | 354 | self.to_file(file_string) 355 | 356 | for i in range(len(self.java_class.method)): 357 | self.output_method_op(i) 358 | self.to_file_shift(-1) 359 | 360 | file_string = '}' 361 | self.to_file(file_string) 362 | 363 | self.output_file = None 364 | fd_java.close() 365 | 366 | @staticmethod 367 | def get_parent_files(file_smali): 368 | path = os.path.dirname(file_smali) 369 | file = os.path.basename(file_smali) 370 | [file, ext] = os.path.splitext(file) 371 | 372 | ps = file.split('$') 373 | fps = [] 374 | 375 | for i in range(len(ps) - 1): 376 | p = ps[0] 377 | for ii in range(1, i + 1): 378 | p = p + '$' + ps[ii] 379 | 380 | fp = os.path.join(path, p + ext) 381 | if p != file and os.path.exists(fp): 382 | fps.append(fp) 383 | 384 | return fps 385 | 386 | def append_method_to_file(self, string, line=None): 387 | pass 388 | 389 | def do_translate(self, line): 390 | line = line.strip() 391 | 392 | if len(line) != 0: 393 | if line.startswith('.'): 394 | self.do_dot(line) 395 | elif self.annotation_mode: 396 | pass 397 | elif self.array_data_mode: 398 | if self.java_label is None: 399 | exit() 400 | pass 401 | elif self.java_switch is not None: 402 | self.do_switch_case(line) 403 | elif line.startswith(':'): 404 | self.do_label(line) 405 | elif line.startswith('#'): 406 | self.do_commit(line) 407 | else: 408 | self.do_command(line) 409 | 410 | def do_parent_translate(self, line): 411 | line = line.strip() 412 | 413 | if len(line) != 0: 414 | if line[0] == '.': 415 | self.do_parent_dot(line) 416 | elif self.java_access is not None: 417 | self.do_parent_command(line) 418 | 419 | def to_java_class_name(self, part, is_add=True): 420 | ret = '' 421 | obj = False 422 | array = 0 423 | start = 0 424 | for i in range(len(part)): 425 | if not obj: 426 | if part[i] == '[': 427 | array = array + 1 428 | elif part[i] == 'L': 429 | obj = True 430 | start = i 431 | else: 432 | ret = self.FLAG[part[i]] 433 | is_add = False 434 | break 435 | else: 436 | if part[i] == ';': 437 | cls = part[start + 1: i] 438 | cls = cls.replace('/', '.') 439 | ret = cls 440 | break 441 | if is_add: 442 | self.import_class.append(ret) 443 | 444 | ret = ret.split('.')[-1] 445 | 446 | while array > 0: 447 | ret = (ret + '[]') 448 | array = array - 1 449 | 450 | return ret 451 | 452 | def make_function(self, part): 453 | # part : Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V 454 | start = part.find('>') + 1 455 | end = part.find('(') 456 | 457 | func = part[start:end] 458 | if func == '': 459 | part2 = part.split(';') 460 | cls = self.to_java_class_name(part2[0] + ';') 461 | func = cls 462 | 463 | return func 464 | 465 | @staticmethod 466 | def make_params(part): 467 | # part : p0,p1 468 | # part : v0..v5 469 | if part.find('..') > 0: 470 | r_mode = True 471 | else: 472 | r_mode = False 473 | # part = part.replace('{', '') 474 | # part = part.replace('}', '') 475 | part = part.replace(',', ' ') 476 | part = part.replace('..', ' ') 477 | 478 | params = part.split() 479 | if not r_mode: 480 | if len(params) == 0 or params[0] == '': 481 | return [] 482 | return params 483 | else: 484 | param2s = [] 485 | vp = params[0][0] 486 | st = int(params[0][1:]) 487 | end = int(params[1][1:]) + 1 488 | for i in range(st, end): 489 | param2s.append(vp + str(i)) 490 | return param2s 491 | 492 | @staticmethod 493 | def make_field(part): 494 | # Lcom/tutor/apkinstaller/ApkInstaller;->apkWeb:Landroid/webkit/WebView; 495 | start = part.find('>') + 1 496 | end = part.find(':') 497 | return part[start:end] 498 | 499 | def make_params_class(self, part): 500 | cs = [] 501 | obj = False 502 | array = 0 503 | start = 0 504 | for i in range(len(part)): 505 | if not obj: 506 | if part[i] == '[': 507 | array = array + 1 508 | elif part[i] == 'L': 509 | obj = True 510 | start = i 511 | else: 512 | cls = part[i - array:i + array + 1] 513 | cls = self.to_java_class_name(cls) 514 | cs.append(cls) 515 | obj = False 516 | array = 0 517 | else: 518 | if part[i] == ';': 519 | cls = part[start - array:i + 1] 520 | cls = self.to_java_class_name(cls) 521 | cs.append(cls) 522 | obj = False 523 | array = 0 524 | return cs 525 | 526 | def do_dot(self, line): 527 | op = re_gra.to_op(line) 528 | if op is not None: 529 | cmd = op['CMD'] 530 | if cmd == '.line': 531 | number = op['NUM'] 532 | if self.cur_line is not None and self.java_op is not None: 533 | self.java_op.set_line_end(True) 534 | self.cur_line = number 535 | elif cmd == '.class': 536 | self.java_class = JavaClass(op['CLS']) 537 | for attr in op['ATTR']: 538 | self.java_class.add_attr(attr) 539 | elif cmd == '.super': 540 | name = self.to_java_class_name(op['CLS']) 541 | self.java_class.set_super(name) 542 | elif cmd == '.source': 543 | pass 544 | elif cmd == '.implements': 545 | name = self.to_java_class_name(op['CLS']) 546 | self.java_class.add_implement(name) 547 | elif cmd == '.field': 548 | var = op['VAR'] 549 | vt = self.to_java_class_name(op['VT']) 550 | value = None 551 | if 'VAL' in op: 552 | value = op['VAL'] 553 | field = self.java_class.add_field(var, vt, value) 554 | for attr in op['ATTR']: 555 | field.add_attr(attr) # self.JavaClass.add_attr(attr) 556 | elif cmd == '.end field': 557 | pass 558 | elif cmd == '.method': 559 | if op['FUNC'] != '': 560 | func_name = op['FUNC'] 561 | else: 562 | func_name = self.java_class.name 563 | fun_types = self.make_params_class(op['PARAMS']) 564 | fun_ret = self.to_java_class_name(op['FT']) 565 | self.java_method = JavaMethod(func_name, fun_ret) 566 | self.java_class.add_method(self.java_method) 567 | for attr in op['ATTR']: 568 | self.java_method.add_attr(attr) 569 | for fun_type in fun_types: 570 | self.java_method.add_param_type(fun_type) 571 | self.java_op = None 572 | self.java_ops = {} 573 | self.registers = {} 574 | elif cmd == '.parameter': 575 | self.java_method.add_param(op['VAR']) 576 | elif cmd == '.param': 577 | if 'VAR' in op: 578 | self.java_method.add_param(op['VAR']) 579 | elif cmd == '.end param': 580 | pass 581 | elif cmd == '.prologue': 582 | pass 583 | elif cmd == '.end method': 584 | self.java_method = None 585 | self.java_op = None 586 | self.java_ops = None 587 | self.registers = None 588 | self.cur_line = None 589 | self.cur_label = None 590 | elif cmd == '.locals': 591 | pass 592 | elif cmd == '.local' or cmd == '.restart local': 593 | mode = False 594 | var = op['VAR'] 595 | reg = op['REG'] 596 | cls = self.to_java_class_name(op['CLS']) 597 | if reg[0:2] != 'p0': 598 | if self.java_op is not None and ( 599 | self.java_op.output == reg or self.java_method.reg.get_register( 600 | self.java_op.output) == reg): 601 | self.java_op.set_local(cls, var, mode) 602 | self.java_method.add_local(cls, var) 603 | else: 604 | if self.java_label is not None and self.java_label.catch == reg: 605 | self.java_label.catch = var 606 | java_opl = JavaOp('nop') 607 | java_opl.set_output(reg) 608 | self.java_method.add_op(java_opl) 609 | java_opl.set_local(cls, var, mode) 610 | self.java_method.add_local(cls, var) 611 | elif cmd == '.end local': 612 | self.java_op.set_local_end(op['REG']) 613 | elif cmd == '.array-data': 614 | self.array_data_mode = True 615 | elif cmd == '.end array-data': 616 | self.array_data_mode = False 617 | elif cmd == '.annotation': 618 | self.annotation_mode = True 619 | elif cmd == '.end annotation': 620 | self.annotation_mode = False 621 | elif cmd == '.sparse-switch' or cmd == '.packed-switch': 622 | if 'VAL' in op: 623 | self.switchBase = int(op['VAL'], 16) 624 | self.java_switch = JavaSwitch() 625 | self.java_method.add_switch(self.java_label.name, self.java_switch) 626 | 627 | elif cmd == '.end sparse-switch' or cmd == '.end packed-switch': 628 | self.java_switch = None 629 | elif cmd == '.catch' or cmd == '.catchall': 630 | if cmd == '.catchall': 631 | cls = 'Exception' 632 | else: 633 | cls = self.to_java_class_name(op['CLS']) 634 | label_catch = op['LBL_C'] 635 | label_try_start = op['LBL_TS'] 636 | label_try_end = op['LBL_TE'] 637 | self.java_method.set_try_catch(label_try_start, label_try_end, cls, label_catch) 638 | java_opl = JavaOp('catch') 639 | java_opl.add_input(cls) 640 | java_opl.add_input(label_catch) 641 | self.java_method.add_op(java_opl) 642 | elif cmd == '.source': 643 | pass 644 | elif cmd == '.registers': 645 | pass 646 | else: 647 | self.debug('' + line) 648 | else: 649 | self.debug('' + line) 650 | 651 | def do_label(self, line): # fix 652 | # :cond_1 653 | name = line[1:] 654 | 655 | self.cur_label = name 656 | self.java_label = JavaLabel(name) 657 | self.java_method.add_label(name, self.java_label) 658 | self.java_label.set_start(self.java_method.get_op_count()) 659 | 660 | if self.cur_line is not None and self.java_op is not None: 661 | self.java_op.set_line_end(True) 662 | # self.JavaOp = None 663 | java_opl = JavaOp('nop') 664 | self.java_method.add_op(java_opl) 665 | java_opl.set_label(self.cur_label) 666 | 667 | def do_commit(self, line): 668 | pass 669 | 670 | def do_command(self, line): 671 | if line.startswith('invoke'): 672 | self.do_invoke(line) 673 | elif line.startswith('const'): 674 | if line[6:10] == 'wide': # 64 bit 675 | self.do_const(line, 2) 676 | if line[6:12] == 'high16': 677 | self.do_const(line, 0) # 16 bit 678 | else: 679 | self.do_const(line, 1) # 32 bit 680 | elif line.startswith('iget'): 681 | self.do_put_get(line, 'get') 682 | elif line.startswith('iput'): 683 | self.do_put_get(line, 'put') 684 | elif line.startswith('sget'): 685 | self.do_static_put_get(line, 'sget') 686 | elif line.startswith('sput'): 687 | self.do_static_put_get(line, 'sput') 688 | elif line.startswith('aget'): 689 | self.do_array_put_get(line, 'aget') 690 | elif line.startswith('aput'): 691 | self.do_array_put_get(line, 'aput') 692 | elif line.startswith('move'): 693 | self.do_move(line) 694 | elif line.startswith('return'): 695 | self.do_return(line) 696 | elif line.startswith('goto'): 697 | self.do_goto(line) 698 | elif line.startswith('new'): 699 | self.do_new(line) 700 | elif line.startswith('fill-array-data'): 701 | self.do_fill_array(line) 702 | elif line.startswith('if'): 703 | self.do_if(line) 704 | elif line.startswith('check'): 705 | self.do_check(line) 706 | elif line.find('-to-') > 0: 707 | self.do_to(line) 708 | elif line.startswith('add') or line.startswith('sub') or line.startswith('mul') or line.startswith( 709 | 'div') or line.startswith('rem') or line.startswith('and') or line.startswith('or') or line.startswith( 710 | 'xor') or line.startswith('shl') or line.startswith('shr') or line.startswith('ushr'): 711 | self.do_calculate2(line) 712 | elif line.startswith('not') or line.startswith('neg'): 713 | self.do_calculate(line) 714 | elif line.startswith('cmp'): 715 | self.do_cmp(line) 716 | elif line.startswith('sparse-switch') or line.startswith('packed-switch'): 717 | self.do_switch(line) 718 | pass 719 | elif line.startswith('array-length'): 720 | self.do_array(line) 721 | elif line.startswith('throw'): 722 | self.do_throw(line) 723 | elif line.startswith('instance-of'): 724 | self.do_instance_of(line) 725 | elif line.startswith('nop'): 726 | self.java_op = JavaOp('nop') 727 | self.java_method.add_op(self.java_op) 728 | elif line.startswith('monitor'): 729 | self.do_monitor(line) 730 | else: 731 | # execute-inline 732 | # invoke-virtual/range {vx..vy},methodtocall 733 | # filled-new-array {parameters},type_id 734 | # const-class vx,type_id 735 | self.debug(' command:' + line) 736 | 737 | # if self.cur_label != None and self.JavaOp != None: 738 | # self.JavaOp.set_label(self.cur_label) 739 | # self.cur_label = None 740 | if self.java_op is not None and self.cur_line is not None: 741 | self.java_op.set_line(self.cur_line) # self.cur_line = None 742 | 743 | def do_invoke(self, line): 744 | line = line.replace(', ', ',') 745 | line = line.replace(' .. ', '..') 746 | part = line.split('},') 747 | part2 = part[0].split() 748 | func = self.make_function(part[1]) 749 | params = self.make_params(part2[1][1:]) 750 | cs = self.make_params_class(part[1].split('(')[1].split(')')[0]) 751 | # print cs 752 | 753 | s = 0 754 | if not line.startswith('-static', 6): 755 | s = 1 756 | 757 | for i in range(len(cs)): 758 | if cs[i] == 'double' or cs[i] == 'long': 759 | del params[i + 1 + s] 760 | # print params 761 | 762 | ret_class = line.split(')')[-1] 763 | 764 | if ret_class != 'V': 765 | pass 766 | 767 | if line.startswith('-super', 6): # fix 768 | # invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V 769 | self.java_op = JavaOp('invoke') 770 | self.java_op.add_input('super') 771 | self.java_op.add_input(func) 772 | del params[0] 773 | # for param in params: 774 | # self.JavaOp.add_input(param) 775 | for i in range(len(cs)): 776 | self.java_op.add_input(cs[i]) 777 | self.java_op.add_input(params[i]) 778 | 779 | self.java_op.set_type(ret_class) 780 | self.java_method.add_op(self.java_op) 781 | 782 | elif line.startswith('-direct', 6): # fix 783 | # invoke-direct {v0}, Ljava/lang/RuntimeException;->()V 784 | for i in range(len(params)): 785 | if self.registers is not None and params[i] in self.registers.keys(): 786 | params[i] = self.registers[params[i]] 787 | self.registers = None 788 | 789 | obj = params[0] 790 | 791 | if obj in self.java_ops.keys(): 792 | self.java_op = JavaOp('new-invoke') 793 | self.java_op.set_output(self.java_ops[obj].output) 794 | self.java_op.set_input(self.java_ops[obj].input) 795 | # self.JavaOp = self.JavaOps[obj] 796 | # for i in range(1,len(params)): 797 | # self.JavaOp.add_input(params[i]) 798 | del params[0] 799 | for i in range(len(cs)): 800 | self.java_op.add_input(cs[i]) 801 | self.java_op.add_input(params[i]) 802 | self.java_method.add_op(self.java_op) 803 | del self.java_ops[obj] 804 | else: 805 | self.java_op = JavaOp('invoke') 806 | self.java_op.add_input(params[0]) 807 | self.java_op.add_input(func) 808 | # for i in range(1,len(params)): 809 | # self.JavaOp.add_input(params[i]) 810 | del params[0] 811 | for i in range(len(cs)): 812 | self.java_op.add_input(cs[i]) 813 | self.java_op.add_input(params[i]) 814 | self.java_op.set_type(ret_class) 815 | self.java_method.add_op(self.java_op) 816 | 817 | elif line.startswith('-static', 6): 818 | # invoke-static {p1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z 819 | part3 = part[1].split(';') 820 | func_class = self.to_java_class_name(part3[0] + ';') 821 | 822 | self.java_op = JavaOp('invoke') 823 | self.java_op.add_input(func_class) 824 | self.java_op.add_input(func) 825 | # for param in params: 826 | # self.JavaOp.add_input(param) 827 | for i in range(len(cs)): 828 | self.java_op.add_input(cs[i]) 829 | self.java_op.add_input(params[i]) 830 | self.java_op.set_type(ret_class) 831 | self.java_method.add_op(self.java_op) 832 | 833 | elif line.startswith('-virtual', 6) or line.startswith('-interface', 6): 834 | # invoke-virtual {v2, v0}, Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V 835 | # func_class = self.make_class(part3[0]+';') 836 | 837 | self.java_op = JavaOp('invoke') 838 | self.java_op.add_input(params[0]) 839 | self.java_op.add_input(func) 840 | # for i in range(1,len(params)): 841 | # self.JavaOp.add_input(params[i]) 842 | del params[0] 843 | for i in range(len(cs)): 844 | self.java_op.add_input(cs[i]) 845 | self.java_op.add_input(params[i]) 846 | 847 | self.java_op.set_type(ret_class) 848 | self.java_method.add_op(self.java_op) 849 | 850 | else: 851 | self.debug(' invoke :' + line) 852 | 853 | @staticmethod 854 | def next_reg(reg): 855 | n = int(reg[1:]) 856 | n = n + 1 857 | ret = reg[0] + str(n) 858 | return ret 859 | 860 | @staticmethod 861 | def get_value(value, bit): 862 | if value[0] != "0" and value[0] != "-": 863 | return value 864 | if value[-1] == 'L': 865 | value = value[0:-1] 866 | ret = int(value, 16) >> (bit * 8) 867 | if ret < 0: 868 | ret = - ((-ret) & 0xFFFFFFFF) 869 | else: 870 | ret = ret & 0xFFFFFFFF 871 | # return hex(ret)[0:-1] 872 | return str(ret) 873 | 874 | def do_const(self, line, bit): 875 | # const/high16 v2, 0x7f03 876 | # const/high16 p0, 0x3f800000 # 1.0f 877 | # const-string v2, ", " 878 | part = line.split(',', 1) 879 | part2 = part[0].split() 880 | var = part2[1] 881 | part[1] = part[1].split('#')[0] 882 | part[1] = part[1].strip() 883 | if bit == 0: 884 | if part[1][0] == '-': 885 | value = 0x10000 + int(part[1], 16) 886 | value = hex(value) + '0000' 887 | else: 888 | value = part[1].strip() + '0000' 889 | bit = 1 890 | else: 891 | value = part[1].strip() 892 | 893 | for i in range(bit): 894 | v = self.get_value(value, i) 895 | self.java_op = JavaOp('const') 896 | self.java_op.set_output(var) 897 | self.java_op.add_input(v) 898 | self.java_op.add_input(bit) 899 | self.java_method.add_op(self.java_op) 900 | var = self.next_reg(var) 901 | 902 | def do_put_get(self, line, pg): # fix 903 | # iput-object v2, p0, Lcom/tutor/apkinstaller/ApkInstaller;->apkWeb:Landroid/webkit/WebView; 904 | line = line.replace(', ', ',') 905 | part = line.split() 906 | part2 = part[1].split(',') 907 | field = self.make_field(part2[2]) 908 | obj = part2[1] 909 | value = part2[0] 910 | 911 | self.java_op = JavaOp(pg) # put/get 912 | # self.JavaOp.set_output(value) 913 | self.java_op.add_input(obj) 914 | self.java_op.add_input(field) 915 | if pg == 'put': 916 | self.java_op.add_input(value) 917 | else: 918 | self.java_op.set_output(value) 919 | 920 | self.java_method.add_op(self.java_op) 921 | 922 | def do_static_put_get(self, line, pg): # fix 923 | # sput-object v1, Lcom/moji/mjweather/activity/AddCityActivity;->mHotCitys:[Ljava/lang/String; 924 | line = line.replace(', ', ' ') 925 | part = line.split() 926 | cls = self.to_java_class_name(part[2]) 927 | field = self.make_field(part[2]) 928 | value = part[1] 929 | 930 | self.java_op = JavaOp(pg) # sput/sget 931 | # self.JavaOp.set_output(value) 932 | self.java_op.add_input(cls) 933 | self.java_op.add_input(field) 934 | if pg == 'sput': 935 | self.java_op.add_input(value) 936 | else: 937 | self.java_op.set_output(value) 938 | self.java_method.add_op(self.java_op) 939 | 940 | def do_array_put_get(self, line, pg): # fix 941 | # aget-object v3, v3, v0 942 | line = line.replace(', ', ' ') 943 | part = line.split() 944 | 945 | value = part[1] 946 | obj = (part[2]) 947 | aid = (part[3]) 948 | 949 | self.java_op = JavaOp(pg) # aput/aget 950 | # self.JavaOp.set_output(value) 951 | self.java_op.add_input(obj) 952 | self.java_op.add_input(aid) 953 | if pg == 'aput': 954 | self.java_op.add_input(value) 955 | else: 956 | self.java_op.set_output(value) 957 | self.java_method.add_op(self.java_op) 958 | 959 | def do_move(self, line): 960 | if line.startswith('-result', 4): 961 | # move-result-object v2 962 | part = line.split() 963 | ret = part[1] 964 | 965 | self.java_op.set_output(ret) 966 | pass 967 | elif line.startswith('-exception', 4): 968 | # move-exception v0 969 | part = line.split() 970 | ret = part[1] 971 | 972 | java_label = self.java_method.get_label(self.cur_label) 973 | 974 | if java_label is not None: 975 | java_label.set_catch(ret) 976 | else: 977 | print('') 978 | print(self.java_op.op) 979 | # self.JavaOp = JavaOp('move-exception') 980 | # self.JavaOp.set_output(ret) 981 | # self.JavaOp.add_input('exception') 982 | # self.JavaMethod.add_op(self.JavaOp) 983 | pass 984 | else: 985 | # move-object/from16 v0, p0 986 | line = line.replace(', ', ' ') 987 | part = line.split() 988 | var = part[1] 989 | value = part[2] 990 | self.java_op = JavaOp('move') 991 | self.java_op.set_output(var) 992 | self.java_op.add_input(value) 993 | self.java_method.add_op(self.java_op) 994 | if self.registers is not None: 995 | self.registers[var] = value 996 | 997 | def do_return(self, line): 998 | if line.startswith('-void', 6): 999 | # return-void 1000 | self.java_op = JavaOp('return') 1001 | self.java_method.add_op(self.java_op) 1002 | else: 1003 | part = line.split() 1004 | if len(part) > 1: 1005 | ret = part[1] 1006 | 1007 | self.java_op = JavaOp('return') 1008 | self.java_op.add_input(ret) 1009 | self.java_method.add_op(self.java_op) 1010 | 1011 | if self.java_label is not None: 1012 | self.java_label.set_end(self.java_method.get_op_count()) 1013 | self.java_label = None 1014 | 1015 | def do_goto(self, line): 1016 | part = line.split(':') 1017 | label = part[1] 1018 | 1019 | if self.java_op is not None: 1020 | self.java_op.set_line_end(True) 1021 | 1022 | self.java_op = JavaOp('goto') 1023 | self.java_op.add_input(label) 1024 | self.java_method.add_op(self.java_op) 1025 | 1026 | if self.java_label is not None: 1027 | self.java_label.set_end(self.java_method.get_op_count()) 1028 | self.java_label = None 1029 | 1030 | def do_new(self, line): 1031 | line = line.replace(',', '') 1032 | part = line.split() 1033 | if line.startswith('-instance', 3): 1034 | # new-instance v1, Lcom/moji/mjweather/activity/AddCityActivity$1; 1035 | var = part[1] 1036 | cls = self.to_java_class_name(part[2]) 1037 | 1038 | self.java_op = JavaOp('new') 1039 | self.java_op.set_output(var) 1040 | self.java_op.add_input(cls) 1041 | self.java_method.add_op(self.java_op) 1042 | 1043 | self.java_ops[var] = self.java_op 1044 | self.registers = {} 1045 | 1046 | elif line.startswith('-array', 3): 1047 | # new-array v1, v1, [Ljava/lang/String; 1048 | var = part[1] 1049 | size = (part[2]) 1050 | cls = self.to_java_class_name(part[3]) 1051 | 1052 | self.java_op = JavaOp('anew') 1053 | self.java_op.set_output(var) 1054 | self.java_op.add_input(cls) 1055 | self.java_op.add_input(size) 1056 | self.java_method.add_op(self.java_op) 1057 | 1058 | def do_fill_array(self, line): 1059 | # fill-array-data v0, :array_0 1060 | line = line.replace(',', '') 1061 | part = line.split() 1062 | 1063 | if self.java_op is not None and self.java_op.op == 'anew': 1064 | label = part[2][1:] 1065 | self.java_op.add_input(label) 1066 | else: 1067 | print(' fill array') 1068 | 1069 | def do_if(self, line): 1070 | line = line.replace(',', '') 1071 | part = line.split() 1072 | if line.startswith('z', 5): 1073 | # if-nez v1, :cond_0 1074 | cal = part[0][3:5] 1075 | c0 = (part[1]) 1076 | label = part[2][1:] 1077 | 1078 | self.java_op = JavaOp('ifz') 1079 | self.java_op.add_input(cal) 1080 | self.java_op.add_input(c0) 1081 | self.java_op.add_input(label) 1082 | self.java_method.add_op(self.java_op) 1083 | else: 1084 | # if-ge v0, v1, :cond_1 1085 | cal = part[0][3:5] 1086 | c0 = (part[1]) 1087 | c1 = (part[2]) 1088 | label = part[3][1:] 1089 | 1090 | self.java_op = JavaOp('if') 1091 | self.java_op.add_input(cal) 1092 | self.java_op.add_input(c0) 1093 | self.java_op.add_input(c1) 1094 | self.java_op.add_input(label) 1095 | self.java_method.add_op(self.java_op) 1096 | 1097 | def do_check(self, line): 1098 | # check-cast v1, Landroid/widget/ImageButton; 1099 | line = line.replace(',', '') 1100 | part = line.split() 1101 | var = part[1] 1102 | cls = self.to_java_class_name(part[2]) 1103 | 1104 | self.java_op = JavaOp('check') 1105 | self.java_op.set_output(var) 1106 | self.java_op.add_input(var) 1107 | self.java_op.add_input(cls) 1108 | self.java_method.add_op(self.java_op) 1109 | 1110 | def do_calculate2(self, line): # fix 1111 | # mul-double/2addr v3, v5 1112 | line = line.replace(',', ' ') 1113 | part = line.split() 1114 | part2 = part[0].split('-') 1115 | 1116 | if part[0].find('float') > 0: 1117 | fpart = '-float' 1118 | else: 1119 | fpart = '' 1120 | 1121 | if part[0].find('2addr') > 0: 1122 | ret = part[1] 1123 | cal = part2[0] 1124 | p1 = part[1] 1125 | p2 = part[2] 1126 | 1127 | self.java_op = JavaOp('cal2' + fpart) 1128 | self.java_op.set_output(ret) 1129 | self.java_op.add_input(cal) 1130 | self.java_op.add_input(p1) 1131 | self.java_op.add_input(p2) 1132 | self.java_method.add_op(self.java_op) 1133 | 1134 | elif part[0].find('lit') > 0: 1135 | # add-int/lit8 v9, v8, 0x1 1136 | ret = part[1] 1137 | cal = part2[0] 1138 | p1 = (part[2]) 1139 | p2 = part[3] 1140 | 1141 | self.java_op = JavaOp('cal2-lit' + fpart) 1142 | self.java_op.set_output(ret) 1143 | self.java_op.add_input(cal) 1144 | self.java_op.add_input(p1) 1145 | self.java_op.add_input(p2) 1146 | self.java_method.add_op(self.java_op) 1147 | else: 1148 | ret = part[1] 1149 | cal = part2[0] 1150 | p1 = (part[2]) 1151 | p2 = (part[3]) 1152 | 1153 | self.java_op = JavaOp('cal2' + fpart) 1154 | self.java_op.set_output(ret) 1155 | self.java_op.add_input(cal) 1156 | self.java_op.add_input(p1) 1157 | self.java_op.add_input(p2) 1158 | self.java_method.add_op(self.java_op) 1159 | 1160 | def do_calculate(self, line): 1161 | # not-int vx, vy 1162 | line = line.replace(',', ' ') 1163 | part = line.split() 1164 | part2 = part[0].split('-') 1165 | 1166 | var = part[1] 1167 | value = (part[2]) 1168 | cal = part2[0] 1169 | 1170 | self.java_op = JavaOp('cal') 1171 | self.java_op.set_output(var) 1172 | self.java_op.add_input(cal) 1173 | self.java_op.add_input(value) 1174 | self.java_method.add_op(self.java_op) 1175 | 1176 | def do_cmp(self, line): 1177 | # cmpl-float v13, v11, v7 1178 | line = line.replace(',', ' ') 1179 | part = line.split() 1180 | ret = part[1] 1181 | value1 = (part[2]) 1182 | value2 = (part[3]) 1183 | 1184 | self.java_op = JavaOp('cmp') 1185 | self.java_op.set_output(ret) 1186 | self.java_op.add_input(value1) 1187 | self.java_op.add_input(value2) 1188 | self.java_method.add_op(self.java_op) 1189 | 1190 | def do_switch(self, line): # fix 1191 | # sparse-switch v1, :sswitch_data_0 1192 | # packed-switch p0, :pswitch_data_0 1193 | line = line.replace(',', ' ') 1194 | part = line.split() 1195 | 1196 | var = (part[1]) 1197 | data = part[2][1:] 1198 | 1199 | self.java_op = JavaOp('switch') 1200 | self.java_op.add_input(var) 1201 | self.java_op.add_input(data) 1202 | self.java_method.add_op(self.java_op) 1203 | 1204 | def do_switch_case(self, line): 1205 | line = line.replace(' ', '') 1206 | part = line.split('->') 1207 | 1208 | if len(part) == 2: 1209 | # 0x1 -> :sswitch_0 1210 | value = part[0] 1211 | label = part[1][1:] 1212 | 1213 | else: 1214 | # :pswitch_0 1215 | value = str(self.switchBase) 1216 | self.switchBase = self.switchBase + 1 1217 | label = part[0][1:] 1218 | 1219 | self.java_switch.add_case(value, label) 1220 | 1221 | def do_array(self, line): 1222 | # array-length v1, v1 1223 | line = line.replace(',', ' ') 1224 | part = line.split() 1225 | ret = part[1] 1226 | obj = (part[2]) 1227 | 1228 | self.java_op = JavaOp('length') 1229 | self.java_op.set_output(ret) 1230 | self.java_op.add_input(obj) 1231 | self.java_method.add_op(self.java_op) 1232 | 1233 | def do_to(self, line): 1234 | # int-to-double v3, v3 1235 | line = line.replace(',', '') 1236 | part = line.split() 1237 | part2 = part[0].split('-to-') 1238 | t_data = part[1] 1239 | f_data = part[2] 1240 | t_type = part2[1] 1241 | 1242 | self.java_op = JavaOp('to') 1243 | self.java_op.set_output(t_data) 1244 | self.java_op.add_input(t_type) 1245 | self.java_op.add_input(f_data) 1246 | self.java_method.add_op(self.java_op) 1247 | 1248 | def do_throw(self, line): 1249 | # throw v0 1250 | part = line.split() 1251 | value = (part[1]) 1252 | 1253 | self.java_op = JavaOp('throw') 1254 | self.java_op.add_input(value) 1255 | self.java_method.add_op(self.java_op) 1256 | 1257 | if self.java_label is not None: 1258 | self.java_label.set_end(self.java_method.get_op_count()) 1259 | self.java_label = None 1260 | 1261 | def do_instance_of(self, line): 1262 | # instance-of v8, v7, Landroid/widget/TextView; 1263 | line = line.replace(',', '') 1264 | part = line.split() 1265 | reg = part[1] 1266 | value = (part[2]) 1267 | cls = self.to_java_class_name(part[3]) 1268 | 1269 | self.java_op = JavaOp('instanceof') 1270 | self.java_op.set_output(reg) 1271 | self.java_op.add_input(value) 1272 | self.java_op.add_input(cls) 1273 | self.java_method.add_op(self.java_op) 1274 | 1275 | def do_monitor(self, line): 1276 | # monitor-enter p0 1277 | self.java_op = JavaOp('monitor') 1278 | self.java_method.add_op(self.java_op) 1279 | 1280 | if line.startswith('-enter', 7): 1281 | pass 1282 | elif line.startswith('-exit', 7): 1283 | pass 1284 | else: 1285 | self.debug(' ' + line) 1286 | 1287 | def get_access(self, name): 1288 | if name in self.access.keys(): 1289 | return self.access[name] 1290 | return None 1291 | 1292 | def output_method_op(self, mid): 1293 | method = self.java_class.method[mid] 1294 | if method.name.startswith('access$'): 1295 | return 1296 | 1297 | # print '\tFunction Name: ' + method.name 1298 | method_static_offset = 1 1299 | string = '' 1300 | if method.name != '': 1301 | for attr in method.attr: 1302 | string = string + attr + '' 1303 | if attr == 'static': 1304 | method_static_offset = 0 1305 | 1306 | string = method.ret_type + ' ' + method.name + '(' 1307 | 1308 | if method_static_offset == 1: 1309 | method.reg.set_register('p0', 'this', True) 1310 | 1311 | for i in range(len(method.paramType)): 1312 | if i < len(method.param): 1313 | string = string + method.paramType[i] + ' ' + method.param[i] 1314 | method.reg.set_register('p' + str(i + method_static_offset), method.param[i], True) 1315 | else: 1316 | string = string + method.paramType[i] + ' ' + 'p' + str(i + method_static_offset) 1317 | method.reg.set_register('p' + str(i + method_static_offset), 'p' + str(i + method_static_offset), 1318 | True) 1319 | 1320 | if len(method.paramType) != i + 1: 1321 | string = string + ',' 1322 | string = string + ')' 1323 | else: 1324 | string = string + 'static' 1325 | string = string + '{' 1326 | self.to_file(string) 1327 | self.to_file_shift(1) 1328 | 1329 | for local in method.local: 1330 | string = local[0] + ' ' + local[1] + ';' 1331 | self.to_file(string) 1332 | 1333 | for oid in range(len(method.op)): 1334 | self.op2java(method, method.op, oid) 1335 | self.to_file_shift(-1) 1336 | 1337 | string = '}' 1338 | self.to_file(string) 1339 | self.to_file('') 1340 | 1341 | def to_file_shift(self, shift): 1342 | self.output_shift = self.output_shift + shift 1343 | 1344 | def to_file(self, string, shift=None): 1345 | if shift is None: 1346 | shift = self.output_shift 1347 | for i in range(shift): 1348 | string = '\t' + string 1349 | 1350 | self.output_file.write(string + '\n') 1351 | 1352 | @staticmethod 1353 | def op_to_show(method, op, output, string, const=False): 1354 | if op.is_local(): 1355 | c_var = op.local[1] 1356 | if c_var != string: 1357 | string = c_var + ' = ' + string + ';' 1358 | else: 1359 | string = '' 1360 | method.reg.set_register(op.output, c_var, True) 1361 | elif op.is_line_end(): 1362 | string = output + ' = ' + string + ';' 1363 | method.reg.set_register(op.output, None) 1364 | else: 1365 | method.reg.set_register(op.output, string, None, const) 1366 | string = '' 1367 | return string 1368 | 1369 | @staticmethod 1370 | def op_to_show_dircet(method, op, output, string): 1371 | if op.is_local(): 1372 | cvar = op.local[1] 1373 | string = cvar + ' = ' + string + ';' 1374 | method.reg.set_register(op.output, cvar, True) 1375 | else: 1376 | string = output + ' = ' + string + ';' 1377 | method.reg.set_register(op.output, None) 1378 | return string 1379 | 1380 | @staticmethod 1381 | def float2mbf4byte(f): 1382 | p_ieee = struct.pack('f', f) 1383 | sbin = [0] * 4 1384 | for i in range(4): 1385 | sbin[i] = ord(p_ieee[i]) 1386 | return sbin 1387 | 1388 | @staticmethod 1389 | def to_float(f_str): 1390 | flag = '' 1391 | if f_str.startswith('-'): 1392 | return f_str 1393 | 1394 | if f_str.isdigit(): 1395 | num = (int(f_str)) 1396 | f_str = num.to_bytes(length=4, byteorder='big') 1397 | 1398 | return str(struct.unpack('f', f_str)[0]) 1399 | else: 1400 | return flag + f_str 1401 | 1402 | def op2java(self, method, ops, oid): 1403 | op = ops[oid] 1404 | 1405 | inputs = method.reg.get_registers(op.input) 1406 | output_l = method.reg.get_local(op.output) 1407 | 1408 | regs = op.get_local_end() 1409 | for reg in regs: 1410 | method.reg.set_register(reg, None, False) 1411 | 1412 | if op.label is not None: 1413 | mode = method.is_try_label(op.label) 1414 | if mode is None: 1415 | if not op.label.startswith('sswitch_data_') and not op.label.startswith('pswitch_data_'): 1416 | self.to_file(op.label + ':', 0) 1417 | if op.label.startswith('cond_'): 1418 | method.reg.clear_register() 1419 | elif mode: 1420 | self.to_file('try{') 1421 | self.to_file_shift(1) 1422 | elif not mode: 1423 | self.to_file_shift(-1) 1424 | self.to_file('}') 1425 | if op.line is not None and method.cur_line != op.line: 1426 | self.to_file('//line ' + op.line, 0) 1427 | method.cur_line = op.line 1428 | 1429 | op_string = None 1430 | if op.op == 'catch': 1431 | cls = op.input[0] 1432 | label = op.input[1] 1433 | 1434 | java_label = method.get_label(label) 1435 | 1436 | if java_label.catch is None: 1437 | var = 'exp' 1438 | else: 1439 | var = method.reg.get_local(java_label.catch) 1440 | 1441 | self.to_file('catch (' + cls + ' ' + var + '){') 1442 | self.to_file_shift(1) 1443 | self.to_file('goto ' + label + ';') 1444 | self.to_file_shift(-1) 1445 | # self.to_file('}') 1446 | op_string = '}' 1447 | pass 1448 | elif op.op == 'sget': 1449 | if self.java_class.name != inputs[0]: 1450 | op_string = inputs[0] + '.' + inputs[1] 1451 | else: 1452 | op_string = inputs[1] 1453 | op_string = self.op_to_show(method, op, output_l, op_string) 1454 | elif op.op == 'sput': 1455 | if self.java_class.name != inputs[0]: 1456 | op_string = inputs[0] + '.' + op.input[1] + ' = ' + inputs[2] + ';' 1457 | else: 1458 | op_string = op.input[1] + ' = ' + inputs[2] + ';' 1459 | elif op.op == 'get': 1460 | # if inputs[0] == 'this': 1461 | # op_string = inputs[0] + '->' + inputs[1] 1462 | # else: 1463 | op_string = inputs[0] + '.' + inputs[1] 1464 | op_string = self.op_to_show(method, op, output_l, op_string) 1465 | elif op.op == 'put': 1466 | # if inputs[0] == 'this': 1467 | # op_string = inputs[0] + '->' + op.input[1] + ' = ' + inputs[2] + ';' 1468 | # else: 1469 | op_string = inputs[0] + '.' + op.input[1] + ' = ' + inputs[2] + ';' 1470 | elif op.op == 'const': 1471 | op_string = inputs[0] 1472 | op_string = self.op_to_show(method, op, output_l, op_string, True) 1473 | elif op.op == 'if': 1474 | op_string = 'if (' + inputs[1] + ' ' + self.COMPARE[op.input[0]] + ' ' + inputs[2] + ') ' + 'goto ' + \ 1475 | op.input[3] + ';' 1476 | elif op.op == 'new': 1477 | op_string = '' 1478 | elif op.op == 'new-invoke': 1479 | op_string = 'new ' + op.input[0] + '(' 1480 | for i in range(2, len(inputs), 2): 1481 | if inputs[i - 1] == 'float': 1482 | p = self.to_float(inputs[i]) 1483 | else: 1484 | p = inputs[i] 1485 | if len(inputs) != i + 1: 1486 | op_string = op_string + p + ', ' 1487 | else: 1488 | op_string = op_string + p 1489 | op_string = op_string + ')' 1490 | op_string = self.op_to_show(method, op, output_l, op_string) 1491 | elif op.op == 'return': 1492 | op_string = 'return' 1493 | if len(inputs) == 1: 1494 | op_string = op_string + ' ' + inputs[0] 1495 | op_string = op_string + ';' 1496 | elif op.op == 'goto': 1497 | op_string = 'goto ' + op.input[0] + ';' 1498 | elif op.op == 'invoke': 1499 | access = self.get_access(op.input[1]) 1500 | # access = None 1501 | if access is None: 1502 | op_string = inputs[0] + '.' + op.input[1] + '(' 1503 | for i in range(3, len(inputs), 2): 1504 | if inputs[i - 1] == 'float': 1505 | p = self.to_float(inputs[i]) 1506 | else: 1507 | p = inputs[i] 1508 | if len(op.input) != i + 1: 1509 | op_string = op_string + p + ', ' 1510 | else: 1511 | op_string = op_string + p 1512 | op_string = op_string + ')' 1513 | if op.output is not None: 1514 | op_string = self.op_to_show(method, op, output_l, op_string) 1515 | else: 1516 | if op.output is not None: 1517 | op_string = access 1518 | if op.output is not None: 1519 | op_string = self.op_to_show(method, op, output_l, op_string) 1520 | else: 1521 | op_string = inputs[-1] 1522 | op_string = access + ' = ' + op_string 1523 | if op_string != '': 1524 | op_string = op_string + ';' 1525 | 1526 | elif op.op == 'ifz': 1527 | op_string = 'if (' + inputs[1] + ' ' + self.COMPARE[op.input[0]] + ' ' + '0' + ') ' + 'goto ' + op.input[ 1528 | 2] + ';' 1529 | elif op.op == 'cal': 1530 | op_string = self.CALCULATE[op.input[0]] + inputs[1] 1531 | op_string = self.op_to_show(method, op, output_l, op_string) 1532 | 1533 | elif op.op.startswith('cal2-lit'): 1534 | # print self.to_float(inputs[0]) 1535 | flag = self.CALCULATE[op.input[0]] 1536 | # if (op.is_local() and op.local[1] == output_l and inputs[2] == '0x1' and (flag == '+' or flag == '-')): 1537 | if (op.is_local() and op.local[1] == inputs[1] and (inputs[2] == '1' or inputs[2] == '0x1') and ( 1538 | flag == '+' or flag == '-')): 1539 | op_string = inputs[1] + ' ' + flag + flag 1540 | if not op.is_line_end(): 1541 | method.reg.set_register(op.input[1], op_string) 1542 | method.reg.set_register(op.output, op.local[1], True) 1543 | op_string = '' 1544 | else: 1545 | if op.op[9:].startswith('float'): 1546 | p1 = self.to_float(inputs[1]) 1547 | p2 = self.to_float(op.input[2]) 1548 | else: 1549 | p1 = (inputs[1]) 1550 | p2 = (op.input[2]) 1551 | if p1.startswith('-'): 1552 | p1 = '(' + p1 + ')' 1553 | if p2.startswith('-'): 1554 | p2 = '(' + p2 + ')' 1555 | op_string = p1 + ' ' + self.CALCULATE[op.input[0]] + ' ' + p2 1556 | op_string = self.op_to_show(method, op, output_l, op_string) 1557 | elif op.op.startswith('cal2'): 1558 | if op.op[5:10] == 'float': 1559 | p1 = self.to_float(inputs[1]) 1560 | p2 = self.to_float(inputs[2]) 1561 | else: 1562 | p1 = (inputs[1]) 1563 | p2 = (inputs[2]) 1564 | 1565 | if len(p1) == 0: 1566 | print(op.input[0]) 1567 | print(inputs[1]) 1568 | print(inputs[2]) 1569 | 1570 | if p1.startswith('-'): 1571 | p1 = '(' + p1 + ')' 1572 | if p2.startswith('-'): 1573 | p2 = '(' + p2 + ')' 1574 | op_string = p1 + ' ' + self.CALCULATE[op.input[0]] + ' ' + p2 1575 | op_string = self.op_to_show(method, op, output_l, op_string) 1576 | elif op.op == 'switch': 1577 | op_string = 'swtich(' + inputs[0] + ')' + '{' 1578 | self.to_file(op_string) 1579 | self.to_file_shift(1) 1580 | for case in method.switch[op.input[1]].case: 1581 | op_string = 'case ' + case[0] + ': ' + 'goto ' + case[1] + ';' 1582 | self.to_file(op_string) 1583 | self.to_file_shift(-1) 1584 | op_string = '}' 1585 | elif op.op == 'move': 1586 | op_string = inputs[0] 1587 | op_string = self.op_to_show(method, op, output_l, op_string) 1588 | elif op.op == 'move-exception': 1589 | op_string = inputs[0] 1590 | method.reg.set_register(op.output, op_string, None, False) 1591 | op_string = '' 1592 | elif op.op == 'check': 1593 | op_string = '(' + op.input[1] + ')' + inputs[0] 1594 | op_string = self.op_to_show(method, op, output_l, op_string) 1595 | elif op.op == 'throw': 1596 | op_string = 'throw ' + inputs[0] + ';' 1597 | elif op.op == 'to': 1598 | op_string = '(' + op.input[0] + ')' + inputs[1] 1599 | op_string = self.op_to_show(method, op, output_l, op_string) 1600 | elif op.op == 'anew': 1601 | cls = op.input[0][0:-2] # int[] -> int 1602 | op_string = 'new ' + cls + '[' + inputs[1] + ']' 1603 | op_string = self.op_to_show_dircet(method, op, output_l, op_string) 1604 | elif op.op == 'instanceof': 1605 | op_string = inputs[0] + ' instanceof ' + op.input[1] 1606 | op_string = self.op_to_show(method, op, output_l, op_string) 1607 | elif op.op == 'aput': 1608 | op_string = inputs[0] + '[' + inputs[1] + '] = ' + inputs[2] 1609 | elif op.op == 'aget': 1610 | op_string = inputs[0] + '[' + inputs[1] + ']' 1611 | op_string = self.op_to_show(method, op, output_l, op_string) 1612 | elif op.op == 'length': 1613 | op_string = inputs[0] + '.length()' 1614 | op_string = self.op_to_show(method, op, output_l, op_string) 1615 | elif op.op == 'cmp': 1616 | op_string = '(' + inputs[0] + '>' + inputs[1] + '?1:' + inputs[0] + '<' + inputs[1] + '?-1:0)' 1617 | op_string = self.op_to_show(method, op, output_l, op_string) 1618 | elif op.op == 'monitor': 1619 | op_string = '' 1620 | elif op.op == 'nop': 1621 | if op.is_local(): 1622 | c_var = op.local[1] 1623 | method.reg.set_register(op.output, c_var, True) 1624 | op_string = '' 1625 | 1626 | if op_string == '': 1627 | pass 1628 | elif op_string is not None: 1629 | self.to_file(op_string) 1630 | else: 1631 | print(' ' + op.op) 1632 | 1633 | def do_parent_dot(self, line): 1634 | if line.startswith('method', 1): # fix 1635 | # .method public static main([Ljava/lang/String;)V 1636 | part = line.split() 1637 | part2 = part[-1].split('(') 1638 | 1639 | func_name = part2[0] 1640 | 1641 | if func_name.startswith('access'): 1642 | self.java_access = func_name 1643 | 1644 | elif line.startswith('end method', 1): 1645 | self.access[self.java_access] = self.cur_access 1646 | self.java_access = None 1647 | 1648 | def do_parent_command(self, line): 1649 | if line.startswith('iget'): 1650 | self.do_parent_put_get(line) 1651 | elif line.startswith('sget'): 1652 | self.do_parent_static_put_get(line) 1653 | 1654 | def do_parent_put_get(self, line): # fix 1655 | # iput-object v2, p0, Lcom/tutor/apkinstaller/ApkInstaller;->apkWeb:Landroid/webkit/WebView; 1656 | line = line.replace(', ', ',') 1657 | part = line.split() 1658 | part2 = part[1].split(',') 1659 | cls = self.to_java_class_name(part2[2]) 1660 | field = self.make_field(part2[2]) 1661 | self.cur_access = cls + '.' + 'this.' + field 1662 | 1663 | def do_parent_static_put_get(self, line): # fix 1664 | # sput-object v1, Lcom/moji/mjweather/activity/AddCityActivity;->mHotCitys:[Ljava/lang/String; 1665 | line = line.replace(', ', ' ') 1666 | part = line.split() 1667 | cls = self.to_java_class_name(part[2]) 1668 | field = self.make_field(part[2]) 1669 | self.cur_access = cls + '.' + 'this.' + field 1670 | 1671 | 1672 | def list_file(dir_name): 1673 | files = [] 1674 | try: 1675 | ls = os.listdir(dir_name) 1676 | except FileNotFoundError: 1677 | print('dir access deny') 1678 | else: 1679 | for l in ls: 1680 | filename = os.path.join(dir_name, l) 1681 | if os.path.isdir(filename): 1682 | file_names = list_file(filename) 1683 | for filename in file_names: 1684 | files.append(filename) 1685 | else: 1686 | files.append(filename) 1687 | 1688 | return files 1689 | 1690 | 1691 | def to_java(file_smali, file_java): 1692 | sm = SmaliFile(file_smali) 1693 | sm.scan_file() 1694 | sm.to_java(file_java) 1695 | 1696 | 1697 | def to_javas(dir_s, dir_j): 1698 | lists = list_file(dir_s) 1699 | 1700 | for file_smali in lists: 1701 | file_path = os.path.dirname(file_smali) 1702 | file_name = os.path.basename(file_smali) 1703 | [file_name, ext] = os.path.splitext(file_name) 1704 | 1705 | path_file_name = os.path.join(file_path, file_name) 1706 | if ext == '.smali': 1707 | print('FileName:' + file_smali) 1708 | file_java = path_file_name.replace(dir_s, dir_j, 1) + '.java' 1709 | path = os.path.dirname(file_java) 1710 | if not os.path.isdir(path): 1711 | os.makedirs(path) 1712 | 1713 | to_java(file_smali, file_java) 1714 | 1715 | 1716 | if __name__ == "__main__": 1717 | if len(sys.argv) == 3: 1718 | dir_smali = sys.argv[1] 1719 | dir_java = sys.argv[2] 1720 | elif len(sys.argv) == 2: 1721 | dir_smali = sys.argv[1] 1722 | dir_java = dir_smali 1723 | else: 1724 | dir_smali = 'smali' 1725 | dir_java = 'smali' 1726 | if not os.path.exists(dir_smali): 1727 | print('smali2java [input dir] [output dir]') 1728 | print('version: 2.00') 1729 | exit() 1730 | 1731 | to_javas(dir_smali, dir_java) 1732 | --------------------------------------------------------------------------------