当前位置: 首页 > news >正文

逆向工具之 unidbg 执行 so

1、unidbg 入门

unidbg 是一款基于 unicorn 和 dynarmic 的逆向工具, 可以直接调用 Android 和 IOS 的 so 文件,无论是黑盒调用 so 层算法,还是白盒 trace 输出 so 层寄存器值变化都是一把利器~ 尤其是动态 trace 方面堪比 ida trace,给用不起 ida 的我带来极大安慰~!

项目地址:https://github.com/zhkl0228/unidbg

做脱机协议,首先要找到关键的加密代码,然而这些代码一般都在so里面,因为逆向c/c++的难度远比java大多了!找到关键代码后,一般情况下是逐行分析,然后自己写代码复现整个加密过程。但是,有些非标准的加密算法是由一个团队实现的,整个过程非常复杂。逆向人员再去逐行分析和复现,有点“不划算”!怎么才能直接调用so里面的这些关键代码了?可以通过前面的介绍的frida hook,也可以通过今天介绍的这个so的模拟框架--unidbg!官方的功能介绍如下:

  • Emulation of the JNI Invocation API so JNI_OnLoad can be called.
  • Support JavaVM, JNIEnv.
  • Emulation of syscalls instruction.
  • Support ARM32 and ARM64.
  • Inline hook, thanks to Dobby.
  • Android import hook, thanks to xHook.
  • iOS fishhook and substrate and whale hook.
  • unicorn backend support simple console debugger, gdb stub, instruction trace, memory read/write trace.
  • Support iOS objc and swift runtime.
  • Support dynarmic fast backend.
  • Support Apple M1 hypervisor, the fastest ARM64 backend.
  • Support Linux KVM backend with Raspberry Pi B4.

看着很多,有点唬人,实际并不复杂,以本文分享的为例:我们平时开发android app,在Android studio配置好各种环境和参数后是能直接在java层调用so层函数的。那么在unidbg,也能实现同样的功能:即调用so层的函数!这也是unidbg最核心的功能之一了!具体该怎么操作了? 步骤一:当然是先去unidbg的官网下载unidbg的框架啦,然后用intelij打开,里面能看到作者已经写好的各种java的测试工程代码,如下:

IDEA导入unidbg工程

:https://code.newban.cn/151.html

逆向工具之 unidbg(在 pc 端模拟执行 so 文件中的函数)

基本使用

  • 创建模拟器
  • 创建虚拟机
  • 加载so
  • 调用so层函数

简单罗列了一下基本的使用, 十分好上手~!

// 创建模拟器实例,建议使用实际进程名,可以规避进程名校验
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.xxx").build();
// 创建模拟器内存接口
final Memory memory = emulator.getMemory();
// 设置系统类库解析
memory.setLibraryResolver(new AndroidResolver(23));
// 创建 Android 虚拟机,传入 APK,unidbg 可以协助做一部分签名工作
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/xxx/xxx.apk"));
// 加载 so 到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/xxx/libxxx.so"),true);
// 获取 so 模块的句柄
module = dm.getModule();
// 设置 JNI 
vm.setJni(this);
// 打印日志
vm.setVerbose(true);
// 调用 JNI_Onload
dm.callJNI_OnLoad(emulator);

// 构建函数参数格式
List<Object> args = new ArrayList<>(10);
// 各种基本参数格式兼容
// 参数1:JNIEnv *env
args.add(vm.getJNIEnv());
// 参数2:jobject或jclass 用不到直接填0即可
// 创建 jobject, 如果没用到的话可以不写
// cNative = vm.resolveClass("com/xxx/xxx");
// DvmObject<?> cnative = cNative.newObject(null);
// args.add(cnative.hashCode());
args.add(0);
// 参数3 字符串对象
String input = "abcdef";
args.add(vm.addLocalObject(new StringObject(vm, input)));
// 参数4 bytes 数组
String input = "abcdef";
byte[] input_bytes = input.getBytes(StandardCharsets.UTF_8);
ByteArray input_byte_array = new ByteArray(vm,input_bytes);
args.add(vm.addLocalObject(input_byte_array));
// 参数5 bool 
// false 填 0,true 填 1
args.add(1);
// unidbg 主动调用函数
// 第二个参数是函数偏移量(thumb 记得+1)
// 第三个参数是参数列表
Number number = module.callFunction(emulator,0x10618, args.toArray());

