Skip to content

Commit

Permalink
Use apiset & reloc
Browse files Browse the repository at this point in the history
  • Loading branch information
serpilliere committed Oct 30, 2020
1 parent c9f2216 commit 9dd500f
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 54 deletions.
40 changes: 28 additions & 12 deletions miasm/analysis/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,16 @@ def init_stack(self):
self.jitter.stack_base = self.STACK_BASE
self.jitter.init_stack()

def init_loader(self):
def init_loader(self, options):
from miasm.jitter.loader.pe import LoaderWindows
from miasm.os_dep.win_api_x86_32 import winobjs

# Import manager
loader = LoaderWindows()
if options.loader_start_address:
loader_start_address = int(options.loader_start_address, 0)
else:
loader_start_address = None
loader = LoaderWindows(loader_start_address=loader_start_address)
self.loader = loader
winobjs.loader = loader

Expand Down Expand Up @@ -262,7 +266,7 @@ def load_main_pe(self, options):

def load_base_dll(self):
from miasm.os_dep.win_api_x86_32 import winobjs
from miasm.jitter.loader.pe import vm_load_pe_libs, preload_pe
from miasm.jitter.loader.pe import vm_load_pe_libs, fix_pe_imports

# Load libs in memory
self.name2module.update(
Expand All @@ -276,8 +280,8 @@ def load_base_dll(self):
)

# Patch libs imports
for pe in viewvalues(self.name2module):
preload_pe(self.jitter.vm, pe, self.loader)
for name, pe in self.name2module.items():
fix_pe_imports(self.jitter.vm, pe, self.loader, pe_name=name)

def load_dependencies(self):
from miasm.os_dep.win_api_x86_32 import winobjs
Expand All @@ -302,25 +306,27 @@ def set_call_handler(self, custom_methods):
methods.update(custom_methods)
self.jitter.add_lib_handler(self.loader, methods)

def fix_preload_pe(self):
def fix_pe_imports(self):
# Fix pe imports
from miasm.jitter.loader.pe import preload_pe
from miasm.jitter.loader.pe import fix_pe_imports

preload_pe(self.jitter.vm, self.pe, self.loader)
fix_pe_imports(
self.jitter.vm, self.pe, self.loader, pe_name=self.fname_basename
)

def __init__(self, jitter, options, custom_methods=None):
self.fname_basename = os.path.basename(options.filename).lower()
self.jitter = jitter

self.init_stack()
self.init_loader()
self.init_loader(options)
self.load_main_pe(options)
if options.loadbasedll:
self.load_base_dll()
if options.dependencies:
self.load_dependencies()

self.fix_preload_pe()
self.fix_pe_imports()
self.set_call_handler(custom_methods)

# Manage SEH
Expand Down Expand Up @@ -358,6 +364,12 @@ def update_parser(cls, parser):
action="store_true",
help="Don't log function calls",
)
parser.add_argument(
"-x",
"--loader_start_address",
default=None,
help="Reloc libraries starting at load base address",
)


class Sandbox_WinXP_x86_32(Sandbox):
Expand Down Expand Up @@ -391,7 +403,7 @@ def call(self, addr, *args, **kwargs):
class OS_Win10(OS_WinXP32):
VERSION = "10.0.15063.0"

def init_loader(self):
def init_loader(self, options):
from miasm.jitter.loader.pe import LoaderWindows
from miasm.os_dep.windows import apiset
from miasm.os_dep.win_api_x86_32 import winobjs
Expand All @@ -403,7 +415,11 @@ def init_loader(self):
apiset = ApiSet(apiset_file)

# Import manager
loader = LoaderWindows(apiset=apiset)
if options.loader_start_address:
loader_start_address = int(options.loader_start_address, 0)
else:
loader_start_address = None
loader = LoaderWindows(apiset=apiset, loader_start_address=loader_start_address)
self.loader = loader
winobjs.loader = loader

Expand Down
104 changes: 68 additions & 36 deletions miasm/jitter/loader/pe.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from builtins import map
import os
import re
import struct
import json
import logging
Expand All @@ -22,6 +23,8 @@
log.addHandler(hnd)
log.setLevel(logging.INFO)

match_hyphen_digit = re.compile(".*-[\d]+-[\d]+$")


