Last active
September 4, 2025 04:08
-
-
Save osyu/539562687791dfd0de9c153c0feabcbf to your computer and use it in GitHub Desktop.
Ghidra script to find and rename CRIWARE SDK functions in binaries where they're statically linked
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Finds and renames CRIWARE SDK functions based on their unique error strings. | |
| #@author osyu, robbie01 | |
| #@category Symbol | |
| #@keybinding | |
| #@menupath | |
| #@toolbar | |
| import json | |
| import re | |
| from ghidra.app.util import XReferenceUtils | |
| from ghidra.program.model.block import IsolatedEntrySubModel | |
| from ghidra.program.util import DefinedStringIterator | |
| NAME = 'CRI function finder' | |
| ECODE_RX = re.compile(r'^([EW]\d{4,}(?:[a-zA-Z\d]+)?)') | |
| mode = askChoice(NAME, 'Set or dump?', ('set', 'dump'), 'dump') | |
| if mode == 'set': | |
| ren_unk = askChoice(NAME, 'Rename unknowns?', ('yes', 'no'), 'no') | |
| fname = str(askFile('Select ecode dump file', 'Sold!')) | |
| print('%s with file %s' % (mode, fname)) | |
| if mode == 'set': | |
| with open(fname, 'r') as f: | |
| ecodes = json.load(f) | |
| submodel = IsolatedEntrySubModel(currentProgram) | |
| else: | |
| ecodes = {} | |
| for string in DefinedStringIterator.forProgram(currentProgram): | |
| match = ECODE_RX.match(string.getValue()) | |
| if not match: | |
| continue | |
| ecode = match.group(1) | |
| unknown = False | |
| if mode == 'set' and ecode not in ecodes: | |
| if ren_unk == 'yes': | |
| unknown = True | |
| else: | |
| continue | |
| refs = XReferenceUtils.getXReferences(string, -1) | |
| funcs = set() | |
| for ref in refs: | |
| func = getFunctionContaining(ref.getFromAddress()) | |
| if func: | |
| funcs.add(func) | |
| if mode == 'dump': | |
| funcs = [x.name for x in funcs if not x.name.startswith('FUN_')] | |
| if len(funcs) == 0: | |
| continue | |
| if ecode not in ecodes: | |
| ecodes[ecode] = funcs | |
| else: | |
| ecodes[ecode].extend(funcs) | |
| else: | |
| if len(funcs) == 0: | |
| starts = set() | |
| for ref in refs: | |
| blocks = submodel.getCodeBlocksContaining( | |
| ref.getFromAddress(), monitor) | |
| starts.update(x.getFirstStartAddress() for x in blocks) | |
| if unknown: | |
| for start in starts: | |
| funcs.add(createFunction(start, None)) | |
| elif len(starts) == 0: | |
| print('* %s exists, but has no refs.\n names: %s' % | |
| (ecode, ecodes[ecode])) | |
| continue | |
| elif len(starts) > 1: | |
| print('* %s has multiple dead refs.\n names: %s\n refs: %s' % | |
| (ecode, ecodes[ecode], starts)) | |
| continue | |
| else: | |
| funcs.add(createFunction(next(iter(starts)), None)) | |
| if unknown: | |
| for func in funcs: | |
| func.setName('criUnk_%s' % func.getEntryPoint(), | |
| ghidra.program.model.symbol.SourceType.DEFAULT) | |
| continue | |
| elif len(funcs) > 1: | |
| print('* %s matched multiple funcs.\n names: %s\n matches: %s' % | |
| (ecode, ecodes[ecode], funcs)) | |
| elif len(ecodes[ecode]) > 1: | |
| print('* %s matched only %s, but has multiple names.\n names: %s' % | |
| (ecode, next(iter(funcs)), ecodes[ecode])) | |
| else: | |
| next(iter(funcs)).setName(ecodes[ecode][0], | |
| ghidra.program.model.symbol.SourceType.DEFAULT) | |
| if mode == 'dump': | |
| with open(fname, 'w') as f: | |
| json.dump(ecodes, f) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Update for 11.4: replace
DefinedDataIteratorwithDefinedStringIterator, and replaceDefinedDataIterator.definedStringswithDefinedStringIterator.forProgram