// unicorn trace(贼好用!!!堪比 ida trace!!!)
String traceFile = "trace.txt";
PrintStream traceStream = null;
try{
    traceStream = new PrintStream(new FileOutputStream(traceFile), true);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
// 核心 trace 开启代码,也可以自己指定函数地址和偏移量
emulator.traceCode(module.base,module.base+module.size).setRedirect(traceStream);
// 获取最终返回值,同时运行过程中的汇编代码和寄存器值会写入到文件中
return vm.getObject(number.intValue()).getValue().toString();

输出结果:

看看 trace log,建议搭配 010 editor 使用, 更香

示例

​目录 unidbg-android/src/test/java 放置了很多示例,足以支撑入门

这些测试的 demo 代码已经说明了 unidbg 的接口和核心功能了。今天就用 kanxue 提供的 so 来说明核心 api 和 调用流程!

unidbg 调用so层函数 ( 普通的so方法、jni_onload调用、jni函数调用 )

1、选择执行引擎:如果明确使用了以下代码,那么unidbg使用dynarmic引擎,否则默认使用unicorn引擎!

static {
        DynarmicLoader.useDynarmic();
    }

2、创建虚拟机/模拟器,并执行虚拟机的类型是art还是dailvik:

AndroidARMEmulator emulator= new AndroidARMEmulator("com.sun.jna",null,null);
final Memory memory = emulator.getMemory();
VM vm = emulator.createDalvikVM();
vm.setVerbose(true);//这里如果是true,后续调用jni_onload的时候就能打印日志

3、指定SDK的版本,这里用23版本:

LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);

4、开始加载so库了:

Module unicorn08module=emulator.loadLibrary(new File("D:\\xxxx\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\unicorncourse08\\unicorn08.so"));

5、调用so层的导出函数:这两个都是导出函数,直接用符号名就行了;

Number result=unicorn08module.callFunction(emulator,"_Z3addii",1,2)[0];//导出函数直接用符号名就行了
System.out.println("_Z3addii result:"+result.intValue());
//_Z7add_sixiiiiii
result=unicorn08module.callFunction(emulator,"_Z7add_sixiiiiii",1,2,3,4,5,6)[0];
System.out.println("_Z7add_sixiiiiii result:"+result.intValue());

这个是打印的结果:

_Z3addii result:3
_Z7add_sixiiiiii result:21

6、这两个都是对字符串做操作的,so层仅仅求了传入字符串的长度:

MemoryBlock block1=memory.malloc(10,true);
UnidbgPointer str1_ptr=block1.getPointer();
str1_ptr.write("hello".getBytes());
String content=ARM.readCString(emulator.getBackend(),str1_ptr.peer);
System.out.println("_Z15getstringlengthPKc:"+str1_ptr.toString()+"---"+content);
result=unicorn08module.callFunction(emulator,"_Z15getstringlengthPKc",new PointerNumber(str1_ptr))[0];
Number r0value=emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R0);
System.out.println("_Z15getstringlengthPKc result:"+result.intValue()+"----"+r0value);

MemoryBlock block2=memory.malloc(10,true);
UnidbgPointer str2_ptr=block2.getPointer();
str2_ptr.write("666".getBytes());
String content2=ARM.readCString(emulator.getBackend(),str2_ptr.peer);
System.out.println("_Z16getstringlength2PKcS0_:"+str2_ptr.toString()+"---"+content2);
result=unicorn08module.callFunction(emulator,"_Z16getstringlength2PKcS0_",new PointerNumber(str1_ptr),new PointerNumber(str2_ptr))[0];
r0value=emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R0);
System.out.println("_Z16getstringlength2PKcS0_ result:"+result.intValue()+"----"+r0value);

打印结果:

_Z15getstringlengthPKc:RW@0x4016b000---hello
_Z15getstringlengthPKc result:5----5
_Z16getstringlength2PKcS0_:RW@0x4016c000---666
_Z16getstringlength2PKcS0_ result:8----8

7、这核心的核心:直接调用jni_onload

vm.callJNI_OnLoad(emulator,unicorn08module);

打印结果:这里可以看到分别在so的0x8c7、0xccb调用了FindClass和RegisterNative方法,然后注册MainActivity这个类的stringFromJNI2方法,该方法和so层中0xb35的方法是映射的!

JNIEnv->FindClass(com/example/unicorncourse08/MainActivity) was called from RX@0x40000c87[libnative-lib.so]0xc87
JNIEnv->RegisterNatives(com/example/unicorncourse08/MainActivity, unidbg@0xbffff778, 1) was called from RX@0x40000ccb[libnative-lib.so]0xccb
RegisterNative(com/example/unicorncourse08/MainActivity, stringFromJNI2(Ljava/lang/String;)Ljava/lang/String;, RX@0x40000b35[libnative-lib.so]0xb35)

去so的0xc87和0xccb查看,果然是FindClass和RegisterNative方法,unidbg 诚不我欺! 作为逆向,其实最重要的还是最后那个打印结果:java层的stringFromJNI2方法就是和so层的这个方法是映射的:

进入里面调用的各个函数仔细分析,发现这个函数还是比较简单:先接受传入的string,再打印出来!由于这个函数并未导出,但是和java层的函数做了映射,所以这里也可以直接通过java层的名字来直接调用,代码如下:

