[{"content":"Sample bazaar.abuse.ch — aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b\nfrom pathlib import Path WORKING_DIR = Path(\u0026#39;/path/to/rhadamanthys/\u0026#39;) SAMPLE_PATH = WORKING_DIR.joinpath(\u0026#39;aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b.exe\u0026#39;) DB_PATH = WORKING_DIR.joinpath(\u0026#39;rdm-testing.bndb\u0026#39;) 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:\nx = typelibrary.TypeLibrary.from_name(bv.arch, \u0026#39;amsi.dll\u0026#39;) bv.add_type_library(x) ResolveModule (0x14000d520) Prototype: HANDLE rdm_ResolveModule(module_ref*)\nstruct 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\u0026rsquo;s output variable.\n# Create the struct rdm_string_builder = StructureBuilder.create( members=[(IntegerType.create(8, False), \u0026#39;len\u0026#39;), (IntegerType.create(8, False), \u0026#39;unknown\u0026#39;), (ArrayType.create(CharType.create(), 16), \u0026#39;string\u0026#39;)]) view.define_user_type(\u0026#39;rdm_string\u0026#39;, 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 = \u0026#39;rdm_ResolveModule\u0026#39; rdm_string = view.get_type_by_name(\u0026#39;rdm_string\u0026#39;) 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[\u0026#39;string\u0026#39;][:v.value[\u0026#39;len\u0026#39;]].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\u0026#39;h_{module_name}\u0026#39; dest_var.name = var_name name_and_type_next_data_var(view, dest_var, xref.address, var_name, view.get_type_by_name(\u0026#39;HMODULE\u0026#39;)) Recovered modules: kernel32.dll, ole32, oleaut32, mscoree.\n# load the appropriate type lib for each module for module in runtime_loaded_modules: if module[-4:] != \u0026#39;.dll\u0026#39;: module = f\u0026#39;{module}.dll\u0026#39; type_lib = typelibrary.TypeLibrary.from_name(view.arch, module) if type_lib: view.add_type_library(type_lib) else: display(f\u0026#39;No type library for {module}\u0026#39;) ResolveFunction (0x14000d880) target_func = view.get_function_at(0x14000d880) target_func.name = \u0026#39;rdm_ResolveFunction\u0026#39; 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.\nConvertUTF8toUTF16 (0x14000f5d0) target_func = view.get_function_at(0x14000f5d0) target_func.name = \u0026#39;rdm_ConvertUTF8toUTF16\u0026#39; rdm_string = view.get_type_by_name(\u0026#39;rdm_string\u0026#39;) 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[\u0026#39;string\u0026#39;][:v.value[\u0026#39;len\u0026#39;]].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\u0026#39;h_{module_name}\u0026#39; dest_var.name = var_name This pulls additional ntdll.dll and kernel32.dll references out of the UTF-8/UTF-16 conversion path.\nResolveFunction2 (0x14001a490) target_func = view.get_function_at(0x14001a490) target_func.name = \u0026#39;rdm_ResolveFunction2\u0026#39; 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.\nview.update_analysis_and_wait() view.save_auto_snapshot() ","permalink":"https://malware.engineer/posts/rhadamanthys-mar-2024/","summary":"\u003ch2 id=\"sample\"\u003eSample\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://bazaar.abuse.ch/download/aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b\"\u003ebazaar.abuse.ch — \u003ccode\u003eaa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003epathlib\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e Path\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWORKING_DIR \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e Path(\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#39;/path/to/rhadamanthys/\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSAMPLE_PATH \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e WORKING_DIR\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003ejoinpath(\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#39;aa1b1384dbf123607f6f63132f355e7486ccd67a0e03d3af2db40a333b70797b.exe\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDB_PATH \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e WORKING_DIR\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003ejoinpath(\u003cspan style=\"color:#a5d6ff\"\u003e\u0026#39;rdm-testing.bndb\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eview \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e binaryninja\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003eopen_view(SAMPLE_PATH)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eview\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003ecreate_database(DB_PATH)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"helpers\"\u003eHelpers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# Search type libraries for matching name, return prototype.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#d2a8ff;font-weight:bold\"\u003esearch_type_libraries\u003c/span\u003e(fname: str, view: BinaryView):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#8b949e;font-style:italic\"\u003e# not super efficient to search every single time...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#ff7b72\"\u003efor\u003c/span\u003e typelib \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003ein\u003c/span\u003e view\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003etype_libraries:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ff7b72\"\u003efor\u003c/span\u003e name, obj \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003ein\u003c/span\u003e typelib\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003enamed_objects\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003eitems():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#ff7b72\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003enot\u003c/span\u003e isinstance(obj, FunctionType):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#ff7b72\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#ff7b72\"\u003eif\u003c/span\u003e fname \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e==\u003c/span\u003e name:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#ff7b72\"\u003ereturn\u003c/span\u003e obj\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#d2a8ff;font-weight:bold\"\u003ename_and_type_next_data_var\u003c/span\u003e(view: BinaryView, v: Variable, address: int, name: str, new_type: Type):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    mlil \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e view\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003eget_functions_containing(address)[\u003cspan style=\"color:#a5d6ff\"\u003e0\u003c/span\u003e]\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003emlil\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#ff7b72\"\u003efor\u003c/span\u003e op \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003ein\u003c/span\u003e mlil\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003eget_var_uses(v):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ff7b72\"\u003eif\u003c/span\u003e isinstance(op, MediumLevelILStore) \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003eand\u003c/span\u003e isinstance(op\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003edest, MediumLevelILConstPtr):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            data_var \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e view\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003eget_data_var_at(op\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003edest\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003econstant)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            data_var\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            data_var\u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e.\u003c/span\u003etype \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e new_type\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdding a type library:\u003c/p\u003e","title":"Binary Ninja Automation: Naming and Typing Variables in Dynamic API Resolution"},{"content":"Static Emotet Configuration Extraction The goal here is to reproduce this technique from VMRay\u0026rsquo;s post using Binary Ninja. This post from Open Analysis was also very helpful. With those posts as the foundation I was able to focus on the Binary Ninja API.\nThis turned out to be much easier than anticipated, Binary Ninja\u0026rsquo;s High Level Intermediate Language did most of the work once I figured out how to access it.\nSample used: c688e079a16b3345c83a285ac2ae8dd48680298085421c225680f26ceae73eb7\nWhy HLIL The VMRay post linked above does an excellent job of explaining why using an Intermediate Representation is a good way to tackle configuration for samples like this. I highly suggest you read that before continuing.\nAs you can see below there is a ton of garbage happening in this function.\nSwitching to HLIL makes it clear most of the instructions in this function is junk and can be ignored. This makes it relatively easy to extract the data we need.\nfrom ipaddress import ip_address, IPv4Address import struct from typing import Tuple import binaryninja Sample sha256: c688e079a16b3345c83a285ac2ae8dd48680298085421c225680f26ceae73eb7\nFunctions containing IPs are distinct variables.\nview = binaryninja.open_view(\u0026#34;./c688e079a16b3345c83a285ac2ae8dd48680298085421c225680f26ceae73eb7\u0026#34;) Create Target Function List I used the following criteria for selecting functions to process.\nfunction signature int64_t sub_XXXXXXX(int32_t*, int32_t*) must have exactly one basic block I thought more criteria would be needed but this worked just fine, granted this was only used on a single sample.\ndef check_parameter_types(params): for param in params: # if params don\u0026#39;t match criteria bail from loop if not (param.type and param.type.get_string() == \u0026#39;int32_t*\u0026#39;): return False return True c2_funcs = [] for f in view.functions: if len(f.parameter_vars.vars) == 2 and len(f.basic_blocks) == 1: if check_parameter_types(f.parameter_vars.vars): c2_funcs.append(f) display(c2_funcs) Extract C2 values from Function Body Get the address where each parameter value is set. Iterate over the HLIL instructions to find our reference address. Extract constant and convert. def extract_values(func) -\u0026gt; Tuple[IPv4Address, int]: # Get references to parameters ip_arg = func.parameter_vars.vars[0] port_arg = func.parameter_vars.vars[1] # Address where parameters are referenced ip_ref = func.get_hlil_var_refs(ip_arg)[0] port_ref = func.get_hlil_var_refs(port_arg)[0] for instr in func.hlil.instructions: if instr.address == ip_ref.address: # and instr.operation == HighLevelILOperation.HLIL_ASSIGN: 17 ip = IPv4Address(instr.operands[1].value.value.to_bytes(4, byteorder=\u0026#39;little\u0026#39;)) elif instr.address == port_ref.address: port_bytes = instr.operands[1].value.value.to_bytes(4, byteorder=\u0026#39;little\u0026#39;) port = struct.unpack(\u0026#39;\u0026lt;H\u0026#39;,port_bytes[2:4])[0] return (ip, port) for func in c2_funcs: try: ip, port = extract_values(func) print(f\u0026#39;{ip}:{port}\u0026#39;) except (IndexError, TypeError) as err: print(f\u0026#39;{func.name} failed\u0026#39;) Extracted C2s (63 entries) 206.189.28.199:8080 164.68.99.3:8080 51.91.76.89:8080 185.4.135.165:8080 58.227.42.236:80 213.241.20.155:443 1.234.21.73:7080 159.65.88.10:8080 160.16.142.56:8080 91.207.28.33:8080 216.158.226.206:443 63.142.250.212:443 150.95.66.124:8080 51.91.7.5:8080 188.44.20.25:443 94.23.45.86:4143 82.165.152.127:8080 163.44.196.120:8080 5.9.116.246:8080 153.126.146.25:7080 103.75.201.2:443 172.104.251.154:8080 185.157.82.211:8080 79.137.35.198:8080 201.94.166.162:443 45.176.232.124:443 189.126.111.200:7080 209.250.246.206:443 51.254.140.238:7080 209.126.98.206:8080 103.43.46.182:443 167.99.115.35:8080 131.100.24.231:80 72.15.201.15:8080 151.106.112.196:8080 45.235.8.30:8080 27.54.89.58:8080 103.132.242.26:8080 146.59.226.45:443 101.50.0.91:8080 102.222.215.74:443 1.234.2.232:8080 183.111.227.137:8080 45.118.115.99:8080 77.81.247.144:8080 149.56.131.28:8080 196.218.30.83:443 103.70.28.102:8080 134.122.66.193:8080 203.114.109.124:443 173.212.193.249:8080 46.55.222.11:443 197.242.150.244:8080 209.97.163.214:443 212.24.98.99:8080 167.172.253.162:8080 119.193.124.41:7080 185.8.212.130:7080 110.232.117.186:8080 107.182.225.142:8080 212.237.17.99:8080 129.232.188.93:443 158.69.222.101:443 Struct Example sha256: 9a4d2c6776f97e0afb4a0a99bfd40b34ac7ac2932693e587161536bb9acc9497\nIn this slightly more complex example C2 data is referenced via a struct which requires a bit more setup than the first example. Other than the struct everything else is similar.\nview2 = binaryninja.open_view(\u0026#34;./9a4d2c67-emotet.dll\u0026#34;) Create Target Function List Same idea as above with the appropriate function signature.\nc2_funcs_2 = [] for f in view2.functions: if len(f.parameter_vars.vars) == 1 and len(f.basic_blocks) == 1: if check_parameter_types(f.parameter_vars.vars): c2_funcs_2.append(f) display(c2_funcs_2) Create the struct The structure for c2 data in C is defined below:\nstruct C2 { int32_t ip int32_t port } Creating this in binary ninja is relatively simple with the proper documentation. Once the struct is created it needs to be registered with the binary view. As the documentation says, the structure and it\u0026rsquo;s name are defined separately. That mapping is held in the view, hence the need to register the struct with define_user_type.\n# Create the struct c2_struct = Type.structure(members=[Type.int(4, False, \u0026#39;ip\u0026#39;),Type.int(4, False, \u0026#39;port\u0026#39;)]) # Register with view view2.define_user_type(\u0026#39;c2_struct\u0026#39;, c2_struct) Extract Values This function is very similar to the first extract values function with a few key changes.\nInstead of getting the reference to each parameter, we first set the type to a pointer to the c2_struct type and get references to that. Use offsets to determine which value we are dealing with. There might be a better way to do this but I\u0026rsquo;m relatively new to Binary Ninja and haven\u0026rsquo;t figured it out yet. UPDATE: Use instr = func.get_low_level_il_at(ref).high_level_il to get our instruction directly rather than iterating over all instructions in the function. I have not been able to get this to work on the original sample. def extract_values_2(arch, func) -\u0026gt; Tuple[IPv4Address, int]: c2 = func.parameter_vars.vars[0] c2.name = \u0026#39;c2_struct\u0026#39; # set the type c2.type = Type.pointer(arch, c2_struct) # get references refs = [ref.address for ref in func.get_hlil_var_refs(c2)] for ref in refs: # go through low level IL to get high level instr = func.get_low_level_il_at(ref).high_level_il if instr.operands[0].offset == 0: ip = IPv4Address(instr.operands[1].value.value.to_bytes(4, byteorder=\u0026#39;little\u0026#39;)) elif instr.operands[0].offset == 4: port_bytes = instr.operands[1].value.value.to_bytes(4, byteorder=\u0026#39;little\u0026#39;) port = struct.unpack(\u0026#39;\u0026lt;H\u0026#39;,port_bytes[2:4])[0] return (ip, port) Process Functions Once setup is complete, just process the list of functions and print the C2 data.\nfor func in c2_funcs_2: try: ip, port = extract_values_2(view2.arch, func) print(f\u0026#39;{ip}:{port}\u0026#39;) except (IndexError, TypeError) as err: print(f\u0026#39;{func.name} failed\u0026#39;) Extracted C2s (64 entries) 73.255.26.122:12165 65.117.236.252:35278 108.164.161.77:11218 202.29.237.114:8080 110.251.57.20:56526 29.57.112.54:60474 25.75.78.170:23341 112.93.218.223:49565 60.130.68.172:14401 58.147.102.233:8030 90.243.35.105:28965 115.94.169.146:41714 30.56.33.4:32895 17.10.201.147:48369 69.124.172.227:64455 100.94.178.152:50457 198.211.118.165:443 62.15.215.195:32086 142.93.76.76:7080 108.43.184.195:16490 102.37.33.173:31347 165.227.153.100:8080 51.247.86.38:970 0.0.0.0:0 73.204.243.234:58720 30.130.55.57:48205 44.90.202.236:30791 86.1.1.244:52762 89.26.150.193:51072 118.22.245.177:14480 57.238.78.1:61990 51.0.140.144:52538 37.76.233.246:43490 86.156.61.105:10249 109.14.95.27:9268 75.202.215.95:76 68.22.90.86:60712 90.115.43.9:24380 98.99.175.223:18253 29.176.5.126:1313 20.85.29.7:33464 159.65.163.220:443 99.249.232.6:54384 106.162.95.165:42569 20.242.46.35:11211 47.130.199.198:40935 203.217.140.239:8080 110.34.174.151:33232 37.213.70.226:5112 100.163.237.26:50860 52.235.208.107:57114 91.143.35.250:37303 128.199.93.156:7080 114.26.45.46:56163 99.17.231.243:39482 90.188.179.131:63754 198.27.67.35:8080 37.8.5.45:42542 41.190.19.230:11714 38.218.214.127:24861 76.202.173.217:13855 75.55.249.32:60920 87.180.53.36:33400 116.125.120.88:443 ","permalink":"https://malware.engineer/posts/binary-ninja-hlil-emotet/","summary":"\u003ch2 id=\"static-emotet-configuration-extraction\"\u003eStatic Emotet Configuration Extraction\u003c/h2\u003e\n\u003cp\u003eThe goal here is to reproduce \u003ca href=\"https://www.vmray.com/cyber-security-blog/statically-extracting-emotet-configuration/\"\u003ethis technique\u003c/a\u003e from VMRay\u0026rsquo;s post using Binary Ninja. \u003ca href=\"https://research.openanalysis.net/emotet/emulation/config/dumpulator/malware/2022/05/19/emotet_x64_emulation.html\"\u003eThis post\u003c/a\u003e from Open Analysis was also very helpful. With those posts as the foundation I was able to focus on the Binary Ninja API.\u003c/p\u003e\n\u003cp\u003eThis turned out to be much easier than anticipated, Binary Ninja\u0026rsquo;s High Level Intermediate Language did most of the work once I figured out how to access it.\u003c/p\u003e\n\u003cp\u003eSample used: \u003ccode\u003ec688e079a16b3345c83a285ac2ae8dd48680298085421c225680f26ceae73eb7\u003c/code\u003e\u003c/p\u003e","title":"Using Binary Ninja's HLIL for Config Extraction"},{"content":"Overview Extract configuration from a process memory dump containing a running beacon. Initial extraction on the full minidump file fails (TODO: figure out exactly why it fails). Using yara to find the segment where beacon is residing passing that segment to dissect.cobaltstrike allows the proper configuration extraction.\nLibraries dissect.cobaltstrike minidump yara Samples Sample was chosen somewhat randomly from the results of this virustotal search.\nVT Search: crowdsourced_yara_rule:000aa75fc2|CobaltStrike_Sleep_Decoder_Indicator trid:\u0026quot;Windows Minidump\u0026quot;\nfrom dissect.cobaltstrike.beacon import BeaconConfig from dissect.cobaltstrike import beacon from minidump.minidumpfile import MinidumpFile import yara from pprint import pprint SAMPLE = \u0026#34;../../malware/minidumps/beacon.mdmp\u0026#34; Attempt Extraction on full dump Using the normal method and the iter_beacon_config_blocks method recommened by the documentation.\nBoth of these methods fail to extract a valid configuration.\nbconfig = BeaconConfig.from_path(SAMPLE, all_xor_keys=True) bconfig.version # \u0026lt;BeaconVersion \u0026#39;Unknown\u0026#39;, tuple=None, date=None\u0026gt; found = False with open(SAMPLE, \u0026#39;rb\u0026#39;) as f: for config_block, extra_data in beacon.iter_beacon_config_blocks(f): try: bconfig = beacon.BeaconConfig(config_block) if not len(bconfig.domains): continue found = True except ValueError: continue print(bconfig, bconfig.domain_uri_pairs) if not found: print(\u0026#34;No beacons found.\u0026#34;) # No beacons found. Find Beacon Segment Yara Setup rule = yara.compile(source=r\u0026#39;\u0026#39;\u0026#39; rule CobaltStrike_Sleep_Decoder_Indicator { meta: description = \u0026#34;Detects CobaltStrike sleep_mask decoder\u0026#34; author = \u0026#34;yara@s3c.za.net\u0026#34; date = \u0026#34;2021-07-19\u0026#34; strings: $sleep_decoder = { 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 4C 8B 51 08 41 8B F0 48 8B EA 48 8B D9 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 } condition: $sleep_decoder } \u0026#39;\u0026#39;\u0026#39;) matches = rule.match(SAMPLE) for match in matches: for s in match.strings: print(f\u0026#39;Rule: {match.rule}\\t\\tOffset: 0x{s[0]:x}\\tString: {s[1]}\u0026#39;) Rule: CobaltStrike_Sleep_Decoder_Indicator Offset: 0xafc9c1 String: $sleep_decoder Rule: CobaltStrike_Sleep_Decoder_Indicator Offset: 0xb9db9c String: $sleep_decoder Setup Minidump md = MinidumpFile.parse(SAMPLE) print(f\u0026#39;OS: {md.sysinfo.OperatingSystem}\\tProcess: {md.modules.modules[0].name}\u0026#39;) # OS: Windows Server 2012 R2 Process: C:\\Windows\\System32\\svchost.exe Find the segments Use minidump to iterate over all segments and run the yara rule on each segments contents.\n# segment_addr, offset, buf decoders = [] reader = md.get_reader() for segment in md.memory_segments.memory_segments: va = segment.start_virtual_address size = segment.size buf = reader.read(va , size) matches = rule.match(data=buf) for match in matches: for s in match.strings: for info in md.memory_info.infos: if info.BaseAddress == va: print(f\u0026#39;String: {s[1]}\\tSegment Addr: 0x{va:x}\\tOffset: 0x{s[0]:x}\\tSize {size:x}\\tProtect: {info.Protect}\\tType: {info.Type}\u0026#39;) decoders.append((va, s[0], buf)) String: $sleep_decoder Segment Addr: 0x25bda48000 Offset: 0x442cd Size d1000 Protect: PAGE_READWRITE Type: MEM_PRIVATE String: $sleep_decoder Segment Addr: 0x25bdb50000 Offset: 0x0 Size 2000 Protect: PAGE_READWRITE Type: MEM_PRIVATE Find modules If the matches happened to fall within a loaded module this code would provide the module names and could be extracted and written to disk.\n# find in modules, don\u0026#39;t need since our type is PRIVATE (not MAPPED or IMAGE) for match in decoders: for module in md.modules.modules: if module.inrange(match[0]): print(module.name) Extract Configuration Pass matching segment data to dissect.cobaltstrike and print config.\nfor match in decoders: try: bconfig = BeaconConfig.from_bytes(match[2], all_xor_keys=True) pprint(bconfig.settings) except ValueError as exc: print(f\u0026#39;Segment: 0x{match[0]:x}: Config not found.\u0026#39;) mappingproxy(OrderedDict([(\u0026#39;SETTING_PROTOCOL\u0026#39;, 8), (\u0026#39;SETTING_PORT\u0026#39;, 443), (\u0026#39;SETTING_SLEEPTIME\u0026#39;, 45000), (\u0026#39;SETTING_MAXGET\u0026#39;, 1403644), (\u0026#39;SETTING_JITTER\u0026#39;, 37), (\u0026#39;SETTING_PUBKEY\u0026#39;, \u0026#39;c1b23fce5720138b846a78d3193d3ef1d2da0672d4c01b706ba0c29f3aab301f\u0026#39;), (\u0026#39;SETTING_DOMAINS\u0026#39;, \u0026#39;cp.ikmcontact.com,/jquery-3.3.1.min.js\u0026#39;), (\u0026#39;SETTING_DOMAIN_STRATEGY\u0026#39;, 0), (\u0026#39;SETTING_SPAWNTO_X86\u0026#39;, \u0026#39;%windir%\\\\syswow64\\\\dllhost.exe\u0026#39;), (\u0026#39;SETTING_SPAWNTO_X64\u0026#39;, \u0026#39;%windir%\\\\sysnative\\\\dllhost.exe\u0026#39;), (\u0026#39;SETTING_C2_VERB_GET\u0026#39;, \u0026#39;GET\u0026#39;), (\u0026#39;SETTING_C2_VERB_POST\u0026#39;, \u0026#39;POST\u0026#39;), (\u0026#39;SETTING_WATERMARK\u0026#39;, 23333), (\u0026#39;SETTING_USERAGENT\u0026#39;, \u0026#39;Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) \u0026#39; \u0026#39;like Gecko\u0026#39;), (\u0026#39;SETTING_SUBMITURI\u0026#39;, \u0026#39;/jquery-3.3.2.min.js\u0026#39;), (\u0026#39;SETTING_HOST_HEADER\u0026#39;, \u0026#39;Host: cp.ikmcontact.com\\r\\n\u0026#39;), ... ])) Segment: 0x25bdb50000: Config not found. ","permalink":"https://malware.engineer/posts/beacon-mdmp/","summary":"\u003ch2 id=\"overview\"\u003eOverview\u003c/h2\u003e\n\u003cp\u003eExtract configuration from a process memory dump containing a running beacon. Initial extraction on the full minidump file fails (TODO: figure out exactly why it fails). Using yara to find the segment where beacon is residing passing that segment to dissect.cobaltstrike allows the proper configuration extraction.\u003c/p\u003e\n\u003ch2 id=\"libraries\"\u003eLibraries\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://dissect-cobaltstrike.readthedocs.io/en/latest/index.html\"\u003edissect.cobaltstrike\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/skelsec/minidump\"\u003eminidump\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://yara.readthedocs.io/en/stable/index.html\"\u003eyara\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"samples\"\u003eSamples\u003c/h2\u003e\n\u003cp\u003eSample was chosen somewhat randomly from the results of this virustotal search.\u003c/p\u003e\n\u003cp\u003eVT Search: \u003ccode\u003ecrowdsourced_yara_rule:000aa75fc2|CobaltStrike_Sleep_Decoder_Indicator trid:\u0026quot;Windows Minidump\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003edissect.cobaltstrike.beacon\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e BeaconConfig\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003edissect.cobaltstrike\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e beacon\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eminidump.minidumpfile\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e MinidumpFile\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eyara\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ff7b72\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003epprint\u003c/span\u003e \u003cspan style=\"color:#ff7b72\"\u003eimport\u003c/span\u003e pprint\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSAMPLE \u003cspan style=\"color:#ff7b72;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a5d6ff\"\u003e\u0026#34;../../malware/minidumps/beacon.mdmp\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"attempt-extraction-on-full-dump\"\u003eAttempt Extraction on full dump\u003c/h2\u003e\n\u003cp\u003eUsing the normal method and the \u003ca href=\"https://dissect-cobaltstrike.readthedocs.io/en/latest/examples.html#memory-dumps\"\u003eiter_beacon_config_blocks method recommened by the documentation\u003c/a\u003e.\u003c/p\u003e","title":"Extracting Beacon Configuration from Minidump"}]