文章目录

  • 1.unidbg的介绍
  • 2.unidbg的安装
    • 2.1.下载unidbg工具
    • 2.2.导入IDEA
    • 2.3.验证导入是否成功
  • 3.unidbg的使用
    • 3.1.目标方法静态分析
    • 3.2.模拟执行目标方法
    • 3.3.算法分析
      • 3.3.1.OLLVM去混淆
      • 3.3.2.指令级Hook辅助分析
      • 3.3.3.unidbg寄存器trace分析
  • 4.补充知识
    • 4.1.JNIEnv、jobject、jclass
      • 4.1.2.JNIEnv指针是什么东西?
      • 4.1.3.jobject和jclass又有什么区别?

1.unidbg的介绍

unidbg是一款基于unicorn的用来模拟执行二进制程序的逆向工具,可以让安全研究人员直接在PC上运行android或ios的动态库文件并调用其中的方法,来看下该工具支持的功能。
支持功能如下:

  • 支持模拟执行JNI调用API,以便可以调用JNI_OnLoad
  • 支持JavaVM,JNIEnv
  • 支持模拟syscalls调用
  • 支持ARM32和ARM64
  • 基于HookZz实现的inline hook
  • 基于xHook实现的import hook
  • 支持iOS fishhook、substrate、whale hook
  • 支持简单的控制台调试器、gdb、IDA、android调试器服务器、指令跟踪、内存读/写跟踪。
  • 支持iOS objc和Swift运行

2.unidbg的安装

2.1.下载unidbg工具

下载地址:unidbg

2.2.导入IDEA

打开IDEA,点击Import Project按钮。
这里需要说明一下,可能有些小伙伴一打开IDEA不是下图页面而是以往的项目页面,这种情况则直接在IDEA工具的File->New下面点击Project from Existing Sources…按钮一样可以进入到下一步的页面。

进入这个页面后将前面下载好的unidbg源码路径填入之后点击OK按钮。

然后会弹出下面这个页面,让我们选择项目类型,选择Maven项目,点击Finish按钮后IDEA就会开始导入工作。

这时候可能需要稍微等待一会,IDEA在导入项目的时候会下载一些依赖,记得保持网络通畅,之后等就完事儿了。

2.3.验证导入是否成功

等IDEA将整个项目导入完成后可以看到和下图类似的页面,主要看下左边红框中的内容,这是整个项目的视图,可以看到整个unidbg项目包含了unidbg-android、unidbg-api、unidbg-ios共三个子项目。

我们在unidbg-android子项目中找到作者给我们提供的测试类"com.bytedance.frameworks.core.encrypt",然后点击运行TTEncrypt.main方法。

如果运行结果如下,则表示整个项目导入成功。
之后开始我们正式的分析工作。

3.unidbg的使用

3.1.目标方法静态分析

成功测试完作者给出的例子后,我们可以找一些例子来简单学习下unidbg的使用,这里直接使用白龙大佬博客中的例子(liboasiscore.so)。
首先看下本次需要调用的目标方法,目标方法在target.dex文件中,这个文件是白龙大佬脱壳出来的,对脱壳感兴趣的小伙伴可以试下脱壳。之前也有说过常用脱壳方法"实战sign分析-某某合伙人_v4.0.9"
样本APK:绿洲_v3.5.8
这里我们直接将target.dex丢到JEB工具中并定位到com.weibo.xvideo.NativeApi类,这是个全是Native方法的类,本次要模拟执行的方法是名为s的native方法,包含两个参数,参数1是个byte型数组,参数2是个boolean型变量。

现在有了目标方法,但我们并不知道这个方法是在哪里实现的,所以还需要找到这个方法的实现地址,这样我们才能使用unidbg进行模拟执行。
根据NativeApi构造函数中的System.loadLibrary可知这些native方法都在liboasiscore.so中实现的,使用解压软件可在原apk中的以下目录找到这个so文件。
这里需要注意你自己的测试机是多少位的系统,我这里是64位的,所以选用的是arm64-v8a目录下的so。

