def _extract_messagetable(dll: pefile.PE, locale_id: LocaleID) -> Mapping[int, str]: mmap = dll.get_memory_mapped_image() entries = dll.DIRECTORY_ENTRY_RESOURCE.entries offset, size = _traverse_resources(entries, (RT_MESSAGETABLE, 1, locale_id.value)) data = mmap[offset : offset + size] return dict(_read_messagetable_resource(data))
def get_icon_group(pe_file: pefile.PE, data_entry: pefile.Structure) -> Optional[list]: try: data_rva = data_entry.OffsetToData size = data_entry.Size data = pe_file.get_memory_mapped_image()[data_rva:data_rva + size] file_offset = pe_file.get_offset_from_rva(data_rva) grp_icon_dir = pefile.Structure(GRPICONDIR_format, file_offset=file_offset) grp_icon_dir.__unpack__(data) if grp_icon_dir.Reserved == 0 or grp_icon_dir.Type == 1: offset = grp_icon_dir.sizeof() entries = list() for idx in range(0, grp_icon_dir.Count): grp_icon = pefile.Structure(GRPICONDIRENTRY_format, file_offset=file_offset + offset) grp_icon.__unpack__(data[offset:]) offset += grp_icon.sizeof() entries.append(grp_icon) return entries except pefile.PEFormatError: pass return None
def map_and_load(self, path, execute_now=False): ql = self.ql pe = PE(path, fast_load=True) # Make sure no module will occupy the NULL page if self.next_image_base > pe.OPTIONAL_HEADER.ImageBase: IMAGE_BASE = self.next_image_base pe.relocate_image(IMAGE_BASE) else: IMAGE_BASE = pe.OPTIONAL_HEADER.ImageBase IMAGE_SIZE = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) while IMAGE_BASE + IMAGE_SIZE < self.heap_base_address: if not ql.mem.is_mapped(IMAGE_BASE, 1): self.next_image_base = IMAGE_BASE + 0x10000 ql.mem.map(IMAGE_BASE, IMAGE_SIZE) pe.parse_data_directories() data = bytearray(pe.get_memory_mapped_image()) ql.mem.write(IMAGE_BASE, bytes(data)) logging.info("[+] Loading %s to 0x%x" % (path, IMAGE_BASE)) entry_point = IMAGE_BASE + pe.OPTIONAL_HEADER.AddressOfEntryPoint if self.entry_point == 0: # Setting entry point to the first loaded module entry point, so the debugger can break. self.entry_point = entry_point logging.info("[+] PE entry point at 0x%x" % entry_point) self.install_loaded_image_protocol(IMAGE_BASE, IMAGE_SIZE) self.images.append( self.coverage_image( IMAGE_BASE, IMAGE_BASE + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, path)) if execute_now: logging.info( f'[+] Running from 0x{entry_point:x} of {path}') assembler = self.ql.create_assembler() code = f""" mov rcx, {IMAGE_BASE} mov rdx, {self.gST} mov rax, {entry_point} call rax """ runcode, _ = assembler.asm(code) ptr = ql.os.heap.alloc(len(runcode)) ql.mem.write(ptr, bytes(runcode)) ql.os.exec_arbitrary(ptr, ptr + len(runcode)) else: self.modules.append((path, IMAGE_BASE, entry_point, pe)) return True else: IMAGE_BASE += 0x10000 pe.relocate_image(IMAGE_BASE) return False
def configExtract(rawData): pe = PE(data=rawData) try: rt_string_idx = [ entry.id for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries].index(RESOURCE_TYPE['RT_RCDATA']) except ValueError as e: return None except AttributeError as e: return None rt_string_directory = pe.DIRECTORY_ENTRY_RESOURCE.entries[rt_string_idx] for entry in rt_string_directory.directory.entries: if str(entry.name) == "CFG": data_rva = entry.directory.entries[0].data.struct.OffsetToData size = entry.directory.entries[0].data.struct.Size data = pe.get_memory_mapped_image()[data_rva:data_rva + size] return data
def config_extract(raw_data): try: pe = PE(data=raw_data) try: rt_string_idx = [ entry.id for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries].index(RESOURCE_TYPE['RT_RCDATA']) except ValueError: return None except AttributeError: return None rt_string_directory = pe.DIRECTORY_ENTRY_RESOURCE.entries[rt_string_idx] for entry in rt_string_directory.directory.entries: if str(entry.name) == "XX-XX-XX-XX" or str(entry.name) == "CG-CG-CG-CG": data_rva = entry.directory.entries[0].data.struct.OffsetToData size = entry.directory.entries[0].data.struct.Size data = pe.get_memory_mapped_image()[data_rva:data_rva + size] config = data.split('####@####') return config except: return None
def get_icon(pe_file: pefile.PE, icon_rsrcs: pefile.ResourceDirEntryData, idx: int) -> Optional[bytearray]: if idx < 0: try: idx = [entry.id for entry in icon_rsrcs.directory.entries].index(-idx) except ValueError: return None else: idx = idx if idx < len(icon_rsrcs.directory.entries) else None if idx is None: return None icon_id = icon_rsrcs.directory.entries[idx] icon_entry = icon_id.directory.entries[0] if icon_entry.struct.DataIsDirectory: return None data_rva = icon_entry.data.struct.OffsetToData size = icon_entry.data.struct.Size data = pe_file.get_memory_mapped_image()[data_rva:data_rva + size] return data
def map_and_load(self, path: str, exec_now: bool = False): """Map and load a module into memory. The specified module would be mapped and loaded into the address set in the `next_image_base` member. It is the caller's responsibility to make sure that the memory is available. On success, `next_image_base` will be updated accordingly. Args: path : path of the module binary to load exec_now : execute module right away; will be enququed if not Raises: QlMemoryMappedError : when `next_image_base` is not available """ ql = self.ql pe = PE(path, fast_load=True) # use image base only if it does not point to NULL image_base = pe.OPTIONAL_HEADER.ImageBase or self.next_image_base image_size = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) assert (image_base % 0x1000) == 0, 'image base is expected to be page-aligned' if image_base != pe.OPTIONAL_HEADER.ImageBase: pe.relocate_image(image_base) pe.parse_data_directories() data = bytes(pe.get_memory_mapped_image()) ql.mem.map(image_base, image_size, info="[module]") ql.mem.write(image_base, data) ql.log.info(f'Module {path} loaded to {image_base:#x}') entry_point = image_base + pe.OPTIONAL_HEADER.AddressOfEntryPoint ql.log.info(f'Module entry point at {entry_point:#x}') # the 'entry_point' member is used by the debugger. if not set, set it # to the first loaded module entry point so the debugger can break if self.entry_point == 0: self.entry_point = entry_point self.install_loaded_image_protocol(image_base, image_size) # this would be used later be os.find_containing_image self.images.append( self.coverage_image(image_base, image_base + image_size, path)) # update next memory slot to allow sequencial loading. its availability # is unknown though self.next_image_base = image_base + image_size module_info = (path, image_base, entry_point) # execute the module right away or enqueue it if exec_now: # call entry point while retaining the current return address self.execute_module(*module_info, eoe_trap=None) else: self.modules.append(module_info)
def get_icon_info(self, pe: pefile.PE) -> Tuple[str, str, str, str]: """Get icon in PNG format and information for searching for similar icons @return: tuple of (image data in PNG format encoded as base64, md5 hash of image data, md5 hash of "simplified" image for fuzzy matching) """ if not pe or not hasattr(pe, "DIRECTORY_ENTRY_RESOURCE"): return None, None, None, None try: idx = [entry.id for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries] if pefile.RESOURCE_TYPE["RT_GROUP_ICON"] not in idx: return None, None, None, None rt_group_icon_idx = idx.index( pefile.RESOURCE_TYPE["RT_GROUP_ICON"]) rt_group_icon_dir = pe.DIRECTORY_ENTRY_RESOURCE.entries[ rt_group_icon_idx] entry = rt_group_icon_dir.directory.entries[0] offset = entry.directory.entries[0].data.struct.OffsetToData size = entry.directory.entries[0].data.struct.Size peicon = PEGroupIconDir( pe.get_memory_mapped_image()[offset:offset + size]) bigwidth = 0 bigheight = 0 bigbpp = 0 bigidx = -1 iconidx = 0 if hasattr(peicon, "icons") and peicon.icons: for idx, icon in enumerate(peicon.icons): if icon.bWidth >= bigwidth and icon.bHeight >= bigheight and icon.wBitCount >= bigbpp: bigwidth = icon.bWidth bigheight = icon.bHeight bigbpp = icon.wBitCount bigidx = icon.nID iconidx = idx rt_icon_idx = False rt_icon_idx_tmp = [ entry.id for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries ] if pefile.RESOURCE_TYPE["RT_ICON"] in rt_icon_idx_tmp: rt_icon_idx = rt_icon_idx_tmp.index( pefile.RESOURCE_TYPE["RT_ICON"]) if not isinstance(rt_icon_idx, int): return None, None, None, None rt_icon_dir = pe.DIRECTORY_ENTRY_RESOURCE.entries[rt_icon_idx] for entry in rt_icon_dir.directory.entries: if entry.id == bigidx: offset = entry.directory.entries[ 0].data.struct.OffsetToData size = entry.directory.entries[0].data.struct.Size icon = peicon.get_icon_file( iconidx, pe.get_memory_mapped_image()[offset:offset + size]) with BytesIO() as byteio: byteio.write(icon) byteio.seek(0) try: with Image.open( byteio) as img, BytesIO() as output: img.save(output, format="PNG") dhash = self.generate_icon_dhash(img) img = (img.resize( (8, 8), Image.BILINEAR).convert("RGB").convert( "P", palette=Image.ADAPTIVE, colors=2).convert("L")) lowval = img.getextrema()[0] img = img.point(lambda i: 255 if i > lowval else 0).convert("1") simplified = bytearray(img.getdata()) m = hashlib.md5() m.update(output.getvalue()) fullhash = m.hexdigest() m = hashlib.md5() m.update(simplified) simphash = m.hexdigest() icon = base64.b64encode( output.getvalue()).decode() except ValueError: log.error( "parse_pe.py -> get_incon_info -> buffer is not large enough" ) return None, None, None, None except OSError as e: log.error(e) return None, None, None, None return icon, fullhash, simphash, dhash except Exception as e: log.error(e, exc_info=True) return None, None, None, None