//调用jni函数,对于动态注册的jni函数必须在完成地址的绑定才能调用
System.out.println("stringFromJNI1-------------------------");
DvmClass MainActivity_dvmclass=vm.resolveClass("com/example/unicorncourse08/MainActivity");//先把类找到,这里的原理很像反射
DvmObject resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI1(Ljava/lang/String;)Ljava/lang/String;","helloworld");//再通过类去调用里面的函数
System.out.println("resultobj:"+resultobj);
resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI1(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellokanxue"));
System.out.println("resultobj:"+resultobj);
System.out.println("stringFromJNI1-------------------------");

//动态注册的jni函数stringFromJNI2
resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI2(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellostringFromJNI2"));
System.out.println("resultobj:"+resultobj);
System.out.println("stringFromJNI2-------------------------");


DvmObject mainactivity=MainActivity_dvmclass.newObject(null);
mainactivity.callJniMethodObject(emulator,"stringFromJNI2(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellostringFromJNI2"));
System.out.println("resultobj:"+resultobj);
System.out.println("stringFromJNI2-------------------------");

打印的结果如下:这里面除了我们显式调用println打印的日志,还有unidbg这个框架自身打印的日志(主要是JNIenv这个类的函数调用):

stringFromJNI1-------------------------
Find native function Java_com_example_unicorncourse08_MainActivity_stringFromJNI1(Ljava/lang/String;)Ljava/lang/String; => RX@0x40000a71[libnative-lib.so]0xa71
JNIEnv->GetStringUtfChars("helloworld") was called from RX@0x40000b03[libnative-lib.so]0xb03
JNIEnv->NewStringUTF("helloworld") was called from RX@0x40000b2f[libnative-lib.so]0xb2f
resultobj:"helloworld"
Find native function Java_com_example_unicorncourse08_MainActivity_stringFromJNI1(Ljava/lang/String;)Ljava/lang/String; => RX@0x40000a71[libnative-lib.so]0xa71
JNIEnv->GetStringUtfChars("hellokanxue") was called from RX@0x40000b03[libnative-lib.so]0xb03
[main]I/stringFromJNI1: content:helloworld,length:10
[main]I/stringFromJNI1: content:hellokanxue,length:11
[main]I/stringFromJNI2: content:hellostringFromJNI2,length:19
[main]I/stringFromJNI2: content:hellostringFromJNI2,length:19
JNIEnv->NewStringUTF("hellokanxue") was called from RX@0x40000b2f[libnative-lib.so]0xb2f
resultobj:"hellokanxue"
stringFromJNI1-------------------------
Find native function Java_com_example_unicorncourse08_MainActivity_stringFromJNI2(Ljava/lang/String;)Ljava/lang/String; => RX@0x40000b35[libnative-lib.so]0xb35
JNIEnv->GetStringUtfChars("hellostringFromJNI2") was called from RX@0x40000b03[libnative-lib.so]0xb03
JNIEnv->NewStringUTF("hellostringFromJNI2") was called from RX@0x40000b2f[libnative-lib.so]0xb2f
resultobj:"hellostringFromJNI2"
stringFromJNI2-------------------------
Find native function Java_com_example_unicorncourse08_MainActivity_stringFromJNI2(Ljava/lang/String;)Ljava/lang/String; => RX@0x40000b35[libnative-lib.so]0xb35
JNIEnv->GetStringUtfChars("hellostringFromJNI2") was called from RX@0x40000b03[libnative-lib.so]0xb03
JNIEnv->NewStringUTF("hellostringFromJNI2") was called from RX@0x40000b2f[libnative-lib.so]0xb2f
resultobj:"hellostringFromJNI2"
stringFromJNI2-------------------------

完整的代码:

package com.unicorncourse08;

import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.PointerNumber;
import com.github.unidbg.arm.ARM;
import com.github.unidbg.arm.backend.dynarmic.DynarmicLoader;
import com.github.unidbg.linux.android.AndroidARMEmulator;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.MemoryBlock;
import com.github.unidbg.pointer.UnidbgPointer;
import unicorn.ArmConst;
import java.io.File;

public class MainActivity {
    static {
        DynarmicLoader.useDynarmic();
    }
    public static void main(String[] args) {
        AndroidARMEmulator emulator= new AndroidARMEmulator("com.sun.jna",null,null);
        final Memory memory = emulator.getMemory();

        VM vm = emulator.createDalvikVM();
        vm.setVerbose(true);

        LibraryResolver resolver = new AndroidResolver(23);
        memory.setLibraryResolver(resolver);
        Module unicorn08module=emulator.loadLibrary(new File("D:\\xxxx\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\unicorncourse08\\unicorn08.so"));
        Number result=unicorn08module.callFunction(emulator,"_Z3addii",1,2)[0];//导出函数直接用符号名就行了
        System.out.println("_Z3addii result:"+result.intValue());

        //_Z7add_sixiiiiii
        result=unicorn08module.callFunction(emulator,"_Z7add_sixiiiiii",1,2,3,4,5,6)[0];
        System.out.println("_Z7add_sixiiiiii result:"+result.intValue());

        //_Z15getstringlengthPKc
        MemoryBlock block1=memory.malloc(10,true);
        UnidbgPointer str1_ptr=block1.getPointer();
        str1_ptr.write("hello".getBytes());
        String content=ARM.readCString(emulator.getBackend(),str1_ptr.peer);
        System.out.println("_Z15getstringlengthPKc:"+str1_ptr.toString()+"---"+content);
        result=unicorn08module.callFunction(emulator,"_Z15getstringlengthPKc",new PointerNumber(str1_ptr))[0];
        Number r0value=emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R0);
        System.out.println("_Z15getstringlengthPKc result:"+result.intValue()+"----"+r0value);

        MemoryBlock block2=memory.malloc(10,true);
        UnidbgPointer str2_ptr=block2.getPointer();
        str2_ptr.write("666".getBytes());
        String content2=ARM.readCString(emulator.getBackend(),str2_ptr.peer);
        System.out.println("_Z16getstringlength2PKcS0_:"+str2_ptr.toString()+"---"+content2);
        result=unicorn08module.callFunction(emulator,"_Z16getstringlength2PKcS0_",new PointerNumber(str1_ptr),new PointerNumber(str2_ptr))[0];
        r0value=emulator.getBackend().reg_read(ArmConst.UC_ARM_REG_R0);
        System.out.println("_Z16getstringlength2PKcS0_ result:"+result.intValue()+"----"+r0value);


        //调用jni_OnLoad函数
        vm.callJNI_OnLoad(emulator,unicorn08module);

        //调用jni函数,对于动态注册的jni函数必须在完成地址的绑定才能调用
        System.out.println("stringFromJNI1-------------------------");
        DvmClass MainActivity_dvmclass=vm.resolveClass("com/example/unicorncourse08/MainActivity");//先把类找到,这里的原理很像反射
        DvmObject resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI1(Ljava/lang/String;)Ljava/lang/String;","helloworld");//再通过类去调用里面的函数
        System.out.println("resultobj:"+resultobj);
        resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI1(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellokanxue"));
        System.out.println("resultobj:"+resultobj);
        System.out.println("stringFromJNI1-------------------------");

        //动态注册的jni函数stringFromJNI2
        resultobj=MainActivity_dvmclass.callStaticJniMethodObject(emulator,"stringFromJNI2(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellostringFromJNI2"));
        System.out.println("resultobj:"+resultobj);
        System.out.println("stringFromJNI2-------------------------");


        DvmObject mainactivity=MainActivity_dvmclass.newObject(null);
        mainactivity.callJniMethodObject(emulator,"stringFromJNI2(Ljava/lang/String;)Ljava/lang/String;",new StringObject(vm, "hellostringFromJNI2"));
        System.out.println("resultobj:"+resultobj);
        System.out.println("stringFromJNI2-------------------------");

    }
}