找到目标方法所在的so之后,可以使用IDA工具对这个so进行静态分析,以此来拿到目标方法的地址。
但本次目标方法的地址单纯的使用IDA静态分析并不能直接获取,一般如果native方法使用静态绑定可通过在IDA的函数窗口搜索java等关键字即可定位到目标方法,这里主要得益于静态绑定的方法命名需要按照固定的格式的原因,而本次目标方法采用的是动态绑定的方法,动态绑定就没有必须按照固定格式命名这么个限制,所以需要先找到so中动态绑定目标方法的位置,才能定位到目标方法代码实现的位置。

一般so对native方法动态绑定的实现都放在JNI_OnLoad方法中,首先定位到JNI_OnLoad方法,再去找动态绑定的实现。

找到JNI_OnLoad方法后又发现这个方法实际上被OLLVM混淆过,这样看来想通过静态分析找到目标方法的位置就不太现实了,到此我们的静态分析结束,下面开始我们的动态分析过程。
之前的动态分析都是采用Frida框架对应用中的一些关键方法进行hook的方式来分析的,这次采用另一种方式来实现动态分析—模拟执行。

3.2.模拟执行目标方法

打开前面配置好的unidbg工程,在工程的unidbg-android子项目的src/test/java/目录下创建一个com.sina.oasis.Oasis类并继承自AbstractJni类,主要用来编写我们的模拟执行so的代码,并将目标so文件liboasiscore.so和原apk也放到该目录下。

整个框架准备完毕后开始在com.sina.oasis.Oasis类中编写模拟执行的代码。
因为目标方法s是在JNI_OnLoad方法中实现的动态绑定,所以需要先实现对JNI_OnLoad方法的模拟执行。
JNI_OnLoad方法模拟执行如下:

