def test_MACHO_bin_sh(assertion): macho_bin = open(__dir__ + 'sh', 'rb').read() e = MACHO(macho_bin) macho_bin_hash = hashlib.md5(macho_bin).hexdigest() d = e.pack() assertion(macho_bin_hash, hashlib.md5(d).hexdigest(), 'Packing after reading /bin/sh')
def test_MACHO_prebind_64(assertion): global log_history macho_64 = open(__dir__ + 'macho_64.out', 'rb').read() e = MACHO(macho_64) d = e.virt[0x100000f50:0x100000f62] assertion('structure definie\0', d.decode('latin1'), 'Extract chunk from mapped memory, in a section (64 bits)') e.virt[0x100000f50:0x100000f5c] = 'Hello World\0'.encode('latin1') d = e.pack() assertion('b29fe575093a6f68a54131e59138e1d8', hashlib.md5(d).hexdigest(), 'Writing in memory (interval) (64 bits)') e.virt[0x100000f50] = 'Hello World\0'.encode('latin1') d = e.pack() assertion('b29fe575093a6f68a54131e59138e1d8', hashlib.md5(d).hexdigest(), 'Writing in memory (address) (64 bits)') e.add( macho.Section(parent=macho.sectionHeader(parent=e.load), content='arbitrary content'.encode('latin1'))) d = e.pack() assertion('be836b2b8adcff60bcc7ca1d712a92a9', hashlib.md5(d).hexdigest(), 'Adding a section (64 bits)') e = MACHO(macho_64) e.add(type=macho.LC_SEGMENT_64, segname='__NEWTEXT', initprot=macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, content='some binary data'.encode('latin1')) d = e.pack() assertion('b4ad381503c51b6dc9dc3d79fb8ca568', hashlib.md5(d).hexdigest(), 'Adding a segment (64 bits)')
def test_COFF_tms320(assertion): out_tms320 = open(__dir__+'/binary_input/C28346_Load_Program_to_Flash.out', 'rb').read() e = Coff(out_tms320) d = e.SHList.display().encode('latin1') assertion('a63cf686186105b83e49509f213b20ea', hashlib.md5(d).hexdigest(), 'Display Sections from COFF')
def test_MACHO_one_loader(assertion): global log_history f = struct.pack("<IIIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 12, 0, macho.LC_PREBIND_CKSUM, 12, 0) e = MACHO(f) d = e.pack() assertion(f, d, 'Parsing data, with one LC_PREBIND_CKSU loader')
def test_PE_addsections_32(assertion): global log_history e = PE() d = e.pack() assertion('901e6383ee161b569af1d35d3f77b038', hashlib.md5(d).hexdigest(), 'Creation of a standard empty PE') e.SHList.add_section(name = 'new', rawsize = 0x1000) d = e.pack() assertion('15aefbcc8f4b39e9484df8b1ed277c75', hashlib.md5(d).hexdigest(), 'Adding a section to an empty PE') e.SHList.add_section(name = 'nxt', rawsize = 0x1000) d = e.virt[0x401000:0x402000] assertion('620f0b67a91f7f74151bc5be745b7110', hashlib.md5(d).hexdigest(), 'Extract chunk from mapped memory, across multiple sections') for _ in range(89): e.SHList.add_section(name = 'nxt', rawsize = 0x1000) assertion([('error', ('Cannot add section %s: not enough space for section list', 'nxt'), {})], log_history, 'Add too many sections (logs)') log_history = [] assertion(90, # Should be 91 if the last section could been added len(e.SHList), 'Add too many sections')
def test_COFF_invalid(assertion): # Now, we parse COFF files try: # Not COFF: OptHdr size too big e = Coff(open(__dir__+'/binary_input/README.txt', 'rb').read()) assertion(0,1, 'Not COFF') except ValueError: pass
def test_MACHO_empty_loader(assertion): f = struct.pack("<IIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 8, 0, 0, 8) e = MACHO(f) assertion(1, len(e.load), 'Parsing data, with one empty loader (lhlist length)') d = e.pack() assertion(f, d, 'Parsing data, with one empty loader (pack)')
def test_MACHO_macho64_exe(assertion): macho_64 = open(__dir__ + 'macho_64.out', 'rb').read() macho_64_hash = hashlib.md5(macho_64).hexdigest() e = MACHO(macho_64) d = e.pack() assertion(macho_64_hash, hashlib.md5(d).hexdigest(), 'Packing after reading 64-bit Mach-O executable')
def test_RPRC_empty(assertion): e = RPRC() d = e.pack() assertion('865001a37fa24754bd17012e85d2bfff', hashlib.md5(d).hexdigest(), 'Creation of a standard empty RPRC') d = RPRC(d).pack() assertion('865001a37fa24754bd17012e85d2bfff', hashlib.md5(d).hexdigest(), 'Creation of a standard empty RPRC; fix point')
def test_ELF_invalid_entsize(assertion): global log_history # Some various ways for an ELF to be detected as invalid e = ELF() e.symbols.sh.entsize = 24 e = ELF(e.pack()) assertion([('error', ('SymTable has invalid entsize %d instead of %d', 24, 16), {})], log_history, 'Invalid entsize for symbols (logs)') log_history = []
def test_MD_windows(assertion): md = open(__dir__+'/binary_input/windows.dmp', 'rb').read() assertion('82a09a9d801bddd1dc94dfb9ba6eddf0', hashlib.md5(md).hexdigest(), 'Reading windows.dmp') e = Minidump(md) d = e.dump().encode('latin1') assertion('48cae6cc782305b611f6e8b82049b9a0', hashlib.md5(d).hexdigest(), 'Displaying the content of windows.dmp')
def test_MACHO_additional_padding(assertion): global log_history f = struct.pack("<IIIIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 16, 0, macho.LC_PREBIND_CKSUM, 12, 0, 0) e = MACHO(f) assertion([('warn', ('LoadCommands have %d bytes of additional padding', 4), {})], log_history, 'Parsing invalid data, with padding after load commands (logs)') log_history = []
def test_MD_i386(assertion): md = open(__dir__+'/binary_input/minidump-i386.dmp', 'rb').read() assertion('0f2ee1a0a2e6351e64929197c07679e6', hashlib.md5(md).hexdigest(), 'Reading minidump-i386.dmp') e = Minidump(md) d = e.dump().encode('latin1') assertion('c89c01352e515874b00d998b1ad06998', hashlib.md5(d).hexdigest(), 'Displaying the content of minidump-i386.dmp')
def test_MACHO_toolarge_cmds(assertion): global log_history f = struct.pack("<IIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 0xffff, 0, 0, 8) e = MACHO(f) assertion([('error', ('LoadCommands longer than file length', ), {}), ('warn', ('Part of the file was not parsed: %d bytes', 1), {})], log_history, 'Parsing a invalid output with big sizeofcmds (logs)') log_history = []
def test_MD_x86_64(assertion): md = open(__dir__+'/binary_input/minidump-x86_64.dmp', 'rb').read() assertion('ecde7af61615e05ffcde1f064c1a22f8', hashlib.md5(md).hexdigest(), 'Reading minidump-x86_64.dmp') e = Minidump(md) d = e.dump().encode('latin1') assertion('4357695a7e265aca04bb2809485b8634', hashlib.md5(d).hexdigest(), 'Displaying the content of minidump-x86_64.dmp')
def test_MACHO_one_loader_padding(assertion): global log_history f = struct.pack("<IIIIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 16, 0, macho.LC_PREBIND_CKSUM, 16, 0, 0) e = MACHO(f) assertion( [('warn', ('%s has %d bytes of additional padding', 'prebind_cksum_command', 4), {})], log_history, 'Parsing invalid data, with one LC_PREBIND_CKSU loader with padding (logs)' ) log_history = []
def test_MACHO_extend_segment(assertion): macho_64 = open(__dir__ + 'macho_64.out', 'rb').read() e = MACHO(macho_64) for l in e.load: if getattr(l, 'segname', None) == "__LINKEDIT": break e.load.extendSegment(l, 0x1000) d = e.pack() assertion('405962fd8a4fe751c0ea4fe1a9d02c1e', hashlib.md5(d).hexdigest(), 'Extend segment') assertion([], log_history, 'No non-regression test created unwanted log messages')
def test_MACHO_zero_cmds(assertion): global log_history f = struct.pack("<IIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 0, 0, 0, 8) e = MACHO(f) assertion([('error', ('Too many load command: %d commands cannot fit in %d bytes', 1, 0), {}), ('warn', ('Part of the file was not parsed: %d bytes', 1), {})], log_history, 'Parsing a invalid output with zero sizeofcmds (logs)') log_history = []
def test_ELF_invalid_shstrndx(assertion): global log_history e = ELF() e.Ehdr.shstrndx = 20 e = ELF(e.pack()) assertion([('error', ('No section of index shstrndx=20',), {})], log_history, 'Invalid shstrndx (logs)') assertion(88, e.Ehdr.shoff, 'Normal e.Ehdr.shoff') log_history = []
def test_MACHO_one_loader_too_short(assertion): global log_history f = struct.pack("<IIIIIIIIIII", macho.MH_MAGIC, macho.CPU_TYPE_I386, 0, 0, 1, 8, 0, macho.LC_PREBIND_CKSUM, 8, 0, 0) e = MACHO(f) assertion( [('warn', ('%s is %d bytes too short', 'prebind_cksum_command', 4), {})], log_history, 'Parsing invalid data, with one LC_PREBIND_CKSU loader, too short (logs)' ) log_history = []
def test_MACHO_loader_lc_build_version(assertion): global log_history macho_lcbuild = open(__dir__ + 'macho_lcbuild.out', 'rb').read() macho_lcbuild_hash = hashlib.md5(macho_lcbuild).hexdigest() e = MACHO(macho_lcbuild) d = e.pack() assertion(macho_lcbuild_hash, hashlib.md5(d).hexdigest(), "Packing after reading executable with LC_BUILD_VERSION") d = ('\n'.join([_ for l in e.load for _ in l.otool()])).encode('latin1') assertion('6dd985753ccf51b0d5c7470126d43a6c', hashlib.md5(d).hexdigest(), 'Otool-like output for LC in executable with LC_BUILD_VERSION')
def test_MACHO_unixthread_64(assertion): macho_64 = open(__dir__ + 'macho_64.out', 'rb').read() e = MACHO(macho_64) changeMainToUnixThread(e) d = e.pack() assertion('a77d64572857d5414ae414852b930370', hashlib.md5(d).hexdigest(), 'Migrating from LC_MAIN to LC_UNIXTHREAD (64 bits)') insert_start_function(e) d = e.pack() assertion( '16b63a2d3cdb3549fe9870b805eb80f5', hashlib.md5(d).hexdigest(), 'Migrating from LC_MAIN to LC_UNIXTHREAD with new segment (64 bits)')
def test_ELF_creation(assertion): e = ELF( e_type = elf.ET_REL, # Default value e_machine = elf.EM_386, # Default value sections = ['.text', '.text.startup', '.group', '.data', '.rodata.str1.4', '.rodata.cst4', '.bss', '.eh_frame', '.comment', '.note.GNU-stack', ], relocs = ['.text'], # These sections will have relocs ) d = e.pack() assertion('dc3f17080d002ba0bfb3aec9f3bec8b2', hashlib.md5(d).hexdigest(), 'Creation of an ELF with a given list of sections')
def test_ELF_offset_to_sections(assertion): global log_history data = StrPatchwork(ELF().pack()) data[88+20] = struct.pack("<I", 0x1000) e = ELF(data) assertion([('error', ('Offset to end of section %d after end of file', 0), {})], log_history, 'Section offset+size too far away (logs)') log_history = [] data[88+16] = struct.pack("<I", 0x1000) e = ELF(data) assertion([('error', ('Offset to section %d after end of file', 0), {})], log_history, 'Section offset very far away (logs)') log_history = [] data[32] = struct.pack("<I", 100) # e.Ehdr.shoff e = ELF(data) assertion([('error', ('Offset to end of section headers after end of file',), {}), ('error', ('No section of index shstrndx=2',), {})], log_history, 'SH offset too far away (logs)') log_history = [] data[32] = struct.pack("<I", 0x2000) # e.Ehdr.shoff e = ELF(data) assertion([('error', ('Offset to section headers after end of file',), {}), ('error', ('No section of index shstrndx=2',), {})], log_history, 'SH offset very far away (logs)') log_history = []
def test_MACHO_lib_tls(assertion): macho_lib = open(__dir__ + 'libcoretls.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 libcoretls') 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('d7983c780f70e8c81d277ee0f7f8a27d', hashlib.md5(d).hexdigest(), 'dyldinfo-like output for rebase and export (libcoretls)')
def test_ELF_wordsize_endianess(assertion): global log_history data = StrPatchwork(ELF().pack()) data[4] = struct.pack("B", 4) e = ELF(data) assertion([('error', ('Invalid ELF, wordsize defined to %d', 128), {})], log_history, 'Invalid ELF word size (logs)') log_history = [] data = StrPatchwork(ELF().pack()) data[5] = struct.pack("B", 0) e = ELF(data) assertion([('error', ('Invalid ELF, endianess defined to %d', 0), {})], log_history, 'Invalid ELF endianess (logs)') log_history = []
def test_PE_invalids(assertion): # Some various ways for a PE to be detected as invalid e = PE() data = StrPatchwork(e.pack()) try: e.NTsig.signature = 0x2000 e = PE(e.pack()) assertion(0,1, 'Not a PE, invalid NTsig') except ValueError: pass try: e.DOShdr.lfanew = 0x200000 data[60] = struct.pack("<I", e.DOShdr.lfanew) e = PE(data) assertion(0,1, 'Not a PE, NTsig offset after eof') except ValueError: pass
def test_MACHO_unixthread_32(assertion): # The function changeMainToUnixThread migrates a Mach-O binary for # recent MacOSX (using a LC_MAIN loader) to a Mac-O binary for older # versions of MacOSX (10.7 and older, using a LC_UNIXTHREAD loader). macho_32 = open(__dir__ + 'macho_32.out', 'rb').read() e = MACHO(macho_32) changeMainToUnixThread(e) d = e.pack() assertion('1aa73a50d1b941c560f08c20926f9a05', hashlib.md5(d).hexdigest(), 'Migrating from LC_MAIN to LC_UNIXTHREAD (32 bits)') insert_start_function(e) d = e.pack() assertion( '14e8007a3b5b5070c56ea2a43b6b888e', hashlib.md5(d).hexdigest(), 'Migrating from LC_MAIN to LC_UNIXTHREAD with new segment (32 bits)')
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_MACHO_changeUUID(assertion): macho_64 = open(__dir__ + 'macho_64.out', 'rb').read() e = MACHO(macho_64) e.changeUUID("2A0405CF8B1F3502A605695A54C407BB") uuid_pos, = e.load.getpos(macho.LC_UUID) lh = e.load[uuid_pos] assertion((0x2A0405CF, 0x8B1F, 0x3502, 0xA605, 0x695A, 0x54C407BB), lh.uuid, 'UUID change') assertion('<LC_UUID 2A0405CF-8B1F-3502-A605-695A54C407BB>', repr(lh), 'UUID change (repr)') d = e.pack() assertion('f86802506fb24de2ac2bebd9101326e9', hashlib.md5(d).hexdigest(), 'UUID change (pack)') lh.uuid = (0, 0xAAAA, 0, 0, 0, 0x11111111) assertion((0, 0xAAAA, 0, 0, 0, 0x11111111), lh.uuid, 'set UUID') d = e.pack() assertion('c8457df239deb4c51c316bd6670a445e', hashlib.md5(d).hexdigest(), 'set UUID (pack)')