总结一下,上述API包括了3种so函数的调用方法:

  • 普通的so方法
  • jni_onload调用
  • jni 函数调用

上面举例的这些内容相对简单,并不涉及到so层调用java层的函数。如果遇到so层函数调用java层函数怎么办么?我们如果自己在apk写java代码调用so层函数,遇到so通过反射调用java层函数时,需要自己补上java层对应的类、方法和变量,因为这些需要执行的代码是绕不过去的!unidbg是这么样的么? 答案是肯定的!

so 通过反射调用 java 层 ( 补环境 )

比如下面的这个so层的方法,会在jni_onload中被调用;这里需要获取java层普通变量、static变量后打印出来;也会获取java层的普通方法然后调用,这该怎么办了?

上面说了:so层调用java层的代码肯定是要补齐的(如果直接简单粗暴改so层代码,可能导致别人原来的逻辑错误)! 这里该怎么实操了? 大概的思路是:

  • 自己补上缺失的方法(当然java层的方法可以用jadx、jeb等反编译得到,不用自己反编译smali),这里缺失了base64方法,需要补上!
  • 自己补上缺失的变量,方法同上!
  • 重写getStaticObjectField、getObjectField、callObjectMethodV等方法,然后检测传入的参数。一旦发现使用/调用的是java层变量、方法,用自己补上的哪些代码替换(原理像不像平时常用的hook?)

说了那么多,完整的demo代码如下:

package com.unicorncourse08;

import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidARMEmulator;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.jni.ProxyClassFactory;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.virtualmodule.android.JniGraphics;
import org.apache.commons.codec.binary.Base64;
import sun.applet.Main;

import java.io.File;
import java.lang.reflect.Field;

public class MainActivitymethod1 extends AbstractJni {

    private static DvmClass MainActivityClass;

