#Recover function names from logger function calls. | |
#@author @Jackson_T | |
#@category _NEW_ | |
#@keybinding | |
#@menupath | |
#@toolbar | |
import re | |
from ghidra.program.model.symbol import SourceType | |
from ghidra.util.exception import InvalidInputException | |
''' | |
Example logger function prototype: | |
void sub_140001000(int log_level, | |
char* function_name, | |
char* file_name, | |
char* format_str, | |
...); | |
''' | |
logger_addr = 0x140001000 # CHANGE THIS TO THE ADDRESS OF THE LOGGER FUNCTION | |
logger_regx = re.compile(r'.*\(.*,"(?P<name>.*?)","(?P<file>.*?)".*\)') # CHANGE THIS REGEX TO MATCH THE FUNCTION PROTOTYPE | |
logger_name = getFunctionContaining(toAddr(logger_addr)).getName() | |
# Initialize decompiler. | |
flat_program_api = ghidra.program.flatapi.FlatProgramAPI(getCurrentProgram(), getMonitor()) | |
decompiler_api = ghidra.app.decompiler.flatapi.FlatDecompilerAPI(flat_program_api) | |
decompiler_api.initialize() | |
decompiler = decompiler_api.getDecompiler() | |
def get_functions_referencing_logger(): | |
functions = [] | |
references_to_logger = getReferencesTo(toAddr(logger_addr)) | |
for reference in references_to_logger: | |
function = getFunctionContaining(reference.getFromAddress()) | |
if function: | |
functions.append(function) | |
# Return list without duplicates. | |
return list(set(functions)) | |
def extract_function_name_from_token(function_metadata, function, node): | |
if type(node) == ghidra.app.decompiler.ClangStatement: | |
if str(node).startswith(logger_name): | |
if function not in function_metadata: | |
call_str = str(node) | |
print((call_str[:100] + '...') if len(call_str) > 100 else call_str) | |
function_metadata[function] = logger_regx.match(str(node)).groupdict() | |
return | |
else: | |
for i in range(node.numChildren()): | |
extract_function_name_from_token(function_metadata, function, node.Child(i)) | |
def collect_function_metadata_from_logger_call(): | |
function_metadata = {} | |
functions = get_functions_referencing_logger() | |
getMonitor().initialize(len(functions)) | |
for function in functions: | |
node = decompiler.decompileFunction(function, 0, None).getCCodeMarkup() | |
extract_function_name_from_token(function_metadata, function, node) | |
getMonitor().incrementProgress(1) | |
getMonitor().setMessage('Recovering name of {0}...'.format(function)) | |
return function_metadata | |
def rename_functions(): | |
function_metadata = collect_function_metadata_from_logger_call() | |
for function, metadata in function_metadata.items(): | |
print('(I): Renaming {0} to {1}...'.format(function, metadata['name'])) | |
# Account for class name if there is one. | |
if '::' in metadata['name']: | |
function_name = metadata['name'].split('::')[-1] | |
class_name = metadata['name'].split('::')[-2] | |
namespace = getNamespace(None, class_name) | |
# Create a namespace for the class if there isn't one. | |
if not namespace: | |
namespace = getCurrentProgram().getSymbolTable().createClass( | |
None, | |
class_name, | |
SourceType.USER_DEFINED) | |
# If there isn't a class name, use the global namespace. | |
else: | |
function_name = metadata['name'] | |
namespace = None | |
# Set the namespace. | |
if namespace: | |
function.setParentNamespace(namespace) | |
# Set the function name. | |
try: | |
function.setName(function_name, SourceType.USER_DEFINED) | |
except InvalidInputException as e: | |
print('(E): {0}'.format(e)) | |
rename_functions() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment