def parseFile(vw, filename, baseaddr=None): arch = vw.config.viv.parsers.blob.arch bigend = vw.config.viv.parsers.blob.bigend if baseaddr is None: baseaddr = vw.config.viv.parsers.blob.baseaddr try: envi.getArchModule(arch) except Exception: raise v_exc.BlobArchException() vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'unknown') vw.setMeta('Format', 'blob') vw.setMeta('bigend', bigend) vw.setMeta('DefaultCall', archcalls.get(arch, 'unknown')) with open(filename, 'rb') as f: bytez = f.read() fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) vw.setFileMeta(fname, 'sha256', v_parsers.sha256Bytes(bytez)) vw.addMemoryMap(baseaddr, 7, filename, bytez) vw.addSegment(baseaddr, len(bytez), '%.8x' % baseaddr, 'blob')
def parseFile(vw, filename, baseaddr=None): arch = vw.config.viv.parsers.ihex.arch if not arch: raise Exception('IHex loader *requires* arch option (-O viv.parsers.ihex.arch=\\"<archname>\\")') envi.getArchModule(arch) vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'Unknown') vw.setMeta('Format', 'ihex') vw.setMeta('DefaultCall', archcalls.get(arch, 'unknown')) # might we make use of baseaddr, even though it's an IHEX? for now, no. fname = vw.addFile(filename, 0, v_parsers.md5File(filename)) vw.setFileMeta(filename, 'sha256', v_parsers.sha256File(filename)) ihex = v_ihex.IHexFile() with open(filename, 'rb') as f: ihex.vsParse(f.read()) for addr, perms, notused, bytes in ihex.getMemoryMaps(): vw.addMemoryMap(addr, perms, fname, bytes) vw.addSegment(addr, len(bytes), '%.8x' % addr, fname)
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, e: vs = None vw.vprint('Failed to load version info resource due to %s' % (repr(e),))
def parseFile(vw, filename): arch = vw.config.viv.parsers.ihex.arch if not arch: raise Exception('IHex loader *requires* arch option (-O viv.parsers.ihex.arch=\\"<archname>\\")') envi.getArchModule(arch) vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'Unknown') vw.setMeta('Format', 'ihex') fname = vw.addFile(filename, 0, v_parsers.md5File(filename)) ihex = v_ihex.IHexFile() ihex.vsParse(open(filename, 'rb').read()) for addr, perms, notused, bytes in ihex.getMemoryMaps(): vw.addMemoryMap(addr, perms, fname, bytes) vw.addSegment(addr, len(bytes), '%.8x' % addr, fname)
def parseFile(vw, filename): arch = vw.config.viv.parsers.ihex.arch if not arch: raise Exception( 'IHex loader *requires* arch option (-O viv.parsers.ihex.arch=\\"<archname>\\")' ) envi.getArchModule(arch) vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'Unknown') vw.setMeta('Format', 'ihex') fname = vw.addFile(filename, 0, v_parsers.md5File(filename)) ihex = v_ihex.IHexFile() ihex.vsParse(open(filename, 'rb').read()) for addr, perms, notused, bytes in ihex.getMemoryMaps(): vw.addMemoryMap(addr, perms, fname, bytes) vw.addSegment(addr, len(bytes), '%.8x' % addr, fname)
def parseFile(vw, filename): arch = vw.config.viv.parsers.blob.arch bigend = vw.config.viv.parsers.blob.bigend baseaddr = vw.config.viv.parsers.blob.baseaddr try: envi.getArchModule(arch) except Exception as e: raise Exception('Blob loader *requires* arch option (-O viv.parsers.blob.arch="<archname>")') vw.setMeta('Architecture', arch) vw.setMeta('Platform','unknown') vw.setMeta('Format','blob') vw.setMeta('bigend', bigend) fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) bytez = open(filename, "rb").read() vw.addMemoryMap(baseaddr, 7, filename, bytez) vw.addSegment( baseaddr, len(bytez), '%.8x' % baseaddr, 'blob' )
def parseFile(vw, filename, baseaddr=None): arch = vw.config.viv.parsers.ihex.arch if not arch: raise Exception('IHex loader *requires* arch option (-O viv.parsers.ihex.arch=\\"<archname>\\")') envi.getArchModule(arch) vw.setMeta('Architecture', arch) vw.setMeta('Platform','Unknown') vw.setMeta('Format','ihex') vw.setMeta('DefaultCall', archcalls.get(arch,'unknown')) # might we make use of baseaddr, even though it's an IHEX? for now, no. fname = vw.addFile(filename, 0, v_parsers.md5File(filename)) ihex = v_ihex.IHexFile() ihex.vsParse( file(filename, 'rb').read() ) for addr, perms, notused, bytes in ihex.getMemoryMaps(): vw.addMemoryMap( addr, perms, fname, bytes ) vw.addSegment( addr, len(bytes), '%.8x' % addr, fname )
def parseFile(vw, filename): arch = vw.config.viv.parsers.blob.arch bigend = vw.config.viv.parsers.blob.bigend baseaddr = vw.config.viv.parsers.blob.baseaddr try: envi.getArchModule(arch) except Exception as e: raise Exception( 'Blob loader *requires* arch option (-O viv.parsers.blob.arch="<archname>")' ) vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'unknown') vw.setMeta('Format', 'blob') vw.setMeta('bigend', bigend) fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) bytez = open(filename, "rb").read() vw.addMemoryMap(baseaddr, 7, filename, bytez) vw.addSegment(baseaddr, len(bytez), '%.8x' % baseaddr, 'blob')
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 parseFile(vw, filename): arch = vw.config.viv.parsers.blob.arch baseaddr = vw.config.viv.parsers.blob.baseaddr try: envi.getArchModule(arch) except Exception, e: raise Exception( 'Blob loader *requires* arch option (-O viv.parsers.blob.arch="<archname>")' ) vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'unknown') vw.setMeta('Format', 'blob') fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) bytez = file(filename, "rb").read() vw.addMemoryMap(baseaddr, 7, filename, bytez) vw.addSegment(baseaddr, len(bytez), '%.8x' % baseaddr, 'blob') def parseMemory(vw, memobj, baseaddr): va, size, perms, fname = memobj.getMemoryMap(baseaddr) if not fname: fname = 'map_%.8x' % baseaddr bytes = memobj.readMemory(va, size) fname = vw.addFile(fname, baseaddr, v_parsers.md5Bytes(bytes)) vw.addMemoryMap(va, perms, fname, bytes)
def parseFile(vw, filename): arch = vw.config.viv.parsers.blob.arch baseaddr = vw.config.viv.parsers.blob.baseaddr try: envi.getArchModule(arch) except Exception, e: raise Exception('Blob loader *requires* arch option (-O viv.parsers.blob.arch="<archname>")') vw.setMeta('Architecture', arch) vw.setMeta('Platform','unknown') vw.setMeta('Format','blob') fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) bytez = file(filename, "rb").read() vw.addMemoryMap(baseaddr, 7, filename, bytez) vw.addSegment( baseaddr, len(bytez), '%.8x' % baseaddr, 'blob' ) def parseMemory(vw, memobj, baseaddr): va,size,perms,fname = memobj.getMemoryMap(baseaddr) if not fname: fname = 'map_%.8x' % baseaddr bytes = memobj.readMemory(va, size) fname = vw.addFile(fname, baseaddr, v_parsers.md5Bytes(bytes)) vw.addMemoryMap(va, perms, fname, bytes)
def loadElfIntoWorkspace(vw, elf, filename=None): arch = arch_names.get(elf.e_machine) if arch == None: raise Exception("Unsupported Architecture: %d\n", elf.e_machine) platform = elf.getPlatform() # setup needed platform/format vw.setMeta('Architecture', arch) vw.setMeta('Platform', platform) vw.setMeta('Format', 'elf') vw.setMeta('DefaultCall', archcalls.get(arch,'unknown')) vw.addNoReturnApi("*.exit") # Base addr is earliest section address rounded to pagesize # NOTE: This is only for prelink'd so's and exe's. Make something for old style so. addbase = False if not elf.isPreLinked() and elf.isSharedObject(): addbase = True baseaddr = elf.getBaseAddress() #FIXME make filename come from dynamic's if present for shared object if filename == None: filename = "elf_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(filename.lower(), baseaddr, fhash) strtabs = {} secnames = [] for sec in elf.getSections(): secnames.append(sec.getName()) pgms = elf.getPheaders() secs = elf.getSections() for pgm in pgms: if pgm.p_type == Elf.PT_LOAD: if vw.verbose: vw.vprint('Loading: %s' % (repr(pgm))) bytez = elf.readAtOffset(pgm.p_offset, pgm.p_filesz) bytez += "\x00" * (pgm.p_memsz - pgm.p_filesz) pva = pgm.p_vaddr if addbase: pva += baseaddr vw.addMemoryMap(pva, pgm.p_flags & 0x7, fname, bytez) #FIXME perms else: if vw.verbose: vw.vprint('Skipping: %s' % repr(pgm)) if len(pgms) == 0: # fall back to loading sections as best we can... if vw.verbose: vw.vprint('elf: no program headers found!') maps = [ [s.sh_offset,s.sh_size] for s in secs if s.sh_offset and s.sh_size ] maps.sort() merged = [] for i in xrange(len(maps)): if merged and maps[i][0] == (merged[-1][0] + merged[-1][1]): merged[-1][1] += maps[i][1] continue merged.append( maps[i] ) baseaddr = 0x05000000 for offset,size in merged: bytez = elf.readAtOffset(offset,size) vw.addMemoryMap(baseaddr + offset, 0x7, fname, bytez) for sec in secs: if sec.sh_offset and sec.sh_size: sec.sh_addr = baseaddr + sec.sh_offset # First add all section definitions so we have them for sec in secs: sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr vw.addSegment(sva, size, sname, fname) # Now trigger section specific analysis for sec in secs: #FIXME dup code here... sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr if sname == ".interp": vw.makeString(sva) elif sname == ".init": vw.makeName(sva, "init_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".fini": vw.makeName(sva, "fini_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".dynamic": # Imports makeDynamicTable(vw, sva, sva+size) # FIXME section names are optional, use dynamic info from .dynamic elif sname == ".dynstr": # String table for dynamics makeStringTable(vw, sva, sva+size) elif sname == ".dynsym": #print "LINK",sec.sh_link for s in makeSymbolTable(vw, sva, sva+size): pass #print "########################.dynsym",s # If the section is really a string table, do it if sec.sh_type == Elf.SHT_STRTAB: makeStringTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_SYMTAB: makeSymbolTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_REL: makeRelocTable(vw, sva, sva+size, addbase, baseaddr) if sec.sh_flags & Elf.SHF_STRINGS: print "FIXME HANDLE SHF STRINGS" # Let pyelf do all the stupid string parsing... for r in elf.getRelocs(): rtype = Elf.getRelocType(r.r_info) rlva = r.r_offset if addbase: rlva += baseaddr try: # If it has a name, it's an externally # resolved "import" entry, otherwise, just a regular reloc if arch in ('i386','amd64'): name = r.getName() if name: if rtype == Elf.R_386_JMP_SLOT: vw.makeImport(rlva, "*", name) # FIXME elf has conflicting names for 2 relocs? #elif rtype == Elf.R_386_GLOB_DAT: #vw.makeImport(rlva, "*", name) elif rtype == Elf.R_386_32: pass else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) if arch == 'arm': name = r.getName() if name: if rtype == Elf.R_ARM_JUMP_SLOT: vw.makeImport(rlva, "*", name) else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) except vivisect.InvalidLocation, e: print "NOTE",e
def loadElfIntoWorkspace(vw, elf, filename=None): arch = arch_names.get(elf.e_machine) if arch == None: raise Exception("Unsupported Architecture: %d\n", elf.e_machine) # setup needed platform/format vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'unknown') vw.setMeta('Format', 'elf') # FIXME try to determine *which* Elf system... if arch == 'i386': vw.setMeta("DefaultCall", "cdecl") elif arch == 'amd64': vw.setMeta("DefaultCall", "sysvamd64call") vw.addNoReturnApi("*.exit") # Base addr is earliest section address rounded to pagesize # NOTE: This is only for prelink'd so's and exe's. Make something for old style so. addbase = False if not elf.isPreLinked() and elf.isSharedObject(): addbase = True baseaddr = elf.getBaseAddress() #FIXME make filename come from dynamic's if present for shared object if filename == None: filename = "elf_%.8x" % baseaddr fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) strtabs = {} secnames = [] for sec in elf.getSections(): secnames.append(sec.getName()) for pgm in elf.getPheaders(): if pgm.p_type == Elf.PT_LOAD: if vw.verbose: vw.vprint('Loading: %s' % (repr(pgm))) bytes = elf.readAtOffset(pgm.p_offset, pgm.p_filesz) bytes += "\x00" * (pgm.p_memsz - pgm.p_filesz) pva = pgm.p_vaddr if addbase: pva += baseaddr vw.addMemoryMap(pva, pgm.p_flags & 0x7, fname, bytes) #FIXME perms else: if vw.verbose: vw.vprint('Skipping: %s' % repr(pgm)) # First add all section definitions so we have them for sec in elf.getSections(): sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr vw.addSegment(sva, size, sname, fname) # Now trigger section specific analysis for sec in elf.getSections(): #FIXME dup code here... sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr if sname == ".interp": vw.makeString(sva) elif sname == ".init": vw.makeName(sva, "init_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".fini": vw.makeName(sva, "fini_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".dynamic": # Imports makeDynamicTable(vw, sva, sva+size) # FIXME section names are optional, use dynamic info from .dynamic elif sname == ".dynstr": # String table for dynamics makeStringTable(vw, sva, sva+size) elif sname == ".dynsym": #print "LINK",sec.sh_link for s in makeSymbolTable(vw, sva, sva+size): pass #print "########################.dynsym",s # If the section is really a string table, do it if sec.sh_type == Elf.SHT_STRTAB: makeStringTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_SYMTAB: # FIXME 32 bit specific! #print "FOUND A SYMBOL TABLE!" makeSymbolTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_REL: makeRelocTable(vw, sva, sva+size, addbase, baseaddr) if sec.sh_flags & Elf.SHF_STRINGS: print "FIXME HANDLE SHF STRINGS" # Let pyelf do all the stupid string parsing... for r in elf.getRelocs(): rtype = Elf.getRelocType(r.r_info) rlva = r.r_offset if addbase: rlva += baseaddr try: # If it has a name, it's an externally # resolved "import" entry, otherwise, just a regular reloc if arch in ('i386','amd64'): name = r.getName() if name: if rtype == Elf.R_386_JMP_SLOT: vw.makeImport(rlva, "*", name) # FIXME elf has conflicting names for 2 relocs? #elif rtype == Elf.R_386_GLOB_DAT: #vw.makeImport(rlva, "*", name) elif rtype == Elf.R_386_32: pass else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) if arch == 'arm': name = r.getName() if name: if rtype == Elf.R_ARM_JUMP_SLOT: vw.makeImport(rlva, "*", name) else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) except vivisect.InvalidLocation, e: print "NOTE",e
def _loadMacho(vw, filebytes, filename=None, baseaddr=None): # We fake them to *much* higher than norm so pointer tests do better... if baseaddr is None: baseaddr = vw.config.viv.parsers.macho.baseaddr if filename is None: filename = 'macho_%.8x' % baseaddr # FIXME more than one! # Check for the FAT binary magic... if filebytes[:4].encode('hex') in ('cafebabe', 'bebafeca'): archhdr = None fatarch = vw.config.viv.parsers.macho.fatarch archlist = [] offset = 0 fat = vs_macho.fat_header() offset = fat.vsParse(filebytes, offset=offset) for i in xrange(fat.nfat_arch): ar = vs_macho.fat_arch() offset = ar.vsParse(filebytes, offset=offset) archname = vs_macho.mach_cpu_names.get(ar.cputype) if archname == fatarch: archhdr = ar break archlist.append((archname,ar)) if not archhdr: # If we don't have a specified arch, exception! vw.vprint('Mach-O Fat Binary Architectures:') for archname, ar in archlist: vw.vprint('0x%.8x 0x%.8x %s' % (ar.offset, ar.size, archname)) raise Exception('Mach-O Fat Binary: Please specify arch with -O viv.parsers.macho.fatarch="<archname>"') filebytes = filebytes[archhdr.offset:archhdr.offset+archhdr.size] # Instantiate the parser wrapper and parse bytes macho = vs_macho.mach_o() macho.vsParse(filebytes) arch = vs_macho.mach_cpu_names.get(macho.mach_header.cputype) if arch is None: raise Exception('Unknown MACH-O arch: %.8x' % macho.mach_header.cputype) # Setup arch/plat/fmt vw.setMeta('Architecture', arch) vw.setMeta("Platform", "Darwin") vw.setMeta("Format", "macho") vw.setMeta('DefaultCall', archcalls.get(arch,'unknown')) # Add the file entry hash = "unknown hash" if os.path.exists(filename): hash = viv_parsers.md5File(filename) fname = vw.addFile(filename, baseaddr, hash) # Add the memory maps and segments from the macho definition for segname, rva, perms, segbytes in macho.getSegments(): segbase = baseaddr + rva vw.addMemoryMap(segbase, perms, fname, segbytes) vw.addSegment(segbase, len(segbytes), segname, fname) # Add the library dependancies for libname in macho.getLibDeps(): # FIXME hack.... libname = libname.split('/')[-1] libname = libname.split('.')[0].lower() vw.addLibraryDependancy(libname) return fname
def _loadMacho(vw, filebytes, filename=None): # We fake them to *much* higher than norm so pointer tests do better... baseaddr = vw.config.viv.parsers.macho.baseaddr if filename == None: filename = 'macho_%.8x' % baseaddr # FIXME more than one! # Check for the FAT binary magic... if filebytes[:4].encode('hex') in ('cafebabe', 'bebafeca'): archhdr = None fatarch = vw.config.viv.parsers.macho.fatarch archlist = [] offset = 0 fat = vs_macho.fat_header() offset = fat.vsParse(filebytes, offset=offset) for i in xrange(fat.nfat_arch): ar = vs_macho.fat_arch() offset = ar.vsParse(filebytes, offset=offset) archname = vs_macho.mach_cpu_names.get(ar.cputype) if archname == fatarch: archhdr = ar break archlist.append((archname,ar)) if not archhdr: # If we don't have a specified arch, exception! vw.vprint('Mach-O Fat Binary Architectures:') for archname, ar in archlist: vw.vprint('0x%.8x 0x%.8x %s' % (ar.offset, ar.size, archname)) raise Exception('Mach-O Fat Binary: Please specify arch with -O viv.parsers.macho.fatarch="<archname>"') filebytes = filebytes[archhdr.offset:archhdr.offset+archhdr.size] # Instantiate the parser wrapper and parse bytes macho = vs_macho.mach_o() macho.vsParse(filebytes) arch = vs_macho.mach_cpu_names.get(macho.mach_header.cputype) if arch == None: raise Exception('Unknown MACH-O arch: %.8x' % macho.mach_header.cputype) # Setup arch/plat/fmt vw.setMeta('Architecture', arch) vw.setMeta("Platform", "Darwin") vw.setMeta("Format", "macho") vw.setMeta('DefaultCall', archcalls.get(arch,'unknown')) # Add the file entry hash = "unknown hash" if os.path.exists(filename): hash = viv_parsers.md5File(filename) fname = vw.addFile(filename, baseaddr, hash) # Add the memory maps and segments from the macho definition for segname, rva, perms, segbytes in macho.getSegments(): segbase = baseaddr + rva vw.addMemoryMap(segbase, perms, fname, segbytes) vw.addSegment(segbase, len(segbytes), segname, fname) # Add the library dependancies for libname in macho.getLibDeps(): # FIXME hack.... libname = libname.split('/')[-1] libname = libname.split('.')[0].lower() vw.addLibraryDependancy(libname) return fname
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) vw.setMeta('DefaultCall', defcalls.get(arch, 'unknown')) # 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 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 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.*\.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 != 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 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 loadElfIntoWorkspace(vw, elf, filename=None, baseaddr=None): # analysis of discovered functions and data locations should be stored until the end of loading data_ptrs = [] new_pointers = [] new_functions = [] arch = arch_names.get(elf.e_machine) if arch is None: raise Exception("Unsupported Architecture: %d\n", elf.e_machine) platform = elf.getPlatform() # setup needed platform/format vw.setMeta('Architecture', arch) vw.setMeta('Platform', platform) vw.setMeta('Format', 'elf') vw.setMeta('DefaultCall', archcalls.get(arch,'unknown')) vw.addNoReturnApi("*.abort") vw.addNoReturnApi("*.exit") vw.addNoReturnApi("*._exit") vw.addNoReturnApi("*.longjmp") vw.addNoReturnApi("*._setjmp") vw.addNoReturnApi("*.j__ZSt9terminatev") vw.addNoReturnApi("*.std::terminate(void)") vw.addNoReturnApi("*.__assert_fail") vw.addNoReturnApi("*.__stack_chk_fail") # for VivWorkspace, MSB==1, LSB==0... which is the same as True/False vw.setEndian(elf.getEndian()) # Base addr is earliest section address rounded to pagesize # NOTE: This is only for prelink'd so's and exe's. Make something for old style so. addbase = False if not elf.isPreLinked() and elf.isSharedObject(): addbase = True if baseaddr is None: baseaddr = elf.getBaseAddress() #FIXME make filename come from dynamic's if present for shared object if filename is None: filename = "elf_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(filename.lower(), baseaddr, fhash) strtabs = {} secnames = [] for sec in elf.getSections(): secnames.append(sec.getName()) pgms = elf.getPheaders() secs = elf.getSections() for pgm in pgms: if pgm.p_type == Elf.PT_LOAD: if pgm.p_memsz == 0: continue logger.info('Loading: %s', repr(pgm)) bytez = elf.readAtOffset(pgm.p_offset, pgm.p_filesz) bytez += "\x00" * (pgm.p_memsz - pgm.p_filesz) pva = pgm.p_vaddr if addbase: pva += baseaddr vw.addMemoryMap(pva, pgm.p_flags & 0x7, fname, bytez) # FIXME perms else: logger.info('Skipping: %s', repr(pgm)) if len(pgms) == 0: # fall back to loading sections as best we can... if vw.verbose: vw.vprint('elf: no program headers found!') maps = [ [s.sh_offset,s.sh_size] for s in secs if s.sh_offset and s.sh_size ] maps.sort() merged = [] for i in xrange(len(maps)): if merged and maps[i][0] == (merged[-1][0] + merged[-1][1]): merged[-1][1] += maps[i][1] continue merged.append( maps[i] ) baseaddr = 0x05000000 for offset,size in merged: bytez = elf.readAtOffset(offset,size) vw.addMemoryMap(baseaddr + offset, 0x7, fname, bytez) for sec in secs: if sec.sh_offset and sec.sh_size: sec.sh_addr = baseaddr + sec.sh_offset # First add all section definitions so we have them for sec in secs: sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr vw.addSegment(sva, size, sname, fname) # since getFileByVa is based on segments, and ELF Sections seldom cover all the # loadable memory space.... we'll add PT_LOAD Program Headers, only at the # end. If we add them first, they're always the matching segments. At the # end, they make more of a default segment pcount = 0 if vw.getFileByVa(baseaddr) is None: for phdr in elf.getPheaders(): if phdr.p_type != Elf.PT_LOAD: continue sva = phdr.p_vaddr if addbase: sva += baseaddr vw.addSegment(sva, phdr.p_memsz, 'PHDR%d' % pcount, fname) pcount += 1 # load information from dynamics: f_init = elf.dyns.get(Elf.DT_INIT) if f_init is not None: if addbase: f_init += baseaddr vw.makeName(f_init, "init_function", filelocal=True) vw.addEntryPoint(f_init) f_fini = elf.dyns.get(Elf.DT_FINI) if f_fini is not None: if addbase: f_fini += baseaddr vw.makeName(f_fini, "fini_function", filelocal=True) vw.addEntryPoint(f_fini) f_inita = elf.dyns.get(Elf.DT_INIT_ARRAY) if f_inita is not None: f_initasz = elf.dyns.get(Elf.DT_INIT_ARRAYSZ) makeFunctionTable(elf, vw, f_inita, f_initasz, 'init_array', new_functions, new_pointers, baseaddr, addbase) f_finia = elf.dyns.get(Elf.DT_FINI_ARRAY) if f_finia is not None: f_finiasz = elf.dyns.get(Elf.DT_FINI_ARRAYSZ) makeFunctionTable(elf, vw, f_finia, f_finiasz, 'fini_array', new_functions, new_pointers, baseaddr, addbase) f_preinita = elf.dyns.get(Elf.DT_PREINIT_ARRAY) if f_preinita is not None: f_preinitasz = elf.dyns.get(Elf.DT_PREINIT_ARRAY) makeFunctionTable(elf, vw, f_preinita, f_preinitasz, 'preinit_array', new_functions, new_pointers, baseaddr, addbase) # dynamic table phdr = elf.getDynPHdr() # file offset? if phdr is not None: sva, size = phdr.p_vaddr, phdr.p_memsz if addbase: sva += baseaddr # getDynInfo returns (offset, filesz) makeDynamicTable(vw, sva, sva+size) # if there's no Dynamics PHDR, don't bother trying to parse it from Sections. It doesn't exist. # dynstr table sva, size = elf.getDynStrTabInfo() if sva is not None: if addbase: sva += baseaddr makeStringTable(vw, sva, sva+size) # dynsyms table sva, symsz, size = elf.getDynSymTabInfo() if sva is not None: if addbase: sva += baseaddr for s in makeSymbolTable(vw, sva, sva+size): logger.info("######################## .dynsym %r",s) # Now trigger section specific analysis # Test to make sure Dynamics info is king. Sections info should not overwrite Dynamics for sec in secs: sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr # if we've already defined a location at this address, skip it. (eg. DYNAMICS) if vw.getLocation(sva) == sva: continue if sname == ".interp": vw.makeString(sva) elif sname == ".init": vw.makeName(sva, "init_function", filelocal=True) new_functions.append(("init_function", sva)) elif sname == ".init_array": makeFunctionTable(elf, vw, sec.sh_addr, size, 'init_function', new_functions, new_pointers, baseaddr, addbase) elif sname == ".fini": vw.makeName(sva, "fini_function", filelocal=True) new_functions.append(("fini_function", sva)) elif sname == ".fini_array": makeFunctionTable(elf, vw, sec.sh_addr, size, 'fini_function', new_functions, new_pointers, baseaddr, addbase) elif sname == ".dynamic": # Imports makeDynamicTable(vw, sva, sva+size) elif sname == ".dynstr": # String table for dynamics makeStringTable(vw, sva, sva+size) elif sname == ".dynsym": logger.debug("LINK\t%r",sec.sh_link) for s in makeSymbolTable(vw, sva, sva+size): logger.info("######################## .dynsym %r",s) # If the section is really a string table, do it if sec.sh_type == Elf.SHT_STRTAB: makeStringTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_SYMTAB: makeSymbolTable(vw, sva, sva+size) elif sec.sh_type == Elf.SHT_REL: makeRelocTable(vw, sva, sva+size, addbase, baseaddr) elif sec.sh_type == Elf.SHT_RELA: makeRelocTable(vw, sva, sva+size, addbase, baseaddr, addend=True) if sec.sh_flags & Elf.SHF_STRINGS: makeStringTable(vw, sva, sva+size) # get "Dynamics" based items, like NEEDED libraries (dependencies) elfmeta = {} for d in elf.getDynamics(): if d.d_tag == Elf.DT_NEEDED: name = d.getName() name = name.split('.')[0].lower() vw.addLibraryDependancy(name) else: logger.debug("DYNAMIC:\t%r",d) elfmeta[Elf.dt_names.get(d.d_tag)] = d.d_value vw.setFileMeta(fname, 'ELF_DYNAMICS', elfmeta) # create a VaSet instead? setMeta allows more free-form info, but isn't currently accessible from the gui vw.setFileMeta(fname, 'addbase', addbase) # applyRelocs is specifically prior to "process Dynamic Symbols" because Dynamics-only symbols # (ie. not using Section Headers) may not get all the symbols. Some ELF's simply list too # small a space using SYMTAB and SYMTABSZ applyRelocs(elf, vw, addbase, baseaddr) # process Dynamic Symbols - this must happen *after* relocations, which can expand the size of this for s in elf.getDynSyms(): stype = s.getInfoType() sva = s.st_value if sva == 0: continue if addbase: sva += baseaddr if sva == 0: continue dmglname = demangle(s.name) logger.debug('dynsyms: 0x%x: %r', sva, dmglname) if stype == Elf.STT_FUNC or \ (stype == Elf.STT_GNU_IFUNC and arch in ('i386', 'amd64')): # HACK: linux is what we're really after. try: new_functions.append(("DynSym: STT_FUNC", sva)) vw.addExport(sva, EXP_FUNCTION, dmglname, fname, makeuniq=True) vw.setComment(sva, s.name) except Exception as e: vw.vprint('addExport Failure: (%s) %s' % (s.name, e)) elif stype == Elf.STT_OBJECT: if vw.isValidPointer(sva): try: vw.addExport(sva, EXP_DATA, dmglname, fname, makeuniq=True) vw.setComment(sva, s.name) except Exception as e: vw.vprint('WARNING: %s' % e) elif stype == Elf.STT_HIOS: # So aparently Elf64 binaries on amd64 use HIOS and then # s.st_other cause that's what all the kewl kids are doing... sva = s.st_other if addbase: sva += baseaddr if vw.isValidPointer(sva): try: new_functions.append(("DynSym: STT_HIOS", sva)) vw.addExport(sva, EXP_FUNCTION, dmglname, fname, makeuniq=True) vw.setComment(sva, s.name) except Exception as e: vw.vprint('WARNING: %s' % e) elif stype == Elf.STT_MDPROC: # there's only one that isn't HI or LO... sva = s.st_other if addbase: sva += baseaddr if vw.isValidPointer(sva): try: vw.addExport(sva, EXP_DATA, dmglname, fname, makeuniq=True) vw.setComment(sva, s.name) except Exception as e: vw.vprint('WARNING: %s' % e) else: logger.debug("DYNSYM:\t%r\t%r\t%r\t%r", s, s.getInfoType(), 'other', hex(s.st_other)) vw.addVaSet("FileSymbols", (("Name", VASET_STRING),("va", VASET_ADDRESS))) vw.addVaSet("WeakSymbols", (("Name", VASET_STRING),("va", VASET_ADDRESS))) # apply symbols to workspace (if any) relocs = elf.getRelocs() impvas = [va for va, x, y, z in vw.getImports()] expvas = [va for va, x, y, z in vw.getExports()] for s in elf.getSymbols(): sva = s.st_value dmglname = demangle(s.name) logger.debug('symbol val: 0x%x\ttype: %r\tbind: %r\t name: %r', sva, Elf.st_info_type.get(s.st_info, s.st_info), Elf.st_info_bind.get(s.st_other, s.st_other), s.name) if s.st_info == Elf.STT_FILE: vw.setVaSetRow('FileSymbols', (dmglname, sva)) continue if s.st_info == Elf.STT_NOTYPE: # mapping symbol if arch in ('arm', 'thumb', 'thumb16'): symname = s.getName() if addbase: sva += baseaddr if symname == '$a': # ARM code logger.info('mapping (NOTYPE) ARM symbol: 0x%x: %r',sva, dmglname) new_functions.append(("Mapping Symbol: $a", sva)) elif symname == '$t': # Thumb code logger.info('mapping (NOTYPE) Thumb symbol: 0x%x: %r',sva, dmglname) new_functions.append(("Mapping Symbol: $t", sva + 1)) elif symname == '$d': # Data Items (eg. literal pool) logger.info('mapping (NOTYPE) data symbol: 0x%x: %r',sva, dmglname) data_ptrs.append(sva) # if the symbol has a value of 0, it is likely a relocation point which gets updated sname = demangle(s.name) if sva == 0: for reloc in relocs: rname = demangle(reloc.name) if rname == sname: sva = reloc.r_offset logger.info('sva==0, using relocation name: %x: %r', sva, rname) break dmglname = demangle(sname) # TODO: make use of _ZTSN (typeinfo) and _ZTVN (vtable) entries if name demangles cleanly if addbase: sva += baseaddr if vw.isValidPointer(sva) and len(dmglname): try: if s.st_other == Elf.STB_WEAK: logger.info('WEAK symbol: 0x%x: %r', sva, sname) vw.setVaSetRow('WeakSymbols', (sname, sva)) dmglname = '__weak_' + dmglname if sva in impvas or sva in expvas: imps = [imp for imp in vw.getImports() if imp[0] == sva] exps = [exp for exp in vw.getExports() if exp[0] == sva] logger.debug('skipping Symbol naming for existing Import/Export: 0x%x (%r) (%r) (%r)', sva, s.name, imps, exps) else: vw.makeName(sva, dmglname, filelocal=True, makeuniq=True) except Exception as e: logger.warn("WARNING:\t%r",e) if s.st_info == Elf.STT_FUNC: new_functions.append(("STT_FUNC", sva)) if addbase: eentry = baseaddr + elf.e_entry else: eentry = elf.e_entry if vw.isValidPointer(eentry): vw.addExport(eentry, EXP_FUNCTION, '__entry', fname) new_functions.append(("ELF Entry", eentry)) if vw.isValidPointer(baseaddr): sname = 'elf.Elf%d' % (vw.getPointerSize() * 8) vw.makeStructure(baseaddr, sname) # mark all the entry points for analysis later for cmnt, fva in new_functions: logger.info('adding function from ELF metadata: 0x%x (%s)', fva, cmnt) vw.addEntryPoint(fva) # addEntryPoint queue's code analysis for later in the analysis pass # mark all the pointers for analysis later for va, tva, pname in new_pointers: logger.info('adding pointer 0x%x -> 0x%x', va, tva) vw.setVaSetRow('PointersFromFile', (va, tva, fname, pname)) return fname
def loadElfIntoWorkspace(vw, elf, filename=None): arch = arch_names.get(elf.e_machine) if arch == None: raise Exception("Unsupported Architecture: %d\n", elf.e_machine) platform = elf.getPlatform() # setup needed platform/format vw.setMeta('Architecture', arch) vw.setMeta('Platform', platform) vw.setMeta('Format', 'elf') vw.setMeta('DefaultCall', archcalls.get(arch, 'unknown')) vw.addNoReturnApi("*.exit") # Base addr is earliest section address rounded to pagesize # NOTE: This is only for prelink'd so's and exe's. Make something for old style so. addbase = False if not elf.isPreLinked() and elf.isSharedObject(): addbase = True baseaddr = elf.getBaseAddress() #FIXME make filename come from dynamic's if present for shared object if filename == None: filename = "elf_%.8x" % baseaddr fhash = "unknown hash" if os.path.exists(filename): fhash = v_parsers.md5File(filename) fname = vw.addFile(filename.lower(), baseaddr, fhash) strtabs = {} secnames = [] for sec in elf.getSections(): secnames.append(sec.getName()) pgms = elf.getPheaders() secs = elf.getSections() for pgm in pgms: if pgm.p_type == Elf.PT_LOAD: if vw.verbose: vw.vprint('Loading: %s' % (repr(pgm))) bytez = elf.readAtOffset(pgm.p_offset, pgm.p_filesz) bytez += "\x00" * (pgm.p_memsz - pgm.p_filesz) pva = pgm.p_vaddr if addbase: pva += baseaddr vw.addMemoryMap(pva, pgm.p_flags & 0x7, fname, bytez) #FIXME perms else: if vw.verbose: vw.vprint('Skipping: %s' % repr(pgm)) if len(pgms) == 0: # fall back to loading sections as best we can... if vw.verbose: vw.vprint('elf: no program headers found!') maps = [[s.sh_offset, s.sh_size] for s in secs if s.sh_offset and s.sh_size] maps.sort() merged = [] for i in xrange(len(maps)): if merged and maps[i][0] == (merged[-1][0] + merged[-1][1]): merged[-1][1] += maps[i][1] continue merged.append(maps[i]) baseaddr = 0x05000000 for offset, size in merged: bytez = elf.readAtOffset(offset, size) vw.addMemoryMap(baseaddr + offset, 0x7, fname, bytez) for sec in secs: if sec.sh_offset and sec.sh_size: sec.sh_addr = baseaddr + sec.sh_offset # First add all section definitions so we have them for sec in secs: sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr vw.addSegment(sva, size, sname, fname) # Now trigger section specific analysis for sec in secs: #FIXME dup code here... sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr if sname == ".interp": vw.makeString(sva) elif sname == ".init": vw.makeName(sva, "init_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".fini": vw.makeName(sva, "fini_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".dynamic": # Imports makeDynamicTable(vw, sva, sva + size) # FIXME section names are optional, use dynamic info from .dynamic elif sname == ".dynstr": # String table for dynamics makeStringTable(vw, sva, sva + size) elif sname == ".dynsym": #print "LINK",sec.sh_link for s in makeSymbolTable(vw, sva, sva + size): pass #print "########################.dynsym",s # If the section is really a string table, do it if sec.sh_type == Elf.SHT_STRTAB: makeStringTable(vw, sva, sva + size) elif sec.sh_type == Elf.SHT_SYMTAB: makeSymbolTable(vw, sva, sva + size) elif sec.sh_type == Elf.SHT_REL: makeRelocTable(vw, sva, sva + size, addbase, baseaddr) if sec.sh_flags & Elf.SHF_STRINGS: print "FIXME HANDLE SHF STRINGS" # Let pyelf do all the stupid string parsing... for r in elf.getRelocs(): rtype = Elf.getRelocType(r.r_info) rlva = r.r_offset if addbase: rlva += baseaddr try: # If it has a name, it's an externally # resolved "import" entry, otherwise, just a regular reloc if arch in ('i386', 'amd64'): name = r.getName() if name: if rtype == Elf.R_386_JMP_SLOT: vw.makeImport(rlva, "*", name) # FIXME elf has conflicting names for 2 relocs? #elif rtype == Elf.R_386_GLOB_DAT: #vw.makeImport(rlva, "*", name) elif rtype == Elf.R_386_32: pass else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) if arch == 'arm': name = r.getName() if name: if rtype == Elf.R_ARM_JUMP_SLOT: vw.makeImport(rlva, "*", name) else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) except vivisect.InvalidLocation, e: print "NOTE", e
def loadElfIntoWorkspace(vw, elf, filename=None): arch = arch_names.get(elf.e_machine) if arch == None: raise Exception("Unsupported Architecture: %d\n", elf.e_machine) # setup needed platform/format vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'unknown') vw.setMeta('Format', 'elf') # FIXME try to determine *which* Elf system... if arch == 'i386': vw.setMeta("DefaultCall", "cdecl") elif arch == 'amd64': vw.setMeta("DefaultCall", "sysvamd64call") vw.addNoReturnApi("*.exit") # Base addr is earliest section address rounded to pagesize # NOTE: This is only for prelink'd so's and exe's. Make something for old style so. addbase = False if not elf.isPreLinked() and elf.isSharedObject(): addbase = True baseaddr = elf.getBaseAddress() #FIXME make filename come from dynamic's if present for shared object if filename == None: filename = "elf_%.8x" % baseaddr fname = vw.addFile(filename, baseaddr, v_parsers.md5File(filename)) strtabs = {} secnames = [] for sec in elf.getSections(): secnames.append(sec.getName()) for pgm in elf.getPheaders(): if pgm.p_type == Elf.PT_LOAD: if vw.verbose: vw.vprint('Loading: %s' % (repr(pgm))) bytes = elf.readAtOffset(pgm.p_offset, pgm.p_filesz) bytes += "\x00" * (pgm.p_memsz - pgm.p_filesz) pva = pgm.p_vaddr if addbase: pva += baseaddr vw.addMemoryMap(pva, pgm.p_flags & 0x7, fname, bytes) #FIXME perms else: if vw.verbose: vw.vprint('Skipping: %s' % repr(pgm)) # First add all section definitions so we have them for sec in elf.getSections(): sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr vw.addSegment(sva, size, sname, fname) # Now trigger section specific analysis for sec in elf.getSections(): #FIXME dup code here... sname = sec.getName() size = sec.sh_size if sec.sh_addr == 0: continue # Skip non-memory mapped sections sva = sec.sh_addr if addbase: sva += baseaddr if sname == ".interp": vw.makeString(sva) elif sname == ".init": vw.makeName(sva, "init_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".fini": vw.makeName(sva, "fini_function", filelocal=True) vw.addEntryPoint(sva) elif sname == ".dynamic": # Imports makeDynamicTable(vw, sva, sva + size) # FIXME section names are optional, use dynamic info from .dynamic elif sname == ".dynstr": # String table for dynamics makeStringTable(vw, sva, sva + size) elif sname == ".dynsym": #print "LINK",sec.sh_link for s in makeSymbolTable(vw, sva, sva + size): pass #print "########################.dynsym",s # If the section is really a string table, do it if sec.sh_type == Elf.SHT_STRTAB: makeStringTable(vw, sva, sva + size) elif sec.sh_type == Elf.SHT_SYMTAB: # FIXME 32 bit specific! #print "FOUND A SYMBOL TABLE!" makeSymbolTable(vw, sva, sva + size) elif sec.sh_type == Elf.SHT_REL: makeRelocTable(vw, sva, sva + size, addbase, baseaddr) if sec.sh_flags & Elf.SHF_STRINGS: print "FIXME HANDLE SHF STRINGS" # Let pyelf do all the stupid string parsing... for r in elf.getRelocs(): rtype = Elf.getRelocType(r.r_info) rlva = r.r_offset if addbase: rlva += baseaddr try: # If it has a name, it's an externally # resolved "import" entry, otherwise, just a regular reloc if arch in ('i386', 'amd64'): name = r.getName() if name: if rtype == Elf.R_386_JMP_SLOT: vw.makeImport(rlva, "*", name) # FIXME elf has conflicting names for 2 relocs? #elif rtype == Elf.R_386_GLOB_DAT: #vw.makeImport(rlva, "*", name) elif rtype == Elf.R_386_32: pass else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) if arch == 'arm': name = r.getName() if name: if rtype == Elf.R_ARM_JUMP_SLOT: vw.makeImport(rlva, "*", name) else: vw.verbprint('unknown reloc type: %d %s (at %s)' % (rtype, name, hex(rlva))) except vivisect.InvalidLocation, e: print "NOTE", e