├── .gitignore ├── README.md ├── core ├── __init__.py ├── analyse.py ├── binary.py ├── flag │ ├── binary.json │ └── signature.json ├── generate.py └── signature.py ├── pyflirt.py └── screenshot ├── after.png └── before.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyflirt 2 | **pyflirt** is a map file genarator for binary file based on flirt signature. Since it is used with ollydbg, only intel x86 binary is currently supported. This tool was succesfully tested with ollydbg 1.10 MapConv plugin to import the map file. 3 | 4 | Installation 5 | ============ 6 | 7 | **Dependencies** 8 | 9 | Require [python capstone binding](https://github.com/aquynh/capstone/tree/master/bindings/python) for disassembly 10 | 11 | **Install** 12 | 13 | git clone https://github.com/mokhdzanifaeq/pyflirt 14 | 15 | Usage 16 | ===== 17 | ``` 18 | usage: pyflirt.py [-h] -b path -s path -o path 19 | 20 | optional arguments: 21 | -h, --help show this help message and exit 22 | -b path, --bin path path of binary file to be analysed 23 | -s path, --sig path path of signature file to be analysed 24 | -o path, --out path path of map file to be generated 25 | ``` 26 | 27 | Screenshots 28 | =========== 29 | **Before:** 30 | 31 | ![before](/screenshot/before.png?raw=true) 32 | 33 | **After:** 34 | 35 | ![after](/screenshot/after.png?raw=true) 36 | 37 | License 38 | ======= 39 | Feel free to update the code as you like, fix bugs and implement new features. 40 | 41 | Credits 42 | ======= 43 | * rheax - understanding flirt file format 44 | * [aquynh](https://github.com/aquynh) - capstone framework 45 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhdzanifaeq/pyflirt/162f9d0a6836cd7cd8733e943cb51b6d3df02bfa/core/__init__.py -------------------------------------------------------------------------------- /core/analyse.py: -------------------------------------------------------------------------------- 1 | from capstone import * 2 | from capstone.x86 import * 3 | from ctypes import c_ushort 4 | from struct import unpack 5 | 6 | class function: 7 | def __init__(self, binary, sections, tree): 8 | self.binary = binary 9 | self.sections = sections 10 | self.tree = tree 11 | self.count = 0 12 | self.functions = {} 13 | self.referenced_functions = {} 14 | self.get_functions() 15 | 16 | def get_functions(self): 17 | md = Cs(CS_ARCH_X86, CS_MODE_32) 18 | md.skipdata = True 19 | # search for function in each section 20 | for i, section in enumerate(self.sections): 21 | # check if section is executable 22 | if section.characteristics & 0x20000020: 23 | raw_section = self.binary[section.pointer_to_raw_data:section.pointer_to_raw_data + section.virtual_size] 24 | for insn in md.disasm(raw_section, 0): 25 | # search for call instruction that point to relative address 26 | if insn.mnemonic == "call" and insn.op_str[:2] == "0x": 27 | address = int(insn.op_str, 0) 28 | if not address in self.functions: 29 | self.functions[address] = {"section": i} 30 | if not self.functions: 31 | raise Exception("cannot find any function") 32 | # get functions name based on flirt signature 33 | for data in self.functions.items(): 34 | for child_node in self.tree["child_list"]: 35 | if self.node_match(child_node, data, 0): 36 | break 37 | # check for skipped referenced function 38 | while True: 39 | temp = self.referenced_functions 40 | for data in self.referenced_functions.items(): 41 | for child_node in self.tree["child_list"]: 42 | if self.node_match(child_node, data, 0): 43 | break 44 | # stop if does not found any referenced function 45 | if not self.referenced_functions or temp == self.referenced_functions: 46 | break 47 | if not self.count: 48 | raise Exception("cannot find matched function") 49 | 50 | def node_match(self, node, data, offset): 51 | if self.pattern_match(node, data[0] + self.sections[data[1]["section"]].pointer_to_raw_data + offset): 52 | if node["child_list"]: 53 | for child_node in node["child_list"]: 54 | if self.node_match(child_node, data, offset + node["length"]): 55 | return True 56 | elif node["module_list"]: 57 | for module in node["module_list"]: 58 | if self.module_match(module, data): 59 | return True 60 | return False 61 | 62 | def pattern_match(self, node, offset): 63 | for i in xrange(node["length"]): 64 | if not node["variant_bool_array"][i]: 65 | if node["pattern_bytes"][i] != ord(self.binary[offset + i]): 66 | return False 67 | return True 68 | 69 | def module_match(self, module, data): 70 | base = data[0] + self.sections[data[1]["section"]].pointer_to_raw_data 71 | # check crc 72 | if module["crc16"] != self.crc16(base + 32, module["crc_length"]): 73 | return False 74 | # check tail bytes 75 | for tail_byte in module.get("tail_bytes", []): 76 | if self.binary[base + 32 + module["crc_length"] + tail_byte["offset"]] != tail_byte["value"]: 77 | return False 78 | # check referenced function 79 | if "referenced_functions" in module: 80 | for ref_function in module["referenced_functions"]: 81 | # get addess for referenced function 82 | ref_offset = base + ref_function["offset"] 83 | call_opcode = ord(self.binary[ref_offset - 1]) 84 | # relative or absolute call? still unsure if absolute is used 85 | if call_opcode == 0xe8: 86 | ref_address = unpack("> 1) ^ 0x8408 120 | else: 121 | crc >>= 1 122 | data >>= 1 123 | crc = ~crc 124 | data = crc 125 | crc = (crc << 8) | ((data >> 8) & 0xff) 126 | return c_ushort(crc).value 127 | -------------------------------------------------------------------------------- /core/binary.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname 2 | from ctypes import * 3 | from struct import * 4 | import json 5 | import datetime 6 | 7 | class file_header(Structure): 8 | _fields_ = [ 9 | ("magic", c_char * 4), 10 | ("machine", c_ushort), 11 | ("number_of_sections", c_ushort), 12 | ("time_date_stamp", c_uint), 13 | ("pointer_to_symbol_table", c_uint), 14 | ("number_of_symbols", c_uint), 15 | ("size_of_optional_header", c_ushort), 16 | ("characteristics", c_ushort) 17 | ] 18 | 19 | class optional_header(Structure): 20 | _fields_ = [ 21 | ("magic", c_ushort), 22 | ("major_linker_version", c_ubyte), 23 | ("minor_linker_version", c_ubyte), 24 | ("size_of_code", c_uint), 25 | ("size_of_initialized_data", c_uint), 26 | ("size_of_uninitialized_data", c_uint), 27 | ("address_of_entry_point", c_uint), 28 | ("base_of_code", c_uint), 29 | ("base_of_data", c_uint), 30 | ("image_base", c_uint), 31 | ("section_alignment", c_uint), 32 | ("file_alignment", c_uint), 33 | ("major_operating_system_version", c_ushort), 34 | ("minor_operating_system_version", c_ushort), 35 | ("major_image_version", c_ushort), 36 | ("minor_image_version", c_ushort), 37 | ("major_subsystem_version", c_ushort), 38 | ("minor_subsystem_version", c_ushort), 39 | ("win32Version_value", c_uint), 40 | ("size_of_image", c_uint), 41 | ("size_of_headers", c_uint), 42 | ("check_sum", c_uint), 43 | ("subsystem", c_ushort), 44 | ("dll_characteristics", c_ushort), 45 | ("size_of_stack_reserve", c_uint), 46 | ("size_of_stack_commit", c_uint), 47 | ("size_of_heap_reserve", c_uint), 48 | ("size_of_heap_commit", c_uint), 49 | ("loader_flags", c_uint), 50 | ("number_of_rva_and_sizes", c_uint) 51 | ] 52 | 53 | class section_header(Structure): 54 | _fields_ = [ 55 | ("name", c_char * 8), 56 | ("virtual_size", c_uint), 57 | ("virtual_address", c_uint), 58 | ("size_of_raw_data", c_uint), 59 | ("pointer_to_raw_data", c_uint), 60 | ("pointer_to_relocations", c_uint), 61 | ("pointer_to_linenumbers", c_uint), 62 | ("number_of_relocations", c_ushort), 63 | ("number_of_linenumbers", c_ushort), 64 | ("characteristics", c_uint) 65 | ] 66 | 67 | class pe: 68 | def __init__(self, binary, dump = False): 69 | self.binary = binary 70 | # magic 71 | if self.binary[:2] != "MZ": 72 | raise Exception("binary is not supported") 73 | self.parse_pe_header() 74 | if dump: 75 | self.dump_pe_header() 76 | 77 | def parse_pe_header(self): 78 | # skip to PE offset 79 | skip = unpack("= section.virtual_address and val <= section.virtual_address + section.virtual_size: 125 | print field_name, ": %d (%s)" % (val, section.name) 126 | break 127 | elif field_name == "dll_characteristics": 128 | print "dll_characteristics :", 129 | for dll_characteristic, fflag in flag["dll_characteristic"].iteritems(): 130 | if fflag & val: 131 | print dll_characteristic, 132 | print "(0x%X)" % val 133 | else: 134 | print field_name, ":", val 135 | print "\nSECTION_HEADER" 136 | for section in self.sections: 137 | print "" 138 | for field_name, field_type in section_header._fields_: 139 | val = getattr(section, field_name) 140 | if field_name == "characteristics": 141 | print "characteristics :", 142 | for characteristic, fflag in flag["section_characteristic"].iteritems(): 143 | if fflag & val: 144 | print characteristic, 145 | print "(0x%X)" % val 146 | else: 147 | print field_name, ":", val 148 | -------------------------------------------------------------------------------- /core/flag/binary.json: -------------------------------------------------------------------------------- 1 | { 2 | "characteristic": { 3 | "RELOCS_STRIPPED": 1, 4 | "EXECUTABLE_IMAGE": 2, 5 | "LINE_NUMS_STRIPPED": 4, 6 | "LOCAL_SYMS_STRIPPED": 8, 7 | "AGGRESIVE_WS_TRIM": 16, 8 | "LARGE_ADDRESS_AWARE": 32, 9 | "BYTES_REVERSED_LO": 128, 10 | "32BIT_MACHINE": 256, 11 | "DEBUG_STRIPPED": 512, 12 | "REMOVABLE_RUN_FROM_SWAP": 1024, 13 | "NET_RUN_FROM_SWAP": 2048, 14 | "SYSTEM": 4096, 15 | "DLL": 8192, 16 | "UP_SYSTEM_ONLY": 16384, 17 | "BYTES_REVERSED_HI": 32768 18 | }, 19 | "dll_characteristic": { 20 | "DYNAMIC_BASE": 64, 21 | "FORCE_INTEGRITY": 128, 22 | "NX_COMPAT": 256, 23 | "NO_ISOLATION": 512, 24 | "NO_SEH": 1024, 25 | "NO_BIND": 2048, 26 | "WDM_DRIVER": 8192, 27 | "TERMINAL_SERVER_AWARE": 32768 28 | }, 29 | "section_characteristic": { 30 | "CNT_CODE": 32, 31 | "CNT_INITIALIZED_DATA": 64, 32 | "CNT_UNINITIALIZED_DATA": 128, 33 | "MEM_DISCARDABLE": 33554432, 34 | "MEM_NOT_CACHED": 67108864, 35 | "MEM_NOT_PAGED": 134217728, 36 | "MEM_SHARED": 268435456, 37 | "MEM_EXECUTE": 536870912, 38 | "MEM_READ": 1073741824, 39 | "MEM_WRITE": 2147483648 40 | }, 41 | "subsystem": { 42 | "0": "UNKNOWN", 43 | "1": "NATIVE", 44 | "2": "WINDOWS_GUI", 45 | "3": "WINDOWS_CUI", 46 | "5": "OS2_CUI", 47 | "7": "POSIX_CUI", 48 | "9": "WINDOWS_CE_GUI", 49 | "10": "EFI_APPLICATION", 50 | "11": "EFI_BOOT_SERVICE_DRIVER", 51 | "12": "EFI_RUNTIME_DRIVER", 52 | "13": "EFI_ROM", 53 | "14": "XBOX", 54 | "16": "WINDOWS_BOOT_APPLICATION" 55 | } 56 | } -------------------------------------------------------------------------------- /core/flag/signature.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": { 3 | "0": "386", 4 | "1": "Z80", 5 | "2": "I860", 6 | "3": "8051", 7 | "4": "TMS", 8 | "5": "6502", 9 | "6": "PDP", 10 | "7": "68K", 11 | "8": "JAVA", 12 | "9": "6800", 13 | "10": "ST7", 14 | "11": "MC6812", 15 | "12": "MIPS", 16 | "13": "ARM", 17 | "14": "TMSC6", 18 | "15": "PPC", 19 | "16": "80196", 20 | "17": "Z8", 21 | "18": "SH", 22 | "19": "NET", 23 | "20": "AVR", 24 | "21": "H8", 25 | "22": "PIC", 26 | "23": "SPARC", 27 | "24": "ALPHA", 28 | "25": "HPPA", 29 | "26": "H8500", 30 | "27": "TRICORE", 31 | "28": "DSP56K", 32 | "29": "C166", 33 | "30": "ST20", 34 | "31": "IA64", 35 | "32": "I960", 36 | "33": "F2MC", 37 | "34": "TMS320C54", 38 | "35": "TMS320C55", 39 | "36": "TRIMEDIA", 40 | "37": "M32R", 41 | "38": "NEC_78K0", 42 | "39": "NEC_78K0S", 43 | "40": "M740", 44 | "41": "M7700", 45 | "42": "ST9", 46 | "43": "FR", 47 | "44": "MC6816", 48 | "45": "M7900", 49 | "46": "TMS320C3", 50 | "47": "KR1878", 51 | "48": "AD218X", 52 | "49": "OAKDSP", 53 | "50": "TLCS900", 54 | "51": "C39", 55 | "52": "CR16", 56 | "53": "MN102L00", 57 | "54": "TMS320C1X", 58 | "55": "NEC_V850X", 59 | "56": "SCR_ADPT", 60 | "57": "EBC", 61 | "58": "MSP430", 62 | "59": "SPU", 63 | "60": "DALVIK" 64 | }, 65 | "file_types": { 66 | "DOS_EXE_OLD": 1, 67 | "DOS_COM_OLD": 2, 68 | "BIN": 4, 69 | "DOSDRV": 8, 70 | "NE": 16, 71 | "INTELHEX": 32, 72 | "MOSHEX": 64, 73 | "LX": 128, 74 | "LE": 256, 75 | "NLM": 512, 76 | "COFF": 1024, 77 | "PE": 2048, 78 | "OMF": 4096, 79 | "SREC": 8192, 80 | "ZIP": 16384, 81 | "OMFLIB": 32768, 82 | "AR": 65536, 83 | "LOADER": 131072, 84 | "ELF": 262144, 85 | "W32RUN": 524288, 86 | "AOUT": 1048576, 87 | "PILOT": 2097152, 88 | "DOS_EXE": 4194304, 89 | "DOS_COM": 8388608, 90 | "AIXAR": 16777216 91 | }, 92 | "os_types": { 93 | "MSDOS": 1, 94 | "WIN": 2, 95 | "OS2": 4, 96 | "NETWARE": 8, 97 | "UNIX": 16, 98 | "OTHER": 32 99 | }, 100 | "app_types": { 101 | "CONSOLE": 1, 102 | "GRAPHICS": 2, 103 | "EXE": 4, 104 | "DLL": 8, 105 | "DRV": 16, 106 | "SINGLE_THREADED": 32, 107 | "MULTI_THREADED": 64, 108 | "16_BIT": 128, 109 | "32_BIT": 256, 110 | "64_BIT": 512 111 | }, 112 | "features": { 113 | "STARTUP": 1, 114 | "CTYPE_CRC": 2, 115 | "2BYTE_CTYPE": 4, 116 | "ALT_CTYPE_CRC": 8, 117 | "COMPRESSED": 16 118 | }, 119 | "parse": { 120 | "MORE_PUBLIC_NAMES": 1, 121 | "READ_TAIL_BYTES": 2, 122 | "READ_REFERENCED_FUNCTIONS": 4, 123 | "MORE_MODULES_WITH_SAME_CRC": 8, 124 | "MORE_MODULES": 16 125 | }, 126 | "function": { 127 | "LOCAL": 2, 128 | "UNRESOLVED_COLLISION": 8, 129 | "NEGATIVE_OFFSET": 16 130 | } 131 | } -------------------------------------------------------------------------------- /core/generate.py: -------------------------------------------------------------------------------- 1 | class _map: 2 | def __init__(self, functions, path): 3 | with open(path, "w") as self.file: 4 | self.write_function(functions) 5 | 6 | def write_function(self, functions): 7 | self.file.write("\n\n Address Publics by Value\n") 8 | for address in sorted(functions): 9 | #only print fucntions with name 10 | if "name" in functions[address]: 11 | # section:address name 12 | self.file.write("\n %04X:%08X %s" % (functions[address]["section"] + 1, address, functions[address]["name"])) 13 | -------------------------------------------------------------------------------- /core/signature.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname 2 | from ctypes import * 3 | import zlib 4 | import json 5 | 6 | class header_v5(Structure): 7 | _pack_ = 1 8 | _fields_ = [ 9 | ("magic", c_char * 6), 10 | ("version", c_ubyte), 11 | ("arch", c_ubyte), 12 | ("file_types", c_uint), 13 | ("os_types", c_ushort), 14 | ("app_types", c_ushort), 15 | ("features", c_ushort), 16 | ("old_n_functions", c_ushort), 17 | ("crc16", c_ushort), 18 | ("ctype", c_char * 12), 19 | ("library_name_len", c_ubyte), 20 | ("ctypes_crc16", c_ushort) 21 | ] 22 | 23 | class header_v6_v7(Structure): 24 | _fields_ = [ 25 | ("n_functions", c_uint) 26 | ] 27 | 28 | class header_v8_v9(Structure): 29 | _fields_ = [ 30 | ("pattern_size", c_ushort) 31 | ] 32 | 33 | class idasig: 34 | def __init__(self, binary, dump = False): 35 | self.binary = binary 36 | self.dump = dump 37 | with open(dirname(__file__) + "/flag/signature.json") as JSONdata: 38 | self.flag = json.load(JSONdata) 39 | self.parse_flirt() 40 | 41 | def read_byte(self): 42 | byte = self.buffer[self.skip] 43 | self.skip += 1 44 | return ord(byte) 45 | 46 | def read_short(self): 47 | return (self.read_byte() << 8) + self.read_byte() 48 | 49 | def read_word(self): 50 | return (self.read_short() << 16) + self.read_short() 51 | 52 | def read_max_2_bytes(self): 53 | byte = self.read_byte() 54 | if byte & 0x80: 55 | return ((byte & 0x7f) << 8) + self.read_byte() 56 | return byte 57 | 58 | def read_multiple_bytes(self): 59 | byte = self.read_byte() 60 | if byte & 0x80 != 0x80: 61 | return byte 62 | if byte & 0xc0 != 0xc0: 63 | return ((byte & 0x7f) << 8) + self.read_byte(); 64 | if byte & 0xe0 != 0xe0: 65 | return ((byte & 0x3f) << 24) + (self.read_byte() << 16) + self.read_short(); 66 | return self.read_word() 67 | 68 | def parse_flirt(self): 69 | self.header = {} 70 | self.tree = {} 71 | self.skip = 0 72 | self.parse_header() 73 | if self.dump: 74 | self.dump_header() 75 | # check for compression 76 | if self.header["features"] & self.flag["features"]["COMPRESSED"]: 77 | # zlib(15) or deflate(-15) 78 | wbit = zlib.MAX_WBITS if ord(self.binary[self.skip]) == 0x78 else -zlib.MAX_WBITS 79 | self.buffer = zlib.decompress(self.binary[self.skip:], wbit) 80 | else: 81 | self.buffer = self.binary[self.skip:] 82 | self.skip = 0 83 | self.parse_tree(self.tree) 84 | 85 | def parse_header(self): 86 | v5 = header_v5.from_buffer_copy(self.binary) 87 | self.skip += sizeof(header_v5) 88 | for field_name, field_type in v5._fields_: 89 | self.header[field_name] = getattr(v5, field_name) 90 | if self.header["magic"] != "IDASGN": 91 | raise Exception("not a signature file") 92 | if self.header["version"] < 5 or self.header["version"] > 9: 93 | raise Exception("unsupported signature version") 94 | if self.header["version"] >= 6: 95 | v6_v7 = header_v6_v7.from_buffer_copy(self.binary[self.skip:]) 96 | self.skip += sizeof(header_v6_v7) 97 | self.header["n_functions"] = v6_v7.n_functions 98 | if self.header["version"] >= 8: 99 | v8_v9 = header_v8_v9.from_buffer_copy(self.binary[self.skip:]) 100 | self.skip += sizeof(header_v8_v9) 101 | self.header["pattern_size"] = v8_v9.pattern_size 102 | self.header["signature"] = self.binary[self.skip:self.skip+self.header["library_name_len"]].decode() 103 | self.skip += self.header["library_name_len"] 104 | 105 | def parse_tree(self, node): 106 | branches = self.read_multiple_bytes() 107 | # no branches == leaf 108 | if branches == 0: 109 | self.parse_leaf(node) 110 | node["child_list"] = [] 111 | for i in xrange(branches): 112 | child_node = {} 113 | # length 114 | child_node["length"] = self.read_byte() 115 | # variant mask 116 | if child_node["length"] < 0x10: 117 | child_node["variant_mask"] = self.read_max_2_bytes() 118 | elif child_node["length"] <= 0x20: 119 | child_node["variant_mask"] = self.read_multiple_bytes() 120 | elif child_node["length"] <= 0x40: 121 | child_node["variant_mask"] = (self.read_multiple_bytes() << 32) + self.read_multiple_bytes() 122 | # pattern bytes 123 | child_node["variant_bool_array"] = [] 124 | child_node["pattern_bytes"] = [] 125 | current_mask_bit = 1 << (child_node["length"] - 1) 126 | for j in xrange(child_node["length"]): 127 | if child_node["variant_mask"] & current_mask_bit != 0: 128 | child_node["variant_bool_array"].append(True) 129 | child_node["pattern_bytes"].append(0x00) 130 | else: 131 | child_node["variant_bool_array"].append(False) 132 | child_node["pattern_bytes"].append(self.read_byte()) 133 | current_mask_bit >>= 1 134 | self.parse_tree(child_node) 135 | node["child_list"].append(child_node) 136 | 137 | def parse_leaf(self, node): 138 | node["module_list"] = [] 139 | while True: 140 | crc_length = self.read_byte() 141 | crc16 = self.read_short() 142 | while True: 143 | module = {} 144 | module["crc_length"] = crc_length 145 | module["crc16"] = crc16 146 | module["length"] = self.read_multiple_bytes() if self.header["version"] >= 9 else self.read_max_2_bytes() 147 | # public function 148 | module["public_functions"] = [] 149 | offset = 0 150 | while True: 151 | public_function = {} 152 | offset += self.read_multiple_bytes() if self.header["version"] >= 9 else self.read_max_2_bytes() 153 | public_function["offset"] = offset 154 | current_byte = self.read_byte() 155 | if current_byte < 0x20: 156 | if current_byte & self.flag["function"]["LOCAL"]: 157 | public_function["is_local"] = True 158 | if current_byte & self.flag["function"]["UNRESOLVED_COLLISION"]: 159 | public_function["is_collision"] = True 160 | if current_byte & self.flag["function"]["NEGATIVE_OFFSET"]: 161 | public_function["offset"] *= -1 162 | # if bool(current_byte & 0x01) or bool(current_byte & 0x04): 163 | current_byte = self.read_byte() 164 | public_function["name"] = "" 165 | while current_byte >= 0x20: 166 | public_function["name"] += chr(current_byte) 167 | current_byte = self.read_byte() 168 | flags = current_byte 169 | module["public_functions"].append(public_function) 170 | if not flags & self.flag["parse"]["MORE_PUBLIC_NAMES"]: 171 | break 172 | # tail bytes 173 | if flags & self.flag["parse"]["READ_TAIL_BYTES"]: 174 | module["tail_bytes"] = [] 175 | n_tail_bytes = self.read_byte() if self.header["version"] >= 8 else 1 176 | for i in xrange(n_tail_bytes): 177 | tail_bytes = {} 178 | tail_bytes["offset"] = self.read_multiple_bytes() if self.header["version"] >= 9 else self.read_max_2_bytes() 179 | tail_bytes["value"] = self.read_byte() 180 | module["tail_bytes"].append(tail_bytes) 181 | # referenced function 182 | if flags & self.flag["parse"]["READ_REFERENCED_FUNCTIONS"]: 183 | module["referenced_functions"] = [] 184 | n_referenced_functions = self.read_byte() if self.header["version"] >= 8 else 1 185 | for i in xrange(n_referenced_functions): 186 | referenced_function = {} 187 | referenced_function["offset"] = self.read_multiple_bytes() if self.header["version"] >= 9 else self.read_max_2_bytes() 188 | name_length = self.read_byte() 189 | if name_length == 0: 190 | name_length = self.read_multiple_bytes() 191 | referenced_function["name"] = "" 192 | for j in xrange(name_length): 193 | referenced_function["name"] += chr(self.read_byte()) 194 | if referenced_function["name"][name_length - 1] == 0: 195 | referenced_function["offset"] *= -1 196 | module["referenced_functions"].append(referenced_function) 197 | if len(module["referenced_functions"]) > 1: 198 | print module 199 | exit() 200 | node["module_list"].append(module) 201 | if not flags & self.flag["parse"]["MORE_MODULES_WITH_SAME_CRC"]: 202 | break 203 | if not flags & self.flag["parse"]["MORE_MODULES"]: 204 | break 205 | 206 | def dump_header(self): 207 | print "signature :", self.header["signature"], 208 | print "(%i modules)" % self.header["n_functions"] if self.header["version"] >= 6 else "" 209 | print "version :", self.header["version"] 210 | print "arch :", self.flag["arch"][str(self.header["arch"])], "(0x%X)" % self.header["arch"] 211 | print "file_types :", 212 | for file_type, fflag in self.flag["file_types"].iteritems(): 213 | if fflag & self.header["file_types"]: 214 | print file_type, 215 | print "(0x%X)" % self.header["file_types"] 216 | print "os_types :", 217 | for os_type, fflag in self.flag["os_types"].iteritems(): 218 | if fflag & self.header["os_types"]: 219 | print os_type, 220 | print "(0x%X)" % self.header["os_types"] 221 | print "app_types :", 222 | for app_type, fflag in self.flag["app_types"].iteritems(): 223 | if fflag & self.header["app_types"]: 224 | print app_type, 225 | print "(0x%X)" % self.header["app_types"] 226 | print "features :", 227 | for feature, fflag in self.flag["features"].iteritems(): 228 | if fflag & self.header["features"]: 229 | print feature, 230 | print "(0x%X)" % self.header["features"] -------------------------------------------------------------------------------- /pyflirt.py: -------------------------------------------------------------------------------- 1 | from core.signature import idasig 2 | from core.binary import pe 3 | from core.analyse import function 4 | from core.generate import _map 5 | from argparse import ArgumentParser 6 | from sys import argv 7 | 8 | def raw(path): 9 | with open(path, "rb") as fd: 10 | return fd.read() 11 | 12 | if __name__ == '__main__': 13 | parser = ArgumentParser(description='generate map file for intel x86 binary based on flirt signature') 14 | parser.add_argument("-b", "--bin", metavar="path", help="path of binary file to be analysed", required=True) 15 | parser.add_argument("-s", "--sig", metavar="path", help="path of signature file to be analysed", required=True) 16 | parser.add_argument("-o", "--out", metavar="path", help="path of map file to be generated", required=True) 17 | if len(argv) == 1: 18 | parser.print_help() 19 | exit() 20 | args = parser.parse_args() 21 | try: 22 | raw_binary = raw(args.bin) 23 | raw_signature = raw(args.sig) 24 | pe_header = pe(raw_binary) 25 | flirt = idasig(raw_signature) 26 | anal = function(raw_binary, pe_header.sections, flirt.tree) 27 | _map(anal.functions, args.out) 28 | except Exception as e: 29 | print "[ERROR]", e 30 | -------------------------------------------------------------------------------- /screenshot/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhdzanifaeq/pyflirt/162f9d0a6836cd7cd8733e943cb51b6d3df02bfa/screenshot/after.png -------------------------------------------------------------------------------- /screenshot/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokhdzanifaeq/pyflirt/162f9d0a6836cd7cd8733e943cb51b6d3df02bfa/screenshot/before.png --------------------------------------------------------------------------------