├── .gitignore ├── snapshot.png ├── HelloWorld.py ├── grepDeobfuscationRoutines.py ├── getMethodsFromClass.py ├── getMethodSignatures.py ├── getMethodSignatures.java ├── AlertMarker.py ├── ListingMethods.py ├── README.md ├── ASTRemoveDummySwitch.py ├── fixObfousClass.py ├── InvokedMethods.py ├── ObadDecrypt.py ├── RenameObfuscatedClasses.py ├── ObadUnreflect.py ├── DeCluster.java ├── DeClusterMod.java └── DexGuardStringDecoder.java /.gitignore: -------------------------------------------------------------------------------- 1 | .metadata 2 | *.py~ 3 | *.java~ 4 | *.class 5 | -------------------------------------------------------------------------------- /snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enovella/jebscripts/HEAD/snapshot.png -------------------------------------------------------------------------------- /HelloWorld.py: -------------------------------------------------------------------------------- 1 | # JEB sample script 2 | # http://www.android-decompiler.com/ 3 | # display MessageBox and get pressed button value 4 | # 5 | # Copyright (c) 2013 SecureBrain 6 | import os 7 | 8 | from jeb.api import IScript 9 | from jeb.api.ui import JebUI 10 | from jeb.api.ui.JebUI import ButtonGroupType 11 | from jeb.api.ui.JebUI import IconType 12 | 13 | class HelloWorld(IScript): 14 | def run(self, j): 15 | ui = j.getUI() 16 | ret = ui.displayMessageBox("question", "Hello World?", IconType.QUESTION, ButtonGroupType.YES_NO_CANCEL) 17 | wp = { 0: 'Cancel', 1: 'OK', 2:'Yes', 3:'No' } 18 | print "'%s'is pressed!!" % wp[ret] 19 | 20 | -------------------------------------------------------------------------------- /grepDeobfuscationRoutines.py: -------------------------------------------------------------------------------- 1 | import re 2 | import string 3 | 4 | PATH = "/tmp/methodsignatures.txt" 5 | REGEXES = [ 6 | r'.*\([BSI][BSI][BSI]\)Ljava/lang/String;$', 7 | r'.*\([BSI][BSI]Ljava/lang/String;\)Ljava/lang/String;$', 8 | r'.*\([BSI]Ljava/lang/String;\)Ljava/lang/String;$', 9 | r'.*\([BSI]Ljava/lang/String;[BSI]\)Ljava/lang/String;$', 10 | r'.*\(Ljava/lang/String;[BSI]\)Ljava/lang/String;$', 11 | r'.*\(Ljava/lang/String;[BSI][BSI]\)Ljava/lang/String;$', 12 | r'.*\([BSI]Ljava/lang/String;\)Ljava/lang/String;$', 13 | ] 14 | 15 | lines = [] 16 | with open(PATH,"r") as f: 17 | lines = f.readlines() 18 | 19 | for line in lines: 20 | for regex in REGEXES: 21 | matches = re.findall(regex, line) 22 | if matches: 23 | print str(matches[0]).decode('unicode-escape') 24 | 25 | -------------------------------------------------------------------------------- /getMethodsFromClass.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | import os 5 | import time 6 | import codecs # used for writing UTF-8 into a file 7 | 8 | from jeb.api import IScript 9 | from jeb.api.ui import View 10 | from jeb.api.dex import Dex 11 | from jeb.api.ast import Class, Field, Method, Call, Constant, StaticField, NewArray, Compound, Assignment, Identifier, Optimizer 12 | 13 | 14 | class getMethodsFromClass(IScript): 15 | 16 | def run(self, jeb): 17 | self.jeb = jeb 18 | self.dex = self.jeb.getDex() 19 | self.clsdata = [] 20 | self.classrequested = self.dex.getClass("k") 21 | 22 | self.clsdata = self.classrequested.getData() 23 | dmethods = clsdata.getDirectMethods() 24 | vmethods = clsdata.getVirtualMethods() 25 | 26 | 27 | for method in dmethods: 28 | print method 29 | 30 | print "[+] Done!" 31 | -------------------------------------------------------------------------------- /getMethodSignatures.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import codecs # used for writing UTF-8 into a file 4 | 5 | from jeb.api import IScript 6 | from jeb.api.ui import View 7 | from jeb.api.dex import Dex 8 | from jeb.api.ast import Class, Field, Method, Call, Constant, StaticField, NewArray, Compound, Assignment, Identifier, Optimizer 9 | 10 | 11 | class getMethodSignatures(IScript): 12 | 13 | def run(self, jeb): 14 | self.jeb = jeb 15 | self.dex = self.jeb.getDex() 16 | self.path = "/tmp/methodsignatures.txt" 17 | 18 | methodSigList = self.dex.getMethodSignatures(True) 19 | 20 | #f = codecs.open(self.path, "w", encoding="ascii", errors="ignore") 21 | f = codecs.open(self.path, "w", encoding="utf-8", errors="ignore") 22 | 23 | for method in methodSigList: 24 | f.write(repr(method)+'\n') 25 | 26 | print "[+] Output file located at %s" %(self.path) 27 | print "[+] Done!" 28 | -------------------------------------------------------------------------------- /getMethodSignatures.java: -------------------------------------------------------------------------------- 1 | import jeb.api.IScript; 2 | /*import jeb.api.ui.View;*/ 3 | import jeb.api.dex.Dex; 4 | import jeb.api.JebInstance; 5 | import java.util.*; 6 | import java.io.*; 7 | 8 | public class getMethodSignatures implements IScript { 9 | 10 | private static String path = "/tmp/methodsignatures.txt"; 11 | 12 | public void run(JebInstance jeb){ 13 | Dex dex = jeb.getDex(); 14 | List methodSigList = new ArrayList(); 15 | 16 | methodSigList = dex.getMethodSignatures(true); 17 | 18 | try{ 19 | File file = new File(path); 20 | FileOutputStream fos = new FileOutputStream(file); 21 | 22 | for (String method : methodSigList) 23 | fos.write((method + "\n").getBytes()); 24 | }catch(Exception e){ 25 | 26 | } 27 | 28 | jeb.print("[+] Output file located at "+path); 29 | jeb.print("[+] Done!"); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /AlertMarker.py: -------------------------------------------------------------------------------- 1 | # JEB sample script 2 | # http://www.android-decompiler.com/ 3 | # 4 | # AlertMarker.py 5 | # Set(unset) alert marker to focued method. 6 | # 7 | # Copyright (c) 2013 SecureBrain 8 | from jeb.api import IScript 9 | from jeb.api.dex import Dex 10 | from jeb.api.ui import View 11 | 12 | import string 13 | 14 | class AlertMarker(IScript): 15 | 16 | def run(self, jeb): 17 | self.jeb = jeb 18 | self.dex = jeb.getDex() 19 | self.ui = jeb.getUI() 20 | success = self.start() 21 | 22 | def start(self): 23 | view = self.ui.getView(View.Type.ASSEMBLY) 24 | msig = view.getCodePosition().getSignature() 25 | md = self.dex.getMethodData(msig) 26 | if not md: 27 | print 'caret is not in method.' 28 | return 29 | 30 | f = md.getUserFlags() 31 | print 'target:' + msig 32 | if (f & Dex.FLAG_ALERT) == 0: 33 | print 'set alert marker' 34 | md.setUserFlags(f | Dex.FLAG_ALERT) 35 | else: 36 | print 'unset alert' 37 | md.setUserFlags(f & ~Dex.FLAG_ALERT) 38 | 39 | view.refresh() 40 | -------------------------------------------------------------------------------- /ListingMethods.py: -------------------------------------------------------------------------------- 1 | # JEB sample script 2 | # http://www.android-decompiler.com/ 3 | # listing all methods in dex 4 | # 5 | # Copyright (c) 2013 SecureBrain 6 | import os 7 | 8 | from jeb.api import IScript 9 | 10 | class ListingMethods(IScript): 11 | def run(self, j): 12 | self.dex = j.getDex() 13 | if not self.dex: 14 | print 'Error! Please provide an input file.' 15 | j.exit() 16 | 17 | self.listing() 18 | 19 | def listing(self): 20 | print "listing all methods in dex..." 21 | for i in range(0, self.dex.getClassCount()): 22 | cls = self.dex.getClass(i) 23 | print self.dex.getType(cls.getClasstypeIndex()) 24 | cls_data = cls.getData() 25 | if cls_data is None: 26 | continue 27 | 28 | for md in cls_data.getDirectMethods(): 29 | print "\t%s" % self.method_def_str(md) 30 | 31 | for md in cls_data.getVirtualMethods(): 32 | print "\t%s" % self.method_def_str(md) 33 | 34 | print "" 35 | 36 | def method_def_str(self, method_data): 37 | m = self.dex.getMethod(method_data.getMethodIndex()) 38 | name = m.getName() 39 | proto = self.dex.getPrototype(m.getPrototypeIndex()) 40 | proto_strs = map(lambda p: self.dex.getType(p), proto.getParameterTypeIndexes()) 41 | ret = self.dex.getType(proto.getReturnTypeIndex()) 42 | acc = self.accessor(method_data.getAccessFlags()) 43 | return "%s %s %s(%s)" % (' '.join(acc), ret, name, ', '.join(proto_strs)) 44 | 45 | def accessor(self, flg): 46 | accessor_strings = { 47 | 0x1: 'public', 48 | 0x2: 'private', 49 | 0x4: 'protected', 50 | 0x8: 'static', 51 | 0x10: 'final', 52 | 0x20: 'synchronized', 53 | 0x40: 'bridge', 54 | 0x80: 'varargs', 55 | 0x100: 'native', 56 | 0x200: 'interface', 57 | 0x400: 'abstract', 58 | 0x800: 'strict', 59 | 0x1000: 'synthetic', 60 | 0x2000: 'annotation', 61 | 0x4000: 'enum', 62 | 0x10000: 'constructor', 63 | 0x20000: 'declared-synchronized', 64 | } 65 | return map(lambda i: accessor_strings[i], filter(lambda k: k&flg, accessor_strings.keys())) 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JEB Android decompiler scripts 2 | A set of JEB Python/Java scripts for reverse engineering Android obfuscated code 3 | 4 | ## JEB Python scripts 5 | - [HelloWorld.py](HelloWorld.py) 6 | - Display message box. 7 | - [ListingMethods.py](ListingMethods.py) 8 | - Print all methods in dex. 9 | - [InvokedMethods.py](InvokedMethods.py) 10 | - Get caret position and print invoked methods from it. 11 | - [RenameObfuscatedClasses.py](RenameObfuscatedClasses.py) 12 | - Rename obfuscated class names by using super class name. 13 | - [AlertMarker.py](AlertMarker.py) 14 | - Set(unset) alert marker to focused method. 15 | - [getMethodSignatures.py](getMethodSignatures.py) 16 | - Get method signatures from an APK and store them into a text file for parsing it later on. (Use the Java version) 17 | - [getMethodsFromClass.py](getMethodsFromClass.py) 18 | - Get method from a class and print them out. 19 | - [ASTRemoveDummySwitch.py](ASTRemoveDummySwitch.py) 20 | - Remove dummy switches 21 | - [ObadDecrypt.py](ObadDecrypt.py) 22 | - Decrypt Obad strings before performing unreflection 23 | - [ObadUnreflect.py](ObadUnreflect.py) 24 | - Replace reflection calls by direct method calls 25 | - [fixObfousClass.py](fixObfousClass.py) 26 | - Rename obfuscated class name with source name. (fr0zenrain) 27 | 28 | ![imaing](https://bytebucket.org/dudux/jebscripts/raw/ecf2962bebeedfcbef918a42f989e860fe939205/snapshot.png) 29 | 30 | Recover android dex's class name obfouscator by proguard with "-keepattributes SourceFile". 31 | Most app need save the crash log, so the do not clear source name,maybe some nest class can not fix. 32 | 33 | ## JEB Java scripts 34 | - [DeCluster.java](DeCluster.java) 35 | - Renaming obfuscated class/methods/fields names (@jcase) 36 | - [DeClusterMod.java](DeClusterMod.java) 37 | - Renaming obfuscated class/methods/fields names with filter and string format added 38 | - [DexGuardStringDecoder.java](DexGuardStringDecoder.java) 39 | - Decrypt DexGuard encrypted Strings (Anton Kosterin) 40 | - [getMethodSignatures.java](getMethodSignatures.java) 41 | - Get method signatures from an APK and store them into a text file for parsing it later on. 42 | 43 | 44 | ## Extra Python scripts 45 | - [grepDeobfuscationRoutines.py](grepDeobfuscationRoutines.py) 46 | - Filter possible obfuscator routines from a method signatures list (First use getMethodSignatures.java) 47 | 48 | # JEB Sample Scripts 49 | Sample automation scripts for [JEB(Android Interactive Decompiler)](http://www.android-decompiler.com/index.php). 50 | 51 | ## Usage 52 | Usage of JEB automation is [here](http://www.android-decompiler.com/manual.php#automation). 53 | 54 | JEB API reference is [here](http://www.android-decompiler.com/apidoc/). 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ASTRemoveDummySwitch.py: -------------------------------------------------------------------------------- 1 | # JEB script - demo AST API 2 | # Remove dummy switches 3 | # See www.android-decompiler.com/blog 4 | 5 | import sys 6 | import os 7 | import time 8 | from jeb.api import IScript 9 | from jeb.api import EngineOption 10 | from jeb.api.ui import View 11 | from jeb.api.dex import Dex 12 | from jeb.api.ast import SwitchStm, Compound, Constant, Goto, Label, Optimizer 13 | 14 | 15 | 16 | class ASTRemoveDummySwitch(IScript): 17 | 18 | def run(self, jeb): 19 | self.jeb = jeb 20 | self.dex = self.jeb.getDex() 21 | 22 | # change or allow user-input or caret-based selection 23 | msig = 'Lcom/example/android/snake/TileView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V' 24 | r = jeb.decompileMethod(msig) 25 | 26 | m = jeb.getDecompiledMethodTree(msig) 27 | self.checkBlock(m.getBody()) 28 | 29 | opt = Optimizer(jeb, m) 30 | opt.removeUselessGotos() 31 | opt.removeUnreferencedLabels() 32 | 33 | 34 | def checkBlock(self, block, level=0): 35 | i = 0 36 | while i < block.size(): 37 | stm = block.get(i) 38 | #print '%s%s' % (' '*level, stm) 39 | if isinstance(stm, SwitchStm): 40 | gotostm = self.checkDummySwitch(stm) 41 | if gotostm: 42 | block.set(i, gotostm) 43 | print '-> Switch replaced by goto %s' % gotostm.getLabel().getName() 44 | label = gotostm.getLabel() 45 | r = self.findFirstLabel(block, i + 1, level) 46 | if r and r[1] == level and r[0].getName() == label.getName(): 47 | print '-> Removing [%d, %d)' % (i + 1, r[2]) 48 | j = i + 1 49 | cnt = r[2] - j 50 | while cnt > 0: 51 | block.remove(j) 52 | cnt -= 1 53 | elif isinstance(stm, Compound): 54 | for b in stm.getBlocks(): 55 | self.checkBlock(b, level+1) 56 | i += 1 57 | 58 | 59 | def findFirstLabel(self, block, begin=0, level=0): 60 | i = begin 61 | while i < block.size(): 62 | stm = block.get(i) 63 | if isinstance(stm, Label): 64 | return (stm, level, i) 65 | elif isinstance(stm, Compound): 66 | for b in stm.getBlocks(): 67 | r = self.findFirstLabel(b, 0, level+1) 68 | if r: 69 | return r 70 | i += 1 71 | return None 72 | 73 | 74 | def checkDummySwitch(self, sw): 75 | val = sw.getSwitchedExpression() 76 | if not isinstance(val, Constant): 77 | return None 78 | 79 | val = val.getInt() 80 | b = sw.getCaseBody(val) 81 | if not b or b.size() != 1: 82 | return None 83 | 84 | stm0 = b.get(0) 85 | if not isinstance(stm0, Goto): 86 | return None 87 | 88 | label = stm0.getLabel() 89 | return stm0 -------------------------------------------------------------------------------- /fixObfousClass.py: -------------------------------------------------------------------------------- 1 | #? name=fix Obfous Class, author=frozenrain 2 | # A JEB python script 3 | # fixObfous.py 4 | # Rename obfuscated class name with source name. 5 | 6 | import re 7 | 8 | from jeb.api import IScript 9 | from jeb.api import EngineOption 10 | from jeb.api.ui import JebUI 11 | from jeb.api.ui import View 12 | 13 | 14 | class fixObfousClass(IScript): 15 | def run(self, j): 16 | self.dex = j.getDex() 17 | self.jeb = j 18 | ui = j.getUI() 19 | 20 | # rename obfuscated class names 21 | self.rename_classes() 22 | 23 | # refresh view 24 | ui.getView(View.Type.ASSEMBLY).refresh() 25 | ui.getView(View.Type.JAVA).refresh() 26 | ui.getView(View.Type.CLASS_HIERARCHY).refresh() 27 | 28 | 29 | def rename_classes(self): 30 | for i in range(0, self.dex.getClassCount()): 31 | cls = self.dex.getClass(i) 32 | src_name = self.dex.getString(cls.getSourceIndex()) 33 | cls_name = self.dex.getType(cls.getClasstypeIndex()) 34 | ret = self.rename_from_source(cls_name, src_name) 35 | if ret: 36 | new_name = self.dex.getType(cls.getClasstypeIndex()) 37 | print "rename from '%s' to '%s'" % (cls_name, new_name) 38 | 39 | def is_nest_class(self, className): 40 | return className != None and className.rfind("$") != -1 41 | 42 | def should_rename_class(self, className, sourceName): 43 | if not className or not sourceName: 44 | return False 45 | if self.is_nest_class(className): 46 | return False 47 | if(len(className)> 3): 48 | return False 49 | for i in range(len(className)): 50 | if (className[i] < 'a'): 51 | return False 52 | return True 53 | 54 | def get_true_class_name(self, className): 55 | if (self.is_nest_class(className)): 56 | return None 57 | if className != None: 58 | pos = className.rfind("/") 59 | if pos != -1: 60 | return className[pos+1:len(className)-1] 61 | return None 62 | 63 | def fix_class_name(self, className, sourceName): 64 | pos = className.rfind("/") 65 | if pos != -1: 66 | return className[::-1].replace(self.get_true_class_name(className)[::-1], sourceName[::-1], 1)[::-1] + ';' 67 | 68 | def rename_from_source(self,cls_name, src_name): 69 | if (src_name != None and len(src_name) > 0 ): 70 | src_name = src_name[:src_name.rfind(".")] 71 | cls_name = cls_name[:len(cls_name)] 72 | if (self.should_rename_class(self.get_true_class_name(cls_name), src_name)): 73 | #fixedClassName = self.fix_class_name(cls_name, src_name); 74 | if(self.jeb.renameClass(cls_name, src_name)): 75 | print "rename " , cls_name , " to " , src_name 76 | else: 77 | print "error rename " , cls_name , " to " , src_name 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /InvokedMethods.py: -------------------------------------------------------------------------------- 1 | # JEB sample script 2 | # http://www.android-decompiler.com/ 3 | # 4 | # InvokedMethods.py 5 | # listing invoked method by the method which the caret focus on. 6 | # 7 | # Copyright (c) 2013 SecureBrain 8 | import re 9 | 10 | from jeb.api import IScript 11 | from jeb.api import EngineOption 12 | from jeb.api.ui import JebUI 13 | from jeb.api.ui import View 14 | from jeb.api.dex import DexDalvikInstruction 15 | 16 | class InvokedMethods(IScript): 17 | def run(self, j): 18 | self.dex = j.getDex() 19 | ui = j.getUI() 20 | 21 | if not self.dex: 22 | print 'Error! Please provide an input file.' 23 | j.exit() 24 | 25 | # collect method information in dex. 26 | self.collect_method_data() 27 | 28 | # get caret information on assembly window 29 | asm_view = ui.getView(View.Type.ASSEMBLY) 30 | cp = asm_view.getCodePosition() 31 | sig = cp.getSignature() 32 | 33 | if sig in self.method_dict: 34 | print "caret is on '%s'." % sig 35 | invoked = self.invoked_method(self.method_dict[sig]) 36 | for meth in invoked: 37 | print "\t%s" % meth 38 | print "%d methods are invoked." % len(invoked) 39 | else: 40 | print "caret is not foucused on method..." 41 | 42 | 43 | # collect method information in dex. 44 | def collect_method_data(self): 45 | self.method_dict = {} # key is method name(partial sig) 46 | 47 | # each class 48 | for i in range(0, self.dex.getClassCount()): 49 | cls = self.dex.getClass(i) 50 | cls_name = self.dex.getType(cls.getClasstypeIndex()) 51 | cls_data = cls.getData() 52 | if cls_data is None: 53 | continue 54 | 55 | # about static method 56 | for md in cls_data.getDirectMethods(): 57 | m = self.dex.getMethod(md.getMethodIndex()) 58 | self.method_dict[self.method_def(m)] = md 59 | 60 | # about instance method 61 | for md in cls_data.getVirtualMethods(): 62 | m = self.dex.getMethod(md.getMethodIndex()) 63 | self.method_dict[self.method_def(m)] = md 64 | 65 | 66 | def method_def(self, dex_method): 67 | name = dex_method.getName() 68 | cls_name = self.dex.getType(dex_method.getClassTypeIndex()) 69 | proto = self.dex.getPrototype(dex_method.getPrototypeIndex()) 70 | proto_strs = map(lambda p: self.dex.getType(p), proto.getParameterTypeIndexes()) 71 | ret = self.dex.getType(proto.getReturnTypeIndex()) 72 | 73 | # 'clas name'->'method name'('arguments'...)'return type' 74 | return "%s->%s(%s)%s" % (cls_name, name, ''.join(proto_strs), ret) 75 | 76 | 77 | def invoked_method(self, method_data): 78 | code_item = method_data.getCodeItem() 79 | invoked = [] 80 | p = re.compile('^invoke-') 81 | 82 | for inst in code_item.getInstructions(): 83 | if p.match(inst.getMnemonic()): 84 | # if instruction opcode starts with 'invoke-...' 85 | for param in inst.getParameters(): 86 | # get the arcument operand 87 | if param.getType() == DexDalvikInstruction.TYPE_IDX: 88 | m = self.dex.getMethod(param.getValue()) # invoked method 89 | invoked.append(self.method_def(m)) 90 | 91 | invoked = list(set(invoked)) # uniq array 92 | invoked.sort() 93 | return invoked 94 | 95 | -------------------------------------------------------------------------------- /ObadDecrypt.py: -------------------------------------------------------------------------------- 1 | # JEB script - demo AST API 2 | # Obad, Part 1: Decrypt Obad strings before performing unreflection 3 | # See www.android-decompiler.com/blog 4 | 5 | import sys 6 | import os 7 | import time 8 | from jeb.api import IScript 9 | from jeb.api import EngineOption 10 | from jeb.api.ui import View 11 | from jeb.api.dex import Dex 12 | from jeb.api.ast import Class, Field, Method, Call, Constant, StaticField, NewArray 13 | 14 | 15 | 16 | class ObadDecrypt(IScript): 17 | 18 | def run(self, jeb): 19 | self.jeb = jeb 20 | self.dex = self.jeb.getDex() 21 | self.cstbuilder = Constant.Builder(jeb) 22 | 23 | self.csig = 'Lcom/android/system/admin/OclIIOlC;' 24 | self.encbytes = [] 25 | self.mname_decrypt = None 26 | # the encryption keys could be determined by analyzing the decryption method 27 | self.keys = [33, 96, -2] # (added to 'length', added to 'curChar', delta) 28 | 29 | r = jeb.decompileClass(self.csig) 30 | if not r: 31 | print 'Could not find class "%s"' % csig 32 | return 33 | 34 | c = jeb.getDecompiledClassTree(self.csig) 35 | 36 | wanted_flags = Dex.ACC_PRIVATE|Dex.ACC_STATIC|Dex.ACC_FINAL 37 | for f in c.getFields(): 38 | fsig = f.getSignature() 39 | if fsig.endswith(':[B'): 40 | fd = self.dex.getFieldData(fsig) 41 | if fd.getAccessFlags() & wanted_flags == wanted_flags: 42 | print 'Found field:', fsig 43 | 44 | findex = fd.getFieldIndex() 45 | for mindex in self.dex.getFieldReferences(findex): 46 | mname = self.dex.getMethod(mindex).getName(False) 47 | if mname != '': 48 | self.mname_decrypt = mname 49 | 50 | for m2 in c.getMethods(): 51 | if m2.getName() == '': 52 | s0 = m2.getBody().get(0) 53 | if isinstance(s0.getLeft(), StaticField) and s0.getLeft().getField().getSignature() == f.getSignature(): 54 | array = s0.getRight() 55 | if isinstance(array, NewArray): 56 | for v in array .getInitialValues(): 57 | self.encbytes.append(v.getByte()) 58 | break 59 | break 60 | 61 | if len(self.encbytes) == 0: 62 | print 'Encrypted strings byte array not found' 63 | return 64 | 65 | if not self.mname_decrypt: 66 | print 'Decryption method was not found' 67 | return 68 | 69 | for m in c.getMethods(): 70 | print 'Decrypting strings in method: %s' % m.getName() 71 | self.decryptMethodStrings(m) 72 | 73 | 74 | def decryptMethodStrings(self, m): 75 | block = m.getBody() 76 | i = 0 77 | while i < block.size(): 78 | stm = block.get(i) 79 | self.checkElement(block, stm) 80 | i += 1 81 | 82 | 83 | def checkElement(self, parent, e): 84 | if isinstance(e, Call): 85 | mname = e.getMethod().getName() 86 | if mname == self.mname_decrypt: 87 | v = [] 88 | for arg in e.getArguments(): 89 | if isinstance(arg, Constant): 90 | v.append(arg.getInt()) 91 | if len(v) == 3: 92 | decrypted_string = self.decrypt(v[2], v[0], v[1]) 93 | parent.replaceSubElement(e, self.cstbuilder.buildString(decrypted_string)) 94 | print ' Decrypted string: %s' % repr(decrypted_string) 95 | 96 | for subelt in e.getSubElements(): 97 | if isinstance(subelt, Class) or isinstance(subelt, Field) or isinstance(subelt, Method): 98 | continue 99 | self.checkElement(e, subelt) 100 | 101 | 102 | def decrypt(self, length, curChar, pos): 103 | length += self.keys[0] 104 | curChar += self.keys[1] 105 | r = '' 106 | for i in range(length): 107 | r += chr(curChar & 0xFF) 108 | if pos >= len(self.encbytes): 109 | break 110 | curEncodedChar = self.encbytes[pos] 111 | pos += 1 112 | curChar = curChar + curEncodedChar + self.keys[2] 113 | return r -------------------------------------------------------------------------------- /RenameObfuscatedClasses.py: -------------------------------------------------------------------------------- 1 | #? name=Rename Obfuscated Classes, shortcut=Ctrl+Shift+R, author=Masata Nishida 2 | # JEB sample script 3 | # http://www.android-decompiler.com/ 4 | # 5 | # RenameObfuscatedClasses.py 6 | # Rename obfuscated class names. 7 | # 8 | # Copyright (c) 2013 SecureBrain 9 | import re 10 | 11 | from jeb.api import IScript 12 | from jeb.api import EngineOption 13 | from jeb.api.ui import JebUI 14 | from jeb.api.ui import View 15 | 16 | class RenameObfuscatedClasses(IScript): 17 | def run(self, j): 18 | self.dex = j.getDex() 19 | self.jeb = j 20 | ui = j.getUI() 21 | 22 | # rename obfuscated class names 23 | self.rename_classes() 24 | 25 | # refresh view 26 | ui.getView(View.Type.ASSEMBLY).refresh() 27 | ui.getView(View.Type.JAVA).refresh() 28 | ui.getView(View.Type.CLASS_HIERARCHY).refresh() 29 | 30 | 31 | def rename_classes(self): 32 | for i in range(0, self.dex.getClassCount()): 33 | cls = self.dex.getClass(i) 34 | cls_name = self.dex.getType(cls.getClasstypeIndex()) 35 | ret = self.rename_by_super_class(cls) 36 | ret2 = self.rename_by_accessor(cls) 37 | ret3 = self.rename_by_interfaces(cls) 38 | ret = ret or ret2 or ret3 39 | 40 | if ret: 41 | new_name = self.dex.getType(cls.getClasstypeIndex()) 42 | print "rename from '%s' to '%s'" % (cls_name, new_name) 43 | 44 | 45 | def append_cls_name(self, cls, append_str): 46 | p = re.compile("^.*\/([\w$]+);$") 47 | # cls_name has package path 48 | cls_name = self.dex.getType(cls.getClasstypeIndex()) 49 | if cls_name.find(append_str) == -1: 50 | s = re.search(p, cls_name) 51 | simple_new_name = s.group(1) + '_' + append_str 52 | return self.jeb.renameClass(cls_name, simple_new_name) 53 | else: 54 | return False 55 | 56 | def rename_by_super_class(self, cls): 57 | rename_targets = { 58 | 'Landroid/app/Service;': 'Service', 59 | 'Landroid/app/Activity;': 'Activity', 60 | 'Landroid/content/BroadcastReceiver;': 'Receiver', 61 | 'Landroid/content/ContentProvider;': 'Provider', 62 | 'Ljava/lang/Thread;': 'Thread', 63 | 'Landroid/os/AsyncTask;': 'AsyncTask', 64 | 'Ljava/util/TimerTask;': 'TimerTask', 65 | 'Landroid/database/sqlite/SQLiteDatabase;': 'SQLiteDatabase', 66 | 'Landroid/database/sqlite/SQLiteOpenHelper;': 'SQLiteOpenHelper', 67 | 'Landroid/database/ContentObserver;': 'ContentObserver', 68 | 'Landroid/os/Handler;': 'Handler', 69 | } 70 | super_cls_type = self.dex.getType(cls.getSuperclassIndex()) 71 | if super_cls_type in rename_targets.keys(): 72 | val = rename_targets[super_cls_type] 73 | self.append_cls_name(cls, val) 74 | return True 75 | else: 76 | return False 77 | 78 | def rename_by_accessor(self, cls): 79 | flg = cls.getAccessFlags() 80 | ret = False 81 | if flg & 0x200: 82 | self.append_cls_name(cls, 'Interface') 83 | ret = True 84 | if flg & 0x400: 85 | self.append_cls_name(cls, 'Abstract') 86 | ret = True 87 | if flg & 0x4000: 88 | self.append_cls_name(cls, 'Enum') 89 | ret = True 90 | return ret 91 | 92 | 93 | def rename_by_interfaces(self, cls): 94 | ret = False 95 | for idx in cls.getInterfaceIndexes(): 96 | if_name = self.dex.getType(idx) 97 | if if_name.endswith('ClickListener;'): 98 | self.append_cls_name(cls, 'ClickListener') 99 | ret = True 100 | if if_name.endswith('CancelListener;'): 101 | self.append_cls_name(cls, 'CancelListener') 102 | ret = True 103 | if if_name == 'Ljava/lang/Runnable;': 104 | self.append_cls_name(cls, 'Runnable') 105 | ret = True 106 | if if_name == 'Landroid/os/IInterface;': 107 | self.append_cls_name(cls, 'IInterface') 108 | ret = True 109 | 110 | return ret -------------------------------------------------------------------------------- /ObadUnreflect.py: -------------------------------------------------------------------------------- 1 | # JEB script - demo AST API 2 | # Obad, Part 2: Replace reflection calls by direct method calls 3 | # See www.android-decompiler.com/blog 4 | 5 | import sys 6 | import os 7 | import time 8 | from jeb.api import IScript 9 | from jeb.api.ui import View 10 | from jeb.api.dex import Dex 11 | from jeb.api.ast import Class, Field, Method, Call, Constant, StaticField, NewArray, Compound, Assignment, Identifier, Optimizer 12 | 13 | 14 | 15 | class ObadUnreflect(IScript): 16 | 17 | def run(self, jeb): 18 | self.jeb = jeb 19 | self.dex = self.jeb.getDex() 20 | 21 | v = self.jeb.getUI().getView(View.Type.JAVA) 22 | if not v: 23 | print 'Switch to the Java view, position the caret somewhere inside the method to be decompiled' 24 | return 25 | 26 | self.msig = v.getCodePosition().getSignature() 27 | print 'Cursor: %s' % self.msig 28 | 29 | r = jeb.decompile(self.msig, False, False) 30 | if not r: 31 | print 'Could not find method' 32 | return 33 | 34 | m = jeb.getDecompiledMethodTree(self.msig) 35 | self.revertReflection(m.getBody()) 36 | 37 | 38 | def revertReflection(self, block): 39 | i = 0 40 | while i < block.size(): 41 | stm = block.get(i) 42 | if isinstance(stm, Assignment): 43 | if stm.isSimpleAssignment() and isinstance(stm.getRight(), Call): 44 | self.processCall(stm, stm.getRight()) 45 | elif isinstance(stm, Call): 46 | self.processCall(block, stm) 47 | elif isinstance(stm, Compound): 48 | for b in stm.getBlocks(): 49 | self.revertReflection(b) 50 | i += 1 51 | 52 | 53 | # note: could add support for instantiation, eg, Class.forName().getConstructor().newInstance() 54 | def processCall(self, parent, elt0): 55 | m = elt0.getMethod() 56 | if m.getSignature() == 'Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;': 57 | elt1 = elt0.getArguments().get(0) 58 | if isinstance(elt1, Call): 59 | if elt1.getMethod().getSignature() == 'Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;': 60 | elt2 = elt1.getArguments().get(0) 61 | if isinstance(elt2, Call): 62 | if elt2.getMethod().getSignature() == 'Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;': 63 | print 'Found match: Class.forName(...).getMethod(...).invoke(...)' 64 | new_elt0 = self.check(elt2, elt1, elt0) 65 | if new_elt0: 66 | print ' Removing relection' 67 | parent.replaceSubElement(elt0, new_elt0) 68 | 69 | 70 | def check(self, c0, c1, c2): 71 | arg = c0.getArguments().get(0) 72 | if self.isConstantString(arg): 73 | cname = arg.getString() 74 | 75 | arg = c1.getArguments().get(1) 76 | if self.isConstantString(arg): 77 | mname = arg.getString() 78 | 79 | if not cname or not mname: 80 | return False 81 | 82 | sig = 'L%s;->%s' % (cname.replace('.', '/'), mname) 83 | 84 | sig += '(' 85 | i = 2 86 | while i < c1.getArguments().size(): 87 | arg = c1.getArguments().get(i) 88 | v = self.processTypeIdentifier(arg) 89 | if not v: 90 | break 91 | sig += v 92 | i += 1 93 | # simplification: assume no return value 94 | # would need to be either infered (from code), or fetched from doc 95 | sig += ')V' 96 | 97 | print ' %s' % sig 98 | 99 | is_static = False 100 | args = [] 101 | i = 1 102 | while i < c2.getArguments().size(): 103 | arg = c2.getArguments().get(i) 104 | if isinstance(arg, Constant) and arg.isNull(): 105 | if i == 1: 106 | is_static = True 107 | elif i == 2 and c2.getArguments().size() == 3: 108 | break 109 | else: 110 | args.append(arg) 111 | else: 112 | args.append(arg) 113 | i += 1 114 | 115 | m2 = self.dex.addMethodReference(sig) 116 | index = m2.getIndex() 117 | mb = Method.Builder(self.jeb) 118 | ast_method = mb.build(index, is_static) 119 | #print ast_method 120 | 121 | # TODO: primitive arguments should be auto-unboxed 122 | ast_call = Call.build(ast_method, False, args) 123 | #print ast_call 124 | return ast_call 125 | 126 | 127 | def isConstantString(self, e): 128 | return isinstance(e, Constant) and e.getType() == 'Ljava/lang/String;' 129 | 130 | 131 | def processTypeIdentifier(self, arg): 132 | primtypes = { 133 | 'Ljava/lang/Boolean;->TYPE:Ljava/lang/Class;': 'Z', 134 | 'Ljava/lang/Byte;->TYPE:Ljava/lang/Class;': 'B', 135 | 'Ljava/lang/Character;->TYPE:Ljava/lang/Class;':'C', 136 | 'Ljava/lang/Word;->TYPE:Ljava/lang/Class;': 'W', 137 | 'Ljava/lang/Integer;->TYPE:Ljava/lang/Class;': 'I', 138 | 'Ljava/lang/Float;->TYPE:Ljava/lang/Class;': 'F', 139 | 'Ljava/lang/Double;->TYPE:Ljava/lang/Class;': 'D', 140 | } 141 | 142 | if isinstance(arg, StaticField): 143 | return primtypes.get(arg.getField().getSignature()) 144 | 145 | elif isinstance(arg, Call): 146 | if arg.getMethod().getSignature() == 'Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;': 147 | c = arg.getArguments().get(0) 148 | if self.isConstantString(c): 149 | return 'L%s;' % c.getString().replace('.', '/') 150 | 151 | elif isinstance(arg, Constant) and arg.isNull(): 152 | # (simplification) assume no var-arg: getMethod(name, null) 153 | # -> no argument 154 | return None 155 | 156 | raise ' Cannot process UNKNOWN type: %s' % arg -------------------------------------------------------------------------------- /DeCluster.java: -------------------------------------------------------------------------------- 1 | //? name=DeCluster v1.1.2, help=This Java file is a JEB plugin 2 | 3 | import jeb.api.IScript; 4 | import jeb.api.JebInstance; 5 | import jeb.api.dex.Dex; 6 | import jeb.api.dex.DexField; 7 | import jeb.api.ui.View; 8 | import java.util.List; 9 | 10 | 11 | public class DeCluster implements IScript { 12 | int showErrors = 0; // Show errors, slows the plugin down greatly. 13 | int renameShort = 1; // Rename short names, such as a, Ab, AbC 14 | int renameAll = 0; // Renames all classes, regardless if the match the isValid rules 15 | int renameNonLatin = 1; // Rename classes using non-latin chars 16 | int smartRename = 1; // Rename classes based on their type /not implemented/ 17 | 18 | @SuppressWarnings("unchecked") 19 | public void run(JebInstance jeb) { 20 | jeb.print("DeCluster Plugin v1.1.2"); 21 | jeb.print("By jcase@cunninglogic.com"); 22 | 23 | String classPre = "Class_"; 24 | String innerPre = "InnerClass_"; 25 | String fieldPre = "Field_"; 26 | String methodPre = "Method_"; 27 | 28 | String classService = "Service_"; 29 | String classReceiver = "Receiver_"; 30 | String classActivity = "Activity_"; 31 | 32 | 33 | 34 | if (showErrors == 0) { 35 | jeb.print("Show Errors is disabled"); 36 | } else { 37 | jeb.print("Show Errors is enabled, this slows the script down!"); 38 | } 39 | 40 | int count = 0; 41 | 42 | if (!jeb.isFileLoaded()) { 43 | 44 | jeb.print("Please load a dex file"); 45 | 46 | } else { 47 | 48 | jeb.print("Renaming fields..."); 49 | List myArr = jeb.getDex().getFieldSignatures(true); 50 | for (int i = myArr.size()-1; i >= 0; i--) { 51 | String fieldName = myArr.get(i); 52 | 53 | if (!isValid(fieldName.substring(fieldName.indexOf(">")+1, fieldName.indexOf(":")))) { 54 | ++count; 55 | 56 | try { 57 | if(!jeb.setFieldComment(fieldName, "Renamed from " +fieldName)) { 58 | if (showErrors != 0) 59 | jeb.print("Error commenting field " + fieldName); 60 | } 61 | if(!jeb.renameField(fieldName,fieldPre + Integer.toString(count))) { 62 | if (showErrors != 0) 63 | jeb.print("Error renaming field " + fieldName); 64 | } 65 | 66 | } catch(NullPointerException e) { 67 | if (showErrors != 0) 68 | jeb.print(e.toString() + " when renaming" + fieldName); 69 | 70 | } catch(RuntimeException e) { 71 | if (showErrors != 0) 72 | jeb.print(e.toString() + " when renaming" + fieldName); 73 | 74 | } 75 | } 76 | 77 | } 78 | 79 | count = 0; 80 | myArr.clear(); 81 | 82 | jeb.print("Renaming methods..."); 83 | myArr = jeb.getDex().getMethodSignatures(true); 84 | for (int i = myArr.size()-1; i >= 0; i--) { 85 | String methodName = myArr.get(i); 86 | 87 | if (!isValid(methodName.substring(methodName.indexOf(">")+1, methodName.indexOf("(")))) { 88 | 89 | ++count; 90 | 91 | 92 | try { 93 | if(!jeb.setMethodComment(methodName, "Renamed from " +methodName)) { 94 | if (showErrors != 0) 95 | jeb.print("Error commenting method " + methodName); 96 | } 97 | if(!jeb.renameMethod(methodName,methodPre + Integer.toString(count))) { 98 | if (showErrors != 0) 99 | jeb.print("Error renaming method " + methodName); 100 | } 101 | 102 | } catch(NullPointerException e) { 103 | if (showErrors != 0) 104 | jeb.print(e.toString() + " when renaming" + methodName); 105 | 106 | } catch(RuntimeException e) { 107 | if (showErrors != 0) 108 | jeb.print(e.toString() + " when renaming" + methodName); 109 | 110 | } 111 | 112 | 113 | } 114 | 115 | } 116 | 117 | count = 0; 118 | myArr.clear(); 119 | 120 | jeb.print("Renaming classes..."); 121 | myArr = jeb.getDex().getClassSignatures(true); 122 | 123 | for (int i = myArr.size()-1; i >= 0; i--) { 124 | String className = myArr.get(i); 125 | 126 | 127 | if (!isValid(className.substring(className.lastIndexOf("/")+1, className.length()-1))) { 128 | ++count; 129 | 130 | try { 131 | 132 | if(!jeb.setClassComment(className, "Renamed from " +className)) { 133 | if (showErrors != 0) 134 | jeb.print("Error commenting class " + className); 135 | } 136 | 137 | 138 | if (className.contains("$")) { 139 | if(!jeb.renameClass(className,innerPre + Integer.toString(count))) { 140 | if (showErrors != 0) 141 | jeb.print("Error renaming class " + className); 142 | } 143 | } else { 144 | if(!jeb.renameClass(className,classPre + Integer.toString(count))) { 145 | if (showErrors != 0) 146 | jeb.print("Error renaming class " + className); 147 | } 148 | } 149 | 150 | } catch(NullPointerException e) { 151 | if (showErrors != 0) 152 | jeb.print(e.toString() + " when renaming" + className); 153 | 154 | } catch(RuntimeException e) { 155 | if (showErrors != 0) 156 | jeb.print(e.toString() + " when renaming" + className); 157 | 158 | } 159 | } 160 | 161 | } 162 | 163 | jeb.getUI().getView(View.Type.CLASS_HIERARCHY).refresh(); 164 | jeb.getUI().getView(View.Type.ASSEMBLY).refresh(); 165 | jeb.print("Finished Renaming"); 166 | } 167 | 168 | } 169 | 170 | public boolean isValid (String name){ 171 | // This needs work 172 | 173 | // Handle inner classes 174 | if (name.contains("$")) 175 | name.equals(name.replace("$","")); 176 | 177 | // Trying to do away with null pointers in method comments, not working. 178 | if (name == null || name.length() == 0 || name.contains("") || name.contains("")) 179 | return true; 180 | 181 | // Rename all classes 182 | if (renameAll != 0) 183 | return false; 184 | 185 | // Rename short class names, like output from ProGuard/Allatori 186 | if (renameShort != 0 && name.length() <= 3) 187 | return false; 188 | 189 | // Rename classes using non-latin chars 190 | if (renameNonLatin != 0 && !name.matches("\\w+")) 191 | return false; 192 | 193 | return true; 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /DeClusterMod.java: -------------------------------------------------------------------------------- 1 | /** 2 | ? name=DeCluster v1.1.3, help=This Java file is a JEB plugin 3 | 4 | 1.1.3 Add string.format for classes names 5 | 1.1.2 @jcase 6 | */ 7 | import jeb.api.IScript; 8 | import jeb.api.JebInstance; 9 | import jeb.api.dex.Dex; 10 | import jeb.api.dex.DexField; 11 | import jeb.api.ui.View; 12 | import java.util.List; 13 | 14 | 15 | public class DeClusterMod implements IScript { 16 | int showErrors = 0; // Show errors, slows the plugin down greatly. 17 | int renameShort = 1; // Rename short names, such as a, Ab, AbC 18 | int renameAll = 0; // Renames all classes, regardless if the match the isValid rules 19 | int renameNonLatin = 1; // Rename classes using non-latin chars 20 | int smartRename = 1; // Rename classes based on their type /not implemented/ 21 | 22 | @SuppressWarnings("unchecked") 23 | public void run(JebInstance jeb) { 24 | jeb.print("DeCluster Plugin v1.1.3"); 25 | jeb.print("By jcase@cunninglogic.com"); 26 | jeb.print("Small Mods by @enovella_"); 27 | 28 | String classPre = "Class_"; 29 | String innerPre = "InnerClass_"; 30 | String fieldPre = "Field_"; 31 | String methodPre = "Method_"; 32 | 33 | String classService = "Service_"; 34 | String classReceiver = "Receiver_"; 35 | String classActivity = "Activity_"; 36 | 37 | 38 | 39 | if (showErrors == 0) { 40 | jeb.print("Show Errors is disabled"); 41 | } else { 42 | jeb.print("Show Errors is enabled, this slows the script down!"); 43 | } 44 | 45 | int count = 0; 46 | 47 | if (!jeb.isFileLoaded()) { 48 | 49 | jeb.print("Please load a dex file"); 50 | 51 | } else { 52 | 53 | jeb.print("Renaming fields..."); 54 | List myArr = jeb.getDex().getFieldSignatures(true); 55 | for (int i = myArr.size()-1; i >= 0; i--) { 56 | String fieldName = myArr.get(i); 57 | 58 | if (!isValid(fieldName.substring(fieldName.indexOf(">")+1, fieldName.indexOf(":")))) { 59 | ++count; 60 | 61 | 62 | try { 63 | if(!jeb.setFieldComment(fieldName, "Renamed from " +fieldName)) { 64 | if (showErrors != 0) 65 | jeb.print("Error commenting field " + fieldName); 66 | } 67 | if(!jeb.renameField(fieldName,fieldPre + String.format("%05d", count))) { 68 | if (showErrors != 0) 69 | jeb.print("Error renaming field " + fieldName); 70 | } 71 | 72 | } catch(NullPointerException e) { 73 | if (showErrors != 0) 74 | jeb.print(e.toString() + " when renaming" + fieldName); 75 | 76 | } catch(RuntimeException e) { 77 | if (showErrors != 0) 78 | jeb.print(e.toString() + " when renaming" + fieldName); 79 | 80 | } 81 | } 82 | 83 | } 84 | 85 | count = 0; 86 | myArr.clear(); 87 | 88 | jeb.print("Renaming methods..."); 89 | myArr = jeb.getDex().getMethodSignatures(true); 90 | for (int i = myArr.size()-1; i >= 0; i--) { 91 | String methodName = myArr.get(i); 92 | 93 | if (!isValid(methodName.substring(methodName.indexOf(">")+1, methodName.indexOf("(")))) { 94 | 95 | ++count; 96 | 97 | 98 | try { 99 | if(!jeb.setMethodComment(methodName, "Renamed from " +methodName)) { 100 | if (showErrors != 0) 101 | jeb.print("Error commenting method " + methodName); 102 | } 103 | if(!jeb.renameMethod(methodName,methodPre + String.format("%05d", count))) { 104 | if (showErrors != 0) 105 | jeb.print("Error renaming method " + methodName); 106 | } 107 | 108 | } catch(NullPointerException e) { 109 | if (showErrors != 0) 110 | jeb.print(e.toString() + " when renaming" + methodName); 111 | 112 | } catch(RuntimeException e) { 113 | if (showErrors != 0) 114 | jeb.print(e.toString() + " when renaming" + methodName); 115 | 116 | } 117 | 118 | 119 | } 120 | 121 | } 122 | 123 | count = 0; 124 | myArr.clear(); 125 | 126 | jeb.print("Renaming classes..."); 127 | myArr = jeb.getDex().getClassSignatures(true); 128 | 129 | for (int i = myArr.size()-1; i >= 0; i--) { 130 | String className = myArr.get(i); 131 | 132 | /** Modification */ 133 | if (className.contains("com.add.your.package.to.blacklist.right.here") || className.contains("android/support/")) { 134 | if (showErrors != 0) 135 | jeb.print(className + " is skipped!"); 136 | continue; 137 | } 138 | /** */ 139 | 140 | if (!isValid(className.substring(className.lastIndexOf("/")+1, className.length()-1))) { 141 | ++count; 142 | 143 | try { 144 | 145 | if(!jeb.setClassComment(className, "Renamed from " +className)) { 146 | if (showErrors != 0) 147 | jeb.print("Error commenting class " + className); 148 | } 149 | 150 | 151 | if (className.contains("$")) { 152 | if(!jeb.renameClass(className,innerPre + String.format("%05d", count))) { 153 | if (showErrors != 0) 154 | jeb.print("Error renaming class " + className); 155 | } 156 | } else { 157 | if(!jeb.renameClass(className,classPre + String.format("%05d", count))) { 158 | if (showErrors != 0) 159 | jeb.print("Error renaming class " + className); 160 | } 161 | } 162 | 163 | } catch(NullPointerException e) { 164 | if (showErrors != 0) 165 | jeb.print(e.toString() + " when renaming" + className); 166 | 167 | } catch(RuntimeException e) { 168 | if (showErrors != 0) 169 | jeb.print(e.toString() + " when renaming" + className); 170 | 171 | } 172 | } 173 | 174 | } 175 | 176 | jeb.getUI().getView(View.Type.CLASS_HIERARCHY).refresh(); 177 | jeb.getUI().getView(View.Type.ASSEMBLY).refresh(); 178 | jeb.print("Finished Renaming"); 179 | } 180 | 181 | } 182 | 183 | public boolean isValid (String name){ 184 | // This needs work 185 | 186 | // Handle inner classes 187 | if (name.contains("$")) 188 | name.equals(name.replace("$","")); 189 | 190 | // Trying to do away with null pointers in method comments, not working. 191 | if (name == null || name.length() == 0 || name.contains("") || name.contains("")) 192 | return true; 193 | 194 | // Rename all classes 195 | if (renameAll != 0) 196 | return false; 197 | 198 | // Rename short class names, like output from ProGuard/Allatori 199 | if (renameShort != 0 && name.length() <= 3) 200 | return false; 201 | 202 | // Rename classes using non-latin chars 203 | if (renameNonLatin != 0 && !name.matches("\\w+")) 204 | return false; 205 | 206 | return true; 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /DexGuardStringDecoder.java: -------------------------------------------------------------------------------- 1 | import jeb.api.IScript; 2 | import jeb.api.JebInstance; 3 | import jeb.api.ast.*; 4 | import jeb.api.ast.Class; 5 | import jeb.api.dex.Dex; 6 | import jeb.api.dex.DexCodeItem; 7 | import jeb.api.dex.DexFieldData; 8 | import jeb.api.dex.DexMethod; 9 | import jeb.api.ui.JavaView; 10 | import jeb.api.ui.View; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Created by AKosterin on 29/06/15. 16 | */ 17 | public class DexGuardStringDecoder implements IScript{ 18 | private static int encint = 0; 19 | private static String mname_decrypt = ""; 20 | private static byte[] encbytes = new byte[0]; 21 | 22 | public void run(JebInstance jebInstance) { 23 | jebInstance.print("DexGuardStringDecoder_Start"); 24 | 25 | if(!jebInstance.isFileLoaded()){ 26 | jebInstance.print("Please load a dex file"); 27 | return; 28 | } 29 | 30 | Dex dex = jebInstance.getDex(); 31 | JavaView view = (JavaView) jebInstance.getUI().getView(View.Type.JAVA); 32 | 33 | String methodname = view.getCodePosition().getSignature(); 34 | jebInstance.print(methodname); 35 | 36 | DexCodeItem code = dex.getMethodData(methodname).getCodeItem(); 37 | 38 | if(code == null){ 39 | jebInstance.print("MethodData not found in DEX!!!"); 40 | return; 41 | } 42 | 43 | jebInstance.decompileClass(getClassFromSignature(methodname)); 44 | 45 | String str1 = getClassFromSignature(methodname); 46 | jebInstance.print(str1); 47 | 48 | Class mClass = jebInstance.getDecompiledClassTree(str1); 49 | for(Field mField : (List) mClass.getFields()){ 50 | if(mField.getSignature().endsWith(":[B")) { 51 | jebInstance.print(mField.getSignature()); 52 | DexFieldData dfd = dex.getFieldData(mField.getSignature()); 53 | 54 | int wanted_flags = Dex.ACC_PRIVATE | Dex.ACC_STATIC | Dex.ACC_FINAL; 55 | if ((dfd.getAccessFlags() & wanted_flags) == wanted_flags) { 56 | jebInstance.print("Found field:" + mField.getSignature()); 57 | 58 | int fIndex = dfd.getFieldIndex(); 59 | 60 | for (Integer mIndex : (List) dex.getFieldReferences(fIndex)) { 61 | DexMethod mMethod = dex.getMethod(mIndex); 62 | if(mMethod.getSignature(true).contains("(III)Ljava/lang/String;")){ 63 | mname_decrypt = mMethod.getName(); 64 | } 65 | } 66 | 67 | for (Method method : (List) mClass.getMethods()) { 68 | if (method.getName().equals("")) { 69 | for (int i = 0 ; i < method.getBody().size(); i++) { 70 | Statement st1 = method.getBody().get(i); 71 | if (st1 instanceof Assignment) { 72 | Assignment ass1 = (Assignment) st1; 73 | ILeftExpression le1 = ass1.getLeft(); 74 | if ((le1 instanceof StaticField) && ((StaticField) le1).getField().getSignature().equals(mField.getSignature())) { 75 | IExpression e1 = ass1.getRight(); 76 | if (e1 instanceof NewArray) { 77 | NewArray na1 = (NewArray) e1; 78 | for (IExpression e2 : ((List) na1.getInitialValues())) { 79 | if (e2 instanceof Constant) { 80 | Constant c1 = (Constant) e2; 81 | byte b1 = c1.getByte(); 82 | byte[] c = new byte[encbytes.length + 1]; 83 | System.arraycopy(encbytes, 0, c, 0, encbytes.length); 84 | c[encbytes.length] = b1; 85 | encbytes = c; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } else if(mField.getSignature().endsWith(":I")) { 96 | jebInstance.print(mField.getSignature()); 97 | DexFieldData dfd = dex.getFieldData(mField.getSignature()); 98 | 99 | int wanted_flags = Dex.ACC_PRIVATE | Dex.ACC_STATIC; 100 | if ((dfd.getAccessFlags() & wanted_flags) == wanted_flags) { 101 | jebInstance.print("Found field:" + mField.getSignature()); 102 | 103 | for (Method method : (List) mClass.getMethods()) { 104 | if (method.getName().equals("")) { 105 | for (int i = 0 ; i < method.getBody().size(); i++) { 106 | Statement st1 = method.getBody().get(i); 107 | if (st1 instanceof Assignment) { 108 | Assignment ass1 = (Assignment) st1; 109 | ILeftExpression le1 = ass1.getLeft(); 110 | if ((le1 instanceof StaticField) && ((StaticField) le1).getField().getSignature().equals(mField.getSignature())) { 111 | IExpression e1 = ass1.getRight(); 112 | if (e1 instanceof Constant) { 113 | Constant const1 = (Constant) e1; 114 | 115 | encint = const1.getInt(); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | if(encbytes.length == 0 ){ 127 | jebInstance.print("Encrypted strings byte array not found"); 128 | } 129 | 130 | if (mname_decrypt.isEmpty()){ 131 | jebInstance.print("Decryption method was not found"); 132 | } 133 | 134 | if(encint == 0 ){ 135 | jebInstance.print("May be decrypt int was not found"); 136 | } 137 | 138 | for(Method m : (List) mClass.getMethods()){ 139 | jebInstance.print("Decrypting strings in method: " + m.getName()); 140 | for (int i = 0; i < m.getBody().size(); i++) { 141 | Statement mStatement = m.getBody().get(i); 142 | 143 | checkElement(jebInstance, m.getBody(), mStatement, 0); 144 | } 145 | } 146 | 147 | jebInstance.getUI().getView(View.Type.JAVA).refresh(); 148 | 149 | jebInstance.print("DexGuardStringDecoder_End"); 150 | } 151 | 152 | private static void checkElement(JebInstance jebInstance,IElement parent, IElement iElement, int level){ 153 | if (iElement instanceof Call){ 154 | Call mCall = (Call) iElement; 155 | String methodName = mCall.getMethod().getName(); 156 | if(methodName.equals(mname_decrypt)){ 157 | int[] params = new int[0]; 158 | StringBuilder strb = new StringBuilder(); 159 | strb.append(methodName).append("("); 160 | for (IExpression mIExpression : (List) mCall.getArguments()) { 161 | if (mIExpression instanceof Constant) { 162 | Constant mConstant = (Constant) mIExpression; 163 | 164 | params = addToIntArray(params, mConstant.getInt()); 165 | 166 | strb.append(mConstant.getInt()).append(", "); 167 | } else if (mIExpression instanceof ArrayElt) { 168 | ArrayElt mArrayElt = (ArrayElt) mIExpression; 169 | 170 | IExpression mIExpression2 = mArrayElt.getIndex(); 171 | 172 | if (mIExpression2 instanceof Constant) { 173 | Constant mConstant = (Constant) mIExpression2; 174 | 175 | params = addToIntArray(params, (int) encbytes[mConstant.getInt()]); 176 | 177 | strb.append(encbytes[mConstant.getInt()]).append(", "); 178 | } else { 179 | strb.append("encbytes[").append(mIExpression2.getClass().getName()).append("], "); 180 | } 181 | } else if(mIExpression instanceof StaticField) { 182 | params = addToIntArray(params, encint); 183 | 184 | strb.append(encint).append(", "); 185 | } else if(mIExpression instanceof Expression){ 186 | Expression mExpression = (Expression) mIExpression; 187 | 188 | if(mExpression.getLeft() != null) { 189 | 190 | if (mExpression.getLeft() instanceof StaticField && mExpression.getRight() instanceof Constant) { 191 | Constant mConstant = (Constant) mExpression.getRight(); 192 | 193 | int mint; 194 | 195 | if(mExpression.getOperator().toString().equals("&")){ 196 | mint = encint & mConstant.getInt(); 197 | 198 | params = addToIntArray(params, mint); 199 | strb.append(mint).append(", "); 200 | } else if(mExpression.getOperator().toString().equals("|")) { 201 | mint = encint | mConstant.getInt(); 202 | params = addToIntArray(params, mint); 203 | strb.append(mint).append(", "); 204 | } else if(mExpression.getOperator().toString().equals("-")) { 205 | mint = encint - mConstant.getInt(); 206 | params = addToIntArray(params, mint); 207 | strb.append(mint).append(", "); 208 | } else if(mExpression.getOperator().toString().equals("+")) { 209 | mint = encint + mConstant.getInt(); 210 | params = addToIntArray(params, mint); 211 | strb.append(mint).append(", "); 212 | } else if(mExpression.getOperator().toString().equals("*")) { 213 | mint = encint * mConstant.getInt(); 214 | params = addToIntArray(params, mint); 215 | strb.append(mint).append(", "); 216 | } else { 217 | strb.append(encint).append(" ").append(mExpression.getOperator().toString()).append(" ").append(mConstant.getInt()).append(", "); 218 | } 219 | } if (mExpression.getLeft() instanceof ArrayElt && mExpression.getRight() instanceof Constant) { 220 | ArrayElt mArrayElt = (ArrayElt) mExpression.getLeft(); 221 | Constant mConstant = (Constant) mExpression.getRight(); 222 | 223 | IExpression mIExpression2 = mArrayElt.getIndex(); 224 | 225 | if (mIExpression2 instanceof Constant) { 226 | Constant mConstant2 = (Constant) mIExpression2; 227 | 228 | int mint; 229 | 230 | if(mExpression.getOperator().toString().equals("&")) { 231 | mint = encbytes[mConstant2.getInt()] & mConstant.getInt(); 232 | params = addToIntArray(params, mint); 233 | strb.append(mint).append(", "); 234 | } else if(mExpression.getOperator().toString().equals("|")){ 235 | mint = encbytes[mConstant2.getInt()] | mConstant.getInt(); 236 | params = addToIntArray(params, mint); 237 | strb.append(mint).append(", "); 238 | } else if(mExpression.getOperator().toString().equals("-")){ 239 | mint = encbytes[mConstant2.getInt()] - mConstant.getInt(); 240 | params = addToIntArray(params, mint); 241 | strb.append(mint).append(", "); 242 | } else if(mExpression.getOperator().toString().equals("+")){ 243 | mint = encbytes[mConstant2.getInt()] + mConstant.getInt(); 244 | params = addToIntArray(params, mint); 245 | strb.append(mint).append(", "); 246 | } else if(mExpression.getOperator().toString().equals("*")){ 247 | mint = encbytes[mConstant2.getInt()] * mConstant.getInt(); 248 | params = addToIntArray(params, mint); 249 | strb.append(mint).append(", "); 250 | } else { 251 | strb.append(encbytes[mConstant2.getInt()]).append(" ").append(mExpression.getOperator().toString()).append(" ").append(mConstant.getInt()).append(", "); 252 | } 253 | } else { 254 | strb.append("jeb.api.ast.Expression(").append(mExpression.getLeft().getClass().getName()).append(" ").append(((Expression) mIExpression).getOperator().getClass().getName()).append(" ").append(mExpression.getRight().getClass().getName()).append("), "); 255 | } 256 | } else { 257 | strb.append("jeb.api.ast.Expression(").append(mExpression.getLeft().getClass().getName()).append(" ").append(((Expression) mIExpression).getOperator().getClass().getName()).append(" ").append(mExpression.getRight().getClass().getName()).append("), "); 258 | } 259 | } else { 260 | if(mExpression.getRight() instanceof ArrayElt){ 261 | ArrayElt mArrayElt = (ArrayElt) mExpression.getRight(); 262 | 263 | IExpression mIExpression2 = mArrayElt.getIndex(); 264 | 265 | if (mIExpression2 instanceof Constant) { 266 | Constant mConstant = (Constant) mIExpression2; 267 | 268 | params = addToIntArray(params, -1 * encbytes[mConstant.getInt()]); 269 | 270 | strb.append(-1 * encbytes[mConstant.getInt()]).append(", "); 271 | } else { 272 | strb.append("jeb.api.ast.Expression(").append(((Expression) mIExpression).getOperator().getClass().getName()).append(" ").append(mExpression.getRight().getClass().getName()).append("), "); 273 | } 274 | } else { 275 | strb.append("jeb.api.ast.Expression(").append(((Expression) mIExpression).getOperator().getClass().getName()).append(" ").append(mExpression.getRight().getClass().getName()).append("), "); 276 | } 277 | 278 | } 279 | 280 | 281 | } else { 282 | strb.append(mIExpression.getClass().getName()).append(", "); 283 | } 284 | } 285 | 286 | strb.delete(strb.length() - 2, strb.length()); 287 | 288 | strb.append(");"); 289 | 290 | if (params.length == 3){ 291 | String decyptStr = decrypt_djAR(params[0], params[1], params[2]); 292 | strb.append(" - ").append(decyptStr); 293 | parent.replaceSubElement(iElement, (new Constant.Builder(jebInstance)).buildString(decyptStr)); 294 | } 295 | 296 | jebInstance.print(strb.toString()); 297 | } 298 | } 299 | 300 | for (IElement element : (List) iElement.getSubElements()){ 301 | if (!((element instanceof Class) || (element instanceof Field) || (element instanceof Method))){ 302 | checkElement(jebInstance, iElement, element, level + 1); 303 | } 304 | } 305 | } 306 | 307 | private static final String getClassFromSignature(String sig) { 308 | return sig.split(";")[0].split("$")[0] + ";";// 309 | } 310 | 311 | private static final int[] addToIntArray(int[] array, int mint){ 312 | int[] array2 = new int[array.length + 1]; 313 | System.arraycopy(array, 0, array2, 0, array.length); 314 | array2[array.length] = mint; 315 | return array2; 316 | } 317 | 318 | 319 | //Could be determined by analyzing the decryption method 320 | private static final String decrypt_djAR(int arg9, int arg10, int arg11) { 321 | int v5; 322 | byte[] v6 = encbytes; 323 | int v1 = arg10 + 65; 324 | int v0 = 615 - arg9; 325 | int v3 = arg11 + 2; 326 | byte[] v2 = new byte[v3]; 327 | int v8 = v3 - 1; 328 | v3 = 0; 329 | v5 = v0; 330 | while(v3 != v8){ 331 | v2[v3] = ((byte)v1); 332 | v0 = v6[v5]; 333 | ++v3; 334 | ++v5; 335 | v1 += -v0; 336 | 337 | } 338 | 339 | v2[v3] = ((byte)v1); 340 | return new String(v2); 341 | } 342 | } 343 | --------------------------------------------------------------------------------