逆向工具之 unidbg 执行 so

news2024/11/30 0:40:49

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
  • 轻小说
  • 美团
  • ......
  • 懂车帝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础机器学习做游戏辅助第十四课--原神自动钓鱼(四)yolov5目标检测

一、yolo介绍 目标检测有两种实现,一种是one-stage,另一种是two-stage,它们的区别如名称所体现的,two-stage有一个region proposal过程,可以理解为网络会先生成目标候选区域,然后把所有的区域放进分类器分类,而one-stage会先把图片分割成一个个的image patch,然后每个im…

关于SqlServer高并发死锁现象的分析排查

问题描述 通过定期对生产环境SqlServer日志的梳理&#xff0c;发现经常会出现类似事务与另一个进程被死锁在资源上&#xff0c;并且已被选作死锁牺牲品&#xff0c;请重新运行该事务的异常&#xff0c;简单分析一下原因&#xff1a;在高并发场境下&#xff0c;多个事务同时对某…

Ubuntu 使用Nohup 部署/启动/关闭程序

目录 一、什么是nohup&#xff1f; 二、nohup能做什么&#xff1f; 三、nohup如何使用&#xff1f; 四、怎么查看/关闭使用nohup运行的程序&#xff1f; 命令 实例 一、什么是nohup&#xff1f; nohup 命令运行由 Command参数和任何相关的 Arg参数指定的命令&#xff0c…

【微信小程序】--WXML WXSS JS 逻辑交互介绍(四)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

Kotlin 34. recyclerView 案例:显示列表

Kotlin 案例1. recyclerView&#xff1a;显示列表 这里&#xff0c;我们将通过几个案例来介绍如何使用recyclerView。RecyclerView 是 ListView 的高级版本。 当我们有很长的项目列表需要显示的时候&#xff0c;我们就可以使用 RecyclerView。 它具有重用其视图的能力。 在 Re…

【C语言】-程序编译的环境和预处理详解-让你轻松理解程序是怎么运行的!!

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 程序的编译前言一、 程序的翻译环境和执行环境二、 详解翻译环境2.1编译环境2.1.1预编…

代码随想录算法训练营第七天 | 454.四数相加II 、 383. 赎金信、15. 三数之和、18. 四数之和 、总结

打卡第七天&#xff0c;还是哈希表。 今日任务 454.四数相加II383.赎金信15.三数之和18.四数之和总结 454.四数相加II 代码随想录 class Solution { public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, ve…

单元测试面试秘籍分享

1. 什么是单元测试 “在计算机编程中&#xff0c;单元测试又称为模块测试&#xff0c;是针对程序模块来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中&#xff0c;一个单元就是单个程序、函数、过程等&#xff1b;对于面向对象编程&#xff0c;最…

j-vxe-table 下拉搜索选择框数据加载过多导致前端崩溃问题

Jeeg-boot j-vxe-table 下拉搜索选择框数据加载过多导致前端崩溃问题 最近用到了Jeeg-boot j-vxe-table的组件&#xff0c;这组件时真J8难用&#xff0c;还好多BUG&#xff0c;想用个slot插槽也用不了&#xff0c;好像官方写了个基础就没怎么管了。&#x1f611; 问题&#xf…

google hacker语句

哎&#xff0c;我就是沾边&#xff0c;就是不打实战(&#xffe3;o&#xffe3;) . z Z 文章目录前言一、什么是谷歌Docker&#xff1f;二、受欢迎的谷歌docker语句谷歌docker的例子日志文件易受攻击的 Web 服务器打开 FTP 服务器SSH私钥电子邮件列表实时摄像机MP3、电影和 PDF…

php调试配置

错误信息输出 错误日志 nginx把对php的请求发给php-fpm fastcgi进程来处理&#xff0c;默认的php-fpm只会输出php-fpm的错误信息&#xff0c;在php-fpm的errors log里也看不到php的errorlog。原因是php-fpm的配置文件php-fpm.conf中默认是关闭worker进程的错误输出&#xff0…

【MySQL进阶】 锁

&#x1f60a;&#x1f60a;作者简介&#x1f60a;&#x1f60a; &#xff1a; 大家好&#xff0c;我是南瓜籽&#xff0c;一个在校大二学生&#xff0c;我将会持续分享Java相关知识。 &#x1f389;&#x1f389;个人主页&#x1f389;&#x1f389; &#xff1a; 南瓜籽的主页…

Mybatis源码学习笔记(四)之Mybatis执行增删改查方法的流程解析

1 Mybatis流程解析概述 Mybatis框架在执行增伤改的流程基本相同&#xff0c; 很简单&#xff0c;这个大家只要自己写个测试demo跟一下源码,基本就能明白是怎么回事&#xff0c;查询操作略有不同&#xff0c; 这里主要通过查询操作来解析一下整个框架的流程设计实现。 2 Mybat…

【python】argparse 模块的使用、Pycharm中使用argparse

目录1、简介2、使用步骤1&#xff09;导入argparse模块&#xff0c;并创建解释器2&#xff09;添加所需参数3&#xff09;解析参数3、使用 pycharm 传递参数给 argparse1、简介 argparse 模块是 Python 标准库中提供的一个命令行解析模块&#xff0c;它可以让使用者以类似 Uni…

给安全平台编写插件模块的思路分享

一、背景 最近在GitHub看到一个新的开源安全工具&#xff0c;可以把工具都集成到一个平台里&#xff0c;觉得挺有意思&#xff0c;但是平台现有的工具不是太全&#xff0c;我想把自己的工具也集成进去&#xff0c;所以研究了一番 蜻蜓安全工作台是一个安全工具集成平台&#x…

我的零分周赛:CSDN周赛第30期,成绩“0”分,天然气定单、小艺读书、买苹果、圆桌

CSDN周赛第30期&#xff0c;成绩“0”分&#xff0c;天然气定单、小艺读书、买苹果&#x1f34e;、圆桌。 (本文获得CSDN质量评分【91】)【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教…

steam搬砖项目,小投入高回报,可放大操作,(内附教学资料)

我必须要说&#xff0c;steam搬砖项目就是全网门槛最低的副业&#xff0c;有手就行&#xff01; 本人90后底层员工一枚&#xff0c;新入csgo搬砖项目&#xff0c;轻松翻身 什么做抖音、海外问卷、直播卖货&#xff0c;电商等等对比我这个都是小钱。我这个方法是利用了大部分人…

C++线程/阻塞/同步异步----2

本章节内容为记录改写RTK代码时&#xff0c;学习的知识 同步和异步区别 1.定义不同&#xff1a;同步需要将通信双方的时钟统一到一个频率上&#xff0c;异步通信发送的字符间隔时间可以是任意的; 2.准确性不同&#xff1a;同步通信需要比较高精度的精确度&#xff0c;异步则不…

【算法基础】栈与队列

一、栈1.1 模拟栈实现一个栈&#xff0c;栈初始为空&#xff0c;支持四种操作&#xff1a;push x – 向栈顶插入一个数 x&#xff1b;pop – 从栈顶弹出一个数&#xff1b;empty – 判断栈是否为空&#xff1b;query – 查询栈顶元素。现在要对栈进行 M 个操作&#xff0c;其中…

【2023最火教程】Python性能测试框架Locust实战教程(建议收藏)

01、认识Locust Locust是一个比较容易上手的分布式用户负载测试工具。它旨在对网站&#xff08;或其他系统&#xff09;进行负载测试&#xff0c;并确定系统可以处理多少个并发用户&#xff0c;Locust 在英文中是 蝗虫 的意思&#xff1a;作者的想法是在测试期间&#xff0c;放…