Open cesaryuan opened 3 months ago
Can you share the binaries in question? This is probably best addressed with a quick script we can write up for you as opposed to a change in the product at least in the near-term.
I have previously ran into a similar case and I also think that writing a script for it would be the best choice. @cesaryuan could you please share the binary with us so that it would be easier for us to work with it?
I have previously ran into a similar case and I also think that writing a script for it would be the best choice
@psifertex @xusheng6 Sorry for late reply. Thanks for your advice, but I cannot share the binary due to some privacy issues.
However, after checking the API documents,. I resolved it by writing a script. Here is the script.
MFC_template_classname_map = {
'CStringT<wchar_t,class StrTraitMFC_DLL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > >': 'CStringW',
'CStringT<char,class StrTraitMFC_DLL<char,class ATL::ChTraitsCRT<char> > >': 'CStringA',
'CArray<class CRect,class CRect>': 'CArray<CRect>',
'CSimpleStringT<char,0>': 'CSimpleStringA0',
'CSimpleStringT<wchar_t,0>': 'CSimpleStringW0',
'CSimpleStringT<char,1>': 'CSimpleStringA1',
'CSimpleStringT<wchar_t,1>': 'CSimpleStringW1',
}
def replace_template_classname(name: str):
'''
Used to replace the template class name to the human-readable name
'''
for template_classname, new_classname in MFC_template_classname_map.items():
name = name.replace(template_classname, new_classname)
return name
import json
mfc90u_exports = json.load(open(r'C:\Users\cesar\AppData\Roaming\Binary Ninja\plugins\nodejs-napi-types-importer\mfc90u_exports.json'))
def get_function_by_ordinal(view: BinaryView, ordinal: int) -> Function | None:
'''
Due to issue [Combing the function name info from PDB and the ordinal info from PE when hanlding PE exports](https://github.com/Vector35/binaryninja-api/issues/5217).
We have to use the following code to get the function by ordinal
The mfc90u_exports.json is generated by pefile
import pefile
def extract_exports(exe_file_path):
try:
pe = pefile.PE(exe_file_path)
export_entries = []
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
export_entry = {
"Name": exp.name.decode() if exp.name else None,
"Ordinal": exp.ordinal,
"Address": hex(pe.OPTIONAL_HEADER.ImageBase + exp.address)
}
export_entries.append(export_entry)
return export_entries
except Exception as e:
print(f"Error extracting export table: {e}")
return None
```
'''
for export in mfc90u_exports:
if export['Ordinal'] == ordinal:
addr = int(export['Address'], 16)
return view.get_function_at(addr)
return None
def rename_imported_function(bv: BinaryView, imported_dll_bndb: str = "D:\user\Desktop\mfc90u.dll.bndb"): ''' This function is used to rename all functions and data variables in bv whose name starts with 'Ordinalmfc90u'
:param bv: The binary view of the target binary
:param imported_dll_bndb: The path to the bndb file of mfc90u.dll which having the function names
'''
with load(imported_dll_bndb) as bv_mfc:
pass
def get_qualified_func_name(bv, func: Function):
type, names = demangle.demangle_ms(bv.platform, func.name)
if type is None:
return func.name
for i, name in enumerate(names):
names[i] = replace_template_classname(name)
qualified_name = demangle.get_qualified_name(names)
# if name_list.get(qualified_name) is not None:
# print(f'Name {qualified_name} already exists')
# print(name_list[qualified_name])
# print(func_mfc)
# continue
# name_list[qualified_name] = func_mfc
return qualified_name.replace("ATL::", "")
for _, data_var in bv.data_vars.items():
if data_var.name is not None and data_var.name.startswith('Ordinal_mfc90u') and isinstance(data_var.type, PointerType):
if not data_var.symbol:
continue
ordinal = data_var.symbol.ordinal
func_mfc = get_function_by_ordinal(bv_mfc, ordinal)
if func_mfc is None:
print(f'Function with ordinal {ordinal} not found')
continue
qualified_name = get_qualified_func_name(bv, func_mfc)
# print(qualified_name)
data_var.type = Type.pointer(bv.arch, func_mfc.type)
data_var.name = qualified_name
for func in bv.functions:
if func.name is not None and func.name.startswith('Ordinal_mfc90u'):
ordinal = func.symbol.ordinal
func_mfc = get_function_by_ordinal(bv_mfc, ordinal)
if func_mfc is None:
continue
qualified_name = get_qualified_func_name(bv, func_mfc)
func.type = func_mfc.type
func.name = qualified_name
rename_imported_function(bv)
This is a kind of easy task but it might take a bit of time before we get to it. What needs to happen is we need to create an ordinal-only TypeLibrary for all of the MFC libraries. So we first open and apply the pdb then write a script that exports a type library from this.
FWIW, this is something Ghidra does as well. It ships XML files mapping the ordinal numbers to symbol names. e.g.: https://github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/Features/Base/data/symbols/win64/mfc140u.exports
What is the feature you'd like to have? Rename imported ordinal function to its original name which can be get from pdb.
Is your feature request related to a problem? Use case: I am dealing with a crackme that uses MFC, and the import functions are all like Ordinal_mfc90u_286. I loaded the MFC DLL in binja and applied the PDB. Is there a simple way to rename all ordinal functions in crackme based on the information of the MFC dll that has loaded the PDB?
Are any alternative solutions acceptable? No
Additional Information: