about summary refs log tree commit diff stats
path: root/miasm2/jitter/loader/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'miasm2/jitter/loader/utils.py')
-rw-r--r--miasm2/jitter/loader/utils.py197
1 files changed, 197 insertions, 0 deletions
diff --git a/miasm2/jitter/loader/utils.py b/miasm2/jitter/loader/utils.py
new file mode 100644
index 00000000..de269f83
--- /dev/null
+++ b/miasm2/jitter/loader/utils.py
@@ -0,0 +1,197 @@
+import logging
+
+log = logging.getLogger('loader_common')
+hnd = logging.StreamHandler()
+hnd.setFormatter(logging.Formatter("[%(levelname)s]: %(message)s"))
+log.addHandler(hnd)
+log.setLevel(logging.CRITICAL)
+
+
+def canon_libname_libfunc(libname, libfunc):
+    dn = libname.split('.')[0]
+    if type(libfunc) == str:
+        return "%s_%s" % (dn, libfunc)
+    else:
+        return str(dn), libfunc
+
+
+class libimp:
+
+    def __init__(self, lib_base_ad=0x71111000, **kargs):
+        self.name2off = {}
+        self.libbase2lastad = {}
+        self.libbase_ad = lib_base_ad
+        self.lib_imp2ad = {}
+        self.lib_imp2dstad = {}
+        self.fad2cname = {}
+        self.fad2info = {}
+        self.all_exported_lib = []
+
+    def lib_get_add_base(self, name):
+        name = name.lower().strip(' ')
+        if not "." in name:
+            log.debug('warning adding .dll to modulename')
+            name += '.dll'
+            log.debug('%s' % name)
+
+        if name in self.name2off:
+            ad = self.name2off[name]
+        else:
+            ad = self.libbase_ad
+            log.debug('new lib %s %s' % (name, hex(ad)))
+            self.name2off[name] = ad
+            self.libbase2lastad[ad] = ad + 0x1
+            self.lib_imp2ad[ad] = {}
+            self.lib_imp2dstad[ad] = {}
+            self.libbase_ad += 0x1000
+        return ad
+
+    def lib_get_add_func(self, libad, imp_ord_or_name, dst_ad=None):
+        if not libad in self.name2off.values():
+            raise ValueError('unknown lib base!', hex(libad))
+
+        # test if not ordinatl
+        # if imp_ord_or_name >0x10000:
+        #    imp_ord_or_name = vm_get_str(imp_ord_or_name, 0x100)
+        #    imp_ord_or_name = imp_ord_or_name[:imp_ord_or_name.find('\x00')]
+
+        #/!\ can have multiple dst ad
+        if not imp_ord_or_name in self.lib_imp2dstad[libad]:
+            self.lib_imp2dstad[libad][imp_ord_or_name] = set()
+        self.lib_imp2dstad[libad][imp_ord_or_name].add(dst_ad)
+
+        if imp_ord_or_name in self.lib_imp2ad[libad]:
+            return self.lib_imp2ad[libad][imp_ord_or_name]
+        # log.debug('new imp %s %s' % (imp_ord_or_name, dst_ad))
+        ad = self.libbase2lastad[libad]
+        self.libbase2lastad[libad] += 0x11  # arbitrary
+        self.lib_imp2ad[libad][imp_ord_or_name] = ad
+
+        name_inv = dict([(x[1], x[0]) for x in self.name2off.items()])
+        c_name = canon_libname_libfunc(name_inv[libad], imp_ord_or_name)
+        self.fad2cname[ad] = c_name
+        self.fad2info[ad] = libad, imp_ord_or_name
+        return ad
+
+    def check_dst_ad(self):
+        for ad in self.lib_imp2dstad:
+            all_ads = self.lib_imp2dstad[ad].values()
+            all_ads.sort()
+            for i, x in enumerate(all_ads[:-1]):
+                if x is None or all_ads[i + 1] is None:
+                    return False
+                if x + 4 != all_ads[i + 1]:
+                    return False
+        return True
+
+    def add_export_lib(self, e, name):
+        self.all_exported_lib.append(e)
+        # will add real lib addresses to database
+        if name in self.name2off:
+            ad = self.name2off[name]
+        else:
+            log.debug('new lib %s' % name)
+            ad = e.NThdr.ImageBase
+            libad = ad
+            self.name2off[name] = ad
+            self.libbase2lastad[ad] = ad + 0x1
+            self.lib_imp2ad[ad] = {}
+            self.lib_imp2dstad[ad] = {}
+            self.libbase_ad += 0x1000
+
+            ads = get_export_name_addr_list(e)
+            todo = ads
+            # done = []
+            while todo:
+                # for imp_ord_or_name, ad in ads:
+                imp_ord_or_name, ad = todo.pop()
+
+                # if export is a redirection, search redirected dll
+                # and get function real addr
+                ret = is_redirected_export(e, ad)
+                if ret:
+                    exp_dname, exp_fname = ret
+                    # log.debug('export redirection %s' % imp_ord_or_name)
+                    # log.debug('source %s %s' % (exp_dname, exp_fname))
+                    exp_dname = exp_dname + '.dll'
+                    exp_dname = exp_dname.lower()
+                    # if dll auto refes in redirection
+                    if exp_dname == name:
+                        libad_tmp = self.name2off[exp_dname]
+                        if not exp_fname in self.lib_imp2ad[libad_tmp]:
+                            # schedule func
+                            todo = [(imp_ord_or_name, ad)] + todo
+                            continue
+                    elif not exp_dname in self.name2off:
+                        raise ValueError('load %r first' % exp_dname)
+                    c_name = canon_libname_libfunc(exp_dname, exp_fname)
+                    libad_tmp = self.name2off[exp_dname]
+                    ad = self.lib_imp2ad[libad_tmp][exp_fname]
+                    # log.debug('%s' % hex(ad))
+                # if not imp_ord_or_name in self.lib_imp2dstad[libad]:
+                #    self.lib_imp2dstad[libad][imp_ord_or_name] = set()
+                # self.lib_imp2dstad[libad][imp_ord_or_name].add(dst_ad)
+
+                # log.debug('new imp %s %s' % (imp_ord_or_name, hex(ad)))
+                self.lib_imp2ad[libad][imp_ord_or_name] = ad
+
+                name_inv = dict([(x[1], x[0]) for x in self.name2off.items()])
+                c_name = canon_libname_libfunc(
+                    name_inv[libad], imp_ord_or_name)
+                self.fad2cname[ad] = c_name
+                self.fad2info[ad] = libad, imp_ord_or_name
+
+    def gen_new_lib(self, target_pe, filter=lambda _: True):
+        """Gen a new DirImport description
+        @target_pe: PE instance
+        @filter: (boolean f(address)) restrict addresses to keep
+        """
+
+        new_lib = []
+        for lib_name, ad in self.name2off.items():
+            # Build an IMAGE_IMPORT_DESCRIPTOR
+
+            # Get fixed addresses
+            out_ads = dict() # addr -> func_name
+            for func_name, dst_addresses in self.lib_imp2dstad[ad].items():
+                out_ads.update({addr:func_name for addr in dst_addresses})
+
+            # Filter available addresses according to @filter
+            all_ads = [addr for addr in out_ads.keys() if filter(addr)]
+            log.debug('ads: %s' % map(hex, all_ads))
+            if not all_ads:
+                continue
+
+            # Keep non-NULL elements
+            all_ads.sort()
+            for i, x in enumerate(all_ads):
+                if x not in [0,  None]:
+                    break
+            all_ads = all_ads[i:]
+
+            while all_ads:
+                # Find libname's Import Address Table
+                othunk = all_ads[0]
+                i = 0
+                while i + 1 < len(all_ads) and all_ads[i] + 4 == all_ads[i + 1]:
+                    i += 1
+                # 'i + 1' is IAT's length
+
+                # Effectively build an IMAGE_IMPORT_DESCRIPTOR
+                funcs = [out_ads[addr] for addr in all_ads[:i + 1]]
+                try:
+                    rva = target_pe.virt2rva(othunk)
+                except pe.InvalidOffset:
+                    pass
+                else:
+                    new_lib.append(({"name": lib_name,
+                                     "firstthunk": rva},
+                                    funcs)
+                                   )
+
+                # Update elements to handle
+                all_ads = all_ads[i + 1:]
+
+        return new_lib
+
+