|
12 | 12 | DecompilerInterface, |
13 | 13 | ) |
14 | 14 | from libbs.artifacts import ( |
15 | | - Function, FunctionHeader, Comment, StackVariable, FunctionArgument, Artifact, Decompilation, Context |
| 15 | + Function, FunctionHeader, Comment, StackVariable, FunctionArgument, Artifact, Decompilation, Context, |
| 16 | + Struct, StructMember |
16 | 17 | ) |
17 | 18 | from .artifact_lifter import AngrArtifactLifter |
18 | 19 |
|
@@ -256,8 +257,9 @@ def _set_function(self, func: Function, **kwargs) -> bool: |
256 | 257 | # re-decompile a function if needed |
257 | 258 | decompilation = self.decompile_function(angr_func).codegen |
258 | 259 | changes = super()._set_function(func, decompilation=decompilation, **kwargs) |
259 | | - if not self.headless: |
260 | | - self.refresh_decompilation(func.addr) |
| 260 | + if not self.headless and changes: |
| 261 | + # Use "retype_variable" event to trigger proper UI refresh including type reflow |
| 262 | + self.refresh_decompilation(func.addr, event="retype_variable") |
261 | 263 |
|
262 | 264 | return changes |
263 | 265 |
|
@@ -348,12 +350,34 @@ def _set_stack_variable(self, svar: StackVariable, decompilation=None, **kwargs) |
348 | 350 | return changed |
349 | 351 |
|
350 | 352 | dec_svar = AngrInterface.find_stack_var_in_codegen(decompilation, svar.offset) |
351 | | - if dec_svar and svar.name and svar.name != dec_svar.name: |
352 | | - # TODO: set the types of the stack vars |
| 353 | + if not dec_svar: |
| 354 | + return changed |
| 355 | + |
| 356 | + # Set the name if provided and different |
| 357 | + if svar.name and svar.name != dec_svar.name: |
353 | 358 | dec_svar.name = svar.name |
354 | 359 | dec_svar.renamed = True |
355 | 360 | changed = True |
356 | 361 |
|
| 362 | + # Set the type if provided |
| 363 | + if svar.type: |
| 364 | + try: |
| 365 | + from angr.sim_type import parse_type |
| 366 | + types_store = self.main_instance.project.kb.types |
| 367 | + arch = self.main_instance.project.arch |
| 368 | + |
| 369 | + # Parse the type string into a SimType |
| 370 | + sim_type = parse_type(svar.type, predefined_types=types_store, arch=arch) |
| 371 | + sim_type = sim_type.with_arch(arch) |
| 372 | + |
| 373 | + # Get the variable manager and set the type |
| 374 | + variable_kb = decompilation._variable_kb if hasattr(decompilation, '_variable_kb') else self.main_instance.project.kb |
| 375 | + variable_manager = variable_kb.variables[svar.addr] |
| 376 | + variable_manager.set_variable_type(dec_svar, sim_type, all_unified=True, mark_manual=True) |
| 377 | + changed = True |
| 378 | + except Exception as e: |
| 379 | + l.warning(f"Failed to set stack variable type for {svar.name}: {e}") |
| 380 | + |
357 | 381 | return changed |
358 | 382 |
|
359 | 383 | def _set_comment(self, comment: Comment, decompilation=None, **kwargs) -> bool: |
@@ -382,6 +406,109 @@ def _set_comment(self, comment: Comment, decompilation=None, **kwargs) -> bool: |
382 | 406 | func_addr = comment.func_addr or self.get_closest_function(comment.addr) |
383 | 407 | return changed & self.refresh_decompilation(func_addr) |
384 | 408 |
|
| 409 | + # structs |
| 410 | + def _structs(self) -> Dict[str, Struct]: |
| 411 | + """ |
| 412 | + Returns a dict of libbs.Struct that contain the name and size of each struct in the decompiler. |
| 413 | + """ |
| 414 | + from angr.sim_type import SimStruct, TypeRef |
| 415 | + structs = {} |
| 416 | + types_store = self.main_instance.project.kb.types |
| 417 | + |
| 418 | + for type_ref in types_store.iter_own(): |
| 419 | + if not isinstance(type_ref, TypeRef): |
| 420 | + continue |
| 421 | + sim_type = type_ref.type |
| 422 | + if isinstance(sim_type, SimStruct): |
| 423 | + structs[type_ref.name] = Struct(type_ref.name, sim_type.size // 8 if sim_type.size else 0, {}) |
| 424 | + |
| 425 | + return structs |
| 426 | + |
| 427 | + def _get_struct(self, name) -> Optional[Struct]: |
| 428 | + """ |
| 429 | + Get a struct by name from the TypesStore. |
| 430 | + """ |
| 431 | + from angr.sim_type import SimStruct, TypeRef |
| 432 | + types_store = self.main_instance.project.kb.types |
| 433 | + |
| 434 | + try: |
| 435 | + type_ref = types_store[name] |
| 436 | + except KeyError: |
| 437 | + return None |
| 438 | + |
| 439 | + if not isinstance(type_ref, TypeRef): |
| 440 | + return None |
| 441 | + |
| 442 | + sim_struct = type_ref.type |
| 443 | + if not isinstance(sim_struct, SimStruct): |
| 444 | + return None |
| 445 | + |
| 446 | + return self._angr_struct_to_libbs(name, sim_struct) |
| 447 | + |
| 448 | + def _set_struct(self, struct: Struct, header=True, members=True, **kwargs) -> bool: |
| 449 | + """ |
| 450 | + Create or update a struct in the TypesStore. |
| 451 | + """ |
| 452 | + from angr.sim_type import SimStruct, TypeRef, parse_type |
| 453 | + from collections import OrderedDict |
| 454 | + |
| 455 | + types_store = self.main_instance.project.kb.types |
| 456 | + arch = self.main_instance.project.arch |
| 457 | + |
| 458 | + # Build the fields OrderedDict from LibBS struct members |
| 459 | + fields = OrderedDict() |
| 460 | + if members and struct.members: |
| 461 | + sorted_members = sorted(struct.members.items(), key=lambda x: x[0]) |
| 462 | + for offset, member in sorted_members: |
| 463 | + # Parse the member type string into a SimType |
| 464 | + try: |
| 465 | + sim_type = parse_type(member.type, predefined_types=types_store, arch=arch) |
| 466 | + except Exception: |
| 467 | + # Fallback to a simple int type with the right size if parsing fails |
| 468 | + from angr.sim_type import SimTypeInt |
| 469 | + sim_type = SimTypeInt(signed=False).with_arch(arch) |
| 470 | + |
| 471 | + fields[member.name] = sim_type.with_arch(arch) |
| 472 | + |
| 473 | + # Create the SimStruct |
| 474 | + sim_struct = SimStruct(fields, name=struct.name, pack=True) |
| 475 | + sim_struct = sim_struct.with_arch(arch) |
| 476 | + |
| 477 | + # Wrap it in a TypeRef and store it |
| 478 | + type_ref = TypeRef(struct.name, sim_struct) |
| 479 | + types_store[struct.name] = type_ref |
| 480 | + |
| 481 | + return True |
| 482 | + |
| 483 | + def _del_struct(self, name) -> bool: |
| 484 | + """ |
| 485 | + Delete a struct from the TypesStore. |
| 486 | + """ |
| 487 | + types_store = self.main_instance.project.kb.types |
| 488 | + |
| 489 | + if name in types_store.data: |
| 490 | + del types_store.data[name] |
| 491 | + return True |
| 492 | + |
| 493 | + return False |
| 494 | + |
| 495 | + @staticmethod |
| 496 | + def _angr_struct_to_libbs(name: str, sim_struct: "angr.sim_type.SimStruct") -> Struct: |
| 497 | + """ |
| 498 | + Convert an angr SimStruct to a LibBS Struct. |
| 499 | + """ |
| 500 | + members = {} |
| 501 | + if sim_struct._arch is not None: |
| 502 | + offsets = sim_struct.offsets |
| 503 | + for field_name, sim_type in sim_struct.fields.items(): |
| 504 | + offset = offsets.get(field_name, 0) |
| 505 | + type_str = sim_type.c_repr() if sim_type else None |
| 506 | + size = sim_type.size // 8 if sim_type and sim_type.size else 0 |
| 507 | + members[offset] = StructMember(field_name, offset, type_str, size) |
| 508 | + |
| 509 | + size = sim_struct.size // 8 if sim_struct.size else 0 |
| 510 | + return Struct(name, size, members) |
| 511 | + |
385 | 512 | # |
386 | 513 | # Utils |
387 | 514 | # |
@@ -431,13 +558,16 @@ def addr_starts_instruction(self, addr) -> bool: |
431 | 558 |
|
432 | 559 | return addr in node.instruction_addrs |
433 | 560 |
|
434 | | - def refresh_decompilation(self, func_addr): |
| 561 | + def refresh_decompilation(self, func_addr, event=None): |
435 | 562 | if self.headless: |
436 | 563 | return False |
437 | 564 |
|
438 | 565 | self.workspace.jump_to(func_addr) |
439 | 566 | view = self.workspace._get_or_create_view("pseudocode", CodeView) |
440 | | - view.codegen.am_event() |
| 567 | + if event: |
| 568 | + view.codegen.am_event(event=event) |
| 569 | + else: |
| 570 | + view.codegen.am_event() |
441 | 571 | view.focus() |
442 | 572 | return True |
443 | 573 |
|
|
0 commit comments