概述
Fuzz Android Native库
https://github.com/vanhauser-thc/AFLplusplus/tree/master/qbdi_mode
__int64 __fastcall Java_com_whatsapp_Mp4Ops_mp4check(JNIEnv_ *a1, __int64 a2, __int64 a3, char a4)
{
// 转换UTF-8字符串到 C 字符串
fpath = (a1->functions->GetStringUTFChars)(a1, v5, 0LL);
clock_gettime(1, &tp);
// 目标函数, 处理视频文件
v8 = sub_79C70(fpath, &tp, 2 * (v4 == 0), 0);
a1: JNIEnv
a2: jobject
a3: 文件路径
a4: 一个bool变量,为0或者1
void *offset_func = dlsym(handle, "Java_com_whatsapp_Mp4Ops_mp4check");
if (NULL == offset_func) {
printf("getprocaddress errorn");
return 1;
}
p_target_func = (target_func)((unsigned char *)offset_func + 0x45af0);
printf("target function addr: %xn", p_target_func);
QBDI_NOINLINE int fuzz_func() {
if (afl_setup()) { afl_forkserver(); }
unsigned long len = 0;
char * data = read_file(FPATH, &len);
printf("In fuzz_funcn");
struct timespec tp;
clock_gettime(1, &tp);
p_target_func(FPATH, &tp, 1);
printf("execute p_target_func:%pn", p_target_func);
exit(0);
return 1;
}
# find / -name libwhatsapp.so 2>o
/data/app/com.whatsapp-wMSOMeRwydbzJJmi-G1wEw==/lib/x86_64/libwhatsapp.so
# ls ./libQBDI.so
./libQBDI.so
# pwd
/data/lsl
# export LD_LIBRARY_PATH=/data/lsl:/data/app/com.whatsapp-wMSOMeRwydbzJJmi-G1wEw==/lib/x86_64/
# ./loader /data/app/com.whatsapp-wMSOMeRwydbzJJmi-G1wEw==/lib/x86_64/libwhatsapp.so xxx.mp4
target function addr: 7eaf9c70
In fuzz_func
execute p_target_func:0x702f7eaf9c70
./afl-fuzz -i mp4in/ -o mp4out -m 5000 -t 3000 -p exploit -- ./loader /data/app/com.whatsapp-wMSOMeRwydbzJJmi-G1wEw==/lib/x86_64/libwhatsapp.so @@
基于unicorn的Fuzz
https://github.com/vanhauser-thc/AFLplusplus/tree/master/unicorn_mode
$ cd unicorn_mode
$ ./build_unicorn_support.sh
x86 demoorn
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v4; // rax
size_t v5; // [rsp+20h] [rbp-10h]
char *s; // [rsp+28h] [rbp-8h]
if ( argc <= 1 )
return -1;
s = (char *)argv[1];
v5 = strlen(argv[1]);
if ( v5 <= 0x13 )
return -2;
while ( 1 )
{
v4 = v5--;
if ( !v4 )
break;
if ( v5 <= 0x11 && v5 > 2 && v5 <= 0x11 )
s[v5] = s[v5 + 1]; // s 不可写
}
if ( *s > 16 && *s <= 31 )
{
s[1];
s[2];
}
return 0;
}
int main(int argc, char **argv, char **envp) {
bool tracing = false;
// 首先获取输入文件的参数
char *filename = argv[1];
if (argc > 2 && !strcmp(argv[1], "-t")) {
tracing = true;
filename = argv[2];
}
// 创建一个unicorn实例, 架构是 x86(UC_ARCH_X86), 64位(UC_MODE_64)
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u (%s)n",
err, uc_strerror(err));
return -1;
}
// 首先读取二进制程序到内存
off_t len = afl_mmap_file(BINARY_FILE, &file_contents);
// 在unicorn中映射内存
mem_map_checked(uc, BASE_ADDRESS, len, UC_PROT_ALL);
// 最后将文件的内容写入刚刚映射的内存
if (uc_mem_write(uc, BASE_ADDRESS, file_contents, len) != UC_ERR_OK) {
printf("Error writing to CODEn");
}
// 释放host的内存
munmap(file_contents, len);
uint64_t start_address = CODE_ADDRESS; // main 函数的地址
uint64_t end_address = END_ADDRESS; // main函数的最后一条指令
// 设置 RIP 寄存器的值
uc_reg_write(uc, UC_X86_REG_RIP, &start_address); // address of entry point of main()
// 映射一块可读、可写的内存用来当作栈
mem_map_checked(uc, STACK_ADDRESS - STACK_SIZE, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE);
// 将 RSP 设置为刚刚分配到的内存地址
uint64_t stack_val = STACK_ADDRESS;
uc_reg_write(uc, UC_X86_REG_RSP, &stack_val);
// 映射一块内存用来存放输入数据, INPUT_LOCATION 为 0x10000
mem_map_checked(uc, INPUT_LOCATION, INPUT_SIZE_MAX, UC_PROT_READ);
// 往INPUT_LOCATION+8处存放一个指针,用作argv[1]
uc_mem_write(uc, INPUT_LOCATION + 8, "x16x00x01", 3);
// 设置 argv 为 INPUT_LOCATION, argc为2
uc_reg_write(uc, UC_X86_REG_RSI, &INPUT_LOCATION); // argv
uc_reg_write(uc, UC_X86_REG_RDI, &EMULATED_ARGC); // argc == 2
INPUT_LOCATION + 0:NULL, argv[0]
INPUT_LOCATION + 8: INPUT_LOCATION + 16, argv[1]
INPUT_LOCATION + 16:存放 argv[1] 的数据
.text:0000000000101168 mov rdi, rax ; s
.text:000000000010116B call _strlen
.text:0000000000101170 mov [rbp+var_10], rax
// Add our strlen hook (for this specific testcase only)
int strlen_hook_pos = BASE_ADDRESS + 0x116b;
uc_hook strlen_hook;
uc_hook_add(uc, &strlen_hook, UC_HOOK_CODE, hook_strlen, NULL, strlen_hook_pos, strlen_hook_pos);
static void hook_strlen(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
uint64_t addr;
uint32_t count = 0;
char c = 0;
// 首先获取 strlen 的入参
uc_reg_read(uc, UC_X86_REG_RDI, &addr);
while(1)
{
// 读一个字节
uc_err ret = uc_mem_read(uc, addr, &c, 1);
if(ret != UC_ERR_OK)
{
break;
}
// 搜索字符串的结尾
if(c == 'x00')
{
break;
}
addr++;
count++;
}
printf("strlen ret: %dn", count);
// 通过修改 RAX 来设置strlen的返回值
uc_reg_write(uc, UC_X86_REG_RAX, &count);
// 通过修改 RIP 来忽略 call _strlen 指令的执行
uint64_t next_addr = address + size;
uc_reg_write(uc, UC_X86_REG_RIP, &next_addr);
}
uc_afl_ret afl_ret = uc_afl_fuzz(
uc, // unicorn实例
filename, // 输入文件,每一轮Fuzz时uc_afl_fuzz里面会读取文件内容到host的内存
place_input_callback, // 一个回调函数,用于把刚刚读入的文件内容设置成目标程序获取数据
&end_address, // 结束地址数组
1, // 结束地址数组的长度
crash_handler, // 用于在crash时或者每次此时完成后调用
false, // 如果为true每次测试结束都会调用该函数来判断是否发生了crash.
1, // 指定执行多少次后才新fork进程,类比AFL的persistent模式,为1的话表示每次都fork新进程来测试
NULL // additional data pointer
);
switch(afl_ret) {
case UC_AFL_RET_ERROR:
printf("Error starting to fuzz");
return -3;
break;
case UC_AFL_RET_NO_AFL:
printf("No AFL attached - We are done with a single run.");
break;
default:
break;
}
static bool place_input_callback(
uc_engine *uc,
char *input,
size_t input_len,
uint32_t persistent_round,
void *data
){
// 如果数据太长,返回false,表示忽略这个测试用例
if (input_len < 1 || input_len >= INPUT_SIZE_MAX - INPUT_OFFSET) {
return false;
}
//设置寄存器状态,因为如果是persistent模式的话需要重复执行同一个函数,所以参数和 RIP, RSP需要设置
uc_reg_write(uc, UC_X86_REG_RIP, &CODE_ADDRESS);
uc_reg_write(uc, UC_X86_REG_RSI, &INPUT_LOCATION);
uc_reg_write(uc, UC_X86_REG_RDI, &EMULATED_ARGC);
uc_reg_write(uc, UC_X86_REG_RSP, &STACK_ADDRESS);
// 设置末尾为 x00
input[input_len-1] = ' ';
// 设置测试数据到 unicorn中
uc_mem_write(uc, INPUT_LOCATION + INPUT_OFFSET, input, input_len);
// 返回 true表示用例已经设置完毕,可以开始测试
return true;
}
$ cd AFLplusplus/unicorn_mode/samples/c
$ cc -w -I../../unicornafl/include -g -g -c harness.c
$ cc -L../../unicornafl harness.o ../../unicornafl/libunicornafl.a -L../../unicornafl -lpthread -lm -g -lrt -o harness
~/AFLplusplus/afl-fuzz -U -i in -o out -- ./harness @@
https://github.com/vanhauser-thc/AFLplusplus/blob/master/unicorn_mode/samples/c/harness.c
mips with elf loader
https://github.com/PAGalaxyLab/uniFuzzer
int main(int argc, char **argv) {
char *target = getenv("UF_TARGET"); // 主程序的路径
char *preload = getenv("UF_PRELOAD"); // preload的路径
char *libPath = getenv("UF_LIBPATH"); // 库搜索路径,下面加载 target 会在libPath中搜索库
// 数据输入文件,用于接收 AFL 生成的变异数据
char* filename = argv[1];
// 加载 target 和 preload,并且处理好符号重定位
uc = loadELF(target, preload, libPath);
// 初始化一些内存
uc_init_mem(uc);
uc_afl_ret afl_ret = uc_afl_fuzz(
uc, // The unicorn instance we prepared
filename, // Filename of the input to process. In AFL this is usually the '@@' placeholder, outside it's any input file.
place_input_callback, // Callback that places the input (automatically loaded from the file at filename) in the unicorninstance
&END, // Where to exit (this is an array)
1, // Count of end addresses
crash_handler, // Optional calback to run after each exec
true, // true, if the optional callback should be run also for non-crashes
1000000, // For persistent mode: How many rounds to run
NULL // additional data pointer
);
switch(afl_ret) {
case UC_AFL_RET_ERROR:
printf("Error starting to fuzzn");
return -3;
break;
case UC_AFL_RET_NO_AFL:
printf("No AFL attached - We are done with a single run.n");
break;
default:
break;
}
int uc_init_mem(uc_engine *uc) {
// 分配内存用于 heap
heapBase = mmap(NULL, HEAP_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
if(uc_mem_map_ptr(uc, heapBase, HEAP_SIZE, UC_PROT_READ | UC_PROT_WRITE, heapBase) != UC_ERR_OK) {
fprintf(stderr, "uc mapping heap failedn");
return 1;
}
// 分配内存用于栈
stackTop = mmap(NULL, STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
if(uc_mem_map_ptr(uc, stackTop, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE, stackTop) != UC_ERR_OK) {
fprintf(stderr, "uc mapping stack failedn");
return 1;
}
// 分配空间,用于存放 AFL 测试数据,最终会被目标函数处理
dataAddr = mmap(NULL, DATA_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0);
if(uc_mem_map_ptr(uc, dataAddr, DATA_SIZE, UC_PROT_READ | UC_PROT_WRITE, dataAddr) != UC_ERR_OK) {
fprintf(stderr, "uc mapping data failedn");
return 1;
}
return 0;
}
static bool place_input_callback(
uc_engine *uc,
char *input,
size_t input_len,
uint32_t persistent_round,
void *data
){
if(input_len < 0)
{
return false;
}
if(input_len > 255)
{
input_len = 255;
}
// 设置寄存器的值,包括栈寄存器SP, 参数寄存器A0,PC、RA、T9等
uint32_t reg;
reg = stackTop+STACK_SIZE - 0x200;
uc_reg_write(uc, UC_MIPS_REG_SP, ®);
reg = dataAddr;
uc_reg_write(uc, UC_MIPS_REG_A0, ®);
reg = START;
uc_reg_write(uc, UC_MIPS_REG_T9, ®);
reg = RA;
uc_reg_write(uc, UC_MIPS_REG_RA, ®);
uc_reg_write(uc, UC_MIPS_REG_PC, &START);
// 设置 demo-libcpreload.so 里面的heapBoundaryGOT, 用于子实现的malloc使用
*heapBoundaryGOT = heapBase;
// 拷贝AFL的测试数据到 unicorn
memcpy(dataAddr, input, input_len);
return true;
}
gcc -DUF_DEBUG test.c -w -I../../unicornafl/include -g -IelfLoader elfLoader/elfLoader.c elfLoader/arm.c elfLoader/i386.c elfLoader/mips.c elfLoader/dl-hash.c -L../../unicornafl -lpthread -g -lrt ../../unicornafl/libunicornafl.a -lm -o test
UF_TARGET=/home/hac425/AFLplusplus/unicorn_mode/samples/c/demo-vuln UF_PRELOAD=/home/hac425/AFLplusplus/unicorn_mode/samples/c/demo-libcpreload.so UF_LIBPATH=/usr/mipsel-linux-gnu/lib/ ./test init.txt
UF_TARGET=/home/hac425/AFLplusplus/unicorn_mode/samples/c/demo-vuln UF_PRELOAD=/home/hac425/AFLplusplus/unicorn_mode/samples/c/demo-libcpreload.so UF_LIBPATH=/usr/mipsel-linux-gnu/lib/ ~/AFLplusplus/afl-fuzz -U -i in -o out -- ./test @@
http://galaxylab.com.cn/%e5%9f%ba%e4%ba%8eunicorn%e5%92%8clibfuzzer%e7%9a%84%e6%a8%a1%e6%8b%9f%e6%89%a7%e8%a1%8cfuzzing/
总结
☆ END ☆
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论