Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
simplify — 安卓虚拟机与去混淆器 | Kitploit
工具/GitHubGitHub/calebfenton/simplify
Android安全动态分析 (沙盒)逆向工程恶意软件分析二进制分析
GitHubcalebfenton/simplify

simplify

安卓虚拟机与去混淆器

查看仓库
4.7k455235年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Simplify

Build Status Coverage Status Coverity Scan Build Status

通用Android反混淆器

Simplify 通过虚拟执行应用来理解其行为,然后尝试优化代码,使其行为相同但更易于人类理解。每种优化类型都简单且通用,因此无论使用了何种具体的混淆技术都适用。

前后对比

左侧代码是混淆应用的反编译结果,右侧代码是经过反混淆处理的代码。

大量方法调用,含义不清 哇,如此字面量,如此含义

概述

项目包含三个部分:smalivm、simplify 和 demo 应用。

  1. smalivm:提供用于执行 Dalvik 方法的虚拟机沙箱。执行方法后,它会返回一个包含所有执行路径上所有可能的寄存器和类值的图。即使某些值未知(如文件和网络 I/O),它也能工作。例如,任何 if 或 switch 条件如果包含未知值,则两个分支都会被执行。
  2. simplify:分析来自 smalivm 的执行图,并应用常量传播、死代码消除、去反射以及一些窥孔优化。这些优化相对简单,但反复组合使用可以解密字符串、去除反射并大幅简化代码。它不会重命名方法和类。
  3. demoapp:包含简单且注释详尽的示例,演示如何在您自己的项目中使用 smalivm。如果您正在构建需要执行 Dalvik 代码的东西,请查看它。

用法

usage: java -jar simplify.jar <input> [options]
deobfuscates a dalvik executable
 -et,--exclude-types <pattern>   Exclude classes and methods which include REGEX, eg: "com/android", applied after include-types
 -h,--help                       Display this message
 -ie,--ignore-errors             Ignore errors while executing and optimizing methods. This may lead to unexpected behavior.
    --include-support            Attempt to execute and optimize classes in Android support library packages, default: false
 -it,--include-types <pattern>   Limit execution to classes and methods which include REGEX, eg: ";->targetMethod\("
    --max-address-visits <N>     Give up executing a method after visiting the same address N times, limits loops, default: 10000
    --max-call-depth <N>         Do not call methods after reaching a call depth of N, limits recursion and long method chains, default: 50
    --max-execution-time <N>     Give up executing a method after N seconds, default: 300
    --max-method-visits <N>      Give up executing a method after executing N instructions in that method, default: 1000000
    --max-passes <N>             Do not run optimizers on a method more than N times, default: 100
 -o,--output <file>              Output simplified input to FILE
    --output-api-level <LEVEL>   Set output DEX API compatibility to LEVEL, default: 15
 -q,--quiet                      Be quiet
    --remove-weak                Remove code even if there are weak side effects, default: true
 -v,--verbose <LEVEL>            Set verbosity to LEVEL, default: 0

构建

构建需要安装 Java 开发工具包 8 (JDK)。

由于本项目包含 Android 框架的子模块,请使用 --recursive 参数克隆:

git clone --recursive https://github.com/CalebFenton/simplify.git

或者随时更新子模块:

git submodule update --init --recursive

然后,构建一个包含所有依赖的单一 jar 包:

./gradlew fatjar

Simplify 的 jar 包将位于 simplify/build/libs/。您可以通过简化提供的混淆示例应用来测试它是否正常工作。运行方式如下(可能需要调整 simplify.jar 的路径):

java -jar simplify/build/libs/simplify.jar -it "org/cf/obfuscated" -et "MainActivity" simplify/obfuscated-app.apk

要了解哪些内容会被反混淆,请查看 Obfuscated App 的 README。

故障排除

如果 Simplify 执行失败,请按顺序尝试以下建议:

  1. 使用 -it 选项仅定位少量方法或类。
  2. 如果失败是因为超出最大访问次数,请尝试增大 --max-address-visits、--max-call-depth 和 --max-method-visits。
  3. 尝试使用 -v 或 -v 2 运行,并结合日志和 DEX 或 APK 的哈希值报告问题。
  4. 再试一次,但不要中断眼神接触。Simplify 可以感知恐惧。

如果在 Windows 上构建失败,并出现类似于以下错误:

Could not find tools.jar. Please check that C:\Program Files\Java\jre1.8.0_151 contains a valid JDK installation.

这意味着 Gradle 无法找到正确的 JDK 路径。请确保已安装 JDK,将 JAVA_HOME 环境变量设置为您的 JDK 路径,并确保关闭后重新打开构建时使用的命令提示符。

