def test_MACHO_lib_ecpg(assertion): macho_lib = open(__dir__ + 'libecpg.6.5.dylib', 'rb').read() e = MACHO(macho_lib) macho_lib_hash = hashlib.md5(macho_lib).hexdigest() d = e.pack() assertion(macho_lib_hash, hashlib.md5(d).hexdigest(), 'Packing after reading postgresql library') d = ('\n'.join([_ for l in e.load for _ in l.otool()])).encode('latin1') assertion('df729c8806748bba93ef960787036d37', hashlib.md5(d).hexdigest(), 'Otool-like output including section size "past end of file"') d = ('\n'.join([_ for l in e.load for _ in l.otool(llvm=7)])).encode('latin1') assertion( '7038d70ea2d7caf8b4a2adc3c9c01ef9', hashlib.md5(d).hexdigest(), 'Otool-like output including section size "past end of file", llvm version 7' ) assertion( e.symbols[1].otool(), 'execute.c NO_SECT 0x4 D 0x00000000 0000', 'Display symbol with N_STAB type')
def test_MACHO_lib_system(assertion): macho_lib = open(__dir__ + 'libSystem.B.dylib', 'rb').read() e = MACHO(macho_lib) macho_lib_hash = hashlib.md5(macho_lib).hexdigest() d = e.pack() assertion(macho_lib_hash, hashlib.md5(d).hexdigest(), 'Packing after reading libSystem') bind_s = [ _ for a in e.arch for _ in a.sect if getattr(_, 'type', None) in ('rebase_', 'export_') ] d = ('\n'.join([str(_) for s in bind_s for _ in s.info])).encode('latin1') assertion('81bc735570cb8f78099579fcf6a29f65', hashlib.md5(d).hexdigest(), 'dyldinfo-like output for rebase and export (libSystem)') bind_s = [ _ for a in e.arch for _ in a.sect if getattr(_, 'type', None) == 'rebase_' ] d = ('\n'.join([str(_) for s in bind_s for _ in s])).encode('latin1') assertion('c71cebc604ba70bfd348a3e08f7ea20c', hashlib.md5(d).hexdigest(), 'dyldinfo-like output for rebase opcodes (libSystem)')
def test_MACHO_ios_lyonmetro(assertion): global log_history macho_ios = open(__dir__ + 'LyonMetro', 'rb').read() e = MACHO(macho_ios) assertion([ ('warn', ('Some encrypted text is not parsed with the section headers of LC_SEGMENT(__TEXT)', ), {}), ('warn', ('parse_dynamic_symbols() can only be used with x86 architectures, not %s', 12), {}), ('warn', ('Part of the file was not parsed: %d bytes', 3908), {}) ], log_history, 'Parsing LyonMetro iOS app (logs)') log_history = [] macho_ios_hash = hashlib.md5(macho_ios).hexdigest() d = e.pack() assertion(macho_ios_hash, hashlib.md5(d).hexdigest(), 'Packing after reading iOS application LyonMetro') assertion(e.entrypoint, 0x2f50, 'Entrypoint in iOS application LyonMetro') d = ('\n'.join([_ for l in e.load for _ in l.otool()])).encode('latin1') assertion('7bac82cc00b5cce2cb96344d678508e5', hashlib.md5(d).hexdigest(), 'Otool-like output including LC_VERSION_MIN_IPHONEOS')
def test_MACHO_minimal(assertion): global log_history # Simple tests of object creation e = MACHO(struct.pack("<I", macho.MH_MAGIC)) assertion([('warn', ( 'parse_dynamic_symbols() can only be used with x86 architectures, not %s', 0), {})], log_history, 'Parsing a minimal data, with Mach-O magic number only (logs)') log_history = [] assertion(e.entrypoint, -1, 'No entrypoint in a truncated Mach-O header') assertion([('error', ('Not a unique loader with entrypoint: []', ), {})], log_history, 'No entrypoint in a truncated Mach-O header (logs)') log_history = [] d = e.pack() assertion('37b830a1776346543c72ff53fbbe2b4a', hashlib.md5(d).hexdigest(), 'Parsing a minimal data, with Mach-O magic number only')
def test_COFF_ckermit(assertion): # C-Kermit binary for OSF1 out_osf1 = open(__dir__+'/binary_input/cku200.dec-osf-1.3a', 'rb').read() e = Coff(out_osf1) d = repr(e.OSF1Symbols).encode('latin1') assertion('c7df867846612e6fc1c52a8042f706cc', hashlib.md5(d).hexdigest(), 'Display OSF/1 Symbols') # C-Kermit binary for Clipper CLIX e = Coff(open(__dir__+'/binary_input/cku196.clix-3.1', 'rb').read()) # C-Kermit binary for Apollo e = Coff(open(__dir__+'/binary_input/cku193a05.apollo-sr10-s5r3', 'rb').read()) # C-Kermit XCOFF32 binary for AIX e = Coff(open(__dir__+'/binary_input/cku190.rs6aix32c-3.2.4', 'rb').read()) # C-Kermit eCOFF32 binary for MIPS, big endian e = Coff(open(__dir__+'/binary_input/cku192.irix40', 'rb').read()) # C-Kermit eCOFF32 binary for MIPS, little endian e = Coff(open(__dir__+'/binary_input/cku192.ultrix43c-mips3', 'rb').read())
def test_ELF_small32(assertion): global log_history elf_small = open(__dir__+'/binary_input/elf_small.out', 'rb').read() assertion('d5284d5f438e25ef5502a0c1de97d84f', hashlib.md5(elf_small).hexdigest(), 'Reading elf_small.out') e = ELF(elf_small) d = e.pack() assertion('d5284d5f438e25ef5502a0c1de97d84f', hashlib.md5(d).hexdigest(), 'Packing after reading elf_small.out') # Packed file is identical :-) d = repr(e.ph).encode('latin1') assertion('ab4b1e52e7532789592878872910a2a1', hashlib.md5(d).hexdigest(), 'Display Program Headers') d = repr(e.sh).encode('latin1') assertion('ddf01165114eb70bd27910e4c5b03c09', hashlib.md5(d).hexdigest(), 'Display Section Headers (repr)') d = e.sh.readelf_display().encode('latin1') assertion('08da11fa164d7013561db398c068ac71', hashlib.md5(d).hexdigest(), 'Display Section Headers (readelf)') d = e.getsectionbyname('.symtab').readelf_display().encode('latin1') assertion('943434f4cde658b1659b7d8db39d9e60', hashlib.md5(d).hexdigest(), 'Display Symbol Table') assertion(' 49: 0804a01c 0 NOTYPE GLOBAL DEFAULT ABS _edata', e.getsectionbyname('.symtab')['_edata'].readelf_display(), 'Get symbol by name, found') assertion(' 2: 00000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail', e.getsectionbyname('.dynsym')[2].readelf_display(), 'Get symbol by index, found') d = e.getsectionbytype(elf.SHT_SYMTAB).pack() assertion('4ed5a808faff1ca7c6a766ae45ebf377', hashlib.md5(d).hexdigest(), 'Get existing section by type') d = e.getsectionbyname('.text').pack() assertion('7149c6e4b8baaab8beebfeb818585638', hashlib.md5(d).hexdigest(), 'Get existing section by name') d = e.getsectionbyvad(0x080483d0+0x100).pack() assertion('7149c6e4b8baaab8beebfeb818585638', hashlib.md5(d).hexdigest(), 'Get existing section by address') d = e.getsectionbyname('no_sect') assertion(None, d, 'Get non-existing section by name') d = e.getsectionbyvad(0x1000) assertion(None, d, 'Get non-existing section by address') d = e[0x100:0x120] assertion('5e94f899265a799826a46ec86a293e16', hashlib.md5(d).hexdigest(), 'Extract chunk from raw data') assertion(e[0x100:0x120], e._content[0x100:0x120], 'Extract chunk from raw data, deprecated API') assertion(True, e.virt.is_addr_in(0x080483d0), 'Address in mapped virtual memory') assertion(False, e.virt.is_addr_in(0x08048000), 'Address not in mapped virtual memory') d = e.virt[0x080483d0:0x080483e0] assertion('9d225ebfd0f9562b74b17c5a4653dc6f', hashlib.md5(d).hexdigest(), 'Extract chunk from mapped memory, in a section') try: d = e.virt[0x08040000:0x08040020] assertion(0,1, 'Extract chunk from non-mapped memory') except ValueError: pass assertion(e.virt[0x080483d0:0x080483e0], e.virt(0x080483d0,0x080483e0), 'Extract chunk from mapped memory, old API') e.virt[0x080483d0:0x080483e0] = e.virt[0x080483d0:0x080483e0] d = e.pack() assertion('d5284d5f438e25ef5502a0c1de97d84f', hashlib.md5(d).hexdigest(), 'Writing in memory (interval)') e.virt[0x080483d0] = e.virt[0x080483d0:0x080483e0] d = e.pack() assertion('d5284d5f438e25ef5502a0c1de97d84f', hashlib.md5(d).hexdigest(), 'Writing in memory (address)') assertion(0x804a028, len(e.virt), 'Max virtual address') assertion([('warn', ('__len__ deprecated',), {})], log_history, '__len__ deprecated (logs)') log_history = [] # Find leave; ret assertion(0x8048481, e.virt.find(struct.pack('BB', 0xc9, 0xc3)), 'Find pattern (existing)') assertion(-1, e.virt.find(struct.pack('BBBB', 1,2,3,4)), 'Find pattern (not existing)')
def test_PE_manipulate(assertion): global log_history pe_mingw = open(__dir__+'/binary_input/pe_mingw.exe', 'rb').read() e = PE(pe_mingw) # Packed file is not identical :-( # Are missing: # - the data between the end of DOS header and the start of PE header # - the padding after the list of sections, before the first section # - many parts of directories d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Packing after reading pe_mingw.exe') d = PE(d).pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Packing after reading pe_mingw.exe; fix point') d = e.SHList.display().encode('latin1') assertion('ba631f3f172712b6526e284269c1ecbb', hashlib.md5(d).hexdigest(), 'Display Sections from PE') d = e.Symbols.display().encode('latin1') assertion('1ee89dc3dc2104190734747d148b7511', hashlib.md5(d).hexdigest(), 'Display COFF Symbols') assertion('__gnu_exception_handler@4', e.Symbols.getbyindex(2).name, 'Get symbol by index, found') assertion(None, e.Symbols.getbyindex(2000), 'Get symbol by index, not existing') d = e.getsectionbyname('.text').pack() assertion('ad0d51a670cb6cd2015499840ffefb8f', hashlib.md5(d).hexdigest(), 'Get existing section by name') d = e.getsectionbyoff(0x400+0x100).pack() assertion('ad0d51a670cb6cd2015499840ffefb8f', hashlib.md5(d).hexdigest(), 'Get existing section by offset') d = e.getsectionbyvad(0x400000+0x1000+0x100).pack() assertion('ad0d51a670cb6cd2015499840ffefb8f', hashlib.md5(d).hexdigest(), 'Get existing section by address') d = e.getsectionbyname('no_sect') assertion(None, d, 'Get non-existing section by name') d = e.getsectionbyoff(0x80000) assertion(None, d, 'Get non-existing section by offset') d = e.getsectionbyvad(0x1000) assertion(None, d, 'Get non-existing section by address') d = e[0x100:0x120] assertion('6b8897a89909959320f8adfc1d81c9ee', hashlib.md5(d).hexdigest(), 'Extract chunk from raw data') assertion(True, e.virt.is_addr_in(0x401000), 'Address in mapped virtual memory') assertion(False, e.virt.is_addr_in(0x201000), 'Address not in mapped virtual memory') d = e.virt[0x401000] assertion('4c614360da93c0a041b22e537de151eb', hashlib.md5(d).hexdigest(), 'Extract byte from mapped memory, in a section') d = e.virt[0x400100] assertion('93b885adfe0da089cdf634904fd59f71', hashlib.md5(d).hexdigest(), 'Extract byte from mapped memory, in no section') d = e.virt[0x400100:0x400120] assertion('6b8897a89909959320f8adfc1d81c9ee', hashlib.md5(d).hexdigest(), 'Extract chunk from mapped memory, in headers') d = e.virt[0x401000:0x401020] assertion('21ac18c2564a3b408b31aae0af19d502', hashlib.md5(d).hexdigest(), 'Extract chunk from mapped memory, in a section') d = e.virt[0x100:0x200] # One null byte assertion([('warn', ('unknown rva address! -3fff00',), {})], log_history, 'Extract chunk from non-mapped memory (logs)') log_history = [] assertion('93b885adfe0da089cdf634904fd59f71', hashlib.md5(d).hexdigest(), 'Extract chunk from non-mapped memory') assertion(e.virt[0x401000:0x401020], e.virt(0x401000,0x401020), 'Extract chunk from mapped memory, old API') e[0x100:0x120] = e[0x100:0x120] d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Writing in raw data') e.rva.set(0x1100, e.virt[0x401100:0x401120]) d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Writing at RVA') e.virt[0x401100:0x401120] = e.virt[0x401100:0x401120] d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Writing in memory (interval)') e.virt[0x401100] = e.virt[0x401100:0x401120] d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Writing in memory (address)') e.virt[0x400100:0x400120] = e.virt[0x400100:0x400120] assertion([('warn', ('Cannot write at RVA %s', slice(256, 288, None)), {})], log_history, 'Writing at invalid RVA (logs)') log_history = [] assertion(0x468e71, len(e.virt), 'Max virtual address') assertion([('warn', ('__len__ deprecated',), {})], log_history, '__len__ deprectated (logs)') log_history = [] # Find leave; ret assertion(0x401294, e.virt.find(struct.pack('BB', 0xc9, 0xc3)), 'Find pattern (from the start)') assertion(0x4014B4, e.virt.rfind(struct.pack('BB', 0xc9, 0xc3)), 'Find pattern (from the end)') e.SHList.align_sections() d = e.pack() assertion('2f08b8315c4e0a30d51a8decf104345c', hashlib.md5(d).hexdigest(), 'Align sections') # Remove Bound Import directory # Usually, its content is not stored in any section... that's # a future version of elfesteem will need to manage this # specific directory in a specific way. e.NThdr.optentries[pe.DIRECTORY_ENTRY_BOUND_IMPORT].rva = 0 e.NThdr.optentries[pe.DIRECTORY_ENTRY_BOUND_IMPORT].size = 0 # Create new sections with all zero content s_redir = e.SHList.add_section(name = "redir", size = 0x1000) s_test = e.SHList.add_section(name = "test", size = 0x1000) s_rel = e.SHList.add_section(name = "rel", size = 0x5000) d = e.pack() assertion('439f6c698d3d5238d88c5ccef99761e2', hashlib.md5(d).hexdigest(), 'Adding sections') d = PE(d).pack() assertion('439f6c698d3d5238d88c5ccef99761e2', hashlib.md5(d).hexdigest(), 'Adding sections; fix point') e = PE(pe_mingw) # Delete the last sections => OK for _ in range(2): del e.SHList._array[-1] e.SHList._size -= 40 e.COFFhdr.numberofsections -= 1 # Add two Descriptors in the Import Directory e.DirImport.add_dlldesc( [({"name":"kernel32.dll", "firstthunk":s_test.addr}, ["CreateFileA", "SetFilePointer", "WriteFile", "CloseHandle", ] ), ({"name":"USER32.dll", "firstthunk":None}, ["SetDlgItemInt", "GetMenu", "HideCaret", ] ) ] ) s_myimp = e.SHList.add_section(name="myimp", rawsize=len(e.DirImport)) e.DirImport.set_rva(s_myimp.addr) assertion(0x4050a8, e.DirImport.get_funcvirt('KERNEL32.dll','ExitProcess'), 'Import ExitProcess') assertion(None, e.DirImport.get_funcvirt(None,'LoadStringW'), 'Import LoadStringW') assertion(None, e.DirExport.get_funcvirt('SetUserGeoID'), 'Export SetUserGeoID') d = e.pack() assertion('8a3a1c8c9aa2db211e1d34c7efbb8473', hashlib.md5(d).hexdigest(), 'Adding new imports') d = PE(d).pack() assertion([('warn', ('Section %d size %#x not aligned to %#x', 5, 294, 512), {})], log_history, 'Adding new imports (logs)') log_history = [] assertion('8a3a1c8c9aa2db211e1d34c7efbb8473', hashlib.md5(d).hexdigest(), 'Adding new imports; fix point') # Add an export if e.DirExport.expdesc is None: e.DirExport.create(['coco']) assertion(0x40703e, e.DirExport.get_funcvirt('coco'), 'Export: get_funcvirt') # 'eval' avoids warnings with python2.3 assertion({1: eval("0xdeedc0fe"), 'coco': eval("0xdeedc0fe")}, e.export_funcs(), 'Export: export_funcs') d = e.pack() assertion('47a864481296d88f908126fb822ded59', hashlib.md5(d).hexdigest(), 'Adding new exports') d = PE(d).pack() assertion([('warn', ('Section %d size %#x not aligned to %#x', 5, 294, 512), {})], log_history, 'Adding new exports (logs)') log_history = [] assertion('47a864481296d88f908126fb822ded59', hashlib.md5(d).hexdigest(), 'Adding new exports; fix point') # Add a new Descriptor in the Import Directory e.DirImport.add_dlldesc([ ({"name":"MyDLL.dll"}, ["MyFunc"]) ]) e.DirImport.set_rva(None) assertion('47a864481296d88f908126fb822ded59', hashlib.md5(d).hexdigest(), 'Adding imports, no specified section')
def test_PE_empty64(assertion): e = PE(wsize=64) d = e.pack() assertion('863bf62f521b0cad3209e42cff959eed', hashlib.md5(d).hexdigest(), 'Creation of a standard empty PE+')
def test_PE_ange(assertion): global log_history # Parse some ill-formed PE made by Ange Albertini e = PE(open(__dir__+'/binary_input/Ange/resourceloop.exe', 'rb').read()) assertion([('warn', ('Resource tree too deep',), {})]*212, log_history, 'Ange/resourceloop.exe (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/namedresource.exe', 'rb').read()) assertion([], log_history, 'Ange/namedresource.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/weirdsord.exe', 'rb').read()) assertion([('warn', ('Section %d offset %#x not aligned to %#x', 0, 513, 16384), {}), ('warn', ('Section %d size %#x not aligned to %#x', 0, 270, 16384), {})], log_history, 'Ange/weirdsord.exe (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/nosectionW7.exe', 'rb').read()) assertion([('warn', ('Number of rva %d does not match sizeofoptionalheader %d', 16, 0), {})], log_history, 'Ange/nosectionW7.exe (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/imports_relocW7.exe', 'rb').read()) assertion([], log_history, 'Ange/imports_relocW7.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/imports_tinyXP.exe', 'rb').read()) assertion([], log_history, 'Ange/imports_tinyXP.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/bottomsecttbl.exe', 'rb').read()) assertion([('warn', ('Number of rva %d does not match sizeofoptionalheader %d', 16, 696), {})], log_history, 'Ange/bottomsecttbl.exe (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/delayfake.exe', 'rb').read()) assertion([], log_history, 'Ange/delayfake.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/exportobf.exe', 'rb').read()) assertion([], log_history, 'Ange/exportobf.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/dllbound-ld.exe', 'rb').read()) assertion([], log_history, 'Ange/dllbound-ld.exe (logs)') e = PE(open(__dir__+'/binary_input/Ange/d_tiny.dll', 'rb').read()) assertion([('warn', ('Opthdr magic %#x', 31074), {}), ('warn', ('Number of rva %d does not match sizeofoptionalheader %d', 0, 13864), {}), ('warn', ('Windows 8 needs at least 13 directories, %d found', 0), {}), ('warn', ('Too many symbols: %d', 541413408), {}), ('warn', ('File too short for StrTable -0x61746127 != 0x0',), {})], log_history, 'Ange/d_tiny.dll (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/dllfw.dll', 'rb').read()) assertion([], log_history, 'Ange/dllfw.dll (logs)') e = PE(open(__dir__+'/binary_input/Ange/tinydllXP.dll', 'rb').read()) assertion([('warn', ('Number of rva %d does not match sizeofoptionalheader %d', 0, 0), {}), ('warn', ('Windows 8 needs at least 13 directories, %d found', 0), {}), ('warn', ('File too short for StrTable 0x55 != 0xc258016a',), {})], log_history, 'Ange/tinydllXP.dll (logs)') log_history = [] e = PE(open(__dir__+'/binary_input/Ange/resourceloop.exe', 'rb').read()) log_history = [] d = e.DirRes.display().encode('latin1') assertion('98701be30b09759a64340e5245e48195', hashlib.md5(d).hexdigest(), 'Display Directory RESOURCE that is too deep')