def parseSections(self): self.sections = [] off = self.IMAGE_DOS_HEADER.e_lfanew + len(self.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sbytes = self.readAtOffset(off, secsize * self.IMAGE_NT_HEADERS.FileHeader.NumberOfSections) while sbytes: s = vstruct.getStructure("pe.IMAGE_SECTION_HEADER") s.vsParse(sbytes[:secsize]) self.sections.append(s) sbytes = sbytes[secsize:]
def parseSections(self): self.sections = [] off = self.IMAGE_DOS_HEADER.e_lfanew + len(self.IMAGE_NT_HEADERS) off -= len(self.IMAGE_NT_HEADERS.OptionalHeader.DataDirectory) off += self.IMAGE_NT_HEADERS.OptionalHeader.NumberOfRvaAndSizes * len(vstruct.getStructure("pe.IMAGE_DATA_DIRECTORY")) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sbytes = self.readAtOffset(off, secsize * self.IMAGE_NT_HEADERS.FileHeader.NumberOfSections) while sbytes: s = vstruct.getStructure("pe.IMAGE_SECTION_HEADER") s.vsParse(sbytes[:secsize]) self.sections.append(s) sbytes = sbytes[secsize:]
def __init__(self, fd, inmem=False): object.__init__(self) self.inmem = inmem fd.seek(0) self.fd = fd self.pe32p = False self.psize = 4 self.high_bit_mask = 0x80000000 self.IMAGE_DOS_HEADER = vstruct.getStructure("pe.IMAGE_DOS_HEADER") dosbytes = self.readAtOffset(0, len(self.IMAGE_DOS_HEADER)) self.IMAGE_DOS_HEADER.vsParse(dosbytes) nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS") # Parse in a default 32 bit, and then check for 64... if nt.FileHeader.Machine in [ IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 ]: nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS64") self.pe32p = True self.psize = 8 self.high_bit_mask = 0x8000000000000000 self.IMAGE_NT_HEADERS = nt
def __init__(self, fd, inmem=False): """ Construct a PE object. use inmem=True if you are using a MemObjFile or other "memory like" image. """ object.__init__(self) self.inmem = inmem self.fd = fd self.fd.seek(0) self.pe32p = False self.psize = 4 self.IMAGE_DOS_HEADER = vstruct.getStructure("pe.IMAGE_DOS_HEADER") dosbytes = fd.read(len(self.IMAGE_DOS_HEADER)) self.IMAGE_DOS_HEADER.vsParse(dosbytes) nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS") # Parse in a default 32 bit, and then check for 64... if nt.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64: nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS64") self.pe32p = True self.psize = 8 self.IMAGE_NT_HEADERS = nt
def __init__(self, fd, inmem=False): """ Construct a PE object. use inmem=True if you are using a MemObjFile or other "memory like" image. """ object.__init__(self) self.inmem = inmem self.filesize = None self.max_rva = None if not inmem: fd.seek(0, os.SEEK_END) self.filesize = fd.tell() fd.seek(0) self.fd = fd self.pe32p = False self.psize = 4 self.high_bit_mask = 0x80000000 self.IMAGE_DOS_HEADER = vstruct.getStructure("pe.IMAGE_DOS_HEADER") dosbytes = self.readAtOffset(0, len(self.IMAGE_DOS_HEADER)) self.IMAGE_DOS_HEADER.vsParse(dosbytes) nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS") # Parse in a default 32 bit, and then check for 64... if nt.FileHeader.Machine in [ IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 ]: nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS64") self.pe32p = True self.psize = 8 self.high_bit_mask = 0x8000000000000000 self.IMAGE_NT_HEADERS = nt
def getStruct(self, sname, va=None): """ Retrieve a vstruct structure optionally populated with memory from the specified address. Returns a standard vstruct object. """ # Check if we need to parse symbols for a library libbase = sname.split('.')[0] self._loadBinaryNorm(libbase) if self.vsbuilder.hasVStructNamespace(libbase): vs = self.vsbuilder.buildVStruct(sname) # FIXME this is deprecated and should die... else: vs = vstruct.getStructure(sname) if vs == None: return None if va == None: return vs bytez = self.readMemory(va, len(vs)) vs.vsParse(bytez) return vs
def getStruct(self, sname, va=None): """ Retrieve a vstruct structure optionally populated with memory from the specified address. Returns a standard vstruct object. """ # Check if we need to parse symbols for a library libbase = sname.split('.')[0] self._loadBinaryNorm(libbase) if self.vsbuilder.hasVStructNamespace(libbase): vs = self.vsbuilder.buildVStruct(sname) # FIXME this is deprecated and should die... else: vs = vstruct.getStructure(sname) if vs is None: return None if va is None: return vs bytez = self.readMemory(va, len(vs)) vs.vsParse(bytez) return vs
def readStructAtOffset(self, offset, structname): s = vstruct.getStructure(structname) sbytes = self.readAtOffset(offset, len(s)) if not sbytes: return None s.vsParse(sbytes) return s
def readStructAtRva(self, rva, structname, check=False): s = vstruct.getStructure(structname) slen = len(s) if check and not self.checkRva(rva, size=slen): return None bytes = self.readAtRva(rva, len(s)) s.vsParse(bytes) return s
def getStruct(self, sname, address): """ Retrieve a vstruct structure populated with memory from the specified address. Returns a standard vstruct object. """ vs = vstruct.getStructure(sname) bytes = self.readMemory(address, len(vs)) vs.vsParse(bytes) return vs
def parseImports(self): self.imports = [] idir = self.IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] poff = self.rvaToOffset(idir.VirtualAddress) if poff == 0: return x = vstruct.getStructure("pe.IMAGE_IMPORT_DIRECTORY") isize = len(x) x.vsParse(self.readAtOffset(poff, isize)) while x.Name != 0: liboff = self.rvaToOffset(x.Name) libname = self.readAtOffset(liboff, 256).split("\x00")[0] idx = 0 noff = self.rvaToOffset(x.OriginalFirstThunk) aoff = self.rvaToOffset(x.FirstThunk) while True: ava = self.readPointerAtOffset(aoff+(self.psize*idx)) if ava == 0: break nva = self.readPointerAtOffset(noff+(self.psize*idx)) #FIXME high bit testing for 64 bit if nva & 0x80000000: name = ordlookup.ordLookup(libname, nva & 0x7fffffff) else: nameoff = self.rvaToOffset(nva) + 2 # Skip the short "hint" name = self.readAtOffset(nameoff, 256).split("\x00")[0] self.imports.append((x.FirstThunk+(idx*self.psize),libname,name)) idx += 1 poff += isize x.vsParse(self.readAtOffset(poff, len(x)))
def parseImports(self): self.imports = [] idir = self.IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] poff = self.rvaToOffset(idir.VirtualAddress) if poff == 0: return x = vstruct.getStructure("pe.IMAGE_IMPORT_DIRECTORY") isize = len(x) x.vsParse(self.readAtOffset(poff, isize)) while x.Name != 0: liboff = self.rvaToOffset(x.Name) libname = self.readAtOffset(liboff, 256).split("\x00")[0] idx = 0 noff = self.rvaToOffset(x.OriginalFirstThunk) aoff = self.rvaToOffset(x.FirstThunk) while True: ava = self.readPointerAtOffset(aoff + (self.psize * idx)) if ava == 0: break nva = self.readPointerAtOffset(noff + (self.psize * idx)) # FIXME high bit testing for 64 bit if nva & 0x80000000: name = ordlookup.ordLookup(libname, nva & 0x7FFFFFFF) else: nameoff = self.rvaToOffset(nva) + 2 # Skip the short "hint" name = self.readAtOffset(nameoff, 256).split("\x00")[0] self.imports.append((x.FirstThunk + (idx * self.psize), libname, name)) idx += 1 poff += isize x.vsParse(self.readAtOffset(poff, len(x)))
def getSignature(self): ''' Returns the SignatureEntry vstruct if the pe has an embedded certificate, None if the magic bytes are NOT set in the security directory entry AND the size of the signature entry is less than 0. ''' ds = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_SECURITY) va = ds.VirtualAddress size = ds.Size if size <= 0: return None bytez = self.readAtOffset(va, size) if not bytez: return None se = vstruct.getStructure('pe.SignatureEntry') se.vsParse(bytez) if se.magic != "\x00\x02\x02\x00": return None return se
def __init__(self, fd, inmem=False): """ Construct a PE object. use inmem=True if you are using a MemObjFile or other "memory like" image. """ object.__init__(self) self.inmem = inmem self.filesize = None if not inmem: fd.seek(0, os.SEEK_END) self.filesize = fd.tell() fd.seek(0) self.fd = fd self.pe32p = False self.psize = 4 self.high_bit_mask = 0x80000000 self.IMAGE_DOS_HEADER = vstruct.getStructure("pe.IMAGE_DOS_HEADER") dosbytes = self.readAtOffset(0, len(self.IMAGE_DOS_HEADER)) self.IMAGE_DOS_HEADER.vsParse(dosbytes) nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS") # Parse in a default 32 bit, and then check for 64... if nt.FileHeader.Machine in [ IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 ]: nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew, "pe.IMAGE_NT_HEADERS64") self.pe32p = True self.psize = 8 self.high_bit_mask = 0x8000000000000000 self.IMAGE_NT_HEADERS = nt
vsver = vs.getVersionValue('FileVersion') if vsver != None and len(vsver): # add check to split seeing samples with spaces and nothing else.. parts = vsver.split() if len(parts): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS),("Library", VASET_STRING))) vw.addVaSet('pe:ordinals', (('Address', VASET_ADDRESS),('Ordinal',VASET_INTEGER))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len(pe.IMAGE_NT_HEADERS.FileHeader) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion
def loadPeIntoWorkspace(vw, pe, filename=None, baseaddr=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch is None: raise Exception("Machine %.4x is not supported for PE!" % mach) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) vw.setMeta('DefaultCall', defcalls.get(arch, 'unknown')) # Set ourselves up for extended windows binary analysis if baseaddr is None: baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase + codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname != None: fvivname = dllname if fvivname is None: fvivname = "pe_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(fvivname.lower(), baseaddr, fhash) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it try: vs = pe.getVS_VERSIONINFO() except Exception as e: vs = None vw.vprint('Failed to load version info resource due to %s' % (repr(e), )) if vs is not None: vsver = vs.getVersionValue('FileVersion') if vsver is not None and len(vsver): # add check to split seeing samples with spaces and nothing else.. parts = vsver.split() if len(parts): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS), ("Library", VASET_STRING))) vw.addVaSet('pe:ordinals', (('Address', VASET_ADDRESS), ('Ordinal', VASET_INTEGER))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len( pe.IMAGE_NT_HEADERS.FileHeader ) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len( pe.IMAGE_NT_HEADERS ) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign # Lines below will extend the header to one page. In reality, some # samples put code after the header but in the same page, so it is better # to stick with header_size. See FLARE-662. #if secrem != 0: # header += "\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != "PE": raise Exception("We only support PE exe's") if not vw.isLocation(baseaddr + magicaddr): padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = baseaddr + magicaddr + 4 ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources carvepes = vw.config.viv.parsers.pe.carvepes deaddirs = [ PE.IMAGE_DIRECTORY_ENTRY_EXPORT, PE.IMAGE_DIRECTORY_ENTRY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_RESOURCE, PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION, PE.IMAGE_DIRECTORY_ENTRY_SECURITY, PE.IMAGE_DIRECTORY_ENTRY_BASERELOC, PE.IMAGE_DIRECTORY_ENTRY_DEBUG, PE.IMAGE_DIRECTORY_ENTRY_COPYRIGHT, PE.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, PE.IMAGE_DIRECTORY_ENTRY_GLOBALPTR, PE.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, PE.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_IAT, PE.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ] deadvas = [ddir.VirtualAddress] for datadir in deaddirs: d = pe.getDataDirectory(datadir) if d.VirtualAddress: deadvas.append(d.VirtualAddress) for idx, sec in enumerate(pe.sections): logger.debug('loading section number %d', idx) mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = (sec.VirtualAddress == ddir.VirtualAddress) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if secrva >= codebase and secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva and entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx + 1 >= len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx + 1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) if secname and (secname == '.data'): # Allow truncated .data segment. secbytes = pe.readAtOffset(secoff, readsize, shortok=True) else: secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen logger.debug('mapping section: %d %s', idx, secname) vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) except Exception as e: logging.getLogger("parsers.pe.loadPE").warning( "Error Loading Section (%s size:%d rva:%.8x offset: %d): %s", secname, secfsize, secrva, secoff, e) vw.addExport(entry, EXP_FUNCTION, '__entry', fname) vw.addEntryPoint(entry) # store the actual reloc section virtual address reloc_va = pe.getDataDirectory( PE.IMAGE_DIRECTORY_ENTRY_BASERELOC).VirtualAddress if reloc_va: reloc_va += baseaddr vw.setFileMeta(fname, "reloc_va", reloc_va) for rva, rtype in pe.getRelocations(): # map PE reloc to VIV reloc ( or dont... ) vtype = relmap.get(rtype) if vtype is None: logger.info('Skipping PE Relocation type: %d at %d (no handler)', rtype, rva) continue mapoffset = vw.readMemoryPtr(rva + baseaddr) - baseaddr vw.addRelocation(rva + baseaddr, vtype, mapoffset) for rva, lname, iname in pe.getImports(): if vw.probeMemory(rva + baseaddr, 4, e_mem.MM_READ): vw.makeImport(rva + baseaddr, lname, iname) # Tell vivisect about ntdll functions that don't exit... vw.addNoReturnApi("ntdll.RtlExitUserThread") vw.addNoReturnApi("kernel32.ExitProcess") vw.addNoReturnApi("kernel32.ExitThread") vw.addNoReturnApi("kernel32.FatalExit") vw.addNoReturnApiRegex("^msvcr.*\._CxxThrowException$") vw.addNoReturnApiRegex("^msvcr.*\.abort$") vw.addNoReturnApiRegex("^msvcr.*\.exit$") vw.addNoReturnApiRegex("^msvcr.*\._exit$") vw.addNoReturnApiRegex("^msvcr.*\._exit$") vw.addNoReturnApiRegex("^msvcr.*\.quick_exit$") # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/cexit-c-exit?view=vs-2019 # vw.addNoReturnApiRegex("^msvcr.*\._cexit$") # vw.addNoReturnApiRegex("^msvcr.*\._c_exit$") vw.addNoReturnApi("ntoskrnl.KeBugCheckEx") exports = pe.getExports() for rva, ord, name in exports: eva = rva + baseaddr # Functions exported by ordinal only have no name if not name: name = "Ordinal_" + str(ord) try: vw.setVaSetRow('pe:ordinals', (eva, ord)) vw.addExport(eva, EXP_UNTYPED, name, fname) if vw.probeMemory(eva, 1, e_mem.MM_EXEC): vw.addEntryPoint(eva) except Exception as e: vw.vprint('addExport Failed: %s.%s (0x%.8x): %s' % (fname, name, eva, e)) # Save off the ordinals... vw.setFileMeta(fname, 'ordinals', exports) fwds = pe.getForwarders() for rva, name, forwardname in fwds: vw.makeName(rva + baseaddr, "forwarder_%s.%s" % (fname, name)) vw.makeString(rva + baseaddr) vw.setFileMeta(fname, 'forwarders', fwds) # Check For SafeSEH list... if pe.IMAGE_LOAD_CONFIG != None: vw.setFileMeta(fname, "SafeSEH", True) va = pe.IMAGE_LOAD_CONFIG.SEHandlerTable if va != 0: vw.makeName(va, "%s.SEHandlerTable" % fname) count = pe.IMAGE_LOAD_CONFIG.SEHandlerCount # RP BUG FIX - sanity check the count if count * 4 < pe.filesize and vw.isValidPointer(va): # XXX - CHEAP HACK for some reason we have binaries still thorwing issues.. try: # Just cheat and use the workspace with memory maps in it already for h in vw.readMemoryFormat(va, "<%dP" % count): sehva = baseaddr + h vw.addEntryPoint(sehva) #vw.hintFunction(sehva, meta={'SafeSEH':True}) except: vw.vprint("SEHandlerTable parse error") # Last but not least, see if we have symbol support and use it if we do if vt_win32.dbghelp: s = vt_win32.Win32SymbolParser(-1, filename, baseaddr) # We don't want exports or whatever because we already have them s.symopts |= vt_win32.SYMOPT_EXACT_SYMBOLS s.parse() # Add names for any symbols which are missing them for symname, symva, size, flags in s.symbols: if not vw.isValidPointer(symva): continue try: if vw.getName(symva) is None: vw.makeName(symva, symname, filelocal=True) except Exception, e: vw.vprint("Symbol Load Error: %s" % e) # Also, lets set the locals/args name hints if we found any vw.setFileMeta(fname, 'PELocalHints', s._sym_locals)
def loadPeIntoWorkspace(vw, pe, filename=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch == None: raise Exception("Machine %.4x is not supported for PE!" % mach) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) defcall = defcalls.get(arch) if defcall: vw.setMeta("DefaultCall", defcall) # Set ourselvs up for extended windows binary analysis baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase + codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname != None: fvivname = dllname if fvivname == None: fvivname = "pe_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(fvivname.lower(), baseaddr, fhash) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it vs = pe.getVS_VERSIONINFO() if vs != None: vsver = vs.getVersionValue('FileVersion') if vsver != None and len(vsver): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS), ("Library", VASET_STRING))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len( pe.IMAGE_NT_HEADERS.FileHeader ) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len( pe.IMAGE_NT_HEADERS ) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign if secrem != 0: header += "\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != "PE": raise Exception("We only support PE exe's") if not vw.isLocation(baseaddr + magicaddr): padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = baseaddr + magicaddr + 4 ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources carvepes = vw.config.viv.parsers.pe.carvepes deaddirs = [ PE.IMAGE_DIRECTORY_ENTRY_EXPORT, PE.IMAGE_DIRECTORY_ENTRY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_RESOURCE, PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION, PE.IMAGE_DIRECTORY_ENTRY_SECURITY, PE.IMAGE_DIRECTORY_ENTRY_BASERELOC, PE.IMAGE_DIRECTORY_ENTRY_DEBUG, PE.IMAGE_DIRECTORY_ENTRY_COPYRIGHT, PE.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, PE.IMAGE_DIRECTORY_ENTRY_GLOBALPTR, PE.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, PE.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_IAT, PE.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ] deadvas = [ddir.VirtualAddress] for datadir in deaddirs: d = pe.getDataDirectory(datadir) if d.VirtualAddress: deadvas.append(d.VirtualAddress) for idx, sec in enumerate(pe.sections): mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = (sec.VirtualAddress == ddir.VirtualAddress) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if secrva >= codebase and secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva and entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx + 1 >= len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx + 1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) except Exception, e: print "Error Loading Section (%s size:%d rva:%.8x offset: %d): %s" % ( secname, secfsize, secrva, secoff, e)
def loadPeIntoWorkspace(vw, pe, filename=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch == None: raise Exception("Machine %.4x is not supported for PE!" % mach ) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) defcall = defcalls.get(arch) if defcall: vw.setMeta("DefaultCall", defcall) # Set ourselvs up for extended windows binary analysis baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase+codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname != None: fvivname = dllname if fvivname == None: fvivname = "pe_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(fvivname.lower(), baseaddr, fhash) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it vs = pe.getVS_VERSIONINFO() if vs != None: vsver = vs.getVersionValue('FileVersion') if vsver != None: vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS),("Library", VASET_STRING))) # Tell vivisect about ntdll functions that don't exit... vw.addNoReturnApi("ntdll.RtlExitUserThread") vw.addNoReturnApi("kernel32.ExitProcess") vw.addNoReturnApi("kernel32.ExitThread") vw.addNoReturnApi("kernel32.FatalExit") vw.addNoReturnApi("msvcrt._CxxThrowException") vw.addNoReturnApi("msvcrt.abort") vw.addNoReturnApi("ntoskrnl.KeBugCheckEx") # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len(pe.IMAGE_NT_HEADERS.FileHeader) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign if secrem != 0: header += "\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != "PE": raise Exception("We only support PE exe's") padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = padloc[L_VA] + padloc[L_SIZE] ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources for idx, sec in enumerate(pe.sections): mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = ( sec.VirtualAddress == ddir.VirtualAddress ) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... if subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if secrva >= codebase and secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva and entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx+1 > len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx+1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) except Exception, e: print "Error Loading Section (%s size:%d rva:%.8x offset: %d): %s" % (secname,secfsize,secrva,secoff,e)
def writeBeingDebugged(trace, val): peb = trace.parseExpression("peb") ps = vstruct.getStructure("win32.PEB") off = ps.vsGetOffset("BeingDebugged") trace.writeMemoryFormat(peb+off, "<B", val)
def readStructAtOffset(self, offset, structname): s = vstruct.getStructure(structname) bytes = self.readAtOffset(offset, len(s)) # print "%s: %s" % (structname, bytes.encode('hex')) s.vsParse(bytes) return s
def loadPeIntoWorkspace(vw, pe, filename=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch is None: raise Exception("Machine %.4x is not supported for PE!" % mach) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) defcall = defcalls.get(arch) if defcall: vw.setMeta("DefaultCall", defcall) # Set ourselvs up for extended windows binary analysis baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase + codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname != None: fvivname = dllname if fvivname == None: fvivname = "pe_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(fvivname.lower(), baseaddr, fhash) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it vs = pe.getVS_VERSIONINFO() if vs != None: vsver = vs.getVersionValue('FileVersion') if vsver != None and len(vsver): # add check to split seeing samples with spaces and nothing else.. parts = vsver.split() if len(parts): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS), ("Library", VASET_STRING))) vw.addVaSet('pe:ordinals', (('Address', VASET_ADDRESS), ('Ordinal', VASET_INTEGER))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len( pe.IMAGE_NT_HEADERS.FileHeader) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len( pe.IMAGE_NT_HEADERS) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign if secrem != 0: header += b"\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != b"PE": raise Exception("We only support PE exe's") if not vw.isLocation(baseaddr + magicaddr): padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = baseaddr + magicaddr + 4 ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources carvepes = vw.config.viv.parsers.pe.carvepes deaddirs = [PE.IMAGE_DIRECTORY_ENTRY_EXPORT, PE.IMAGE_DIRECTORY_ENTRY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_RESOURCE, PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION, PE.IMAGE_DIRECTORY_ENTRY_SECURITY, PE.IMAGE_DIRECTORY_ENTRY_BASERELOC, PE.IMAGE_DIRECTORY_ENTRY_DEBUG, PE.IMAGE_DIRECTORY_ENTRY_COPYRIGHT, PE.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, PE.IMAGE_DIRECTORY_ENTRY_GLOBALPTR, PE.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, PE.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_IAT, PE.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] deadvas = [ddir.VirtualAddress] for datadir in deaddirs: d = pe.getDataDirectory(datadir) if d.VirtualAddress: deadvas.append(d.VirtualAddress) for idx, sec in enumerate(pe.sections): mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = (sec.VirtualAddress == ddir.VirtualAddress) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if codebase <= secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx + 1 >= len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx + 1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += b"\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) # FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not (chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is # larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += b"\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) # FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not (chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) except Exception as e: print(("Error Loading Section (%s size:%d rva:%.8x offset: %d): %s" % (secname, secfsize, secrva, secoff, e))) raise e vw.addExport(entry, EXP_FUNCTION, '__entry', fname) vw.addEntryPoint(entry) # store the actual reloc section virtual address reloc_va = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_BASERELOC).VirtualAddress if reloc_va: reloc_va += baseaddr vw.setFileMeta(fname, "reloc_va", reloc_va) for rva, rtype in pe.getRelocations(): # map PE reloc to VIV reloc ( or dont... ) vtype = relmap.get(rtype) if vtype == None: continue vw.addRelocation(rva + baseaddr, vtype) for rva, lname, iname in pe.getImports(): if vw.probeMemory(rva + baseaddr, 4, e_mem.MM_READ): vw.makeImport(rva + baseaddr, lname, iname) # Tell vivisect about ntdll functions that don't exit... vw.addNoReturnApi("ntdll.RtlExitUserThread") vw.addNoReturnApi("kernel32.ExitProcess") vw.addNoReturnApi("kernel32.ExitThread") vw.addNoReturnApi("kernel32.FatalExit") vw.addNoReturnApiRegex("^msvcr.*\._CxxThrowException$") vw.addNoReturnApiRegex("^msvcr.*\.abort$") vw.addNoReturnApi("ntoskrnl.KeBugCheckEx") exports = pe.getExports() for rva, ord, name in exports: eva = rva + baseaddr try: vw.setVaSetRow('pe:ordinals', (eva, ord)) vw.addExport(eva, EXP_UNTYPED, name, fname) if vw.probeMemory(eva, 1, e_mem.MM_EXEC): vw.addEntryPoint(eva) except Exception as e: vw.vprint('addExport Failed: %s.%s (0x%.8x): %s' % (fname, name, eva, e)) # Save off the ordinals... vw.setFileMeta(fname, 'ordinals', exports) fwds = pe.getForwarders() for rva, name, forwardname in fwds: vw.makeName(rva + baseaddr, "forwarder_%s.%s" % (fname, name)) vw.makeString(rva + baseaddr) vw.setFileMeta(fname, 'forwarders', fwds) # Check For SafeSEH list... if pe.IMAGE_LOAD_CONFIG != None: vw.setFileMeta(fname, "SafeSEH", True) va = pe.IMAGE_LOAD_CONFIG.SEHandlerTable if va != 0: vw.makeName(va, "%s.SEHandlerTable" % fname) count = pe.IMAGE_LOAD_CONFIG.SEHandlerCount # RP BUG FIX - sanity check the count if count * 4 < pe.filesize and vw.isValidPointer(va): # XXX - CHEAP HACK for some reason we have binaries still thorwing issues.. try: # Just cheat and use the workspace with memory maps in it already for h in vw.readMemoryFormat(va, "<%dP" % count): sehva = baseaddr + h vw.addEntryPoint(sehva) # vw.hintFunction(sehva, meta={'SafeSEH':True}) except: vw.vprint("SEHandlerTable parse error") # Last but not least, see if we have symbol support and use it if we do if vt_win32.dbghelp: s = vt_win32.Win32SymbolParser(-1, filename, baseaddr) # We don't want exports or whatever because we already have them s.symopts |= vt_win32.SYMOPT_EXACT_SYMBOLS s.parse() # Add names for any symbols which are missing them for symname, symva, size, flags in s.symbols: if not vw.isValidPointer(symva): continue try: if vw.getName(symva) == None: vw.makeName(symva, symname, filelocal=True) except Exception as e: vw.vprint("Symbol Load Error: %s" % e) # Also, lets set the locals/args name hints if we found any vw.setFileMeta(fname, 'PELocalHints', s._sym_locals) # if it has an EXCEPTION directory parse if it has the pdata edir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION) if edir.VirtualAddress and arch == 'amd64': va = edir.VirtualAddress + baseaddr vamax = va + edir.Size while va < vamax: f = vw.makeStructure(va, 'pe.IMAGE_RUNTIME_FUNCTION_ENTRY') if not vw.isValidPointer(baseaddr + f.UnwindInfoAddress): break # FIXME UNWIND_INFO *requires* DWORD alignment, how is it enforced? fva = f.BeginAddress + baseaddr uiva = baseaddr + f.UnwindInfoAddress # Possible method 1... # uiva = baseaddr + (f.UnwindInfoAddress & 0xfffffffc ) # Possible method 2... #uirem = f.UnwindInfoAddress % 4 #if uirem: #uiva += ( 4 - uirem ) uinfo = vw.getStructure(uiva, 'pe.UNWIND_INFO') ver = uinfo.VerFlags & 0x7 if ver != 1: vw.vprint('Unwind Info Version: %d (bailing on .pdata)' % ver) break flags = uinfo.VerFlags >> 3 # Check if it's a function *block* rather than a function *entry* if not (flags & PE.UNW_FLAG_CHAININFO): vw.addEntryPoint(fva) va += len(f) # auto-mark embedded PEs as "dead data" to prevent code flow... if carvepes: pe.fd.seek(0) fbytes = pe.fd.read() for offset, i in pe_carve.carve(fbytes, 1): # Found a sub-pe! subpe = pe_carve.CarvedPE(fbytes, offset, chr(i)) pebytes = subpe.readAtOffset(0, subpe.getFileSize()) rva = pe.offsetToRva(offset) vw.markDeadData(rva, rva + len(pebytes)) return fname
def writeBeingDebugged(trace, val): peb = trace.parseExpression("peb") ps = vstruct.getStructure("win32.PEB") off = ps.vsGetOffset("BeingDebugged") trace.writeMemoryFormat(peb + off, "<B", val)
def readStructAtRva(self, rva, structname): s = vstruct.getStructure(structname) bytes = self.readAtRva(rva, len(s)) # print "%s: %s" % (structname, bytes.encode('hex')) s.vsParse(bytes) return s
def readStructAtRva(self, rva, structname): s = vstruct.getStructure(structname) bytes = self.readAtRva(rva, len(s)) #print "%s: %s" % (structname, bytes.encode('hex')) s.vsParse(bytes) return s
def parseImportTable(self, x, irva, is_imports=True): ''' Parse a standard or delayed import table, adding to imports_list. Start with x and irva set to the first entry in the table. ''' imports_list = [] isize = len(x) while True: if is_imports: entry_name = x.Name else: entry_name = x.rvaDLLName if not self.checkRva(entry_name): break # RP BUG FIX - we can't assume that we have 256 bytes to read libname = self.readStringAtRva(entry_name, maxsize=256).decode('utf-8') idx = 0 if is_imports: imp_by_name = x.OriginalFirstThunk if imp_by_name == 0: imp_by_name = x.FirstThunk save_name = x.FirstThunk else: imp_by_name = x.rvaINT if imp_by_name == 0: imp_by_name = x.rvaIAT save_name = x.rvaIAT if not self.checkRva(imp_by_name): break while True: arrayoff = self.psize * idx if self.filesize is not None and arrayoff > self.filesize: return [] # we probably put garbage in the list ibn_rva = self.readPointerAtRva(imp_by_name + arrayoff) if ibn_rva == 0: break if ibn_rva & self.high_bit_mask: funcname = ordlookup.ordLookup(libname, ibn_rva & 0x7fffffff) elif not self.checkRva(ibn_rva): break else: # RP BUG FIX - we can't use this API on this call because we can have binaries that put their import table # right at the end of the file, statically saying the imported function name is 128 will cause use to potentially # over run our read and traceback... diff = self.getMaxRva() - ibn_rva - 2 ibn = vstruct.getStructure("pe.IMAGE_IMPORT_BY_NAME") ibn.vsGetField('Name').vsSetLength(min(diff, 128)) bytes = self.readAtRva(ibn_rva, len(ibn), shortok=True) if not bytes: break try: ibn.vsParse(bytes) except: idx += 1 continue funcname = ibn.Name imports_list.append((save_name + arrayoff, libname, funcname)) idx += 1 irva += isize # RP BUG FIX - if the import table is at the end of the file we can't count on the ending to be null if not self.checkRva(irva, size=isize): break x.vsParse(self.readAtRva(irva, isize)) return imports_list
def readStructAtOffset(self, offset, structname): s = vstruct.getStructure(structname) bytes = self.readAtOffset(offset, len(s)) #print "%s: %s" % (structname, bytes.encode('hex')) s.vsParse(bytes) return s
def writeBeingDebugged(self, trace, val): peb = trace.parseExpression('peb') ps = vstruct.getStructure('win32.PEB') off = ps.vsGetOffset('BeingDebugged') trace.writeMemoryFormat(peb + off, '<B', val)
def writeBeingDebugged(self, trace, val): peb = trace.parseExpression('peb') ps = vstruct.getStructure('win32.PEB') off = ps.vsGetOffset('BeingDebugged') trace.writeMemoryFormat(peb+off, '<B', val)
def parseImports(self): self.imports = [] idir = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT) # RP BUG FIX - invalid IAT entry will point of range of file irva = idir.VirtualAddress x = self.readStructAtRva(irva, 'pe.IMAGE_IMPORT_DIRECTORY', check=True) if x == None: return isize = len(x) while self.checkRva(x.Name): # RP BUG FIX - we can't assume that we have 256 bytes to read libname = self.readStringAtRva(x.Name, maxsize=256) idx = 0 imp_by_name = x.OriginalFirstThunk if imp_by_name == 0: imp_by_name = x.FirstThunk if not self.checkRva(imp_by_name): break while True: arrayoff = self.psize * idx if self.filesize != None and arrayoff > self.filesize: self.imports = [] # we probably put grabage in here.. return ibn_rva = self.readPointerAtRva(imp_by_name+arrayoff) if ibn_rva == 0: break if ibn_rva & self.high_bit_mask: funcname = ordlookup.ordLookup(libname, ibn_rva & 0x7fffffff) else: # RP BUG FIX - we can't use this API on this call because we can have binaries that put their import table # right at the end of the file, statically saying the imported function name is 128 will cause use to potentially # over run our read and traceback... diff = self.getMaxRva() - ibn_rva - 2 ibn = vstruct.getStructure("pe.IMAGE_IMPORT_BY_NAME") ibn.vsGetField('Name').vsSetLength( min(diff, 128) ) bytes = self.readAtRva(ibn_rva, len(ibn), shortok=True) if not bytes: break try: ibn.vsParse(bytes) except: idx+=1 continue funcname = ibn.Name self.imports.append((x.FirstThunk+arrayoff,libname,funcname)) idx += 1 irva += isize # RP BUG FIX - if the import table is at the end of the file we can't count on the ending to be null if not self.checkRva(irva, size=isize): break x.vsParse(self.readAtRva(irva, isize))
esp = tr.getRegisterByName('esp') reta = unpack('I',tr.readMemory(esp,4))[0] print '[*] OpenProcess called from ' + hex(reta) for hit in tr.searchMemory("\x00\x00\x5b"): if hit >= reta-0x10000 and hit<= reta+0x10000: if tr.readMemory(hit+3,1) not in ['\x00','\x25']: print self.decodecf(tr,hit+2) def decodecf(self,tr,addr): key,size = unpack('II',tr.readMemory(addr-12,8)) mem = tr.readMemory(addr,size) print '[+] Found Config[0..%d] @ 0x%x with key: %X' % (size,addr,key) return ''.join([chr(ord(mem[i]) ^ (key % (i+1))) for i in range(0,size)]) t = vtrace.getTrace() t.execute(sys.argv[1]) peb = t.parseExpression('peb') off = vstruct.getStructure('win32.PEB').vsGetOffset('BeingDebugged') t.writeMemory(peb+off,"\x00") notif = Notifier() t.registerNotifier(vtrace.NOTIFY_ALL,notif) bp = WorkDbg(t.parseExpression('kernel32.OpenProcess')) t.addBreakpoint(bp) #print t.getBreakpoints() t.run()
def loadPeIntoWorkspace(vw, pe, filename=None, baseaddr=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch is None: raise Exception("Machine %.4x is not supported for PE!" % mach) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) vw.setMeta('DefaultCall', defcalls.get(arch, 'unknown')) # Set ourselves up for extended windows binary analysis if baseaddr is None: baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase + codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname is not None: fvivname = dllname if fvivname is None: fvivname = "pe_%.8x" % baseaddr # grab the file bytes for hashing pe.fd.seek(0) bytez = pe.fd.read() fhash = v_parsers.md5Bytes(bytez) sha256 = v_parsers.sha256Bytes(bytez) # create the file and store md5 and sha256 hashes fname = vw.addFile(fvivname.lower(), baseaddr, fhash) vw.setFileMeta(fname, 'sha256', sha256) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it try: vs = pe.getVS_VERSIONINFO() except Exception as e: vs = None vw.vprint('Failed to load version info resource due to %s' % (repr(e), )) if vs is not None: vsver = vs.getVersionValue('FileVersion') if vsver is not None and len(vsver): # add check to split seeing samples with spaces and nothing else.. parts = vsver.split() if len(parts): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS), ("Library", VASET_STRING))) vw.addVaSet('pe:ordinals', (('Address', VASET_ADDRESS), ('Ordinal', VASET_INTEGER))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len( pe.IMAGE_NT_HEADERS.FileHeader ) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len( pe.IMAGE_NT_HEADERS ) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign if secrem != 0: header += "\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != "PE": raise Exception("We only support PE exe's") if not vw.isLocation(baseaddr + magicaddr): padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = baseaddr + magicaddr + 4 ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") ohstruct = vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") nxcompat = ohstruct.DllCharacteristics & PE.IMAGE_DLLCHARACTERISTICS_NX_COMPAT # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources carvepes = vw.config.viv.parsers.pe.carvepes deaddirs = [ PE.IMAGE_DIRECTORY_ENTRY_EXPORT, PE.IMAGE_DIRECTORY_ENTRY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_RESOURCE, PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION, PE.IMAGE_DIRECTORY_ENTRY_SECURITY, PE.IMAGE_DIRECTORY_ENTRY_BASERELOC, PE.IMAGE_DIRECTORY_ENTRY_DEBUG, PE.IMAGE_DIRECTORY_ENTRY_COPYRIGHT, PE.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, PE.IMAGE_DIRECTORY_ENTRY_GLOBALPTR, PE.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, PE.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_IAT, PE.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ] deadvas = [ddir.VirtualAddress] for datadir in deaddirs: d = pe.getDataDirectory(datadir) if d.VirtualAddress: deadvas.append(d.VirtualAddress) for idx, sec in enumerate(pe.sections): mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = (sec.VirtualAddress == ddir.VirtualAddress) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... # However, there is the DLLCHARACTERISTICS NXCOMPAT flag to take into account, # which works with the OS to prevent certain pages of memory from achieving # execution unless they're marked with the execute bit # so we can't just blindly mark these as executable quite yet. if not nxcompat: if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if secrva >= codebase and secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva and entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if not nxcompat: if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx + 1 >= len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx + 1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase + len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not ( chars & PE.IMAGE_SCN_MEM_EXECUTE) and not ( chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase + len(secbytes)) except Exception as e: logger.warning( "Error Loading Section (%s size:%d rva:%.8x offset: %d): %s", secname, secfsize, secrva, secoff, e) vw.addExport(entry, EXP_FUNCTION, '__entry', fname) vw.addEntryPoint(entry) # store the actual reloc section virtual address reloc_va = pe.getDataDirectory( PE.IMAGE_DIRECTORY_ENTRY_BASERELOC).VirtualAddress if reloc_va: reloc_va += baseaddr vw.setFileMeta(fname, "reloc_va", reloc_va) for rva, rtype in pe.getRelocations(): # map PE reloc to VIV reloc ( or dont... ) vtype = relmap.get(rtype) if vtype is None: logger.info('Skipping PE Relocation type: %d at %d (no handler)', rtype, rva) continue mapoffset = vw.readMemoryPtr(rva + baseaddr) - baseaddr vw.addRelocation(rva + baseaddr, vtype, mapoffset) for rva, lname, iname in pe.getImports(): if vw.probeMemory(rva + baseaddr, 4, e_mem.MM_READ): vw.makeImport(rva + baseaddr, lname, iname) # Tell vivisect about ntdll functions that don't exit... vw.addNoReturnApi("ntdll.RtlExitUserThread") vw.addNoReturnApi("kernel32.ExitProcess") vw.addNoReturnApi("kernel32.ExitThread") vw.addNoReturnApi("kernel32.FatalExit") vw.addNoReturnApiRegex("^msvcr.*\._CxxThrowException$") vw.addNoReturnApiRegex("^msvcr.*\.abort$") vw.addNoReturnApiRegex("^msvcr.*\.exit$") vw.addNoReturnApiRegex("^msvcr.*\._exit$") vw.addNoReturnApiRegex("^msvcr.*\.quick_exit$") # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/invalid-parameter-functions?view=vs-2019 # TODO: Again, there's a couple in there that have conditional termination that we should check for #vw.addNoReturnApiRegex("vcruntime140.__std_terminate") vw.addNoReturnApiRegex( "^api_ms_win_crt_runtime_.*\._invalid_parameter_noinfo_noreturn$") vw.addNoReturnApiRegex("^api_ms_win_crt_runtime_.*\.exit$") vw.addNoReturnApiRegex("^api_ms_win_crt_runtime_.*\._exit$") # TODO: we should add abort and terminate on the conditions that there are no signal handlers # registered # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/cexit-c-exit?view=vs-2019 # vw.addNoReturnApiRegex("^msvcr.*\._cexit$") # vw.addNoReturnApiRegex("^msvcr.*\._c_exit$") vw.addNoReturnApi("ntoskrnl.KeBugCheckEx") exports = pe.getExports() for rva, ord, name in exports: eva = rva + baseaddr # Functions exported by ordinal only have no name if not name: name = "Ordinal_" + str(ord) try: vw.setVaSetRow('pe:ordinals', (eva, ord)) vw.addExport(eva, EXP_UNTYPED, name, fname) if vw.probeMemory(eva, 1, e_mem.MM_EXEC): vw.addEntryPoint(eva) except Exception as e: vw.vprint('addExport Failed: %s.%s (0x%.8x): %s' % (fname, name, eva, e)) # Save off the ordinals... vw.setFileMeta(fname, 'ordinals', exports) fwds = pe.getForwarders() for rva, name, forwardname in fwds: vw.makeName(rva + baseaddr, "forwarder_%s.%s" % (fname, name)) vw.makeString(rva + baseaddr) vw.setFileMeta(fname, 'forwarders', fwds) # Check For SafeSEH list... if pe.IMAGE_LOAD_CONFIG is not None: vw.setFileMeta(fname, "SafeSEH", True) va = pe.IMAGE_LOAD_CONFIG.SEHandlerTable if va != 0: vw.makeName(va, "%s.SEHandlerTable" % fname) count = pe.IMAGE_LOAD_CONFIG.SEHandlerCount # RP BUG FIX - sanity check the count if count * 4 < pe.filesize and vw.isValidPointer(va): # XXX - CHEAP HACK for some reason we have binaries still thorwing issues.. try: # Just cheat and use the workspace with memory maps in it already for h in vw.readMemoryFormat(va, "<%dP" % count): sehva = baseaddr + h vw.addEntryPoint(sehva) #vw.hintFunction(sehva, meta={'SafeSEH':True}) except: vw.vprint("SEHandlerTable parse error") # Last but not least, see if we have symbol support and use it if we do if vt_win32.dbghelp: s = vt_win32.Win32SymbolParser(-1, filename, baseaddr) # We don't want exports or whatever because we already have them s.symopts |= vt_win32.SYMOPT_EXACT_SYMBOLS s.parse() # Add names for any symbols which are missing them for symname, symva, size, flags in s.symbols: if not vw.isValidPointer(symva): continue try: if vw.getName(symva) is None: vw.makeName(symva, symname, filelocal=True) except Exception as e: vw.vprint("Symbol Load Error: %s" % e) # Also, lets set the locals/args name hints if we found any vw.setFileMeta(fname, 'PELocalHints', s._sym_locals) # if it has an EXCEPTION directory parse if it has the pdata edir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION) if edir.VirtualAddress and arch == 'amd64': va = edir.VirtualAddress + baseaddr vamax = va + edir.Size while va < vamax: f = vw.makeStructure(va, 'pe.IMAGE_RUNTIME_FUNCTION_ENTRY') if not vw.isValidPointer(baseaddr + f.UnwindInfoAddress): break # FIXME UNWIND_INFO *requires* DWORD alignment, how is it enforced? fva = f.BeginAddress + baseaddr uiva = baseaddr + f.UnwindInfoAddress # Possible method 1... #uiva = baseaddr + (f.UnwindInfoAddress & 0xfffffffc ) # Possible method 2... #uirem = f.UnwindInfoAddress % 4 #if uirem: #uiva += ( 4 - uirem ) uinfo = vw.getStructure(uiva, 'pe.UNWIND_INFO') ver = uinfo.VerFlags & 0x7 if ver != 1: vw.vprint('Unwind Info Version: %d (bailing on .pdata)' % ver) break flags = uinfo.VerFlags >> 3 # Check if it's a function *block* rather than a function *entry* if not (flags & PE.UNW_FLAG_CHAININFO): vw.addEntryPoint(fva) va += len(f) # auto-mark embedded PEs as "dead data" to prevent code flow... if carvepes: pe.fd.seek(0) fbytes = pe.fd.read() for offset, i in pe_carve.carve(fbytes, 1): # Found a sub-pe! subpe = pe_carve.CarvedPE(fbytes, offset, chr(i)) pebytes = subpe.readAtOffset(0, subpe.getFileSize()) rva = pe.offsetToRva(offset) + baseaddr vw.markDeadData(rva, rva + len(pebytes)) return fname
def loadPeIntoWorkspace(vw, pe, filename=None): mach = pe.IMAGE_NT_HEADERS.FileHeader.Machine arch = arch_names.get(mach) if arch == None: raise Exception("Machine %.4x is not supported for PE!" % mach ) vw.setMeta('Architecture', arch) vw.setMeta('Format', 'pe') platform = 'windows' # Drivers are platform "winkern" so impapi etc works subsys = pe.IMAGE_NT_HEADERS.OptionalHeader.Subsystem if subsys == PE.IMAGE_SUBSYSTEM_NATIVE: platform = 'winkern' vw.setMeta('Platform', platform) defcall = defcalls.get(arch) if defcall: vw.setMeta("DefaultCall", defcall) # Set ourselvs up for extended windows binary analysis baseaddr = pe.IMAGE_NT_HEADERS.OptionalHeader.ImageBase entry = pe.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint + baseaddr entryrva = entry - baseaddr codebase = pe.IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode codesize = pe.IMAGE_NT_HEADERS.OptionalHeader.SizeOfCode codervamax = codebase+codesize fvivname = filename # This will help linkers with files that are re-named dllname = pe.getDllName() if dllname != None: fvivname = dllname if fvivname == None: fvivname = "pe_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = md5File(filename) fname = vw.addFile(fvivname.lower(), baseaddr, fhash) symhash = e_symcache.symCacheHashFromPe(pe) vw.setFileMeta(fname, 'SymbolCacheHash', symhash) # Add file version info if VS_VERSIONINFO has it vs = pe.getVS_VERSIONINFO() if vs != None: vsver = vs.getVersionValue('FileVersion') if vsver != None and len(vsver): # add check to split seeing samples with spaces and nothing else.. parts = vsver.split() if len(parts): vsver = vsver.split()[0] vw.setFileMeta(fname, 'Version', vsver) # Setup some va sets used by windows analysis modules vw.addVaSet("Library Loads", (("Address", VASET_ADDRESS),("Library", VASET_STRING))) vw.addVaSet('pe:ordinals', (('Address', VASET_ADDRESS),('Ordinal',VASET_INTEGER))) # SizeOfHeaders spoofable... curr_offset = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER")) sec_offset = pe.IMAGE_DOS_HEADER.e_lfanew + 4 + len(pe.IMAGE_NT_HEADERS.FileHeader) + pe.IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader if sec_offset != curr_offset: header_size = sec_offset + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize else: header_size = pe.IMAGE_DOS_HEADER.e_lfanew + len(pe.IMAGE_NT_HEADERS) + pe.IMAGE_NT_HEADERS.FileHeader.NumberOfSections * secsize # Add the first page mapped in from the PE header. header = pe.readAtOffset(0, header_size) secalign = pe.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment subsys_majver = pe.IMAGE_NT_HEADERS.OptionalHeader.MajorSubsystemVersion subsys_minver = pe.IMAGE_NT_HEADERS.OptionalHeader.MinorSubsystemVersion secrem = len(header) % secalign if secrem != 0: header += "\x00" * (secalign - secrem) vw.addMemoryMap(baseaddr, e_mem.MM_READ, fname, header) vw.addSegment(baseaddr, len(header), "PE_Header", fname) hstruct = vw.makeStructure(baseaddr, "pe.IMAGE_DOS_HEADER") magicaddr = hstruct.e_lfanew if vw.readMemory(baseaddr + magicaddr, 2) != "PE": raise Exception("We only support PE exe's") if not vw.isLocation( baseaddr + magicaddr ): padloc = vw.makePad(baseaddr + magicaddr, 4) ifhdr_va = baseaddr + magicaddr + 4 ifstruct = vw.makeStructure(ifhdr_va, "pe.IMAGE_FILE_HEADER") vw.makeStructure(ifhdr_va + len(ifstruct), "pe.IMAGE_OPTIONAL_HEADER") # get resource data directory ddir = pe.getDataDirectory(PE.IMAGE_DIRECTORY_ENTRY_RESOURCE) loadrsrc = vw.config.viv.parsers.pe.loadresources carvepes = vw.config.viv.parsers.pe.carvepes deaddirs = [PE.IMAGE_DIRECTORY_ENTRY_EXPORT, PE.IMAGE_DIRECTORY_ENTRY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_RESOURCE, PE.IMAGE_DIRECTORY_ENTRY_EXCEPTION, PE.IMAGE_DIRECTORY_ENTRY_SECURITY, PE.IMAGE_DIRECTORY_ENTRY_BASERELOC, PE.IMAGE_DIRECTORY_ENTRY_DEBUG, PE.IMAGE_DIRECTORY_ENTRY_COPYRIGHT, PE.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, PE.IMAGE_DIRECTORY_ENTRY_GLOBALPTR, PE.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, PE.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_IAT, PE.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, PE.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] deadvas = [ddir.VirtualAddress] for datadir in deaddirs: d = pe.getDataDirectory(datadir) if d.VirtualAddress: deadvas.append(d.VirtualAddress) for idx, sec in enumerate(pe.sections): mapflags = 0 chars = sec.Characteristics if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ isrsrc = ( sec.VirtualAddress == ddir.VirtualAddress ) if isrsrc and not loadrsrc: continue # If it's for an older system, just about anything # is executable... if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and not isrsrc: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_MEM_READ: mapflags |= e_mem.MM_READ if chars & PE.IMAGE_SCN_MEM_WRITE: mapflags |= e_mem.MM_WRITE if chars & PE.IMAGE_SCN_MEM_EXECUTE: mapflags |= e_mem.MM_EXEC if chars & PE.IMAGE_SCN_CNT_CODE: mapflags |= e_mem.MM_EXEC secrva = sec.VirtualAddress secvsize = sec.VirtualSize secfsize = sec.SizeOfRawData secbase = secrva + baseaddr secname = sec.Name.strip("\x00") secrvamax = secrva + secvsize # If the section is part of BaseOfCode->SizeOfCode # force execute perms... if secrva >= codebase and secrva < codervamax: mapflags |= e_mem.MM_EXEC # If the entry point is in this section, force execute # permissions. if secrva <= entryrva and entryrva < secrvamax: mapflags |= e_mem.MM_EXEC if not vw.config.viv.parsers.pe.nx and subsys_majver < 6 and mapflags & e_mem.MM_READ: mapflags |= e_mem.MM_EXEC if sec.VirtualSize == 0 or sec.SizeOfRawData == 0: if idx+1 >= len(pe.sections): continue # fill the gap with null bytes.. nsec = pe.sections[idx+1] nbase = nsec.VirtualAddress + baseaddr plen = nbase - secbase readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase+len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not (chars & PE.IMAGE_SCN_MEM_EXECUTE) and not (chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase+len(secbytes)) continue # if SizeOfRawData is greater than VirtualSize we'll end up using VS in our read.. if sec.SizeOfRawData < sec.VirtualSize: if sec.SizeOfRawData > pe.filesize: continue plen = sec.VirtualSize - sec.SizeOfRawData try: # According to http://code.google.com/p/corkami/wiki/PE#section_table if SizeOfRawData is larger than VirtualSize, VS is used.. readsize = sec.SizeOfRawData if sec.SizeOfRawData < sec.VirtualSize else sec.VirtualSize secoff = pe.rvaToOffset(secrva) secbytes = pe.readAtOffset(secoff, readsize) secbytes += "\x00" * plen vw.addMemoryMap(secbase, mapflags, fname, secbytes) vw.addSegment(secbase, len(secbytes), secname, fname) # Mark dead data on resource and import data directories if sec.VirtualAddress in deadvas: vw.markDeadData(secbase, secbase+len(secbytes)) #FIXME create a mask for this if not (chars & PE.IMAGE_SCN_CNT_CODE) and not (chars & PE.IMAGE_SCN_MEM_EXECUTE) and not (chars & PE.IMAGE_SCN_MEM_WRITE): vw.markDeadData(secbase, secbase+len(secbytes)) except Exception, e: logging.getLogger("parsers.pe.loadPE").warning("Error Loading Section (%s size:%d rva:%.8x offset: %d): %s", secname, secfsize, secrva, secoff, e)