Background
TrapFuzz的思路Fuzzing Android native库,这就是个简单的Demo,只针对黑盒的库。
honggfuzz on Android
- 设置好NDK路径
brew install automake
构建所有的arch(arm64-v8a, armeabi, armeabi-v7a, x86, x86_64)
坑1: libunwind编译的各种问题:
macos不好使,换linux去编译,然后用ndk r20.
传到手机上试试看:
然后就是写个demo,在手机上跑一下看看情况
hfuzz-cc is missing on android build · Issue #341 · google/honggfuzz
No coverage information on android · Issue #342 · google/honggfuzz
参数 fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls
这个参数的话会有警告信息,应该是clang 参数的问题。
后面参考了谷歌的文档,替换了参数,结果没警告了,但是cov还是0.
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 31 32 33 34 35 36 37
| $ cat Android.mk LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_PATH = . LOCAL_MODULE := hfuzz LOCAL_EXPORT_C_INCLUDES := $HOME/honggfuzz/includes LOCAL_SRC_FILES := /home/muhe/honggfuzz/libs/arm64-v8a/libhfuzz.a LOCAL_ARM_MODE := arm include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS) LOCAL_PATH = . LOCAL_MODULE := hfuzzcommon LOCAL_EXPORT_C_INCLUDES := $HOME/honggfuzz/includes LOCAL_SRC_FILES := /home/muhe/honggfuzz/obj/local/arm64-v8a/libcommon.a LOCAL_ARM_MODE := arm include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS) LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon LOCAL_SRC_FILES := fuzz_test.c LOCAL_MODULE := fuzz_test LOCAL_ARM_MODE := arm
include $(BUILD_EXECUTABLE)
$ cat Application.mk APP_BUILD_SCRIPT := ./Android.mk APP_STL := c++_shared APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer APP_LDFLAGS := -fsanitize=address
$
|
退回到honggfuzz 2.2 然后用最开始 #342 那个issue的编译参数是可以的
完整项目:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| $ cat Android.mk LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_PATH = . LOCAL_MODULE := hfuzz LOCAL_EXPORT_C_INCLUDES := $HOME/honggfuzz/includes LOCAL_SRC_FILES := /home/muhe/honggfuzz/libs/arm64-v8a/libhfuzz.a include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS) LOCAL_PATH = . LOCAL_MODULE := hfuzzcommon LOCAL_EXPORT_C_INCLUDES := $HOME/honggfuzz/includes LOCAL_SRC_FILES := /home/muhe/honggfuzz/obj/local/arm64-v8a/libcommon.a include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS) LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon LOCAL_SRC_FILES := fuzz_test.c LOCAL_MODULE := fuzz_test LOCAL_CFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls LOCAL_LD_FLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls
include $(BUILD_EXECUTABLE)
$ cat Application.mk APP_BUILD_SCRIPT := ./Android.mk
$ cat fuzz_test.c
int test_target(char* input_file_path, char* argv_0) { char *crash = NULL; FILE *fp = fopen(input_file_path, "rb"); char c; if (!fp) { printf("Error opening file\\n"); return 0; } if (fread(&c, 1, 1, fp) != 1) { printf("Error reading file\\n"); fclose(fp); return 0; } if (c != 't') { printf("Error 1\\n"); fclose(fp); return 0; } if (fread(&c, 1, 1, fp) != 1) { printf("Error reading file\\n"); fclose(fp); return 0; } if (c != 'e') { printf("Error 2\\n"); fclose(fp); return 0; } if (fread(&c, 1, 1, fp) != 1) { printf("Error reading file\\n"); fclose(fp); return 0; } if (c != 's') { printf("Error 3\\n"); fclose(fp); return 0; } if (fread(&c, 1, 1, fp) != 1) { printf("Error reading file\\n"); fclose(fp); return 0; } if (c != 't') { printf("Error 4\\n"); fclose(fp); return 0; } printf("!!!!!!!!!!OK!!!!!!!!!!\\n");
if (fread(&c, 1, 1, fp) != 1) { printf("Error reading file\\n"); fclose(fp); return 0; } if (c == '1') { // cause a crash crash[0] = 1; } else if (c == '2') { char buffer[5] = { 0 }; // stack-based overflow to trigger the GS cookie corruption for (int i = 0; i < 5; ++i) strcat(buffer, argv_0); printf("buffer: %s\\n", buffer); } else { printf("Error 5\\n"); } fclose(fp); return 0; }
int main(int argc, char** argv) { if(argc < 2) { printf("Usage: %s <input file>\\n", argv[0]); return 0; }
//regular single target call return test_target(argv[1], argv[0]); }
|
ndk构建命令:
1
| ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk TARGET_ARCH_ABI=arm64-v8a
|
write harness for .so
使用native-harness-target
参考项目 : https://github.com/CalebFenton/native-harness-target
Android 7.1.1
可以使用这个方式跑起来,不过速度及其的慢。
TODO :
- 速度问题,考虑docker Android或者qemu-kvm
- port 到 Android 10
Android 10 :
rednaga/native-shim
1 2 3 4 5 6 7 8 9 10 11 12
| export LD_LIBRARY_PATH=`pwd`:/apex/com.android.runtime/lib::$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=`pwd`:/apex/com.android.runtime/lib64:$LD_LIBRARY_PATH 255|walleye:/data/local/tmp [*] native-shim - diff [+] Attempting to load : [ libstr-crypt.so ] [+] Library Loaded! [+] Initializing JavaVM Instance [+] Initialization success (vm=0x74eb6901c0, env=0x74eb6e06c0) [+] Found JNI_OnLoad, attempting to call [+] Closing library walleye:/data/local/tmp
|
libpl_droidsonroids_gif.so测试
work with honggfuzz
经典的 patch 跳转指令,实现一个debugger来获取覆盖率的方案
get all JUMP INS
获取patch需要patch的指令地址,直接从p0tools里抄
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import idautils import idaapi import ida_nalt import idc
from os.path import expanduser home = expanduser("~")
patchpoints = set()
max_offset = 0 for seg_ea in idautils.Segments(): name = idc.get_segm_name(seg_ea) if name != ".text": continue
start = idc.get_segm_start(seg_ea) end = idc.get_segm_end(seg_ea) print(hex(start), hex(end)) for func_ea in idautils.Functions(start, end): f = idaapi.get_func(func_ea) if not f: continue for block in idaapi.FlowChart(f): if start <= block.start_ea < end: max_offset = max(max_offset, block.start_ea) patchpoints.add(block.start_ea) else: print("Warning, broken CFG?")
size = max_offset rem = size % 0x1000 if rem != 0: size += 0x1000 - rem
with open(home + "/Desktop/patches.txt", "w") as f: f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\\n') f.write('\\n'.join(map(hex, sorted(patchpoints)))) f.write('\\n')
print("Done, found {} patchpoints".format(len(patchpoints)))
|
Patch or hook INS
问题 : 需要想办法做到 hfuzzcc一样的效果,即 把 libhfuzz.a 链接进目标binary,不然没有桩信息。
看起来就是一层wrapper,给clang/gcc编译的时候增加了 CFLAGS
和 LDFLAGS
,看起来只需要按照需求把参数放到 Android.mk
即可。
这里参考ImageIO例子中的编译的参数
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 31 32 33 34 35 36 37 38 39 40 41 42 43
| cc -I/Users/vuln_test/honggfuzz/includes/ -Wno-unused-command-line-argument -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls -mllvm -sanitizer-coverage-prune-blocks=1 -fno-inline -fno-builtin -fno-omit-frame-pointer -D__NO_STRING_INLINES
-DHFND_FUZZING_ENTRY_FUNCTION_CXX(x,y)=extern const char* LIBHFNETDRIVER_module_netdriver;const char** LIBHFNETDRIVER_tmp1 = &LIBHFNETDRIVER_module_netdriver;extern "C" int HonggfuzzNetDriver_main(x,y);int HonggfuzzNetDriver_main(x,y)
-DHFND_FUZZING_ENTRY_FUNCTION(x,y)=extern const char* LIBHFNETDRIVER_module_netdriver;const char** LIBHFNETDRIVER_tmp1 = &LIBHFNETDRIVER_module_netdriver;int HonggfuzzNetDriver_main(x,y);int HonggfuzzNetDriver_main(x,y)
-Wl,-U,_HonggfuzzNetDriver_main -Wl,-U,_LIBHFUZZ_module_instrument -Wl,-U,_LIBHFUZZ_module_memorycmp **-o runner** **runner.m** -framework Foundation -framework CoreGraphics -framework AppKit /tmp/libhfnetdriver.501.7140081f7cd58e92.a /tmp/**libhfuzz**.501.2fdc27091cd8b54d.a /tmp/libhfuzz.501.a5556386f906dc80.a -pthread -ldl include $(CLEAR_VARS) LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon LOCAL_SRC_FILES := fuzz_test.c LOCAL_MODULE := fuzz_test LOCAL_CFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls \\ -fno-omit-frame-pointer -fno-inline -fno-builtin \\ -fno-omit-frame-pointer -Wl,-u,_LIBHFUZZ_module_instrument -Wl,-u,_LIBHFUZZ_module_memorycmp -ldl
LOCAL_LDFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls \\ -fno-inline -fno-builtin \\ -fno-omit-frame-pointer -Wl,-u,_LIBHFUZZ_module_instrument -Wl,-u,_LIBHFUZZ_module_memorycm
|
harness里需要主动调用 initializeTrapfuzz()
看起来一切都不错!
获取指定so地址也有了:
参考这里修改honggfuzz的代码即可
后续&问题
这个方案主要是效率实在是太差了,性能损耗都在jvm获取那里了,本来也是工作之余的一个小点子,后面也没深入去看了,个人最开始的想法是 winafl模式搬到安卓上 lol…
参考
https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html
https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/trapfuzz.patch