#! /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", }