jeb插件扩展

关于Android 安全工具jeb的基本使用和自定义插件扩展。

1.基本使用

A.针对对象

  • jar文件(利用dx工具将jar文件转换为dex文件)
  • dex文件
  • apk文件

B.基本操作

  • 重命名(n)
  • 跟踪(Enter, 双击)
  • 返回(Esc)
  • 前进(Ctrl + Enter)
  • 帮助(H)
  • 条目信息(I)
  • 交叉引用(X),源码交叉引用(Ctrl + X)
  • 注释(;或者 C)
  • 改变进制数(B)
  • 反编译(Tab)

C.参考

【移动安全基础篇】——04、JEB使用

2.jeb插件扩展(java/python)

除了一些简单的增加分析效率的扩展(如提取资源文件,打印指定方法的交叉引用等),比较重要的扩展应用就是实现字符串解密、混淆后的apk代码的部分还原等功能。

A.API手册

可在jeb的帮助中查看其可用API,如下图。


关键API(位于jeb.jar中):jeb.api.ast(Abstract Syntax Tree-抽象语法树),以如下图所示的函数isZtz162为例——

其AST结构如图所示——

可以通过如下代码来递归打印一个Method中的各个Element——

#!python
class test(IScript):
def run(self, j):
    self.instance = j
    sig = self.instance.getUI().getView(View.Type.JAVA).getCodePosition().getSignature()
    currentMethod = self.instance.getDecompiledMethodTree(sig)
    self.instance.print("scanning method: " + currentMethod.getSignature())
    body = currentMethod.getBody()
    self.instance.print(repr(body))
    for i in range(body.size()):
        self.viewElement(body.get(i),1)
def viewElement(self, element, depth):
    self.instance.print("    "*depth+repr(element))
    for sub in element.getSubElements():
        self.viewElement(sub, depth+1)

JebAPI 之 jeb.api.ast
Android应用分析进阶教程之一——初识jebapi

B.区分脚本和插件

JEB客户端扩展(脚本)应该用Python编写;JEB后端扩展(插件)应该用Java 编写(某些类的后端插件可能用Python编写)。

(1)脚本(python)

  1. jeb1支持java脚本和python脚本,jeb2目前只支持python脚本。

  2. 使用方法:在jeb安装目录的scripts目录下放置jython-standalone-2.7.0.jar和自定义python脚本,然后在用jeb分析apk的过程中,File -> Scripts -> Run script即可。

JEB2插件,导出APK资源文件
JEB脚本开发折腾小记
jeb2 java 脚本插件

(2)插件(java)

一、jeb插件支持用java和python语言编写。

二、java插件编写环境配置:在eclipse中建立java工程,然后将javadoc路径指向jeb/doc/apidoc.zip,添加library jeb.jar即可。

三、插件安装:将eclipse中的java项目导出为jar,然后放到jeb安装目录的coreplugins目录下,jeb在启动时就会加载相应的插件。

#简单的java插件示例——SampleEnginesPlugin.java

package com.pnf.plugintest;

import java.util.List;
import java.util.Map;

import com.pnfsoftware.jeb.core.IEnginesContext;
import com.pnfsoftware.jeb.core.IEnginesPlugin;
import com.pnfsoftware.jeb.core.IOptionDefinition;
import com.pnfsoftware.jeb.core.IPluginInformation;
import com.pnfsoftware.jeb.core.PluginInformation;
import com.pnfsoftware.jeb.core.Version;
import com.pnfsoftware.jeb.util.logging.GlobalLog;
import com.pnfsoftware.jeb.util.logging.ILogger;

/**
 * Sample plugin.
 * 
 * @author Nicolas Falliere
 */
public class SampleEnginesPlugin implements IEnginesPlugin {
    private static final ILogger logger = GlobalLog.getLogger(SampleEnginesPlugin.class);

    @Override
    public IPluginInformation getPluginInformation() {
        return new PluginInformation("Sample Engines Plugin", "A sample JEB back-end plugin", "PNF Software",
                Version.create(1, 0, 1));
    }

    @Override
    public void load(IEnginesContext context) {
        logger.info("Sample plugin is loaded");
    }