def get_pe_dependencies(pe_obj):
"""Collect the shared libraries upon which this PE depends.
Expand Down Expand Up @@ -89,23 +92,26 @@ def get_import_address_pe(e):
return import2addr


def preload_pe(vm, e, loader, patch_vm_imp=True):
def fix_pe_imports(vm, e, loader, patch_vm_imp=True, pe_name=None):
import_information = get_import_address_pe(e)
dyn_funcs = {}
# log.debug('imported funcs: %s' % import_information)
for (libname, funcname), ads in viewitems(import_information):
for ad in ads:
libname = force_str(libname)
if loader.apiset:
libname = loader.apiset.get_redirection(libname)
libname = loader.apiset.get_redirection(libname, pe_name)
if libname.startswith("api-ms"):
fds
ad_base_lib = loader.lib_get_add_base(libname)
ad_funcname = loader.lib_get_add_func(ad_base_lib, funcname, ad)

libname_s = canon_libname_libfunc(libname, funcname)
dyn_funcs[libname_s] = ad_funcname
if patch_vm_imp:
vm.set_mem(
ad, struct.pack(cstruct.size2type[e._wsize], ad_funcname))
ad, struct.pack(cstruct.size2type[e._wsize], ad_funcname)
)
return dyn_funcs


Expand Down Expand Up @@ -170,7 +176,7 @@ def get_export_name_addr_list(e):
return out


def vm_load_pe(vm, fdata, align_s=True, load_hdr=True, name="", winobjs=None, **kargs):
def vm_load_pe(vm, fdata, align_s=True, load_hdr=True, name="", winobjs=None, base_addr=None, **kargs):
"""Load a PE in memory (@vm) from a data buffer @fdata
@vm: VmMngr instance
@fdata: data buffer to parse
Expand All @@ -186,6 +192,10 @@ def vm_load_pe(vm, fdata, align_s=True, load_hdr=True, name="", winobjs=None, **
# Parse and build a PE instance
pe = pe_init.PE(fdata, **kargs)

# Optionaly rebase PE
if base_addr is not None:
pe.reloc_to(base_addr)

# Check if all section are aligned
aligned = True
for section in pe.SHList:
Expand Down Expand Up @@ -307,7 +317,7 @@ def vm_load_pe_lib(vm, fname_in, loader, lib_path_base, **kargs):

fname = os.path.join(lib_path_base, fname_in)
with open(fname, "rb") as fstream:
pe = vm_load_pe(vm, fstream.read(), name=fname_in, **kargs)
pe = loader.vm_load_pe(vm, fstream.read(), name=fname_in, **kargs)
loader.add_export_lib(pe, fname_in)
return pe

Expand All @@ -325,13 +335,9 @@ def vm_load_pe_libs(vm, libs_name, loader, lib_path_base, **kargs):
for fname in libs_name:
assert isinstance(fname, str)
out[fname] = vm_load_pe_lib(vm, fname, loader, lib_path_base, **kargs)
return out

return out

def vm_fix_imports_pe_libs(lib_imgs, loader, lib_path_base,
patch_vm_imp=True, **kargs):
for e in viewvalues(lib_imgs):
preload_pe(e, loader, patch_vm_imp=patch_vm_imp)


def vm2pe(myjit, fname, loader=None, e_orig=None,
Expand Down Expand Up @@ -424,11 +430,12 @@ def vm2pe(myjit, fname, loader=None, e_orig=None,

class LoaderWindows(Loader):

def __init__(self, *args, apiset=None, **kwargs):
def __init__(self, *args, apiset=None, loader_start_address=None, **kwargs):
super(LoaderWindows, self).__init__(*args, **kwargs)
# dependency -> redirector
self.created_redirected_imports = {}
self.apiset = apiset
self.loader_start_address = loader_start_address


def add_function(self, dllname, imp_ord_or_name, addr):
Expand Down Expand Up @@ -583,6 +590,19 @@ def gen_new_lib(self, target_pe, filter_import=lambda peobj, ad: True, **kwargs)

return new_lib

def vm_load_pe(self, vm, fdata, align_s=True, load_hdr=True, name="", winobjs=None, **kargs):
pe = vm_load_pe(
vm, fdata,
align_s=align_s,
load_hdr=load_hdr,
name=name, winobjs=winobjs,
base_addr=self.loader_start_address,
**kargs
)
if self.loader_start_address:
self.loader_start_address += pe.NThdr.sizeofimage + 0x1000
return pe


class limbimp_pe(LoaderWindows):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -621,22 +641,24 @@ def vm_load_pe_and_dependencies(vm, fname, name2module, loader,
try:
with open(fname, "rb") as fstream:
log.info('Loading module name %r', fname)
pe_obj = vm_load_pe(
vm, fstream.read(), name=fname, **kwargs)
pe_obj = loader.vm_load_pe(
vm, fstream.read(), name=fname, **kwargs
)
except IOError:
log.error('Cannot open %s' % fname)
name2module[name] = None
continue
name2module[name] = pe_obj

new_dependencies = get_pe_dependencies(pe_obj)
todo += [(name, os.path.join(lib_path_base, name), weight - 1)
for name in new_dependencies]
for libname in new_dependencies:
if loader.apiset:
libname = loader.apiset.get_redirection(libname, name)
todo.append((libname, os.path.join(lib_path_base, libname), weight - 1))

known_export_addresses = {}
to_resolve = {}
for name, pe_obj in name2module.items():
print(name)
if pe_obj is None:
continue
if pe_obj.DirExport.expdesc == None:
Expand All @@ -650,6 +672,9 @@ def vm_load_pe_and_dependencies(vm, fname, name2module, loader,
known_export_addresses[(name, imp_ord_or_name)] = ad
else:
dllname, func_info = ret

dllname = loader.apiset.get_redirection(dllname, name)

dllname = dllname + '.dll'
to_resolve[(name, imp_ord_or_name)] = (dllname, func_info)

Expand Down Expand Up @@ -688,7 +713,7 @@ def vm_load_pe_and_dependencies(vm, fname, name2module, loader,
for dllname, pe_obj in name2module.items():
if pe_obj is None:
continue
preload_pe(vm, pe_obj, loader, patch_vm_imp=True)
fix_pe_imports(vm, pe_obj, loader, patch_vm_imp=True, pe_name=dllname)

return name2module

Expand All @@ -709,8 +734,6 @@ class ApiSet(object):
def __init__(self, fname):
data = json.load(open(fname))
self.version = data['version']
self.hash_factor = data['hash_factor']
self.redirections = data['redirections']
self.hash_entries = data['hashes']

def compute_hash(self, apiset_lib_name):
Expand All @@ -722,26 +745,35 @@ def compute_hash(self, apiset_lib_name):
hashk = (hashk * self.hash_factor + ord(c)) & ((1 << 32) - 1)
return hashk

def get_redirection(self, libname):
def get_redirected_host(self, libname, entries, parent):
if len(entries) == 1:
assert "" in entries
log.debug("ApiSet %s => %s" % (libname, entries[""]))
libname = entries[""]
else:
if parent in entries:
libname = entries[parent]
else:
libname = entries[""]
return libname

def get_redirection(self, libname, parent_name):
has_dll = libname.endswith(".dll")
if has_dll:
libname = libname[:-4]
if libname in self.redirections:
values = self.redirections[libname]
if len(values) == 1:
assert "" in values
log.warn("ApiSet %s => %s" % (libname, values[""]))
libname = values[""]
else:
libname_dll = "%s.dll" % libname
if libname_dll in values:
fds
else:
libname = values[""]
hash_to_search = self.compute_hash(libname)
if hash_to_search in self.hash_entries:
fds
name_nodll = libname[:-4]
else:
name_nodll = libname

# Remove last hyphen part to compute crc
if match_hyphen_digit.match(name_nodll):
cname = name_nodll[:name_nodll.rfind('-')]
else:
cname = name_nodll
values = self.hash_entries.get(cname, None)
if not values:
# No entry found
return libname
libname = self.get_redirected_host(cname, values, parent_name)
if has_dll and not libname.endswith('.dll'):
libname += ".dll"
elif not has_dll and libname.endswith('.dll'):
Expand Down
22 changes: 16 additions & 6 deletions miasm/loader/pe_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,10 +620,20 @@ def reloc_to(self, imgbase):
reloc_type, off = reloc.rel
if reloc_type == 0 and off == 0:
continue
if reloc_type != 3:
raise NotImplementedError('Reloc type not supported')
off += rva
value = struct.unpack('I', self.rva.get(off, off + 4))[0]
value += offset
self.rva.set(off, struct.pack('I', value & 0xFFFFFFFF))
if reloc_type == 3:
off += rva
value = struct.unpack('I', self.rva.get(off, off + 4))[0]
value += offset
data = struct.pack('I', value & 0xFFFFFFFF)
self.rva.set(off, data)
self.img_rva[off] = data
elif reloc_type == 10:
off += rva
value = struct.unpack('Q', self.rva.get(off, off + 8))[0]
value += offset
data = struct.pack('Q', value & 0xFFFFFFFFFFFFFFFF)
self.rva.set(off, data)
self.img_rva[off] = data
else:
raise NotImplementedError('Reloc type not supported')
self.NThdr.ImageBase = imgbase

0 comments on commit 9dd500f

Please sign in to comment.