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
[](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