c example now uses persistent mode
DELETE
unicorn_mode/samples/c/a.out
unicorn_mode/samples/c/a.out
Binary files a/unicorn_mode/samples/c/a.out and /dev/null differ

MODIFY
unicorn_mode/samples/c/harness.c
unicorn_mode/samples/c/harness.c
#include <unicorn/unicorn.h>
// Path to the file containing the binary to emulate
-#define BINARY_FILE ("simple_target_x86_64")
+#define BINARY_FILE ("persistent_target_x86_64")
// Memory map for the code to be tested
// Arbitrary address where code to test will be loaded
static const int64_t BASE_ADDRESS = 0x100000;
-static const int64_t CODE_ADDRESS = 0x101119;
-static const int64_t END_ADDRESS = 0x1011d7;
+static const int64_t CODE_ADDRESS = 0x101139;
+static const int64_t END_ADDRESS = 0x10120d;
// Address of the stack (Some random address again)
static const int64_t STACK_ADDRESS = (((int64_t) 0x01) << 58);
// Size of the stack (arbitrarily chosen, just make it big enough)
@@ -52,15 +52,33 @@ static const int64_t ALIGNMENT = 0x1000;
// In our special case, we emulate main(), so argc is needed.
static const uint64_t EMULATED_ARGC = 2;
+// The return from our fake strlen
+static size_t current_input_len = 0;
+
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
}
-static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
-{
+static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
}
+/*
+The sample uses strlen, since we don't have a loader or libc, we'll fake it.
+We know the strlen will return the lenght of argv[1] that we just planted.
+It will be a lot faster than an actual strlen for this specific purpose.
+*/
+static void hook_strlen(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
+ //Hook
+ //116b: e8 c0 fe ff ff call 1030 <strlen@plt>
+ // We place the return at RAX
+ //printf("Strlen hook at addr 0x%lx (size: 0x%x), result: %ld\n", address, size, current_input_len);
+ uc_reg_write(uc, UC_X86_REG_RAX, &current_input_len);
+ // We skip the actual call by updating RIP
+ uint64_t next_addr = address + size;
+ uc_reg_write(uc, UC_X86_REG_RIP, &next_addr);
+}
+
/* Unicorn page needs to be 0x1000 aligned, apparently */
static uint64_t pad(uint64_t size) {
if (size % ALIGNMENT == 0) return size;
@@ -107,8 +125,6 @@ static bool place_input_callback(
// Test input too short or too long, ignore this testcase
return false;
}
- // We need a valid c string, make sure it never goes out of bounds.
- input[input_len-1] = '\0';
// For persistent mode, we have to set up stack and memory each time.
uc_reg_write(uc, UC_X86_REG_RIP, &CODE_ADDRESS); // Set the instruction pointer back
@@ -116,10 +132,14 @@ static bool place_input_callback(
uc_reg_write(uc, UC_X86_REG_RSI, &INPUT_LOCATION); // argv
uc_reg_write(uc, UC_X86_REG_RDI, &EMULATED_ARGC); // argc == 2
- // Make sure the input is 0 terminated.
- //input[input_len-1] = '\0';
+ // We need a valid c string, make sure it never goes out of bounds.
+ input[input_len-1] = '\0';
// Write the testcase to unicorn.
uc_mem_write(uc, INPUT_LOCATION + INPUT_OFFSET, input, input_len);
+
+ // store input_len for the faux strlen hook
+ current_input_len = input_len;
+
return true;
}
@@ -211,6 +231,11 @@ int main(int argc, char **argv, char **envp) {
uc_hook_add(uc, &hooks[1], UC_HOOK_CODE, hook_code, NULL, BASE_ADDRESS, BASE_ADDRESS + len - 1);
}
+ // 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);
+
printf("Starting to fuzz :)\n");
fflush(stdout);

RENAME
unicorn_mode/samples/c/simple_target.c
unicorn_mode/samples/c/persistent_target.c
* Written by Nathan Voss <njvoss99@gmail.com>
* Adapted by Lukas Seidel <seidel.1@campus.tu-berlin.de>
*/
+#include <stdint.h>
+#include <string.h>
int main(int argc, char** argv) {
- if(argc < 2){
- return -1;
- }
+ if (argc < 2) return -1;
char *data_buf = argv[1];
+ uint64_t data_len = strlen(data_buf);
+ if (data_len < 20) return -2;
- if (data_buf[20] != 0) {
- // Cause an 'invalid read' crash if data[0..3] == '\x01\x02\x03\x04'
+ for (; data_len --> 0 ;) {
+ if (data_len >= 18) continue;
+ if (data_len > 2 && data_len < 18) {
+ ((char *)data_len)[(uint64_t)data_buf] = data_buf[data_len + 1];
+ } else if (data_buf[9] == 0x90 && data_buf[10] != 0x00 && data_buf[11] == 0x90) {
+ // Cause a crash if data[10] is not zero, but [9] and [11] are zero
unsigned char invalid_read = *(unsigned char *) 0x00000000;
- } else if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
+ }
+ }
+ if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
// Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]
unsigned char invalid_read = *(unsigned char *) 0x00000000;
- } else if (data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) {
- // Cause a crash if data[10] is not zero, but [9] and [11] are zero
- unsigned char invalid_read = *(unsigned char *) 0x00000000;
}
return 0;

ADD
unicorn_mode/samples/c/persistent_target_x86_64
unicorn_mode/samples/c/persistent_target_x86_64
Binary files /dev/null and b/unicorn_mode/samples/c/persistent_target_x86_64 differ

c example now uses persistent mode