本文转载自: https://www.zybuluo.com/oro-oro/note/143651

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整体结构如下: 

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

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

2. NonStateMent 部分

 

2.1 Class, Method, Field

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

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

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

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

import jeb.api.IScript;
import jeb.api.JebInstance;
import jeb.api.ast.Field;
import jeb.api.ast.Method;
import jeb.api.dex.Dex;
import java.util.List;
public class TestASTClass implements IScript {
    @Override
    public void run(JebInstance jebInstance) {
        Dex dex = jebInstance.getDex();
        List<String> classSignatures = dex.getClassSignatures(true);
        for (String classSignature : classSignatures) {
            if (!classSignature.contains("MainActivity")) {
                continue;
            }
            jebInstance.print(classSignature);
            jeb.api.ast.Class decompiledClassTree = jebInstance.getDecompiledClassTree(classSignature);
            if (decompiledClassTree == null) {
                continue;
            }
            jebInstance.print("\nField : ");
            List<Field> fields = decompiledClassTree.getFields();
            for (Field field : fields) {
                jebInstance.print(field.getSignature());
            }
            jebInstance.print("\nMethod : ");
            List<Method> methods = decompiledClassTree.getMethods();
            for (Method method : methods) {
                jebInstance.print(method.getSignature());
            }
            jebInstance.print("\nInnerClass : ");
            List<jeb.api.ast.Class> innerClasses = decompiledClassTree.getInnerClasses();
            for (jeb.api.ast.Class cls : innerClasses) {
                jebInstance.print(cls.getType());
            }
            String type = decompiledClassTree.getType();
            jebInstance.print("\nType : " + type);
        }
    }
}

2.2 ArrayElt, Identifier, InstanceField, StaticField

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

这3个都实现了ILeftExpression

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

if (field instanceof InstanceField) {
    }

2.3 ConditionalExpression, Constant, Expression, TypeReference

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

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

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 部分

 

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项目,随便写个简单的加密算法。

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

Jeb 显示为:

package com.test.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
    public static final byte[] bytes;
    static {
        MainActivity.bytes = new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33};
    }
    public MainActivity() {
        super();
    }
    String decode(int a, int b) {
        return new String(new byte[]{MainActivity.bytes[a], MainActivity.bytes[b]});
    }
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(2130968601);
        System.out.println(this.decode(0, 6));
    }
}

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

