def add_section(self, hdr, name, VirtualSize, VirtualAddress, Characteristics=0xe0000020): if len(name) > 8: print("Error section name too long") return import_section_hdr = IMAGE_SECTION_HEADER( bytes(name, 'ascii'), # Name VirtualSize, # VirtualSize VirtualAddress, # VirtualAddress VirtualSize, # SizeOfRawData VirtualAddress, # PointerToRawData 0, # PointerToRelocations 0, # PointerToLinenumbers 0, # NumberOfRelocations 0, # NumberOfLinenumbers Characteristics, # Characteristics ) hdr.section_list.append(import_section_hdr) # Correct Value of Number of Sections hdr.pe_header.NumberOfSections += 1 # Fix SizeOfHeaders hdr.opt_header.SizeOfHeaders = alignments( hdr.opt_header.SizeOfHeaders + len(bytes(IMAGE_SECTION_HEADER())), hdr.opt_header.FileAlignment) return hdr
def add_import_section_api(self, hdr, virtualmemorysize, totalsize, check_space=True): # Set check_space to false if the pe-header was relocated if check_space: rva_to_section_table = hdr.dos_header.e_lfanew + len( bytes(_IMAGE_FILE_HEADER())) + len( bytes(_IMAGE_OPTIONAL_HEADER())) number_of_sections = hdr.pe_header.NumberOfSections end_of_section_table = rva_to_section_table + len( bytes(IMAGE_SECTION_HEADER())) * number_of_sections beginning_of_first_section = sys.maxsize for section in hdr.section_list: if section.VirtualAddress < beginning_of_first_section: beginning_of_first_section = section.VirtualAddress if end_of_section_table + len(bytes( IMAGE_SECTION_HEADER())) >= beginning_of_first_section: print("Not enough space for additional section") return import_section = IMAGE_SECTION_HEADER( bytes(".impdata", 'ascii'), # Name 0x10000, # VirtualSize virtualmemorysize - 0x10000, # VirtualAddress 0x10000, # SizeOfRawData virtualmemorysize - 0x10000, # PointerToRawData 0, # PointerToRelocations 0, # PointerToLinenumbers 0, # NumberOfRelocations 0, # NumberOfLinenumbers 0xe0000020, # Characteristics ) hdr.section_list.append(import_section) # Correct Value of Number of Sections hdr.pe_header.NumberOfSections += 1 # Fix SizeOfHeaders hdr.opt_header.SizeOfHeaders = alignments( hdr.opt_header.SizeOfHeaders + len(bytes(IMAGE_SECTION_HEADER())), hdr.opt_header.FileAlignment) return hdr
def init_uc(self): # Calculate required memory pe = pefile.PE(self.sample.path) self.sample.BASE_ADDR = pe.OPTIONAL_HEADER.ImageBase # 0x400000 self.sample.unpacker.BASE_ADDR = self.sample.BASE_ADDR self.sample.virtualmemorysize = self.getVirtualMemorySize() self.STACK_ADDR = 0x0 self.STACK_SIZE = 1024 * 1024 STACK_START = self.STACK_ADDR + self.STACK_SIZE # self.sample.unpacker.secs += [{"name": "stack", "vaddr": self.STACK_ADDR, "vsize": self.STACK_SIZE}] stack_sec_header = IMAGE_SECTION_HEADER( "stack".encode('ascii'), self.STACK_SIZE, self.STACK_ADDR, self.STACK_SIZE, 0, 0, 0, 0, 0, 0, ) self.sample.unpacker.secs.append(SectionHeader(stack_sec_header)) self.HOOK_ADDR = STACK_START + 0x3000 + 0x1000 # Start unicorn emulator with x86-32bit architecture self.uc = Uc(UC_ARCH_X86, UC_MODE_32) if self.sample.unpacker.startaddr is None: self.sample.unpacker.startaddr = self.entrypoint(pe) self.sample.loaded_image = pe.get_memory_mapped_image(ImageBase=self.sample.BASE_ADDR) self.sample.virtualmemorysize = align(self.sample.virtualmemorysize + 0x10000, page_size=4096) # Space possible IAT rebuilding self.sample.unpacker.virtualmemorysize = self.sample.virtualmemorysize self.uc.mem_map(self.sample.BASE_ADDR, self.sample.virtualmemorysize) self.uc.mem_write(self.sample.BASE_ADDR, self.sample.loaded_image) self.setup_processinfo() # Load DLLs self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/KernelBase.dll", 0x73D00000) self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/kernel32.dll", 0x755D0000) self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/ntdll.dll", 0x77400000) # initialize machine registers self.uc.mem_map(self.STACK_ADDR, self.STACK_SIZE) self.uc.reg_write(UC_X86_REG_ESP, self.STACK_ADDR + int(self.STACK_SIZE / 2)) self.uc.reg_write(UC_X86_REG_EBP, self.STACK_ADDR + int(self.STACK_SIZE / 2)) self.uc.mem_write(self.uc.reg_read(UC_X86_REG_ESP) + 0x8, bytes([1])) # -> PEtite Stack Operations? self.uc.reg_write(UC_X86_REG_EAX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EBX, self.PEB_BASE) self.uc.reg_write(UC_X86_REG_ECX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EDX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_ESI, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EDI, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EFLAGS, 0x244) new_pe = PE(self.uc, self.sample.BASE_ADDR) prot_val = lambda x, y: True if x & y != 0 else False for s in new_pe.section_list: self.sample.atn[( s.VirtualAddress + self.sample.BASE_ADDR, s.VirtualAddress + self.sample.BASE_ADDR + s.VirtualSize)] = convert_to_string( s.Name) self.sample.ntp[convert_to_string(s.Name)] = ( prot_val(s.Characteristics, 0x20000000), prot_val(s.Characteristics, 0x40000000), prot_val(s.Characteristics, 0x80000000)) # for s in pe.sections: # atn[(s.VirtualAddress + self.sample.BASE_ADDR, s.VirtualAddress + self.sample.BASE_ADDR + s.Misc_VirtualSize)] = s.Name # ntp[s.Name] = (s.IMAGE_SCN_MEM_EXECUTE, s.IMAGE_SCN_MEM_READ, s.IMAGE_SCN_MEM_WRITE) # init syscall handling and prepare hook memory for return values self.apicall_handler = WinApiCalls(self) self.uc.mem_map(self.HOOK_ADDR, 0x1000) # self.sample.unpacker.secs += [{"name": "hooks", "vaddr": self.HOOK_ADDR, "vsize": 0x1000}] hook_sec_header = IMAGE_SECTION_HEADER( "hooks".encode('ascii'), 0x1000, self.HOOK_ADDR, 0x1000, 0, 0, 0, 0, 0, 0, ) self.sample.unpacker.secs.append(SectionHeader(stack_sec_header)) hexstr = bytes.fromhex('000000008b0425') + struct.pack('<I', self.HOOK_ADDR) + bytes.fromhex( 'c3') # mov eax, [HOOK]; ret -> values of syscall are stored in eax self.uc.mem_write(self.HOOK_ADDR, hexstr) # handle imports # TODO Update when custom loader available for lib in pe.DIRECTORY_ENTRY_IMPORT: descriptor = ImportDescriptor(None, lib.struct.Characteristics, lib.struct.TimeDateStamp, lib.struct.ForwarderChain, lib.struct.Name, lib.struct.FirstThunk) fct_list = [] for i in lib.imports: fct_list.append(i.name) imp = Import(descriptor, lib.dll.decode('ascii'), fct_list) self.sample.original_imports.append(imp) for func in lib.imports: func_name = func.name.decode() if func.name is not None else f"no name: 0x{func.address:02x}" dll_name = lib.dll.decode() if lib.dll is not None else "-- unknown --" self.sample.imports.add(func_name) curr_hook_addr = self.apicall_handler.add_hook(self.uc, func_name, dll_name) self.uc.mem_write(func.address, struct.pack('<I', curr_hook_addr)) hdr = PE(self.uc, self.sample.BASE_ADDR) # Patch DLLs with hook # Hardcoded values used for speed improvement -> Offsets can be calculated with utils.calc_export_offset_of_dll self.apicall_handler.add_hook(self.uc, "VirtualProtect", "KernelBase.dll", 0x73D00000 + 0x1089f0) self.apicall_handler.add_hook(self.uc, "VirtualAlloc", "KernelBase.dll", 0x73D00000 + 0xd4600) self.apicall_handler.add_hook(self.uc, "VirtualFree", "KernelBase.dll", 0x73D00000 + 0xd4ae0) self.apicall_handler.add_hook(self.uc, "LoadLibraryA", "KernelBase.dll", 0x73D00000 + 0xf20d0) self.apicall_handler.add_hook(self.uc, "GetProcAddress", "KernelBase.dll", 0x73D00000 + 0x102870) self.apicall_handler.add_hook(self.uc, "VirtualProtect", "kernel32.dll", 0x755D0000 + 0x16760) self.apicall_handler.add_hook(self.uc, "VirtualAlloc", "kernel32.dll", 0x755D0000 + 0x166a0) self.apicall_handler.add_hook(self.uc, "VirtualFree", "kernel32.dll", 0x755D0000 + 0x16700) self.apicall_handler.add_hook(self.uc, "LoadLibraryA", "kernel32.dll", 0x755D0000 + 0x157b0) self.apicall_handler.add_hook(self.uc, "GetProcAddress", "kernel32.dll", 0x755D0000 + 0x14ee0) # Add hooks self.uc.hook_add(UC_HOOK_CODE, self.hook_code) self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE | UC_HOOK_MEM_FETCH, self.hook_mem_access) self.uc.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
def parse_disk_to_header(sample, query_header=None): offsets = {} # Read DOS Header with open(sample, 'rb') as f: dos_read = bytearray(f.read(header_sizes["_IMAGE_DOS_HEADER"])) dos_header = _IMAGE_DOS_HEADER.from_buffer(dos_read) if getattr(dos_header, "e_magic") != 0x5A4D: print(f"e_magic = {getattr(dos_header, 'e_magic')}") print("Wrong DOS Magic Value (MZ). Aborting...") raise InvalidPEFile e_lfanew = getattr(dos_header, "e_lfanew") if query_header == "e_lfanew": return e_lfanew if query_header == "_IMAGE_DOS_HEADER": return dos_header offsets["_IMAGE_DOS_HEADER"] = 0 # Read PE Header pe_hdr_offset = e_lfanew f.seek(pe_hdr_offset) offsets["_IMAGE_FILE_HEADER"] = pe_hdr_offset pe_read = bytearray(f.read(header_sizes["_IMAGE_FILE_HEADER"])) pe_header = _IMAGE_FILE_HEADER.from_buffer(pe_read) if getattr(pe_header, "Signature") != 0x4550: print(f"Signature: {getattr(pe_header, 'Signature')}") print("Wrong PE Header Signature. Aborting...") raise InvalidPEFile number_of_sections = getattr(pe_header, "NumberOfSections") if query_header == "NumberOfSections": return number_of_sections if query_header == "_IMAGE_FILE_HEADER": return pe_header # Read Optional Header opt_hdr_offset = pe_hdr_offset + header_sizes["_IMAGE_FILE_HEADER"] f.seek(opt_hdr_offset) offsets["_IMAGE_OPTIONAL_HEADER"] = opt_hdr_offset opt_read = bytearray(f.read(header_sizes["_IMAGE_OPTIONAL_HEADER"])) opt_header = _IMAGE_OPTIONAL_HEADER.from_buffer(opt_read) if getattr(opt_header, "Magic") != 0x10B: print(f"OPT Magic: {getattr(opt_header, 'Magic')}") print("Wrong Optional Header Magic. Aborting...") raise InvalidPEFile if query_header == "_IMAGE_OPTIONAL_HEADER": return opt_header if query_header == "_IMAGE_DATA_DIRECTORY": return getattr(opt_header, "DataDirectory") rva_to_IMAGE_IMPORT_DESCRIPTOR = getattr( getattr(opt_header, "DataDirectory")[1], "VirtualAddress") size_import_table = getattr( getattr(opt_header, "DataDirectory")[1], "Size") #import_table = get_imp(uc, rva_to_IMAGE_IMPORT_DESCRIPTOR, base_addr, size_import_table) import_descriptor_table = [] # TODO Use this when custom loader finished #for x in range(int((size_import_table / header_sizes["IMAGE_IMPORT_DESCRIPTOR"]))): # f.seek(rva_to_IMAGE_IMPORT_DESCRIPTOR) # imp_descriptor_read = bytearray(f.read(header_sizes["IMAGE_IMPORT_DESCRIPTOR"])) # imp_descriptor = IMAGE_IMPORT_DESCRIPTOR.from_buffer(imp_descriptor_read) # if getattr(imp_descriptor, "Characteristics") != 0 or getattr(imp_descriptor, "FirstThunk") != 0: # import_descriptor_table.append(imp_descriptor) # rva_to_IMAGE_IMPORT_DESCRIPTOR += header_sizes["IMAGE_IMPORT_DESCRIPTOR"] # else: # break if query_header == "IMPORTS": return import_descriptor_table # Read Section Header section_hdr_offset = opt_hdr_offset + header_sizes[ "_IMAGE_OPTIONAL_HEADER"] offsets["IMAGE_SECTION_HEADER"] = section_hdr_offset section_headers = [] for i in range(number_of_sections): f.seek(section_hdr_offset) sec_read = bytearray(f.read(header_sizes["IMAGE_SECTION_HEADER"])) sec_header = IMAGE_SECTION_HEADER.from_buffer(sec_read) section_headers.append(sec_header) section_hdr_offset += header_sizes["IMAGE_SECTION_HEADER"] if query_header == "IMAGE_SECTION_HEADER": return section_headers if query_header == "Offsets": return offsets headers = { "_IMAGE_DOS_HEADER": dos_header, "_IMAGE_FILE_HEADER": pe_header, "_IMAGE_OPTIONAL_HEADER": opt_header, "IMAGE_SECTION_HEADER": section_headers, "IMPORTS": import_descriptor_table, } return headers
def parse_memory_to_header(uc, base_addr, query_header=None): # Read DOS Header uc_dos = uc.mem_read(base_addr, header_sizes["_IMAGE_DOS_HEADER"]) dos_header = _IMAGE_DOS_HEADER.from_buffer(uc_dos) if getattr(dos_header, "e_magic") != 0x5A4D: print(f"e_magic = {getattr(dos_header, 'e_magic')}") print("Wrong DOS Magic Value (MZ). Aborting...") raise InvalidPEFile e_lfanew = getattr(dos_header, "e_lfanew") if query_header == "e_lfanew": return e_lfanew if query_header == "_IMAGE_DOS_HEADER": return dos_header # Read PE Header pe_hdr_offset = base_addr + e_lfanew uc_pe = uc.mem_read(pe_hdr_offset, header_sizes["_IMAGE_FILE_HEADER"]) pe_header = _IMAGE_FILE_HEADER.from_buffer(uc_pe) if getattr(pe_header, "Signature") != 0x4550: print(f"Signature: {getattr(pe_header, 'Signature')}") print("Wrong PE Header Signature. Aborting...") raise InvalidPEFile number_of_sections = getattr(pe_header, "NumberOfSections") if query_header == "NumberOfSections": return number_of_sections if query_header == "_IMAGE_FILE_HEADER": return pe_header # Read Optional Header opt_hdr_offset = pe_hdr_offset + header_sizes["_IMAGE_FILE_HEADER"] uc_opt = uc.mem_read(opt_hdr_offset, header_sizes["_IMAGE_OPTIONAL_HEADER"]) opt_header = _IMAGE_OPTIONAL_HEADER.from_buffer(uc_opt) if getattr(opt_header, "Magic") != 0x10B: print(f"OPT Magic: {getattr(opt_header, 'Magic')}") print("Wrong Optional Header Magic. Aborting...") raise InvalidPEFile if query_header == "_IMAGE_OPTIONAL_HEADER": return opt_header if query_header == "_IMAGE_DATA_DIRECTORY": return getattr(opt_header, "DataDirectory") rva_to_IMAGE_IMPORT_DESCRIPTOR = getattr( getattr(opt_header, "DataDirectory")[1], "VirtualAddress") size_import_table = getattr( getattr(opt_header, "DataDirectory")[1], "Size") import_table = get_imp(uc, rva_to_IMAGE_IMPORT_DESCRIPTOR, base_addr, size_import_table) if query_header == "IMPORTS": return import_table # Read Section Header section_hdr_offset = opt_hdr_offset + header_sizes["_IMAGE_OPTIONAL_HEADER"] section_headers = [] for i in range(number_of_sections): uc_sec = uc.mem_read(section_hdr_offset, header_sizes["IMAGE_SECTION_HEADER"]) sec_header = IMAGE_SECTION_HEADER.from_buffer(uc_sec) section_headers.append(sec_header) section_hdr_offset += header_sizes["IMAGE_SECTION_HEADER"] if query_header == "IMAGE_SECTION_HEADER": return section_headers headers = { "_IMAGE_DOS_HEADER": dos_header, "_IMAGE_FILE_HEADER": pe_header, "_IMAGE_OPTIONAL_HEADER": opt_header, "IMAGE_SECTION_HEADER": section_headers, "IMPORTS": import_table, } return headers
import struct from ctypes import * from datetime import datetime from unicorn import UcError from unipacker.pe_structs import _IMAGE_DOS_HEADER, _IMAGE_FILE_HEADER, _IMAGE_OPTIONAL_HEADER, IMAGE_SECTION_HEADER, \ _IMAGE_DATA_DIRECTORY, IMAGE_IMPORT_DESCRIPTOR, SectionHeader, DosHeader, PEHeader, OptionalHeader, \ ImportDescriptor, DataDirectory from unipacker.utils import InvalidPEFile, ImportValues, get_string header_sizes = { "_IMAGE_DOS_HEADER": len(bytes(_IMAGE_DOS_HEADER())), # 0x40 "_IMAGE_FILE_HEADER": len(bytes(_IMAGE_FILE_HEADER())), # 0x18 "_IMAGE_OPTIONAL_HEADER": len(bytes(_IMAGE_OPTIONAL_HEADER())), # 0xE0 "IMAGE_SECTION_HEADER": len(bytes(IMAGE_SECTION_HEADER())), # 0x28 "_IMAGE_DATA_DIRECTORY": len(bytes(_IMAGE_DATA_DIRECTORY())), # 0x8 "IMAGE_IMPORT_DESCRIPTOR": len(bytes(IMAGE_IMPORT_DESCRIPTOR())), } short_hdr_names = { "DOS": "_IMAGE_DOS_HEADER", "DOS_HEADER": "_IMAGE_DOS_HEADER", "DOS_HDR": "_IMAGE_DOS_HEADER", "IMAGE_DOS_HEADER": "_IMAGE_DOS_HEADER", "PE": "_IMAGE_FILE_HEADER", "PE_HEADER": "_IMAGE_FILE_HEADER", "PE_HDR": "_IMAGE_FILE_HEADER", "FILE_HEADER": "_IMAGE_FILE_HEADER", "FILE_HDR": "_IMAGE_FILE_HEADER", "IMAGE_FILE_HEADER": "_IMAGE_FILE_HEADER",