Repository: airbus-cyber/afl_ghidra_emu Branch: master Commit: 751c0df730b1 Files: 13 Total size: 42.3 KB Directory structure: gitextract_9yizunl3/ ├── .gitignore ├── README.md ├── afl_bridge_external/ │ ├── Makefile │ ├── afl_bridge_external.c │ └── types.h ├── examples/ │ ├── ppc/ │ │ ├── bin/ │ │ │ └── keygenme_ppc.elf │ │ └── fuzz_ppc_check_serial.py │ └── xtensa/ │ ├── bin/ │ │ └── keygenme_xtensa.elf │ └── fuzz_xtensa_check_serial.py ├── libAFL/ │ ├── __init__.py │ └── libAFL.py └── libUtils/ ├── __init__.py └── libUtils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .pyc .class .log ================================================ FILE: README.md ================================================ # afl_ghidra_emu [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) afl_ghidra_emu is no longer maintained and replaced by **[Ghidralligator](https://github.com/airbus-cyber/ghidralligator). Try it!** afl_ghidra_emu allows to fuzz exotic architecture using AFL++ and Ghidra emulation with code coverage functionality. For more information, read this [article](https://github.com/airbus-cyber/blogpost/tree/main/fuzzing-exotic-arch-with-afl-using-ghidra-emulator).

