AFL之llvm mode部分源码分析
Posted 看雪学院
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AFL之llvm mode部分源码分析相关的知识,希望对你有一定的参考价值。
本文为看雪论坛优秀文章
看雪论坛作者ID:ScUpax0s
afl-clang-fast.c源码阅读
main
find_obj(argv[0])
edit_params(argc, argv)
1. 传统模式:使用afl-llvm-pass.so注入来插桩。
2. 'trace-pc-guard' mode:使用原生的 LLVM instrumentation callbacks
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
if (!strcmp(cur, "-m32")) bit_mode = 32;
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
if (!strcmp(cur, "-m64")) bit_mode = 64;
if (!strcmp(cur, "-x")) x_set = 1;
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
if (!strcmp(cur, "-Wl,-z,defs") ||
!strcmp(cur, "-Wl,--no-undefined")) continue;
-D__AFL_LOOP(_A)=
({ static volatile char *_B __attribute__((used));
_B = (char*)
__attribute__((visibility("default")))int _L(unsigned int) __asm__("___afl_persistent_loop");
_L(_A); })
-D__AFL_INIT()=
do {
static volatile char *_A __attribute__((used));
_A = (char*)
__attribute__((visibility("default")))void _I(void) __asm__("___afl_manual_init");
_I(); } while (0)
32位:obj_path/afl-llvm-rt-32.o
64位:obj_path/afl-llvm-rt-64.o
如果没有特别设置:obj_path/afl-llvm-rt.o
afl-llvm-pass.so.cc源码阅读
https://zhuanlan.zhihu.com/p/122522485
https://llvm.org/docs/WritingAnLLVMPass.html#introduction-what-is-a-pass
在AFL中只有一个Pass:
namespace {
class AFLCoverage : public ModulePass {
public:
static char ID;
AFLCoverage() : ModulePass(ID) { }
bool runOnModule(Module &M) override;
};
}
LLVMContext &C = M.getContext();
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
char be_quiet = 0;
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-llvm-pass " cBRI VERSION cRST " by <lszekeres@google.com> ");
} else be_quiet = 1;
/* Decide instrumentation ratio */
char* inst_ratio_str = getenv("AFL_INST_RATIO");
unsigned int inst_ratio = 100;
if (inst_ratio_str) {
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio ||
inst_ratio > 100)
FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)");
}
GlobalVariable *AFLMapPtr =
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
GlobalVariable *AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
BasicBlock::iterator IP = BB.getFirstInsertionPt();
IRBuilder<> IRB(&(*IP));
if (AFL_R(100) >= inst_ratio) continue;
/* Make up cur_loc */
unsigned int cur_loc = AFL_R(MAP_SIZE);
ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc)
LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc);
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
/* Load SHM pointer */
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *MapPtrIdx =
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc))
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
/* Set prev_loc to cur_loc >> 1 */
StoreInst *Store =
IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
else OKF("Instrumented %u locations (%s mode, ratio %u%%).",
inst_blocks, getenv("AFL_HARDEN") ? "hardened" :
((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
"ASAN/MSAN" : "non-hardened"), inst_ratio);
afl-llvm-rt.o.c源码阅读
deferred instrumentation
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
void __afl_manual_init(void) {
static u8 init_done;
if (!init_done) {
__afl_map_shm();
__afl_start_forkserver();
init_done = 1;
}
}
没有初始化,那么首先调用__afl_map_shm()设置共享内存。然后调用__afl_start_forkserver()起forkserver。最后设置为已经初始化。
/* SHM setup. */
static void __afl_map_shm(void) {
u8 *id_str = getenv(SHM_ENV_VAR);//通过环境变量读取id
/* If we're running under AFL, attach to the appropriate region, replacing the
early-stage __afl_area_initial region that is needed to allow some really
hacky .init code to work correctly in projects such as OpenSSL. */
if (id_str) { //如果读取成功
u32 shm_id = atoi(id_str);
__afl_area_ptr = shmat(shm_id, NULL, 0);//获取shm的地址为__afl_area_ptr
/* Whooooops. */
if (__afl_area_ptr == (void *)-1) _exit(1);
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us. */
__afl_area_ptr[0] = 1; //设置__afl_area_ptr[0]为1
}
}
static void __afl_start_forkserver(void) {
static u8 tmp[4];
s32 child_pid;
u8 child_stopped = 0;
/* Phone home and tell the parent that we're OK. If parent isn't there,
assume we're not running in forkserver mode and just execute program. */
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; //向状态管道写入4字节告知已启动
while (1) {
u32 was_killed;
int status;
/* Wait for parent by reading from the pipe. Abort if read fails. */
/* 当子进程超时,父进程会kill掉子进程 */
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
/* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old
process. */
/* 如果在persistent mode下,且子进程已经被killed */
if (child_stopped && was_killed) {
child_stopped = 0;
if (waitpid(child_pid, &status, 0) < 0) _exit(1);
}
if (!child_stopped) { //如果子进程真的彻底结束了
/* Once woken up, create a clone of our process. */
//重新fork一次
child_pid = fork();
if (child_pid < 0) _exit(1);
/* In child process: close fds, resume execution. */
//如果是fork出的子进程
if (!child_pid) {
close(FORKSRV_FD); //关闭对应描述符。然后返回执行真正的程序
close(FORKSRV_FD + 1);
return;
}
} else {
/* Special handling for persistent mode: if the child is alive but
currently stopped, simply restart it with SIGCONT. */
/* 如果子进程并非彻底结束而是暂停 */
/* 重新启动这个暂停的子进程 */
kill(child_pid, SIGCONT);
child_stopped = 0;
}
/* In parent process: write PID to pipe, then wait for child. */
//在父进程(fork-server)中,向afl-fuzzer写4字节(子进程pid)到管道,告知fuzzer
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1);
//读取子进程退出状态
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0)
_exit(1);
/* In persistent mode, the child stops itself with SIGSTOP to indicate
a successful run. In this case, we want to wake it up without forking
again. */
//子进程收到停止信号,此时子进程可能是停止或结束。
if (WIFSTOPPED(status)) child_stopped = 1; //child_stopped=1则不确定究竟是否彻底结束
//向状态管道写入4字节
if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1);
}
}
persistent mode
while (__AFL_LOOP(1000)) {
/* Read input data. */
/* Call library code to be fuzzed. */
/* Reset state. */
}
/* A simplified persistent mode handler, used as explained in README.llvm. */
int __afl_persistent_loop(unsigned int max_cnt) {
static u8 first_pass = 1;
static u32 cycle_cnt;
if (first_pass) {
if (is_persistent) {
memset(__afl_area_ptr, 0, MAP_SIZE);
__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;
}
cycle_cnt = max_cnt;
first_pass = 0;
return 1;
}
if (is_persistent) {
if (--cycle_cnt) {
raise(SIGSTOP);
__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;
return 1;
} else {
__afl_area_ptr = __afl_area_initial;
}
}
return 0;
}
当第一次运行到AFL_LOOP 时,进行初始化然后return 1,此时满足```while (AFL_LOOP(1000))```,于是执行一次fuzz。
trace-pc-guard mode
如果想使用这个功能需要设置:
AFL_TRACE_PC=1
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
It remains non-operational in the traditional, plugin-backed LLVM mode.
For more info about 'trace-pc-guard', see README.llvm.
The first function (__sanitizer_cov_trace_pc_guard) is called back on every
edge (as opposed to every basic block). */
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
__afl_area_ptr[*guard]++;
}
/* Init callback. Populates instrumentation IDs. Note that we're using
ID of 0 as a special value to indicate non-instrumented bits. That may
still touch the bitmap, but in a fairly harmless way. */
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
u32 inst_ratio = 100;
u8* x;
if (start == stop || *start) return;
x = getenv("AFL_INST_RATIO");
if (x) inst_ratio = atoi(x);
if (!inst_ratio || inst_ratio > 100) {
fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100). ");
abort();
}
/* Make sure that the first element in the range is always set - we use that
to avoid duplicate calls (which can happen as an artifact of the underlying
implementation in LLVM). */
*(start++) = R(MAP_SIZE - 1) + 1;
while (start < stop) {
if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;
else *start = 0;
start++;
}
}
*(start++) = R(MAP_SIZE - 1) + 1;
while (start < stop) {
if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;
else *start = 0;
start++;
}
看雪ID:ScUpax0s
https://bbs.pediy.com/user-home-876323.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
以上是关于AFL之llvm mode部分源码分析的主要内容,如果未能解决你的问题,请参考以下文章
技术分享AFL源码分析(III)——afl-fuzz分析(Part 1)
ollvm源码分析 - Pass之SplitBaiscBlocks
LLVM 之 Clang 静态分析器篇:程序缺陷诊断——除零错误
LLVM 之 Clang 静态分析器篇:程序缺陷诊断——内存泄露