unidbg 实现 JNI 与 Java 交互

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

Android 示例代码

在 com.cyrus.example.unidbg.UnidbgActivity 编写一个静态变量 a 和非静态变量 b,还有 base64 方法。

代码如下:

package com.cyrus.example.unidbg

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.util.Base64

class UnidbgActivity : AppCompatActivity() {

    companion object {
        // 静态变量 a
        var a: String? = null
    }

    // 非静态变量 b
    var b: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_unidbg)

        // 初始化静态变量 a 和 非静态变量 b
        a = "StaticA"
        b = "NonStaticB"
    }

    // Base64 方法
    fun base64(content: String): String {
        val combined = (a ?: "") + content + (b ?: "")
        return Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP)
    }
    
    // Native 方法 sign
    external fun sign(content: String): String
}

再实现一个 native 方法 sign 接受一个字符串 content,将 java 层 UnidbgActivity 中 静态变量 a + content + 非静态变量 b 拼接,再调用 java 层的 base64 方法传入拼接后的字符串得到加密串作为返回值。

cpp 源码如下:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_cyrus_example_unidbg_UnidbgActivity_sign(JNIEnv *env, jobject thiz, jstring content) {
    // 获取 content 字符串
    const char *contentChars = env->GetStringUTFChars(content, nullptr);

    // 获取静态变量 a 和 非静态变量 b
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID aField = env->GetStaticFieldID(clazz, "a", "Ljava/lang/String;");
    jfieldID bField = env->GetFieldID(clazz, "b", "Ljava/lang/String;");

    jstring a = (jstring) env->GetStaticObjectField(clazz, aField);
    jstring b = (jstring) env->GetObjectField(thiz, bField);

    // 将 a, content 和 b 拼接
    const char *aChars = env->GetStringUTFChars(a, nullptr);
    const char *bChars = env->GetStringUTFChars(b, nullptr);

    std::string combined = std::string(aChars) + contentChars + std::string(bChars);

    // 释放字符串
    env->ReleaseStringUTFChars(content, contentChars);
    env->ReleaseStringUTFChars(a, aChars);
    env->ReleaseStringUTFChars(b, bChars);

    // 调用 base64 方法
    jmethodID base64Method = env->GetMethodID(clazz, "base64", "(Ljava/lang/String;)Ljava/lang/String;");
    jstring combinedStr = env->NewStringUTF(combined.c_str());
    jstring base64Result = (jstring) env->CallObjectMethod(thiz, base64Method, combinedStr);

    return base64Result;
}

点击按钮 sign 调用 sign 方法加密字符串并 toast,编译运行得到 apk 文件。

word/media/image1.png

逆向分析 apk

把 apk 拖入 JEB , 经过反编译后可以找到 调用了 目标 jni 方法 sign

word/media/image2.png

JEB 下载地址:https://bbs.kanxue.com/thread-282148.htm

模拟目标 JNI 方法

把 apk 中目标 so 解压并放到 resources 目录下

通过 unidbg 加载 so 并调用 目标 jni 方法 sign,源码如下:

package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;

public class Demo2 extends AbstractJni {

    public static void main(String[] args) {
        Demo2 demo = new Demo2();
        demo.run();
        demo.destroy();
    }

    private void destroy() {
        IOUtils.close(emulator);
    }

    private final AndroidEmulator emulator;

    private Demo2() {
        // 创建一个 64 位的 Android 模拟器实例
        emulator = AndroidEmulatorBuilder
                .for64Bit()  // 设置为 64 位模拟
                .setProcessName("com.cyrus.example") // 进程名称
                .build();    // 创建模拟器实例

        // 获取模拟器的内存实例
        Memory memory = emulator.getMemory();

        // 创建一个库解析器,并设置 Android 版本为 23(Android 6.0)
        LibraryResolver resolver = new AndroidResolver(23);

        // 将库解析器设置到模拟器的内存中,确保加载库时能够解析符号
        memory.setLibraryResolver(resolver);

    }

    public void run(){
        // 加载共享库 libunidbg.so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        Module module = emulator.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/libunidbg.so"));

        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值