    @Override
    /*
    * staticcontent是java层的静态变量;getStaticObjectField,一旦检测到so层引用这个变量,那么自己返回这个变量的值
    * */
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        System.out.println("getStaticObjectField->"+signature);
        if(signature.equals("com/example/testjni/MainActivity->staticcontent:Ljava/lang/String;")){
            return new StringObject(vm,"staticcontent");//源码 public static string staticcontent = "staticcontent"
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    /*
    * objcontent是java层的变量;这里重写getObjectField方法,一旦检测到so层引用这个变量,那么自己返回这个变量的值
    * */
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        System.out.println("getObjectField->"+signature);
        if(signature.equals("com/example/testjni/MainActivity->objcontent:Ljava/lang/String;")){
            return new StringObject(vm,"objcontent");//public string objcontent
        }
        return super.getObjectField(vm, dvmObject, signature);
    }
    /*
    * java层的方法,这里需要复现,否则不知道怎么执行
    * */
    public String base64(String arg3) {
        String result=Base64.encodeBase64String(arg3.getBytes());
        return result;
    }

    @Override
    /*
    * base64是java层的方法,这里重写callObjectMethodV方法:一旦发现调用的是java层的base64方法,这里就用自己复现的base64方法替换
    * */
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        System.out.println("callObjectMethodV->"+signature);
        if(signature.equals("com/example/testjni/MainActivity->base64(Ljava/lang/String;)Ljava/lang/String;")){
            DvmObject dvmobj=vaList.getObjectArg(0);
            String arg= (String) dvmobj.getValue();
            String result=base64(arg);
            return new StringObject(vm,result);
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    public static void main(String[] args) {
        MainActivitymethod1 mainActivitymethod1=new MainActivitymethod1();
        AndroidARMEmulator emulator = new AndroidARMEmulator("org.telegram.messenger",null,null);
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        VM vm = emulator.createDalvikVM();
        vm.setVerbose(true);
        vm.setJni(mainActivitymethod1);
        DalvikModule dm = vm.loadLibrary(new File("D:\\xxxx\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\unicorncourse08\\calljava.so"), true);
        dm.callJNI_OnLoad(emulator);
        MainActivityClass=vm.resolveClass("com/example/testjni/MainActivity");
        DvmObject obj=MainActivityClass.newObject(null);
        obj.callJniMethodObject(emulator,"base64byjni(Ljava/lang/String;)Ljava/lang/String;","callbase64byjni");
  }
}

整个逻辑其实并不复杂,从main函数开始:创建模拟器、创建虚拟机、加载so、调用so层函数!打印的结果如下:

JNIEnv->FindClass(com/example/testjni/MainActivity) was called from RX@0x400009df[libnative-lib.so]0x9df
JNIEnv->RegisterNatives(com/example/testjni/MainActivity, unidbg@0xbffff768, 1) was called from RX@0x40000f19[libnative-lib.so]0xf19
RegisterNative(com/example/testjni/MainActivity, stringFromJNI(Ljava/lang/String;)Ljava/lang/String;, RX@0x40000cb1[libnative-lib.so]0xcb1)
Find native function Java_com_example_testjni_MainActivity_base64byjni(Ljava/lang/String;)Ljava/lang/String; => RX@0x4000088d[libnative-lib.so]0x88d
JNIEnv->FindClass(com/example/testjni/MainActivity) was called from RX@0x400009df[libnative-lib.so]0x9df
getStaticObjectField->com/example/testjni/MainActivity->staticcontent:Ljava/lang/String;
JNIEnv->GetStaticObjectField(class com/example/testjni/MainActivity, staticcontent Ljava/lang/String; => "staticcontent") was called from RX@0x40000aa5[libnative-lib.so]0xaa5
JNIEnv->GetStringUtfChars("staticcontent") was called from RX@0x40000adb[libnative-lib.so]0xadb
[main]I/stringFromJNI: staticcontent:staticcontent
[main]I/stringFromJNI: objcontent:objcontent
getObjectField->com/example/testjni/MainActivity->objcontent:Ljava/lang/String;
JNIEnv->GetObjectField(com.example.testjni.MainActivity@7b3300e5, objcontent Ljava/lang/String; => "objcontent") was called from RX@0x40000b11[libnative-lib.so]0xb11
JNIEnv->GetStringUtfChars("objcontent") was called from RX@0x40000adb[libnative-lib.so]0xadb
JNIEnv->GetMethodID(com/example/testjni/MainActivity.base64(Ljava/lang/String;)Ljava/lang/String;) was called from RX@0x40000b55[libnative-lib.so]0xb55
callObjectMethodV->com/example/testjni/MainActivity->base64(Ljava/lang/String;)Ljava/lang/String;
JNIEnv->CallObjectMethodV(com.example.testjni.MainActivity@7b3300e5, base64("callbase64byjni") => "Y2FsbGJhc2U2NGJ5am5p") was called from RX@0x40000bb1[libnative-lib.so]0xbb1
JNIEnv->GetStringUtfChars("Y2FsbGJhc2U2NGJ5am5p") was called from RX@0x40000adb[libnative-lib.so]0xadb
JNIEnv->NewStringUTF("Y2FsbGJhc2U2NGJ5am5p") was called from RX@0x40000c05[libnative-lib.so]0xc05
[main]I/stringFromJNI: base64result:Y2FsbGJhc2U2NGJ5am5p

大杀器Unidbg真正的威力

:https://zhuanlan.zhihu.com/p/419170736

大杀器内置的HOOK框架

当然Unidbg还内置了多种HOOK框架,今天讲一个分析So比较实用的一款HookZz

// 1. 获取HookZz对象
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz,支持inline hook,文档看https://github.com/jmpews/HookZz
// 2. enable hook
hookZz.enable_arm_arm64_b_branch(); // 测试enable_arm_arm64_b_branch,可有可无
index = 0;
 
hookZz.replace(module.findSymbolByName("lrand48"), new ReplaceCallback() {
    @Override
    public void postCall(Emulator<?> emulator, HookContext context) {
 
            ((EditableArm32RegisterContext)context).setR0(0x12345678);
 
        int ptrace_args0 = context.getIntArg(0);
        System.out.println("lrand48=" + ptrace_args0);
 
    }
 
 
},true);
 
 
//aesdecode hook
hookZz.wrap((module.base)+0x39634+1, new WrapCallback<RegisterContext>() { // inline wrap导出函数
    UnidbgPointer addr = null;
 
    @Override
    // 4. 方法执行前
    public void preCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
        addr= ctx.getPointerArg(0);
        UnidbgPointer pointerArg = ctx.getPointerArg(1);
        UnidbgPointer pointer = pointerArg.getPointer(12);
        int anInt = pointerArg.getInt(8);
        byte[] byteArray = pointer.getByteArray(0, anInt);
        String s =xuzi1(byteArray);
        System.out.println("aes aesdecode= " + s);
 
    }
 
    @Override
    // 5. 方法执行后
    public void postCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
 
        byte[] aaaa = addr.getPointer(0).getPointer(12).getByteArray(0,0x30);
        String s =xuzi1(aaaa);
        System.out.println("aes aesdecode1= " + s);
    }
 
});

