# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.

import hashlib
from typing import Any

from Crypto.Cipher import ARC4

import speakeasy.windows.objman as objman
import speakeasy.winenv.arch as _arch
import speakeasy.winenv.defs.registry.reg as regdefs
import speakeasy.winenv.defs.windows.advapi32 as adv32
import speakeasy.winenv.defs.windows.kernel32 as k32
import speakeasy.winenv.defs.windows.windows as windefs
from speakeasy.profiler_events import PROC_CREATE, REG_CREATE, REG_LIST, REG_OPEN, REG_READ, REG_WRITE

from .. import api

SERVICE_STATUS_HANDLE_BASE = 0x1000


class AdvApi32(api.ApiHandler):
    """
    Implements exported functions from advapi32.dll
    """

    name = "advapi32"
    apihook = api.ApiHandler.apihook
    impdata = api.ApiHandler.impdata

    def __init__(self, emu):
        super().__init__(emu)
        self.funcs: dict[str, Any] = {}
        self.data: dict[str, Any] = {}
        self.hash_objects: dict[int, Any] = {}
        self.key_objects: dict[int, Any] = {}
        self.k32types = k32
        self.win = adv32
        self.curr_rand: int = 0
        self.curr_handle: int = 0x2800
        self.service_status_handle: int = SERVICE_STATUS_HANDLE_BASE

        self.rc4: Any | None = None

        super().__get_hook_attrs__(self)

    def get_handle(self):
        self.curr_handle += 4
        return self.curr_handle

    @apihook("RegOpenKey", argc=3, conv=_arch.CALL_CONV_STDCALL)
    def RegOpenKey(self, emu, argv, ctx={}):
        """
        LSTATUS RegOpenKeyA(
          HKEY   hKey,
          LPCSTR lpSubKey,
          PHKEY  phkResult
        );
        """

        hKey, lpSubKey, phkResult = argv
        rv = windefs.ERROR_SUCCESS
        hnd = 0

        hkey_name = regdefs.get_hkey_type(hKey)
        if hkey_name:
            argv[0] = hkey_name
            if not hnd and not lpSubKey:
                hnd = hKey
        else:
            key_obj = emu.regman.get_key_from_handle(hKey)
            if not key_obj:
                return windefs.ERROR_PATH_NOT_FOUND
            hkey_name = key_obj.path

        cw = self.get_char_width(ctx)
        if lpSubKey:
            lpSubKey = self.read_mem_string(lpSubKey, cw)
            argv[1] = lpSubKey

            if hkey_name and lpSubKey:
                if not lpSubKey.startswith("\\"):
                    lpSubKey = "\\" + lpSubKey
                lpSubKey = hkey_name + lpSubKey

            hnd = self.reg_open_key(lpSubKey, create=False)
            if not hnd:
                rv = windefs.ERROR_PATH_NOT_FOUND

            self.record_registry_access_event(lpSubKey, REG_OPEN, handle=hnd)

        if phkResult and hnd:
            self.mem_write(phkResult, hnd.to_bytes(self.get_ptr_size(), "little"))

        return rv

    @apihook("RegOpenKeyEx", argc=5, conv=_arch.CALL_CONV_STDCALL)
    def RegOpenKeyEx(self, emu, argv, ctx={}):
        """
        LSTATUS RegOpenKeyEx(
          HKEY   hKey,
          LPTSTR lpSubKey,
          DWORD  ulOptions,
          REGSAM samDesired,
          PHKEY  phkResult
        );
        """

        hKey, lpSubKey, ulOptions, samDesired, phkResult = argv
        rv = windefs.ERROR_SUCCESS

        hnd = 0

        hkey_name = regdefs.get_hkey_type(hKey)
        if hkey_name:
            argv[0] = hkey_name
            if not hnd and not lpSubKey:
                hnd = hKey

        cw = self.get_char_width(ctx)
        if lpSubKey:
            lpSubKey = self.read_mem_string(lpSubKey, cw)
            argv[1] = lpSubKey

            if hkey_name and lpSubKey:
                if not lpSubKey.startswith("\\"):
                    lpSubKey = "\\" + lpSubKey
                lpSubKey = hkey_name + lpSubKey

            hnd = self.reg_open_key(lpSubKey, create=False)
            if not hnd:
                rv = windefs.ERROR_PATH_NOT_FOUND

            self.record_registry_access_event(lpSubKey, REG_OPEN, handle=hnd)

        if phkResult and hnd:
            self.mem_write(phkResult, hnd.to_bytes(self.get_ptr_size(), "little"))

        return rv

    @apihook("RegQueryValueEx", argc=6, conv=_arch.CALL_CONV_STDCALL)
    def RegQueryValueEx(self, emu, argv, ctx={}):
        """
        LSTATUS RegQueryValueEx(
          HKEY    hKey,
          LPTSTR  lpValueName,
          LPDWORD lpReserved,
          LPDWORD lpType,
          LPBYTE  lpData,
          LPDWORD lpcbData
        );
        """

        hKey, lpValueName, lpReserved, lpType, lpData, lpcbData = argv
        rv = windefs.ERROR_SUCCESS

        cw = self.get_char_width(ctx)
        if lpValueName:
            lpValueName = self.read_mem_string(lpValueName, cw)
            argv[1] = lpValueName

        type_name = regdefs.get_value_type(lpType)
        if type_name:
            argv[3] = type_name

        length = 0
        if lpcbData:
            length = self.mem_read(lpcbData, 4)
            length = int.from_bytes(length, "little")
            argv[5] = length

        key = self.reg_get_key(hKey)
        if key:
            val = key.get_value(lpValueName)
            if val:
                output = b""
                typ = val.get_type()
                data = val.get_data()
                if typ == "REG_SZ":
                    output = data.encode("utf-8")

                if not lpData and not lpcbData:
                    rv = windefs.ERROR_SUCCESS
                else:
                    if lpcbData:
                        self.mem_write(lpcbData, len(output).to_bytes(4, "little"))

                    if len(output) > length:
                        rv = windefs.ERROR_INSUFFICIENT_BUFFER
                    else:
                        if lpData:
                            self.mem_write(lpData, output)

            # For now, return an empty buffer
            else:
                output = b"\x00" * length
                if lpData:
                    try:
                        self.mem_write(lpData, output)
                    except Exception:
                        return windefs.ERROR_INVALID_PARAMETER
                if lpcbData:
                    self.mem_write(lpcbData, len(output).to_bytes(4, "little"))
                rv = windefs.ERROR_SUCCESS

            kp = key.get_path()
            self.record_registry_access_event(
                kp,
                REG_READ,
                value_name=lpValueName,
                data=output,
                size=length,
                buffer=lpData,
            )

        return rv

    @apihook("RegSetValueEx", argc=6, conv=_arch.CALL_CONV_STDCALL)
    def RegSetValueEx(self, emu, argv, ctx={}):
        """
        LSTATUS RegSetValueEx(
          HKEY       hKey,
          LPCSTR     lpValueName,
          DWORD      Reserved,
          DWORD      dwType,
          const BYTE *lpData,
          DWORD      cbData
        );
        """

        hKey, lpValueName, _reserved, dwType, lpData, cbData = argv

        key = self.reg_get_key(hKey)
        if not key:
            return windefs.ERROR_INVALID_HANDLE

        cw = self.get_char_width(ctx)
        value_name = ""
        if lpValueName:
            value_name = self.read_mem_string(lpValueName, cw)
            argv[1] = value_name

        value_data = ""
        if lpData and cbData:
            raw = self.mem_read(lpData, cbData)
            if dwType in (regdefs.REG_SZ, regdefs.REG_EXPAND_SZ):
                value_data = raw.decode("utf-16le" if cw == 2 else "utf-8", errors="ignore").rstrip("\x00")
            elif dwType == regdefs.REG_MULTI_SZ:
                value_data = raw.decode("utf-16le" if cw == 2 else "utf-8", errors="ignore").rstrip("\x00")
            elif dwType == regdefs.REG_DWORD:
                value_data = int.from_bytes(raw[:4].ljust(4, b"\x00"), "little")
            elif dwType == regdefs.REG_QWORD:
                value_data = int.from_bytes(raw[:8].ljust(8, b"\x00"), "little")
            else:
                value_data = raw

        value = key.get_value(value_name)
        if value:
            value.type = dwType
            value.data = value.normalize_value(dwType, value_data)
        else:
            key.create_value(value_name, dwType, value_data)

        self.record_registry_access_event(
            key.get_path(),
            REG_WRITE,
            value_name=value_name,
            data=raw if lpData and cbData else b"",
            handle=hKey,
            size=cbData,
            buffer=lpData,
        )

        return windefs.ERROR_SUCCESS

    @apihook("RegCloseKey", argc=1, conv=_arch.CALL_CONV_STDCALL)
    def RegCloseKey(self, emu, argv, ctx={}):
        """
        LSTATUS RegCloseKey(
          HKEY hKey
        );
        """

        (hKey,) = argv
        rv = windefs.ERROR_SUCCESS

        key = self.reg_get_key(hKey)
        if not key:
            rv = windefs.ERROR_INVALID_HANDLE

        return rv

    @apihook("RegEnumKey", argc=4, conv=_arch.CALL_CONV_STDCALL)
    def RegEnumKey(self, emu, argv, ctx={}):
        """
        LSTATUS RegEnumKey(
          HKEY  hKey,
          DWORD dwIndex,
          LPTSTR lpName,
          DWORD cchName
        );
        """

        hKey, dwIndex, lpName, cchName = argv

        _argv = argv + [0, 0, 0, 0]
        rv = self.RegEnumKeyEx(emu, _argv, ctx)
        argv[:] = _argv[:4]

        return rv

    @apihook("RegEnumKeyEx", argc=8, conv=_arch.CALL_CONV_STDCALL)
    def RegEnumKeyEx(self, emu, argv, ctx={}):
        """
        LSTATUS RegEnumKeyEx(
            HKEY      hKey,
            DWORD     dwIndex,
            LPSTR     lpName,
            LPDWORD   lpcchName,
            LPDWORD   lpReserved,
            LPSTR     lpClass,
            LPDWORD   lpcchClass,
            PFILETIME lpftLastWriteTime
        );
        """

        hKey, dwIndex, lpName, cchName, res, pcls, cchcls, last_write = argv

        cw = self.get_char_width(ctx)
        rv = windefs.ERROR_INVALID_HANDLE
        if hKey:
            key = self.reg_get_key(hKey)
            argv[0] = key.get_path()
            if not key:
                rv = windefs.ERROR_INVALID_HANDLE
            else:
                subkeys = self.reg_get_subkeys(key)
                if (dwIndex + 1) > len(subkeys):
                    rv = windefs.ERROR_NO_MORE_ITEMS
                else:
                    if lpName:
                        sk = subkeys[dwIndex]
                        name = sk.get_path()
                        if cw == 2:
                            name = name.encode("utf-16le")
                        else:
                            name = name.encode("utf-8")
                        self.mem_write(lpName, name)
                        rv = windefs.ERROR_SUCCESS
            self.record_registry_access_event(key.get_path(), REG_LIST)
        return rv

    @apihook("RegCreateKey", argc=3)
    def RegCreateKey(self, emu, argv, ctx={}):
        """
        LSTATUS RegCreateKey(
            HKEY    hKey,
            LPCWSTR lpSubKey,
            PHKEY   phkResult
        );
        """
        hkey, lpSubKey, phkResult = argv
        rv = windefs.ERROR_INVALID_HANDLE
        if hkey:
            key = self.reg_get_key(hkey)
            argv[0] = key.get_path()
            if not key:
                rv = windefs.ERROR_INVALID_HANDLE
            else:
                cw = self.get_char_width(ctx)
                if lpSubKey:
                    lpSubKey = self.read_mem_string(lpSubKey, cw)
                    argv[1] = lpSubKey
                    sub_key_path = key.get_path() + "\\" + lpSubKey
                    self.emu.reg_create_key(sub_key_path)
                    self.record_registry_access_event(sub_key_path, REG_CREATE)
                else:
                    hkey = (hkey).to_bytes(self.get_ptr_size(), "little")
                    self.mem_write(phkResult, hkey)
                    rv = windefs.ERROR_SUCCESS
        return rv

    @apihook("RegCreateKeyEx", argc=9, conv=_arch.CALL_CONV_STDCALL)
    def RegCreateKeyEx(self, emu, argv, ctx={}):
        """
        LSTATUS RegCreateKeyExA(
          HKEY                  hKey,
          LPCSTR                lpSubKey,
          DWORD                 Reserved,
          LPSTR                 lpClass,
          DWORD                 dwOptions,
          REGSAM                samDesired,
          const LPSECURITY_ATTRIBUTES lpSecurityAttributes,
          PHKEY                 phkResult,
          LPDWORD               lpdwDisposition
        );
        """
        hKey, lpSubKey, _reserved, _lpClass, _dwOptions, _samDesired, _sa, phkResult, lpdwDisposition = argv

        key_path = ""
        hkey_name = regdefs.get_hkey_type(hKey)
        if hkey_name:
            argv[0] = hkey_name
            key_path = hkey_name
        else:
            key_obj = self.reg_get_key(hKey)
            if not key_obj:
                return windefs.ERROR_INVALID_HANDLE
            key_path = key_obj.get_path()

        cw = self.get_char_width(ctx)
        if lpSubKey:
            sub_key = self.read_mem_string(lpSubKey, cw)
            argv[1] = sub_key
            if key_path and sub_key:
                if not sub_key.startswith("\\"):
                    sub_key = "\\" + sub_key
                key_path = key_path + sub_key

        existing = self.emu.reg_get_key(path=key_path)
        hnd = self.reg_open_key(key_path, create=True)
        if not hnd:
            return windefs.ERROR_PATH_NOT_FOUND

        if phkResult:
            self.mem_write(phkResult, hnd.to_bytes(self.get_ptr_size(), "little"))

        if lpdwDisposition:
            disp = 2 if existing else 1
            self.mem_write(lpdwDisposition, disp.to_bytes(4, "little"))

        self.record_registry_access_event(key_path, REG_CREATE, handle=hnd)
        return windefs.ERROR_SUCCESS

    @apihook("RegDeleteValue", argc=2, conv=_arch.CALL_CONV_STDCALL)
    def RegDeleteValue(self, emu, argv, ctx={}):
        """
        LSTATUS RegDeleteValueA(
          HKEY   hKey,
          LPCSTR lpValueName
        );
        """
        hKey, lpValueName = argv

        key = self.reg_get_key(hKey)
        if not key:
            return windefs.ERROR_INVALID_HANDLE

        cw = self.get_char_width(ctx)
        value_name = ""
        if lpValueName:
            value_name = self.read_mem_string(lpValueName, cw)
            argv[1] = value_name

        value = key.get_value(value_name)
        if not value:
            return windefs.ERROR_FILE_NOT_FOUND

        key.values.remove(value)
        return windefs.ERROR_SUCCESS

    @apihook("RegQueryInfoKey", argc=12, conv=_arch.CALL_CONV_STDCALL)
    def RegQueryInfoKey(self, emu, argv, ctx={}):
        # TODO: stub
        """
        LSTATUS RegQueryInfoKeyA(
          HKEY      hKey,
          LPSTR     lpClass,
          LPDWORD   lpcchClass,
          LPDWORD   lpReserved,
          LPDWORD   lpcSubKeys,
          LPDWORD   lpcbMaxSubKeyLen,
          LPDWORD   lpcbMaxClassLen,
          LPDWORD   lpcValues,
          LPDWORD   lpcbMaxValueNameLen,
          LPDWORD   lpcbMaxValueLen,
          LPDWORD   lpcbSecurityDescriptor,
          PFILETIME lpftLastWriteTime
        );
        """

        (
            hKey,
            lpClass,
            lpcchClass,
            _,
            subkeys,
            max_subkey_len,
            max_class_len,
            values,
            max_value_name_len,
            max_value_len,
            sec_desc,
            last_write,
        ) = argv

        rv = windefs.ERROR_SUCCESS

        hkey_name = regdefs.get_hkey_type(hKey)
        if hkey_name:
            argv[0] = hkey_name

        key = self.reg_get_key(hKey)
        if not key:
            rv = windefs.ERROR_INVALID_HANDLE

        return rv

    @apihook("OpenProcessToken", argc=3, conv=_arch.CALL_CONV_STDCALL)
    def OpenProcessToken(self, emu, argv, ctx={}):
        """
        BOOL OpenProcessToken(
          HANDLE  ProcessHandle,
          DWORD   DesiredAccess,
          PHANDLE pTokenHandle
        );
        """

        hProcess, DesiredAccess, pTokenHandle = argv
        rv = 0

        if hProcess == self.get_max_int():
            obj = emu.get_current_process()
        else:
            obj = self.get_object_from_handle(hProcess)

        if obj:
            token = obj.token
            hToken = token.get_handle()

            if pTokenHandle:
                hnd = (hToken).to_bytes(self.get_ptr_size(), "little")
                self.mem_write(pTokenHandle, hnd)
                rv = 1
                emu.set_last_error(windefs.ERROR_SUCCESS)
            else:
                emu.set_last_error(windefs.ERROR_INVALID_PARAMETER)

        return rv

    @apihook("OpenThreadToken", argc=4, conv=_arch.CALL_CONV_STDCALL)
    def OpenThreadToken(self, emu, argv, ctx={}):
        """
        BOOL OpenThreadToken(
            HANDLE  ThreadHandle,
            DWORD   DesiredAccess,
            BOOL    OpenAsSelf,
            PHANDLE TokenHandle
        );
        """

        ThreadHandle, DesiredAccess, OpenAsSelf, pTokenHandle = argv
        rv = 0

        if ThreadHandle == self.get_max_int():
            obj = emu.get_current_thread()
        else:
            obj = self.get_object_from_handle(ThreadHandle)

        if obj:
            token = obj.token
            hToken = token.get_handle()

            if pTokenHandle:
                hnd = (hToken).to_bytes(self.get_ptr_size(), "little")
                self.mem_write(pTokenHandle, hnd)
                rv = 1
                emu.set_last_error(windefs.ERROR_SUCCESS)
            else:
                emu.set_last_error(windefs.ERROR_INVALID_PARAMETER)

        return rv

    @apihook("DuplicateTokenEx", argc=6, conv=_arch.CALL_CONV_STDCALL)
    def DuplicateTokenEx(self, emu, argv, ctx={}):
        """
        BOOL DuplicateTokenEx(
          HANDLE                       hExistingToken,
          DWORD                        dwDesiredAccess,
          LPSECURITY_ATTRIBUTES        lpTokenAttributes,
          SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
          TOKEN_TYPE                   TokenType,
          PHANDLE                      phNewToken
        );
        """

        (hExistingToken, access, token_attrs, imp_level, toktype, phNewToken) = argv
        rv = 0

        obj = self.get_object_from_handle(hExistingToken)

        if obj:
            new_token = emu.new_object(objman.Token)
            hnd_new_token = new_token.get_handle()

            if phNewToken:
                hnd = (hnd_new_token).to_bytes(self.get_ptr_size(), "little")
                self.mem_write(phNewToken, hnd)
                rv = 1
                emu.set_last_error(windefs.ERROR_SUCCESS)
            else:
                emu.set_last_error(windefs.ERROR_INVALID_PARAMETER)

        return rv

    @apihook("SetTokenInformation", argc=4, conv=_arch.CALL_CONV_STDCALL)
    def SetTokenInformation(self, emu, argv, ctx={}):
        """
        BOOL SetTokenInformation(
          HANDLE                  TokenHandle,
          TOKEN_INFORMATION_CLASS TokenInformationClass,
          LPVOID                  TokenInformation,
          DWORD                   TokenInformationLength
        );
        """

        handle, info_class, info, info_len = argv

        rv = 1

        return rv

    @apihook("StartServiceCtrlDispatcher", argc=1)
    def StartServiceCtrlDispatcher(self, emu, argv, ctx={}):
        """
        BOOL StartServiceCtrlDispatcher(
          const SERVICE_TABLE_ENTRY *lpServiceStartTable
        );
        """
        (lpServiceStartTable,) = argv

        try:
            cw = self.get_char_width(ctx)
        except Exception:
            cw = 1

        ste = self.win.SERVICE_TABLE_ENTRY(emu.get_ptr_size())
        entry = self.mem_cast(ste, lpServiceStartTable)

        argv[0] = "lpServiceStartTable=["

        while entry.lpServiceName != windefs.NULL or entry.lpServiceProc != windefs.NULL:
            service_name = "Service"
            if entry.lpServiceName != windefs.NULL:
                service_name = self.read_mem_string(entry.lpServiceName, cw)
                argv[0] += f" {{ lpServiceName={service_name}"
            else:
                argv[0] += " { lpServiceName=NULL"

            if entry.lpServiceProc != windefs.NULL:
                service_main = entry.lpServiceProc
                argv[0] += f", lpServiceProc={hex(service_main)} }} "
                argc, svc_argv = emu.build_service_main_args(service_name, char_width=cw)
                self.queue_run("thread.service", service_main, [argc, svc_argv])
            else:
                argv[0] += ", lpServiceProc=NULL } "

            lpServiceStartTable += self.sizeof(ste)
            ste = self.win.SERVICE_TABLE_ENTRY(emu.get_ptr_size())
            entry = self.mem_cast(ste, lpServiceStartTable)

        argv[0] += "]"

        rv = True
        emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("RegisterServiceCtrlHandler", argc=2)
    def RegisterServiceCtrlHandler(self, emu, argv, ctx={}):
        """
        SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerA(
            LPCSTR             lpServiceName,
            LPHANDLER_FUNCTION lpHandlerProc
            );
        """

        lpServiceName, lpHandlerProc = argv

        # dummy SERVICE_STATUS_HANDLE
        self.service_status_handle += 1

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return self.service_status_handle

    @apihook("RegisterServiceCtrlHandlerEx", argc=3)
    def RegisterServiceCtrlHandlerEx(self, emu, argv, ctx={}):
        """
        SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerExA(
            LPCSTR                lpServiceName,
            LPHANDLER_FUNCTION_EX lpHandlerProc,
            LPVOID                lpContext
        );
        """
        lpServiceName, lpHandlerProc, lpContext = argv

        return self.RegisterServiceCtrlHandler(self, emu, [lpServiceName, lpHandlerProc], ctx)

    @apihook("SetServiceStatus", argc=2)
    def SetServiceStatus(self, emu, argv, ctx={}):
        """
        BOOL SetServiceStatus(
            SERVICE_STATUS_HANDLE hServiceStatus,
            LPSERVICE_STATUS      lpServiceStatus
            );
        """

        hServiceStatus, lpServiceStatus = argv

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return 0x1

    @apihook("RevertToSelf", argc=0)
    def RevertToSelf(self, emu, argv, ctx={}):
        """
        BOOL RevertToSelf();
        """
        return 1

    @apihook("ImpersonateLoggedOnUser", argc=1)
    def ImpersonateLoggedOnUser(self, emu, argv, ctx={}):
        """
        BOOL ImpersonateLoggedOnUser(
        HANDLE hToken
        );
        """
        return 1

    @apihook("OpenSCManager", argc=3)
    def OpenSCManager(self, emu, argv, ctx={}):
        """
        SC_HANDLE OpenSCManager(
          LPCSTR lpMachineName,
          LPCSTR lpDatabaseName,
          DWORD  dwDesiredAccess
        );
        """
        lpMachineName, lpDatabaseName, dwDesiredAccess = argv

        hScm = self.mem_alloc(size=8)
        emu.set_last_error(windefs.ERROR_SUCCESS)

        return hScm

    @apihook("CreateService", argc=13)
    def CreateService(self, emu, argv, ctx={}):
        """
        SC_HANDLE CreateServiceA(
          SC_HANDLE hSCManager,
          LPCSTR    lpServiceName,
          LPCSTR    lpDisplayName,
          DWORD     dwDesiredAccess,
          DWORD     dwServiceType,
          DWORD     dwStartType,
          DWORD     dwErrorControl,
          LPCSTR    lpBinaryPathName,
          LPCSTR    lpLoadOrderGroup,
          LPDWORD   lpdwTagId,
          LPCSTR    lpDependencies,
          LPCSTR    lpServiceStartName,
          LPCSTR    lpPassword
        );
        """
        (
            hScm,
            svc_name,
            disp_name,
            access,
            svc_type,
            start_type,
            error_ctrl,
            bin_path,
            load_group,
            tag_id,
            deps,
            svc_start_name,
            password,
        ) = argv

        cw = self.get_char_width(ctx)

        if svc_name:
            _sname = self.read_mem_string(svc_name, cw)
            argv[1] = _sname
        if disp_name:
            _dname = self.read_mem_string(disp_name, cw)
            argv[2] = _dname
        if bin_path:
            _bpname = self.read_mem_string(bin_path, cw)
            argv[7] = _bpname

        hSvc = self.mem_alloc(size=8)
        emu.set_last_error(windefs.ERROR_SUCCESS)

        return hSvc

    @apihook("StartService", argc=3)
    def StartService(self, emu, argv, ctx={}):
        """
        BOOL StartService(
          SC_HANDLE hService,
          DWORD     dwNumServiceArgs,
          LPCSTR    *lpServiceArgVectors
        );
        """
        hService, dwNumServiceArgs, lpServiceArgVectors = argv

        rv = 1

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("StartServiceA", argc=3)
    def StartServiceA(self, emu, argv, ctx={}):
        return self.StartService(emu, argv, ctx)

    @apihook("ControlService", argc=3)
    def ControlService(self, emu, argv, ctx={}):
        """
        BOOL ControlService(
          [in]  SC_HANDLE        hService,
          [in]  DWORD            dwControl,
          [out] LPSERVICE_STATUS lpServiceStatus
        );
        """
        hService, dwControl, lpServiceStatus = argv

        rv = 1

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("QueryServiceStatus", argc=2)
    def QueryServiceStatus(self, emu, argv, ctx={}):
        """
        BOOL QueryServiceStatus(
          SC_HANDLE        hService,
          LPSERVICE_STATUS lpServiceStatus
        );
        """
        hService, lpServiceStatus = argv

        if not hService:
            emu.set_last_error(windefs.ERROR_INVALID_HANDLE)
            return 0

        if lpServiceStatus:
            service_status = (
                (0x10).to_bytes(4, "little")
                + (0x4).to_bytes(4, "little")
                + (0x1).to_bytes(4, "little")
                + (0x0).to_bytes(4, "little")
                + (0x0).to_bytes(4, "little")
                + (0x0).to_bytes(4, "little")
                + (0x0).to_bytes(4, "little")
            )
            self.mem_write(lpServiceStatus, service_status)

        emu.set_last_error(windefs.ERROR_SUCCESS)
        return 1

    @apihook("QueryServiceConfig", argc=4)
    @apihook("QueryServiceConfigA", argc=4)
    @apihook("QueryServiceConfigW", argc=4)
    def QueryServiceConfig(self, emu, argv, ctx={}):
        """
        BOOL QueryServiceConfigA(
          SC_HANDLE               hService,
          LPQUERY_SERVICE_CONFIGA lpServiceConfig,
          DWORD                   cbBufSize,
          LPDWORD                 pcbBytesNeeded
        );
        """
        hService, lpServiceConfig, cbBufSize, pcbBytesNeeded = argv

        if not hService:
            emu.set_last_error(windefs.ERROR_INVALID_HANDLE)
            return 0

        ptr_size = self.get_ptr_size()
        required = (4 * 4) + (5 * ptr_size)

        if pcbBytesNeeded:
            self.mem_write(pcbBytesNeeded, required.to_bytes(4, "little"))

        if not lpServiceConfig or cbBufSize < required:
            emu.set_last_error(windefs.ERROR_INSUFFICIENT_BUFFER)
            return 0

        buf = bytearray()
        buf.extend((0x10).to_bytes(4, "little"))
        buf.extend((0x2).to_bytes(4, "little"))
        buf.extend((0x1).to_bytes(4, "little"))
        buf.extend((0).to_bytes(ptr_size, "little"))
        buf.extend((0).to_bytes(ptr_size, "little"))
        buf.extend((0).to_bytes(4, "little"))
        buf.extend((0).to_bytes(ptr_size, "little"))
        buf.extend((0).to_bytes(ptr_size, "little"))
        buf.extend((0).to_bytes(ptr_size, "little"))

        self.mem_write(lpServiceConfig, bytes(buf))
        emu.set_last_error(windefs.ERROR_SUCCESS)
        return 1

    @apihook("CloseServiceHandle", argc=1)
    def CloseServiceHandle(self, emu, argv, ctx={}):
        """
        BOOL CloseServiceHandle(
          SC_HANDLE hSCObject
        );
        """
        (CloseServiceHandle,) = argv

        self.mem_free(CloseServiceHandle)

        rv = 1

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("ChangeServiceConfig", argc=11)
    def ChangeServiceConfig(self, emu, argv, ctx={}):
        """
        BOOL ChangeServiceConfigA(
          SC_HANDLE hService,
          DWORD     dwServiceType,
          DWORD     dwStartType,
          DWORD     dwErrorControl,
          LPCSTR    lpBinaryPathName,
          LPCSTR    lpLoadOrderGroup,
          LPDWORD   lpdwTagId,
          LPCSTR    lpDependencies,
          LPCSTR    lpServiceStartName,
          LPCSTR    lpPassword,
          LPCSTR    lpDisplayName
        );
        """
        (
            _hService,
            _dwServiceType,
            _dwStartType,
            _dwErrorControl,
            lpBinaryPathName,
            lpLoadOrderGroup,
            _lpdwTagId,
            lpDependencies,
            lpServiceStartName,
            lpPassword,
            lpDisplayName,
        ) = argv

        cw = self.get_char_width(ctx)

        if lpBinaryPathName:
            argv[4] = self.read_mem_string(lpBinaryPathName, cw)
        if lpLoadOrderGroup:
            argv[5] = self.read_mem_string(lpLoadOrderGroup, cw)
        if lpDependencies:
            argv[7] = self.read_mem_string(lpDependencies, cw)
        if lpServiceStartName:
            argv[8] = self.read_mem_string(lpServiceStartName, cw)
        if lpPassword:
            argv[9] = self.read_mem_string(lpPassword, cw)
        if lpDisplayName:
            argv[10] = self.read_mem_string(lpDisplayName, cw)

        emu.set_last_error(windefs.ERROR_SUCCESS)
        return 1

    @apihook("ChangeServiceConfig2", argc=3)
    def ChangeServiceConfig2(self, emu, argv, ctx={}):
        """
        BOOL ChangeServiceConfig2(
          SC_HANDLE hService,
          DWORD     dwInfoLevel,
          LPVOID    lpInfo
        );
        """
        hService, dwInfoLevel, lpInfo = argv

        rv = 1

        emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("SystemFunction036", argc=2)
    def RtlGenRandom(self, emu, argv, ctx={}):
        """
        BOOLEAN RtlGenRandom(
            PVOID RandomBuffer,
            ULONG RandomBufferLength
        );
        """
        RandomBuffer, RandomBufferLength = argv

        rv = False
        if RandomBuffer and RandomBufferLength:
            buf = bytes([i for i in range(RandomBufferLength)])
            self.mem_write(RandomBuffer, buf)
            rv = True

        return rv

    @apihook("CryptAcquireContext", argc=5)
    def CryptAcquireContext(self, emu, argv, ctx={}):
        """
        BOOL CryptAcquireContext(
            HCRYPTPROV *phProv,
            LPCSTR     szContainer,
            LPCSTR     szProvider,
            DWORD      dwProvType,
            DWORD      dwFlags
        );
        """
        phProv, szContainer, szProvider, dwProvType, dwFlags = argv
        cont_str, prov_str = "", ""
        cw = self.get_char_width(ctx)
        rv = False

        if szContainer:
            cont_str = self.read_mem_string(szContainer, cw)
            argv[1] = cont_str
        if szProvider:
            prov_str = self.read_mem_string(szProvider, cw)
            argv[2] = prov_str

        cm = emu.get_crypt_manager()
        hnd = cm.crypt_open(cname=cont_str, pname=prov_str, ptype=dwProvType, flags=dwFlags)

        if hnd and phProv:
            self.mem_write(phProv, hnd.to_bytes(emu.get_ptr_size(), "little"))
            rv = True
            emu.set_last_error(windefs.ERROR_SUCCESS)

        return rv

    @apihook("CryptGenRandom", argc=3)
    def CryptGenRandom(self, emu, argv, ctx={}):
        """
        BOOL CryptGenRandom(
            HCRYPTPROV hProv,
            DWORD      dwLen,
            BYTE       *pbBuffer
        );
        """
        hProv, dwLen, pbBuffer = argv
        rv = False

        if pbBuffer:
            out = b"A" * dwLen
            self.mem_write(pbBuffer, out)
            rv = True

        return rv

    @apihook("AllocateAndInitializeSid", argc=11)
    def AllocateAndInitializeSid(self, emu, argv, ctx={}):
        """
        BOOL AllocateAndInitializeSid(
            PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,
            BYTE                      nSubAuthorityCount,
            DWORD                     nSubAuthority0,
            DWORD                     nSubAuthority1,
            DWORD                     nSubAuthority2,
            DWORD                     nSubAuthority3,
            DWORD                     nSubAuthority4,
            DWORD                     nSubAuthority5,
            DWORD                     nSubAuthority6,
            DWORD                     nSubAuthority7,
            PSID                      *pSid
        );
        """
        auth, count, sa0, sa1, sa2, sa3, sa4, sa5, sa6, sa7, pSid = argv
        rv = False

        if pSid:
            sid = self.mem_alloc(0x100, tag="api.struct.SID")
            self.mem_write(pSid, sid.to_bytes(emu.get_ptr_size(), "little"))
            rv = True

        return rv

    @apihook("CheckTokenMembership", argc=3)
    def CheckTokenMembership(self, emu, argv, ctx={}):
        """
        BOOL CheckTokenMembership(
            HANDLE TokenHandle,
            PSID   SidToCheck,
            PBOOL  IsMember
        );
        """
        TokenHandle, SidToCheck, IsMember = argv
        rv = False

        if IsMember:
            self.mem_write(IsMember, (1).to_bytes(4, "little"))
            rv = True
        return rv

    @apihook("FreeSid", argc=1)
    def FreeSid(self, emu, argv, ctx={}):
        """
        PVOID FreeSid(
            PSID pSid
        );
        """
        (pSid,) = argv
        rv = pSid

        if pSid:
            self.mem_free(pSid)
            rv = 0
        return rv

    @apihook("CryptReleaseContext", argc=2)
    def CryptReleaseContext(self, emu, argv, ctx={}):
        """
        BOOL CryptReleaseContext(
            HCRYPTPROV hProv,
            DWORD      dwFlags
        );
        """
        hProv, dwFlags = argv
        rv = True

        cm = emu.get_crypt_manager()
        cm.crypt_close(hProv)

        return rv

    @apihook("GetCurrentHwProfile", argc=1)
    def GetCurrentHwProfile(self, emu, argv, ctx={}):
        """
        BOOL GetCurrentHwProfileA(
          LPHW_PROFILE_INFOA lpHwProfileInfo
        );
        """
        (lpHwProfileInfo,) = argv

        if not lpHwProfileInfo:
            emu.set_last_error(windefs.ERROR_INVALID_PARAMETER)
            return 0

        cw = self.get_char_width(ctx)
        guid = "{00000000-0000-0000-0000-000000000000}"
        profile_name = "Speakeasy HW Profile"

        if cw == 2:
            guid_bytes = (guid + "\x00").encode("utf-16le")
            name_bytes = (profile_name + "\x00").encode("utf-16le")
            guid_bytes = guid_bytes.ljust(39 * 2, b"\x00")[: 39 * 2]
            name_bytes = name_bytes.ljust(80 * 2, b"\x00")[: 80 * 2]
        else:
            guid_bytes = (guid + "\x00").encode("utf-8")
            name_bytes = (profile_name + "\x00").encode("utf-8")
            guid_bytes = guid_bytes.ljust(39, b"\x00")[:39]
            name_bytes = name_bytes.ljust(80, b"\x00")[:80]

        out = (0).to_bytes(4, "little") + guid_bytes + name_bytes
        self.mem_write(lpHwProfileInfo, out)
        emu.set_last_error(windefs.ERROR_SUCCESS)
        return 1

    @apihook("GetUserName", argc=2)
    def GetUserName(self, emu, argv, ctx={}):
        """
        BOOL GetUserName(
            LPSTR   lpBuffer,
            LPDWORD pcbBuffer
        );
        """
        lpBuffer, pcbBuffer = argv
        rv = False
        cw = self.get_char_width(ctx)

        user_name = emu.config.user.name
        argv[0] = user_name

        if lpBuffer:
            if cw == 2:
                out = user_name.encode("utf-16le")
            elif cw == 1:
                out = user_name.encode("utf-8")
            self.mem_write(lpBuffer, out)
            rv = True
        if pcbBuffer:
            self.mem_write(pcbBuffer, (len(user_name)).to_bytes(4, "little"))

        return rv

    @apihook("LookupPrivilegeValue", argc=3)
    def LookupPrivilegeValue(self, emu, argv, ctx={}):
        """
        BOOL LookupPrivilegeValue(
            LPCSTR lpSystemName,
            LPCSTR lpName,
            PLUID  lpLuid
        );
        """
        sysname, name, luid = argv
        rv = False
        cw = self.get_char_width(ctx)

        if sysname:
            sysname = self.read_mem_string(sysname, cw)
            argv[0] = sysname
        if name:
            name = self.read_mem_string(name, cw)
            argv[1] = name
            rv = True

        return rv

    @apihook("AdjustTokenPrivileges", argc=6)
    def AdjustTokenPrivileges(self, emu, argv, ctx={}):
        """
        BOOL AdjustTokenPrivileges(
            HANDLE            TokenHandle,
            BOOL              DisableAllPrivileges,
            PTOKEN_PRIVILEGES NewState,
            DWORD             BufferLength,
            PTOKEN_PRIVILEGES PreviousState,
            PDWORD            ReturnLength
        );
        """
        rv = True

        return rv

    @apihook("GetTokenInformation", argc=5)
    def GetTokenInformation(self, emu, argv, ctx={}):
        """
        BOOL GetTokenInformation(
            HANDLE                  TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            LPVOID                  TokenInformation,
            DWORD                   TokenInformationLength,
            PDWORD                  ReturnLength
        );
        """
        hnd, info_class, info, info_len, ret_len = argv
        rv = True

        if not info_len:
            rv = False
            emu.set_last_error(windefs.ERROR_INSUFFICIENT_BUFFER)

        if info_class == 20 and info and emu.config.user.is_admin:
            self.mem_write(info, (1).to_bytes(4, "little"))
        if ret_len:
            self.mem_write(ret_len, (4).to_bytes(4, "little"))

        return rv

    @apihook("EqualSid", argc=2)
    def EqualSid(self, emu, argv, ctx={}):
        """
        BOOL EqualSid(
            PSID pSid1,
            PSID pSid2
        );
        """
        sid1, sid2 = argv
        rv = False

        if sid1 and sid2:
            s1 = self.mem_read(sid1, 10)
            s2 = self.mem_read(sid2, 10)
            if s1 == s2:
                rv = True

        return rv

    @apihook("GetSidIdentifierAuthority", argc=1)
    def GetSidIdentifierAuthority(self, emu, argv, ctx={}):
        """
        PSID_IDENTIFIER_AUTHORITY GetSidIdentifierAuthority(
          [in] PSID pSid
        );
        """
        (sid,) = argv

        # IdentifierAuthority is at offset 0x02 in the SID structure
        return sid + 2

    @apihook("GetSidSubAuthorityCount", argc=1)
    def GetSidSubAuthorityCount(self, emu, argv, ctx={}):
        """
        PUCHAR GetSidSubAuthorityCount(
            PSID pSid
        );
        """
        (sid,) = argv
        rv = 0

        if sid:
            rv = sid + 1

        return rv

    @apihook("GetSidSubAuthority", argc=2)
    def GetSidSubAuthority(self, emu, argv, ctx={}):
        """
        PDWORD GetSidSubAuthority(
          [in] PSID  pSid,
          [in] DWORD nSubAuthority
        );
        """
        sid, nsub = argv

        # SubAuthorities begin at offset 0x8
        return sid + 8 + (nsub * 4)

    @apihook("LookupAccountName", argc=7)
    def LookupAccountName(self, emu, argv, ctx={}):
        """
        BOOL LookupAccountNameA(
          [in, optional]  LPCSTR        lpSystemName,
          [in]            LPCSTR        lpAccountName,
          [out, optional] PSID          Sid,
          [in, out]       LPDWORD       cbSid,
          [out, optional] LPSTR         ReferencedDomainName,
          [in, out]       LPDWORD       cchReferencedDomainName,
          [out]           PSID_NAME_USE peUse
        );
        """

        ptr_sysname, ptr_acctname, ptr_sid, ptr_cbsid, ptr_domname, ptr_cchdomname, ptr_peuse = argv
        rv = 0

        cw = self.get_char_width(ctx)

        if ptr_sysname:
            sn = self.read_mem_string(ptr_sysname, cw)
            argv[0] = sn

        if not ptr_acctname:
            return rv

        acctname = self.read_mem_string(ptr_acctname, cw)
        argv[1] = acctname

        user = emu.config.user.name
        # Currently only supporting user SIDs specified in the config
        if user != acctname:
            return rv

        str_sid = emu.config.user.sid
        if not str_sid:
            return rv

        argv[2] = str_sid
        sid_struct = windefs.convert_sid_str_to_struct(emu.get_ptr_size(), str_sid)
        side_struct_size = sid_struct.sizeof()

        cbsid = self.mem_read(ptr_cbsid, 4)
        cbsid = int.from_bytes(cbsid, "little")
        argv[3] = cbsid
        if not cbsid:
            self.mem_write(ptr_cbsid, side_struct_size.to_bytes(4, "little"))
            return rv

        if cbsid < side_struct_size:
            return rv

        domain = emu.config.domain
        cchdomname = self.mem_read(ptr_cchdomname, 4)
        cbcchdomname = int.from_bytes(cchdomname, "little")
        argv[5] = cbcchdomname
        if not cbcchdomname:
            buf_size = len(domain) + 1
            self.mem_write(ptr_cchdomname, buf_size.to_bytes(4, "little"))
            return rv

        rv = 1

        self.mem_write(ptr_sid, self.get_bytes(sid_struct))

        self.write_mem_string(domain, ptr_domname, cw)
        argv[4] = domain

        # Currently only supporting user SIDs (SidTypeUser = 1)
        self.mem_write(ptr_peuse, (1).to_bytes(4, "little"))
        argv[6] = 1

        return rv

    @apihook("LookupAccountSid", argc=7)
    def LookupAccountSid(self, emu, argv, ctx={}):
        """
        BOOL LookupAccountSid(
            LPCSTR        lpSystemName,
            PSID          Sid,
            LPSTR         Name,
            LPDWORD       cchName,
            LPSTR         ReferencedDomainName,
            LPDWORD       cchReferencedDomainName,
            PSID_NAME_USE peUse
        );
        """
        sysname, sid, name, cchname, domname, cchdomname, peuse = argv
        rv = False

        cw = self.get_char_width(ctx)

        if not cchname or not cchdomname:
            return rv

        name_size = self.mem_read(cchname, 4)
        name_size = int.from_bytes(name_size, "little")

        dom_size = self.mem_read(cchdomname, 4)
        dom_size = int.from_bytes(dom_size, "little")

        self.write_mem_string("myuser", name, cw)
        self.write_mem_string("mydomain", domname, cw)
        rv = True

        if sysname:
            sn = self.read_mem_string(sysname, cw)
            argv[0] = sn

        return rv

    @apihook("CreateProcessAsUser", argc=11, conv=_arch.CALL_CONV_STDCALL)
    def CreateProcessAsUser(self, emu, argv, ctx={}):
        """
        BOOL CreateProcessAsUser(
          HANDLE                hToken,
          LPCSTR                lpApplicationName,
          LPSTR                 lpCommandLine,
          LPSECURITY_ATTRIBUTES lpProcessAttributes,
          LPSECURITY_ATTRIBUTES lpThreadAttributes,
          BOOL                  bInheritHandles,
          DWORD                 dwCreationFlags,
          LPVOID                lpEnvironment,
          LPCSTR                lpCurrentDirectory,
          LPSTARTUPINFOA        lpStartupInfo,
          LPPROCESS_INFORMATION lpProcessInformation
        );
        """
        token, app, cmd, pa, ta, inherit, flags, env, cd, si, ppi = argv

        cw = self.get_char_width(ctx)
        cmdstr = ""
        appstr = ""
        if app:
            appstr = self.read_mem_string(app, cw)
            argv[1] = appstr
        if cmd:
            cmdstr = self.read_mem_string(cmd, cw)
            if not appstr:
                appstr = cmdstr.split(" ")[0]
            argv[2] = cmdstr

        proc = emu.create_process(path=appstr, cmdline=cmdstr)
        proc_hnd = self.get_object_handle(proc)

        thread = proc.threads[0]
        thread_hnd = self.get_object_handle(thread)

        _pi = self.k32types.PROCESS_INFORMATION(emu.get_ptr_size())
        data = self.mem_cast(_pi, ppi)
        _pi.hProcess = proc_hnd
        _pi.hThread = thread_hnd
        _pi.dwProcessId = proc.pid
        _pi.dwThreadId = thread.tid

        self.mem_write(ppi, self.get_bytes(data))

        rv = 1

        self.record_process_event(proc, PROC_CREATE)
        return rv

    @apihook("CryptCreateHash", argc=5)
    def CryptCreateHash(self, emu, argv, ctx={}):
        """
        BOOL CryptCreateHash(
          HCRYPTPROV hProv,
          ALG_ID     Algid,
          HCRYPTKEY  hKey,
          DWORD      dwFlags,
          HCRYPTHASH *phHash
        );
        """

        hash_algs = {
            0x00008004: ("CALG_SHA1", hashlib.sha1),
            0x0000800C: ("CALG_SHA_256", hashlib.sha256),
            0x0000800D: ("CALG_SHA_384", hashlib.sha384),
            0x0000800E: ("CALG_SHA_512", hashlib.sha512),
            0x00008003: ("CALG_MD5", hashlib.md5),
        }

        hProv, Algid, hKey, dwFlags, phHash = argv
        argv[1] = hash_algs.get(Algid, Algid)[0]

        if hKey != 0:
            return 0

        if Algid not in hash_algs:
            emu.set_last_error(adv32.NTE_BAD_ALGID)
            return 0

        hnd = self.get_handle()
        self.hash_objects.update({hnd: hash_algs[Algid][1]()})
        self.mem_write(phHash, hnd.to_bytes(self.get_ptr_size(), "little"))
        return 1

    @apihook("CryptHashData", argc=4)
    def CryptHashData(self, emu, argv, ctx={}):
        """
        BOOL CryptHashData(
          HCRYPTHASH hHash,
          const BYTE *pbData,
          DWORD      dwDataLen,
          DWORD      dwFlags
        );
        """

        hHash, pbData, dwDataLen, dwFlags = argv
        hnd = self.hash_objects.get(hHash, None)
        if hnd is None:
            emu.set_last_error(windefs.ERROR_INVALID_HANDLE)
            return 0

        if dwDataLen <= 0:
            return 0

        data = self.mem_read(pbData, dwDataLen)
        hnd.update(data)
        return 1

    @apihook("CryptGetHashParam", argc=5)
    def CryptGetHashParam(self, emu, argv, ctx={}):
        """
        BOOL CryptGetHashParam(
          HCRYPTHASH hHash,
          DWORD      dwParam,
          BYTE       *pbData,
          DWORD      *pdwDataLen,
          DWORD      dwFlags
        );
        """
        hHash, dwParam, pbData, pdwDataLen, dwFlags = argv

        param_enums = {1: "HP_ALGID", 2: "HP_HASHVAL", 4: "HP_HASHSIZE", 5: "HP_HMAC_INFO"}

        if dwParam in param_enums.keys():
            argv[1] = param_enums[dwParam]

        return 1

    @apihook("CryptDestroyHash", argc=1)
    def CryptDestroyHash(self, emu, argv, ctx={}):
        """
        BOOL CryptDestroyHash(
          HCRYPTHASH hHash
        );
        """
        return 1

    @apihook("CryptDeriveKey", argc=5)
    def CryptDeriveKey(self, emu, argv, ctx={}):
        """
        BOOL CryptDeriveKey(
          HCRYPTPROV hProv,
          ALG_ID     Algid,
          HCRYPTHASH hBaseData,
          DWORD      dwFlags,
          HCRYPTKEY  *phKey
        );
        """

        hProv, Algid, hBaseData, dwFlags, phKey = argv

        # Only RC4 supported right now
        if Algid != 0x6801:
            return 0

        hnd = self.hash_objects.get(hBaseData, None)

        if hnd is None:
            emu.set_last_error(windefs.ERROR_INVALID_HANDLE)
            return 0

        # CryptDeriveKey zeroes out the last 11 bytes of the hash,
        # so we gotta do the same before it is written to the
        # phKey structure
        fixed_digest = hnd.digest()[:5] + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

        ptrsz = emu.get_ptr_size()

        hKey = self.win.HCRYPTKEY(ptrsz)
        hKey.Algid = Algid
        hKey.keylen = hnd.digest_size
        hKey.keyp = self.mem_alloc(hKey.keylen)

        hKeyp = self.mem_alloc(hKey.sizeof())

        self.mem_write(hKey.keyp, fixed_digest)

        self.mem_write(hKeyp, hKey.get_bytes())
        self.mem_write(phKey, hKeyp.to_bytes(ptrsz, "little"))

        return 1

    @apihook("CryptDecrypt", argc=6)
    def CryptDecrypt(self, emu, argv, ctx={}):
        """
        BOOL CryptDecrypt(
          HCRYPTKEY  hKey,
          HCRYPTHASH hHash,
          BOOL       Final,
          DWORD      dwFlags,
          BYTE       *pbData,
          DWORD      *pdwDataLen
        );
        """

        hKey, hHash, Final, dwFlags, pbData, pdwDataLen = argv

        # Hashing not supported
        if hHash:
            return 0

        ptrsz = emu.get_ptr_size()

        hKey = self.mem_cast(self.win.HCRYPTKEY(ptrsz), hKey)

        # Only RC4 supported right now
        if hKey.Algid != 0x6801:
            return 0

        encdatalen_b = self.mem_read(pdwDataLen, 4)
        encdatalen = int.from_bytes(encdatalen_b, "little")

        encdata = self.mem_read(pbData, encdatalen)

        key = self.mem_read(hKey.keyp, hKey.keylen)

        if self.rc4 is None:
            self.rc4 = ARC4.new(key)

        dec = self.rc4.decrypt(encdata)
        declen = len(dec)

        self.mem_write(pbData, dec)
        self.mem_write(pdwDataLen, int.to_bytes(declen, 4, "little"))

        if Final:
            self.rc4 = None

        return 1

    @apihook("RegGetValue", argc=7, conv=_arch.CALL_CONV_STDCALL)
    def RegGetValue(self, emu, argv, ctx={}):
        """
        LSTATUS RegGetValueW(
            HKEY    hkey,
            LPCWSTR lpSubKey,
            LPCWSTR lpValue,
            DWORD   dwFlags,
            LPDWORD pdwType,
            PVOID   pvData,
            LPDWORD pcbData
            );
        """

        hKey, lpSubKey, lpValue, dwFlags, lpType, lpData, lpcbData = argv
        rv = windefs.ERROR_SUCCESS

        cw = self.get_char_width(ctx)
        if lpSubKey:
            lpSubKey = self.read_mem_string(lpSubKey, cw)
            argv[1] = lpSubKey

        if lpValue:
            lpValue = self.read_mem_string(lpValue, cw)
            argv[2] = lpValue

        type_name = regdefs.get_value_type(lpType)
        if type_name:
            argv[4] = type_name

        length = 0
        if lpcbData:
            length = self.mem_read(lpcbData, 4)
            length = int.from_bytes(length, "little")

        key = self.reg_get_key(hKey)
        if key:
            val = key.get_value(lpValue)
            if val:
                output = b""

                if lpcbData:
                    self.mem_write(lpcbData, len(output).to_bytes(4, "little"))

                if len(output) > length:
                    rv = windefs.ERROR_INSUFFICIENT_BUFFER
                else:
                    self.mem_write(lpData, output)

            # For now, return an empty buffer
            else:
                output = b"\x00" * length
                self.mem_write(lpData, output)
                rv = windefs.ERROR_SUCCESS

            kp = key.get_path()
            self.record_registry_access_event(
                kp,
                REG_READ,
                value_name=lpValue,
                data=output,
                size=length,
                buffer=lpData,
            )

        return rv

    @apihook("EnumServicesStatus", argc=8, conv=_arch.CALL_CONV_STDCALL)
    def EnumServicesStatus(self, emu, argv, ctx={}):
        """
        BOOL EnumServicesStatusA(
          SC_HANDLE              hSCManager,
          DWORD                  dwServiceType,
          DWORD                  dwServiceState,
          LPENUM_SERVICE_STATUSA lpServices,
          DWORD                  cbBufSize,
          LPDWORD                pcbBytesNeeded,
          LPDWORD                lpServicesReturned,
          LPDWORD                lpResumeHandle
        );
        """
        (
            hSCManager,
            dwServiceType,
            dwServiceState,
            lpServices,
            cbBufSize,
            pcbBytesNeeded,
            lpServicesReturned,
            lpResumeHandle,
        ) = argv

        service_type_str = adv32.get_define_int(dwServiceType, "SERVICE_")
        if service_type_str:
            argv[1] = service_type_str

        service_state_str = adv32.get_define_int(dwServiceState, "SERVICE_")
        if service_state_str:
            argv[2] = service_state_str

        # TODO: Populate service status output
        return 1

    @apihook("OpenService", argc=3, conv=_arch.CALL_CONV_STDCALL)
    def OpenService(self, emu, argv, ctx={}):
        """
        SC_HANDLE OpenServiceA(
          SC_HANDLE hSCManager,
          LPCSTR    lpServiceName,
          DWORD     dwDesiredAccess
        );
        """
        hSCManager, lpServiceName, dwDesiredAccess = argv
        cw = self.get_char_width(ctx)
        svcname = self.read_mem_string(lpServiceName, cw)
        argv[1] = svcname
        return self.get_handle()

    @apihook("DeleteService", argc=1, conv=_arch.CALL_CONV_STDCALL)
    def DeleteService(self, emu, argv, ctx={}):
        """
        BOOL DeleteService(
          SC_HANDLE hService
        );
        """
        return 1