import jeb.api.IScript;
import jeb.api.JebInstance;
import jeb.api.ast.*;
import jeb.api.dex.Dex;
import jeb.api.dex.DexMethod;
import java.util.ArrayList;
import java.util.List;
public class TestASTDecode implements IScript {
    public static final byte[] bytes = new byte[]{'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
    @Override
    public void run(JebInstance jebInstance) {
        Dex dex = jebInstance.getDex();
        List<String> classSignatures = dex.getClassSignatures(true);
        Constant.Builder cstBuilder = new Constant.Builder(jebInstance);
        // 获得decode方法的sig
        int methodCount = dex.getMethodCount();
        String decodeMtdSig;
        for (int i = 0; i < methodCount; i++) {
            DexMethod dexMethod = dex.getMethod(i);
            int index = dexMethod.getIndex();
            decodeMtdSig = dex.getMethod(i).getSignature(true);
            if (decodeMtdSig.contains("Lcom/test/helloworld/MainActivity;->decode")) {
                // 找出所有使用了该方法的地方
                List<Integer> methodReferences = dex.getMethodReferences(index);
                for (Integer refIdx : methodReferences) {
                    DexMethod refDexMethod = dex.getMethod(refIdx);
                    // 找到AST中对应的Method
                    Method decompiledMethodTree = jebInstance.getDecompiledMethodTree(refDexMethod.getSignature(true));
                    // 拿到语句块,遍历所有语句
                    Block block = decompiledMethodTree.getBody();
                    int size = block.size();
                    for (int j = 0; j < size; j++) {
                        Statement statement = block.get(j);
                        jebInstance.print(statement.toString());
                        if (statement instanceof Call) {
                            Call call = (Call) statement;
                            Method method = call.getMethod();
                            jebInstance.print("Call : " + method.getSignature());
                            List<IElement> subElements = call.getSubElements();
                            for (IElement element : subElements) {
                                jebInstance.print("Sub Element : " + element.toString());
                                if (element instanceof Call) {
                                    Call c = (Call) element;
                                    Method m = c.getMethod();
                                    if (m.getSignature().equals(decodeMtdSig)) {
                                        jebInstance.print(">>> " + decodeMtdSig);
                                        List<IExpression> arguments = c.getArguments();
                                        ArrayList<Integer> arrayList = new ArrayList<Integer>();
                                        for (IExpression expression : arguments) {
                                            if (expression instanceof Constant) {
                                                arrayList.add(((Constant) expression).getInt());
                                            }
                                        }
                                        String str = decode(arrayList.get(0), arrayList.get(1));
                                        jebInstance.print("解密后的字符串 : " + str);
                                        // 将当前语句Call的子元素,替换为解密后的字符串
                                        call.replaceSubElement(element, cstBuilder.buildString(str));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    String decode(int a, int b) {
        byte[] ab = new byte[]{bytes[a], bytes[b]};
        return new String(ab);
    }
}

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

package com.test.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
    public static final byte[] bytes;
    static {
        MainActivity.bytes = new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33};
    }
    public MainActivity() {
        super();
    }
    String decode(int a, int b) {
        return new String(new byte[]{MainActivity.bytes[a], MainActivity.bytes[b]});
    }
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(2130968601);
        System.out.println("HW");
    }
}

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

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

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

[转载] 3. JebAPI 之 jeb.api.ast的更多相关文章

  1. [转载] 4. JebAPI 之 jeb.api.ui

    本文转载自: https://www.zybuluo.com/oro-oro/note/145250 JebInstance可以通过getUI()方法来获得jeb.api.ui.JebUI. JebU ...

  2. [转载] 1. JebAPI 之 jeb.api

    本文转载自: https://www.zybuluo.com/oro-oro/note/142707 JEB API 官方地址:https://www.pnfsoftware.com/apidoc/  ...

  3. [转载] 2. JebAPI 之 jeb.api.dex

    本文转载自: https://www.zybuluo.com/oro-oro/note/142842 1. jeb.api.dex.Dex 这个类代表正在被JEB处理的DEX文件. 要想更好的了解这个 ...

  4. [转载] ZooKeeper的Java客户端API

    转载自 http://www.cnblogs.com/ggjucheng/p/3370359.html http://zookeeper.apache.org/doc/trunk/javaExampl ...

  5. [转载]Java 8 日期&时间 API

    Java 8 日期和时间 声明 本文转自http://www.journaldev.com/2800/java-8-date-localdate-localdatetime-instant,以mark ...

  6. (转载) ASP.NET(C#) Web Api 通过文件流下载文件到本地实例

    下载文件到本地是很多项目开发中需要实现的一个很简单的功能.说简单,是从具体的代码实现上来说的,.NET的文件下载方式有很多种,本示例给大家介绍的是ASP.NET Web Api方式返回HttpResp ...

  7. (转载)Java8新的日期API LocalDate, LocalTime

    前言 由于Java Date的各种问题,Java8推出了新的日期API,很受一拨人的追捧. 为什么我们需要新的Java日期/时间API? 在开始研究Java 8日期/时间API之前,让我们先来看一下为 ...

  8. 转载LoadRunner的常用Java API

    Java API是访问Vuser函数的基础,通过LoadRunner的Java API可以在脚本中很容易地创建事务与并发点.获取用户信息等功能. 1. 事务函数(Transaction Functio ...

  9. 【转载】Asp .Net Web Api路由路径问题

    原文章地址:https://www.cnblogs.com/devtester/p/8897302.html MVC也好,WebAPI也好,据我所知,有部分人是因为复杂的路由,而不想去学的.曾经见过一 ...

随机推荐

  1. mysql source命令超大文件导入方法总结

    本文章来给各位朋友介绍利用mysql source命令超大文件导入方法总结,下面收集了两种解决办法,一种是把数据库分文件导出然后再导入,另一种是修改my.ini配置文件,下面我一一给各位朋友介绍. 导 ...

  2. SSD(Single Shot MultiBox Detector)的安装配置和运行

    下文图文介绍转自watersink的博文SSD(Single Shot MultiBox Detector)不得不说的那些事. 该方法出自2016年的一篇ECCV的oral paper,SSD: Si ...

  3. Mvc4系列文章

    http://www.cnblogs.com/duanshuiliu/tag/MVC/

  4. Android Studio IDE 简单学习和介绍

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  5. [转载]QQ空间技术架构之深刻揭密

    1. 拥有5.5亿的活跃用户 2. 过万台的设备 3. 数千万级别的同时在线 4. 数十亿级别的全站PV 5. P级的UGC存储量 6. 每天千亿级别的服务请求 图1--QQ空间海量服务数据规模 接下 ...

  6. android图像与图像处理系列(一、Bitmap和BitmapFactory)

    1.Drawable对象 Android应用添加了Drawabe资源之后,Android SDK会为这份资源文件在R清单文件中创建一个索引项:R.drawable.file_name,接着我们可以在x ...

  7. morhia解决BigDecimal映射问题

    错误信息:morphia No usable constructor for java.math.BigDecimal 解决办法:增加自定义类型转换器,代码如下 package cn.huilink. ...

  8. SpringMVC学习系列(9) 之 实现注解式权限验证

    对大部分系统来说都需要权限管理来决定不同用户可以看到哪些内容,那么如何在Spring MVC中实现权限验证呢?当然我们可以继续使用servlet中的过滤器Filter来实现.但借助于Spring MV ...

  9. WebApi:WebApi的Self Host模式

    不用IIS也能執行ASP.NET Web API 转载:http://blog.darkthread.net/post-2013-06-04-self-host-web-api.aspx 在某些情境, ...

  10. 【学+原】CSS3的2D动画 ——仿NOMOS手表

    看CSS3妙味课堂中有一课是介绍如何做钟表界面,然后三根针都能随着时间转动,然后自己在那个简易的版本上做了一些进一部改进. 最关键的知识点应该是transform-origin这个样式,要选对旋转的中 ...