Sample

bazaar.abuse.ch — aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b

from pathlib import Path

WORKING_DIR = Path('/path/to/rhadamanthys/')
SAMPLE_PATH = WORKING_DIR.joinpath('aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b.exe')
DB_PATH = WORKING_DIR.joinpath('rdm-testing.bndb')

view = binaryninja.open_view(SAMPLE_PATH)
view.create_database(DB_PATH)

Helpers

# Search type libraries for matching name, return prototype.
def search_type_libraries(fname: str, view: BinaryView):
    # not super efficient to search every single time...
    for typelib in view.type_libraries:
        for name, obj in typelib.named_objects.items():
            if not isinstance(obj, FunctionType):
                continue
            if fname == name:
                return obj
def name_and_type_next_data_var(view: BinaryView, v: Variable, address: int, name: str, new_type: Type):
    mlil = view.get_functions_containing(address)[0].mlil

    for op in mlil.get_var_uses(v):
        if isinstance(op, MediumLevelILStore) and isinstance(op.dest, MediumLevelILConstPtr):
            data_var = view.get_data_var_at(op.dest.constant)
            data_var.name = name
            data_var.type = new_type

Adding a type library:

x = typelibrary.TypeLibrary.from_name(bv.arch, 'amsi.dll')
bv.add_type_library(x)

ResolveModule (0x14000d520)

Prototype: HANDLE rdm_ResolveModule(module_ref*)

struct module_ref {
    uint64_t string_len;
    uint64_t unknown;
    char     module_name[0x8];
};

Loop over xrefs, set the data var passed in as our struct, decode the module name, then load the matching type library and rename the call’s output variable.

# Create the struct
rdm_string_builder = StructureBuilder.create(
    members=[(IntegerType.create(8, False), 'len'),
             (IntegerType.create(8, False), 'unknown'),
             (ArrayType.create(CharType.create(), 16), 'string')])
view.define_user_type('rdm_string', rdm_string_builder)
# Iterate over all calls to rdm_ResolveModule.
# Re-type the data var passed to rdm_string.
# Extract the module name from the newly typed struct.
# Rename the output variable.
runtime_loaded_modules = []

target_func = view.get_function_at(0x14000d520)
target_func.name = 'rdm_ResolveModule'
rdm_string = view.get_type_by_name('rdm_string')
for xref in target_func.caller_sites:
    if isinstance(xref.mlil, MediumLevelILCall):
        param = xref.mlil.params[0]
        if isinstance(param, MediumLevelILConstPtr):
            v = view.get_data_var_at(param.constant)
            v.type = rdm_string

            module_name = v.value['string'][:v.value['len']].decode()
            v.name = module_name
            if module_name not in runtime_loaded_modules:
                runtime_loaded_modules.append(module_name)

            dest_var = xref.mlil.output[0]
            var_name = f'h_{module_name}'
            dest_var.name = var_name
            name_and_type_next_data_var(view, dest_var, xref.address, var_name, view.get_type_by_name('HMODULE'))

Recovered modules: kernel32.dll, ole32, oleaut32, mscoree.

# load the appropriate type lib for each module
for module in runtime_loaded_modules:
    if module[-4:] != '.dll':
        module = f'{module}.dll'

    type_lib = typelibrary.TypeLibrary.from_name(view.arch, module)
    if type_lib:
        view.add_type_library(type_lib)
    else:
        display(f'No type library for {module}')

ResolveFunction (0x14000d880)

target_func = view.get_function_at(0x14000d880)
target_func.name = 'rdm_ResolveFunction'
for xref in target_func.caller_sites:
    if isinstance(xref.mlil, MediumLevelILCall):
        variable = xref.mlil.output[0]
        # 2nd param: rdm_ResolveFunc(HMODULE, string function name)
        func_name = xref.mlil.params[1].string[0]
        variable.name = func_name
        proto = search_type_libraries(func_name, view)
        if proto is not None:
            proto_pointer = PointerType.create(view.arch, proto)
            variable.type = proto_pointer
            name_and_type_next_data_var(view, variable, xref.address, func_name, proto_pointer)

Names recovered include OpenProcess, WideCharToMultiByte, GetThreadContext, AddVectoredExceptionHandler, HeapCreate, CLRCreateInstance, CreateToolhelp32Snapshot, etc.

ConvertUTF8toUTF16 (0x14000f5d0)

target_func = view.get_function_at(0x14000f5d0)
target_func.name = 'rdm_ConvertUTF8toUTF16'
rdm_string = view.get_type_by_name('rdm_string')
for xref in target_func.caller_sites:
    if isinstance(xref.mlil, MediumLevelILCall):
        param = xref.mlil.params[0]
        if isinstance(param, MediumLevelILConstPtr):
            v = view.get_data_var_at(param.constant)
            v.type = rdm_string

            module_name = v.value['string'][:v.value['len']].decode()
            v.name = module_name
            if module_name not in runtime_loaded_modules:
                runtime_loaded_modules.append(module_name)

        dest_var = xref.mlil.output[0]
        var_name = f'h_{module_name}'
        dest_var.name = var_name

This pulls additional ntdll.dll and kernel32.dll references out of the UTF-8/UTF-16 conversion path.

ResolveFunction2 (0x14001a490)

target_func = view.get_function_at(0x14001a490)
target_func.name = 'rdm_ResolveFunction2'
for xref in target_func.caller_sites:
    if isinstance(xref.mlil, MediumLevelILCall):
        variable = xref.mlil.output[0]
        # 2nd param: rdm_ResolveFunc(HMODULE, string function name)
        func_name = xref.mlil.params[1].string[0]
        variable.name = func_name
        proto = search_type_libraries(func_name, view)
        if proto is not None:
            proto_pointer = PointerType.create(view.arch, proto)
            variable.type = proto_pointer
            name_and_type_next_data_var(view, variable, xref.address, func_name, proto_pointer)

A second resolver covers VirtualProtect, GetProcAddress, GetModuleHandleA, LoadLibraryA, CreateFileW, GetModuleFileNameW, and the rest of the runtime API surface.

view.update_analysis_and_wait()
view.save_auto_snapshot()