about summary refs log tree commit diff stats
path: root/miasm/loader/pe.py
diff options
context:
space:
mode:
Diffstat (limited to 'miasm/loader/pe.py')
-rw-r--r--miasm/loader/pe.py1693
1 files changed, 0 insertions, 1693 deletions
diff --git a/miasm/loader/pe.py b/miasm/loader/pe.py
deleted file mode 100644
index 1252e70e..00000000
--- a/miasm/loader/pe.py
+++ /dev/null
@@ -1,1693 +0,0 @@
-#! /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",
-}