|
| 1 | +# Attempts to more aggressively demangle any Microsoft-style mangled symbols. |
| 2 | +# DemanglerCmd is not used as it will filter by program format (e.g. Microsoft |
| 3 | +# Demangler will not be used if the executable format is not PE/COFF). Instead, |
| 4 | +# this script invokes the MicrosoftDemangler directly on any symbol prefixed by |
| 5 | +# `?`. Additionally, this script handles `@name@X` (fastcall) and `_name@X` |
| 6 | +# (stdcall) mangles. |
| 7 | +# @author: Matt Borgerson |
| 8 | +# @category: Symbol |
| 9 | +from ghidra.app.util.demangler import DemanglerOptions |
| 10 | +from ghidra.app.util.demangler.microsoft import MicrosoftDemangler |
| 11 | +from ghidra.program.model.symbol import SourceType |
| 12 | +import re |
| 13 | + |
| 14 | +st = currentProgram.getSymbolTable() |
| 15 | +n = currentProgram.getNamespaceManager().getGlobalNamespace() |
| 16 | + |
| 17 | +numDemangled = 0 |
| 18 | +failures = [] |
| 19 | + |
| 20 | +for s in st.getSymbols(n): |
| 21 | + name = s.getName() |
| 22 | + addr = s.getAddress() |
| 23 | + |
| 24 | + if name.startswith('?'): |
| 25 | + # Attempt using Microsoft demangler |
| 26 | + try: |
| 27 | + print('Demangling with Microsoft Demangler: %s' % name) |
| 28 | + demangled = MicrosoftDemangler().demangle(name, True) |
| 29 | + s.delete() |
| 30 | + demangled.applyTo(currentProgram, addr, DemanglerOptions(), monitor) |
| 31 | + except: |
| 32 | + print('Failed to demangle %s' % name) |
| 33 | + failures.append(name) |
| 34 | + |
| 35 | + elif name.startswith('@') or name.startswith('_'): |
| 36 | + # Attempt decoding @func@0 (__fastcall) and _func@0 (__stdcall) style mangle |
| 37 | + # https://en.wikipedia.org/wiki/Name_mangling#Standardised_name_mangling_in_C++ |
| 38 | + isFastcall, isStdcall = False, False |
| 39 | + realName, bytesInParams = '', 0 |
| 40 | + |
| 41 | + m = re.match('^@(\w+)@([0-9]+)$', name) |
| 42 | + if m is not None: |
| 43 | + isFastcall = True |
| 44 | + realName, bytesInParams = m.groups() |
| 45 | + else: |
| 46 | + m = re.match('^_(\w+)@([0-9]+)$', name) |
| 47 | + if m is not None: |
| 48 | + isStdcall = True |
| 49 | + realName, bytesInParams = m.groups() |
| 50 | + |
| 51 | + if isFastcall or isStdcall: |
| 52 | + print('Demangling: %s' % name) |
| 53 | + bytesInParams = int(bytesInParams) |
| 54 | + |
| 55 | + # Get or create the function |
| 56 | + s.delete() |
| 57 | + f = getFunctionAt(addr) |
| 58 | + if f is None: |
| 59 | + f = createFunction(addr, realName) |
| 60 | + |
| 61 | + if f is None: |
| 62 | + print('Couldn\'t create function for %s' % realName) |
| 63 | + failures.append(name) |
| 64 | + else: |
| 65 | + f.setName(realName, SourceType.ANALYSIS) |
| 66 | + f.setComment(name) |
| 67 | + convention = '__fastcall' if isFastcall else '__stdcall' |
| 68 | + f.setCallingConvention(convention) |
| 69 | + else: |
| 70 | + continue |
| 71 | + |
| 72 | + numDemangled += 1 |
| 73 | + |
| 74 | +print('Demangled %d names' % numDemangled) |
| 75 | +if len(failures) > 0: |
| 76 | + print('Failed to demangle (%d):' % len(failures)) |
| 77 | + for n in sorted(failures): |
| 78 | + print('- %s' % n) |
0 commit comments