【翻译】使用 AFL++ Frida 模式进行 Android 灰盒模糊测试
字数 1040 2025-08-22 12:23:00
使用 AFL++ Frida 模式进行 Android 灰盒模糊测试教学文档
1. 概述
本教学文档详细介绍如何使用 AFL++ 及其 Frida 模式对 Android 应用程序进行灰盒模糊测试。内容包括环境搭建、工具编译、目标分析、线束开发以及实际模糊测试过程。
2. 环境准备
2.1 硬件要求
- 测试设备:已 root 的 Android 设备(示例使用 Samsung Galaxy A32,SM-A325F)
- 开发机:Ubuntu 22.04 x86_64
2.2 软件要求
- AFL++ 4.06c
- Frida GumJs devkit 16.0.13
- Android standalone NDK r25c
- Android 12 (API 31) aarch64
2.3 系统配置
# 安装基础工具
apt update
apt install cmake curl unzip xxd
3. AFL++ 编译与部署
3.1 下载源码
cd /opt
curl https://codeload.github.com/AFLplusplus/AFLplusplus/zip/refs/tags/4.06c --output 4.06c.zip
unzip 4.06c.zip
# 下载Android NDK
curl https://dl.google.com/android/repository/android-ndk-r25c-linux.zip --output ndk.zip
unzip ndk.zip
3.2 获取CMake配置
cd AFLplusplus-4.06c
curl https://raw.githubusercontent.com/quarkslab/android-fuzzing/main/AFLplusplus/CMakeLists.txt --output CMakeLists.txt
3.3 编译AFL++
mkdir build && cd build
cmake -DANDROID_PLATFORM=31 \
-DCMAKE_TOOLCHAIN_FILE=/opt/android-ndk-r25c/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a ..
make
3.4 部署到设备
adb push afl-fuzz afl-frida-trace.so /data/local/tmp
4. 目标应用分析
4.1 目标函数
示例应用包含三个测试场景:
- 标准本机函数:
fuzzMe - 弱链接JNI函数:
Java_qb_blogfuzz_NativeHelper_fuzzMeArray - 强链接JNI函数:
Java_qb_blogfuzz_NativeHelper_fuzzMeWrapper
4.2 关键函数代码
void fuzzMe(const jbyte *buffer, jsize length) {
void (*crashMe)() = nullptr;
if (length < 16) return;
if (buffer[0] == 'Q')
if (buffer[1] == 'u')
if (buffer[2] == 'a')
if (buffer[3] == 'r')
if (buffer[4] == 'k')
if (buffer[5] == 's')
if (buffer[6] == 'l')
if (buffer[7] == '4')
if (buffer[8] == 'b')
if (buffer[9] == 'f')
if (buffer[10] == 'u')
if (buffer[11] == 'z')
if (buffer[12] == 'z')
if (buffer[13] == 'M')
if (buffer[14] == 'e')
if (buffer[15] == '!')
crashMe();
}
5. 标准本机函数模糊测试
5.1 线束开发
关键函数fuzz_one_input:
void fuzz_one_input(const uint8_t *buffer, size_t length) {
fuzzMe((const jbyte *)buffer, (jsize)length);
}
5.2 Frida配置脚本 (afl.js)
const pStartAddr = DebugSymbol.fromName("fuzz_one_input").address;
Afl.setPersistentAddress(pStartAddr);
Afl.setEntryPoint(pStartAddr);
const cm = new CModule(`
#include <string.h>
#include <gum/gumdefs.h>
#define BUF_LEN 256
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
uint32_t length = (input_buf_len > BUF_LEN) ? BUF_LEN : input_buf_len;
memcpy((void *)regs->x[0], input_buf, length);
regs->x[1] = length;
}
`, {
memcpy: Module.getExportByName(null, "memcpy")
});
Afl.setPersistentHook(cm.afl_persistent_hook);
5.3 设备端准备
# 设置CPU性能模式
cd /sys/devices/system/cpu
echo performance | tee cpu*/cpufreq/scaling_governor
# 准备测试目录和样本
cd /data/local/tmp
mkdir in out
dd if=/dev/urandom of=in/sample.bin bs=1 count=16
5.4 启动模糊测试
./afl-fuzz -O -G 256 -i in -o out ./fuzz
6. JNI函数模糊测试
6.1 JNI环境初始化
typedef struct JavaContext {
JavaVM *vm;
JNIEnv *env;
struct JniInvocationImpl *invoc;
} JavaCTX;
int init_java_env(JavaCTX *ctx, char **jvm_options, uint8_t jvm_nb_options) {
// 详细实现参考原文
// 关键步骤包括:
// 1. 加载libandroid_runtime.so
// 2. 获取JniInvocationCreate和JniInvocationInit函数
// 3. 调用JNI_CreateJavaVM
// 4. 注册框架本地方法
}
6.2 JNI函数线束
void fuzz_one_input(const uint8_t *buffer, size_t length) {
jbyteArray jBuffer = (*ctx.env)->NewByteArray(ctx.env, length);
(*ctx.env)->SetByteArrayRegion(ctx.env, jBuffer, 0, length, (const jbyte *)buffer);
Java_qb_blogfuzz_NativeHelper_fuzzMeArray(ctx.env, NULL, jBuffer);
(*ctx.env)->DeleteLocalRef(ctx.env, jBuffer);
}
6.3 Frida配置增强
const MODULE_WHITELIST = [
"fuzz",
"libblogfuzz.so",
];
Module.load("libandroid_runtime.so");
new ModuleMap().values().forEach(m => {
if (!MODULE_WHITELIST.includes(m.name)) {
Afl.print(`Exclude: ${m.base} - ${m.base.add(m.size)} ${m.name}`);
Afl.addExcludedRange(m.base, m.size);
}
});
Afl.setPersistentCount(10000);
7. 结果分析
7.1 崩溃验证
xxd out/default/crashes/id*
00000000: 5175 6172 6b73 6c34 6266 757a 7a4d 6521 Quarksl4bfuzzMe!
8. 性能优化建议
- 限制输入大小:使用
-G参数限制最大输入大小 - 模块排除:通过Frida脚本排除不相关模块的检测
- 持久性计数:适当设置
Afl.setPersistentCount - 白名单机制:只检测目标模块
9. 替代方案
- fpicker-aflpp-android:在应用上下文中进行模糊测试
- AFL++ with QEMU模式:另一种Android模糊测试解决方案
10. 结论
AFL++ Frida模式为Android灰盒模糊测试提供了:
- 灵活的配置选项
- 相对简单的实现方式
- 对JNI函数的良好支持
- 通过覆盖率反馈提高效率
注意:目标与Java元素的交互越多,性能影响越大,需要针对具体场景进行优化。