学习逆向过程中的一些问题与其解决办法

# 前言

学习逆向过程中的一些问题与其解决办法,当然还有大佬们文章内提到的,整理出来方便翻阅,持续更新。

1
{% tip key %}当然大家有什么问题也可在下方评论,如有解决本文问题的方法还请不吝赐教,在此感谢鞠躬!{% endtip %}

# 参考资料

# 资源工具

# IDA

# 通过 JNI_OnLoad 定位 Native 函数

1
{% tip info %}仅限没有经过混淆或加密处理的样本{% endtip %}

PixPin_2023-12-14_18-32-47.gif

# Python

# Python 能不能调用 so 文件的 Native 方法?

Python 也有类似 Unidbg 的工具 ExAndroidNativeEmu - Python 工具 Unidbg 青春版功能有限,但是仅限于对 JAVA 层的交互极少,一旦涉及到 JNI 交互,则需要果断选择 Unidbg,在 Python 中补 JAVA 的逻辑,简直不是人该受的委屈。

# 密码学

# 加密算法大概率是开源或者魔改算法有没有快速验证的方法?

当在分析过程中有猜测或怀疑是某加密算法时可以使用类似 findhash - IDA 脚本 可以检测出哈希算法的脚本,此脚本可检测无论是否魔改常数的 hash 算法 MD5,SHA1、SHA2。

# 汇编指令

# ARM32 有 Thumb 和 ARM 两种指令模式如何确定?

最粗暴的方式就是试错法 module.callFunction 时不加 1 会报错非法指令则表示是 Thumb 模式
第二个办法是从知识角度出发,ARM 模式指令总是 4 字节长度,Thumb 指令长度多数为 2 字节,少部分指令是 4 字节。

# Unidbg

# Unidbg 的 Jnionload 加载出的类是乱码?

so 做了字符串的混淆或加密,以此来对抗分析人员,但字符串总是要解密的,不然怎么用呢?这个解密一般发生在 Init array 节或者 JNI OnLoad 中,又或者是该字符串使用前的任何一个时机

# 对虚拟内存进行修改

Unidbg 提供了两种方法打 Patch,简单的需求可以调用 Unicorn 对虚拟内存进行修改,如下

1
2
3
4
public void patchVerify(){
int patchCode = 0x4FF00100; //
emulator.getMemory().pointer(module.base + 0x1E86).setInt(0,patchCode);
}
1
{% tip warning %}需要注意的是,这儿地址可别+1了,Thumb的+1只在运行和Hook时需要考虑,打Patch可别想。{% endtip %}

# 补环境怎么补补什么?

我们既可以根据报错提示,在 AbstractJni 对应的函数体内,依葫芦画瓢,case "xxx“。
也可以在我们的 zuiyou 类中补,因为 zuiyou 类继承了 AbstractJNI。
关于补法,有两种实践方法都很有道理

  • 全部在用户类中补,防止项目迁移或者 Unidbg 更新带来什么问题,这样做代码的移植性比较好。
  • 自定义 JAVA 方法在用户类中补,通用的方法在 AbstractJNI 中补,这样做的好处是,之后运行的项目如果调用通用方法,就不用做重复的修补工作。

# 如何主动调用一个 Native 函数

在 Frida 中可以使用 NativeFunction API 主动调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function call_65540(base_addr){
// 函数在内存中的地址
var real_addr = base_addr.add(0x65541)
var md5_function = new NativeFunction(real_addr, "int", ["pointer", "int", "pointer"])
// 参数1 明文字符串的指针
var input = "r0ysue";
var arg1 = Memory.allocUtf8String(input);
// 参数2 明文长度
var arg2 = input.length;
// 参数3,存放结果的buffer
var arg3 = Memory.alloc(16);
md5_function(arg1, arg2, arg3);
console.log(hexdump(arg3,{length:0x10}));
}

function callMd5(){
// 确定SO 的基地址
var base_addr = Module.findBaseAddress("libnet_crypto.so");
call_65540(base_addr);
}

// frida -UF -l path\hookright.js


在 Unidbg 也是类似的,只不过换一下 API 罢了,让我们来看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void callMd5(){
List<Object> list = new ArrayList<>(10);

// arg1
String input = "r0ysue";
// malloc memory
MemoryBlock memoryBlock1 = emulator.getMemory().malloc(16, false);
// get memory pointer
UnidbgPointer input_ptr=memoryBlock1.getPointer();
// write plainText on it
input_ptr.write(input.getBytes(StandardCharsets.UTF_8));

// arg2
int input_length = input.length();

// arg3 -- buffer
MemoryBlock memoryBlock2 = emulator.getMemory().malloc(16, false);
UnidbgPointer output_buffer=memoryBlock2.getPointer();

// 填入参入
list.add(input_ptr);
list.add(input_length);
list.add(output_buffer);
// run
module.callFunction(emulator, 0x65540 + 1, list.toArray());

// print arg3
Inspector.inspect(output_buffer.getByteArray(0, 0x10), "output");
};

需要注意,在 Unidbg 中,同样的功能有至少两种实现和写法 ——Unicorn 的原生方法以及 Unidbg 封装后的方法,在阅读别人代码时需要灵活变通。就好比 getR0long emulator.getBackend ().reg_read (ArmConst.UC_ARM_REG_R0),它们都是获取寄存器 R0 的数值。

# JNItrace trace 我们在参数还没完全转换完的情况下,Unidbg 就退出了

这种情况下,可能的原因有很多,但可能性较大的是两个

  • 上下文环境缺失
  • 样本使用某种手段检测或反制了 Unidbg

先看一下是否是上下文的问题,假设是上下文缺失,通俗的讲就是在 SO 加载后到我们的 main 函数调用前的这段时间里,样本需要调用一些函数对 SO 进行初始化,而我们没有注意也没做这个事,这导致了 Unidbg 无法顺利运行。

# 抓包

# 对于服务器校验证书如何抓包?

安卓可以使用 r0capture - 安卓应用层抓包通杀脚本进行抓包分析。

# 验证码

# 遇到验证码验证怎么办?

可以使用 ddddocr - Python 库 通用验证码识别库进行识别,支持老版验证码,当前验证码支持部分滑块文字点选等。