同理,此框架也支持导出函数HOOK以及InlineHOOK 有了这个方法,在你分析一些函数的时候,可以充当Log的效果或者强行改变一些函数的返回值让你更容易的分析,比如本例中笔者改变了Lrand48的返回值,让函数每次都强行返回0x12345678,这样在逆向分析的时候能让一些不确定性变成可控性。

拟执行某段子so实操教程

:https://blog.csdn.net/fenfei331/article/details/117027797

github 上 unidbg 项目

:https://github.com/search?q=unidbg

unidbgweb

:https://github.com/zhaoboy9692/unidbgweb

unidbg的服务化,毒、酷安、快手、小红书、马蜂窝、抖音、今日头条、美团、拼多多、启信宝、天眼查、封面新闻的相关so调用

unidbg_api

脱离安卓手机调用第三方.so文件,集成了spring boot框架提供web服务 可打成jar包一键部署

unidbg-local-server

:https://github.com/gl953236368/uls

  • 小红书
  • 最右
  • 拼多多
  • 携程
  • bilibili
  • 轻小说
  • 美团
  • ......
  • 懂车帝

相关文章:

逆向工具之 unidbg 执行 so

1、unidbg 入门 unidbg 是一款基于 unicorn 和 dynarmic 的逆向工具&#xff0c; 可以直接调用 Android 和 IOS 的 so 文件&#xff0c;无论是黑盒调用 so 层算法&#xff0c;还是白盒 trace 输出 so 层寄存器值变化都是一把利器&#xff5e; 尤其是动态 trace 方面堪比 ida tr…...

zk-STARK/zk-SNARK中IP,PCP,IPCP,IOP,PIOP,LIP,LPCP模型介绍

我们的目标是构造 zkSNARK。在我们的目标场景中&#xff0c;Prover 只需要发送一个简短的证明字符串给 Verifier&#xff0c;而 Verifier 不需要给 Prover 发送任何消息。 直接构造一个满足这个场景的 zkSNARK 可能会很困难。一个更灵活的方式是在先在理想模型下构造证明系统&…...

StreamAPI

StreamAPI 最近开发用上了 Java8的StreamAPI,(咋现在才用?嗯哼,项目需要)自己也不怎么会,来总结一波吧! 别认为好抽象!!!干他就完事 一.StreamAPI介绍 就是用来处理集合的数据 其实到后面会发现和SQL的语句是差不多的~哈哈?你不信?往下面看 Stream:英文翻译叫做流 举个粟子…...

MySQl高可用集群搭建(MGR + ProxySQL + Keepalived)

前言 服务器规划&#xff08;CentOS7.x&#xff09; IP地址主机名部署角色192.168.x.101mysql01mysql192.168.x.102mysql02mysql192.168.x.103mysql03mysql192.168.x.104proxysql01proxysql、keepalived192.168.x.105proxysql02proxysql、keepalived 将安装包 mysql_cluster_…...

