ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

Select the types of activity you want to include in your feed.

Add configurable Graphviz label density controls for compact and forensic visualisation modes

+175 -26
+109 -25
src/cf.c
··· 113 113 dot_escape_limited(sb, s, 28, 96); 114 114 } 115 115 116 + static void dot_label_text_full(tobi_sb *sb, const char *s) { 117 + dot_escape_limited(sb, s, 44, 0); 118 + } 119 + 116 120 typedef struct { 117 121 size_t stmt_next; 118 122 size_t edge_next; 123 + tobi_graph_labels labels; 119 124 } dot_ctx; 120 125 121 126 static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx); ··· 154 159 return emitted; 155 160 } 156 161 157 - static void dot_ir_label(tobi_sb *sb, const tobi_ir *n) { 162 + static void dot_label_value(tobi_sb *sb, const char *s, tobi_graph_labels labels) { 163 + if (labels == TOBI_GRAPH_LABEL_FULL) { 164 + dot_label_text_full(sb, s); 165 + } else { 166 + dot_label_text(sb, s); 167 + } 168 + } 169 + 170 + static void dot_ir_label(tobi_sb *sb, const tobi_ir *n, tobi_graph_labels labels) { 171 + if (labels == TOBI_GRAPH_LABEL_NONE) { 172 + return; 173 + } 158 174 tobi_sb_add(sb, tobi_ir_kind_name(n->kind)); 159 175 if (n->name && n->name[0]) { 160 176 tobi_sb_add(sb, "\\n"); 161 - dot_label_text(sb, n->name); 177 + dot_label_value(sb, n->name, labels); 162 178 } else if (n->path && n->path[0]) { 163 179 tobi_sb_add(sb, "\\n"); 164 - dot_label_text(sb, n->path); 180 + dot_label_value(sb, n->path, labels); 165 181 } else if (n->str && n->str[0]) { 166 182 tobi_sb_add(sb, "\\n"); 167 - dot_label_text(sb, n->str); 183 + dot_label_value(sb, n->str, labels); 184 + } 185 + if (labels == TOBI_GRAPH_LABEL_FULL) { 186 + if (n->path && n->path[0] && (!n->name || strcmp(n->name, n->path) != 0)) { 187 + tobi_sb_add(sb, "\\npath="); 188 + dot_label_value(sb, n->path, labels); 189 + } 190 + if (n->scope && n->scope[0]) { 191 + tobi_sb_add(sb, "\\nscope="); 192 + dot_label_value(sb, n->scope, labels); 193 + } 194 + if (n->target && n->target[0]) { 195 + tobi_sb_add(sb, "\\ntarget="); 196 + dot_label_value(sb, n->target, labels); 197 + } 198 + if (n->str && n->str[0] && (n->name || n->path)) { 199 + tobi_sb_add(sb, "\\nstr="); 200 + dot_label_value(sb, n->str, labels); 201 + } 202 + if (n->has_source && n->source) { 203 + tobi_sb_add(sb, "\\nsource="); 204 + dot_label_value(sb, n->source, labels); 205 + } 206 + if (n->value) { 207 + tobi_sb_printf(sb, "\\nvalue=0x%llx", (unsigned long long)n->value); 208 + } 209 + if (n->bit_len || n->bit_off) { 210 + tobi_sb_printf(sb, "\\nbits=%llu:%llu", (unsigned long long)n->bit_off, 211 + (unsigned long long)n->bit_len); 212 + } 168 213 } 169 214 tobi_sb_printf(sb, "\\noff=0x%zx", n->off); 215 + if (labels == TOBI_GRAPH_LABEL_FULL) { 216 + tobi_sb_printf(sb, "\\nlen=%zu", n->len); 217 + } 170 218 if (n->kind == TOBI_IR_UNKNOWN) { 171 219 tobi_sb_printf(sb, "\\nop=0x%x", n->raw_op); 172 220 } ··· 175 223 static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 176 224 size_t id = ctx->stmt_next++; 177 225 tobi_sb_printf(sb, " n%zu [label=\"", id); 178 - dot_ir_label(sb, n); 226 + dot_ir_label(sb, n, ctx->labels); 179 227 tobi_sb_add(sb, "\"];\n"); 180 228 if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 181 229 (void)dot_block(sb, n->child[0], id, "entry", ctx, NULL); ··· 209 257 tobi_sb_add(sb, " edge [color=\"#000000\",fontcolor=\"#000000\",arrowsize=0.75,penwidth=1.1];\n"); 210 258 } 211 259 212 - static char *dot_cfg(const tobi_ir *root) { 260 + static char *dot_cfg(const tobi_ir *root, tobi_graph_labels labels) { 213 261 tobi_sb sb; 214 262 tobi_sb_init(&sb); 215 263 tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 216 264 dot_common_attrs(&sb); 217 265 if (root) { 218 - dot_ctx ctx = {0, 0}; 266 + dot_ctx ctx = {0, 0, labels}; 219 267 size_t root_id = dot_stmt(&sb, root, &ctx); 220 268 size_t prev = root_id; 221 269 for (size_t i = 0; i < root->child_len; i++) { ··· 231 279 static size_t dot_ast_node(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 232 280 size_t id = ctx->stmt_next++; 233 281 tobi_sb_printf(sb, " n%zu [label=\"", id); 234 - dot_ir_label(sb, n); 282 + dot_ir_label(sb, n, ctx->labels); 235 283 tobi_sb_add(sb, "\"];\n"); 236 284 for (size_t i = 0; i < n->child_len; i++) { 237 285 size_t child_id = dot_ast_node(sb, n->child[i], ctx); ··· 240 288 return id; 241 289 } 242 290 243 - static char *dot_ast(const tobi_ir *root) { 291 + static char *dot_ast(const tobi_ir *root, tobi_graph_labels labels) { 244 292 tobi_sb sb; 245 293 tobi_sb_init(&sb); 246 294 tobi_sb_add(&sb, "digraph tobi_ast {\n"); 247 295 dot_common_attrs(&sb); 248 296 if (root) { 249 - dot_ctx ctx = {0, 0}; 297 + dot_ctx ctx = {0, 0, labels}; 250 298 (void)dot_ast_node(&sb, root, &ctx); 251 299 } 252 300 tobi_sb_add(&sb, "}\n"); ··· 266 314 return 0; 267 315 } 268 316 269 - static char *dot_namespace(const tobi_ns *ns) { 317 + static char *dot_namespace(const tobi_ns *ns, tobi_graph_labels labels) { 270 318 tobi_sb sb; 271 319 tobi_sb_init(&sb); 272 320 tobi_sb_add(&sb, "digraph tobi_namespace {\n"); 273 321 dot_common_attrs(&sb); 274 - tobi_sb_add(&sb, " n0 [label=\"namespace\\n\\\\\"];\n"); 322 + if (labels == TOBI_GRAPH_LABEL_NONE) { 323 + tobi_sb_add(&sb, " n0 [label=\"\"];\n"); 324 + } else { 325 + tobi_sb_add(&sb, " n0 [label=\"namespace\\n\\\\\"];\n"); 326 + } 275 327 if (ns) { 276 328 for (size_t i = 0; i < ns->len; i++) { 277 329 const tobi_ns_ent *e = &ns->items[i]; 278 - tobi_sb_printf(&sb, " n%zu [label=\"%s", i + 1u, tobi_ns_kind_name(e->kind)); 279 - if (e->name && e->name[0]) { 280 - tobi_sb_add(&sb, "\\n"); 281 - dot_label_text(&sb, e->name); 330 + tobi_sb_printf(&sb, " n%zu [label=\"", i + 1u); 331 + if (labels != TOBI_GRAPH_LABEL_NONE) { 332 + tobi_sb_add(&sb, tobi_ns_kind_name(e->kind)); 333 + if (e->name && e->name[0]) { 334 + tobi_sb_add(&sb, "\\n"); 335 + dot_label_value(&sb, e->name, labels); 336 + } 337 + if (e->path && e->path[0]) { 338 + tobi_sb_add(&sb, "\\n"); 339 + dot_label_value(&sb, e->path, labels); 340 + } 341 + tobi_sb_printf(&sb, "\\noff=0x%zx", e->off); 342 + if (labels == TOBI_GRAPH_LABEL_FULL) { 343 + if (e->owner && e->owner[0]) { 344 + tobi_sb_add(&sb, "\\nowner="); 345 + dot_label_value(&sb, e->owner, labels); 346 + } 347 + if (e->target && e->target[0]) { 348 + tobi_sb_add(&sb, "\\ntarget="); 349 + dot_label_value(&sb, e->target, labels); 350 + } 351 + tobi_sb_printf(&sb, "\\nargs=%u flags=0x%x", e->args, e->flags); 352 + if (e->external) { 353 + tobi_sb_add(&sb, "\\nexternal=true"); 354 + } 355 + if (e->has_region_range) { 356 + tobi_sb_printf(&sb, "\\nregion=0x%llx+0x%llx", 357 + (unsigned long long)e->region_offset, 358 + (unsigned long long)e->region_len); 359 + } 360 + if (e->has_source && e->source) { 361 + tobi_sb_add(&sb, "\\nsource="); 362 + dot_label_value(&sb, e->source, labels); 363 + } 364 + } 282 365 } 283 - if (e->path && e->path[0]) { 284 - tobi_sb_add(&sb, "\\n"); 285 - dot_label_text(&sb, e->path); 286 - } 287 - tobi_sb_printf(&sb, "\\noff=0x%zx\"];\n", e->off); 366 + tobi_sb_add(&sb, "\"];\n"); 288 367 } 289 368 for (size_t i = 0; i < ns->len; i++) { 290 369 const tobi_ns_ent *e = &ns->items[i]; ··· 304 383 } 305 384 306 385 char *tobi_cf_dot(const tobi_ir *root) { 307 - return dot_cfg(root); 386 + return dot_cfg(root, TOBI_GRAPH_LABEL_SHORT); 308 387 } 309 388 310 389 char *tobi_graph_dot(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind) { 390 + return tobi_graph_dot_labels(root, ns, kind, TOBI_GRAPH_LABEL_SHORT); 391 + } 392 + 393 + char *tobi_graph_dot_labels(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind, 394 + tobi_graph_labels labels) { 311 395 switch (kind) { 312 396 case TOBI_GRAPH_AST: 313 - return dot_ast(root); 397 + return dot_ast(root, labels); 314 398 case TOBI_GRAPH_NAMESPACE: 315 - return dot_namespace(ns); 399 + return dot_namespace(ns, labels); 316 400 case TOBI_GRAPH_CFG: 317 401 default: 318 - return dot_cfg(root); 402 + return dot_cfg(root, labels); 319 403 } 320 404 }
+9
src/cf.h
··· 11 11 TOBI_GRAPH_NAMESPACE 12 12 } tobi_graph_kind; 13 13 14 + typedef enum tobi_graph_labels { 15 + TOBI_GRAPH_LABEL_SHORT, 16 + TOBI_GRAPH_LABEL_FULL, 17 + TOBI_GRAPH_LABEL_NONE 18 + } tobi_graph_labels; 19 + 14 20 int tobi_cf_recover(tobi_ir *root, tobi_diag_list *diag); 15 21 16 22 char *tobi_cf_dot(const tobi_ir *root); 17 23 18 24 char *tobi_graph_dot(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind); 25 + 26 + char *tobi_graph_dot_labels(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind, 27 + tobi_graph_labels labels); 19 28 20 29 #endif
+31 -1
src/main.c
··· 23 23 " --json print parsed ir as json\n" 24 24 " --dot print recovered control flow as Graphviz DOT\n" 25 25 " --graph K print DOT graph: cfg, ast, namespace\n" 26 + " --graph-labels K\n" 27 + " set graph labels: short, full, none\n" 26 28 " --raw print input/table metadata and aml offsets\n" 27 29 " -o FILE write primary output to FILE instead of stdout\n" 28 30 " --strict treat malformed or unsupported constructs as hard errors\n" ··· 169 171 return 0; 170 172 } 171 173 174 + static int parse_graph_labels(const char *s, tobi_graph_labels *labels) { 175 + if (strcmp(s, "short") == 0) { 176 + *labels = TOBI_GRAPH_LABEL_SHORT; 177 + return 1; 178 + } 179 + if (strcmp(s, "full") == 0) { 180 + *labels = TOBI_GRAPH_LABEL_FULL; 181 + return 1; 182 + } 183 + if (strcmp(s, "none") == 0) { 184 + *labels = TOBI_GRAPH_LABEL_NONE; 185 + return 1; 186 + } 187 + return 0; 188 + } 189 + 172 190 static void print_diags(const tobi_diag_list *dl, int plain) { 173 191 int colour = !plain && isatty(fileno(stderr)); 174 192 for (size_t i = 0; i < dl->len; i++) { ··· 236 254 int stdin_count = 0; 237 255 const char *output_path = NULL; 238 256 tobi_graph_kind graph_kind = TOBI_GRAPH_CFG; 257 + tobi_graph_labels graph_labels = TOBI_GRAPH_LABEL_SHORT; 239 258 const char **files = NULL; 240 259 size_t file_len = 0; 241 260 size_t file_cap = 0; ··· 267 286 return 2; 268 287 } 269 288 mode_graph = 1; 289 + } else if (strcmp(argv[i], "--graph-labels") == 0) { 290 + if (i + 1 >= argc) { 291 + fprintf(stderr, "tobi: --graph-labels requires short, full, or none\n"); 292 + free(files); 293 + return 2; 294 + } 295 + if (!parse_graph_labels(argv[++i], &graph_labels)) { 296 + fprintf(stderr, "tobi: unknown graph label mode %s\n", argv[i]); 297 + free(files); 298 + return 2; 299 + } 270 300 } else if (strcmp(argv[i], "--raw") == 0) { 271 301 mode_raw = 1; 272 302 } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) { ··· 378 408 fputs(s, outfp); 379 409 free(s); 380 410 } else if (mode_graph) { 381 - char *s = tobi_graph_dot(res.root, &res.ns, graph_kind); 411 + char *s = tobi_graph_dot_labels(res.root, &res.ns, graph_kind, graph_labels); 382 412 fputs(s, outfp); 383 413 free(s); 384 414 } else {
+9
test/t_cf.c
··· 59 59 T_STR(dot, "LL00"); 60 60 T_STR(dot, "method"); 61 61 free(dot); 62 + dot = tobi_graph_dot_labels(r.root, &r.ns, TOBI_GRAPH_AST, TOBI_GRAPH_LABEL_FULL); 63 + T_STR(dot, "source=raw"); 64 + T_STR(dot, "len="); 65 + free(dot); 66 + dot = tobi_graph_dot_labels(r.root, &r.ns, TOBI_GRAPH_CFG, TOBI_GRAPH_LABEL_NONE); 67 + T_STR(dot, "digraph tobi_cfg"); 68 + T_CHECK(strstr(dot, "LL00") == NULL); 69 + T_CHECK(strstr(dot, "off=0x") == NULL); 70 + free(dot); 62 71 tobi_parse_result_free(&r); 63 72 64 73 uint8_t amb[] = {0xa0,0x03,0x01,0xa1};
+17
test/t_cli.c
··· 220 220 T_STR(s, "digraph tobi_namespace"); 221 221 T_STR(s, "MTH0"); 222 222 free(s); 223 + const char *argv_full_labels[] = {TOBI_BIN, "--graph", "ast", "--graph-labels", "full", path, NULL}; 224 + s = run_capture_argv(argv_full_labels, NULL, 0, &st); 225 + T_CHECK(st == 0); 226 + T_STR(s, "source="); 227 + T_STR(s, "len="); 228 + free(s); 229 + const char *argv_no_labels[] = {TOBI_BIN, "--dot", "--graph-labels", "none", path, NULL}; 230 + s = run_capture_argv(argv_no_labels, NULL, 0, &st); 231 + T_CHECK(st == 0); 232 + T_STR(s, "digraph tobi_cfg"); 233 + T_CHECK(strstr(s, "MTH0") == NULL); 234 + T_CHECK(strstr(s, "off=0x") == NULL); 235 + free(s); 223 236 { 224 237 const char *out_path = "/tmp/tobi_cli_graph.dot"; 225 238 const char *argv_out[] = {TOBI_BIN, "--dot", path, "-o", out_path, NULL}; ··· 325 338 free(s); 326 339 const char *argv_bad_graph[] = {TOBI_BIN, "--graph", "nope", path, NULL}; 327 340 s = run_capture_argv(argv_bad_graph, NULL, 0, &st); 341 + T_CHECK(st == 2); 342 + free(s); 343 + const char *argv_bad_graph_labels[] = {TOBI_BIN, "--graph-labels", "verbose", path, NULL}; 344 + s = run_capture_argv(argv_bad_graph_labels, NULL, 0, &st); 328 345 T_CHECK(st == 2); 329 346 free(s); 330 347 const char *argv_missing_graph[] = {TOBI_BIN, "--graph", NULL};