├── Smali2Frida.class ├── README.md └── Smali2Frida.java /Smali2Frida.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apkunpacker/Smali2Frida/HEAD/Smali2Frida.class -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smali2Frida 2 | Genarate Frida Hooks from .smali files. 3 | 4 | Use Apktool or other tools to decompile dex file to .smali and use it as 5 | 6 | ```sh 7 | java Smali2Frida "/directory/to/smalifolder" >script.js 8 | ``` 9 | Dirtly coded by Kirlif :) 10 | -------------------------------------------------------------------------------- /Smali2Frida.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.nio.file.Files; 3 | import java.nio.file.Path; 4 | import java.nio.file.Paths; 5 | import java.text.DecimalFormat; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | 16 | public class Smali2Frida { 17 | 18 | static final Map DT_MAP = Stream.of(new String[][] { 19 | { "B", "byte" }, 20 | { "C", "char" }, 21 | { "D", "double" }, 22 | { "F", "float" }, 23 | { "I", "int" }, 24 | { "J", "long" }, 25 | { "S", "short" }, 26 | { "Z", "boolean" }, 27 | }).collect(Collectors.toMap(data -> data[0], data -> data[1])); 28 | 29 | static final String[] DT = DT_MAP.keySet().toArray(new String[DT_MAP.size()]); 30 | 31 | static final Pattern C_PATTERN = Pattern.compile(".class.+?(\\S+?;)", Pattern.UNICODE_CASE); 32 | 33 | static final Pattern M_PATTERN = Pattern.compile(".method.+?(\\S+?)\\((\\S*?)\\)(\\S+)", Pattern.UNICODE_CASE); 34 | 35 | public static void main(String[] args) throws IOException { 36 | long go = System.nanoTime(); 37 | Path root = Paths.get(args[0]); 38 | int klas = 0; 39 | List paths = smaliFiles(root); 40 | for (Path path: paths) { 41 | try { 42 | if (new String(Files.readAllBytes(path)).contains(".class")) { 43 | hook(path, klas); 44 | klas++; 45 | } 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | /* 51 | comment below 3 line if you not need this info 52 | in final output 53 | */ 54 | System.out.println(("Processed classes: " + klas + " / " + paths.size())); 55 | DecimalFormat format = new DecimalFormat("0.00"); 56 | System.out.println("Duration: " + format.format((System.nanoTime() - go) / 1.0E9d) + " s"); 57 | } 58 | 59 | public static void hook(Path path, int klas) throws IOException { 60 | String class_name = ""; 61 | Map map = new HashMap<>(); 62 | List allLines = Files.readAllLines(path); 63 | int i = 0; 64 | for (String line : allLines) { 65 | Matcher c = C_PATTERN.matcher(line); 66 | if (c.matches()) { 67 | class_name = c.group(1); 68 | continue; 69 | } 70 | if (! class_name.isEmpty()) { 71 | Matcher m = M_PATTERN.matcher(line); 72 | if (m.matches()) { 73 | String [] starr = {m.group(1), m.group(2)}; 74 | map.put(i, starr); 75 | i++; 76 | } 77 | } 78 | } 79 | frida(class_name, String.valueOf(klas), map); 80 | } 81 | 82 | public static void frida(String class_name, String class_num, Map map) { 83 | class_name = class_name.substring(1, class_name.length() - 1).replaceAll("/","."); 84 | int last = map.size() - 1; 85 | StringBuilder snippet = new StringBuilder("Java.perform(function() {\n"); 86 | snippet.append(" var klass").append(class_num).append(" = Java.use(\"").append(class_name).append("\");\n"); 87 | for (Map.Entry entry : map.entrySet()) { 88 | String[] value = entry.getValue(); 89 | String method_nam = value[0]; 90 | String method_param = value[1]; 91 | String method_name = method_nam; 92 | if (method_name.equals("")) { 93 | method_nam = "$init"; 94 | } 95 | String op = overloadParam(method_param); 96 | String ap = asperasParam(op); 97 | int key = entry.getKey(); 98 | snippet.append("\n klass").append(class_num).append("[\"").append(method_nam).append("\"]").append(".overload(").append(op).append(").implementation = function(").append(ap).append(")\n"); 99 | snippet.append(" {\n"); 100 | snippet.append(" var ret = this[").append("\"").append(method_nam).append("\"]").append("(").append(ap).append(");\n"); 101 | snippet.append(" console.log(\"").append(method_name).append("\", \"called : \", ret);\n"); 102 | snippet.append(" return ret;\n }"); 103 | if (key == last) { 104 | snippet.append(" \n})"); 105 | } 106 | } 107 | System.out.println(snippet+"\n"); 108 | } 109 | 110 | public static String overloadParam(String method_param) { 111 | StringBuilder res = new StringBuilder(); 112 | if (method_param.isEmpty()){ 113 | return res.toString(); 114 | } 115 | for (String el : method_param.split(";")) { 116 | int i = 0; 117 | while (i < el.length()) { 118 | char c = el.charAt(i); 119 | if (c == 76) { 120 | res.append("'").append(el.substring(i + 1).replaceAll("/", ".")).append("', "); 121 | break; 122 | } 123 | if (c == 91) { 124 | if (el.charAt(i+1) == 76) { 125 | res.append("'").append(el.substring(i).replaceAll("/", ".")).append("', "); 126 | break; 127 | } 128 | int j = i; 129 | while (el.charAt(j) == 91) { 130 | j++; 131 | } 132 | j++; 133 | res.append("'").append(el, i, j).append("', "); 134 | i = j; 135 | continue; 136 | } 137 | String s = String.valueOf(c); 138 | if (Arrays.asList(DT).contains(s)){ 139 | res.append("'").append(DT_MAP.get(s)).append("', "); 140 | i++; 141 | continue; 142 | } 143 | i++; 144 | } 145 | } 146 | return res.substring(0, res.length()-2); 147 | } 148 | 149 | public static String asperasParam(String op) { 150 | StringBuilder res = new StringBuilder(); 151 | if (op.isEmpty()){ 152 | return res.toString(); 153 | } 154 | for (int i = 0; i < op.split(", ").length; i++) { 155 | res.append("var").append(i).append(", "); 156 | } 157 | return res.substring(0, res.length()-2); 158 | } 159 | 160 | public static List smaliFiles(Path path) throws IOException { 161 | if (!Files.isDirectory(path)) { 162 | throw new IllegalArgumentException("Not a directory!"); 163 | } 164 | List paths; 165 | try (Stream walk = Files.walk(path)) { 166 | paths = walk 167 | .filter(Files::isRegularFile) 168 | .filter(p -> p.getFileName().toString().endsWith(".smali")) 169 | .collect(Collectors.toList()); 170 | } 171 | return paths; 172 | } 173 | } 174 | --------------------------------------------------------------------------------