java+Selenium+TestNg搭建自动化测试架构(3)实现POM(page+Object+modal)

1.Page Object是Selenium自动化测试项目开发实践的最佳设计模式之一&#xff0c;通过对界面元素的封装减少冗余代码&#xff0c;同时在后期维护中&#xff0c;若元素定位发生变化&#xff0c;只需要调整页面元素封装的代码&#xff0c;提高测试用例的可维护性。 PageObject设计…...

oracle11g忘记system密码,重置密码

OPW-00001: 无法打开口令文件 cmd.exe 使用管理员身份登录 找到xxx\product\11.2.0\dbhome_1\database\PWDorcl.ora文件&#xff0c;删除 执行orapwd fileD:\app\product\11.2.0\dbhome_1\database\PWDorcl.ora passwordtiger (orapwd 在\product\11.2.0\dbhome_1\BIN目录下…...

黑马 Vue 快速入门 笔记

黑马 Vue 快速入门 笔记0 VUE相关了解0.1 概述0.2 MVVM0.3 JavaScript框架0.4 七大属性0.5 el:挂载点1 VUE基础1.0 第一个vue代码&#xff1a;Hello&#xff0c;vue1.1 v-bind 设置元素的属性 简写 &#xff1a;1.2 v-if &#xff0c; v-else &#xff0c; v-else-ifv-if , v-e…...

HTTP协议知识体系核心重点梳理

HTTP协议知识体系核心重点梳理TCP/IP协议1.四层模型2.通信过程3.tcp三次握手和四次挥手4.tcp安全传输4. 一次HTTP通信流程HTTP协议HTTP/1.1CookieHttp报文格式内容编码分块传输编码HTTP状态码重定向状态码常用的通用首部cache-controlExpiresConnectionTransfer-Encoding常用的…...

Nginx优化与防盗链

Nginx优化与防盗链 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4c…...

自动驾驶路径规划概况

文章目录前言介绍1. 路径规划在自动驾驶系统架构中的位置2. 全局路径规划的分类2.1 基础图搜索算法2.1.1 Dijkstra算法2.1.2 双向搜索算法2.1.3 Floyd算法2.2 启发式算法2.2.1 A*算法2.2.2 D*算法2.3 基于概率采样的算法2.3.1 概率路线图&#xff08;PRM&#xff09;2.3.2 快速…...

某某银行行面试题目汇总--HashMap为什么要扩容

一、HashMap啥时候扩容&#xff0c;为什么扩容&#xff1f; HashMap的默认大小是16。在实际开发过程中&#xff0c;我们需要去存储的数据量往往是大于存储容器的默认大小的。所以&#xff0c;出现容量默认大小不能满足需求时&#xff0c;就需要扩容。而这个扩容的动作是由集合自…...

求职者:“我有五年测试经验”面试官: “不,你只是把一年的工作经验用了五年”

最近看到很多软件测试由于公司裁员而需要重新求职的。他们普遍具有4年甚至更长的工作经验。但求职结果往往都不太理想。 我在与部分软件测试求职者交谈的过程中发现&#xff0c;很多人的工作思路不清晰&#xff0c;技能不扎实&#xff0c;没有持续学习的习惯&#xff0c;但对于…...

Nacos配置中心

什么是配置中心所谓配置中心:在微服务的环境下,将项目需要的配置信息保存在配置中心,需要读取时直接从配置中心读取,方便配置管理的微服务工具我们可以将部分yml文件的内容保存在配置中心一个微服务项目有很多子模块,这些子模块可能在不同的服务器上,如果有一些统一的修改,我们…...

【故障】6、yum不可用

文章目录[toc]一、yum命令不能使用1&#xff09;报错2&#xff09;问题分析3&#xff09;完全删除python及yum重新安装1、删除python2、删除yum3、下载Python依赖rpm包4、下载yum依赖rpm包5、强制安装python6、强制安装yum7、测试一、yum命令不能使用 1&#xff09;报错 Ther…...

深度解读 | 数据资产管理面临诸多挑战,做好这5个措施是关键

日前&#xff0c;大数据技术标准推进委员会&#xff08;中国通信标准化协会下&#xff08;CCSA&#xff09;的专业技术委员会&#xff0c;简称TC601&#xff09;发布《数据资产管理实践白皮书》&#xff08;6.0 版&#xff09;&#xff08;以下简称&#xff1a;报告&#xff09…...

双检测人脸防伪识别方法(活体检测+人脸识别+关键点检测+人像分割)

双检测人脸防伪识别=人脸检测+活体检测+人脸识别 1.人脸关键点+语义分割 使用mediapipe进行视频人脸关键点检测和人像分割: import time import cv2 import mediapipe as mp import numpy as npmp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solution…...

2023年3月 - 笔记

内容已复习 采用下划线标识内容已重写 并补充优化 新建文章并添加超链接 背景颜色 绿色 Python 2023年3月1日 Python 把列表转成元组 # 1、Python 把列表转成元组 使用tuple 即可 list_a [1, 2, 3, 4, 5, 6] list_b tuple(list_a) print(list_b)# 2、如果想把 元组转成列…...

