APK反编译、重打包。这都是老话题了,现在也有不少工具来实现apk反编译,比如apktool-gui。Virtuous Ten Studio等。尤其后者还是很强大的。
大家可以自己去体验。今天给大家介绍的这是一个商业产品,也即不是免费的,不过自然有其特点。
那就是来自于http://www.android-decompiler.com/的JEB
JEB是一个功能强大的为安全专业人士设计的Android应用程序的反编译。反向工程或审计APK文件,并减少许多工程师的分析时间。
1、dextojar
目前的工具在转化到jar都存在不少问题。
EB的独特功能是,其Dalvik字节码反编译为Java源代码的能力
比如这是常规工具的结果:
下图是JEB的结果:
如果说转化jar效果有明显提升的话,对于工程师读懂代码的效率还是有很大提升的。
2、可编辑性:
特别是当他们处理混淆的或受保护的代码块。 JEB的强大的用户界面,可以检查交叉引用,重命名的方法,字段,类,代码和数据之间导航,做笔记,添加注释,以及更多。
3、APK全浏览
Full APK view. Take advantage of the full APK view, including decompressed manifest, resources, certificates, strings, constants, etc. Again, flexibility is key.
也可以直接看到证书内容:
各类资源信息:
4、API for Automation. Use JEB's Application Programming Interface (API) to write Python scripts and plugins, and automate your analysis needs.
可以根据需要程序猿自己定制。比如下面的示例:
JEBReplaceStrings.py
# Sample JEB script (UI:yes, Automation:yes) # Replace code strings by the string 'foobar' # This script shows how to navigate the DEX file # and how to modify strings in the string pool import os from jeb.api import IScript from jeb.api import EngineOption class JEBReplaceStrings(IScript): def run(self, jeb): self.jeb = jeb self.dex = jeb.getDex() if not self.dex: print 'Error! Please provide an input file.' sef.jeb.exit() for msig in self.dex.getMethodSignatures(True): #print msig md = self.dex.getMethodData(msig) if not md: continue code = md.getCodeItem() if not code: continue for insn in code.getInstructions(): if not insn.getMnemonic() in ('const-string', 'const-string/jumbo'): continue stringindex = insn.getParameters()[1].getValue() s = self.dex.getString(stringindex) if s: s2 = self.modifyString(s) if s2 and s2 != s: self.dex.setString(stringindex, s2) def modifyString(self, s): # totally useless: alphanumeric string are replaced by 'foobar' # could be the base script for something more useful, like # string language identification and translation into English if not s.isalnum(): return None return 'foobar'
5、火拼:
JEB和dex2jar+JD-GUI and dex2jar+JAD.开始PK
这是一个标准的RC4加密算法代码。RC4不懂的请百度。
/** * RC4 encryption/decryption routine. * @param key * @param data input/output buffer */ public static void rc4_crypt(byte[] key, byte[] data) { int keylen = key.length; int datalen = data.length; int i; int j; // key scheduling byte[] sbox = new byte[256]; for(i = 0; i < 256; i++) { sbox[i] = (byte)i; } j = 0; for(i = 0; i < 256; i++) { j = ((j + sbox[i] + key[i % keylen]) % 256) & 0xFF; byte tmp = sbox[i]; sbox[i] = sbox[j]; sbox[j] = tmp; } // generate output i = 0; j = 0; int index = 0; while(index < datalen) { i = ((i + 1) % 256) & 0xFF; j = ((j + sbox[i]) % 256) & 0xFF; byte tmp = sbox[i]; sbox[i] = sbox[j]; sbox[j] = tmp; byte k = (byte)(sbox[((sbox[i] + sbox[j]) % 256) & 0xFF]); data[index] ^= k; index++; } }
这是JEB的输出:
// Decompiled by JEB v0.9.0 alpha public static void rc4_crypt(byte[] arg11, byte[] arg12) { int v10 = 0x100; int v5 = arg11.length; int v0 = arg12.length; byte[] v6 = new byte[v10]; int v1 = 0; while(v1 < v10) { v6[v1] = ((byte)v1); ++v1; } int v3 = 0; v1 = 0; while(v1 < v10) { v3 = (v6[v1] + v3 + arg11[v1 % v5]) % 0x100 & 0xFF; byte v7 = v6[v1]; v6[v1] = v6[v3]; v6[v3] = v7; ++v1; } v1 = 0; v3 = 0; int v2 = 0; while(v2 < v0) { v1 = (v1 + 1) % 0x100 & 0xFF; v3 = (v6[v1] + v3) % 0x100 & 0xFF; v7 = v6[v1]; v6[v1] = v6[v3]; v6[v3] = v7; arg12[v2] = ((byte)(arg12[v2] ^ v6[(v6[v1] + v6[v3]) % 0x100 & 0xFF])); ++v2; } }
这是的JD-GUI输出:
// Decompiled by JD-GUI v0.3.5 public static void rc4_crypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2) { int i = paramArrayOfByte1.length; int j = paramArrayOfByte2.length; byte[] arrayOfByte = new byte[256]; int k = 0; int m; int n; label30: int i2; int i3; if (k >= 256) { m = 0; n = 0; if (n < 256) break label68; i2 = 0; i3 = 0; } for (int i4 = 0; ; i4++) { if (i4 >= j) { return; arrayOfByte[k] = ((byte)k); k++; break; label68: m = 0xFF & (m + arrayOfByte[n] + paramArrayOfByte1[(n % i)]) % 256; int i1 = arrayOfByte[n]; arrayOfByte[n] = arrayOfByte[m]; arrayOfByte[m] = i1; n++; break label30; } i2 = 0xFF & (i2 + 1) % 256; i3 = 0xFF & (i3 + arrayOfByte[i2]) % 256; int i5 = arrayOfByte[i2]; arrayOfByte[i2] = arrayOfByte[i3]; arrayOfByte[i3] = i5; paramArrayOfByte2[i4] = ((byte)(arrayOfByte[(0xFF & (arrayOfByte[i2] + arrayOfByte[i3]) % 256)] ^ paramArrayOfByte2[i4])); } }
http://www.android-decompiler.com/comp.php
另外现在很多给dex加点料,让工具崩了的手法。
$ dex2jar obfuscated.dex dex2jar obfuscated.dex -> obfuscated-dex2jar.jar com.googlecode.dex2jar.DexException: while accept method:[LA;.f(I)I] at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:699) at com.googlecode.dex2jar.reader.DexFileReader.acceptClass(DexFileReader.java:446) at com.googlecode.dex2jar.reader.DexFileReader.accept(DexFileReader.java:328) at com.googlecode.dex2jar.v3.Dex2jar.doTranslate(Dex2jar.java:84) at com.googlecode.dex2jar.v3.Dex2jar.to(Dex2jar.java:239) at com.googlecode.dex2jar.v3.Dex2jar.to(Dex2jar.java:230) at com.googlecode.dex2jar.tools.Dex2jarCmd.doCommandLine(Dex2jarCmd.java:107) at com.googlecode.dex2jar.tools.BaseCmd.doMain(BaseCmd.java:168) at com.googlecode.dex2jar.tools.Dex2jarCmd.main(Dex2jarCmd.java:34) Caused by: com.googlecode.dex2jar.DexException: while accept code in method:[LA;.f(I)I] at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:689) ... 8 more Caused by: java.lang.ArrayIndexOutOfBoundsException: 15 at com.googlecode.dex2jar.v3.V3CodeAdapter.visitConstStmt(V3CodeAdapter.java:326) at com.googlecode.dex2jar.reader.DexOpcodeAdapter.x1n(DexOpcodeAdapter.java:262) at com.googlecode.dex2jar.reader.DexCodeReader.acceptInsn(DexCodeReader.java:411) at com.googlecode.dex2jar.reader.DexCodeReader.accept(DexCodeReader.java:330) at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:686) ... 8 more
一个简单的例子混淆一下,里面再加点料。 如何加料从而和各类工具作斗争也是个大学问!!
This example is a very simple piece of code that was run through a flow obfuscator and junk insertion program.
.method public f(I)I const/4 v15, 0x7 move/from16 v6, v15 move v7, v15 and-int/lit8 v6, v15, 0x4 move/from16 v9, v7 const v14, 0x5 add-int v9, v9, v15 mul-int/2addr v7, v9 const v7, 0x5 sub-int/2addr v9, v6 move v11, v7 move/16 v13, v6 goto/16 :180 :30 return v0 :36 goto :38 :38 const v7, 0x6 move v15, v7 const/4 v8, 0x3 move v0, v2 const v15, 0x2 const v3, 0x1 div-int/lit8 v3, v8, 0x3 mul-int/lit8 v8, v3, 0x2 div-int/lit8 v8, v3, 0x2 div-int/2addr v7, v8 const/4 v5, 0x2 and-int v8, v8, v5 const v5, 0x6 goto :6C :6C const v9, 0x1 const/16 v6, 0x2 add-int/lit8 v9, v6, 0x2 move/from16 v10, v9 move/from16 v14, v10 move/16 v5, v6 const/16 v3, 0x0 move/from16 v6, v9 add-int v6, v6, v14 add-int/lit8 v0, v0, 0x1 const v14, 0x6 rem-int v5, v5, v6 const/4 v10, 0x6 shr-int/lit8 v10, v14, 0x5 goto/16 :1EC :AC const v13, 0x2 const/4 v9, 0x7 const/4 v15, 0x1 const/4 v10, 0x2 rem-int/lit16 v9, v13, 0x7 move/from16 v4, v15 ushr-int/2addr v15, v10 const v5, 0x6 shl-int v5, v5, v13 div-int/lit8 v4, v5, 0x1 const v14, 0x6 xor-int v15, v15, v14 const v7, 0x6 move v15, v5 if-ltz v15, :12E :E6 const/16 v7, 0x4 const/16 v4, 0x7 move/16 v14, v5 xor-int/lit16 v7, v14, 0x2 move/from16 v9, v15 div-int v4, v4, v10 move v8, v15 and-int v9, v9, v4 const/16 v4, 0x0 move/from16 v14, v9 const v12, 0x1 move/16 v4, v12 mul-int/lit16 v14, v10, 0x0 move v7, v10 div-int v10, v10, v7 or-int/lit16 v4, v8, 0x6 mul-int v5, v5, v15 goto :12E :12E const v3, 0x7 move/16 v10, v3 ushr-int/lit8 v10, v3, 0x0 and-int/lit8 v10, v3, 0x0 const/4 v4, 0x4 xor-int/lit16 v3, v4, 0x3 move/from16 v6, v4 xor-int/lit8 v3, v6, 0x3 sub-int v10, v10, v4 goto :156 :156 const v4, 0x2 const v6, 0x4 add-int/lit16 v6, v4, 0x6 move v11, v4 const v6, 0x5 move/from16 v10, v11 move/from16 v3, v4 and-int v10, v10, v4 shr-int v4, v4, v6 goto :1E2 :180 const v13, 0x0 move/from16 v7, v13 add-int/lit8 v13, v7, 0x1 const/16 v11, 0x3 div-int v7, v7, v11 xor-int v13, v13, v11 if-ge v7, v13, :36 :19E xor-int/2addr v7, v13 move v10, v13 and-int/lit16 v11, v13, 0x6 const v7, 0x5 const/16 v8, 0x6 rsub-int v10, v11, 0x6 const v13, 0x5 const v5, 0x0 const v5, 0x1 and-int v10, v10, v5 rem-int/lit16 v11, v8, 0x7 move/16 v4, v10 move v10, v13 or-int/2addr v7, v13 rem-int/lit16 v5, v13, 0x0 div-int/2addr v10, v11 goto/16 :36 :1E2 const v11, 0x1 goto/16 :30 :1EC const v8, 0x0 move v9, v8 move/from16 v8, v9 goto/16 :AC .end method
上面的程序看起来好好负责吆!JEB's deobfuscator chimes in and decompiles it to a simple:
你妹,原来就是这么简单的程序呀:
// Decompiled by JEB v0.9.0 alpha public int f(int arg2) { int v0 = arg2 + 1; return v0; }