    @Override
    public List<? extends IOptionDefinition> getExecutionOptionDefinitions() {
        return null;
    }

    @Override
    public void execute(IEnginesContext context) {
        execute(context, null);
    }

    @Override
    public void execute(IEnginesContext engctx, Map<String, String> executionOptions) {
        logger.info("Executing sample plugin");
    }

    @Override
    public void dispose() {
    }
}

B.实例

(1)打印目标方法的交叉引用

from jeb.api import IScript

class MyPlugin(IScript):

def run(self, jeb):

    OpMethod = "sendTextMessage"

    def foo(dex, i, num_hyphen):
        l = dex.getMethodReferences(i)    //获取方法i的交叉引用l
        for k in l:
            print '-'*num_hyphen+dex.getMethod(k).getSignature(True)
            if dex.getMethodReferences(k) != None:
                foo(dex, k, num_hyphen+4)  //递归调用回溯上层函数调用,用'-'*num_hyphen表示调用关系

    print ''
    print ''
    dex = jeb.getDex()
    method_count = dex.getMethodCount()
    print "method_count:"+str(method_count)            
    for i in range(method_count):
        if dex.getMethod(i).getSignature(True).find(OpMethod)>=0:
            print '*'+dex.getMethod(i).getName()
            print dex.getMethod(i).getSignature(True)
            foo(dex, i, 4)

编写jeb插件打印目标方法的交叉引用

(2)处理混淆应用

编写自己的JEB2插件

主要处理逻辑——

  • 对于类:递归寻找它的父类和实现的接口。如果父类和接口包含了有意义的名字:例如SDK类Activity、不能混淆的类名MainActivity,以此为基础进行重命名。
  • 对于Field:根据该Field的类型,重命名其名字。
  • 对于函数:根据该函数的参数类型,重命名其名字。

思路根据被混淆item的基类信息和类型信息/参数信息对其重命名的主要逻辑实现如下——

for clz in codeunit.getClasses(): 
    if isObfuscated(clz):
        name = determineNameFromHierchy(clz) --->1
        rename(clz, name)

for field in codeUnit.getFields():
    if isObfuscated(field):
        name = determineNameByFieldType(field)
        rename(field, name)

for mtd in codeUnit.getMethods():
    if isObfuscated(mtd):
        name = determineNameByArgsType(field)
        rename(field, name)

例如, class IiIiIiIi是继承于class iIiIiIiI, 而iIiIiIiI又继承于Activity/实现了onClickListener, 那么我们就可以使用Activity/onClickListener作为基准重命名两个被混淆的类. 这里的关键在于一个递归获取基类的函数, 代码如下所示——

'''
clzElement is ICodeClass retrieved from ICodeUnit.getClass()
'''
def tryDetermineGodeName(self, clzElement):
    javaunit = self.decomp.decompile(clzElement.getAddress())
    clzElement = javaunit.getClassElement()
    #now clzElement is a IJavaClass

    if not isFuckingName(clzElement.getName()):
      #this is a non-obfuscated name, just return it
      return clzElement.getName()
    ssupers = clzElement.getImplementedInterfaces()
    supers = []
    supers.extend(ssupers)
    # do not directly append on returned list!

    superSig = clzElement.getSupertype().getSignature()
    supers.append(clzElement.getSupertype())

    for superItem in supers:
      sig = superItem.getSignature()
      if sig == "Ljava/lang/Object;":
        #extend from java/lang/Object gives us zero info
        #so try next
        continue
      if not isFuckingName(sig):
        #return first non-obfuscated name
        return sig
      resolvedType = self.targetUnit.getClass(sig)
      if resolvedType:
        #this is a concret class
        guessedName = self.tryDetermineGoodName(resolvedType)
        if guessedName:
          return guessedName
      else:
        #this is a SDK class
        return sig
    #cannot determine name from its supers, return None
    return None

JEB2 script 中调用 api 重命名所有派生类

(3)其他

一个简单的jeb字符串解密脚本
各种Jeb插件,包括混淆恢复(适用于jeb1)
官方的python脚本和java插件的实例