├── XORCheck.py ├── idenLib ├── README.md └── idenLib.py ├── dumpDyn ├── README.md └── dumpDyn.py └── IOCTL_decode.py /XORCheck.py: -------------------------------------------------------------------------------- 1 | import idautils 2 | import idaapi 3 | import idc 4 | 5 | def main(): 6 | print "\n\n\n---------------- XORCheck ---------------" 7 | print "Interesting XORs:" 8 | ea = MinEA() 9 | 10 | while ea < MaxEA(): 11 | ea = FindText(ea, SEARCH_DOWN, 0, 0, "xor") 12 | eCode = idc.isCode(idc.GetFlags(ea)) 13 | if not eCode or ea == idc.BADADDR: 14 | break 15 | if idc.GetOpnd(ea, 0) == idc.GetOpnd(ea, 1): 16 | ea = idc.NextHead(ea) 17 | continue 18 | func = idaapi.get_func(ea) 19 | if func.flags & FUNC_LIB: 20 | ea = idc.NextHead(ea) 21 | continue 22 | print "loc: ", hex(ea), "dis: ", idc.GetDisasm(ea), "func: ", idc.get_func_name(ea) 23 | idc.MakeComm(ea, "XORs: check it!") 24 | ea = idc.NextHead(ea) 25 | 26 | 27 | print "\n--------------- XORCheck EOF --------------\n" 28 | 29 | 30 | main() -------------------------------------------------------------------------------- /idenLib/README.md: -------------------------------------------------------------------------------- 1 | # idenLib.py 2 | 3 | [`idenLib`](https://github.com/secrary/idenLib) (Library Function Identification ) plugin for `IDA Pro` 4 | 5 | ![ida_boost_2](https://user-images.githubusercontent.com/16405698/52437166-1bf3a680-2b0e-11e9-8212-7f017757133b.gif) 6 | 7 | ## Usage 8 | - Copy the plugin under `plugins` folder 9 | - Copy signatures under `SymEx` folder 10 | - Install dependencies: [`zstd`](https://pypi.org/project/zstd/) and [`capstone`](https://pypi.org/project/capstone/) (`pip2 install zstd capstone`) 11 | 12 | - You need to refresh signatures after adding new signature files 13 | ![refresh](https://user-images.githubusercontent.com/16405698/53285741-15933a80-375c-11e9-97cb-d38bb0b5c52e.png) 14 | 15 | 16 | ## Credits 17 | - Disassembly powered by [Capstone](https://www.capstone-engine.org/) 18 | - Decompressing powered by [zstd](https://github.com/facebook/zstd) 19 | - Icon by [freepik](https://www.flaticon.com/authors/freepik) 20 | -------------------------------------------------------------------------------- /dumpDyn/README.md: -------------------------------------------------------------------------------- 1 | # dumpDyn 2 | 3 | Lasha Khasaia [@_qaz_qaz](https://twitter.com/_qaz_qaz) 4 | 5 | If a process allocates a dynamic memory using `VirtualAlloc`, `HeapAlloc`, `new`, etc. and continues execution from that address, most of times, the memory address will be different for each different execution, it means that if we comment, rename variables or set breakpoints, nothing of this will be left in the next execution because the shellcode or code section will take a different memory address. 6 | 7 | `dumpDyn.py` is `IDAPython` plugin(script) which saves `comments`, `names`, `breakpoints`, `functions` from one execution to another. 8 | 9 | ## USAGE 10 | 11 | ![USAGE](https://user-images.githubusercontent.com/16405698/49311939-70f5b980-f4da-11e8-81d6-09bd083d4e49.PNG) 12 | 13 | If `VirtualAlloc`/`VirtualAllocEx` is used to allocate a dynamic memory (which is the case with most malware), you can use icons on the toolbar to `save` and `restore` your work: 14 | 15 | [DEMO: https://www.youtube.com/watch?v=Z53AlWPAwCc](https://www.youtube.com/watch?v=Z53AlWPAwCc) 16 | 17 | ![dumpdyn_1](https://user-images.githubusercontent.com/16405698/49311767-f7f66200-f4d9-11e8-81c5-8f8c648c0c9e.gif) 18 | 19 | In any other case(`HeapAlloc`, `malloc`, `new`, etc), you need to specify memory location and size: 20 | 21 | ![1](https://user-images.githubusercontent.com/16405698/49311821-26743d00-f4da-11e8-883a-7205df03125e.PNG) 22 | 23 | ![2](https://user-images.githubusercontent.com/16405698/49311822-270cd380-f4da-11e8-95e3-256634ff69be.PNG) 24 | 25 | ![3](https://user-images.githubusercontent.com/16405698/49311823-270cd380-f4da-11e8-8e93-e99276de14e0.gif) 26 | 27 | Restore functions from undefined data 28 | 29 | ![4](https://user-images.githubusercontent.com/16405698/49588512-cad50400-f95e-11e8-915e-4e7609fe1a06.gif) 30 | -------------------------------------------------------------------------------- /IOCTL_decode.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # device types 4 | 5 | #define FILE_DEVICE_BEEP 0x00000001 6 | #define FILE_DEVICE_CD_ROM 0x00000002 7 | #define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 8 | #define FILE_DEVICE_CONTROLLER 0x00000004 9 | #define FILE_DEVICE_DATALINK 0x00000005 10 | #define FILE_DEVICE_DFS 0x00000006 11 | #define FILE_DEVICE_DISK 0x00000007 12 | #define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 13 | #define FILE_DEVICE_FILE_SYSTEM 0x00000009 14 | #define FILE_DEVICE_INPORT_PORT 0x0000000a 15 | #define FILE_DEVICE_KEYBOARD 0x0000000b 16 | #define FILE_DEVICE_MAILSLOT 0x0000000c 17 | #define FILE_DEVICE_MIDI_IN 0x0000000d 18 | #define FILE_DEVICE_MIDI_OUT 0x0000000e 19 | #define FILE_DEVICE_MOUSE 0x0000000f 20 | #define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 21 | #define FILE_DEVICE_NAMED_PIPE 0x00000011 22 | #define FILE_DEVICE_NETWORK 0x00000012 23 | #define FILE_DEVICE_NETWORK_BROWSER 0x00000013 24 | #define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 25 | #define FILE_DEVICE_NULL 0x00000015 26 | #define FILE_DEVICE_PARALLEL_PORT 0x00000016 27 | #define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 28 | #define FILE_DEVICE_PRINTER 0x00000018 29 | #define FILE_DEVICE_SCANNER 0x00000019 30 | #define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a 31 | #define FILE_DEVICE_SERIAL_PORT 0x0000001b 32 | #define FILE_DEVICE_SCREEN 0x0000001c 33 | #define FILE_DEVICE_SOUND 0x0000001d 34 | #define FILE_DEVICE_STREAMS 0x0000001e 35 | #define FILE_DEVICE_TAPE 0x0000001f 36 | #define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 37 | #define FILE_DEVICE_TRANSPORT 0x00000021 38 | #define FILE_DEVICE_UNKNOWN 0x00000022 39 | #define FILE_DEVICE_VIDEO 0x00000023 40 | #define FILE_DEVICE_VIRTUAL_DISK 0x00000024 41 | #define FILE_DEVICE_WAVE_IN 0x00000025 42 | #define FILE_DEVICE_WAVE_OUT 0x00000026 43 | #define FILE_DEVICE_8042_PORT 0x00000027 44 | #define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 45 | #define FILE_DEVICE_BATTERY 0x00000029 46 | #define FILE_DEVICE_BUS_EXTENDER 0x0000002a 47 | #define FILE_DEVICE_MODEM 0x0000002b 48 | #define FILE_DEVICE_VDM 0x0000002c 49 | #define FILE_DEVICE_MASS_STORAGE 0x0000002d 50 | #define FILE_DEVICE_SMB 0x0000002e 51 | #define FILE_DEVICE_KS 0x0000002f 52 | #define FILE_DEVICE_CHANGER 0x00000030 53 | #define FILE_DEVICE_SMARTCARD 0x00000031 54 | #define FILE_DEVICE_ACPI 0x00000032 55 | #define FILE_DEVICE_DVD 0x00000033 56 | #define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 57 | #define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 58 | #define FILE_DEVICE_DFS_VOLUME 0x00000036 59 | #define FILE_DEVICE_SERENUM 0x00000037 60 | #define FILE_DEVICE_TERMSRV 0x00000038 61 | #define FILE_DEVICE_KSEC 0x00000039 62 | #define FILE_DEVICE_FIPS 0x0000003A 63 | #define FILE_DEVICE_INFINIBAND 0x0000003B 64 | #define FILE_DEVICE_VMBUS 0x0000003E 65 | #define FILE_DEVICE_CRYPT_PROVIDER 0x0000003F 66 | #define FILE_DEVICE_WPD 0x00000040 67 | #define FILE_DEVICE_BLUETOOTH 0x00000041 68 | #define FILE_DEVICE_MT_COMPOSITE 0x00000042 69 | #define FILE_DEVICE_MT_TRANSPORT 0x00000043 70 | #define FILE_DEVICE_BIOMETRIC 0x00000044 71 | #define FILE_DEVICE_PMI 0x00000045 72 | #define FILE_DEVICE_EHSTOR 0x00000046 73 | #define FILE_DEVICE_DEVAPI 0x00000047 74 | #define FILE_DEVICE_GPIO 0x00000048 75 | #define FILE_DEVICE_USBEX 0x00000049 76 | #define FILE_DEVICE_CONSOLE 0x00000050 77 | #define FILE_DEVICE_NFP 0x00000051 78 | #define FILE_DEVICE_SYSENV 0x00000052 79 | #define FILE_DEVICE_VIRTUAL_BLOCK 0x00000053 80 | #define FILE_DEVICE_POINT_OF_SERVICE 0x00000054 81 | #define FILE_DEVICE_STORAGE_REPLICATION 0x00000055 82 | #define FILE_DEVICE_TRUST_ENV 0x00000056 83 | #define FILE_DEVICE_UCM 0x00000057 84 | #define FILE_DEVICE_UCMTCPCI 0x00000058 85 | #define FILE_DEVICE_PERSISTENT_MEMORY 0x00000059 86 | #define FILE_DEVICE_NVDIMM 0x0000005a 87 | #define FILE_DEVICE_HOLOGRAPHIC 0x0000005b 88 | #define FILE_DEVICE_SDFXHCI 0x0000005c 89 | 90 | methods = {0 : "METHOD_BUFFERED", 1 : "METHOD_IN_DIRECT", 2 : "METHOD_OUT_DIRECT", 3 : "METHOD_NEITHER"} 91 | 92 | access_values = {0 : "FILE_ANY_ACCESS", 1 : "FILE_READ_ACCESS", 2 : "FILE_WRITE_ACCESS", 3 : "FILE_READ_ACCESS | FILE_WRITE_ACCESS"} 93 | 94 | def ctl_decode(CTL_CODE): 95 | method = CTL_CODE & 0b11 96 | devicetype = CTL_CODE >> 16; 97 | access = (CTL_CODE >> 14) & 0b11 98 | function = (CTL_CODE >> 2) & 0xfff 99 | 100 | print("\nDeviceType: {}, Function: {}, Method: {}, Access: {}\n\n".format(hex(devicetype), hex(function), methods[method], access_values[access])) 101 | 102 | def main(): 103 | print("\nUsage:\tctl_decode(code)\n") 104 | if len(sys.argv) > 1: 105 | ctl_decode(int(sys.argv[1])) # only base 10 106 | 107 | main() 108 | 109 | -------------------------------------------------------------------------------- /dumpDyn/dumpDyn.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2018, Lasha Khasaia @_qaz_qaz 4 | # Licensed under the GNU GPL v3. 5 | # 6 | # ------------------------------------------------------------------------------- 7 | 8 | import os 9 | import hashlib 10 | 11 | try: 12 | import cPickle as pickle 13 | except: 14 | import pickle 15 | 16 | import ida_name 17 | import ida_nalt 18 | import ida_dbg 19 | import ida_bytes 20 | import ida_kernwin 21 | import ida_segment 22 | import ida_auto 23 | import ida_funcs 24 | import idaapi 25 | import idautils 26 | 27 | MD5_hash_data_file = None 28 | SIGNATURE_SIZE = 0x10 29 | remove_on_exit_bpts = [] 30 | 31 | class MyDbgHook(ida_dbg.DBG_Hooks): 32 | def dbg_process_exit(self, pid, tid, ea, code): 33 | # remove breakpoints from the dynamically allocated memory 34 | global remove_on_exit_bpts 35 | for n in remove_on_exit_bpts: 36 | ida_dbg.del_bpt(n) 37 | remove_on_exit_bpts = [] 38 | 39 | 40 | class save_class(idaapi.action_handler_t): 41 | def __init__(self): 42 | idaapi.action_handler_t.__init__(self) 43 | 44 | def activate(self, ctx): 45 | save_x() 46 | 47 | return 1 48 | 49 | def update(self, ctx): 50 | return idaapi.AST_ENABLE_FOR_WIDGET if ctx.widget_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_WIDGET 51 | 52 | 53 | class restore_class(idaapi.action_handler_t): 54 | def __init__(self): 55 | idaapi.action_handler_t.__init__(self) 56 | 57 | def activate(self, ctx): 58 | restore_x() 59 | 60 | return 1 61 | 62 | def update(self, ctx): 63 | return idaapi.AST_ENABLE_FOR_WIDGET if ctx.widget_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_WIDGET 64 | 65 | 66 | def save_x(unique_name=None, start=None, size=None): 67 | ea = ida_kernwin.get_screen_ea() 68 | 69 | # signature 70 | if not unique_name: 71 | if not start: 72 | seg = ida_segment.getseg(ea) 73 | start = seg.start_ea 74 | sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) 75 | sig_hash = hashlib.md5(sig_bytes).hexdigest() 76 | unique_name = sig_hash 77 | 78 | if not start or not size: 79 | seg = ida_segment.getseg(ea) 80 | start = seg.start_ea 81 | size = seg.size() 82 | 83 | # (start_addr, end_addr, names, comms) 84 | saved_data = {} 85 | if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): 86 | with open(MD5_hash_data_file, "rb") as ifile: 87 | received_data = pickle.loads(ifile.read()) 88 | if received_data: 89 | saved_data = received_data 90 | 91 | # save names (func_names, labels, etc) 92 | # (addr, name, is_code) 93 | names_addr_name = [] 94 | names = idautils.Names() 95 | for addr, name in names: 96 | if start <= addr <= start + size: 97 | flags = ida_bytes.get_flags(addr) 98 | names_addr_name.append((addr - start, name, ida_bytes.is_code(flags))) 99 | 100 | # save comments 101 | comms_addr_type_comm = [] 102 | # (addr, TYPE, comment) 103 | # type 0:comment 1:rpt_comment 104 | end = start + size 105 | for i in range(start, end + 1): 106 | if ida_bytes.get_cmt(i, 0): # 0 Comment 107 | comms_addr_type_comm.append((i - start, 0, ida_bytes.get_cmt(i, 0))) 108 | if ida_bytes.get_cmt(i, 1): # 1 RptCmt 109 | comms_addr_type_comm.append((i - start, 1, ida_bytes.get_cmt(i, 1))) 110 | 111 | # breakpoints 112 | bpts_addr_size_type = [] 113 | bpt = ida_dbg.bpt_t() 114 | global remove_on_exit_bpts 115 | for i in range(start, end + 1): 116 | if ida_dbg.get_bpt(i, bpt): 117 | bpts_addr_size_type.append((i - start, bpt.size, bpt.type)) 118 | remove_on_exit_bpts.append(i) 119 | 120 | # functions 121 | funcs_addr = [] 122 | flag = ida_bytes.get_flags(start) 123 | if ida_bytes.is_func(flag): 124 | funcs_addr.append(0) # start addr 125 | next_func = ida_funcs.get_next_func(start) 126 | while next_func: 127 | funcs_addr.append(next_func.start_ea - start) 128 | next_func = ida_funcs.get_next_func(next_func.start_ea) 129 | 130 | 131 | # SAVE 132 | saved_data[unique_name] = (start, start + end, names_addr_name, comms_addr_type_comm, bpts_addr_size_type, funcs_addr) 133 | 134 | if MD5_hash_data_file: 135 | with open(MD5_hash_data_file, "wb") as ifile: 136 | serial_data = pickle.dumps(saved_data) 137 | ifile.write(serial_data) 138 | print("dumpDyn::save:\n\ 139 | Name: {}\n\ 140 | Start address: {}".format(unique_name, hex(start))) 141 | 142 | 143 | def restore_x(unique_name=None, start=None): 144 | ea = ida_kernwin.get_screen_ea() 145 | 146 | # signature 147 | if not unique_name: 148 | if not start: 149 | seg = ida_segment.getseg(ea) 150 | start = seg.start_ea 151 | sig_bytes = ida_bytes.get_bytes(start, SIGNATURE_SIZE) 152 | sig_hash = hashlib.md5(sig_bytes).hexdigest() 153 | unique_name = sig_hash 154 | 155 | if not start: 156 | seg = ida_segment.getseg(ea) 157 | start = seg.start_ea 158 | 159 | if MD5_hash_data_file and os.path.isfile(MD5_hash_data_file): 160 | with open(MD5_hash_data_file, "rb") as ifile: 161 | received_data = pickle.loads(ifile.read()) 162 | saved_data = received_data 163 | 164 | print("dumpDyn::restore\n\ 165 | Name: {}\n\ 166 | Restore address: {}\n".format(unique_name, hex(start))) 167 | 168 | # (start_addr, end_addr, names, comms, bpts, funcs) 169 | if unique_name in saved_data: 170 | current_data = saved_data[unique_name] 171 | 172 | # restore names 173 | names = current_data[2] 174 | 175 | for name in names: 176 | # names: (rel_addr, name, is_code) 177 | ida_name.set_name(start + name[0], name[1]) 178 | flags = ida_bytes.get_flags(start + name[0]) 179 | if name[2] and not ida_bytes.is_code(flags): 180 | ida_auto.auto_make_code(start + name[0]) 181 | 182 | # restore comments 183 | # comms: (rel_addr, TYPE, comment) 184 | comms = current_data[3] 185 | for comm in comms: 186 | # 0:MakeComm and 1:MakeRptCmt 187 | ida_bytes.set_cmt(start + comm[0], comm[2], comm[1]) 188 | 189 | # restore breakpoints 190 | # bpts: (rel_addr, size, type) 191 | bpts = current_data[4] 192 | for bpt in bpts: 193 | ida_dbg.add_bpt(start + bpt[0], bpt[1], bpt[2]) 194 | 195 | # restore functions 196 | funcs_addr = current_data[5] 197 | for addr in funcs_addr: 198 | ida_auto.auto_make_proc(start + addr) # make code & func 199 | 200 | 201 | def main(): 202 | print("\nUsage:\n\ 203 | save_x(\"unique_name\", start_addr, size) - save names, comments, breakpoints, functions\n\ 204 | restore_x(\"unique_name\", start_addr) - restore names, comments, breakpoints, functions\n\ 205 | Example:\n\t\ 206 | save_x(\"first_shellcode\", 0x12340000, 0x1000)\n\t\ 207 | restore_x(\"first_shellcode\", 0x12340000)\n\t\ 208 | save_x(\"f1\", here(), 0x1000)\n\t\ 209 | restore_x(\"f1\", here())\n\ 210 | \nBONUS: useful if a process allocated a new segment (e.g. VirtualAlloc) otherwise (HeapAlloc, new, etc.) use the first way\n\t\ 211 | save_x() == save_x(FIRST_0x10_BYTES_HASH_FROM_EA_SEGMENT, START_OF_EA_SEGMENT, SIZEOF_EA_SEGMENT)\n\t\ 212 | restore_x() == restore(FIRST_0x10_BYTES_HASH_FROM_EA_SEGMENT, START_OF_EA_SEGMENT)\n\ 213 | ") 214 | 215 | icon_data_save = "".join([ 216 | "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x10\x00\x00\x00\x10\x04\x03\x00" 217 | "\x00\x00\xED\xDD\xE2\x52\x00\x00\x00\x1E\x50\x4C\x54\x45\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 218 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xB7\x28\x6F\x6A\x00\x00\x00\x09\x74\x52" 219 | "\x4E\x53\x00\xE0\x08\xB8\xD0\x58\x98\x85\x25\x4C\x7E\x68\xAA\x00\x00\x00\x49\x49\x44\x41\x54\x08\xD7\x63\x60" 220 | "\x60\x60\x99\x39\xD3\x01\x48\x11\xC3\xE0\x08\x0D\x9C\x39\x53\x34\xB4\x81\x81\xC9\x72\x26\x10\x4C\x56\x60\x60" 221 | "\x50\x06\x31\x8C\x80\x72\x40\x21\xB0\x00\x50\x08\x2C\x00\x16\x02\x09\x80\x85\x80\x02\x10\x21\x90\x00\x02\xB0" 222 | "\x0B\x82\x41\x01\x03\xDB\x4C\x30\x48\x00\x00\xA9\xC1\x1A\x09\x2E\x8B\x71\x91\x00\x00\x00\x00\x49\x45\x4E\x44" 223 | "\xAE\x42\x60\x82 " 224 | ]) 225 | icon_data_restore = "".join([ 226 | "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x10\x00\x00\x00\x10\x04\x03\x00" 227 | "\x00\x00\xED\xDD\xE2\x52\x00\x00\x00\x1E\x50\x4C\x54\x45\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 228 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xB7\x28\x6F\x6A\x00\x00\x00\x09\x74\x52" 229 | "\x4E\x53\x00\x81\xE0\xD0\x98\x40\xEC\x34\x2D\xD9\x04\x16\x77\x00\x00\x00\x46\x49\x44\x41\x54\x08\xD7\x63\x00" 230 | "\x02\x46\x01\x06\x08\x90\x9C\x08\xA1\x19\x67\xCE\x14\x80\x08\xCC\x9C\x39\x11\x2A\x00\x14\x82\x08\x80\x85\x38" 231 | "\x5C\xDC\x66\xCE\x4C\x71\x69\x00\x0A\x31\xCF\x9C\x69\x00\xA4\x88\x63\xB0\x87\x86\x16\x30\x20\x01\x46\x25\x30" 232 | "\x10\x60\x60\x99\x09\x06\x0E\x00\xB5\x68\x19\x1B\xBF\xF3\x8F\x71\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60" 233 | "\x82 " 234 | ]) 235 | 236 | act_icon_save = idaapi.load_custom_icon(data=icon_data_save, format="png") 237 | act_icon_restore = idaapi.load_custom_icon(data=icon_data_restore, format="png") 238 | 239 | act_name_save = "dumpDyn_save:action" 240 | act_name_restore = "dumpDyn_restore:action" 241 | if idaapi.register_action(idaapi.action_desc_t( 242 | act_name_save, 243 | "save_x", 244 | save_class(), 245 | None, 246 | "save_x", 247 | act_icon_save)): 248 | 249 | # Insert the action in a toolbar 250 | idaapi.attach_action_to_toolbar("DebugToolBar", act_name_save) 251 | 252 | if idaapi.register_action(idaapi.action_desc_t( 253 | act_name_restore, 254 | "restore_x", 255 | restore_class(), 256 | None, 257 | "restore_x", 258 | act_icon_restore)): 259 | # Insert the action in a toolbar 260 | idaapi.attach_action_to_toolbar("DebugToolBar", act_name_restore) 261 | 262 | else: 263 | idaapi.unregister_action(act_name_save) 264 | idaapi.unregister_action(act_name_restore) 265 | 266 | global MD5_hash_data_file 267 | input_filepath = ida_nalt.get_input_file_path() 268 | hasher = hashlib.md5() 269 | with open(input_filepath, 'rb') as afile: 270 | buf = afile.read() 271 | hasher.update(buf) 272 | MD5_hash = hasher.hexdigest() # str 273 | MD5_hash_data_file = input_filepath + "____dumpDyn___" + MD5_hash 274 | 275 | if __name__ == "__main__": 276 | # Remove an existing debug hook 277 | try: 278 | if debughook: 279 | debughook.unhook() 280 | except: 281 | pass 282 | debughook = MyDbgHook() 283 | debughook.hook() 284 | 285 | main() 286 | -------------------------------------------------------------------------------- /idenLib/idenLib.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2019 4 | # Lasha Khasaia @_qaz_qaz 5 | # 6 | # ------------------------------------------------------------------------------- 7 | 8 | 9 | from __future__ import print_function 10 | import ida_name 11 | import ida_bytes 12 | import ida_funcs 13 | import idaapi 14 | import ida_idaapi 15 | import ida_diskio 16 | import ida_ua 17 | import idautils 18 | import ida_name 19 | import ida_nalt 20 | import idc 21 | import os 22 | import zstd 23 | import capstone 24 | import cPickle as pickle 25 | 26 | MIN_FUNC_SIZE = 0x20 27 | MAX_FUNC_SIZE = 0x100 28 | PLUGIN_NAME = "idenLib" 29 | PLUGIN_VERSION = "v0.4" 30 | IDENLIB_CONTACT = "Contact: Lasha Khasaia (@_qaz_qaz)" 31 | 32 | local_appdata = os.getenv('LOCALAPPDATA') 33 | 34 | # __EA64__ is set if IDA is running in 64-bit mode 35 | __EA64__ = ida_idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL 36 | 37 | if __EA64__ : 38 | CAPSTONE_MODE = capstone.CS_MODE_64 39 | SIG_EXT = ".sig64" 40 | idenLibCache = local_appdata + os.sep + PLUGIN_NAME + os.sep + "idenLibCache64" 41 | idenLibCacheMain = local_appdata + os.sep + PLUGIN_NAME + os.sep + "idenLibCacheMain64" 42 | else: 43 | CAPSTONE_MODE = capstone.CS_MODE_32 44 | SIG_EXT = ".sig" 45 | idenLibCache = local_appdata + os.sep + PLUGIN_NAME + os.sep + "idenLibCache" 46 | idenLibCacheMain = local_appdata + os.sep + PLUGIN_NAME + os.sep + "idenLibCacheMain" 47 | idenLib_appdata = local_appdata + os.sep + PLUGIN_NAME 48 | 49 | func_sigs = {} 50 | mainSigs = {} 51 | 52 | ida_dir = ida_diskio.idadir("") 53 | symEx_dir = ida_dir + os.sep + "SymEx" 54 | 55 | def getNames(): 56 | for ea, name in idautils.Names(): 57 | yield name 58 | 59 | def getFiles(path): 60 | for file in os.listdir(path): 61 | if os.path.isfile(os.path.join(path, file)): 62 | yield path + "\\" + file 63 | 64 | # return (start_ea, size) 65 | def getFuncRanges(): 66 | funcs_addr = [] 67 | start = 0 68 | next_func = ida_funcs.get_next_func(start) 69 | while next_func: 70 | size = next_func.size() 71 | if (size) < MIN_FUNC_SIZE: 72 | next_func = ida_funcs.get_next_func(next_func.start_ea) 73 | continue 74 | elif size > MAX_FUNC_SIZE: 75 | size = MAX_FUNC_SIZE 76 | yield (next_func.start_ea, size) 77 | funcs_addr.append(next_func.start_ea - start) 78 | next_func = ida_funcs.get_next_func(next_func.start_ea) 79 | 80 | def getOpcodes(addr, size): 81 | md = capstone.Cs(capstone.CS_ARCH_X86, CAPSTONE_MODE) 82 | md.detail = True 83 | instr_bytes = ida_bytes.get_bytes(addr, size) 84 | opcodes_buf = b'' 85 | for i in md.disasm(instr_bytes, size): 86 | # get last opcode 87 | if (i.opcode[3] != 0): 88 | opcodes_buf += "%02x" % (i.opcode[3]) 89 | elif (i.opcode[2] != 0): 90 | opcodes_buf += "%02x" % (i.opcode[2]) 91 | elif(i.opcode[1] != 0): 92 | opcodes_buf += "%02x" % (i.opcode[1]) 93 | else: 94 | opcodes_buf += "%02x" % (i.opcode[0]) 95 | return opcodes_buf 96 | 97 | def idenLibProcessSignatures(): 98 | global func_sigs 99 | global mainSigs 100 | for file in getFiles(symEx_dir): 101 | if not file.endswith(SIG_EXT): 102 | continue 103 | with open(file, 'rb') as ifile: 104 | sig = ifile.read() 105 | sig = zstd.decompress(sig).strip() 106 | sig = sig.split(b"\n") 107 | for line in sig: 108 | sig_opcodes, name = line.split(" ") 109 | if '_' in sig_opcodes: # "main" signatures 110 | opcodeMain, mainIndexes = sig_opcodes.split('_') 111 | fromFunc, fromBase = mainIndexes.split("!") 112 | mainSigs[opcodeMain] = (name.strip(), int(fromFunc), int(fromBase)) 113 | elif '+' in sig_opcodes: 114 | opcodes, strBranches = sig_opcodes.split('+') 115 | nBranches = int(strBranches) 116 | func_sigs[opcodes.strip()] = (name.strip(), nBranches) 117 | else: 118 | func_sigs[sig_opcodes.strip()] = (name.strip(), 0) 119 | if not os.path.isdir(idenLib_appdata): 120 | os.mkdir(idenLib_appdata) 121 | pickle.dump(func_sigs, open( idenLibCache, "wb" )) 122 | pickle.dump(mainSigs, open( idenLibCacheMain, "wb" )) 123 | print("[idenLib] Signatures refreshed...\n") 124 | 125 | def idenLib(): 126 | global func_sigs 127 | global mainSigs 128 | # function sigs from the current binary 129 | func_bytes_addr = {} 130 | for addr, size in getFuncRanges(): 131 | f_bytes = getOpcodes(addr, size) 132 | func_bytes_addr[f_bytes] = addr 133 | 134 | # load sigs 135 | if not os.path.isdir(symEx_dir): 136 | print("[idenLib - FAILED] There is no {} directory".format(symEx_dir)) 137 | return 138 | 139 | if os.path.isfile(idenLibCache): 140 | func_sigs = pickle.load( open( idenLibCache, "rb" ) ) 141 | if os.path.isfile(idenLibCacheMain): 142 | mainSigs = pickle.load( open( idenLibCacheMain, "rb" ) ) 143 | else: 144 | idenLibProcessSignatures() 145 | # apply sigs 146 | counter = 0 147 | mainDetected = False 148 | for sig_opcodes, addr in func_bytes_addr.items(): 149 | if func_sigs.has_key(sig_opcodes): 150 | func_name = func_sigs[sig_opcodes][0] 151 | current_name = ida_funcs.get_func_name(addr) 152 | if (current_name != func_name): 153 | digit = 1 154 | while func_name in getNames(): 155 | func_name = func_name + str(digit) 156 | digit = digit + 1 157 | ida_name.set_name(addr, func_name, ida_name.SN_NOCHECK) 158 | print("{}: {}".format(hex(addr), func_name)) 159 | counter = counter + 1 160 | if mainSigs.has_key(sig_opcodes): # "main" sig 161 | callInstr = mainSigs[sig_opcodes][1] + addr 162 | if ida_ua.print_insn_mnem(callInstr) == "call": 163 | call_target = idc.get_operand_value(callInstr, 0) 164 | current_name = ida_funcs.get_func_name(call_target) 165 | func_name = mainSigs[sig_opcodes][0] 166 | if (current_name != func_name): 167 | ida_name.set_name(call_target, func_name, ida_name.SN_NOCHECK) 168 | print("{}: {}".format(hex(call_target), func_name)) 169 | counter = counter + 1 170 | mainDetected = True 171 | if not mainDetected: 172 | for entry in idautils.Entries(): 173 | for sig_opcodes, name_funcRva_EntryRva in mainSigs.items(): 174 | callInstr = name_funcRva_EntryRva[2] + entry[2] # from EP 175 | if ida_ua.print_insn_mnem(callInstr) == "call": 176 | fromFunc = name_funcRva_EntryRva[1] 177 | func_start = callInstr - fromFunc 178 | func_opcodes = getOpcodes(func_start, MAX_FUNC_SIZE) 179 | if func_opcodes.startswith(sig_opcodes): 180 | call_target = idc.get_operand_value(callInstr, 0) 181 | current_name = ida_funcs.get_func_name(call_target) 182 | func_name = mainSigs[sig_opcodes][0] 183 | if (current_name != func_name): 184 | ida_name.set_name(call_target, func_name, ida_name.SN_NOCHECK) 185 | print("{}: {}".format(hex(call_target), func_name)) 186 | counter = counter + 1 187 | mainDetected = True 188 | break 189 | 190 | print("[idenLib] Applied to {} function(s)".format(counter)) 191 | 192 | 193 | class idenLib_class(idaapi.action_handler_t): 194 | def __init__(self): 195 | idaapi.action_handler_t.__init__(self) 196 | def activate(self, ctx): 197 | idenLib() 198 | return 1 199 | def update(self, ctx): 200 | return idaapi.AST_ENABLE_FOR_WIDGET if ctx.widget_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_WIDGET 201 | 202 | class AboutHandler(idaapi.action_handler_t): 203 | def __init__(self): 204 | idaapi.action_handler_t.__init__(self) 205 | 206 | def activate(self, ctx): 207 | print(PLUGIN_NAME + " " + PLUGIN_VERSION) 208 | print(IDENLIB_CONTACT) 209 | return 1 210 | 211 | def update(self, ctx): 212 | return idaapi.AST_ENABLE_ALWAYS 213 | 214 | class RefreshHandler(idaapi.action_handler_t): 215 | def __init__(self): 216 | idaapi.action_handler_t.__init__(self) 217 | 218 | def activate(self, ctx): 219 | idenLibProcessSignatures() 220 | return 1 221 | 222 | def update(self, ctx): 223 | return idaapi.AST_ENABLE_ALWAYS 224 | 225 | # icon author: https://www.flaticon.com/authors/freepik 226 | icon_data = "".join([ 227 | "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x18\x00\x00\x00\x18\x08\x03\x00\x00\x00\xD7\xA9\xCD\xCA\x00\x00\x00\x4E\x50\x4C\x54\x45\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xC4\xA2\xA6\x59\x00\x00\x00\x19\x74\x52\x4E\x53\x00\x20\xEE\x4F\xC9\x64\xD3\xB3\x32\x99\x88\x17\x0C\xC1\x5C\x28\xF6\x7F\xE6\xDD\xBB\xA2\x47\x41\x90\xCE\x19\x07\xA1\x00\x00\x00\xC8\x49\x44\x41\x54\x28\xCF\x75\xD1\xDB\xAE\x83\x20\x10\x85\xE1\x35\x08\x0E\xCA\x16\x3C\xDB\xF5\xFE\x2F\xBA\xC7\x58\xDB\xB4\xA1\xFF\x8D\xC8\x27\x48\x02\x7E\x26\xD6\xDF\xE7\x58\x70\x46\xAB\x79\x82\x23\x19\xD4\x31\x55\xC1\x93\x47\x75\xAB\xFD\x10\xA9\xAE\x38\x16\xEA\x0B\x36\x6F\x6D\x88\x56\x8A\xE4\xFC\x02\xA5\xA5\x58\x9C\x73\x19\x23\x99\x6E\x88\x12\xA3\x94\x6B\x2B\x78\x9B\xB8\xA1\xA5\x9B\xE9\x9F\xF0\x20\xA7\x37\x58\x37\x64\x52\xAB\x50\x48\x57\x85\xF3\x21\x55\x18\x6C\xA6\x0A\x3D\xD9\x1B\x68\x37\x7E\x41\xD3\x4E\x0A\x2C\x40\xF7\x05\x12\x60\x2B\x5C\xC2\x70\x43\x0E\x21\x14\xD8\x97\xD0\x02\x8E\xB3\xFD\xA3\x1D\xD4\x0F\xD0\x75\x5D\x77\x03\x1D\x99\xD1\x5B\x25\xED\x21\x34\x09\x93\x8D\xA3\x41\x9E\xEC\xA5\xB3\xA2\xBF\xB6\x7A\xD8\xF8\x04\xD9\xDA\xA1\x76\x5C\x24\x3A\xBD\x6E\x4D\xCE\xD2\xFB\x36\x05\xBF\xFB\x07\x19\xFC\x16\xA4\x38\xC6\x08\x3D\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82" 228 | ]) 229 | 230 | class idenLibMain(idaapi.plugin_t): 231 | flags = idaapi.PLUGIN_UNL 232 | comment = "idenLib - Library Function Identification" 233 | help = IDENLIB_CONTACT 234 | wanted_name = PLUGIN_NAME 235 | wanted_hotkey = '' 236 | 237 | def run(self, arg): 238 | idenLib() 239 | pass 240 | 241 | def term(self): 242 | pass 243 | 244 | def init(self): 245 | act_icon = idaapi.load_custom_icon(data=icon_data, format="png") 246 | act_name = "idenLib:action" 247 | idaapi.register_action(idaapi.action_desc_t( 248 | act_name, 249 | "idenLib - Function Identification", 250 | idenLib_class(), 251 | None, 252 | "idenLib", 253 | act_icon)) 254 | # Insert the action in a toolbar 255 | idaapi.attach_action_to_toolbar("DebugToolBar", act_name) 256 | idaapi.attach_action_to_menu( 257 | 'Edit/idenLib/', 258 | act_name, 259 | idaapi.SETMENU_APP) 260 | 261 | # refresh signatures 262 | act_name = "idenLib:refresh" 263 | idaapi.register_action(idaapi.action_desc_t( 264 | act_name, 265 | "Refresh Signatures", 266 | RefreshHandler(), 267 | None, 268 | "idenLib - Refresh")) 269 | idaapi.attach_action_to_menu( 270 | 'Edit/idenLib/', 271 | act_name, 272 | idaapi.SETMENU_APP) 273 | 274 | # about 275 | act_name = "idenLib:about" 276 | idaapi.register_action(idaapi.action_desc_t( 277 | act_name, 278 | "About", 279 | AboutHandler(), 280 | None, 281 | "idenLib - About")) 282 | idaapi.attach_action_to_menu( 283 | 'Edit/idenLib/', 284 | act_name, 285 | idaapi.SETMENU_APP) 286 | 287 | return idaapi.PLUGIN_OK 288 | 289 | 290 | 291 | def PLUGIN_ENTRY(): 292 | return idenLibMain() 293 | 294 | if __name__ == "__main__": 295 | PLUGIN_ENTRY() --------------------------------------------------------------------------------