Source code for windows.winobject.apisetmap

import ctypes

import windows
import windows.generated_def as gdef

from windows import utils


def get_api_set_map_for_current_process(base):
    base = windows.current_process.peb.ApiSetMap
    version = windows.current_process.read_dword(base)
    if version not in API_SET_MAP_BY_VERSION:
        raise NotImplementedError("ApiSetMap version <{0}> not implemented, please contact me, I need a sample to implement it ;)")
    return API_SET_MAP_BY_VERSION[version](base)


[docs]class ApiSetMap(object): """The base class for the ApiSeMap (see `Runtime DLL name resolution: ApiSetSchema <https://blog.quarkslab.com/runtime-dll-name-resolution-apisetschema-part-ii.html>`_) """ version = None #: The version of the ApiSetMap def __init__(self, base): self.base = base self.target = windows.current_process # helpers def read_apiset_wstring(self, offset, length): return self.target.read_memory(self.base + offset, length).decode("utf-16") # Low-level version-dependent parsing function def entries_array(self): raise NotImplementedError("Should be implemented by subclasses") def get_entry_name(self, entry): raise NotImplementedError("Should be implemented by subclasses") def get_entry_name_basicimpl(self, entry): return self.read_apiset_wstring(entry.NameOffset, entry.NameLength) def values_for_entry(self, entry): raise NotImplementedError("Should be implemented by subclasses") @utils.fixedpropety def apisetmap_dict(self): """The apisetmap dll-mapping content extracted from memory as a :class:`dict` ``key -> value example``:: u'ext-ms-win-advapi32-encryptedfile-l1-1-1' -> u'advapi32.dll' """ res = {} for entry in self.entries_array(): values = self.values_for_entry(entry) if not values: final_value = None else: final_value = values[-1] res[self.get_entry_name(entry)] = final_value return res @utils.fixedpropety def resolution_dict(self): """The :class:`dict` based on :obj:`apisetmap_dict` with only the part checked by ``Windows``. ``Windows`` does not care about what is after the last ``-`` ``key -> value example``:: u'ext-ms-win-advapi32-encryptedfile-l1-1-' -> u'advapi32.dll' """ res = {} for name, resolved_name in self.apisetmap_dict.items(): # ApiSetResolveToHost does not care about last version + extension # It remove everything after the last '-' # Possible to have no '-' ? try: cutname = name[:name.rindex("-") + 1] except ValueError as e: cutname = name res[cutname] = resolved_name return res
[docs] def resolve(self, dllname): """The method used to resolve a DLL name using the ApiSetMap. The behavior should match the non-exported function ``ntdll!ApiSetResolveToHost`` """ try: cutname = dllname[:dllname.rindex("-") + 1] except ValueError as e: return None return self.resolution_dict[cutname]
[docs]class ApiSetMapVersion2(ApiSetMap): """Represent an ApiSetMap version-2""" version = 2 #: The version of the ApiSetMap def namespace(self): return gdef.API_SET_NAMESPACE_ARRAY_V2.from_address(self.base) def entries_array(self): namespace = self.namespace() array_addr = ctypes.addressof(namespace.Array) array_size = namespace.Count return (gdef.API_SET_NAMESPACE_ENTRY_V2 * array_size).from_address(array_addr) get_entry_name = ApiSetMap.get_entry_name_basicimpl def values_for_entry(self, entry): values_array_v2 = (gdef.API_SET_VALUE_ARRAY_V2).from_address(self.base + entry.DataOffset) array_size = values_array_v2.Count array_addr = ctypes.addressof(values_array_v2.Array) values_array = (gdef.API_SET_VALUE_ENTRY_V2 * array_size).from_address(array_addr) res = [] for value in values_array: if value.ValueLength: v = self.read_apiset_wstring(value.ValueOffset, value.ValueLength) res.append(v) return res
[docs]class ApiSetMapVersion4(ApiSetMap): """Represent an ApiSetMap version-4""" version = 4 #: The version of the ApiSetMap def namespace(self): return gdef.API_SET_NAMESPACE_ARRAY_V4.from_address(self.base) def entries_array(self): namespace = self.namespace() array_addr = ctypes.addressof(namespace.Array) array_size = namespace.Count return (gdef.API_SET_NAMESPACE_ENTRY_V4 * array_size).from_address(array_addr) get_entry_name = ApiSetMap.get_entry_name_basicimpl def values_for_entry(self, entry): values_array_v2 = (gdef.API_SET_VALUE_ARRAY_V4).from_address(self.base + entry.DataOffset) array_size = values_array_v2.Count array_addr = ctypes.addressof(values_array_v2.Array) values_array = (gdef.API_SET_VALUE_ENTRY * array_size).from_address(array_addr) res = [] for value in values_array: if value.ValueLength: v = self.read_apiset_wstring(value.ValueOffset, value.ValueLength) res.append(v) return res
[docs]class ApiSetMapVersion6(ApiSetMap): """Represent an ApiSetMap version-6""" version = 6 #: The version of the ApiSetMap def namespace(self): return gdef.API_SET_NAMESPACE_V6.from_address(self.base) get_entry_name = ApiSetMap.get_entry_name_basicimpl def entries_array(self): namespace = self.namespace() array_offset = namespace.EntryOffset array_size = namespace.Count return (gdef.API_SET_NAMESPACE_ENTRY_V6 * array_size).from_address(self.base + array_offset) def values_for_entry(self, entry): values_array = (gdef.API_SET_VALUE_ENTRY * entry.ValueCount).from_address(self.base + entry.ValueOffset) res = [] for value in values_array: if value.ValueLength: v = self.read_apiset_wstring(value.ValueOffset, value.ValueLength) res.append(v) return res
API_SET_MAP_BY_VERSION = { 2: ApiSetMapVersion2, 4: ApiSetMapVersion4, 6: ApiSetMapVersion6, }