版权归作者所有,如有转发,请注明文章出处: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 文件。

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

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

最低0.47元/天 解锁文章
900

被折叠的 条评论
为什么被折叠?



