[关闭]
@oro-oro 2015-08-18T10:53:21.000000Z 字数 7087 阅读 3182

3. JebAPI 之 jeb.api.ast

JebAPI


0. 序

Jeb 本身是支持变量重命名的,所以,混淆了的变量名、类名可以修改。
实际上,它还可以做到这种效果 Decompiled Java Code Manipulation using JEB API – Part 2: Decrypting Strings

例子中的脚本在这:ASTDecryptStrings.py

整体的逻辑:
1. 破解解密算法。
2. 遍历类里面的所有方法
3. 如果方法为解密方法,则提取所有的参数出来,给解密方法。
4. 计算出解密的字符串,最终用replaceSubElement替换掉整个函数调用。

而这些都需要去了解 jeb.api.ast。

1. AST 整体结构简介

AST是Abstract Syntax Tree的简称,也就是抽象语法树,这是用来解析源码的。
源代码有各种各样的结构和语句,譬如类、方法、变量、表达式、常量、For语句、Break语句等等。
这些东西AST都会有对应的类一一对应。

整个jeb.api.ast整体结构如下:
JEBAST

所有的类都直接或间接实现了 IElement。
它有2个重要的方法:

方法 说明
getSubElements() 获取该元素的子元素列表。
replaceSubElement(IElement old_elt, IElement new_elt) 用另外一个元素替换一个元素。

2. NonStateMent 部分

JEBAST_R

2.1 Class, Method, Field

Class:表示一个Java类。类则包含了内部类、方法(Method)、变量(Field)等。

Method:表示一个Java方法。可以用JebInstance.getDecompiledMethodTree来获得一个方法,可以通过Method.Builder创建方法。

Field:表示一个Java变量。可以通过Field.Builder创建变量。

这3类元素都是只读元素,而不是表达式(Expression),跟其他类是有区别的。

  1. import jeb.api.IScript;
  2. import jeb.api.JebInstance;
  3. import jeb.api.ast.Field;
  4. import jeb.api.ast.Method;
  5. import jeb.api.dex.Dex;
  6. import java.util.List;
  7. public class TestASTClass implements IScript {
  8. @Override
  9. public void run(JebInstance jebInstance) {
  10. Dex dex = jebInstance.getDex();
  11. List<String> classSignatures = dex.getClassSignatures(true);
  12. for (String classSignature : classSignatures) {
  13. if (!classSignature.contains("MainActivity")) {
  14. continue;
  15. }
  16. jebInstance.print(classSignature);
  17. jeb.api.ast.Class decompiledClassTree = jebInstance.getDecompiledClassTree(classSignature);
  18. if (decompiledClassTree == null) {
  19. continue;
  20. }
  21. jebInstance.print("\nField : ");
  22. List<Field> fields = decompiledClassTree.getFields();
  23. for (Field field : fields) {
  24. jebInstance.print(field.getSignature());
  25. }
  26. jebInstance.print("\nMethod : ");
  27. List<Method> methods = decompiledClassTree.getMethods();
  28. for (Method method : methods) {
  29. jebInstance.print(method.getSignature());
  30. }
  31. jebInstance.print("\nInnerClass : ");
  32. List<jeb.api.ast.Class> innerClasses = decompiledClassTree.getInnerClasses();
  33. for (jeb.api.ast.Class cls : innerClasses) {
  34. jebInstance.print(cls.getType());
  35. }
  36. String type = decompiledClassTree.getType();
  37. jebInstance.print("\nType : " + type);
  38. }
  39. }
  40. }

2.2 ArrayElt, Identifier, InstanceField, StaticField

ArrayElt : 表示一个数组的元素,如array[index]。
Identifier:表示一个Java标识符或变量。
InstanceField:表示非静态变量,这是一个左值表达式,不要跟jeb.api.ast.Field混淆。
StaticField:表示静态变量,不要跟jeb.api.ast.Field混淆。

这3个都实现了ILeftExpression

下面代码是有问题的,Field不是表达式,因为变量可能用在任何地方,而具体使用的地方,才是表达式。

  1. if (field instanceof InstanceField) {
  2. }

2.3 ConditionalExpression, Constant, Expression, TypeReference

ConditionalExpression:表示条件表达式,如a ? b : c。

Constant:表示一个常量值,类型支持8个主要类型 (boolean, byte, char, short, int, long, float, double) 和字符串。
通过Constant.Builder.可创建一个Constant

  1. Constant constant = new Constant.Builder(jebInstance).buildString("Hello World!");

Expression:算术或逻辑表达式。
一个表达式,包含1个或2个成员(左成员和右成员)和一个操作符(Operator),其中左成员是可选的。
如:

  1. a + 1
  2. a * ((int)b - foo())
  3. !x
  4. x ^ y | z

TypeReference : 表示一个类型引用,用在instanceof表达式中。
如:if(pet instanceof Dog) { ... }

3. Statement 部分

JEBAST-L

3.1 Assignment, Break, Continue, Goto, Label, Monitor, Return, Throw

这些类都是直接继承了 Statement,一看类名就知道什么意思了,这里单单看Assignment

Assignment : 表示赋值语句,如 left = right。

方法 说明
getLeft() 获得赋值语句的左边表达式
getRight() 获得赋值语句的右边表达式
setRight() 直接修改右边语句

3.2 Compound

Compound :混合语句,包含了更多的语句,基于一个或多个语句块。
下面的子类有:Block, DoWhileStm, ForStm, IfStm, SwitchStm, TryStm, WhileStm。