贡献

不要害羞。我认为虚拟执行和反混淆是非常迷人的问题。任何对此感兴趣的人自动就很酷,欢迎贡献,即使只是修正一个拼写错误。欢迎在 Issue 中提问并提交拉取请求。

报告问题

请包含 APK 或 DEX 的链接以及您使用的完整命令。这将大大方便问题的重现(从而修复)。

如果您无法分享样本,请包含文件哈希值(SHA1、SHA256 等)。

优化策略

常量传播

如果某个操作将值放置到可以转为常量的类型中(如字符串、数字或布尔值),此优化会用常量替换该操作。例如:

const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
# 解密为:"Tell me of your homeworld, Usul."
move-result v0

在此示例中,加密字符串被解密并存入 v0。由于字符串是“可常量化的”,move-result v0 可以被替换为 const-string:

const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."

死代码消除

如果移除代码后不可能改变应用的行为,则该代码是死的。最明显的情况是代码不可达,例如 if (false) { // dead })。如果代码是可达的,但不会影响方法外部的任何状态,即没有副作用,则可视为死代码。例如,代码可能不影响方法的返回值、不改变任何类变量、也不执行任何 I/O。在静态分析中这很难判断。幸运的是,smalivm 无需聪明,它只是愚蠢地执行所有能执行的内容,并在无法确定时假定存在副作用。考虑常量传播中的例子:

const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."

在这段代码中,invoke-static 不再影响方法的返回值,并且假设它不做任何奇怪的事情(如写入文件系统或网络套接字),因此没有副作用。它可以被简单地移除。

const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
const-string v0, "Tell me of your homeworld, Usul."

最后,第一个 const-string 给寄存器赋值,但该值从未被使用,即该赋值是死的。它也可以被移除。

const-string v0, "Tell me of your homeworld, Usul."

万岁!

去反射

Java 静态分析的一个主要挑战是反射。如果不进行精细的数据流分析,根本无法知道反射方法的参数是什么。有一些聪明巧妙的方法来实现,但 smalivm 只是通过执行代码来处理。当它发现反射方法调用时,例如:

invoke-virtual {v0, v1, v2}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;

它可以知道 v0、v1 和 v2 的值。如果确定这些值是什么,它就可以将对 Method.invoke() 的调用替换为实际的非反射方法调用。同样的情况也适用于反射的字段和类查找。

窥孔

对于所有不能干净地归入特定类别的内容,采用窥孔优化。这包括移除无用的 check-cast 操作、将 Ljava/lang/String;-><init> 调用替换为 const-string 等等。

反混淆示例

优化前

.method public static test1()I
    .locals 2

    new-instance v0, Ljava/lang/Integer;
    const/4 v1, 0x1
    invoke-direct {v0, v1}, Ljava/lang/Integer;-><init>(I)V

    invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I
    move-result v0

    return v0
.end method

这实际上只是 v0 = 1。

常量传播后

.method public static test1()I
    .locals 2

    new-instance v0, Ljava/lang/Integer;
    const/4 v1, 0x1
    invoke-direct {v0, v1}, Ljava/lang/Integer;-><init>(I)V

    invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I
    const/4 v0, 0x1

    return v0
.end method

move-result v0 被替换为 const/4 v0, 0x1。这是因为 intValue()I 只有一种可能的返回值,并且返回类型可以变为常量。参数 v0 和 v1 是明确的且不会改变。也就是说,在 intValue()I 的每条可能执行路径上,值的共识是一致的。其他可以变为常量的值类型包括:

  • 数字 - const/4、const/16 等
  • 字符串 - const-string
  • 类 - const-class

死代码消除后

.method public static test1()I
    .locals 2

    const/4 v0, 0x1

    return v0
.end method

因为 const/4 v0, 0x1 之上的代码不会影响方法外部的状态(无副作用),所以可以在不改变行为的情况下移除。如果有一个方法调用向文件系统或网络写入内容,则不能移除,因为它会影响方法外部的状态。或者如果 test()I 接受一个可变参数(如 LinkedList),任何访问它的指令都不能被视为死代码。

其他死代码的例子:

  • 未引用的赋值 - 给寄存器赋值但从未使用
  • 未到达/不可达的指令 - if (false) { dead_code(); }

许可证

本工具采用双重许可证:一个适用于闭源项目的商业许可证,以及一个可用于开源软件的 GPL 许可证。

根据您的需求,您必须选择其中之一并遵守其政策。每种许可证类型的政策与协议详情请参阅 LICENSE.COMMERCIAL 和 LICENSE.GPL 文件。

延伸阅读

下载工具