Unidbg Trace 反 OLLVM 控制流平坦化(fla)

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

目标 so 分析

目标方法反汇编视图如下

F5 反汇编代码如下,很明显通过 fla 隐藏了真实的执行流

ini 复制代码
__int64 __usercall dynamicBase64Encode@<X0>(const unsigned __int8 *a1@<X0>, unsigned __int64 a2@<X1>, __int64 a3@<X8>)
{
  int v3; // w8
  int v4; // w8
  unsigned __int8 *v5; // x0
  int v6; // w8
  unsigned __int8 *v7; // x0
  __int64 result; // x0
  int i; // [xsp+20h] [xbp-60h]
  unsigned __int64 v11; // [xsp+30h] [xbp-50h]
  int v12; // [xsp+3Ch] [xbp-44h]
  int v13; // [xsp+40h] [xbp-40h]
  char v16[24]; // [xsp+60h] [xbp-20h] BYREF
  __int64 v17; // [xsp+78h] [xbp-8h]

  v17 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  generateDynamicBase64Alphabet(a2);
  sub_29574(a3);
  v13 = 0;
  v12 = -6;
  v11 = 0LL;
  do
  {
    if ( v11 >= a2 )
      v3 = 491;
    else
      v3 = 26962;
    for ( i = v3; ; i = 24464 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          while ( 1 )
          {
            while ( 1 )
            {
              while ( 1 )
              {
                while ( 1 )
                {
                  while ( i == 491 )
                  {
                    if ( v12 <= -6 )
                      v6 = 4827;
                    else
                      v6 = 2995;
                    i = v6;
                  }
                  if ( i != 2995 )
                    break;
                  v7 = (unsigned __int8 *)sub_2E280(v16, (v13 << 8 >> (v12 + 8)) & 0x3F);
                  std::string::push_back(a3, *v7);
                  i = 4827;
                }
                if ( i != 4827 )
                  break;
                i = 32391;
              }
              if ( i != 5705 )
                break;
              v5 = (unsigned __int8 *)sub_2E280(v16, (v13 >> v12) & 0x3F);
              std::string::push_back(a3, *v5);
              v12 -= 6;
              i = 24464;
            }
            if ( i != 9961 )
              break;
            ++v11;
            i = 29358;
          }
          if ( i != 16827 )
            break;
          i = 9961;
        }
        if ( i != 24464 )
          break;
        if ( v12 < 0 )
          v4 = 16827;
        else
          v4 = 5705;
        i = v4;
      }
      if ( i != 26962 )
        break;
      v13 = a1[v11] | (v13 << 8);
      v12 += 8;
    }
  }
  while ( i == 29358 );
  result = std::string::~string(v16);
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  return result;
}

unidbg 中调用目标 so 函数

把 so 放到 resource 目录下

关于 unidbg 的详细介绍可以参考这篇文章:unidbg 加载 so 并调用 so 中函数

通过 unidbg 加载 so 并执行 so 中目标函数

java 复制代码
package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
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.linux.android.dvm.jni.ProxyClassFactory;
import com.github.unidbg.memory.Memory;

import java.io.File;

public class TraceDemo {

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

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

    private final AndroidEmulator emulator;

    private TraceDemo() {
        // 创建一个 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() {
        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvikVM();
        // 启用虚拟机的调试输出
        vm.setVerbose(true);

        vm.setDvmClassFactory(new ProxyClassFactory());

        // 加载 so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        DalvikModule module = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/lib_ollvm_fla_base64.so"), true);

        // 调用 JNI_Onload
        module.callJNI_OnLoad(emulator);

        // 注册 Base64Activity 类
        DvmClass dvmClass = vm.resolveClass("com/cyrus/example/base64/Base64Activity");
        
        // 创建 Java 对象
        DvmObject object = dvmClass.newObject(null);
        
        // 调用 jni 方法 dynamicBase64Encode 并传参 ByteArray
        DvmObject result = object.callJniMethodObject(emulator, "dynamicBase64Encode([B)Ljava/lang/String;", "lskvIIF".getBytes());

        System.out.println("result:" + result);
    }
}