Block : 表示语句块,是最基本的混合语句,里面包含多个语句。
如:

  1. {
  2. stm0;
  3. stm1;
  4. stm2;
  5. }

其他语句块,则类似。

3.3 Definition, New

Definition : 声明语句。
New: 表示New了一个非数组对象。

3.4 Call, NewArray

Call:表示一个方法调用,如foo(0, 1, "bar")
NewArray:表示New了一个数组对象。

4. 解密demo

准备一个helloworld项目,随便写个简单的加密算法。

  1. package com.test.helloworld;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. public static final byte[] bytes = new byte[]{'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. System.out.println(decode(0, 6));
  11. }
  12. String decode(int a, int b) {
  13. byte[] ab = new byte[]{bytes[a], bytes[b]};
  14. return new String(ab);
  15. }
  16. }

Jeb 显示为:

  1. package com.test.helloworld;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. public static final byte[] bytes;
  6. static {
  7. MainActivity.bytes = new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33};
  8. }
  9. public MainActivity() {
  10. super();
  11. }
  12. String decode(int a, int b) {
  13. return new String(new byte[]{MainActivity.bytes[a], MainActivity.bytes[b]});
  14. }
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. this.setContentView(2130968601);
  18. System.out.println(this.decode(0, 6));
  19. }
  20. }

接下来,我们参考ASTDecryptStrings.py,将所有出现decode解密方法的地方,都替换成解密的字符串。
因为有python的例子了,这里用Java实现。

  1. import jeb.api.IScript;
  2. import jeb.api.JebInstance;
  3. import jeb.api.ast.*;
  4. import jeb.api.dex.Dex;
  5. import jeb.api.dex.DexMethod;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. public class TestASTDecode implements IScript {
  9. public static final byte[] bytes = new byte[]{'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
  10. @Override
  11. public void run(JebInstance jebInstance) {
  12. Dex dex = jebInstance.getDex();
  13. List<String> classSignatures = dex.getClassSignatures(true);
  14. Constant.Builder cstBuilder = new Constant.Builder(jebInstance);
  15. // 获得decode方法的sig
  16. int methodCount = dex.getMethodCount();
  17. String decodeMtdSig;
  18. for (int i = 0; i < methodCount; i++) {
  19. DexMethod dexMethod = dex.getMethod(i);
  20. int index = dexMethod.getIndex();
  21. decodeMtdSig = dex.getMethod(i).getSignature(true);
  22. if (decodeMtdSig.contains("Lcom/test/helloworld/MainActivity;->decode")) {
  23. // 找出所有使用了该方法的地方
  24. List<Integer> methodReferences = dex.getMethodReferences(index);
  25. for (Integer refIdx : methodReferences) {
  26. DexMethod refDexMethod = dex.getMethod(refIdx);
  27. // 找到AST中对应的Method
  28. Method decompiledMethodTree = jebInstance.getDecompiledMethodTree(refDexMethod.getSignature(true));
  29. // 拿到语句块,遍历所有语句
  30. Block block = decompiledMethodTree.getBody();
  31. int size = block.size();
  32. for (int j = 0; j < size; j++) {
  33. Statement statement = block.get(j);
  34. jebInstance.print(statement.toString());
  35. if (statement instanceof Call) {
  36. Call call = (Call) statement;
  37. Method method = call.getMethod();
  38. jebInstance.print("Call : " + method.getSignature());
  39. List<IElement> subElements = call.getSubElements();
  40. for (IElement element : subElements) {
  41. jebInstance.print("Sub Element : " + element.toString());
  42. if (element instanceof Call) {
  43. Call c = (Call) element;
  44. Method m = c.getMethod();
  45. if (m.getSignature().equals(decodeMtdSig)) {
  46. jebInstance.print(">>> " + decodeMtdSig);
  47. List<IExpression> arguments = c.getArguments();
  48. ArrayList<Integer> arrayList = new ArrayList<Integer>();
  49. for (IExpression expression : arguments) {
  50. if (expression instanceof Constant) {
  51. arrayList.add(((Constant) expression).getInt());
  52. }
  53. }
  54. String str = decode(arrayList.get(0), arrayList.get(1));
  55. jebInstance.print("解密后的字符串 : " + str);
  56. // 将当前语句Call的子元素,替换为解密后的字符串
  57. call.replaceSubElement(element, cstBuilder.buildString(str));
  58. }
  59. }
  60. }
  61. }
  62. }
  63. }
  64. }
  65. }
  66. }
  67. String decode(int a, int b) {
  68. byte[] ab = new byte[]{bytes[a], bytes[b]};
  69. return new String(ab);
  70. }
  71. }

执行完后,需要手工刷新, 反编译的代码,就可以看到解密的字符串了。

  1. package com.test.helloworld;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. public static final byte[] bytes;
  6. static {
  7. MainActivity.bytes = new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33};
  8. }
  9. public MainActivity() {
  10. super();
  11. }
  12. String decode(int a, int b) {
  13. return new String(new byte[]{MainActivity.bytes[a], MainActivity.bytes[b]});
  14. }
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. this.setContentView(2130968601);
  18. System.out.println("HW");
  19. }
  20. }

如果想让脚本解密后,自动刷新UI的话,需要调用UI的相关接口。

另外,Jeb使用Jython和Java作为脚本,而dex文件,又可以反编译为jar文件,这意味着可以直接加载jar包,调用解密方法来解密了。

HelloWorld的APK载地址:http://yunpan.cn/cdjW2su55qjR9 访问密码 aff9

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注