浅谈Redisson实现分布式锁对原理

1.Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一&#xff0c;而 Java 是世界上最流行&#xff08;注意&#xff0c;我没有说“最好”&#xff09;的编程语言之一。虽然两者看起来很自然地在一起“工作”&#xff0c;但是要知道&#xff0c;Redis 其实并没有对 Java…...

struts1.2升级struts2.5.30问题汇总

严重: 配置应用程序监听器[org.apache.struts2.tiles.StrutsTilesListener]错误java.lang.NoClassDefFoundError: org/apache/tiles/web/startup/AbstractTilesListenerat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader…...

电动汽车充放电的优化调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

《JeecgBoot系列》 如何设计表单实现“下拉组件二级联动“ ? 以省市二级联动为例

《JeecgBoot系列》 如何设计表单实现"下拉组件二级联动" ? 以省市二级联动为例 一、准备字典表 1.1 创建字典表 CREATE TABLE sys_link_table ( id int NULL, pid int NULL, name varchar(64) null );1.2 准备数据 idpidname1全国21浙江省32杭州市42宁波市51江苏…...

数学小课堂:数学的线索(从猜想到定理再到应用的整个过程)

文章目录 引言I 勾股定理1.1 勾三股四弦五1.2 数学和自然科学的三个本质差别1.3 总结引言 从猜想到定理再到应用的整个过程是数学发展和体系构建常常经历的步骤。 I 勾股定理 勾股定理: 直角三角形两条直角边的平方之和等于斜边的平方,这个定理在国外都被称为毕达哥拉斯定理…...

Collecting package metadata (current_repodata.json): failed

一、问题描述 安装anaconda之后&#xff0c;想创建环境&#xff0c;用了下面这段代码&#xff1a; conda create -n pytorch python3.7 conda创建环境报错了&#xff0c;报了如下这一堆&#xff1a; Collecting package metadata (current_repodata.json): failedUnavailab…...

几十亿工单表,查询优化案例

前言: 之前在某大型保险公司担任技术经理&#xff0c;负责优化话务系统模块&#xff0c;由于系统已经运行10年之久&#xff0c;尤其在话务系统中&#xff0c;沉积了几十亿的话务信息表&#xff0c;业务人员反馈&#xff0c;话务系统历史数据查询部分已经完全查询不动&#xff0…...

LabVIEW应用程序(EXE)无法正确动态调用插件

LabVIEW应用程序&#xff08;EXE&#xff09;无法正确动态调用插件正在构建一个应用程序并使用插件架构&#xff0c;以便可以动态调用将来创建的VI&#xff08;插件&#xff09;。应用程序在LabVIEW开发环境中可以正常运行&#xff0c;但不能作为可执行程序运行。运行可执行文件…...

到了35岁,软件测试职业发展之困惑如何解?

35岁&#xff0c;从工作时间看&#xff0c;工作超过10年&#xff0c;过了7年之痒&#xff0c;多数IT人都已经跳槽几次。 35岁&#xff0c;发展比较好的软件测试人&#xff0c;已经在管理岗位&#xff08;测试经理甚至测试总监&#xff09;或已经成为测试专家或测试架构师。发展…...

Google Guice 3:Bindings(1)

1. 序言 上一篇博客&#xff0c;《Google Guice 2&#xff1a;Mental Model》&#xff0c;讲述了Guice的建模思路&#xff1a;Guice is a map Guice官网认为&#xff1a;binding是一个对象&#xff0c;它对应Guice map中的一个entry&#xff0c;通过创建binding就可以向Guice …...

学习国家颁布的三部信息安全领域法律,理解当前工作中的信息安全合规要求

目录三部信息安全领域的法律文件三部法律的角色定位与联系三部法律的适用范围三部法律的主要履职部门三部法律条文章节结构中的共性三部法律中的一些次重点章节网络安全法的重点章节数据安全法的重点章节个人信息保护法的重点章节关于工业和信息化部行政执法项目清单三部信息安…...

LeetCode_Python_二分查找算法

二分查找算法要求二分查找过程如何更新左右边界实例type1&#xff1a;常规记录中间元素type2&#xff1a;取跳出循环后的左或右边界算法要求 顺序存储结构元素大小有序 二分查找过程 将元素排序&#xff1b;将中间位置记录的这个元素与目标元素比较&#xff1b; 2.1 如果相同&a…...

功能测试三年,是时候做出改变了

前言 测试行业3年多经验&#xff0c;学历大专自考本科&#xff0c;主要测试方向web&#xff0c;PC端&#xff0c;wap站&#xff0c;小程序公众号都测试过&#xff0c;app也测过一些&#xff0c;C端B端都有&#xff0c;除功能外&#xff0c;接口性能也有涉猎&#xff0c;但是不…...