unidbg trace

在 callJniMethodObject 方法之前添加如下代码,trace 指令执行和内存读写,并把 trace log 输出到文件。

ini 复制代码
// 日志保存到文件
PrintStream fileOut;
try {
    // 获取当前时间,格式化为 yyyyMMdd_HHmmss
    String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String filename = "trace_log_" + timestamp + ".txt";

    fileOut = new PrintStream(new FileOutputStream(filename));
} catch (FileNotFoundException e) {
    throw new RuntimeException(e);
}
System.setOut(fileOut);
System.setErr(fileOut);

// trace 指令
emulator.traceCode();
// trace 内存读取
emulator.traceRead();
// trace 内存写入
emulator.traceWrite();

得到 trace log,包含指令流、寄存器变化、内存读写信息

算法还原

根据真实的执行指令流去还原算法执行过程

ini 复制代码
#include <cstdint>
#include <cstdlib>
#include <string>
#include <cstring>

alignas(16) const __int128 xmmword_14B00 = (__int128) 0x4000000000000000LL << 64 | 0x51; // 全局 128-bit 常量 xmmword_14B00
alignas(16) __int128 xmmword_54C30;  // / 全局 128-bit 变量 xmmword_54C30
char *qword_54C40 = nullptr;         // 存储 Base64 码表指针


void sub_23F30() {
    // 分配 80 字节(0x50)内存
    char *v0 = new char[0x50];

    // 复制 xmmword_14B00 的内容到 xmmword_54C30
    xmmword_54C30 = xmmword_14B00;

    // 保存 Base64 码表地址
    qword_54C40 = v0;

    // 复制标准 Base64 码表到 v0
    strcpy(v0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
}


std::string dynamicBase64Encode(const unsigned __int8 *a1, __int64 a2) {

    // 输出
    std::string a3;

    __int128 *v6; // x0
    __int128 v7; // q0
    __int64 v8; // x8
    char *v9; // x12
    __int64 v10; // x14
    char v11; // w12
    char *v12; // x11
    __int64 v13; // x26
    int v14; // w25
    int v15; // w8
    char *v16; // x27
    unsigned int v17; // w28
    int v18; // w8
    char *v19; // x9
    __int128 v20; // [xsp+0h] [xbp-20h] BYREF
    void *v21; // [xsp+10h] [xbp-10h]
    __int64 v22; // [xsp+18h] [xbp-8h]

    // 访问 CPU 状态信息
    // v22 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);

    v6 = (__int128 *) operator new(0x50uLL);

    *(__int64 *) &v7 = 0x2020202020202020LL;
    *((__int64 *) &v7 + 1) = 0x2020202020202020LL;

    v8 = 0LL;
    v21 = v6;
    *((uint8_t *) v6 + 64) = 0;
    *v6 = v7;
    v6[1] = v7;
    v6[2] = v7;
    v6[3] = v7;
    v20 = xmmword_14B00;
    do {
        v9 = (char *) qword_54C40;
        v10 = a2 & 0x3F ^ v8;
        if ((xmmword_54C30 & 1) == 0)
            v9 = (char *) &xmmword_54C30 + 1;
        v11 = v9[v8++];
        if ((v20 & 1) != 0)
            v12 = (char *) v21;
        else
            v12 = (char *) &v20 + 1;
        v12[v10] = v11;
    } while (v8 != 64);

//    *a3 = 0LL;
//    a3[1] = 0LL;
//    a3[2] = 0LL;
    a3.clear(); // 清空字符串

    if (a2) {
        v13 = 0LL;
        v14 = 0;
        v15 = -6;
        if ((v20 & 1) != 0)
            v16 = (char *) v21;
        else
            v16 = (char *) &v20 + 1;
        do {
            v14 = a1[v13] | (v14 << 8);
            if (v15 < -8) {
                v15 += 8;
            } else {
                v17 = v15 + 14;
                do {
                    v17 -= 6;
                    a3.push_back((unsigned __int8) v16[(v14 >> v17) & 0x3FLL]);
                } while (v17 > 5);
                v15 = v17 - 6;
            }
            ++v13;
        } while (v13 != a2);
        if ((unsigned int) v15 > 0xFFFFFFFA) {
            v18 = v14 << 8 >> (v15 + 8);
            if ((v20 & 1) != 0)
                v19 = (char *) v21;
            else
                v19 = (char *) &v20 + 1;
            a3.push_back((unsigned __int8) v19[v18 & 0x3E]);
        }
    }
    if ((v20 & 1) != 0)
        operator delete(v21);

    return a3;
}


int main() {
    // 初始化全局变量
    sub_23F30();

    // 要编码的原始数据
    std::string input = "lskvIIF";

    // 调用 Base64 编码函数
    std::string result = dynamicBase64Encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());

    printf("input: %s\n", input.c_str());
    printf("result: %s", result.c_str());
    return 0;
}