package com.sina.oasis;import com.github.unidbg.AndroidEmulator;
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.DalvikModule;
import com.github.unidbg.linux.android.dvm.DalvikVM;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;public class Oasis extends AbstractJni {private final AndroidEmulator emulator;private final VM vm;private final Module module;Oasis(){// 创建一个模拟器实例,进程名建议依照实际的进程名填写,可以规避一些so中针对进程名校验emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.sina.oasis").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/sina/oasis/lvzhou.apk"));// 加载so到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/sina/oasis/liboasiscore.so"),true);// 获取so模块的句柄module = dm.getModule();// 设置JNIvm.setJni(this);// 打印日志vm.setVerbose(true);// 调用JNI方法dm.callJNI_OnLoad(emulator);;   // 调用JNI_OnLoad}public static void main(String[] args){Oasis oasis = new Oasis();}
}

以上代码实现了在PC上模拟执行目标so并调用JNI_OnLoad方法的功能。

通过输出日志可以看到JNI_OnLoad方法在执行的过程中调用的各种方法,同时还可以看到我们的目标方法被动态绑定时所在内存的地址,unidbg还友好的帮我们输出了该方法在so文件中的地址0x116CC。
有了这个地址就能在IDA中很方便的定位到目标方法的位置。
使用IDA快捷键G能够快速跳转到目标地址。

有了目标方法的地址后下面开始对目标方法进行模拟执行。
目标方法s的模拟执行如下:

package com.sina.oasis;import com.github.unidbg.AndroidEmulator;
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.DalvikModule;
import com.github.unidbg.linux.android.dvm.DalvikVM;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;public class Oasis extends AbstractJni {private final AndroidEmulator emulator;private final VM vm;private final Module module;Oasis(){// 创建一个模拟器实例,进程名建议依照实际的进程名填写,可以规避一些so中针对进程名校验emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.sina.oasis").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/sina/oasis/lvzhou.apk"));// 加载so到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/sina/oasis/liboasiscore.so"),true);// 获取so模块的句柄module = dm.getModule();// 设置JNIvm.setJni(this);// 打印日志vm.setVerbose(true);}public String call_native_s(){// 构造jni方法的参数List<Object> arg_list = new ArrayList<>(10);// 参数1:JNIEnv *envarg_list.add(vm.getJNIEnv());// 参数2:jobject或jclass 一般用不到,直接填0即可arg_list.add(0);// 参数3:bytesString input = "aid=01A-khBWIm48A079Pz_DMW6PyZR8" +"uyTumcCNm4e8awxyC2ANU.&cfrom=28B529501" +"0&cuid=5999578300&noncestr=46274W9279Hr1" +"X49A5X058z7ZVz024&platform=ANDROID&timestamp" +"=1621437643609&ua=Xiaomi-MIX2S__oasis__3.5.8_" +"_Android__Android10&version=3.5.8&vid=10190135" +"94003&wm=20004_90024";byte[] input_bytes = input.getBytes(StandardCharsets.UTF_8);ByteArray input_byte_array = new ByteArray(vm,input_bytes);arg_list.add(vm.addLocalObject(input_byte_array));// 参数4:boolean  false 填入0arg_list.add(0);// 参数准备完毕 调用目标方法Number number = module.callFunction(emulator,0x116CC,arg_list.toArray())[0];return vm.getObject(number.intValue()).getValue().toString();}public static void main(String[] args){Oasis oasis = new Oasis();System.out.println("Native方法返回值:" + oasis.call_native_s());}
}

运行后的输出日志。

可以看到目标方法执行完之后返回的结果是个32位的字符串,一般32位可能是Hash算法,下面来分析下目标方法s使用的算法。

3.3.算法分析

首先使用findhash工具对目标so中可能用到的常见Hash算法进行检测。
使用方法

  • 下载findhash
  • 将findhash.xml和findhash.py放到IDA的plugins目录下
  • 使用IDA7.5加载完so后,在IDA工具栏依次点击edit->plugin->findhash即可运行

findhash工具的检测结果如下:

提示共发现3处疑似哈希函数的代码,同时还友好的帮我们生成了基于frida的hook代码,我们使用生成的代码来验证下算法。

λ frida -U -f com.sina.oasis -l liboasiscore_findhash_1626530188.js --no-pause____/ _  |   Frida 14.2.2 - A world-class dynamic instrumentation toolkit| (_| |> _  |   Commands:/_/ |_|       help      -> Displays the help system. . . .       object?   -> Display information about 'object'. . . .       exit/quit -> Exit. . . .. . . .   More info at https://www.frida.re/docs/home/
Spawned `com.sina.oasis`. Resuming main thread!
TypeError: cannot read property 'add' of nullat hook_suspected_function (/liboasiscore_findhash_1626530188.js:26)at main (/liboasiscore_findhash_1626530188.js:52)at apply (native)at <anonymous> (frida/runtime/core.js:45)

第一次尝试直接报错,分析发现是因为hook的时机不对,因为我们是在程序启动时开始hook的,而此时目标so还没有加载到内存中,所以会报错。
修改代码:

将js代码中自动生成的setImmediate(main);改为setTimeout(main,5000);使之前的立即调用main方法变成5秒后调用main方法,这样就能在目标so加载完成后进行hook,也就不会再报前面的错误了。

λ frida -U -f com.sina.oasis -l liboasiscore_findhash_1626530188.js --no-pause____/ _  |   Frida 14.2.2 - A world-class dynamic instrumentation toolkit| (_| |> _  |   Commands:/_/ |_|       help      -> Displays the help system. . . .       object?   -> Display information about 'object'. . . .       exit/quit -> Exit. . . .. . . .   More info at https://www.frida.re/docs/home/
Spawned `com.sina.oasis`. Resuming main thread!
[Nexus 5X::com.sina.oasis]->

发现,虽然没有报错了,但应用启动后随意操作几下发现却并没命中我们的hook代码,直接使用findhash的脚本还是存在一些问题,作者也说了对arm64适配还有些问题,看来我们只有手动分析了。

3.3.1.OLLVM去混淆

根据前面的分析我们知道该应用对so中的方法都加了OLLVM混淆,手动分析的话那么第一步就是去混淆,这样分析起来才不至于迷失在代码的海洋。
本次使用的思路如下:
1.首先使用unidbg对目标方法进行指令级trace,拿到目标方法所有会被执行的指令地址;
trace代码:主要在原call_native_s()方法中添加了对一定范围内指令执行的监控回调。这里我们使用IDA快捷键"ctrl+s"查看目标so的代码段,将整个代码段的起始地址(0x8C50)和结束地址(0x42ED4)作为监控范围。记得加上so基址;

    public String call_native_s(){// 添加一个指令集hook回调,并输出当前执行指令在so文件中的偏移地址emulator.getBackend().hook_add_new(new CodeHook() {@Overridepublic void onAttach(Unicorn.UnHook unHook) { }@Overridepublic void detach() { }@Overridepublic void hook(Backend backend, long address, int size, Object user) {// 打印当前指令地址,注意需要将实际地址减去so基地址得到代码在文件中的偏移System.out.println(String.format("0x%x",address-module.base));}},module.base+0x8C50,module.base+0x42ED4,null);  // 指定监控起始地址与结束地址,记得加上so基址// 构造jni方法的参数List<Object> arg_list = new ArrayList<>(10);// 参数1:JNIEnv *envarg_list.add(vm.getJNIEnv());// 参数2:jobject或jclass 一般用不到,直接填0即可arg_list.add(0);// 参数3:bytesString input = "aid=01A1heTbypm4P4ovynAZcytJ3Af1fHFAV90gv3_mMmB6Ch9gQ.&cfrom=28B5295010&cuid=5226994080&filter_quick_replay=0&filter_rainbow=0&noncestr=H022wq318Kk508356jZXXl1317G8RS&platform=ANDROID&timestamp=1626584839282&ua=LGE-Nexus5X__oasis__3.5.8__Android__Android8.1.0&version=3.5.8&vid=2003855943799&wm=20004_90024";byte[] input_bytes = input.getBytes(StandardCharsets.UTF_8);ByteArray input_byte_array = new ByteArray(vm,input_bytes);arg_list.add(vm.addLocalObject(input_byte_array));// 参数4:boolean  false 填入0arg_list.add(0);// 参数准备完毕 调用目标方法Number number = module.callFunction(emulator,0x116CC,arg_list.toArray())[0];return vm.getObject(number.intValue()).getValue().toString();}

模拟执行结果:

之后手动将这些地址保存成一个unidbgTrace.log文件。

2.使用IDAPython在IDA中将前面收集到的地址进行标记;
有了目标方法执行的实际指令地址后使用IDAPython脚本在IDA中对这些地址进行标记,表示有效指令。

# -*- coding: utf-8 -*-
import sark
import sysdef do_ollvm_bcf():'''ollvm虚假控制流处理'''log_file = "unidbgTrace.log"trace_addrs = []# 将trace指令的地址读取到trace_addrs列表中with open(log_file, "r") as f:lines = f.readlines()for line in lines:trace_addrs.append(int(line.replace("\n", ""), 16))for addr in trace_addrs:line = sark.line.Line(addr)line.color = 0xEE82EE   # 将执行过的指令通过修改颜色来标记出来。if __name__ == "__main__":do_ollvm_bcf()

IDA加载IDAPython脚本:IDA工具栏依次点击file->script file…之后在弹出来的文件选择框中选择写好的Python脚本文件加载即可。
执行完后跳转到目标地址方法0x116CC处看下,效果如下:
处理后的目标方法的流程图,其中标记为紫色的表示有效会被执行的代码;

不过还有个问题,根据上图可以看出整个方法还是有许多无效的代码块,主要是因为IDA将目标方法解析成了JNI_OnLoad方法的一个分析,所以这里看到的图实际上是包括JNI_OnLoad方法和一些别的方法的代码的。
对于这种情况,在JNI_OnLoad方法开始(0x10CEC)处使用快捷键"u"将整个方法取消定义,之后再跳到目标方法开始(0x116CC)处使用快捷键"p"从当前地址开始解析成方法,处理完成后效果如下:

然后你会发现,好像,,,,这个方法并没有什么虚假控制流。-_-||

问题不大,反正也是学习,说一下,如果有时候发现存在大量的虚假控制流,那么还可以根据有效指令将不会被执行到的指令patch为nop,这样IDA在反编译伪C代码的时候就不会对无效指令进行解析,减少伪C中无效指令的干扰。
使用IDAPython对方法中未执行的指令全部patch成nop指令(0x1f, 0x20, 0x03, 0xd5);
如何知道nop指令的opcode?
使用IDA插件Patcher:IDA工具栏依次点击edit->plugin->keypatch patcher

IDA快捷键:

  • u:取消函数、代码、数据的定义
  • p:从当前地址处开始解析成方法

完整代码如下:

# -*- coding: utf-8 -*-
import sark
import idc
import sysdef patch_nop(addr):'''将指定地址的字节修改成nop'''nop_code = [0x1f, 0x20, 0x03, 0xd5]for i in range(len(nop_code)):idc.patch_byte(addr+i, nop_code[i])def patch_code():# 设置需要处理代码的起始地址和终止地址,并获取范围内所有汇编指令的地址s = 0x10CEC         # 目标方法起始地址e = 0x10CEC+0xFEC   # 目标方法结束地址funcLines = sark.lines(s, e)for line in funcLines:# 判断如果该行代码的颜色是我们标记的颜色,则进入patch逻辑,将代码patch为nop指令。if line.type == "code":if line.color != 0xEE82EE:patch_nop(line.ea)def do_ollvm_bcf():'''ollvm虚假控制流处理'''log_file = "unidbgTrace.log"trace_addrs = []# 将trace指令的地址读取到trace_addrs列表中with open(log_file, "r") as f:lines = f.readlines()for line in lines:trace_addrs.append(int(line.replace("\n", ""), 16))for addr in trace_addrs:line = sark.line.Line(addr)line.color = 0xEE82EE   # 将执行过的指令通过修改颜色来标记出来。if __name__ == "__main__":do_ollvm_bcf()# patch_code()

虽然处理完后还存在一些控制流平坦化的问题,但这并不妨碍我们fuck这操蛋的代码。
F5看下伪C代码,分析前先对目标方法的入参类型进行修复,鼠标选中参数1,IDA快捷键"y"修改变量类型,将参数1恢复成JNIEnv *a1,这样方法中调用的JNI方法就会被自动识别出来。

__int64 __fastcall sub_116CC(JNIEnv *a1, __int64 a2, __int64 a3, char a4)
{// ...v35 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);v6 = 0xFD500367;v7 = (*a1)->GetByteArrayElements(a1, a3, 0LL);v8 = (*a1)->GetArrayLength(a1, (jarray)a3);v9 = v8;v10 = v8 == -1;if ( v8 < -1 )v11 = -1LL;elsev11 = v8 + 1;v12 = !v10;v13 = (char *)operator new[](v11);v14 = &v13[v9];memset(&v13[v9], 0, v12);v15 = v9;v16 = a1;memcpy(v13, v7, v15);*v14 = 0;(*a1)->ReleaseByteArrayElements(a1, (jbyteArray)a3, v7, 0LL);v17 = (const char **)&off_5D0A8;if ( a4 )v17 = (const char **)&off_5D0A0;v18 = *v17;v32[0] = 0LL;v32[1] = 0LL;v33 = 0LL;v19 = strlen(v18);sub_12C90((int)v32, (int)v18, v19);v20 = strlen(&byte_5D230);sub_12FDC((int)v32, (int)&byte_5D230, v20);v21 = strlen(v13);sub_12FDC((int)v32, (int)v13, v21);free(v13);sub_C688(v34, v32);sub_F6DC(v34);if ( ((v29 ^ 0xFE) & v29) != 0 )v23 = 0xC984BF68;elsev23 = 0xA4F8DF4D;for ( i = 0x9C2D2380; ; i = 0x4D9896E ){while ( i <= (int)0xC984BF67 ){if ( i == 0x9C2D2380 ){i = v23;}else{i = 0x4D9896E;v22 = v30;}}if ( i != 0xC984BF68 )break;v22 = (char *)v31;}v25 = (__int64)(*v16)->NewStringUTF(v16, v22);v26 = 0xFD500367;while ( v26 != 0xF409034D ){// ...}while ( v6 != 0xF409034D ){// ...}return v25;
}

捋一下方法的大概逻辑,代码比较少,首先通过v7 = (*a1)->GetByteArrayElements(a1, a3, 0LL);方法拿到Java层传入的待加密字符串,之后申请一块新内存将Java层传入的待加密字符串拷贝进去。
之后分别通过方法sub_12C90(),sub_12FDC(),将两个内置的字符串(“YP1Vty&$Xm*kJkoR,Opk"和”&")与Java层传入的待加密字符串进行拼接,结果存放在变量v32(0x78b5752338)指向的内存中,之后调用free将前面申请的内存进行释放。

方法sub_C688,sub_F6DC则主要是对拼接后的字符串进行处理,应该是主要加密逻辑,但简单分析后发现与这两个方法相关的两个变量v34与v32在之后的代码中并没有被引用,导致无法进一步分析,所以换个方向,试一下根据目标方法的返回值进行反向分析。
思路如下:

  • 1.找到返回值来源
  • 2.监控返回值来源内存的变化
    目标方法的返回值来源:返回值等于v25,v25的数据来源于"(*v16)->NewStringUTF(v16, v22)",所以数据来源于v22,而代码中v22有两处赋值,根据我们打的有效标记可知v22的实际来源是v31,到这里后发现数据流又断掉了,淦,直接看汇编吧。

伪C代码"v22 = (char *)v31;“对应汇编"MOV X1, X12”,再看看X12的来源等于"LDR X12, [SP,#0x110+var_E8]"。
到此分析了整个返回值的数据来源,下面使用Frida的指令级Hook确定一下。

3.3.2.指令级Hook辅助分析

"LDR X12, [SP,#0x110+var_E8]"指令所在地址是0x11870,但是hook时需要下条指令的地址0x11874,这样X12寄存器中的值才是正确的;
代码如下:

        // 指令Hooklet arm_11874 = targetSo.add(0x11874);Interceptor.attach(ptr(arm_11874),{onEnter:function () {// console.log(args[0]);var sp = new NativePointer(Number(this.context.sp) + Number(272 - 232));console.log("arm_11874_onLeave   [ARM:SP+0X110-0XE8]:"+JSON.stringify(sp));console.log(hexdump(sp,{length:100}));console.log("arm_11874_onLeave   [ARM:X12]:"+JSON.stringify(this.context.x12));var x12 = this.context.x12;console.log(hexdump(x12,{length:100}));},onLeave:function (retval){console.log("");}})

分别得到"SP+0X110-0XE8"与X12寄存器的值;

接下来再对"SP,#0x110+var_E8"指向的内存进行监控,看下是在哪出被修改的。

调试可见存放结果的目标内存是在sub_F6DC函数执行前后不一样,所以应该是在这里面被修改的。

3.3.3.unidbg寄存器trace分析

----------------------- 以上全错
以上的方法都太麻烦了,还有更简单更暴力的方法;
通用思路:利用trace功能,将每条汇编指令以及寄存器值的变化全部记录下来,然后分析寄存器中的值是否有标准算法的关键数据。
如下:这里使用unidbg trace功能识别标准MD5算法的魔术IV值,定位到这些数据后离算法的运算逻辑就不远了。这实际上是个标准的MD5加密。

或者也可以一个个尝试标准Hash算法。((:
比较全的一个加解密在线工具CyberChef

4.补充知识

4.1.JNIEnv、jobject、jclass

一般在JNI编程中,javah生成的Native方法的第一个参数永远都是JNIEnv指针,而第二个参数一般都是jobject或jclass中的一个。

4.1.2.JNIEnv指针是什么东西?

JNIEnv顾名思义,指代了Java本地接口环境(Java Native Interface Environment),是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每个成员都指向一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构。

可以看到,JNIEnv中包含了诸多的JNI函数结构体。

4.1.3.jobject和jclass又有什么区别?

jobject与jclass通常作为JNI函数的第二个参数出现,当声明的Native方法为静态方法时,对应的参数就是jclass,因为静态方法不依赖对象实例,而依赖于类,所以参数中传递的是一个jclass类。相反,如果声明的Native方法是非静态方法,则对应的参数是jobject。所以为了能够在Native层访问java中的类和对象,jobject和jclass分别表示对象和类,进而实现成员方法和成员变量的访问。

Android逆向-实战so分析-某洲_v3.5.8_unidbg学习相关推荐

  1. Android逆向-实战sign分析-某某合伙人_v4.0.9

    1.基础分析 1.1.基础分析 工作中经常会对一些app进行安全审计,一般在拿到应用后可以使用APK Helper工具对应用做个简单分析,了解目标的包名,版本号,名称等基础信息,之后再安装到手机上熟悉 ...

  2. Android逆向与病毒分析

    本文由同程旅游安全团队对内移动安全培训的PPT整理而来,面向对象为对移动安全感兴趣的研发同事,所以讲的有些宽泛.介绍了入门Android逆向需要掌握的一些知识点, 通过简单的几个案例讲解Android ...

  3. Android逆向实战 - 腾讯新闻去开屏广告

    上次反编译一个工具类app失败,原因是使用了360加固,回编译后无法启动.一般来讲,大厂的app考虑到性能.兼容性.包体积等,通常不用加固.因此,本次我们选一个大一些的app-腾讯新闻.写在前面:本篇 ...

  4. 什么是Android逆向?如何学习安卓逆向?Android逆向自学笔记入门到实战

    简单地来说,安卓逆向是对已经打包好的APP进行反编译.源码分析了解APP实现逻辑的一门技术.我们可以把安卓安装时用到的APK文件看作一个加密后的压缩包,逆向就是要最大程序地还原出APK打包之前的源码. ...

  5. Android逆向之玩转Xposed模块以劫持登录为例(实战篇)

    上一篇文章<Android逆向之玩转Xposed模块以劫持登录为例(Demo篇)>自编自导了一款劫持登录的Xposed模块,如果仅满于破解自己的APP是多么的悲哀,毕竟市场上的app都是经 ...

  6. [1196]Android逆向工具【反射大师】脱壳实战

    文章目录 关于逆向 逆向的准备 逆向步骤 Android系统 安装Xposed框架 1.下载`Xposed Installer` 2.安装`Xposed Installer` 安装反射大师 1.下载反 ...

  7. Android逆向小技巧①:从Activity下手找到切入点,逆向分析支付宝APP

    明确目标 关于Android应用的解包.反编译,在网上已经有无数文章了,此处不再赘述.当你已经使用 [d2j-dex2jar] 和 [jd-gui] 得到了APK反编译后的JAVA代码,面对庞大的代码 ...

  8. android逆向分析概述_Android存储概述

    android逆向分析概述 Storage is this thing we are all aware of, but always take for granted. Not long ago, ...

  9. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | Class.cpp#findClassNoInit 函数 | DexFile.cpp#dexFindClass 函数分析 )

    文章目录 前言 一.Class.cpp#dvmDefineClass 函数分析 二.Class.cpp#findClassNoInit 函数分析 三.DexFile.cpp#dexFindClass ...

  10. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | native 函数查询 | dalvik_system_DexFile.cpp#defineClassNative 函数 )

    文章目录 前言 一.查询 defineClassNative 函数 二.dalvik_system_DexFile.cpp#Dalvik_dalvik_system_DexFile_defineCla ...

最新文章

  1. PHP+MySQL手工注入问题及修复
  2. Spark机器学习库(MLlib)指南
  3. 9、Power Map—应用拾取坐标系统确定经纬度
  4. 直播 | WWW 2021:用先验知识指导BERT注意力机制的语义文本匹配
  5. 编译问题 文件查找失败: ‘vant‘
  6. boost::undirected_dfs用法的测试程序
  7. go struct{} 空结构体的特点和作用
  8. 【转】ASP.NET MVC框架下使用MVVM模式-KnockOutJS+JQ模板例子
  9. sql server中的varchar和Nvarchar有什么区别?
  10. 新生赛3 1003 字符串最小表示法题目
  11. Java8新特性(Arrays)
  12. 大话数据结构系列之快速排序算法
  13. springboot实现上传图片添加水印
  14. POI 4.1.2 word转html(保留样式及图片)
  15. 如何取消Word文档的只读模式或者限制编辑
  16. java xtend_简化Java语法 Eclipse推出Xtend
  17. 金融数据分析与挖掘具体实现方法
  18. 【docker系列】docker API管理接口增加CA安全认证
  19. 组合数学——插板模型
  20. 静态NAT 如何配置?

热门文章

  1. vscode插件Todo Tree配置
  2. 【狂神说Java】Spring Boot笔记
  3. 如何安装HDDM,无法使用HDDM
  4. 拳皇重生服务器维护,《拳皇97 OL》3月8日更新维护公告
  5. 业务架构实践:一步一步画出业务架构图
  6. 智慧灯杆解决方案之智慧景区(园区)建设
  7. 每日一诗词 —— 临江仙
  8. 增加客流量的方法_7种成熟的方法来增加网站流量
  9. SQLite数据库中的.db-shm文件和.db-wal文件
  10. Android热修复学习之旅开篇——热修复概述