├── README.md ├── demo.gif └── nymaim_idb_cleaner.py /README.md: -------------------------------------------------------------------------------- 1 | # Easy Way Nymaim 2 | An IDA Pro script for creating a clearer IDB for Nymaim malware. 3 | 4 | Nymaim, to obfuscate its code uses a technique I have called "Unreferenced Call" (I don't know if there is another, more precise, name). To do this, it has some functions (115 in almost every sample I have tested) that receive two parameters. Then it does a mathematical operation between both arguments which will result in the address of the function to be executed. 5 | 6 | This IDA Python script I wrote is calculating the result given by the operation inside the "Unreferenced Call" and adds a comment with the value to where the function would jump. This way, reversing and debugging the binary become much easier. 7 | 8 | # Usage/Demo 9 | ![Demo](https://raw.githubusercontent.com/d00rt/easy_way_nymaim/master/demo.gif) 10 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d00rt/easy_way_nymaim/f6ff9472bf2e4fb7f14774a1b1b35f8061a1963b/demo.gif -------------------------------------------------------------------------------- /nymaim_idb_cleaner.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import idc 3 | import pdb 4 | 5 | 6 | PATTRN = "55 89 e5 50 8b 45 04 89 45 10 8b 45 0c ?? 45 08" 7 | COUNT = 0 8 | MOVE_FUNC_ADDR = None # 0x41F271 9 | 10 | 11 | PERFORM = [ 12 | "03", # ADD 13 | "33", # XOR 14 | "2B" # SUB 15 | ] 16 | 17 | 18 | REGISTRY_TABLE = { 19 | 0x8: "EAX", 20 | 0x9: "ECX", 21 | 0xA: "EDX", 22 | 0xB: "EBX", 23 | 0xC: "???", 24 | 0xD: "EBP", 25 | 0xE: "ESI", 26 | 0xF: "EDI " 27 | } 28 | 29 | 30 | def get_value_from_address(address, offset=0): 31 | return struct.unpack("I", idc.GetManyBytes(address + offset, 0x4))[0] 32 | 33 | 34 | def get_value_from_address2(address, offset=4): 35 | return (struct.unpack("I", idc.GetManyBytes(address + offset, 0x4)[::1])[0] & 0xFFFFFFFF) 36 | 37 | 38 | def get_address(address, operation): 39 | num1 = get_value_from_address2(address, -4) 40 | num2 = get_value_from_address2(address, -9) 41 | 42 | if operation == "03": 43 | o1 = (0xFFFFFFFF & (num2 + num1)) 44 | 45 | if operation == "33": 46 | o1 = (0xFFFFFFFF & (num2 ^ num1)) 47 | 48 | if operation == "2B": 49 | o1 = (0xFFFFFFFF & (num2 - num1)) 50 | 51 | next_instuction = address + 5 52 | 53 | next_inst1 = (0xFFFFFFFF & (next_instuction + o1)) 54 | 55 | idc.MakeComm(address, "Jump to {address1}".format(address1=hex(next_inst1).replace("L", ''))) 56 | 57 | 58 | def find_unreferenced_calls(operation): 59 | global COUNT 60 | xrefs = [] 61 | ea = 0 62 | 63 | while ea != BADADDR: 64 | ea = idc.FindBinary(ea, SEARCH_NEXT|SEARCH_DOWN|SEARCH_CASE, PATTRN.replace("??", operation)) 65 | 66 | for xref in XrefsTo(ea): 67 | if xref.frm not in xrefs: 68 | COUNT += 1 69 | 70 | # xrefs.append(xref.frm) 71 | MakeName(xref.to, "unreferenced_call_{count}".format(count=COUNT)) 72 | 73 | # MakeName(xref.to, "") 74 | get_address(xref.frm, operation) 75 | 76 | 77 | def move_registry_to_the_stack(funaddr): 78 | MakeName(funaddr, "move_registry") 79 | 80 | for xref in XrefsTo(funaddr): 81 | argument = ord(GetManyBytes(xref.frm - 1, 0x1)) 82 | registry = REGISTRY_TABLE[argument] 83 | MakeComm(xref.frm, "MOV [ESP] , {reg}".format(reg=registry)) 84 | 85 | ## 86 | # MAIN 87 | ## 88 | 89 | print "[*] Stage 1: Looking for the unreferenced calls" 90 | for op in PERFORM: 91 | find_unreferenced_calls(op) 92 | print "\t[+] Done. {count} unreferenced calls found.".format(count=COUNT) 93 | 94 | # print "[*] Stage 2: Renaming regisrty custom MOV" 95 | # move_registry_to_the_stack(MOVE_FUNC_ADDR) 96 | # print "\t[+] Done." 97 | --------------------------------------------------------------------------------