输出如下:

完整源码

java 复制代码
package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
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.linux.android.dvm.jni.ProxyClassFactory;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TraceDemo {

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

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

    private final AndroidEmulator emulator;

    private TraceDemo() {
        // 创建一个 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() {
        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvikVM();
        // 启用虚拟机的调试输出
        vm.setVerbose(true);

        vm.setDvmClassFactory(new ProxyClassFactory());

        // 加载 so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        DalvikModule module = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/lib_ollvm_fla_base64.so"), true);

        // 调用 JNI_Onload
        module.callJNI_OnLoad(emulator);

        // 注册 Base64Activity 类
        DvmClass dvmClass = vm.resolveClass("com/cyrus/example/base64/Base64Activity");

        // 创建 Java 对象
        DvmObject object = dvmClass.newObject(null);


        // 日志保存到文件
        PrintStream fileOut;
        try {
            // 获取当前时间,格式化为 yyyyMMdd_HHmmss
            String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String filename = "trace_log_" + timestamp + ".txt";

            fileOut = new PrintStream(new FileOutputStream(filename));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        System.setOut(fileOut);
        System.setErr(fileOut);

        // trace 指令
        emulator.traceCode();
        // trace 内存读取
        emulator.traceRead();
        // trace 内存写入
        emulator.traceWrite();

        // 调用 jni 方法 dynamicBase64Encode 并传参 ByteArray
        DvmObject result = object.callJniMethodObject(emulator, "dynamicBase64Encode([B)Ljava/lang/String;", "lskvIIF".getBytes());

        System.out.println("result:" + result);
    }

}

完整源码地址:github.com/CYRUS-STUDI...

相关推荐
鸿蒙布道师31 分钟前
鸿蒙NEXT开发Base64工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
jiet_h1 小时前
Android adb 的功能和用法
android·adb
美狐美颜sdk2 小时前
美颜SDK兼容性挑战:如何让美颜滤镜API适配iOS与安卓?
android·深度学习·ios·美颜sdk·第三方美颜sdk·视频美颜sdk
居然是阿宋2 小时前
深入理解 YUV 颜色空间:从原理到 Android 视频渲染
android·音视频
KevinWang_3 小时前
DialogFragment 不适合复用
android
古鸽100864 小时前
Audio Hal 介绍
android
小叶不焦虑4 小时前
关于 Android 系统回收站的实现
android
木西5 小时前
从0到1搭建一个RN应用从开发测试到上架全流程
android·前端·react native
小橙子20775 小时前
一条命令配置移动端(Android / iOS)自动化环境
android·ios·自动化
和煦的春风5 小时前
案例分析 | SurfaceFlinger Binder RT 被降级到CFS
android