Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions commons/ghidra/ghidra.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
import typing
from enum import Enum
import re

import docker

Expand Down Expand Up @@ -32,7 +33,7 @@ class GhidraAnalysis:
calls: typing.Set[str]

def __init__(self, filename: str) -> None:
self.filename = filename
self.filename = os.path.abspath(filename)
self.decompiled_code = ""
self.calls = set()

Expand Down Expand Up @@ -112,28 +113,45 @@ def __process_decompiled_code(self, lines: typing.List[str]) -> str:

code = self.__replace_undefs(code)
code = self.__replace_longs(code)
code = self.__replace_ghidra_artifacts(code)
code = self.__replace_double_lines(code)
code = self.__replace_comments_for_pycparser(code)

return code

def __replace_undefs(self, code: str) -> None:
return code.replace("undefined4", "int").replace("undefined", "char")
return (
code.replace("undefined8", "long long")
.replace("undefined4", "int")
.replace("undefined2", "short")
.replace("undefined1", "char")
.replace("undefined", "char")
)

def __replace_longs(self, code: str) -> None:
return code.replace("char8", "long")
return (
code.replace("char8", "long")
.replace("ulong", "unsigned long")
.replace("uint", "unsigned int")
.replace("ushort", "unsigned short")
.replace("bool", "int")
)

def __replace_ghidra_artifacts(self, code: str) -> str:
# Remove Ghidra specific keywords and formatting that break pycparser
code = code.replace("processEntry", "")
code = re.sub(r'PTR_FUN_[0-9a-f]+', '0', code)
code = re.sub(r'FUN_[0-9a-f]+', '0', code)
code = re.sub(r'&stack0x[0-9a-f]+', '0', code)
return code

def __replace_double_lines(self, code: str) -> None:
return code.replace("\n\n", "\n")

def __replace_comments_for_pycparser(self, code: str) -> None:
# pycparser won't be able to parse lines with comments.
no_comments_code = []
for line in code.splitlines():
if COMMENT_PREFIX not in line:
no_comments_code.append(line)

return "\n".join(no_comments_code)
def __replace_comments_for_pycparser(self, code: str) -> str:
# Remove all /* ... */ blocks
code = re.sub(r'/\*.*?\*/', '', code, flags=re.DOTALL)
return "\n".join([line for line in code.splitlines() if line.strip()])

def decompile_function(self, function_name: str) -> str:
analysis_report = self.__run_headless_ghidra(
Expand Down
43 changes: 35 additions & 8 deletions commons/ghidra/scripts/decompile_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,47 @@


def main():
function_name = getScriptArgs()[0]
if not function_name:
exit(1)

args = getScriptArgs()
if not args:
print("ERROR: No function name provided")
return

function_name = args[0]
program = getCurrentProgram()
decompiler = DecompInterface()
decompiler.openProgram(program)

function = getGlobalFunctions(function_name)[0]
# Try 1: Find by name (e.g., 'main')
functions = getGlobalFunctions(function_name)
function = None

if functions:
function = functions[0]
else:
# Try 2: Look for any symbol containing 'entry' or 'main'
symbol_table = program.getSymbolTable()
for symbol in symbol_table.getAllSymbols(True):
if symbol.getName() in ["entry", "_start", "main"]:
function = getFunctionAt(symbol.getAddress())
if function: break

# Try 3: Force get function at the primary entry point address
if not function:
entry_points = program.getSymbolTable().getExternalSymbols("entry")
# If still nothing, pick the absolute first function in the manager
if not function:
function = program.getFunctionManager().getFunctions(True).next()

if not function:
print("ERROR: No functions identified in the binary.")
return

results = decompiler.decompileFunction(function, 0, ConsoleTaskMonitor())
code = results.getDecompiledFunction().getC()

print(code)
if results and results.getDecompiledFunction():
code = results.getDecompiledFunction().getC()
print(code)
else:
print("ERROR: Decompilation failed for function at {}".format(function.getEntryPoint()))


if __name__ == "__main__":
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
pwntools = "^4.10.0"
docker = "^6.1.2"
python = "==3.12.7"
pwntools = "==4.15.0"
docker = "==7.1.0"

[build-system]
requires = ["poetry-core"]
Expand Down