def build_content(self): c = StrPatchwork() c[self.NThdr.sizeofheaders - 1] = pe.data_null c[0] = self.DOShdr.pack() # fix image size if len(self.SHList): s_last = self.SHList.shlist[-1] size = s_last.vaddr + s_last.rsize + (self.NThdr.sectionalignment - 1) size &= ~(self.NThdr.sectionalignment - 1) self.NThdr.sizeofimage = size # headers self.build_headers(c) # section headers off = self.DOShdr.lfanew \ + self.NTsig.bytelen \ + self.COFFhdr.bytelen \ + self.COFFhdr.sizeofoptionalheader c[off] = self.SHList.pack() off += self.SHList.bytelen end_of_headers = off # section data # note that the content of directories should have been already # included section data, which is possible because position and # size of sections are known at this point for s in sorted(self.SHList, key=lambda _: _.scnptr): if s.rawsize == 0: continue if end_of_headers > s.scnptr: log.warning("section %s offset %#x overlap pe hdr %#x", s.name, s.scnptr, off) elif off > s.scnptr: log.warning("section %s offset %#x overlap previous section", s.name, s.scnptr) off = s.scnptr + s.rawsize c[s.scnptr:off] = s.section_data.data.pack() # symbols and strings if self.COFFhdr.numberofsymbols: self.COFFhdr.pointertosymboltable = off c[off] = self.Symbols.pack() assert self.Symbols.bytelen == 18 * self.COFFhdr.numberofsymbols off += self.Symbols.bytelen c[off] = self.SymbolStrings.pack() # some headers may have been updated when building sections or symbols self.build_headers(c) # final verifications l = self.DOShdr.lfanew + self.NTsig.bytelen + self.COFFhdr.bytelen if l % 4: log.warning("non aligned coffhdr, bad crc calculation") crcs = self.patch_crc(c.pack(), self.NThdr.CheckSum) c[l + 64] = struct.pack('I', crcs) return c.pack()
def pack(self): c = StrPatchwork() c[0] = self.hdr.pack() of = self.hdr.bytelen for s in self.sections: c[of] = s.pack() of += s.bytelen return c.pack()
def build_content(self): if self.Ehdr.shoff == 0: elf_set_offsets(self) c = StrPatchwork() c[0] = self.Ehdr.pack() c[self.Ehdr.phoff] = self.ph.pack() for s in self.sh: c[s.sh.offset] = s.pack() c[self.Ehdr.shoff] = self.sh.pack() return c.pack()
def build_content(self): if self.Ehdr.shoff == 0: elf_set_offsets(self) c = StrPatchwork() c[0] = self.Ehdr.pack() c[self.Ehdr.phoff] = self.ph.pack() for s in self.sh: c[s.sh.offset] = s.pack() sh = self.sh.pack() if len(sh): # When 'shoff' is invalid, 'sh' is empty, but the line below # is very slow because strpatchwork extends the file. c[self.Ehdr.shoff] = sh return c.pack()
def __init__(self, parent, **kargs): self.parent = parent inheritsexwsize(self, parent, kargs) self.shlist = [] ehdr = self.parent.Ehdr of1 = ehdr.shoff if not of1: # No SH table return filesize = len(parent.content) if of1 > filesize: log.error("Offset to section headers after end of file") return if of1 + ehdr.shnum * ehdr.shentsize > filesize: log.error("Offset to end of section headers after end of file") return for i in range(ehdr.shnum): of2 = of1 + ehdr.shentsize shstr = parent[of1:of2] self.shlist.append(Section.create(self, shstr=shstr)) of1 = of2 assert len(self.shlist) == ehdr.shnum # The shstrtab section is not always valid :-( if 0 <= ehdr.shstrndx < ehdr.shnum: self._shstrtab = self.shlist[ehdr.shstrndx] else: self._shstrtab = None if not isinstance(self._shstrtab, StrTable): class NoStrTab(object): def get_name(self, idx): return "<no-name>" self._shstrtab = NoStrTab() if ehdr.shnum == 0: return for s in self.shlist: if not isinstance(s, NoBitsSection): if s.sh.offset > filesize: log.error("Offset to section %d after end of file", self.shlist.index(s)) continue if s.sh.offset + s.sh.size > filesize: log.error("Offset to end of section %d after end of file", self.shlist.index(s)) continue s.content = StrPatchwork(parent[s.sh.offset:s.sh.offset + s.sh.size]) # Follow dependencies when initializing sections zero = self.shlist[0] todo = self.shlist[1:] done = [] while todo: s = todo.pop(0) if ((s.linksection in done + [zero, NoLinkSection]) and (s.infosection in done + [zero, None])): done.append(s) s.parse_content() else: todo.append(s)
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_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 __init__(self, parent, sh=None, **kargs): self.parent = parent self.phparent = None inheritsexwsize(self, parent, {}) if sh is None: sh = elf.Shdr(parent=self, type=self.sht, name_idx=0, **kargs) self.sh = sh self.content = StrPatchwork()
def __init__(self, data = None, **kargs): self.sections = [] if data is not None: self.content = StrPatchwork(data) self.parse_content() return # Create a RPRC file with no section self.hdr = RPRChdr(parent=self)
def __init__(self, data=None, **kargs): self.sections = [] if data is not None: self.content = StrPatchwork(data) self.parse_content() else: # Create a RPRC file with no section self.hdr = Header(parent=self) self._virt = Virtual(self)
def asm(self): blocs, symbol_pool = parse_asm.parse_txt(mn_aarch64, 'l', self.TXT, symbol_pool = self.myjit.ir_arch.symbol_pool) # fix shellcode addr symbol_pool.set_offset(symbol_pool.getby_name("main"), 0x0) s = StrPatchwork() patches = asmbloc.asm_resolve_final(mn_aarch64, blocs, symbol_pool) for offset, raw in patches.items(): s[offset] = raw self.assembly = str(s)
def make_signature(self): """ Attaches the signature """ # Get the CodeSignature section. It should be the last in the binary cs_sec = self.macho.sect[-1] assert cs_sec == self.get_linkedit_segment().sect[-1] assert isinstance(cs_sec, CodeSignature) sig_cmd = self.get_sig_command() # Set the section's content to be the signature cs_sec.content = StrPatchwork(self.sig_data)
def make_signature(self): assert self.sig.code_dir_blob # Redo the code hashes self._set_code_hashes() # Make the signature signed_attrs: CMSAttributes = make_signed_attrs( self.sig.code_dir_blob.get_hash(self.hash_type), self.hash_type) actual_privkey = load_private_key(self.privkey) signature = rsa_pkcs1v15_sign(actual_privkey, signed_attrs.dump(), self.hash_type_str) # Get the timestamp from Apple digest = get_hash(signature, self.hash_type) tst = CMSAttribute({ "type": CMSAttributeType("signature_time_stamp_token"), "values": [get_timestamp_token(digest, self.hash_type)], }) # Make the CMS self.sig.sig_blob = SignatureBlob() self.sig.sig_blob.cms = make_cms(self.cert, self.hash_type, signed_attrs, signature, CMSAttributes([tst])) # Get the CodeSignature section. It should be the last in the binary cs_sec = self.macho.sect[-1] assert cs_sec == self.get_linkedit_segment().sect[-1] assert isinstance(cs_sec, CodeSignature) sig_cmd = self.get_sig_command() # Serialize the signature f = BytesIO() self.sig.serialize(f) f.write((sig_cmd.datasize - f.tell()) * b"\x00") if self.detach_target: target_dir = os.path.join(self.detach_target, "Contents", "MacOS") os.makedirs(target_dir, exist_ok=True) target_file = os.path.join( target_dir, os.path.basename(self.filename) + f".{CPU_NAMES[self.macho.Mhdr.cputype]}sign", ) with open(target_file, "wb") as tf: tf.write(f.getvalue()) self.files_modified.append(target_file) else: # Set the section's content to be the signature cs_sec.content = StrPatchwork(f.getvalue())
def asm(self): blocks, loc_db = parse_asm.parse_txt(mn_aarch64, 'l', self.TXT, loc_db=self.myjit.ir_arch.loc_db) # fix shellcode addr loc_db.set_location_offset(loc_db.get_name_location("main"), 0x0) s = StrPatchwork() patches = asmblock.asm_resolve_final(mn_aarch64, blocks, loc_db) for offset, raw in patches.items(): s[offset] = raw self.assembly = str(s)
class CDataInstance(CBase): def _initialize(self, f=f): self._size = f(self.parent) self._data = StrPatchwork() def unpack(self, c, o): self._data[0] = c[o:o+self._size] def pack(self): return self._data.pack() def __str__(self): return self.pack().decode('latin1') def __getitem__(self, item): return self._data[item] def __setitem__(self, item, value): self._data[item] = value
def __init__(self, elfstr=None, **kargs): self._virt = virt(self) if elfstr is None: # Create an ELF file, with default header values # kargs can supersede these default values self.wsize = kargs.get('wsize', 32) self.sex = kargs.get('sex', '<') self.Ehdr = elf.Ehdr(parent=self) self.Ehdr.ident = struct.pack( "16B", 0x7f, 0x45, 0x4c, 0x46, # magic number, \x7fELF { 32: 1, 64: 2 }[self.wsize], # EI_CLASS { '<': 1, '>': 2 }[self.sex], # EI_DATA 1, # EI_VERSION 0, # EI_OSABI 0, # EI_ABIVERSION 0, 0, 0, 0, 0, 0, 0) self.Ehdr.version = 1 self.Ehdr.type = kargs.get('e_type', elf.ET_REL) self.Ehdr.machine = kargs.get('e_machine', elf.EM_386) self.Ehdr.ehsize = self.Ehdr.bytelen self.sh = SHList(self) self.ph = PHList(self) elf_default_content(self, **kargs) return self.content = StrPatchwork(elfstr) self.parse_content() try: self.check_coherency() except ValueError: # Report the exception message in a way compatible with most # versions of python. import sys log.error(str(sys.exc_info()[1]))
def asm(self): mn_x86 = self.machine.mn blocks, loc_db = parse_asm.parse_txt(mn_x86, self.arch_attrib, self.TXT, loc_db=self.myjit.ir_arch.loc_db) # fix shellcode addr loc_db.set_location_offset(loc_db.get_name_location("main"), 0x0) output = StrPatchwork() patches = asm_resolve_final(mn_x86, blocks, loc_db) for offset, raw in patches.items(): output[offset] = raw self.assembly = str(output)
def assemble_text(src_text, symbols=[], mach_name="x86_64", mach_attr=64): # 指定アーキテクチャのニーモニックを取得 mnemo = Machine(mach_name).mn # セクションとシンボルの取得 sections, symbol_pool = parse_asm.parse_txt(mnemo, mach_attr, src_text) # シンボル毎のアドレスを設定 for name, addr in symbols: symbol_pool.set_offset(symbol_pool.getby_name(name), addr) # アセンブル patches = asmbloc.asm_resolve_final(mnemo, sections[0], symbol_pool) # アセンブル結果の構築 patch_worker = StrPatchwork() for offset, raw in patches.items(): patch_worker[offset] = raw return str(patch_worker)
def asm(self): mn_x86 = self.machine.mn blocks, symbol_pool = parse_asm.parse_txt( mn_x86, self.arch_attrib, self.TXT, symbol_pool=self.myjit.ir_arch.symbol_pool ) # fix shellcode addr symbol_pool.set_offset(symbol_pool.getby_name("main"), 0x0) output = StrPatchwork() patches = asm_resolve_final(mn_x86, blocks, symbol_pool) for offset, raw in patches.items(): output[offset] = raw self.assembly = str(output)
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 allocate(self): for cs in self.code_signers: # Get fresh calculations of offset and size sig_offset = cs.calculate_sig_offset() sig_size = cs.get_size_estimate() sig_end = sig_offset + sig_size # Get the linkedit segment linkedit_seg = cs.get_linkedit_segment() linkedit_end = linkedit_seg.fileoff + linkedit_seg.filesize cmd = cs.get_sig_command() if cmd is not None: # Existing sig command. Get the CodeSignature section. It should be last one in the binary. cs_sec = cs.macho.sect[-1] sig_size = sig_size if sig_size > cmd.datasize else cmd.datasize else: # No existing sig command, so add one. cmd = linkedit_data_command(cmd=LC_CODE_SIGNATURE, parent=cs.macho.load) # Add the load command cs.macho.load.append(cmd) # Create a CodeSignature section cs_sec = CodeSignature(parent=cmd) # Add it to the binary # Note, don't use linkedit_seg.addSH because linkedit doesn't actually have segments. linkedit_seg.sect.append(cs_sec) cs.macho.sect.add(cs_sec) # Set the size, offset, and empty content of the CodeSignature section cmd.dataoff = sig_offset cmd.datasize = sig_size cs_sec.size = sig_size cs_sec.offset = sig_offset cs_sec.content = StrPatchwork(sig_size * b"\x00") # increase linkedit size end_diff = sig_end - linkedit_end if end_diff > 0: linkedit_seg.filesize += end_diff linkedit_seg.vmsize = round_up(linkedit_seg.filesize, cs.page_size)
def __init__(self, elfstr=None, **kargs): self._virt = virt(self) if elfstr is None: # Create an ELF file, with default header values # kargs can supersede these default values self.wsize = kargs.get('wsize', 32) self.sex = kargs.get('sex', '<') self.Ehdr = elf.Ehdr(parent=self) self.Ehdr.ident = struct.pack( "16B", 0x7f, 0x45, 0x4c, 0x46, # magic number, \x7fELF { 32: 1, 64: 2 }[self.wsize], # EI_CLASS { '<': 1, '>': 2 }[self.sex], # EI_DATA 1, # EI_VERSION 0, # EI_OSABI 0, # EI_ABIVERSION 0, 0, 0, 0, 0, 0, 0) self.Ehdr.version = 1 self.Ehdr.type = kargs.get('e_type', elf.ET_REL) self.Ehdr.machine = kargs.get('e_machine', elf.EM_386) self.Ehdr.ehsize = self.Ehdr.bytelen self.sh = SHList(self) self.ph = PHList(self) elf_default_content(self, **kargs) return self.content = StrPatchwork(elfstr) self.parse_content() self.check_coherency()
def __init__(self, minidump_str): self._content = StrPatchwork(minidump_str) # Specific streams self.modulelist = None self.memory64list = None self.memorylist = None self.memoryinfolist = None self.systeminfo = None # Get information self.streams = [] self.threads = None self.parse_content() # Memory information self.memory = {} # base address (virtual) -> Memory information self.build_memory() self.virt = ContentVirtual(self)
def test_COFF_invalidity(assertion): global log_history # Some various ways for a COFF to be detected as invalid obj_mingw = open(__dir__+'/binary_input/coff_mingw.obj', 'rb').read() obj_mingw = StrPatchwork(obj_mingw) e = COFF(obj_mingw) try: obj_mingw[2] = struct.pack("<H", 0) e = COFF(obj_mingw) assertion(0,1, 'COFF cannot have no section') except ValueError: pass try: obj_mingw[2] = struct.pack("<H", 0x2000) e = COFF(obj_mingw) assertion(0,1, 'Too many sections in COFF') except ValueError: pass try: obj_mingw[2] = struct.pack("<H", 0x100) e = COFF(obj_mingw) assertion(0,1, 'Too many sections in COFF, past end of file') except ValueError: pass try: obj_mingw[2] = struct.pack("<H", 3) obj_mingw[8] = struct.pack("<I", 0x100000) e = COFF(obj_mingw) assertion(0,1, 'COFF invalid ptr to symbol table') except ValueError: pass obj_mingw[8] = struct.pack("<I", 220) obj_mingw[436] = struct.pack("<I", 10000) e = COFF(obj_mingw) assertion([('warn', ('File too short for StrTable 0x4 != 0x2710',), {})], log_history, 'File too short for StrTable (logs)') log_history = [] assertion([], log_history, 'No non-regression test created unwanted log messages')
def __init__(self, parent, **kargs): self.parent = parent inheritsexwsize(self, parent, kargs) self.shlist = [] ehdr = self.parent.Ehdr of1 = ehdr.shoff if not of1: # No SH table return for i in range(ehdr.shnum): of2 = of1 + ehdr.shentsize shstr = parent[of1:of2] self.shlist.append(Section.create(self, shstr=shstr)) of1 = of2 # The shstrtab section is not always valid :-( self._shstrtab = self.shlist[ehdr.shstrndx] if not isinstance(self._shstrtab, StrTable): class NoStrTab(object): def get_name(self, idx): return "<no-name>" self._shstrtab = NoStrTab() for s in self.shlist: if not isinstance(s, NoBitsSection): s._content = StrPatchwork(parent[s.sh.offset:s.sh.offset + s.sh.size]) # Follow dependencies when initializing sections zero = self.shlist[0] todo = self.shlist[1:] done = [] while todo: s = todo.pop(0) if ((s.linksection in done + [zero, None]) and (s.infosection in [zero, None] or s.infosection in done)): done.append(s) s.parse_content() else: todo.append(s) for s in self.shlist: self.do_add_section(s)
class LinkEditSection(BaseSection): type = 'data' def unpack(self, c, o): if self.parent is not None: assert o == self.offset self.content = StrPatchwork(c[o:o + self.size]) def get_size(self): return getattr(self.parent, self.type + 'size') def set_size(self, val): setattr(self.parent, self.type + 'size', val) size = property(get_size, set_size) addr = property(lambda _: 0) def pack(self): return self.content.pack() def __str__(self): return "%-30s %-10s %#010x %#010x" % (self.__class__.__name__, '', self.offset, self.size)
new_dll = [({ "name": "USER32.dll", "firstthunk": s_iat.addr }, ["MessageBoxA"])] pe.DirImport.add_dlldesc(new_dll) s_myimp = pe.SHList.add_section(name="myimp", rawsize=len(pe.DirImport)) pe.DirImport.set_rva(s_myimp.addr) pe.Opthdr.AddressOfEntryPoint = s_text.addr addr_main = pe.rva2virt(s_text.addr) virt = pe.virt output = pe dst_interval = interval([(pe.rva2virt(s_text.addr), pe.rva2virt(s_text.addr + s_text.size))]) else: st = StrPatchwork() addr_main = 0 virt = st output = st # Get and parse the source code with open(args.source) as fstream: source = fstream.read() blocks, symbol_pool = parse_asm.parse_txt(machine.mn, attrib, source) # Fix shellcode addrs symbol_pool.set_offset(symbol_pool.getby_name("main"), addr_main) if args.PE:
def set_content(self, val): self.__content = StrPatchwork(val)
def unpack(self, c, o): if self.parent is not None: assert o == self.offset self.content = StrPatchwork(c[o:o + self.size])
parser.add_argument('-v', "--verbose", help="verbose mode", action="store_true") options = parser.parse_args() if options.endianness == 'b': sandbox = Sandbox_Linux_armb_str elif options.endianness == 'l': sandbox = Sandbox_Linux_arml_str else: raise ValueError("Bad endianness!") sb = sandbox(options.filename, options, globals()) if options.address is None: raise ValueError('invalid address') sb.run() # test correct de xor start = sb.jitter.cpu.R0 stop = sb.jitter.cpu.R1 s = sb.jitter.vm.get_mem(start, stop-start) s = StrPatchwork(s) for i, c in enumerate(s): s[i] = chr(ord(c)^0x11) s = str(s) assert(s == "test string\x00")