## How it works? AFL++ runs a trampoline program (afl_bridge_external) which is in charge of forwarding samples to Ghidra emulation via a TCP socket (Ex: 127.0.0.1:6674/tcp). A python script in Ghidra (fuzz_xtensa_check_serial.py) is responsible for emulating code execution. It listens on a TCP socket (127.0.0.1:6674/tcp) and waits for input data coming from trampoline script. As soon as the script receives input data, the emulation will be started. During the execution, the executed path addresses, and the execution status are sent to afl_bridge_external using established TCP socket. afl_bridge_external reports the execution status and execution path to AFL++ using pipes and shared memory. ## Installation Install [AFL++](https://github.com/AFLplusplus/AFLplusplus) Clone afl_ghidra_emu directory ```bash git clone https://github.com/airbus-cyber/afl_ghidra_emu.git ``` Compile afl_bridge_external ``` cd afl_ghidra_emu/afl_bridge_external make ``` Copy afl_ghidra_emu files to your ghidra script directory ```bash cd ../.. cp –r afl_ghidra_emu/* $USER_HOME/ghidra_scripts/ ``` ## Example: Fuzzing Xtensa binary code keygenme_xtensa.elf ./examples/xtensa/bin/keygenme_xtensa.elf is a *keygenMe* compiled for Xtensa (ex: esp32) architecture. Xtensa is not officially supported in Ghidra yet. So, you need first to install it by following these [instruction](https://github.com/Ebiroll/ghidra-xtensa) #### Load in Ghidra - Create a new project in Ghidra; - Import file ./bin/keygenme_xtensa.elf (arch: Xtensa:LE:32); - Open it in CodeBrowser and execute auto-analyze; - Open Script Manager in "Window" submenu; - Run script fuzz_xtensa_check_serial.py; #### Start Fuzz Make AFL workspace directories ```bash mkdir input output ``` Add first sample ```bash echo -n "BBBBBBBB" > input/sample1.bin ``` Start AFL++ with trampoline program. ```bash afl-fuzz -D -i input -o output afl_bridge_external 127.0.0.1 6674 20 ``` #### Stop Ghidra emulation Stop AFL++ using CTRL+C. If Ghidra emulation still running, we can send "STOP" command: ```bash echo -e "\xff" | nc 127.0.0.1 6674 ``` Do no use Ghidra Cancel button, because it does not properly close the socket. ## Example: Fuzzing PPC binary code keygenme_ppc.elf ./examples/ppc/bin/keygenme_ppc.elf is also a *keygenMe* compiled for PowerPC architecture. Follow the same steps above with PowerPC:BE:32:default architecture in Ghidra and run the script fuzz_ppc_check_serial.py. ================================================ FILE: afl_bridge_external/Makefile ================================================ all: afl_bridge_external afl_bridge_external: afl_bridge_external.c $(CC) -I../../include -o afl_bridge_external afl_bridge_external.c clean: rm -f afl_bridge_external *~ core ================================================ FILE: afl_bridge_external/afl_bridge_external.c ================================================ /* american fuzzy lop++ - afl_brifge_external --------------------------------------------------- Written by Flavian Dola Copyright 2021 by Airbus CyberSecurity. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. Adapted from afl_proxy (https://github.com/AFLplusplus/AFLplusplus/blob/stable/utils/afl_proxy/afl-proxy.c) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 */ #include "types.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FORKSRV_FD 198 #define MAP_SIZE_POW2 16 #define MAP_SIZE (1U << MAP_SIZE_POW2) #define SHM_ENV_VAR "__AFL_SHM_ID" u8 *__afl_area_ptr; u32 __afl_map_size = MAP_SIZE; unsigned long afl_prev_loc = 0; int isSocketServerRunning = 0; int sockfd = -1; int connfd = -1; FILE* logfd; u32 id_sample = 0; #define CONFIG 0x2 #define TRACE 0x3 #define STOP 0xff #define CRASH 0xfe #define END 0xfd #define ERR 0xfc #define EXEC_END_OK 1 #define EXEC_CRASH 2 #define EXEC_ERR -1 #define EXEC_UNK -2 #define CLOSE_SOCKET(sock)({if (sock != -1) {close(sock); sock = -1;} }) #define MAX_SZ_SAMPLE 0x7fff #define SA struct sockaddr /* Error reporting to forkserver controller */ void send_forkserver_error(int error) { u32 status; if (!error || error > 0xffff) return; status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; } /* SHM setup. */ static void __afl_map_shm(void) { char *id_str = getenv(SHM_ENV_VAR); char *ptr; /* NOTE TODO BUG FIXME: if you want to supply a variable sized map then uncomment the following: */ /* if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { u32 val = atoi(ptr); if (val > 0) __afl_map_size = val; } */ if (__afl_map_size > MAP_SIZE) { if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { fprintf(stderr, "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " "be able to run this instrumented program!\n", __afl_map_size); if (id_str) { send_forkserver_error(FS_ERROR_MAP_SIZE); exit(-1); } } else { fprintf(stderr, "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " "be able to run this instrumented program!\n", __afl_map_size); } } if (id_str) { #ifdef USEMMAP const char * shm_file_path = id_str; int shm_fd = -1; unsigned char *shm_base = NULL; /* create the shared memory segment as if it was a file */ shm_fd = shm_open(shm_file_path, O_RDWR, 0600); if (shm_fd == -1) { fprintf(stderr, "shm_open() failed\n"); send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); } /* map the shared memory segment to the address space of the process */ shm_base = mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shm_base == MAP_FAILED) { close(shm_fd); shm_fd = -1; fprintf(stderr, "mmap() failed\n"); send_forkserver_error(FS_ERROR_MMAP); exit(2); } __afl_area_ptr = shm_base; #else u32 shm_id = atoi(id_str); __afl_area_ptr = shmat(shm_id, 0, 0); #endif if (__afl_area_ptr == (void *)-1) { send_forkserver_error(FS_ERROR_SHMAT); exit(1); } /* Write something into the bitmap so that the parent doesn't give up */ __afl_area_ptr[0] = 1; } } /* Fork server logic. */ static void __afl_start_forkserver(void) { u8 tmp[4] = {0, 0, 0, 0}; u32 status = 0; if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); if (status) status |= (FS_OPT_ENABLED); memcpy(tmp, &status, 4); /* Phone home and tell the parent that we're OK. */ if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; } static u32 __afl_next_testcase(u8 *buf, u32 max_len) { s32 status, res = 0xffffff; /* Wait for parent by reading from the pipe. Abort if read fails. */ if (read(FORKSRV_FD, &status, 4) != 4) return 0; /* we have a testcase - read it */ memset(buf, 0, max_len); status = read(0, buf, max_len); /* report that we are starting the target */ if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; return status; } static void __afl_end_testcase(int status) { //int status = 0xffffff; if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); } void clear_afl_trace() { afl_prev_loc = 0; } void afl_maybe_log(unsigned long cur_loc) { cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); unsigned long afl_idx = cur_loc ^ afl_prev_loc; afl_idx &= __afl_map_size - 1; __afl_area_ptr[afl_idx]++; afl_prev_loc = cur_loc >> 1; } int flush_socket() { char c; int r = 1; while (r == 1) { r = recv(sockfd, &c, 1, SO_RCVTIMEO); } } int get_exec_info() { int r = 0; unsigned char buff[4]; u32 rcv_id = 0; clear_afl_trace(); for (;;) { r = recv(sockfd, buff, 1, 0); if (r != 1){ fprintf(logfd, "get_exec_info: Error on recv\n"); fflush(logfd); return(EXEC_ERR); } switch (buff[0]) { case END: r = recv(sockfd, &rcv_id, 4, 0); if (r != 4) { fprintf(logfd, "get_exec_info: (END) Error on rcv_id\n"); fflush(logfd); return(EXEC_ERR); } if (rcv_id != id_sample) { fprintf(logfd, "get_exec_info: (END) rcv_id (%d) not match id_sample (%d)\n", rcv_id, id_sample); fflush(logfd); return(EXEC_ERR); } // Remote execution ended without a crash return(EXEC_END_OK); case CRASH: r = recv(sockfd, &rcv_id, 4, 0); if (r != 4) { fprintf(logfd, "get_exec_info: (CRASH) Error on rcv_id\n"); fflush(logfd); return(EXEC_ERR); } if (rcv_id != id_sample) { fprintf(logfd, "get_exec_info: (CRASH) rcv_id (%d) not match id_sample (%d)\n", rcv_id, id_sample); fflush(logfd); return(EXEC_ERR); } // Remote execution ended with a crash return(EXEC_CRASH); case TRACE: r = recv(sockfd, &rcv_id, 4, 0); if (r != 4) { fprintf(logfd, "get_exec_info: (TRACE) Error on rcv_id\n"); fflush(logfd); return(EXEC_ERR); } if (rcv_id != id_sample) { fprintf(logfd, "get_exec_info: (TRACE) rcv_id (%d) not match id_sample (%d)\n", rcv_id, id_sample); fflush(logfd); return(EXEC_ERR); } r = recv(sockfd, buff, 4, 0); if (r != 4) { fprintf(logfd, "get_exec_info: Error on get exec trace\n"); fflush(logfd); return(EXEC_ERR); } afl_maybe_log(*(unsigned long*) buff); break; case ERR: if (rcv_id != id_sample) { fprintf(logfd, "get_exec_info: (ERR) error received\n"); fflush(logfd); return(EXEC_ERR); } default: fprintf(logfd, "get_exec_info: Error unknown receive code: 0x%X\nn", buff[0]); fflush(logfd); return(EXEC_UNK); } } } int connect_to_ext(char* pIpAddress, u32 port, u32 timeout_ms) { struct sockaddr_in servaddr; int res = 0; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { fprintf(logfd, "socket creation failed...\n"); fflush(logfd); return(res); } // set timeout struct timeval tv; tv.tv_sec = timeout_ms/1000; tv.tv_usec = (timeout_ms % 1000) * 1000; if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) != 0) { fprintf(logfd, "setsockopt creation failed...\n"); fflush(logfd); return(res); } bzero(&servaddr, sizeof(servaddr)); // assign IP, PORT servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(pIpAddress); servaddr.sin_port = htons(port); if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { fprintf(logfd, "connection with the server failed...\n"); fflush(logfd); CLOSE_SOCKET(sockfd); return(res); } res = 1; return res; } int send_all(void *data2send, size_t length) { int res = 0; char *ptr = (char*) data2send; while (length > 0) { int i = send(sockfd, ptr, length, 0); if (i < 1) { return(res); } ptr += i; length -= i; } res = 1; return(res); } int send_input_data(char* pInputData, u16 sz) { int res = 0; int offset = 0; char buf[1+4+sz+MAX_SZ_SAMPLE]; memset(buf, 0, sizeof(buf)); if (pInputData == 0) { return(res); } if (sz > MAX_SZ_SAMPLE) { sz = MAX_SZ_SAMPLE; } buf[offset] = CONFIG; offset++; *(u32*)(buf+offset) = id_sample; offset = offset + sizeof(u32); *(u16*)(buf+offset) = sz; offset = offset + sizeof(u16); memcpy(buf+offset, pInputData, sz); if (1 != send_all(buf, offset+sz)) { fprintf(logfd, "send_input_data: Send failed\n"); fflush(logfd); return(res); } /* Write something into the bitmap so that the parent doesn't give up */ __afl_area_ptr[0] = 1; res = 1; return(res); } void exit_with_segfault() { kill(getpid(), SIGSEGV); sleep(5); } void print_usage() { fprintf(stderr, "USAGE:\n afl_bridge_external IP PORT timeout_ms\n"); fprintf(logfd, "USAGE:\n afl_bridge_external IP PORT timeout_ms\n"); fflush(logfd); return; } int main(int argc, char *argv[]) { /* This is were the testcase data is written into */ u8 buf[MAX_SZ_SAMPLE]; s32 len; int res_exec = 0; // log to log logfd = fopen("./afl_bridge_external.log", "a"); if (logfd == 0) { fprintf(stderr, "Error open log file\n"); goto END_MAIN; } if (argc != 4) { fprintf(stderr, "Error: bad args\n"); fprintf(logfd, "Error: bad args\n"); print_usage(); goto END_MAIN; } if (1 != connect_to_ext(argv[1], atoi(argv[2]), atoi(argv[3])) ) { fprintf(stderr, "Error: Failed to connect %s:%s\n", argv[1], argv[2]); fprintf(logfd, "Error: Failed to connect %s:%s\n", argv[1], argv[2]); fflush(logfd); goto END_MAIN; } /* here you specify the map size you need that you are reporting to afl-fuzz. Any value is fine as long as it can be divided by 32. */ __afl_map_size = MAP_SIZE; // default is 65536 /* then we initialize the shared memory map and start the forkserver */ __afl_map_shm(); __afl_start_forkserver(); while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { id_sample++; if (1 != send_input_data(buf, (u16)len)){ fprintf(logfd, "Error on send input data\n"); goto END_MAIN; } res_exec = get_exec_info(); switch (res_exec) { case EXEC_ERR: fprintf(logfd, "EXEC_ERR: Error on collect execution info\n"); fflush(logfd); //goto END_MAIN; // TODO: to improve.... flush_socket(); __afl_end_testcase(0x0); break; case EXEC_END_OK: // remote execution ended // no crash detect __afl_end_testcase(0x0); break; case EXEC_CRASH: // remote execution crashed // report to AFL //exit_with_segfault(); __afl_end_testcase(0x0005); break; default: fprintf(logfd, "Unknown execution code %d\n", res_exec); fflush(logfd); goto END_MAIN; } } END_MAIN: fclose(logfd); CLOSE_SOCKET(sockfd); return 0; } ================================================ FILE: afl_bridge_external/types.h ================================================ /* american fuzzy lop++ - type definitions and minor macros -------------------------------------------------------- Originally written by Michal Zalewski Now maintained by Marc Heuse , Heiko Eißfeldt , Andrea Fioraldi , Dominik Maier Copyright 2016, 2017 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 */ #ifndef _HAVE_TYPES_H #define _HAVE_TYPES_H #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; #ifdef WORD_SIZE_64 typedef unsigned __int128 uint128_t; typedef uint128_t u128; #endif /* Extended forkserver option values */ /* Reporting errors */ #define FS_OPT_ERROR 0xf800008f #define FS_OPT_GET_ERROR(x) ((x & 0x00ffff00) >> 8) #define FS_OPT_SET_ERROR(x) ((x & 0x0000ffff) << 8) #define FS_ERROR_MAP_SIZE 1 #define FS_ERROR_MAP_ADDR 2 #define FS_ERROR_SHM_OPEN 4 #define FS_ERROR_SHMAT 8 #define FS_ERROR_MMAP 16 /* Reporting options */ #define FS_OPT_ENABLED 0x80000001 #define FS_OPT_MAPSIZE 0x40000000 #define FS_OPT_SNAPSHOT 0x20000000 #define FS_OPT_AUTODICT 0x10000000 #define FS_OPT_SHDMEM_FUZZ 0x01000000 #define FS_OPT_OLD_AFLPP_WORKAROUND 0x0f000000 // FS_OPT_MAX_MAPSIZE is 8388608 = 0x800000 = 2^23 = 1 << 22 #define FS_OPT_MAX_MAPSIZE ((0x00fffffeU >> 1) + 1) #define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1) #define FS_OPT_SET_MAPSIZE(x) \ (x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1)) typedef unsigned long long u64; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; #ifdef WORD_SIZE_64 typedef __int128 int128_t; typedef int128_t s128; #endif #ifndef MIN #define MIN(a, b) \ ({ \ \ __typeof__(a) _a = (a); \ __typeof__(b) _b = (b); \ _a < _b ? _a : _b; \ \ }) #define MAX(a, b) \ ({ \ \ __typeof__(a) _a = (a); \ __typeof__(b) _b = (b); \ _a > _b ? _a : _b; \ \ }) #endif /* !MIN */ #define SWAP16(_x) \ ({ \ \ u16 _ret = (_x); \ (u16)((_ret << 8) | (_ret >> 8)); \ \ }) #define SWAP32(_x) \ ({ \ \ u32 _ret = (_x); \ (u32)((_ret << 24) | (_ret >> 24) | ((_ret << 8) & 0x00FF0000) | \ ((_ret >> 8) & 0x0000FF00)); \ \ }) #define SWAP64(_x) \ ({ \ \ u64 _ret = (_x); \ _ret = \ (_ret & 0x00000000FFFFFFFF) << 32 | (_ret & 0xFFFFFFFF00000000) >> 32; \ _ret = \ (_ret & 0x0000FFFF0000FFFF) << 16 | (_ret & 0xFFFF0000FFFF0000) >> 16; \ _ret = \ (_ret & 0x00FF00FF00FF00FF) << 8 | (_ret & 0xFF00FF00FF00FF00) >> 8; \ _ret; \ \ }) // It is impossible to define 128 bit constants, so ... #ifdef WORD_SIZE_64 #define SWAPN(_x, _l) \ ({ \ \ u128 _res = (_x), _ret; \ char *d = (char *)&_ret, *s = (char *)&_res; \ int i; \ for (i = 0; i < 16; i++) \ d[15 - i] = s[i]; \ u32 sr = 128U - ((_l) << 3U); \ (_ret >>= sr); \ (u128) _ret; \ \ }) #endif #define SWAPNN(_x, _y, _l) \ ({ \ \ char *d = (char *)(_x), *s = (char *)(_y); \ u32 i, l = (_l)-1; \ for (i = 0; i <= l; i++) \ d[l - i] = s[i]; \ \ }) #ifdef AFL_LLVM_PASS #if defined(__linux__) || !defined(__ANDROID__) #define AFL_SR(s) (srandom(s)) #define AFL_R(x) (random() % (x)) #else #define AFL_SR(s) ((void)s) #define AFL_R(x) (arc4random_uniform(x)) #endif #else #if defined(__linux__) || !defined(__ANDROID__) #define SR(s) (srandom(s)) #define R(x) (random() % (x)) #else #define SR(s) ((void)s) #define R(x) (arc4random_uniform(x)) #endif #endif /* ^AFL_LLVM_PASS */ #define STRINGIFY_INTERNAL(x) #x #define STRINGIFY(x) STRINGIFY_INTERNAL(x) #define MEM_BARRIER() __asm__ volatile("" ::: "memory") #if __GNUC__ < 6 #ifndef likely #define likely(_x) (_x) #endif #ifndef unlikely #define unlikely(_x) (_x) #endif #else #ifndef likely #define likely(_x) __builtin_expect(!!(_x), 1) #endif #ifndef unlikely #define unlikely(_x) __builtin_expect(!!(_x), 0) #endif #endif #endif /* ! _HAVE_TYPES_H */ ================================================ FILE: examples/ppc/fuzz_ppc_check_serial.py ================================================ """ Copyright 2021 by Airbus CyberSecurity - Flavian Dola Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import struct import time from ghidra.app.emulator import EmulatorHelper from ghidra.program.model.address import GenericAddress from ghidra.util.task import ConsoleTaskMonitor from ghidra.program.model.block import BasicBlockModel from libAFL import libAFL from libUtils import libUtils # TCP listen PORT PORT = 6674 DEBUG = False REG_FILTER = [ "r1", "r2", "r3", "r4", "r9", "r31" ] D_REG = { "r1": "r1", "r2": "r2", "r3": "r3", "r4": "r4", "r9": "r9", "r31": "r31", } def write_memory(addr, data, sz = None): if type(addr) == GenericAddress: addr = libAFL.addr2int(addr) data_str = data if sz: if data < 0: raise ValueError("data must be positive") data_str = "" while data != 0: b = data & 0xff data_str += struct.pack("B", b) data = data >> 8 i = 0 while i < len(data_str): c = struct.unpack("B", data_str[i])[0] ghidra_addr = toAddr(addr+i) emuHelper.writeMemoryValue(ghidra_addr, 1, c) i += 1 return def apply_hooks(emuHelper, addr, debug=False): """ Apply hook if needed """ # n_addr = get_next_execution_addr(addr) bRes = None addr_int = libUtils.addr2int(addr) if addr_int in D_HOOKS.keys(): if debug: print(" * * * apply_hook: %s - %s" % (str(addr), D_HOOKS[addr_int]["name"])) bRes = D_HOOKS[addr_int]["callback"](emuHelper, addr) return bRes def hook_good_serial(emuHelper, addr): ret = emuHelper.readRegister("r3") if ret == 2: # force crash # => pc = 0 emuHelper.writeRegister(emuHelper.getPCRegister(), 0x0) return True return False D_HOOKS = { 0x1000071c:{ "name": "good_serial", "callback": hook_good_serial, }, } if __name__ == '__main__': emuHelper = EmulatorHelper(currentProgram) monitor = ConsoleTaskMonitor() bbm = BasicBlockModel(currentProgram) ctx = {} ctx = libAFL.init_ctx(ctx, monitor, bbm) res, ctx = libAFL.run_bridge_server_api(ctx, port=PORT) if not res: print("Error on listen on %d tcp port", PORT) exit(1) start_addr = 0x1000063c stop_addr = toAddr(0x1000071c) # Add new memory section to store emulate values addr_section_emu = 0x20000000 sz_emu = 0x100000 pInput = addr_section_emu count = 0 bFirstRun = True isRunning = True while isRunning: # reset previous block reached ctx = libAFL.init_ctx(ctx, monitor, bbm) res, ctx = libAFL.rcv_input(ctx, debug=DEBUG) if not res: if DEBUG: print("Error get config") res, ctx = libAFL.notify_err(ctx) continue if DEBUG: print("CONFIG: %s" % str(ctx)) if libAFL.isStopOrder(ctx): isRunning = False break # Do some stats if count % 1000 == 0: count = 0 if not bFirstRun: stat = 1000.0 / (time.time() - ref_time) print("Exec %d/s" % int(stat)) bFirstRun = False ref_time = time.time() count += 1 write_memory(pInput, libAFL.get_data_input(ctx)) szInput = len(libAFL.get_data_input(ctx)) # set register emuHelper.writeRegister("r3", pInput) emuHelper.writeRegister("r4", szInput) emuHelper.writeRegister(emuHelper.getPCRegister(), start_addr) # Emulation bCrash = False while True: if monitor.isCancelled(): break executionAddress = emuHelper.getExecutionAddress() if apply_hooks(emuHelper, executionAddress): continue if (executionAddress in [stop_addr]): if DEBUG: print("Emulation complete.") break # Print current instruction and the registers we care about if DEBUG: print("\n%s: %s" % (str(executionAddress).upper(), getInstructionAt(executionAddress))) res, ctx = libAFL.notify_code_coverage(ctx, executionAddress, debug=DEBUG) if not res: print("Error on notify_code_coverage") isRunning = False break if DEBUG: for reg in REG_FILTER: reg_value = emuHelper.readRegister(reg) print("\t{} ({}) =\t{:08X}".format(reg, D_REG[reg], reg_value)) # single step emulation success = emuHelper.step(monitor) if success == False: bCrash = True lastError = emuHelper.getLastError() print("Emulation Error: '{}'".format(lastError)) break # End of Emulation if bCrash: res, ctx = libAFL.notify_crash(ctx) else: res, ctx = libAFL.notify_end_exec(ctx) if not res: # Error on notify break # End of prog ctx = libAFL.free_ctx(ctx) ================================================ FILE: examples/xtensa/fuzz_xtensa_check_serial.py ================================================ """ Copyright 2021 by Airbus CyberSecurity - Flavian Dola Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import struct from ghidra.app.emulator import EmulatorHelper from ghidra.program.model.address import GenericAddress from ghidra.util.task import ConsoleTaskMonitor from ghidra.program.model.block import BasicBlockModel import time from libAFL import libAFL # TCP listen PORT PORT = 6674 DEBUG = False REG_FILTER = [ "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9" ] D_REG = { "a0": "a0", "a1": "a1", "a2": "a2", "a3": "a3", "a4": "a4", "a5": "a5", "a6": "a6", "a7": "a7", "a8": "a8", "a9": "a9", } def write_memory(addr, data, sz = None): if type(addr) == GenericAddress: addr = libAFL.addr2int(addr) data_str = data if sz: if data < 0: raise ValueError("data must be positive") data_str = "" while data != 0: b = data & 0xff data_str += struct.pack("B", b) data = data >> 8 i = 0 while i < len(data_str): c = struct.unpack("B", data_str[i])[0] ghidra_addr = toAddr(addr+i) emuHelper.writeMemoryValue(ghidra_addr, 1, c) i += 1 return def apply_hooks(emuHelper, addr, debug=False): """ Apply hook if needed """ # n_addr = get_next_execution_addr(addr) bRes = None addr_int = libAFL.addr2int(addr) if addr_int in D_HOOKS.keys(): if debug: print(" * * * apply_hook: %s - %s" % (str(addr), D_HOOKS[addr_int]["name"])) bRes = D_HOOKS[addr_int]["callback"](emuHelper, addr) return bRes def hook_good_serial(emuHelper, addr): ret = emuHelper.readRegister("a2") if ret == 2: # force crash # => pc = 0 emuHelper.writeRegister(emuHelper.getPCRegister(), 0x0) return True return False D_HOOKS = { 0x400e3120:{ "name": "good_serial", "callback": hook_good_serial, }, } if __name__ == '__main__': emuHelper = EmulatorHelper(currentProgram) monitor = ConsoleTaskMonitor() bbm = BasicBlockModel(currentProgram) ctx = {} ctx = libAFL.init_ctx(ctx, monitor, bbm) res, ctx = libAFL.run_bridge_server_api(ctx, port=PORT) if not res: print("Error on listen on %d tcp port", PORT) exit(1) start_addr = 0x400e30ab stop_addr = toAddr(0x400e3120) # Add new memory section to store emulate values addr_section_emu = 0x20000000 sz_emu = 0x100000 pInput = addr_section_emu count = 0 bFirstRun = True isRunning = True while isRunning: # reset previous block reached ctx = libAFL.init_ctx(ctx, monitor, bbm) res, ctx = libAFL.rcv_input(ctx, debug=DEBUG) if not res: if DEBUG: print("Error get config") res, ctx = libAFL.notify_err(ctx) continue if DEBUG: print("CONFIG: %s" % str(ctx)) if libAFL.isStopOrder(ctx): isRunning = False break # Do some stats if count % 1000 == 0: count = 0 if not bFirstRun: stat = 1000.0 / (time.time() - ref_time) print("Exec %d/s" % int(stat)) bFirstRun = False ref_time = time.time() count += 1 write_memory(pInput, libAFL.get_data_input(ctx)) szInput = len(libAFL.get_data_input(ctx)) # set register emuHelper.writeRegister("a2", pInput) emuHelper.writeRegister("a3", szInput) emuHelper.writeRegister(emuHelper.getPCRegister(), start_addr) # Emulation bCrash = False while True: if monitor.isCancelled(): break executionAddress = emuHelper.getExecutionAddress() if apply_hooks(emuHelper, executionAddress): continue if (executionAddress in [stop_addr]): if DEBUG: print("Emulation complete.") break # Print current instruction and the registers we care about if DEBUG: print("\n%s: %s" % (str(executionAddress).upper(), getInstructionAt(executionAddress))) res, ctx = libAFL.notify_code_coverage(ctx, executionAddress, debug=DEBUG) if not res: print("Error on notify_code_coverage") isRunning = False break if DEBUG: for reg in REG_FILTER: reg_value = emuHelper.readRegister(reg) print("\t{} ({}) =\t{:08X}".format(reg, D_REG[reg], reg_value)) # single step emulation success = emuHelper.step(monitor) if success == False: bCrash = True lastError = emuHelper.getLastError() print("Emulation Error: '{}'".format(lastError)) break # End of Emulation if bCrash: res, ctx = libAFL.notify_crash(ctx) else: res, ctx = libAFL.notify_end_exec(ctx) # End of prog ctx = libAFL.free_ctx(ctx) ================================================ FILE: libAFL/__init__.py ================================================ ================================================ FILE: libAFL/libAFL.py ================================================ #!/usr/bin/python2 """ Copyright 2021 by Airbus CyberSecurity - Flavian Dola Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import struct import socket from libUtils import libUtils CONFIG = "\x02" TRACE = "\x03" STOP = "\xff" CRASH = "\xfe" END = "\xfd" ERR = "\xfc" def run_bridge_server_api(ctx, host="127.0.0.1", port=6666): res = False s_bridge = None try: s_bridge = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s_bridge.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s_bridge.bind((host, port)) s_bridge.listen(1) ctx.update({"sock_bridge": s_bridge}) res = True except Exception as e: print("run_bridge_server_api failed: %s" % str(e)) if s_bridge is not None: s_bridge.close() return res, ctx def parse_cmd(ctx, data, debug=False): res = False if len(data) < 1: # too short if debug: print("parse_cmd: Data too short: %d\n" % len(data)) return res, ctx offset = 0 if data[0] == CONFIG: offset += 1 if len(data[offset:]) < 6: if debug: print("parse_cmd: data invalid length: %d\n" % len(data[offset:])) return res, ctx id_sample = struct.unpack("> 8 api = FlatProgramAPI(emuHelper.getProgram()) i = 0 while i < len(data_str): c = struct.unpack("B", data_str[i])[0] ghidra_addr = api.toAddr(addr+i) emuHelper.writeMemoryValue(ghidra_addr, 1, c) i += 1 del api return def get_string(emuHelper, addr): my_str = "" api = None if type(addr) != GenericAddress: api = FlatProgramAPI(emuHelper.getProgram()) addr = api.toAddr(addr) while True: b = emuHelper.readMemoryByte(addr) if b == 0: break my_str += struct.pack("B", b) addr = addr.add(1) del api return my_str def apply_hooks(emuHelper, d_hooks, addr, debug=False): """ Apply hook if needed """ # n_addr = get_next_execution_addr(addr) bRes = None addr_int = addr2int(addr) if addr_int in d_hooks.keys(): if debug: print(" * * * apply_hook: %s - %s" % (str(addr), d_hooks[addr_int]["name"])) bRes = d_hooks[addr_int]["callback"](emuHelper, addr) return bRes def get_next_execution_addr(addr): if type(addr) in [int, long]: addr = toAddr(addr) return getInstructionAfter(addr).address