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