diff options
Diffstat (limited to 'src/miasm/loader/pe.py')
| -rw-r--r-- | src/miasm/loader/pe.py | 1693 |
1 files changed, 1693 insertions, 0 deletions
diff --git a/src/miasm/loader/pe.py b/src/miasm/loader/pe.py new file mode 100644 index 00000000..1252e70e --- /dev/null +++ b/src/miasm/loader/pe.py @@ -0,0 +1,1693 @@ +#! /usr/bin/env python + +from __future__ import print_function +from builtins import range, str +from collections import defaultdict +import logging +import struct + +from future.builtins import int as int_types +from future.utils import PY3 + +from miasm.core.utils import force_bytes +from miasm.loader.new_cstruct import CStruct +from miasm.loader.strpatchwork import StrPatchwork + +log = logging.getLogger("pepy") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s")) +log.addHandler(console_handler) +log.setLevel(logging.WARN) + + +class InvalidOffset(Exception): + pass + + +class Doshdr(CStruct): + _fields = [("magic", "u16"), + ("cblp", "u16"), + ("cp", "u16"), + ("crlc", "u16"), + ("cparhdr", "u16"), + ("minalloc", "u16"), + ("maxalloc", "u16"), + ("ss", "u16"), + ("sp", "u16"), + ("csum", "u16"), + ("ip", "u16"), + ("cs", "u16"), + ("lfarlc", "u16"), + ("ovno", "u16"), + ("res", "8s"), + ("oemid", "u16"), + ("oeminfo", "u16"), + ("res2", "20s"), + ("lfanew", "u32")] + + +class NTsig(CStruct): + _fields = [("signature", "u32"), + ] + + +class Coffhdr(CStruct): + _fields = [("machine", "u16"), + ("numberofsections", "u16"), + ("timedatestamp", "u32"), + ("pointertosymboltable", "u32"), + ("numberofsymbols", "u32"), + ("sizeofoptionalheader", "u16"), + ("characteristics", "u16")] + + +class Optehdr(CStruct): + _fields = [("rva", "u32"), + ("size", "u32")] + + +def get_optehdr_num(nthdr): + numberofrva = nthdr.numberofrvaandsizes + parent = nthdr.parent_head + entry_size = 8 + if parent.Coffhdr.sizeofoptionalheader < numberofrva * entry_size + len(parent.Opthdr): + numberofrva = (parent.Coffhdr.sizeofoptionalheader - len(parent.Opthdr)) // entry_size + log.warn('Bad number of rva.. using default %d' % numberofrva) + numberofrva = 0x10 + return numberofrva + + +class Opthdr32(CStruct): + _fields = [("magic", "u16"), + ("majorlinkerversion", "u08"), + ("minorlinkerversion", "u08"), + ("SizeOfCode", "u32"), + ("sizeofinitializeddata", "u32"), + ("sizeofuninitializeddata", "u32"), + ("AddressOfEntryPoint", "u32"), + ("BaseOfCode", "u32"), + ("BaseOfData", "u32"), + ] + + +class Opthdr64(CStruct): + _fields = [("magic", "u16"), + ("majorlinkerversion", "u08"), + ("minorlinkerversion", "u08"), + ("SizeOfCode", "u32"), + ("sizeofinitializeddata", "u32"), + ("sizeofuninitializeddata", "u32"), + ("AddressOfEntryPoint", "u32"), + ("BaseOfCode", "u32"), + ] + + +class NThdr(CStruct): + _fields = [("ImageBase", "ptr"), + ("sectionalignment", "u32"), + ("filealignment", "u32"), + ("majoroperatingsystemversion", "u16"), + ("minoroperatingsystemversion", "u16"), + ("MajorImageVersion", "u16"), + ("MinorImageVersion", "u16"), + ("majorsubsystemversion", "u16"), + ("minorsubsystemversion", "u16"), + ("Reserved1", "u32"), + ("sizeofimage", "u32"), + ("sizeofheaders", "u32"), + ("CheckSum", "u32"), + ("subsystem", "u16"), + ("dllcharacteristics", "u16"), + ("sizeofstackreserve", "ptr"), + ("sizeofstackcommit", "ptr"), + ("sizeofheapreserve", "ptr"), + ("sizeofheapcommit", "ptr"), + ("loaderflags", "u32"), + ("numberofrvaandsizes", "u32"), + ("optentries", "Optehdr", lambda c:get_optehdr_num(c)) + ] + + +class Shdr(CStruct): + _fields = [("name", "8s"), + ("size", "u32"), + ("addr", "u32"), + ("rawsize", "u32"), + ("offset", "u32"), + ("pointertorelocations", "u32"), + ("pointertolinenumbers", "u32"), + ("numberofrelocations", "u16"), + ("numberoflinenumbers", "u16"), + ("flags", "u32")] + + + def get_data(self): + parent = self.parent_head + data = parent.img_rva[self.addr:self.addr + self.size] + return data + + def set_data(self, data): + parent = self.parent_head + parent.img_rva[self.addr] = data + + + data = property(get_data, set_data) + +class SHList(CStruct): + _fields = [ + ("shlist", "Shdr", lambda c:c.parent_head.Coffhdr.numberofsections)] + + def add_section(self, name="default", data=b"", **args): + s_align = self.parent_head.NThdr.sectionalignment + s_align = max(0x1000, s_align) + + f_align = self.parent_head.NThdr.filealignment + f_align = max(0x200, f_align) + size = len(data) + rawsize = len(data) + if len(self): + addr = self[-1].addr + self[-1].size + s_last = self[0] + for section in self: + if s_last.offset + s_last.rawsize < section.offset + section.rawsize: + s_last = section + offset = s_last.offset + s_last.rawsize + else: + s_null = bytes(Shdr.unpack(b"\x00" * 0x100)) + offset = self.parent_head.Doshdr.lfanew + len(self.parent_head.NTsig) + len( + self.parent_head.Coffhdr) + self.parent_head.Coffhdr.sizeofoptionalheader + len(bytes(self.parent_head.SHList) + s_null) + addr = 0x2000 + # round addr + addr = (addr + (s_align - 1)) & ~(s_align - 1) + offset = (offset + (f_align - 1)) & ~(f_align - 1) + + attrs = {"name": name, "size": size, + "addr": addr, "rawsize": rawsize, + "offset": offset, + "pointertorelocations": 0, + "pointertolinenumbers": 0, + "numberofrelocations": 0, + "numberoflinenumbers": 0, + "flags": 0xE0000020, + "data": data + } + attrs.update(args) + section = Shdr(self.parent_head, _sex=self.parent_head._sex, + _wsize=self.parent_head._wsize, **attrs) + section.data = data + + if section.rawsize > len(data): + section.data = section.data + b'\x00' * (section.rawsize - len(data)) + section.size = section.rawsize + section.data = bytes(StrPatchwork(section.data)) + section.size = max(s_align, section.size) + + self.append(section) + self.parent_head.Coffhdr.numberofsections = len(self) + + length = (section.addr + section.size + (s_align - 1)) & ~(s_align - 1) + self.parent_head.NThdr.sizeofimage = length + return section + + def align_sections(self, f_align=None, s_align=None): + if f_align == None: + f_align = self.parent_head.NThdr.filealignment + f_align = max(0x200, f_align) + if s_align == None: + s_align = self.parent_head.NThdr.sectionalignment + s_align = max(0x1000, s_align) + + if self is None: + return + + addr = self[0].offset + for section in self: + raw_off = f_align * ((addr + f_align - 1) // f_align) + section.offset = raw_off + section.rawsize = len(section.data) + addr = raw_off + section.rawsize + + def __repr__(self): + rep = ["# section offset size addr flags rawsize "] + for i, section in enumerate(self): + name = force_bytes(section.name) + out = "%-15s" % name.strip(b'\x00').decode() + out += "%(offset)08x %(size)06x %(addr)08x %(flags)08x %(rawsize)08x" % section + out = ("%2i " % i) + out + rep.append(out) + return "\n".join(rep) + + def __getitem__(self, item): + return self.shlist[item] + + def __len__(self): + return len(self.shlist) + + def append(self, section): + self.shlist.append(section) + + +class Rva(CStruct): + _fields = [("rva", "ptr"), + ] + + +class Rva32(CStruct): + _fields = [("rva", "u32"), + ] + + +class DescName(CStruct): + _fields = [("name", (lambda c, raw, off: c.gets(raw, off), + lambda c, value: c.sets(value))) + ] + + def gets(self, raw, off): + name = raw[off:raw.find(b'\x00', off)] + return name, off + len(name) + 1 + + def sets(self, value): + return force_bytes(value) + b"\x00" + + +class ImportByName(CStruct): + _fields = [("hint", "u16"), + ("name", "sz") + ] + + +class ImpDesc_e(CStruct): + _fields = [("originalfirstthunk", "u32"), + ("timestamp", "u32"), + ("forwarderchain", "u32"), + ("name", "u32"), + ("firstthunk", "u32") + ] + + +class struct_array(object): + + def __init__(self, target_class, raw, off, cstr, num=None): + self.l = [] + self.cls = target_class + self.end = None + i = 0 + if not raw: + return + + while (num == None) or (num and i < num): + entry, length = cstr.unpack_l(raw, off, + target_class.parent_head, + target_class.parent_head._sex, + target_class.parent_head._wsize) + if num == None: + if raw[off:off + length] == b'\x00' * length: + self.end = b'\x00' * length + break + self.l.append(entry) + off += length + i += 1 + + def __bytes__(self): + out = b"".join(bytes(x) for x in self.l) + if self.end is not None: + out += self.end + return out + + def __str__(self): + if PY3: + return repr(self) + return self.__bytes__() + + def __getitem__(self, item): + return self.l.__getitem__(item) + + def __len__(self): + return len(self.l) + + def append(self, entry): + self.l.append(entry) + + def insert(self, index, entry): + self.l.insert(index, entry) + + +class DirImport(CStruct): + _fields = [("impdesc", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + if not off: + return None, off + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 + + ofend = off + \ + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_IMPORT].size + out = [] + while off < ofend: + if not 0 <= off < len(self.parent_head.img_rva): + break + imp, length = ImpDesc_e.unpack_l(raw, off) + if (raw[off:off+length] == b'\x00' * length or + imp.name == 0): + # Special case + break + if not (imp.originalfirstthunk or imp.firstthunk): + log.warning("no thunk!!") + break + + out.append(imp) + off += length + imp.dlldescname = DescName.unpack(raw, imp.name, self.parent_head) + if imp.originalfirstthunk and imp.originalfirstthunk < len(self.parent_head.img_rva): + imp.originalfirstthunks = struct_array(self, raw, + imp.originalfirstthunk, + Rva) + else: + imp.originalfirstthunks = None + + if imp.firstthunk and imp.firstthunk < len(self.parent_head.img_rva): + imp.firstthunks = struct_array(self, raw, + imp.firstthunk, + Rva) + else: + imp.firstthunks = None + imp.impbynames = [] + if imp.originalfirstthunk and imp.originalfirstthunk < len(self.parent_head.img_rva): + tmp_thunk = imp.originalfirstthunks + elif imp.firstthunk: + tmp_thunk = imp.firstthunks + for i in range(len(tmp_thunk)): + if tmp_thunk[i].rva & mask_ptr == 0: + try: + entry = ImportByName.unpack(raw, + tmp_thunk[i].rva, + self.parent_head) + except: + log.warning( + 'cannot import from add %s' % tmp_thunk[i].rva + ) + entry = 0 + imp.impbynames.append(entry) + else: + imp.impbynames.append(tmp_thunk[i].rva & (mask_ptr - 1)) + return out, off + + def sete(self, entries): + return b"".join(bytes(entry) for entry in entries) + b"\x00" * (4 * 5) + + def __len__(self): + length = (len(self.impdesc) + 1) * (5 * 4) # ImpDesc_e size + rva_size = self.parent_head._wsize // 8 + for entry in self.impdesc: + length += len(entry.dlldescname) + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + length += (len(entry.originalfirstthunks) + 1) * rva_size + if entry.firstthunk: + length += (len(entry.firstthunks) + 1) * rva_size + for imp in entry.impbynames: + if isinstance(imp, ImportByName): + length += len(imp) + return length + + def set_rva(self, rva, size=None): + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_IMPORT].rva = rva + rva_size = self.parent_head._wsize // 8 + if not size: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_IMPORT].size = len(self) + else: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_IMPORT].size = size + rva += (len(self.impdesc) + 1) * 5 * 4 # ImpDesc size + for entry in self.impdesc: + entry.name = rva + rva += len(entry.dlldescname) + if entry.originalfirstthunk: # and self.parent_head.rva2off(entry.originalfirstthunk): + entry.originalfirstthunk = rva + rva += (len(entry.originalfirstthunks) + 1) * rva_size + # XXX rva fthunk not patched => keep original func addr + # if entry.firstthunk: + # entry.firstthunk = rva + # rva+=(len(entry.firstthunks)+1)*self.parent_head._wsize//8 # Rva size + if entry.originalfirstthunk and entry.firstthunk: + if isinstance(entry.originalfirstthunks, struct_array): + tmp_thunk = entry.originalfirstthunks + elif isinstance(entry.firstthunks, struct_array): + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + elif entry.originalfirstthunk: # and self.parent_head.rva2off(entry.originalfirstthunk): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + + if tmp_thunk == entry.originalfirstthunks: + entry.firstthunks = tmp_thunk + else: + entry.originalfirstthunks = tmp_thunk + for i, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + tmp_thunk[i].rva = rva + rva += len(imp) + + def build_content(self, raw): + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 + + dirimp = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_IMPORT] + of1 = dirimp.rva + if not of1: # No Import + return + raw[self.parent_head.rva2off(of1)] = bytes(self) + for entry in self.impdesc: + raw[self.parent_head.rva2off(entry.name)] = bytes(entry.dlldescname) + if (entry.originalfirstthunk and + self.parent_head.rva2off(entry.originalfirstthunk)): + # Add thunks list and terminating null entry + off = self.parent_head.rva2off(entry.originalfirstthunk) + raw[off] = bytes(entry.originalfirstthunks) + if entry.firstthunk: + # Add thunks list and terminating null entry + off = self.parent_head.rva2off(entry.firstthunk) + raw[off] = bytes(entry.firstthunks) + if (entry.originalfirstthunk and + self.parent_head.rva2off(entry.originalfirstthunk)): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + for j, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + raw[self.parent_head.rva2off(tmp_thunk[j].rva)] = bytes(imp) + + def get_dlldesc(self): + out = [] + for impdesc in self.impdesc: + dllname = impdesc.dlldescname.name + funcs = [] + for imp in impdesc.impbynames: + if isinstance(imp, ImportByName): + funcs.append(imp.name) + else: + funcs.append(imp) + entry = ({"name": dllname, "firstthunk": impdesc.firstthunk}, funcs) + out.append(entry) + return out + + def __repr__(self): + rep = ["<%s>" % self.__class__.__name__] + for i, entry in enumerate(self.impdesc): + out = "%2d %-25s %s" % (i, repr(entry.dlldescname), repr(entry)) + rep.append(out) + for index, imp in enumerate(entry.impbynames): + out = " %2d %-16s" % (index, repr(imp)) + rep.append(out) + return "\n".join(rep) + + def add_dlldesc(self, new_dll): + rva_size = self.parent_head._wsize // 8 + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 + new_impdesc = [] + of1 = None + for import_descriptor, new_functions in new_dll: + if isinstance(import_descriptor.get("name"), str): + import_descriptor["name"] = import_descriptor["name"].encode() + new_functions = [ + funcname.encode() if isinstance(funcname, str) else funcname + for funcname in new_functions + ] + for attr in ["timestamp", "forwarderchain", "originalfirstthunk"]: + if attr not in import_descriptor: + import_descriptor[attr] = 0 + entry = ImpDesc_e(self.parent_head, **import_descriptor) + if entry.firstthunk != None: + of1 = entry.firstthunk + elif of1 == None: + raise RuntimeError("set fthunk") + else: + entry.firstthunk = of1 + entry.dlldescname = DescName(self.parent_head, name=entry.name) + entry.originalfirstthunk = 0 + entry.originalfirstthunks = struct_array(self, None, + None, + Rva) + entry.firstthunks = struct_array(self, None, + None, + Rva) + + impbynames = [] + for new_function in new_functions: + rva_ofirstt = Rva(self.parent_head) + if isinstance(new_function, int_types): + rva_ofirstt.rva = mask_ptr + new_function + ibn = new_function + elif isinstance(new_function, bytes): + rva_ofirstt.rva = True + ibn = ImportByName(self.parent_head) + ibn.name = new_function + ibn.hint = 0 + else: + raise RuntimeError('unknown func type %s' % new_function) + impbynames.append(ibn) + entry.originalfirstthunks.append(rva_ofirstt) + rva_func = Rva(self.parent_head) + if isinstance(ibn, ImportByName): + rva_func.rva = 0xDEADBEEF # default func addr + else: + # ord ?XXX? + rva_func.rva = rva_ofirstt.rva + entry.firstthunks.append(rva_func) + of1 += rva_size + # for null thunk + of1 += rva_size + entry.impbynames = impbynames + new_impdesc.append(entry) + if self.impdesc is None: + self.impdesc = struct_array(self, None, + None, + ImpDesc_e) + self.impdesc.l = new_impdesc + else: + for entry in new_impdesc: + self.impdesc.append(entry) + + def get_funcrva(self, dllname, funcname): + dllname = force_bytes(dllname) + funcname = force_bytes(funcname) + + rva_size = self.parent_head._wsize // 8 + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 - 1 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 - 1 + + for entry in self.impdesc: + if entry.dlldescname.name.lower() != dllname.lower(): + continue + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + if isinstance(funcname, bytes): + for j, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + if funcname == imp.name: + return entry.firstthunk + j * rva_size + elif isinstance(funcname, int_types): + for j, imp in enumerate(entry.impbynames): + if not isinstance(imp, ImportByName): + if tmp_thunk[j].rva & mask_ptr == funcname: + return entry.firstthunk + j * rva_size + else: + raise ValueError('Unknown: %s %s' % (dllname, funcname)) + + def get_funcvirt(self, dllname, funcname): + rva = self.get_funcrva(dllname, funcname) + if rva == None: + return + return self.parent_head.rva2virt(rva) + + +class ExpDesc_e(CStruct): + _fields = [("characteristics", "u32"), + ("timestamp", "u32"), + ("majorv", "u16"), + ("minorv", "u16"), + ("name", "u32"), + ("base", "u32"), + ("numberoffunctions", "u32"), + ("numberofnames", "u32"), + ("addressoffunctions", "u32"), + ("addressofnames", "u32"), + ("addressofordinals", "u32"), + ] + + +class DirExport(CStruct): + _fields = [("expdesc", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + off_o = off + if not off: + return None, off + off_sav = off + if off >= len(raw): + log.warn("export dir malformed!") + return None, off_o + expdesc = ExpDesc_e.unpack(raw, + off, + self.parent_head) + if self.parent_head.rva2off(expdesc.addressoffunctions) == None or \ + self.parent_head.rva2off(expdesc.addressofnames) == None or \ + self.parent_head.rva2off(expdesc.addressofordinals) == None: + log.warn("export dir malformed!") + return None, off_o + self.dlldescname = DescName.unpack(raw, expdesc.name, self.parent_head) + try: + self.f_address = struct_array(self, raw, + expdesc.addressoffunctions, + Rva32, expdesc.numberoffunctions) + self.f_names = struct_array(self, raw, + expdesc.addressofnames, + Rva32, expdesc.numberofnames) + self.f_nameordinals = struct_array(self, raw, + expdesc.addressofordinals, + Ordinal, expdesc.numberofnames) + except RuntimeError: + log.warn("export dir malformed!") + return None, off_o + for func in self.f_names: + func.name = DescName.unpack(raw, func.rva, self.parent_head) + return expdesc, off_sav + + def sete(self, _): + return bytes(self.expdesc) + + def build_content(self, raw): + direxp = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_EXPORT] + of1 = direxp.rva + if self.expdesc is None: # No Export + return + raw[self.parent_head.rva2off(of1)] = bytes(self.expdesc) + raw[self.parent_head.rva2off(self.expdesc.name)] = bytes(self.dlldescname) + raw[self.parent_head.rva2off(self.expdesc.addressoffunctions)] = bytes(self.f_address) + if self.expdesc.addressofnames != 0: + raw[self.parent_head.rva2off(self.expdesc.addressofnames)] = bytes(self.f_names) + if self.expdesc.addressofordinals != 0: + raw[self.parent_head.rva2off(self.expdesc.addressofordinals)] = bytes(self.f_nameordinals) + for func in self.f_names: + raw[self.parent_head.rva2off(func.rva)] = bytes(func.name) + + # XXX BUG names must be alphanumeric ordered + names = [func.name for func in self.f_names] + names_ = names[:] + if names != names_: + log.warn("unsorted export names, may bug") + + def set_rva(self, rva, size=None): + rva_size = self.parent_head._wsize // 8 + if self.expdesc is None: + return + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_EXPORT].rva = rva + if not size: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_EXPORT].size = len(self) + else: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_EXPORT].size = size + rva += len(self.expdesc) + self.expdesc.name = rva + rva += len(self.dlldescname) + self.expdesc.addressoffunctions = rva + rva += len(self.f_address) * 4 + self.expdesc.addressofnames = rva + rva += len(self.f_names) * 4 + self.expdesc.addressofordinals = rva + rva += len(self.f_nameordinals) * 2 # Ordinal size + for func in self.f_names: + func.rva = rva + rva += len(func.name) + + def __len__(self): + rva_size = self.parent_head._wsize // 8 + length = 0 + if self.expdesc is None: + return length + length += len(self.expdesc) + length += len(self.dlldescname) + length += len(self.f_address) * 4 + length += len(self.f_names) * 4 + length += len(self.f_nameordinals) * 2 # Ordinal size + for entry in self.f_names: + length += len(entry.name) + return length + + def __repr__(self): + rep = ["<%s>" % self.__class__.__name__] + if self.expdesc is None: + return "\n".join(rep) + + rep = ["<%s %d (%s) %s>" % (self.__class__.__name__, + self.expdesc.numberoffunctions, self.dlldescname, repr(self.expdesc))] + tmp_names = [[] for _ in range(self.expdesc.numberoffunctions)] + for i, entry in enumerate(self.f_names): + tmp_names[self.f_nameordinals[i].ordinal].append(entry.name) + for i, entry in enumerate(self.f_address): + tmpn = [] + if not entry.rva: + continue + out = "%2d %.8X %s" % (i + self.expdesc.base, entry.rva, repr(tmp_names[i])) + rep.append(out) + return "\n".join(rep) + + def create(self, name='default.dll'): + self.expdesc = ExpDesc_e(self.parent_head) + for attr in ["characteristics", + "timestamp", + "majorv", + "minorv", + "name", + "base", + "numberoffunctions", + "numberofnames", + "addressoffunctions", + "addressofnames", + "addressofordinals", + ]: + setattr(self.expdesc, attr, 0) + + self.dlldescname = DescName(self.parent_head) + self.dlldescname.name = name + self.f_address = struct_array(self, None, + None, + Rva32) + self.f_names = struct_array(self, None, + None, + Rva32) + self.f_nameordinals = struct_array(self, None, + None, + Ordinal) + self.expdesc.base = 1 + + def add_name(self, name, rva=0xdeadc0fe, ordinal=None): + if self.expdesc is None: + return + names = [func.name.name for func in self.f_names] + names_s = names[:] + names_s.sort() + if names_s != names: + log.warn('tab names was not sorted may bug') + names.append(name) + names.sort() + index = names.index(name) + descname = DescName(self.parent_head) + + descname.name = name + wname = Rva32(self.parent_head) + + wname.name = descname + woffset = Rva32(self.parent_head) + woffset.rva = rva + wordinal = Ordinal(self.parent_head) + # func is append to list + if ordinal is None: + wordinal.ordinal = len(self.f_address) + else: + wordinal.ordinal = ordinal + + self.f_address.append(woffset) + # self.f_names.insert(index, wname) + # self.f_nameordinals.insert(index, wordinal) + self.f_names.insert(index, wname) + self.f_nameordinals.insert(index, wordinal) + self.expdesc.numberofnames += 1 + self.expdesc.numberoffunctions += 1 + + def get_funcrva(self, f_str): + if self.expdesc is None: + return None + for i, entry in enumerate(self.f_names): + if f_str != entry.name.name: + continue + ordinal = self.f_nameordinals[i].ordinal + rva = self.f_address[ordinal].rva + return rva + return None + + def get_funcvirt(self, addr): + rva = self.get_funcrva(addr) + if rva == None: + return + return self.parent_head.rva2virt(rva) + + +class Delaydesc_e(CStruct): + _fields = [("attrs", "u32"), + ("name", "u32"), + ("hmod", "u32"), + ("firstthunk", "u32"), + ("originalfirstthunk", "u32"), + ("boundiat", "u32"), + ("unloadiat", "u32"), + ("timestamp", "u32"), + ] + + +class DirDelay(CStruct): + _fields = [("delaydesc", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + if not off: + return None, off + + ofend = off + \ + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_DELAY_IMPORT].size + out = [] + while off < ofend: + if off >= len(raw): + log.warn('warning bad reloc offset') + break + + delaydesc, length = Delaydesc_e.unpack_l(raw, + off, + self.parent_head) + if raw[off:off+length] == b'\x00' * length: + # Special case + break + off += length + out.append(delaydesc) + + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 + + parent = self.parent_head + for entry in out: + isfromva = (entry.attrs & 1) == 0 + if isfromva: + isfromva = lambda x: parent.virt2rva(x) + else: + isfromva = lambda x: x + entry.dlldescname = DescName.unpack(raw, isfromva(entry.name), + self.parent_head) + if entry.originalfirstthunk: + addr = isfromva(entry.originalfirstthunk) + if not 0 <= addr < len(raw): + log.warning("Bad delay") + break + entry.originalfirstthunks = struct_array(self, raw, + addr, + Rva) + else: + entry.originalfirstthunks = None + + if entry.firstthunk: + entry.firstthunks = struct_array(self, raw, + isfromva(entry.firstthunk), + Rva) + else: + entry.firstthunk = None + + entry.impbynames = [] + if entry.originalfirstthunk and self.parent_head.rva2off(isfromva(entry.originalfirstthunk)): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + print(ValueError("no thunk in delay dir!! ")) + return + for i in range(len(tmp_thunk)): + if tmp_thunk[i].rva & mask_ptr == 0: + imp = ImportByName.unpack(raw, + isfromva(tmp_thunk[i].rva), + self.parent_head) + entry.impbynames.append(imp) + else: + entry.impbynames.append( + isfromva(tmp_thunk[i].rva & (mask_ptr - 1))) + # print(repr(entry[-1])) + # raise ValueError('XXX to check') + return out, off + + def sete(self, entries): + return b"".join(bytes(entry) for entry in entries) + b"\x00" * (4 * 8) # DelayDesc_e + + def __len__(self): + rva_size = self.parent_head._wsize // 8 + length = (len(self.delaydesc) + 1) * (4 * 8) # DelayDesc_e + for entry in self.delaydesc: + length += len(entry.dlldescname) + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + length += (len(entry.originalfirstthunks) + 1) * rva_size + if entry.firstthunk: + length += (len(entry.firstthunks) + 1) * rva_size + for imp in entry.impbynames: + if isinstance(imp, ImportByName): + length += len(imp) + return length + + def set_rva(self, rva, size=None): + rva_size = self.parent_head._wsize // 8 + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_DELAY_IMPORT].rva = rva + if not size: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_DELAY_IMPORT].size = len(self) + else: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_DELAY_IMPORT].size = size + rva += (len(self.delaydesc) + 1) * (4 * 8) # DelayDesc_e + parent = self.parent_head + for entry in self.delaydesc: + isfromva = (entry.attrs & 1) == 0 + if isfromva: + isfromva = lambda x: self.parent_head.rva2virt(x) + else: + isfromva = lambda x: x + + entry.name = isfromva(rva) + rva += len(entry.dlldescname) + if entry.originalfirstthunk: # and self.parent_head.rva2off(entry.originalfirstthunk): + entry.originalfirstthunk = isfromva(rva) + rva += (len(entry.originalfirstthunks) + 1) * rva_size + # XXX rva fthunk not patched => fun addr + # if entry.firstthunk: + # entry.firstthunk = rva + # rva+=(len(entry.firstthunks)+1)*pe.Rva._size + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + for i, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + tmp_thunk[i].rva = isfromva(rva) + rva += len(imp) + + def build_content(self, raw): + if len(self.parent_head.NThdr.optentries) < DIRECTORY_ENTRY_DELAY_IMPORT: + return + dirdelay = self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_DELAY_IMPORT] + of1 = dirdelay.rva + if not of1: # No Delay Import + return + raw[self.parent_head.rva2off(of1)] = bytes(self) + for entry in self.delaydesc: + raw[self.parent_head.rva2off(entry.name)] = bytes(entry.dlldescname) + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + raw[self.parent_head.rva2off(entry.originalfirstthunk)] = bytes(entry.originalfirstthunks) + if entry.firstthunk: + raw[self.parent_head.rva2off(entry.firstthunk)] = bytes(entry.firstthunks) + if entry.originalfirstthunk and self.parent_head.rva2off(entry.originalfirstthunk): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + for j, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + raw[self.parent_head.rva2off(tmp_thunk[j].rva)] = bytes(imp) + + def __repr__(self): + rep = ["<%s>" % self.__class__.__name__] + for i, entry in enumerate(self.delaydesc): + out = "%2d %-25s %s" % (i, repr(entry.dlldescname), repr(entry)) + rep.append(out) + for index, func in enumerate(entry.impbynames): + out = " %2d %-16s" % (index, repr(func)) + rep.append(out) + return "\n".join(rep) + + def add_dlldesc(self, new_dll): + if self.parent_head._wsize == 32: + mask_ptr = 0x80000000 + elif self.parent_head._wsize == 64: + mask_ptr = 0x8000000000000000 + new_impdesc = [] + of1 = None + new_delaydesc = [] + for import_descriptor, new_functions in new_dll: + if isinstance(import_descriptor.get("name"), str): + import_descriptor["name"] = import_descriptor["name"].encode() + new_functions = [ + funcname.encode() if isinstance(funcname, str) else funcname + for funcname in new_functions + ] + for attr in ["attrs", "name", "hmod", "firstthunk", "originalfirstthunk", "boundiat", "unloadiat", "timestamp"]: + if not attr in import_descriptor: + import_descriptor[attr] = 0 + entry = Delaydesc_e(self.parent_head, **import_descriptor) + # entry.cstr.__dict__.update(import_descriptor) + if entry.firstthunk != None: + of1 = entry.firstthunk + elif of1 == None: + raise RuntimeError("set fthunk") + else: + entry.firstthunk = of1 + entry.dlldescname = DescName(self.parent_head, name=entry.name) + entry.originalfirstthunk = 0 + entry.originalfirstthunks = struct_array(self, None, + None, + Rva) + entry.firstthunks = struct_array(self, None, + None, + Rva) + + impbynames = [] + for new_function in new_functions: + rva_ofirstt = Rva(self.parent_head) + if isinstance(new_function, int_types): + rva_ofirstt.rva = mask_ptr + new_function + ibn = None + elif isinstance(new_function, bytes): + rva_ofirstt.rva = True + ibn = ImportByName(self.parent_head) + ibn.name = new_function + ibn.hint = 0 + else: + raise RuntimeError('unknown func type %s' % new_function) + impbynames.append(ibn) + entry.originalfirstthunks.append(rva_ofirstt) + + rva_func = Rva(self.parent_head) + if ibn != None: + rva_func.rva = 0xDEADBEEF # default func addr + else: + # ord ?XXX? + rva_func.rva = rva_ofirstt.rva + entry.firstthunks.append(rva_func) + of1 += 4 + # for null thunk + of1 += 4 + entry.impbynames = impbynames + new_delaydesc.append(entry) + if self.delaydesc is None: + self.delaydesc = struct_array(self, None, + None, + Delaydesc_e) + self.delaydesc.l = new_delaydesc + else: + for entry in new_delaydesc: + self.delaydesc.append(entry) + + def get_funcrva(self, func): + for entry in self.delaydesc: + isfromva = (entry.attrs & 1) == 0 + if isfromva: + isfromva = lambda x: self.parent_head.virt2rva(x) + else: + isfromva = lambda x: x + if entry.originalfirstthunk and self.parent_head.rva2off(isfromva(entry.originalfirstthunk)): + tmp_thunk = entry.originalfirstthunks + elif entry.firstthunk: + tmp_thunk = entry.firstthunks + else: + raise RuntimeError("No thunk!") + if isinstance(func, bytes): + for j, imp in enumerate(entry.impbynames): + if isinstance(imp, ImportByName): + if func == imp.name: + return isfromva(entry.firstthunk) + j * 4 + elif isinstance(func, int_types): + for j, imp in enumerate(entry.impbynames): + if not isinstance(imp, ImportByName): + if isfromva(tmp_thunk[j].rva & 0x7FFFFFFF) == func: + return isfromva(entry.firstthunk) + j * 4 + else: + raise ValueError('unknown func type %r' % func) + + def get_funcvirt(self, addr): + rva = self.get_funcrva(addr) + if rva == None: + return + return self.parent_head.rva2virt(rva) + + +class Rel(CStruct): + _fields = [("rva", "u32"), + ("size", "u32") + ] + + +class Reloc(CStruct): + _fields = [("rel", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + rel = struct.unpack('H', raw[off:off + 2])[0] + return (rel >> 12, rel & 0xfff), off + 2 + + def sete(self, value): + return struct.pack('H', (value[0] << 12) | value[1]) + + def __repr__(self): + return '<%d %d>' % (self.rel[0], self.rel[1]) + + +class DirReloc(CStruct): + _fields = [("reldesc", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + if not off: + return None, off + + ofend = off + \ + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_BASERELOC].size + out = [] + while off < ofend: + if off >= len(raw): + log.warn('warning bad reloc offset') + break + reldesc, length = Rel.unpack_l(raw, + off, + self.parent_head) + if reldesc.size == 0: + log.warn('warning null reldesc') + reldesc.size = length + break + of2 = off + length + if of2 + reldesc.size > len(self.parent_head.img_rva): + log.warn('relocation too big, skipping') + break + reldesc.rels = struct_array(self, raw, + of2, + Reloc, + (reldesc.size - length) // 2) # / Reloc size + reldesc.patchrel = False + out.append(reldesc) + off += reldesc.size + return out, off + + def sete(self, entries): + return b"".join( + bytes(entry) + bytes(entry.rels) + for entry in entries + ) + + def set_rva(self, rva, size=None): + if self.reldesc is None: + return + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_BASERELOC].rva = rva + if not size: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_BASERELOC].size = len(self) + else: + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_BASERELOC].size = size + + def build_content(self, raw): + dirrel = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_BASERELOC] + dirrel.size = len(self) + of1 = dirrel.rva + if self.reldesc is None: # No Reloc + return + raw[self.parent_head.rva2off(of1)] = bytes(self) + + def __len__(self): + if self.reldesc is None: + return 0 + length = 0 + for entry in self.reldesc: + length += entry.size + return length + + def __bytes__(self): + return b"".join( + bytes(entry) + bytes(entry.rels) + for entry in self.reldesc + ) + + def __str__(self): + if PY3: + return repr(self) + return self.__bytes__() + + def __repr__(self): + rep = ["<%s>" % self.__class__.__name__] + if self.reldesc is None: + return "\n".join(rep) + for i, entry in enumerate(self.reldesc): + out = "%2d %s" % (i, repr(entry)) + rep.append(out) + """ + #display too many lines... + for ii, m in enumerate(entry.rels): + l = "\t%2d %s"%(ii, repr(m) ) + rep.append(l) + """ + out = "\t%2d rels..." % (len(entry.rels)) + rep.append(out) + return "\n".join(rep) + + def add_reloc(self, rels, rtype=3, patchrel=True): + dirrel = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_BASERELOC] + if not rels: + return + rels.sort() + all_base_ad = set([x & 0xFFFFF000 for x in rels]) + all_base_ad = list(all_base_ad) + all_base_ad.sort() + rels_by_base = defaultdict(list) + while rels: + reloc = rels.pop() + if reloc >= all_base_ad[-1]: + rels_by_base[all_base_ad[-1]].append(reloc) + else: + all_base_ad.pop() + rels_by_base[all_base_ad[-1]].append(reloc) + rels_by_base = [x for x in list(rels_by_base.items())] + rels_by_base.sort() + for o_init, rels in rels_by_base: + # o_init = rels[0]&0xFFFFF000 + offsets = struct_array(self, None, None, Reloc, 0) + for reloc_value in rels: + if (reloc_value & 0xFFFFF000) != o_init: + raise RuntimeError("relocs must be in same range") + reloc = Reloc(self.parent_head) + reloc.rel = (rtype, reloc_value - o_init) + offsets.append(reloc) + while len(offsets) & 3: + reloc = Reloc(self.parent_head) + reloc.rel = (0, 0) + offsets.append(reloc) + reldesc = Rel(self.parent_head) # Reloc(self.parent_head) + reldesc.rva = o_init + reldesc.size = (len(offsets) * 2 + 8) + reldesc.rels = offsets + reldesc.patchrel = patchrel + # if self.reldesc is None: + # self.reldesc = [] + self.reldesc.append(reldesc) + dirrel.size += reldesc.size + + def del_reloc(self, taboffset): + if self.reldesc is None: + return + for rel in self.reldesc: + of1 = rel.rva + i = 0 + while i < len(rel.rels): + reloc = rel.rels[i] + if reloc.rel[0] != 0 and reloc.rel[1] + of1 in taboffset: + print('del reloc', hex(reloc.rel[1] + of1)) + del rel.rels[i] + rel.size -= Reloc._size + else: + i += 1 + + +class DirRes(CStruct): + _fields = [("resdesc", (lambda c, raw, off:c.gete(raw, off), + lambda c, value:c.sete(value)))] + + def gete(self, raw, off): + if not off: + return None, off + if off >= len(self.parent_head.img_rva): + log.warning('cannot parse resources, %X' % off) + return None, off + + off_orig = off + ofend = off + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].size + + resdesc, length = ResDesc_e.unpack_l(raw, + off, + self.parent_head) + off += length + nbr = resdesc.numberofnamedentries + resdesc.numberofidentries + + out = [] + tmp_off = off + resdesc.resentries = struct_array(self, raw, + off, + ResEntry, + nbr) + dir_todo = {off_orig: resdesc} + dir_done = {} + while dir_todo: + off, my_dir = dir_todo.popitem() + dir_done[off] = my_dir + for entry in my_dir.resentries: + off = entry.offsettosubdir + if not off: + # data dir + off = entry.offsettodata + if not 0 <= off < len(raw): + log.warn('bad resource entry') + continue + data = ResDataEntry.unpack(raw, + off, + self.parent_head) + off = data.offsettodata + data.s = StrPatchwork(raw[off:off + data.size]) + entry.data = data + continue + # subdir + if off in dir_done: + log.warn('warning recusif subdir') + continue + if not 0 <= off < len(self.parent_head.img_rva): + log.warn('bad resource entry') + continue + subdir, length = ResDesc_e.unpack_l(raw, + off, + self.parent_head) + nbr = subdir.numberofnamedentries + subdir.numberofidentries + try: + subdir.resentries = struct_array(self, raw, + off + length, + ResEntry, + nbr) + except RuntimeError: + log.warn('bad resource entry') + continue + + entry.subdir = subdir + dir_todo[off] = entry.subdir + return resdesc, off + + def build_content(self, raw): + if self.resdesc is None: + return + of1 = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva + raw[self.parent_head.rva2off(of1)] = bytes(self.resdesc) + length = len(self.resdesc) + dir_todo = { + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva: self.resdesc + } + of1 = of1 + length + raw[self.parent_head.rva2off(of1)] = bytes(self.resdesc.resentries) + dir_done = {} + while dir_todo: + of1, my_dir = dir_todo.popitem() + dir_done[of1] = my_dir + raw[self.parent_head.rva2off(of1)] = bytes(my_dir) + of1 += len(my_dir) + raw[self.parent_head.rva2off(of1)] = bytes(my_dir.resentries) + of_base = of1 + for entry in my_dir.resentries: + of_base += len(entry) + if entry.name_s: + raw[self.parent_head.rva2off(entry.name)] = bytes(entry.name_s) + of1 = entry.offsettosubdir + if not of1: + raw[self.parent_head.rva2off(entry.offsettodata)] = bytes(entry.data) + raw[self.parent_head.rva2off(entry.data.offsettodata)] = bytes(entry.data.s) + continue + dir_todo[of1] = entry.subdir + + def __len__(self): + length = 0 + if self.resdesc is None: + return length + dir_todo = [self.resdesc] + dir_done = [] + while dir_todo: + my_dir = dir_todo.pop() + if my_dir in dir_done: + raise ValueError('Recursive directory') + dir_done.append(my_dir) + length += len(my_dir) + length += len(my_dir.resentries) * 8 # ResEntry size + for entry in my_dir.resentries: + if not entry.offsettosubdir: + continue + if not entry.subdir in dir_todo: + dir_todo.append(entry.subdir) + else: + raise RuntimeError("recursive dir") + + dir_todo = dir_done + while dir_todo: + my_dir = dir_todo.pop() + for entry in my_dir.resentries: + if entry.name_s: + length += len(entry.name_s) + of1 = entry.offsettosubdir + if not of1: + length += 4 * 4 # WResDataEntry size + # XXX because rva may be even rounded + length += 1 + length += entry.data.size + continue + return length + + def set_rva(self, rva, size=None): + if self.resdesc is None: + return + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva = rva + if not size: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].size = len(self) + else: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].size = size + dir_todo = [self.resdesc] + dir_done = {} + while dir_todo: + my_dir = dir_todo.pop() + dir_done[rva] = my_dir + rva += len(my_dir) + rva += len(my_dir.resentries) * 8 # ResEntry size + for entry in my_dir.resentries: + if not entry.offsettosubdir: + continue + if not entry.subdir in dir_todo: + dir_todo.append(entry.subdir) + else: + raise RuntimeError("recursive dir") + dir_todo = dir_done + dir_inv = dict([(x[1], x[0]) for x in list(dir_todo.items())]) + while dir_todo: + rva_tmp, my_dir = dir_todo.popitem() + for entry in my_dir.resentries: + if entry.name_s: + entry.name = rva + rva += len(entry.name_s) + of1 = entry.offsettosubdir + if not of1: + entry.offsettodata = rva + rva += 4 * 4 # ResDataEntry size + # XXX menu rsrc must be even aligned? + if rva % 2: + rva += 1 + entry.data.offsettodata = rva + rva += entry.data.size + continue + entry.offsettosubdir = dir_inv[entry.subdir] + + def __repr__(self): + rep = ["<%s>" % (self.__class__.__name__)] + if self.resdesc is None: + return "\n".join(rep) + dir_todo = [self.resdesc] + resources = [] + index = -1 + while dir_todo: + entry = dir_todo.pop(0) + if isinstance(entry, int): + index += entry + elif isinstance(entry, ResDesc_e): + # resources.append((index, repr(entry))) + dir_todo = [1] + entry.resentries.l + [-1] + dir_todo + elif isinstance(entry, ResEntry): + if entry.offsettosubdir: + resources.append((index, repr(entry))) + dir_todo = [entry.subdir] + dir_todo + else: + resources.append((index, repr(entry))) + else: + raise RuntimeError("zarb") + for i, resource in resources: + rep.append(' ' * 4 * i + resource) + return "\n".join(rep) + + +class Ordinal(CStruct): + _fields = [("ordinal", "u16"), + ] + + +class ResDesc_e(CStruct): + _fields = [("characteristics", "u32"), + ("timestamp", "u32"), + ("majorv", "u16"), + ("minorv", "u16"), + ("numberofnamedentries", "u16"), + ("numberofidentries", "u16") + ] + + +class SUnicode(CStruct): + _fields = [("length", "u16"), + ("value", (lambda c, raw, off:c.gets(raw, off), + lambda c, value:c.sets(value))) + ] + + def gets(self, raw, off): + value = raw[off:off + self.length * 2] + return value, off + self.length + + def sets(self, value): + return self.value + + +class ResEntry(CStruct): + _fields = [("name", (lambda c, raw, off:c._get_name(raw, off), + lambda c, value:c._set_name(value))), + ("offsettodata", (lambda c, raw, off:c._get_offset(raw, off), + lambda c, value:c._set_offset(value))) + ] + + def _get_name(self, raw, off): + self.data = None + # off = self.parent_head.rva2off(off) + name = struct.unpack('I', raw[off:off + 4])[0] + self.name_s = None + if name & 0x80000000: + name = (name & 0x7FFFFFFF) + self.parent_head.NThdr.optentries[ + DIRECTORY_ENTRY_RESOURCE].rva # XXX res rva?? + name &= 0x7FFFFFFF + if name >= len(raw): + raise RuntimeError("Bad resentry") + self.name_s = SUnicode.unpack(raw, + name, + self.parent_head) + return name, off + 4 + + def _set_name(self, name): + if self.name_s: + rva = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva + name = (self.name - rva) + 0x80000000 + return struct.pack('I', name) + + def _get_offset(self, raw, off): + self.offsettosubdir = None + rva = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva + offsettodata_o = struct.unpack('I', raw[off:off + 4])[0] + offsettodata = (offsettodata_o & 0x7FFFFFFF) + rva # XXX res rva?? + if offsettodata_o & 0x80000000: + self.offsettosubdir = offsettodata + return offsettodata, off + 4 + + def _set_offset(self, offset): + rva = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_RESOURCE].rva + offsettodata = offset - rva + if self.offsettosubdir: + offsettodata = (self.offsettosubdir - rva) + 0x80000000 + return struct.pack('I', offsettodata) + + def __repr__(self): + if self.name_s: + nameid = "%s" % repr(self.name_s) + else: + if self.name in RT: # and not self.offsettosubdir: + nameid = "ID %s" % RT[self.name] + else: + nameid = "ID %d" % self.name + if self.offsettosubdir: + offsettodata = "subdir: %x" % self.offsettosubdir + else: + offsettodata = "data: %x" % self.offsettodata + return "<%s %s>" % (nameid, offsettodata) + + +class ResDataEntry(CStruct): + _fields = [("offsettodata", "u32"), + ("size", "u32"), + ("codepage", "u32"), + ("reserved", "u32"), + ] + + +class Symb(CStruct): + _fields = [("name", "8s"), + ("res1", "u32"), + ("res2", "u32"), + ("res3", "u16")] + + +class DirTls(CStruct): + _fields = [ + ("data_start", "ptr"), + ("data_end", "ptr"), + ("addr_index", "ptr"), + ("callbacks", "ptr"), + ("size_of_zero", "u32"), + ("characteristics", "u32") + ] + + def build_content(self, raw): + dirtls = self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_TLS] + of1 = dirtls.rva + if of1 is None: # No Tls + return + raw[self.parent_head.rva2off(of1)] = bytes(self) + + def set_rva(self, rva, size=None): + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_TLS].rva = rva + if not size: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_TLS].size = len(self) + else: + self.parent_head.NThdr.optentries[DIRECTORY_ENTRY_TLS].size = size + + +DIRECTORY_ENTRY_EXPORT = 0 +DIRECTORY_ENTRY_IMPORT = 1 +DIRECTORY_ENTRY_RESOURCE = 2 +DIRECTORY_ENTRY_EXCEPTION = 3 +DIRECTORY_ENTRY_SECURITY = 4 +DIRECTORY_ENTRY_BASERELOC = 5 +DIRECTORY_ENTRY_DEBUG = 6 +DIRECTORY_ENTRY_COPYRIGHT = 7 +DIRECTORY_ENTRY_GLOBALPTR = 8 +DIRECTORY_ENTRY_TLS = 9 +DIRECTORY_ENTRY_LOAD_CONFIG = 10 +DIRECTORY_ENTRY_BOUND_IMPORT = 11 +DIRECTORY_ENTRY_IAT = 12 +DIRECTORY_ENTRY_DELAY_IMPORT = 13 +DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 +DIRECTORY_ENTRY_RESERVED = 15 + + +RT_CURSOR = 1 +RT_BITMAP = 2 +RT_ICON = 3 +RT_MENU = 4 +RT_DIALOG = 5 +RT_STRING = 6 +RT_FONTDIR = 7 +RT_FONT = 8 +RT_ACCELERATOR = 9 +RT_RCDATA = 10 +RT_MESSAGETABLE = 11 +RT_GROUP_CURSOR = 12 +RT_GROUP_ICON = 14 +RT_VERSION = 16 +RT_DLGINCLUDE = 17 +RT_PLUGPLAY = 19 +RT_VXD = 20 +RT_ANICURSOR = 21 +RT_ANIICON = 22 +RT_HTML = 23 +RT_MANIFEST = 24 + + +RT = { + RT_CURSOR: "RT_CURSOR", + RT_BITMAP: "RT_BITMAP", + RT_ICON: "RT_ICON", + RT_MENU: "RT_MENU", + RT_DIALOG: "RT_DIALOG", + RT_STRING: "RT_STRING", + RT_FONTDIR: "RT_FONTDIR", + RT_FONT: "RT_FONT", + RT_ACCELERATOR: "RT_ACCELERATOR", + RT_RCDATA: "RT_RCDATA", + RT_MESSAGETABLE: "RT_MESSAGETABLE", + RT_GROUP_CURSOR: "RT_GROUP_CURSOR", + RT_GROUP_ICON: "RT_GROUP_ICON", + RT_VERSION: "RT_VERSION", + RT_DLGINCLUDE: "RT_DLGINCLUDE", + RT_PLUGPLAY: "RT_PLUGPLAY", + RT_VXD: "RT_VXD", + RT_ANICURSOR: "RT_ANICURSOR", + RT_ANIICON: "RT_ANIICON", + RT_HTML: "RT_HTML", + RT_MANIFEST: "RT_MANIFEST", +} |