使用 vlx-vmengine 进行反混淆

vlx-vmengine-jvm 是一个用 Java 实现的 JVM 字节码执行引擎。使用方法请参阅 https://github.com/vlinx-io/vlx-vmengine-jvm

以下是一段简单的 Java 代码:

class HelloWorld {

    private String name = "";

    public HelloWorld(String name){
        this.name = name;
    }

    public void sayHi(){
        System.out.println("Hi, " + name);
    }

    public static void main(String[] args){
        
        String name = "George";
        HelloWorld hello = new HelloWorld(name);

        hello.sayHi();

    }

}

编译成 class 文件后,使用某混淆引擎进行混淆,得到以下文件:

a.class

使用 jadx 打开后发现,除了 main 函数外,所有其他信息都无法识别,字符串也已加密。

image-20230521195209691

然而,即使经过混淆,类的基本结构和字节码信息仍然存在。使用 ClassViewer 打开 a.class,可以看到类的方法和字节码信息。 image-20230521200149593

无论如何混淆,都只能在静态层面混淆代码并增加分析复杂度。在动态执行时,仍然需要还原程序原始的运行逻辑。使用 vlx-vmengine-jvm 运行 main 方法中的混淆代码,我们得到以下输出:

2023-05-21 18:19:05 [DEBUG] LocalVars: [kotlin.Unit, kotlin.Unit, kotlin.Unit]
2023-05-21 18:19:05 [DEBUG] "L0: SIPUSH"
2023-05-21 18:19:05 [DEBUG] "push" 7144
2023-05-21 18:19:05 [DEBUG] "L3: SIPUSH"
2023-05-21 18:19:05 [DEBUG] "push" -13249
2023-05-21 18:19:05 [DEBUG] "L6: INVOKESTATIC"
2023-05-21 18:19:05 [DEBUG] "#20"
2023-05-21 18:19:05 [DEBUG] "class a, NameAndType(name='a', type='(II)Ljava/lang/String;')"
2023-05-21 18:19:05 [DEBUG] private static java.lang.String a.a(int,int)
2023-05-21 18:19:05 [DEBUG] "pop" -13249
2023-05-21 18:19:05 [DEBUG] "pop" 7144
2023-05-21 18:19:05 [DEBUG] 	Execute method: private static java.lang.String a.a(int,int)
2023-05-21 18:19:05 [DEBUG] 	Args: [7144, -13249]
2023-05-21 18:19:05 [DEBUG] "push" "George"
2023-05-21 18:19:05 [DEBUG] "L9: ASTORE_1"
2023-05-21 18:19:05 [DEBUG] "pop" "George"
2023-05-21 18:19:05 [DEBUG] "localVars[1] = George"
...
Hi, George
2023-05-21 18:19:05 [DEBUG] "L23: RETURN"

从控制台输出中,我们可以看到程序已经还原了其原始行为,即打印 Hi, George。同时,从输出中我们还可以确定字符串解密函数位于 private static java.lang.String a.a(int,int) 中,参数为 7144-13249。如果我们继续使用 vmengine 调试 a.a(int,int) 方法,就可以发现该混淆引擎使用的字符串加密方法。