about summary refs log tree commit diff stats
path: root/src/miasm/os_dep/win_api_x86_32.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/miasm/os_dep/win_api_x86_32.py')
-rw-r--r--src/miasm/os_dep/win_api_x86_32.py3595
1 files changed, 3595 insertions, 0 deletions
diff --git a/src/miasm/os_dep/win_api_x86_32.py b/src/miasm/os_dep/win_api_x86_32.py
new file mode 100644
index 00000000..6e568abb
--- /dev/null
+++ b/src/miasm/os_dep/win_api_x86_32.py
@@ -0,0 +1,3595 @@
+from __future__ import print_function
+#
+# Copyright (C) 2011 EADS France, Fabrice Desclaux <fabrice.desclaux@eads.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+from past.builtins import cmp
+import struct
+import os
+import stat
+import time
+import string
+import logging
+from zlib import crc32
+from io import StringIO
+import time
+import datetime
+
+from future.utils import PY3, viewitems, viewvalues
+
+try:
+    from Crypto.Hash import MD5, SHA
+except ImportError:
+    print("cannot find crypto, skipping")
+
+from miasm.jitter.csts import PAGE_READ, PAGE_WRITE, PAGE_EXEC
+from miasm.core.utils import pck16, pck32, hexdump, whoami, int_to_byte
+from miasm.os_dep.common import heap, windows_to_sbpath
+from miasm.os_dep.common import set_win_str_w, set_win_str_a
+from miasm.os_dep.common import get_fmt_args as _get_fmt_args
+from miasm.os_dep.common import get_win_str_a, get_win_str_w
+from miasm.os_dep.common import encode_win_str_a, encode_win_str_w
+from miasm.os_dep.win_api_x86_32_seh import tib_address
+
+log = logging.getLogger("win_api_x86_32")
+console_handler = logging.StreamHandler()
+console_handler.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s"))
+log.addHandler(console_handler)
+log.setLevel(logging.WARN)
+
+DATE_1601_TO_1970 = 116444736000000000
+
+MAX_PATH = 260
+
+
+"""
+typedef struct tagPROCESSENTRY32 {
+  DWORD     dwSize;
+  DWORD     cntUsage;
+  DWORD     th32ProcessID;
+  ULONG_PTR th32DefaultHeapID;
+  DWORD     th32ModuleID;
+  DWORD     cntThreads;
+  DWORD     th32ParentProcessID;
+  LONG      pcPriClassBase;
+  DWORD     dwFlags;
+  TCHAR     szExeFile[MAX_PATH];
+} PROCESSENTRY32, *PPROCESSENTRY32;
+"""
+
+
+ACCESS_DICT = {0x0: 0,
+               0x1: 0,
+               0x2: PAGE_READ,
+               0x4: PAGE_READ | PAGE_WRITE,
+               0x10: PAGE_EXEC,
+               0x20: PAGE_EXEC | PAGE_READ,
+               0x40: PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+               0x80: PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+               # 0x80: PAGE_EXECUTE_WRITECOPY
+               0x100: 0
+               }
+
+ACCESS_DICT_INV = dict((x[1], x[0]) for x in viewitems(ACCESS_DICT))
+
+
+class whandle(object):
+
+    def __init__(self, name, info):
+        self.name = name
+        self.info = info
+
+    def __repr__(self):
+        return '<%r %r %r>' % (self.__class__.__name__, self.name, self.info)
+
+
+class handle_generator(object):
+
+    def __init__(self):
+        self.offset = 600
+        self.all_handles = {}
+
+    def add(self, name, info=None):
+        self.offset += 1
+        h = whandle(name, info)
+        self.all_handles[self.offset] = h
+
+        log.debug(repr(self))
+        return self.offset
+
+    def __repr__(self):
+        out = '<%r\n' % self.__class__.__name__
+        ks = list(self.all_handles)
+        ks.sort()
+
+        for k in ks:
+            out += "    %r %r\n" % (k, self.all_handles[k])
+        out += '>'
+        return out
+
+    def __contains__(self, e):
+        return e in self.all_handles
+
+    def __getitem__(self, item):
+        return self.all_handles.__getitem__(item)
+
+    def __delitem__(self, item):
+        self.all_handles.__delitem__(item)
+
+
+class c_winobjs(object):
+
+    def __init__(self):
+        self.alloc_ad = 0x20000000
+        self.alloc_align = 0x1000
+        self.heap = heap()
+        self.handle_toolhelpsnapshot = 0xaaaa00
+        self.toolhelpsnapshot_info = {}
+        self.handle_curprocess = 0xaaaa01
+        self.dbg_present = 0
+        self.tickcount = 0
+        self.dw_pid_dummy1 = 0x111
+        self.dw_pid_explorer = 0x222
+        self.dw_pid_dummy2 = 0x333
+        self.dw_pid_cur = 0x444
+        self.module_fname_nux = None
+        self.module_name = "test.exe"
+        self.module_path = "c:\\mydir\\" + self.module_name
+        self.hcurmodule = None
+        self.module_filesize = None
+        self.getversion = 0x0A280105
+        self.getforegroundwindow = 0x333333
+        self.cryptcontext_hwnd = 0x44400
+        self.cryptcontext_bnum = 0x44000
+        self.cryptcontext_num = 0
+        self.cryptcontext = {}
+        self.phhash_crypt_md5 = 0x55555
+        # key used by EncodePointer and DecodePointer
+        # (kernel32)
+        self.ptr_encode_key = 0xabababab
+        self.files_hwnd = {}
+        self.windowlong_dw = 0x77700
+        self.module_cur_hwnd = 0x88800
+        self.module_file_nul = 0x999000
+        self.runtime_dll = None
+        self.current_pe = None
+        self.tls_index = 0xf
+        self.tls_values = {}
+        self.handle_pool = handle_generator()
+        self.handle_mapped = {}
+        self.hkey_handles = {
+            0x80000001: b"hkey_current_user",
+            0x80000002: b"hkey_local_machine"
+        }
+        self.cur_dir = "c:\\tmp"
+
+        self.nt_mdl = {}
+        self.nt_mdl_ad = None
+        self.nt_mdl_cur = 0
+        self.win_event_num = 0x13370
+        self.cryptdll_md5_h = {}
+
+        self.lastwin32error = 0
+        self.mutex = {}
+        self.env_variables = {}
+        self.events_pool = {}
+        self.find_data = None
+
+        self.allocated_pages = {}
+        self.current_datetime = datetime.datetime(
+            year=2017, month=8, day=21,
+            hour=13, minute=37,
+            second=11, microsecond=123456
+        )
+
+winobjs = c_winobjs()
+
+
+process_list = [
+    [
+        0x40,  # DWORD     dwSize;
+        0,  # DWORD     cntUsage;
+        winobjs.dw_pid_dummy1,  # DWORD     th32ProcessID;
+        0x11111111,  # ULONG_PTR th32DefaultHeapID;
+        0x11111112,  # DWORD     th32ModuleID;
+        1,  # DWORD     cntThreads;
+        winobjs.dw_pid_explorer,  # DWORD     th32ParentProcessID;
+        0xbeef,  # LONG      pcPriClassBase;
+        0x0,  # DWORD     dwFlags;
+        "dummy1.exe"  # TCHAR     szExeFile[MAX_PATH];
+    ],
+    [
+        0x40,  # DWORD     dwSize;
+        0,  # DWORD     cntUsage;
+        winobjs.dw_pid_explorer,  # DWORD     th32ProcessID;
+        0x11111111,  # ULONG_PTR th32DefaultHeapID;
+        0x11111112,  # DWORD     th32ModuleID;
+        1,  # DWORD     cntThreads;
+        4,  # DWORD     th32ParentProcessID;
+        0xbeef,  # LONG      pcPriClassBase;
+        0x0,  # DWORD     dwFlags;
+        "explorer.exe"  # TCHAR     szExeFile[MAX_PATH];
+    ],
+
+    [
+        0x40,  # DWORD     dwSize;
+        0,  # DWORD     cntUsage;
+        winobjs.dw_pid_dummy2,  # DWORD     th32ProcessID;
+        0x11111111,  # ULONG_PTR th32DefaultHeapID;
+        0x11111112,  # DWORD     th32ModuleID;
+        1,  # DWORD     cntThreads;
+        winobjs.dw_pid_explorer,  # DWORD     th32ParentProcessID;
+        0xbeef,  # LONG      pcPriClassBase;
+        0x0,  # DWORD     dwFlags;
+        "dummy2.exe"  # TCHAR     szExeFile[MAX_PATH];
+    ],
+
+    [
+        0x40,  # DWORD     dwSize;
+        0,  # DWORD     cntUsage;
+        winobjs.dw_pid_cur,  # DWORD     th32ProcessID;
+        0x11111111,  # ULONG_PTR th32DefaultHeapID;
+        0x11111112,  # DWORD     th32ModuleID;
+        1,  # DWORD     cntThreads;
+        winobjs.dw_pid_explorer,  # DWORD     th32ParentProcessID;
+        0xbeef,  # LONG      pcPriClassBase;
+        0x0,  # DWORD     dwFlags;
+        winobjs.module_name  # TCHAR     szExeFile[MAX_PATH];
+    ],
+
+
+]
+
+
+class hobj(object):
+    pass
+
+
+class mdl(object):
+
+    def __init__(self, ad, l):
+        self.ad = ad
+        self.l = l
+
+    def __bytes__(self):
+        return struct.pack('LL', self.ad, self.l)
+
+    def __str__(self):
+        if PY3:
+            return repr(self)
+        return self.__bytes__()
+
+
+def kernel32_HeapAlloc(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["heap", "flags", "size"])
+    alloc_addr = winobjs.heap.alloc(jitter, args.size, cmt=hex(ret_ad))
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def kernel32_HeapFree(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["heap", "flags", "pmem"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GlobalAlloc(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["uflags", "msize"])
+    alloc_addr = winobjs.heap.alloc(jitter, args.msize)
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def kernel32_LocalFree(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["lpvoid"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_LocalAlloc(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["uflags", "msize"])
+    alloc_addr = winobjs.heap.alloc(jitter, args.msize)
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+def msvcrt_new(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["size"])
+    alloc_addr = winobjs.heap.alloc(jitter, args.size)
+    jitter.func_ret_cdecl(ret_ad, alloc_addr)
+
+globals()['msvcrt_??2@YAPAXI@Z'] = msvcrt_new
+
+def msvcrt_delete(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["ptr"])
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+globals()['msvcrt_??3@YAXPAX@Z'] = msvcrt_delete
+
+def kernel32_GlobalFree(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["addr"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_IsDebuggerPresent(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.dbg_present)
+
+
+def kernel32_CreateToolhelp32Snapshot(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["dwflags", "th32processid"])
+    jitter.func_ret_stdcall(ret_ad, winobjs.handle_toolhelpsnapshot)
+
+
+def kernel32_GetCurrentProcess(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.handle_curprocess)
+
+
+def kernel32_GetCurrentProcessId(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.dw_pid_cur)
+
+
+def kernel32_Process32First(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["s_handle", "ad_pentry"])
+
+    pentry = struct.pack(
+        'IIIIIIIII', *process_list[0][:-1]
+    ) + (process_list[0][-1] + '\x00').encode('utf8')
+    jitter.vm.set_mem(args.ad_pentry, pentry)
+    winobjs.toolhelpsnapshot_info[args.s_handle] = 0
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_Process32Next(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["s_handle", "ad_pentry"])
+
+    winobjs.toolhelpsnapshot_info[args.s_handle] += 1
+    if winobjs.toolhelpsnapshot_info[args.s_handle] >= len(process_list):
+        ret = 0
+    else:
+        ret = 1
+        n = winobjs.toolhelpsnapshot_info[args.s_handle]
+        pentry = struct.pack(
+            'IIIIIIIII', *process_list[n][:-1]) + (process_list[n][-1]+ '\x00').encode('utf8')
+        jitter.vm.set_mem(args.ad_pentry, pentry)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetTickCount(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    winobjs.tickcount += 1
+    jitter.func_ret_stdcall(ret_ad, winobjs.tickcount)
+
+
+def kernel32_GetVersion(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.getversion)
+
+
+def kernel32_GetVersionEx(jitter, str_size, encode_str):
+    ret_ad, args = jitter.func_args_stdcall(["ptr_struct"])
+
+    size = jitter.vm.get_u32(args.ptr_struct)
+    if size in [0x14+str_size, 0x1c+str_size]:
+        tmp = struct.pack(
+            "IIIII%dsHHHBB" % str_size,
+            0x114,            # struct size
+            0x5,              # maj vers
+            0x2,              # min vers
+            0xa28,            # build nbr
+            0x2,              # platform id
+            encode_str("Service pack 4"),
+            3,                # wServicePackMajor
+            0,                # wServicePackMinor
+            0x100,            # wSuiteMask
+            1,                # wProductType
+            0                 # wReserved
+        )
+        tmp = tmp[:size]
+        jitter.vm.set_mem(args.ptr_struct, tmp)
+        ret = 1
+    else:
+        ret = 0
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+kernel32_GetVersionExA = lambda jitter: kernel32_GetVersionEx(jitter, 128,
+                                                              encode_win_str_a)
+kernel32_GetVersionExW = lambda jitter: kernel32_GetVersionEx(jitter, 256,
+                                                              encode_win_str_w)
+
+
+def kernel32_GetPriorityClass(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_SetPriorityClass(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd", "dwpclass"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_CloseHandle(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+def kernel32_EncodePointer(jitter):
+    """
+        PVOID EncodePointer(
+            _In_ PVOID Ptr
+        );
+
+        Encoding globally available pointers helps protect them from being
+        exploited. The EncodePointer function obfuscates the pointer value
+        with a secret so that it cannot be predicted by an external agent.
+        The secret used by EncodePointer is different for each process.
+
+        A pointer must be decoded before it can be used.
+
+    """
+    ret, args = jitter.func_args_stdcall(1)
+    jitter.func_ret_stdcall(ret, args[0] ^ winobjs.ptr_encode_key)
+    return True
+
+def kernel32_DecodePointer(jitter):
+    """
+        PVOID DecodePointer(
+           PVOID Ptr
+        );
+
+        The function returns the decoded pointer.
+
+    """
+    ret, args = jitter.func_args_stdcall(1)
+    jitter.func_ret_stdcall(ret, args[0] ^ winobjs.ptr_encode_key)
+    return True
+
+def user32_GetForegroundWindow(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.getforegroundwindow)
+
+
+def user32_FindWindowA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pclassname", "pwindowname"])
+    if args.pclassname:
+        classname = get_win_str_a(jitter, args.pclassname)
+        log.info("FindWindowA classname %s", classname)
+    if args.pwindowname:
+        windowname = get_win_str_a(jitter, args.pwindowname)
+        log.info("FindWindowA windowname %s", windowname)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def user32_GetTopWindow(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def user32_BlockInput(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["blockit"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptAcquireContext(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["phprov", "pszcontainer",
+                                             "pszprovider", "dwprovtype",
+                                             "dwflags"])
+    prov = get_str(args.pszprovider) if args.pszprovider else "NONE"
+    log.debug('prov: %r', prov)
+    jitter.vm.set_u32(args.phprov, winobjs.cryptcontext_hwnd)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptAcquireContextA(jitter):
+    advapi32_CryptAcquireContext(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def advapi32_CryptAcquireContextW(jitter):
+    advapi32_CryptAcquireContext(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def advapi32_CryptCreateHash(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hprov", "algid", "hkey",
+                                             "dwflags", "phhash"])
+
+    winobjs.cryptcontext_num += 1
+
+    if args.algid == 0x00008003:
+        log.debug('algo is MD5')
+        jitter.vm.set_u32(
+            args.phhash,
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num
+        )
+        winobjs.cryptcontext[
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num] = hobj()
+        winobjs.cryptcontext[
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num].h = MD5.new()
+    elif args.algid == 0x00008004:
+        log.debug('algo is SHA1')
+        jitter.vm.set_u32(
+            args.phhash,
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num
+        )
+        winobjs.cryptcontext[
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num] = hobj()
+        winobjs.cryptcontext[
+            winobjs.cryptcontext_bnum + winobjs.cryptcontext_num].h = SHA.new()
+    else:
+        raise ValueError('un impl algo1')
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptHashData(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hhash", "pbdata", "dwdatalen",
+                                             "dwflags"])
+
+    if not args.hhash in winobjs.cryptcontext:
+        raise ValueError("unknown crypt context")
+
+    data = jitter.vm.get_mem(args.pbdata, args.dwdatalen)
+    log.debug('will hash %X', args.dwdatalen)
+    log.debug(repr(data[:0x10]) + "...")
+    winobjs.cryptcontext[args.hhash].h.update(data)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptGetHashParam(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hhash", "param", "pbdata",
+                                             "dwdatalen", "dwflags"])
+
+    if not args.hhash in winobjs.cryptcontext:
+        raise ValueError("unknown crypt context")
+
+    if args.param == 2:
+        # HP_HASHVAL
+        # XXX todo: save h state?
+        h = winobjs.cryptcontext[args.hhash].h.digest()
+        jitter.vm.set_mem(args.pbdata, h)
+        jitter.vm.set_u32(args.dwdatalen, len(h))
+    elif args.param == 4:
+        # HP_HASHSIZE
+        ret = winobjs.cryptcontext[args.hhash].h.digest_size
+        jitter.vm.set_u32(args.pbdata, ret)
+        jitter.vm.set_u32(args.dwdatalen, 4)
+    else:
+        raise ValueError('not impl', args.param)
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptReleaseContext(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hhash", "flags"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def advapi32_CryptDeriveKey(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hprov", "algid", "hbasedata",
+                                             "dwflags", "phkey"])
+
+    if args.algid == 0x6801:
+        log.debug('using DES')
+    else:
+        raise ValueError('un impl algo2')
+    h = winobjs.cryptcontext[args.hbasedata].h.digest()
+    log.debug('hash %r', h)
+    winobjs.cryptcontext[args.hbasedata].h_result = h
+    jitter.vm.set_u32(args.phkey, args.hbasedata)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptDestroyHash(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hhash"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def advapi32_CryptDecrypt(jitter):
+    # ret_ad, _ = jitter.func_args_stdcall(["hkey", "hhash", "final",
+    #                                       "dwflags", "pbdata",
+    #                                       "pdwdatalen"])
+    raise ValueError("Not implemented")
+    # jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_CreateFile(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["lpfilename", "access",
+                                             "dwsharedmode",
+                                             "lpsecurityattr",
+                                             "dwcreationdisposition",
+                                             "dwflagsandattr",
+                                             "htemplatefile"])
+    if args.lpfilename == 0:
+        jitter.func_ret_stdcall(ret_ad, 0xffffffff)
+        return
+
+    fname = get_str(args.lpfilename)
+    log.info('CreateFile fname %s', fname)
+    ret = 0xffffffff
+
+    log.debug("%r %r", fname.lower(), winobjs.module_path.lower())
+    is_original_file = fname.lower() == winobjs.module_path.lower()
+
+    if fname.upper() in [r"\\.\SICE", r"\\.\NTICE", r"\\.\SIWVID", r'\\.\SIWDEBUG']:
+        pass
+    elif fname.upper() in ['NUL']:
+        ret = winobjs.module_cur_hwnd
+    else:
+        # sandbox path
+        sb_fname = windows_to_sbpath(fname)
+        if args.access & 0x80000000 or args.access == 1:
+            # read and maybe write
+            if args.dwcreationdisposition == 2:
+                # create_always
+                if os.access(sb_fname, os.R_OK):
+                    # but file exist
+                    pass
+                else:
+                    raise NotImplementedError("Untested case")  # to test
+                    # h = open(sb_fname, 'rb+')
+            elif args.dwcreationdisposition == 3:
+                # open_existing
+                if os.access(sb_fname, os.R_OK):
+                    s = os.stat(sb_fname)
+                    if stat.S_ISDIR(s.st_mode):
+                        ret = winobjs.handle_pool.add(sb_fname, 0x1337)
+                    else:
+                        open_mode = 'rb'
+                        if (args.access & 0x40000000) or args.access == 2:
+                            open_mode = 'r+b'
+                        h = open(sb_fname, open_mode)
+                        ret = winobjs.handle_pool.add(sb_fname, h)
+                else:
+                    log.warning("FILE %r (%s) DOES NOT EXIST!", fname, sb_fname)
+            elif args.dwcreationdisposition == 1:
+                # create new
+                if os.access(sb_fname, os.R_OK):
+                    # file exist
+                    # ret = 80
+                    winobjs.lastwin32error = 80
+                else:
+                    # first create an empty file
+                    open(sb_fname, 'wb').close()
+                    # then open
+                    h = open(sb_fname, 'r+b')
+                    ret = winobjs.handle_pool.add(sb_fname, h)
+            elif args.dwcreationdisposition == 4:
+                # open_always
+                if os.access(sb_fname, os.R_OK):
+                    s = os.stat(sb_fname)
+                    if stat.S_ISDIR(s.st_mode):
+                        ret = winobjs.handle_pool.add(sb_fname, 0x1337)
+                    else:
+                        h = open(sb_fname, 'r+b')
+                        ret = winobjs.handle_pool.add(sb_fname, h)
+                else:
+                    raise NotImplementedError("Untested case")
+            else:
+                raise NotImplementedError("Untested case")
+        elif (args.access & 0x40000000) or args.access == 2:
+            # write but not read
+            if args.dwcreationdisposition == 3:
+                # open existing
+                if is_original_file:
+                    # cannot open self in write mode!
+                    pass
+                elif os.access(sb_fname, os.R_OK):
+                    s = os.stat(sb_fname)
+                    if stat.S_ISDIR(s.st_mode):
+                        # open dir
+                        ret = winobjs.handle_pool.add(sb_fname, 0x1337)
+                    else:
+                        h = open(sb_fname, 'wb')
+                        ret = winobjs.handle_pool.add(sb_fname, h)
+                else:
+                    raise NotImplementedError("Untested case")  # to test
+            elif args.dwcreationdisposition == 5:
+                # truncate_existing
+                if is_original_file:
+                    pass
+                else:
+                    raise NotImplementedError("Untested case")  # to test
+            else:
+                # raise NotImplementedError("Untested case") # to test
+                h = open(sb_fname, 'wb')
+                ret = winobjs.handle_pool.add(sb_fname, h)
+        else:
+            raise NotImplementedError("Untested case")
+
+        # h = open(sb_fname, 'rb+')
+        # ret = winobjs.handle_pool.add(sb_fname, h)
+    log.debug('CreateFile ret %x', ret)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_CreateFileA(jitter):
+    kernel32_CreateFile(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_CreateFileW(jitter):
+    kernel32_CreateFile(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_ReadFile(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpbuffer",
+                                             "nnumberofbytestoread",
+                                             "lpnumberofbytesread",
+                                             "lpoverlapped"])
+    if args.hwnd == winobjs.module_cur_hwnd:
+        pass
+    elif args.hwnd in winobjs.handle_pool:
+        pass
+    else:
+        raise ValueError('unknown hwnd!')
+
+    data = None
+    if args.hwnd in winobjs.files_hwnd:
+        data = winobjs.files_hwnd[
+            winobjs.module_cur_hwnd].read(args.nnumberofbytestoread)
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        data = wh.info.read(args.nnumberofbytestoread)
+    else:
+        raise ValueError('unknown filename')
+
+    if data is not None:
+        if (args.lpnumberofbytesread):
+            jitter.vm.set_u32(args.lpnumberofbytesread, len(data))
+        jitter.vm.set_mem(args.lpbuffer, data)
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetFileSize(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpfilesizehight"])
+
+    if args.hwnd == winobjs.module_cur_hwnd:
+        ret = len(open(winobjs.module_fname_nux, "rb").read())
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        ret = len(open(wh.name, "rb").read())
+    else:
+        raise ValueError('unknown hwnd!')
+
+    if args.lpfilesizehight != 0:
+        jitter.vm.set_u32(args.lpfilesizehight, ret)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetFileSizeEx(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpfilesizehight"])
+
+    if args.hwnd == winobjs.module_cur_hwnd:
+        l = len(open(winobjs.module_fname_nux, "rb").read())
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        l = len(open(wh.name, "rb").read())
+    else:
+        raise ValueError('unknown hwnd!')
+
+    if args.lpfilesizehight == 0:
+        raise NotImplementedError("Untested case")
+    jitter.vm.set_mem(args.lpfilesizehight, pck32(
+        l & 0xffffffff) + pck32((l >> 32) & 0xffffffff))
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_FlushInstructionCache(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hprocess", "lpbasead", "dwsize"])
+    jitter.func_ret_stdcall(ret_ad, 0x1337)
+
+
+def kernel32_VirtualProtect(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['lpvoid', 'dwsize',
+                                             'flnewprotect',
+                                             'lpfloldprotect'])
+    # XXX mask hpart
+    flnewprotect = args.flnewprotect & 0xFFF
+    if not flnewprotect in ACCESS_DICT:
+        raise ValueError('unknown access dw!')
+
+    if args.lpfloldprotect:
+        old = jitter.vm.get_mem_access(args.lpvoid)
+        jitter.vm.set_u32(args.lpfloldprotect, ACCESS_DICT_INV[old])
+
+    paddr = args.lpvoid - (args.lpvoid % winobjs.alloc_align)
+    paddr_max = (args.lpvoid + args.dwsize + winobjs.alloc_align - 1)
+    paddr_max_round = paddr_max - (paddr_max % winobjs.alloc_align)
+    psize = paddr_max_round - paddr
+    for addr, items in list(winobjs.allocated_pages.items()):
+        alloc_addr, alloc_size = items
+        if (paddr + psize <= alloc_addr or
+            paddr > alloc_addr + alloc_size):
+            continue
+        size = jitter.vm.get_all_memory()[addr]["size"]
+        # Page is included in Protect area
+        if (paddr <= addr < addr + size <= paddr + psize):
+            log.warn("set page %x %x", addr, ACCESS_DICT[flnewprotect])
+            jitter.vm.set_mem_access(addr, ACCESS_DICT[flnewprotect])
+            continue
+
+        # Page is partly in Protect area: splitting is needed
+        if (addr <= paddr < addr + size or
+            addr <= paddr + psize < addr + size):
+
+            old_access = jitter.vm.get_mem_access(addr)
+
+            # splits = [
+            #     addr -> max(paddr, addr)
+            #     max(paddr, addr) -> min(addr + size, paddr + psize)
+            #     min(addr + size, paddr + psize) -> addr + size
+            # ]
+            splits = [
+                (addr, old_access,
+                 jitter.vm.get_mem(addr, max(paddr, addr) - addr)),
+                (max(paddr, addr), ACCESS_DICT[flnewprotect],
+                 jitter.vm.get_mem(
+                     max(paddr, addr),
+                     min(addr + size, paddr + psize) - max(paddr, addr))),
+                (min(addr + size, paddr + psize), old_access,
+                 jitter.vm.get_mem(
+                     min(addr + size, paddr + psize),
+                     addr + size - min(addr + size, paddr + psize)))
+            ]
+
+            jitter.vm.remove_memory_page(addr)
+            for split_addr, split_access, split_data in splits:
+                if not split_data:
+                    continue
+                log.warn("create page %x %x", split_addr,
+                         ACCESS_DICT[flnewprotect])
+                jitter.vm.add_memory_page(
+                    split_addr, split_access, split_data,
+                    "VirtualProtect split ret 0x%X" % ret_ad)
+                winobjs.allocated_pages[split_addr] = (alloc_addr, alloc_size)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_VirtualAlloc(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['lpvoid', 'dwsize',
+                                             'alloc_type', 'flprotect'])
+
+
+    if not args.flprotect in ACCESS_DICT:
+        raise ValueError('unknown access dw!')
+
+    if args.lpvoid == 0:
+        alloc_addr = winobjs.heap.next_addr(args.dwsize)
+        winobjs.allocated_pages[alloc_addr] = (alloc_addr, args.dwsize)
+        jitter.vm.add_memory_page(
+            alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * args.dwsize,
+            "Alloc in %s ret 0x%X" % (whoami(), ret_ad))
+    else:
+        all_mem = jitter.vm.get_all_memory()
+        if args.lpvoid in all_mem:
+            alloc_addr = args.lpvoid
+            jitter.vm.set_mem_access(args.lpvoid, ACCESS_DICT[args.flprotect])
+        else:
+            alloc_addr = winobjs.heap.next_addr(args.dwsize)
+            winobjs.allocated_pages[alloc_addr] = (alloc_addr, args.dwsize)
+            # alloc_addr = args.lpvoid
+            jitter.vm.add_memory_page(
+                alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * args.dwsize,
+                "Alloc in %s ret 0x%X" % (whoami(), ret_ad))
+
+    log.info('VirtualAlloc addr: 0x%x', alloc_addr)
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def kernel32_VirtualFree(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["lpvoid", "dwsize", "alloc_type"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def user32_GetWindowLongA(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd", "nindex"])
+    jitter.func_ret_stdcall(ret_ad, winobjs.windowlong_dw)
+
+
+def user32_SetWindowLongA(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["hwnd", "nindex", "newlong"])
+    jitter.func_ret_stdcall(ret_ad, winobjs.windowlong_dw)
+
+
+def kernel32_GetModuleFileName(jitter, funcname, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["hmodule", "lpfilename", "nsize"])
+
+    if args.hmodule in [0, winobjs.hcurmodule]:
+        p = winobjs.module_path[:]
+    elif (winobjs.runtime_dll and
+          args.hmodule in viewvalues(winobjs.runtime_dll.name2off)):
+        name_inv = dict(
+            [
+                (x[1], x[0])
+                for x in viewitems(winobjs.runtime_dll.name2off)
+            ]
+        )
+        p = name_inv[args.hmodule]
+    else:
+        log.warning(('Unknown module 0x%x.' +
+                     'Set winobjs.hcurmodule and retry'), args.hmodule)
+        p = None
+
+    if p is None:
+        l = 0
+    elif args.nsize < len(p):
+        p = p[:args.nsize]
+        l = len(p)
+    else:
+        l = len(p)
+
+    if p:
+        set_str(args.lpfilename, p)
+
+    jitter.func_ret_stdcall(ret_ad, l)
+
+
+def kernel32_GetModuleFileNameA(jitter):
+    kernel32_GetModuleFileName(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetModuleFileNameW(jitter):
+    kernel32_GetModuleFileName(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_CreateMutex(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["mutexattr", "initowner",
+                                             "lpname"])
+
+    if args.lpname:
+        name = get_str(args.lpname)
+        log.info("CreateMutex %r", name)
+    else:
+        name = None
+    if args.initowner:
+        if name in winobjs.mutex:
+            raise NotImplementedError("Untested case")
+            # ret = 0
+        else:
+            winobjs.mutex[name] = id(name) & 0xFFFFFFFF
+            ret = winobjs.mutex[name]
+    else:
+        if name in winobjs.mutex:
+            raise NotImplementedError("Untested case")
+            # ret = 0
+        else:
+            winobjs.mutex[name] = id(name) & 0xFFFFFFFF
+            ret = winobjs.mutex[name]
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_CreateMutexA(jitter):
+    kernel32_CreateMutex(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_CreateMutexW(jitter):
+    kernel32_CreateMutex(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def shell32_SHGetSpecialFolderLocation(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwndowner", "nfolder", "ppidl"])
+    jitter.vm.set_u32(args.ppidl, args.nfolder)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_SHGetPathFromIDList(jitter, funcname, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["pidl", "ppath"])
+
+    if args.pidl == 7:  # CSIDL_STARTUP:
+        s = "c:\\doc\\user\\startmenu\\programs\\startup"
+        set_str(args.ppath, s)
+    else:
+        raise ValueError('pidl not implemented', args.pidl)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def shell32_SHGetPathFromIDListW(jitter):
+    kernel32_SHGetPathFromIDList(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def shell32_SHGetPathFromIDListA(jitter):
+    kernel32_SHGetPathFromIDList(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetLastError(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, winobjs.lastwin32error)
+
+
+def kernel32_SetLastError(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["errcode"])
+    # lasterr addr
+    # ad = tib_address + 0x34
+    # jitter.vm.set_mem(ad, pck32(args.errcode))
+    winobjs.lastwin32error = args.errcode
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_RestoreLastError(jitter):
+    kernel32_SetLastError(jitter)
+
+
+def kernel32_LoadLibrary(jitter, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["dllname"])
+
+    libname = get_str(args.dllname, 0x100)
+    ret = winobjs.runtime_dll.lib_get_add_base(libname)
+    log.info("Loading %r ret 0x%x", libname, ret)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_LoadLibraryA(jitter):
+    kernel32_LoadLibrary(jitter, lambda addr, max_char=None:get_win_str_a(jitter, addr, max_char))
+
+
+def kernel32_LoadLibraryW(jitter):
+    kernel32_LoadLibrary(jitter, lambda addr, max_char=None:get_win_str_w(jitter, addr, max_char))
+
+
+def kernel32_LoadLibraryEx(jitter, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["dllname", "hfile", "flags"])
+
+    if args.hfile != 0:
+        raise NotImplementedError("Untested case")
+    libname = get_str(args.dllname, 0x100)
+    ret = winobjs.runtime_dll.lib_get_add_base(libname)
+    log.info("Loading %r ret 0x%x", libname, ret)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_LoadLibraryExA(jitter):
+    kernel32_LoadLibraryEx(jitter, lambda addr, max_char=None:get_win_str_a(jitter, addr, max_char))
+
+
+def kernel32_LoadLibraryExW(jitter):
+    kernel32_LoadLibraryEx(jitter, lambda addr, max_char=None:get_win_str_w(jitter, addr, max_char))
+
+
+def kernel32_GetProcAddress(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["libbase", "fname"])
+    fname = args.fname
+    if fname >= 0x10000:
+        fname = jitter.get_c_str(fname, 0x100)
+        if not fname:
+            fname = None
+    if fname is not None:
+        ad = winobjs.runtime_dll.lib_get_add_func(args.libbase, fname)
+    else:
+        ad = 0
+    log.info("GetProcAddress %r %r ret 0x%x", args.libbase, fname, ad)
+    jitter.add_breakpoint(ad, jitter.handle_lib)
+    jitter.func_ret_stdcall(ret_ad, ad)
+
+
+def kernel32_GetModuleHandle(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["dllname"])
+
+    if args.dllname:
+        libname = get_str(args.dllname)
+        if libname:
+            ret = winobjs.runtime_dll.lib_get_add_base(libname)
+        else:
+            log.warning('unknown module!')
+            ret = 0
+        log.info("GetModuleHandle %r ret 0x%x", libname, ret)
+    else:
+        ret = winobjs.current_pe.NThdr.ImageBase
+        log.info("GetModuleHandle default ret 0x%x", ret)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetModuleHandleA(jitter):
+    kernel32_GetModuleHandle(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_GetModuleHandleW(jitter):
+    kernel32_GetModuleHandle(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_VirtualLock(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["lpaddress", "dwsize"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+class systeminfo(object):
+    oemId = 0
+    dwPageSize = 0x1000
+    lpMinimumApplicationAddress = 0x10000
+    lpMaximumApplicationAddress = 0x7ffeffff
+    dwActiveProcessorMask = 0x1
+    numberOfProcessors = 0x1
+    ProcessorsType = 586
+    dwAllocationgranularity = 0x10000
+    wProcessorLevel = 0x6
+    ProcessorRevision = 0xf0b
+
+    def pack(self):
+        return struct.pack('IIIIIIIIHH',
+                           self.oemId,
+                           self.dwPageSize,
+                           self.lpMinimumApplicationAddress,
+                           self.lpMaximumApplicationAddress,
+                           self.dwActiveProcessorMask,
+                           self.numberOfProcessors,
+                           self.ProcessorsType,
+                           self.dwAllocationgranularity,
+                           self.wProcessorLevel,
+                           self.ProcessorRevision)
+
+
+def kernel32_GetSystemInfo(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["sys_ptr"])
+    sysinfo = systeminfo()
+    jitter.vm.set_mem(args.sys_ptr, sysinfo.pack())
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_IsWow64Process(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["process", "bool_ptr"])
+    jitter.vm.set_u32(args.bool_ptr, 0)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetCommandLine(jitter, set_str):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    alloc_addr = winobjs.heap.alloc(jitter, 0x1000)
+    set_str(alloc_addr, '"%s"' % winobjs.module_path)
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def kernel32_GetCommandLineA(jitter):
+    kernel32_GetCommandLine(jitter, lambda addr, value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetCommandLineW(jitter):
+    kernel32_GetCommandLine(jitter, lambda addr, value: set_win_str_w(jitter, addr, value))
+
+
+def shell32_CommandLineToArgvW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pcmd", "pnumargs"])
+    cmd = get_win_str_w(jitter, args.pcmd)
+    if cmd.startswith('"') and cmd.endswith('"'):
+        cmd = cmd[1:-1]
+    log.info("CommandLineToArgv %r", cmd)
+    tks = cmd.split(' ')
+    addr = winobjs.heap.alloc(jitter, len(cmd) * 2 + 4 * len(tks))
+    addr_ret = winobjs.heap.alloc(jitter, 4 * (len(tks) + 1))
+    o = 0
+    for i, t in enumerate(tks):
+        set_win_str_w(jitter, addr + o, t)
+        jitter.vm.set_u32(addr_ret + 4 * i, addr + o)
+        o += len(t)*2 + 2
+
+    jitter.vm.set_u32(addr_ret + 4 * (i+1), 0)
+    jitter.vm.set_u32(args.pnumargs, len(tks))
+    jitter.func_ret_stdcall(ret_ad, addr_ret)
+
+def cryptdll_MD5Init(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_ctx"])
+    index = len(winobjs.cryptdll_md5_h)
+    h = MD5.new()
+    winobjs.cryptdll_md5_h[index] = h
+
+    jitter.vm.set_u32(args.ad_ctx, index)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def cryptdll_MD5Update(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_ctx", "ad_input", "inlen"])
+
+    index = jitter.vm.get_u32(args.ad_ctx)
+    if not index in winobjs.cryptdll_md5_h:
+        raise ValueError('unknown h context', index)
+
+    data = jitter.vm.get_mem(args.ad_input, args.inlen)
+    winobjs.cryptdll_md5_h[index].update(data)
+    log.debug(hexdump(data))
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def cryptdll_MD5Final(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_ctx"])
+
+    index = jitter.vm.get_u32(args.ad_ctx)
+    if not index in winobjs.cryptdll_md5_h:
+        raise ValueError('unknown h context', index)
+    h = winobjs.cryptdll_md5_h[index].digest()
+    jitter.vm.set_mem(args.ad_ctx + 88, h)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlInitAnsiString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_ctx", "ad_str"])
+
+    s = get_win_str_a(jitter, args.ad_str)
+    l = len(s)
+    jitter.vm.set_mem(args.ad_ctx,
+                      pck16(l) + pck16(l + 1) + pck32(args.ad_str))
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlHashUnicodeString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_ctxu", "case_i", "h_id",
+                                             "phout"])
+
+    if args.h_id != 1:
+        raise ValueError('unk hash unicode', args.h_id)
+
+    l1, l2, ptra = struct.unpack('HHL', jitter.vm.get_mem(args.ad_ctxu, 8))
+    s = jitter.vm.get_mem(ptra, l1)
+    s = s[:-1]
+    hv = 0
+
+    if args.case_i:
+        s = s.lower()
+    for c in s:
+        hv = ((65599 * hv) + ord(c)) & 0xffffffff
+    jitter.vm.set_u32(args.phout, hv)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_RtlMoveMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad_dst", "ad_src", "m_len"])
+    data = jitter.vm.get_mem(args.ad_src, args.m_len)
+    jitter.vm.set_mem(args.ad_dst, data)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlAnsiCharToUnicodeChar(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ad_ad_ch'])
+    ad_ch = jitter.vm.get_u32(args.ad_ad_ch)
+    ch = ord(jitter.vm.get_mem(ad_ch, 1))
+    jitter.vm.set_u32(args.ad_ad_ch, ad_ch + 1)
+    jitter.func_ret_stdcall(ret_ad, ch)
+
+
+def ntdll_RtlFindCharInUnicodeString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["flags", "main_str_ad",
+                                             "search_chars_ad", "pos_ad"])
+
+    if args.flags != 0:
+        raise ValueError('unk flags')
+
+    ml1, ml2, mptra = struct.unpack('HHL',
+                                    jitter.vm.get_mem(args.main_str_ad, 8))
+    sl1, sl2, sptra = struct.unpack(
+        'HHL', jitter.vm.get_mem(args.search_chars_ad, 8))
+    main_data = jitter.vm.get_mem(mptra, ml1)[:-1]
+    search_data = jitter.vm.get_mem(sptra, sl1)[:-1]
+
+    pos = None
+    for i, c in enumerate(main_data):
+        for s in search_data:
+            if s == c:
+                pos = i
+                break
+        if pos:
+            break
+    if pos is None:
+        ret = 0xC0000225
+        jitter.vm.set_u32(args.pos_ad, 0)
+    else:
+        ret = 0
+        jitter.vm.set_u32(args.pos_ad, pos)
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def ntdll_RtlComputeCrc32(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["dwinit", "pdata", "ilen"])
+    data = jitter.vm.get_mem(args.pdata, args.ilen)
+    crc_r = crc32(data, args.dwinit)
+    jitter.func_ret_stdcall(ret_ad, crc_r)
+
+
+def ntdll_RtlExtendedIntegerMultiply(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['multiplicand_low',
+                                             'multiplicand_high',
+                                             'multiplier'])
+    a = (args.multiplicand_high << 32) + args.multiplicand_low
+    a = a * args.multiplier
+    jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff)
+
+
+def ntdll_RtlLargeIntegerAdd(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high',
+                                             'b_low', 'b_high'])
+    a = (args.a_high << 32) + args.a_low + (args.b_high << 32) + args.b_low
+    jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff)
+
+
+def ntdll_RtlLargeIntegerShiftRight(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high', 's_count'])
+    a = ((args.a_high << 32) + args.a_low) >> args.s_count
+    jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff)
+
+
+def ntdll_RtlEnlargedUnsignedMultiply(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['a', 'b'])
+    a = args.a * args.b
+    jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff)
+
+
+def ntdll_RtlLargeIntegerSubtract(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high',
+                                             'b_low', 'b_high'])
+    a = (args.a_high << 32) + args.a_low - (args.b_high << 32) + args.b_low
+    jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff)
+
+
+def ntdll_RtlCompareMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ad1', 'ad2', 'm_len'])
+    data1 = jitter.vm.get_mem(args.ad1, args.m_len)
+    data2 = jitter.vm.get_mem(args.ad2, args.m_len)
+
+    i = 0
+    while data1[i] == data2[i]:
+        i += 1
+        if i >= args.m_len:
+            break
+
+    jitter.func_ret_stdcall(ret_ad, i)
+
+
+def user32_GetMessagePos(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0x00110022)
+
+
+def kernel32_Sleep(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(['t'])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_ZwUnmapViewOfSection(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(['h', 'ad'])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_IsBadReadPtr(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(['lp', 'ucb'])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_KeInitializeEvent(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['my_event', 'my_type',
+                                             'my_state'])
+    jitter.vm.set_u32(args.my_event, winobjs.win_event_num)
+    winobjs.win_event_num += 1
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_RtlGetVersion(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ptr_version'])
+
+    s = struct.pack("IIIII",
+                    0x114,  # struct size
+                    0x5,   # maj vers
+                    0x2,  # min vers
+                    0x666,  # build nbr
+                    0x2,   # platform id
+                    ) + encode_win_str_w("Service pack 4")
+
+    jitter.vm.set_mem(args.ptr_version, s)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_RtlVerifyVersionInfo(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ptr_version'])
+
+    s = jitter.vm.get_mem(args.ptr_version, 0x5 * 4)
+    s_size, s_majv, s_minv, s_buildn, s_platform = struct.unpack('IIIII', s)
+    raise NotImplementedError("Untested case")
+    # jitter.vm.set_mem(args.ptr_version, s)
+    # jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def hal_ExAcquireFastMutex(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def mdl2ad(n):
+    return winobjs.nt_mdl_ad + 0x10 * n
+
+
+def ad2mdl(ad):
+    return ((ad - winobjs.nt_mdl_ad) & 0xFFFFFFFF) // 0x10
+
+
+def ntoskrnl_IoAllocateMdl(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["v_addr", "l", "second_buf",
+                                             "chargequota", "pirp"])
+    m = mdl(args.v_addr, args.l)
+    winobjs.nt_mdl[winobjs.nt_mdl_cur] = m
+    jitter.vm.set_mem(mdl2ad(winobjs.nt_mdl_cur), bytes(m))
+    jitter.func_ret_stdcall(ret_ad, mdl2ad(winobjs.nt_mdl_cur))
+    winobjs.nt_mdl_cur += 1
+
+
+def ntoskrnl_MmProbeAndLockPages(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["p_mdl", "access_mode", "op"])
+
+    if not ad2mdl(args.p_mdl) in winobjs.nt_mdl:
+        raise ValueError('unk mdl', hex(args.p_mdl))
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_MmMapLockedPagesSpecifyCache(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["p_mdl", "access_mode",
+                                             "cache_type", "base_ad",
+                                             "bugcheckonfailure",
+                                             "priority"])
+    if not ad2mdl(args.p_mdl) in winobjs.nt_mdl:
+        raise ValueError('unk mdl', hex(args.p_mdl))
+
+    jitter.func_ret_stdcall(ret_ad, winobjs.nt_mdl[ad2mdl(args.p_mdl)].ad)
+
+
+def ntoskrnl_MmProtectMdlSystemAddress(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["p_mdl", "prot"])
+    if not ad2mdl(args.p_mdl) in winobjs.nt_mdl:
+        raise ValueError('unk mdl', hex(args.p_mdl))
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_MmUnlockPages(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['p_mdl'])
+    if not ad2mdl(args.p_mdl) in winobjs.nt_mdl:
+        raise ValueError('unk mdl', hex(args.p_mdl))
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_IoFreeMdl(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['p_mdl'])
+    if not ad2mdl(args.p_mdl) in winobjs.nt_mdl:
+        raise ValueError('unk mdl', hex(args.p_mdl))
+    del(winobjs.nt_mdl[ad2mdl(args.p_mdl)])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def hal_ExReleaseFastMutex(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_RtlQueryRegistryValues(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["relativeto", "path",
+                                             "querytable",
+                                             "context",
+                                             "environ"])
+    # path = get_win_str_w(jitter, args.path)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntoskrnl_ExAllocatePoolWithTagPriority(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pool_type",
+                                             "nbr_of_bytes",
+                                             "tag", "priority"])
+    alloc_addr = winobjs.heap.next_addr(args.nbr_of_bytes)
+    jitter.vm.add_memory_page(
+        alloc_addr, PAGE_READ | PAGE_WRITE, b"\x00" * args.nbr_of_bytes,
+        "Alloc in %s ret 0x%X" % (whoami(), ret_ad))
+
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def my_lstrcmp(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2"])
+    s1 = get_str(args.ptr_str1)
+    s2 = get_str(args.ptr_str2)
+    log.info("Compare %r with %r", s1, s2)
+    jitter.func_ret_stdcall(ret_ad, cmp(s1, s2))
+
+def msvcrt_wcscmp(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"])
+    s1 = get_win_str_w(jitter, args.ptr_str1)
+    s2 = get_win_str_w(jitter, args.ptr_str2)
+    log.debug("%s('%s','%s')" % (whoami(), s1, s2))
+    jitter.func_ret_cdecl(ret_ad, cmp(s1, s2))
+
+def msvcrt__wcsicmp(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"])
+    s1 = get_win_str_w(jitter, args.ptr_str1)
+    s2 = get_win_str_w(jitter, args.ptr_str2)
+    log.debug("%s('%s','%s')" % (whoami(), s1, s2))
+    jitter.func_ret_cdecl(ret_ad, cmp(s1.lower(), s2.lower()))
+
+def msvcrt__wcsnicmp(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2", "count"])
+    s1 = get_win_str_w(jitter, args.ptr_str1)
+    s2 = get_win_str_w(jitter, args.ptr_str2)
+    log.debug("%s('%s','%s',%d)" % (whoami(), s1, s2, args.count))
+    jitter.func_ret_cdecl(ret_ad, cmp(s1.lower()[:args.count], s2.lower()[:args.count]))
+
+def msvcrt_wcsncpy(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["dst", "src", "n"])
+    src = get_win_str_w(jitter, args.src)
+    dst = src[:args.n]
+    jitter.vm.set_mem(args.dst, b"\x00\x00" * args.n)
+    jitter.vm.set_mem(args.dst, dst.encode("utf-16le"))
+    jitter.func_ret_cdecl(ret_ad, args.dst)
+
+def kernel32_lstrcmpA(jitter):
+    my_lstrcmp(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_lstrcmpiA(jitter):
+    my_lstrcmp(jitter, whoami(), lambda x: get_win_str_a(jitter, x).lower())
+
+
+def kernel32_lstrcmpW(jitter):
+    my_lstrcmp(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_lstrcmpiW(jitter):
+    my_lstrcmp(jitter, whoami(), lambda x: get_win_str_w(jitter, x).lower())
+
+
+def kernel32_lstrcmpi(jitter):
+    my_lstrcmp(jitter, whoami(), lambda x: get_win_str_a(jitter, x).lower())
+
+
+def my_strcpy(jitter, funcname, get_str, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2"])
+    s2 = get_str(args.ptr_str2)
+    set_str(args.ptr_str1, s2)
+    log.info("Copy '%r'", s2)
+    jitter.func_ret_stdcall(ret_ad, args.ptr_str1)
+
+
+def kernel32_lstrcpyW(jitter):
+    my_strcpy(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_lstrcpyA(jitter):
+    my_strcpy(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_lstrcpy(jitter):
+    my_strcpy(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+def msvcrt__mbscpy(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"])
+    s2 = get_win_str_w(jitter, args.ptr_str2)
+    set_win_str_w(jitter, args.ptr_str1, s2)
+    jitter.func_ret_cdecl(ret_ad, args.ptr_str1)
+
+def msvcrt_wcscpy(jitter):
+    return msvcrt__mbscpy(jitter)
+
+
+def kernel32_lstrcpyn(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2",
+                                             "mlen"])
+    s2 = get_win_str_a(jitter, args.ptr_str2)
+    if len(s2) >= args.mlen:
+        s2 = s2[:args.mlen - 1]
+    log.info("Copy '%r'", s2)
+    set_win_str_a(jitter, args.ptr_str1, s2)
+    jitter.func_ret_stdcall(ret_ad, args.ptr_str1)
+
+
+def my_strlen(jitter, funcname, get_str, mylen):
+    ret_ad, args = jitter.func_args_stdcall(["src"])
+    src = get_str(args.src)
+    length = mylen(src)
+    log.info("Len of '%r' -> 0x%x", src, length)
+    jitter.func_ret_stdcall(ret_ad, length)
+
+
+def kernel32_lstrlenA(jitter):
+    my_strlen(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), len)
+
+
+def kernel32_lstrlenW(jitter):
+    my_strlen(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), len)
+
+
+def kernel32_lstrlen(jitter):
+    my_strlen(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), len)
+
+
+def my_lstrcat(jitter, funcname, get_str, set_str):
+    ret_ad, args = jitter.func_args_stdcall(['ptr_str1', 'ptr_str2'])
+    s1 = get_str(args.ptr_str1)
+    s2 = get_str(args.ptr_str2)
+    set_str(args.ptr_str1, s1 + s2)
+    jitter.func_ret_stdcall(ret_ad, args.ptr_str1)
+
+
+def kernel32_lstrcatA(jitter):
+    my_lstrcat(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_lstrcatW(jitter):
+    my_lstrcat(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_GetUserGeoID(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["geoclass"])
+    if args.geoclass == 14:
+        ret = 12345678
+    elif args.geoclass == 16:
+        ret = 55667788
+    else:
+        raise ValueError('unknown geolcass')
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def my_GetVolumeInformation(jitter, funcname, get_str, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["lprootpathname",
+                                             "lpvolumenamebuffer",
+                                             "nvolumenamesize",
+                                             "lpvolumeserialnumber",
+                                             "lpmaximumcomponentlength",
+                                             "lpfilesystemflags",
+                                             "lpfilesystemnamebuffer",
+                                             "nfilesystemnamesize"])
+    if args.lprootpathname:
+        s = get_str(args.lprootpathname)
+        log.info('GetVolumeInformation %r', s)
+
+
+    if args.lpvolumenamebuffer:
+        s = "volumename"
+        s = s[:args.nvolumenamesize]
+        set_str(args.lpvolumenamebuffer, s)
+
+    if args.lpvolumeserialnumber:
+        jitter.vm.set_u32(args.lpvolumeserialnumber, 11111111)
+    if args.lpmaximumcomponentlength:
+        jitter.vm.set_u32(args.lpmaximumcomponentlength, 0xff)
+    if args.lpfilesystemflags:
+        jitter.vm.set_u32(args.lpfilesystemflags, 22222222)
+
+    if args.lpfilesystemnamebuffer:
+        s = "filesystemname"
+        s = s[:args.nfilesystemnamesize]
+        set_str(args.lpfilesystemnamebuffer, s)
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetVolumeInformationA(jitter):
+    my_GetVolumeInformation(
+        jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetVolumeInformationW(jitter):
+    my_GetVolumeInformation(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_MultiByteToWideChar(jitter):
+    MB_ERR_INVALID_CHARS = 0x8
+    CP_ACP  = 0x000
+    CP_1252 = 0x4e4
+
+    ret_ad, args = jitter.func_args_stdcall(["codepage", "dwflags",
+                                             "lpmultibytestr",
+                                             "cbmultibyte",
+                                             "lpwidecharstr",
+                                             "cchwidechar"])
+    if args.codepage != CP_ACP and args.codepage != CP_1252:
+        raise NotImplementedError
+    # according to MSDN:
+    # "Note that, if cbMultiByte is 0, the function fails."
+    if args.cbmultibyte == 0:
+        raise ValueError
+    # according to MSDN:
+    # "Alternatively, this parameter can be set to -1 if the string is
+    #  null-terminated."
+    if args.cbmultibyte == 0xffffffff:
+        src_len = 0
+        while jitter.vm.get_mem(args.lpmultibytestr + src_len, 1) != b'\0':
+            src_len += 1
+        src = jitter.vm.get_mem(args.lpmultibytestr, src_len)
+    else:
+        src = jitter.vm.get_mem(args.lpmultibytestr, args.cbmultibyte)
+    if args.dwflags & MB_ERR_INVALID_CHARS:
+        # will raise an exception if decoding fails
+        s = src.decode("cp1252", errors="replace").encode("utf-16le")
+    else:
+        # silently replace undecodable chars with U+FFFD
+        s = src.decode("cp1252", errors="replace").encode("utf-16le")
+    if args.cchwidechar > 0:
+        # return value is number of bytes written
+        retval = min(args.cchwidechar, len(s))
+        jitter.vm.set_mem(args.lpwidecharstr, s[:retval])
+    else:
+        # return value is number of bytes to write
+        # i.e., size of dest. buffer to allocate
+        retval = len(s)
+    jitter.func_ret_stdcall(ret_ad, retval)
+
+
+def kernel32_WideCharToMultiByte(jitter):
+    """
+        int WideCharToMultiByte(
+          UINT                               CodePage,
+          DWORD                              dwFlags,
+          _In_NLS_string_(cchWideChar)LPCWCH lpWideCharStr,
+          int                                cchWideChar,
+          LPSTR                              lpMultiByteStr,
+          int                                cbMultiByte,
+          LPCCH                              lpDefaultChar,
+          LPBOOL                             lpUsedDefaultChar
+        );
+
+    """
+    CP_ACP  = 0x000
+    CP_1252 = 0x4e4
+
+    ret, args = jitter.func_args_stdcall([
+        'CodePage', 'dwFlags', 'lpWideCharStr', 'cchWideChar',
+        'lpMultiByteStr', 'cbMultiByte', 'lpDefaultChar', 'lpUsedDefaultChar',
+      ])
+    if args.CodePage != CP_ACP and args.CodePage != CP_1252:
+        raise NotImplementedError
+    cchWideChar = args.cchWideChar
+    if cchWideChar == 0xffffffff:
+        cchWideChar = len(get_win_str_w(jitter, args.lpWideCharStr)) + 1
+    src = jitter.vm.get_mem(args.lpWideCharStr, cchWideChar * 2)
+    dst = src.decode("utf-16le").encode("cp1252", errors="replace")
+    if args.cbMultiByte > 0:
+        # return value is the number of bytes written
+        retval = min(args.cbMultiByte, len(dst))
+        jitter.vm.set_mem(args.lpMultiByteStr, dst[:retval])
+    else:
+        # return value is the size of the buffer to allocate
+        # to get the multibyte string
+        retval = len(dst)
+    jitter.func_ret_stdcall(ret, retval)
+
+
+def my_GetEnvironmentVariable(jitter, funcname, get_str, set_str, mylen):
+    ret_ad, args = jitter.func_args_stdcall(["lpname", "lpbuffer",
+                                             "nsize"])
+
+    s = get_str(args.lpname)
+    log.info('GetEnvironmentVariable %r', s)
+    if s in winobjs.env_variables:
+        v = winobjs.env_variables[s]
+    else:
+        log.warning('WARNING unknown env variable %r', s)
+        v = ""
+    set_str(args.lpbuffer, v)
+    jitter.func_ret_stdcall(ret_ad, mylen(v))
+
+
+def kernel32_GetEnvironmentVariableA(jitter):
+    my_GetEnvironmentVariable(jitter, whoami(),
+                              lambda addr:get_win_str_a(jitter, addr),
+                              lambda addr,value: set_win_str_a(jitter, addr, value),
+                              len)
+
+
+def kernel32_GetEnvironmentVariableW(jitter):
+    my_GetEnvironmentVariable(jitter, whoami(),
+                              lambda addr:get_win_str_w(jitter, addr),
+                              lambda addr,value: set_win_str_w(jitter, addr, value),
+                              len)
+
+
+def my_GetSystemDirectory(jitter, funcname, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["lpbuffer", "usize"])
+    s = "c:\\windows\\system32"
+    l = len(s)
+    set_str(args.lpbuffer, s)
+    jitter.func_ret_stdcall(ret_ad, l)
+
+
+
+def kernel32_GetSystemDirectoryA(jitter):
+    my_GetSystemDirectory(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetSystemDirectoryW(jitter):
+    my_GetSystemDirectory(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def my_CreateDirectory(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['lppath', 'secattrib'])
+    # path = get_str(jitter, args.lppath)
+    jitter.func_ret_stdcall(ret_ad, 0x1337)
+
+
+def kernel32_CreateDirectoryW(jitter):
+    my_CreateDirectory(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_CreateDirectoryA(jitter):
+    my_CreateDirectory(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+
+def my_CreateEvent(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["lpeventattributes",
+                                             "bmanualreset",
+                                             "binitialstate",
+                                             "lpname"])
+    s = get_str(args.lpname) if args.lpname else None
+    if not s in winobjs.events_pool:
+        winobjs.events_pool[s] = (args.bmanualreset, args.binitialstate)
+    else:
+        log.warning('WARNING: known event')
+    jitter.func_ret_stdcall(ret_ad, id(s) & 0xFFFFFFFF)
+
+
+def kernel32_CreateEventA(jitter):
+    my_CreateEvent(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_CreateEventW(jitter):
+    my_CreateEvent(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_WaitForSingleObject(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['handle', 'dwms'])
+
+    t_start = time.time() * 1000
+    found = False
+    while True:
+        if args.dwms and args.dwms + t_start > time.time() * 1000:
+            ret = 0x102
+            break
+        for key, value in viewitems(winobjs.events_pool):
+            if key != args.handle:
+                continue
+            found = True
+            if value[1] == 1:
+                ret = 0
+                break
+        if not found:
+            log.warning('unknown handle')
+            ret = 0xffffffff
+            break
+        time.sleep(0.1)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_SetFileAttributesA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["lpfilename",
+                                             "dwfileattributes"])
+    if args.lpfilename:
+        # fname = get_win_str_a(jitter, args.lpfilename)
+        ret = 1
+    else:
+        ret = 0
+        jitter.vm.set_u32(tib_address + 0x34, 3)
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def ntdll_RtlMoveMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["dst", "src", "l"])
+    s = jitter.vm.get_mem(args.src, args.l)
+    jitter.vm.set_mem(args.dst, s)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def ntdll_ZwQuerySystemInformation(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["systeminformationclass",
+                                             "systeminformation",
+                                             "systeminformationl",
+                                             "returnl"])
+    if args.systeminformationclass == 2:
+        # SYSTEM_PERFORMANCE_INFORMATION
+        o = struct.pack('II', 0x22222222, 0x33333333)
+        o += b"\x00" * args.systeminformationl
+        o = o[:args.systeminformationl]
+        jitter.vm.set_mem(args.systeminformation, o)
+    else:
+        raise ValueError('unknown sysinfo class',
+                         args.systeminformationclass)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_ZwProtectVirtualMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid",
+                                             "pdwsize",
+                                             "flnewprotect",
+                                             "lpfloldprotect"])
+
+    ad = jitter.vm.get_u32(args.lppvoid)
+    # dwsize = upck32(jitter.vm.get_mem(args.pdwsize, 4))
+    # XXX mask hpart
+    flnewprotect = args.flnewprotect & 0xFFF
+
+    if not flnewprotect in ACCESS_DICT:
+        raise ValueError('unknown access dw!')
+    jitter.vm.set_mem_access(ad, ACCESS_DICT[flnewprotect])
+
+    # XXX todo real old protect
+    jitter.vm.set_u32(args.lpfloldprotect, 0x40)
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def ntdll_ZwAllocateVirtualMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid",
+                                             "zerobits", "pdwsize",
+                                             "alloc_type",
+                                             "flprotect"])
+
+    # ad = upck32(jitter.vm.get_mem(args.lppvoid, 4))
+    dwsize = jitter.vm.get_u32(args.pdwsize)
+
+    if not args.flprotect in ACCESS_DICT:
+        raise ValueError('unknown access dw!')
+
+    alloc_addr = winobjs.heap.next_addr(dwsize)
+    jitter.vm.add_memory_page(
+        alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * dwsize,
+        "Alloc in %s ret 0x%X" % (whoami(), ret_ad))
+    jitter.vm.set_u32(args.lppvoid, alloc_addr)
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_ZwFreeVirtualMemory(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid",
+                                             "pdwsize", "alloc_type"])
+    # ad = upck32(jitter.vm.get_mem(args.lppvoid, 4))
+    # dwsize = upck32(jitter.vm.get_mem(args.pdwsize, 4))
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlInitString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pstring", "source"])
+    s = get_win_str_a(jitter, args.source)
+    l = len(s) + 1
+    o = struct.pack('HHI', l, l, args.source)
+    jitter.vm.set_mem(args.pstring, o)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlAnsiStringToUnicodeString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["dst", "src", "alloc_str"])
+
+    l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.src, 0x8))
+    s = get_win_str_a(jitter, p_src)
+    l = (len(s) + 1) * 2
+    if args.alloc_str:
+        alloc_addr = winobjs.heap.next_addr(l)
+        jitter.vm.add_memory_page(
+            alloc_addr, PAGE_READ | PAGE_WRITE, b"\x00" * l,
+            "Alloc in %s ret 0x%X" % (whoami(), ret_ad))
+    else:
+        alloc_addr = p_src
+    set_win_str_w(jitter, alloc_addr, s)
+    o = struct.pack('HHI', l, l, alloc_addr)
+    jitter.vm.set_mem(args.dst, o)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_LdrLoadDll(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["path", "flags",
+                                             "modname", "modhandle"])
+
+    l1, l2, p_src = struct.unpack('HHI',
+                                  jitter.vm.get_mem(args.modname, 0x8))
+    s = get_win_str_w(jitter, p_src)
+    libname = s.lower()
+
+    ad = winobjs.runtime_dll.lib_get_add_base(libname)
+    log.info("Loading %r ret 0x%x", s, ad)
+    jitter.vm.set_u32(args.modhandle, ad)
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_RtlFreeUnicodeString(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['src'])
+    # l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.src, 0x8))
+    # s = get_win_str_w(jitter, p_src)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_LdrGetProcedureAddress(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["libbase", "pfname",
+                                             "opt", "p_ad"])
+
+    l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.pfname, 0x8))
+    fname = get_win_str_a(jitter, p_src)
+
+    ad = winobjs.runtime_dll.lib_get_add_func(args.libbase, fname)
+    jitter.add_breakpoint(ad, jitter.handle_lib)
+
+    jitter.vm.set_u32(args.p_ad, ad)
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def ntdll_memset(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['addr', 'c', 'size'])
+    jitter.vm.set_mem(args.addr, int_to_byte(args.c) * args.size)
+    jitter.func_ret_cdecl(ret_ad, args.addr)
+
+
+def msvcrt_memset(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['addr', 'c', 'size'])
+    jitter.vm.set_mem(args.addr, int_to_byte(args.c) * args.size)
+    jitter.func_ret_cdecl(ret_ad, args.addr)
+
+def msvcrt_strrchr(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['pstr','c'])
+    s = get_win_str_a(jitter, args.pstr)
+    c = int_to_byte(args.c).decode()
+    ret = args.pstr + s.rfind(c)
+    log.info("strrchr(%x '%s','%s') = %x" % (args.pstr,s,c,ret))
+    jitter.func_ret_cdecl(ret_ad, ret)
+
+def msvcrt_wcsrchr(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['pstr','c'])
+    s = get_win_str_w(jitter, args.pstr)
+    c = int_to_byte(args.c).decode()
+    ret = args.pstr + (s.rfind(c)*2)
+    log.info("wcsrchr(%x '%s',%s) = %x" % (args.pstr,s,c,ret))
+    jitter.func_ret_cdecl(ret_ad, ret)
+
+def msvcrt_memcpy(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['dst', 'src', 'size'])
+    s = jitter.vm.get_mem(args.src, args.size)
+    jitter.vm.set_mem(args.dst, s)
+    jitter.func_ret_cdecl(ret_ad, args.dst)
+
+def msvcrt_realloc(jitter):
+    ret_ad,args = jitter.func_args_cdecl(['ptr','new_size'])
+    if args.ptr == 0:
+        addr = winobjs.heap.alloc(jitter, args.new_size)
+    else:
+        addr = winobjs.heap.alloc(jitter, args.new_size)
+        size = winobjs.heap.get_size(jitter.vm, args.ptr)
+        data = jitter.vm.get_mem(args.ptr, size)
+        jitter.vm.set_mem(addr, data)
+    jitter.func_ret_cdecl(ret_ad, addr)
+
+def msvcrt_memcmp(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['ps1', 'ps2', 'size'])
+    s1 = jitter.vm.get_mem(args.ps1, args.size)
+    s2 = jitter.vm.get_mem(args.ps2, args.size)
+    ret = cmp(s1, s2)
+    jitter.func_ret_cdecl(ret_ad, ret)
+
+
+def shlwapi_PathFindExtensionA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['path_ad'])
+    path = get_win_str_a(jitter, args.path_ad)
+    i = path.rfind('.')
+    if i == -1:
+        i = args.path_ad + len(path)
+    else:
+        i = args.path_ad + i
+    jitter.func_ret_stdcall(ret_ad, i)
+
+
+def shlwapi_PathRemoveFileSpecW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['path_ad'])
+    path = get_win_str_w(jitter, args.path_ad)
+    i = path.rfind('\\')
+    if i == -1:
+        i = 0
+    jitter.vm.set_mem(args.path_ad + i * 2, b"\x00\x00")
+    path = get_win_str_w(jitter, args.path_ad)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def shlwapi_PathIsPrefixW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ptr_prefix', 'ptr_path'])
+    prefix = get_win_str_w(jitter, args.ptr_prefix)
+    path = get_win_str_w(jitter, args.ptr_path)
+
+    if path.startswith(prefix):
+        ret = 1
+    else:
+        ret = 0
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def shlwapi_PathIsDirectoryW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ptr_path'])
+    fname = get_win_str_w(jitter, args.ptr_path)
+
+    sb_fname = windows_to_sbpath(fname)
+
+    s = os.stat(sb_fname)
+    ret = 0
+    if stat.S_ISDIR(s.st_mode):
+        ret = 1
+
+    jitter.func_ret_cdecl(ret_ad, ret)
+
+
+def shlwapi_PathIsFileSpec(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['path_ad'])
+    path = get_str(args.path_ad)
+    if path.find(':') != -1 and path.find('\\') != -1:
+        ret = 0
+    else:
+        ret = 1
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def shlwapi_PathGetDriveNumber(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['path_ad'])
+    path = get_str(args.path_ad)
+    l = ord(path[0].upper()) - ord('A')
+    if 0 <= l <= 25:
+        ret = l
+    else:
+        ret = -1
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def shlwapi_PathGetDriveNumberA(jitter):
+    shlwapi_PathGetDriveNumber(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def shlwapi_PathGetDriveNumberW(jitter):
+    shlwapi_PathGetDriveNumber(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def shlwapi_PathIsFileSpecA(jitter):
+    shlwapi_PathIsFileSpec(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def shlwapi_PathIsFileSpecW(jitter):
+    shlwapi_PathIsFileSpec(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def shlwapi_StrToIntA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['i_str_ad'])
+    i_str = get_win_str_a(jitter, args.i_str_ad)
+    try:
+        i = int(i_str)
+    except:
+        log.warning('WARNING cannot convert int')
+        i = 0
+
+    jitter.func_ret_stdcall(ret_ad, i)
+
+
+def shlwapi_StrToInt64Ex(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['pstr', 'flags', 'pret'])
+    i_str = get_str(args.pstr)
+
+    if args.flags == 0:
+        r = int(i_str)
+    elif args.flags == 1:
+        r = int(i_str, 16)
+    else:
+        raise ValueError('cannot decode int')
+
+    jitter.vm.set_mem(args.pret, struct.pack('q', r))
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def shlwapi_StrToInt64ExA(jitter):
+    shlwapi_StrToInt64Ex(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def shlwapi_StrToInt64ExW(jitter):
+    shlwapi_StrToInt64Ex(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def user32_IsCharAlpha(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["c"])
+    try:
+        c = int_to_byte(args.c)
+    except:
+        log.error('bad char %r', args.c)
+        c = "\x00"
+    if c.isalpha(jitter):
+        ret = 1
+    else:
+        ret = 0
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def user32_IsCharAlphaA(jitter):
+    user32_IsCharAlpha(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def user32_IsCharAlphaW(jitter):
+    user32_IsCharAlpha(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def user32_IsCharAlphaNumericA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["c"])
+    c = int_to_byte(args.c)
+    if c.isalnum(jitter):
+        ret = 1
+    else:
+        ret = 0
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+def get_fmt_args(jitter, fmt, cur_arg, get_str):
+    return _get_fmt_args(fmt, cur_arg, get_str, jitter.get_arg_n_cdecl)
+
+def msvcrt_sprintf_str(jitter, get_str):
+    ret_ad, args = jitter.func_args_cdecl(['string', 'fmt'])
+    cur_arg, fmt = 2, args.fmt
+    return ret_ad, args, get_fmt_args(jitter, fmt, cur_arg, get_str)
+
+def msvcrt_sprintf(jitter):
+    ret_ad, args, output = msvcrt_sprintf_str(jitter, lambda addr:get_win_str_a(jitter, addr))
+    ret = len(output)
+    log.info("sprintf() = '%s'" % (output))
+    jitter.vm.set_mem(args.string, (output + '\x00').encode('utf8'))
+    return jitter.func_ret_cdecl(ret_ad, ret)
+
+def msvcrt_swprintf(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['string', 'fmt'])
+    cur_arg, fmt = 2, args.fmt
+    output = get_fmt_args(jitter, fmt, cur_arg, lambda addr:get_win_str_w(jitter, addr))
+    ret = len(output)
+    log.info("swprintf('%s') = '%s'" % (get_win_str_w(jitter, args.fmt), output))
+    jitter.vm.set_mem(args.string, output.encode("utf-16le") + b'\x00\x00')
+    return jitter.func_ret_cdecl(ret_ad, ret)
+
+def msvcrt_fprintf(jitter):
+    ret_addr, args = jitter.func_args_cdecl(['file', 'fmt'])
+    cur_arg, fmt = 2, args.fmt
+    output = get_fmt_args(jitter, fmt, cur_arg, lambda addr:get_win_str_a(jitter, addr))
+    ret = len(output)
+    log.info("fprintf(%x, '%s') = '%s'" % (args.file, lambda addr:get_win_str_a(jitter, addr)(args.fmt), output))
+
+    fd = jitter.vm.get_u32(args.file + 0x10)
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+    winobjs.handle_pool[fd].info.write(output)
+
+    return jitter.func_ret_cdecl(ret_addr, ret)
+
+def shlwapi_StrCmpNIA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2",
+                                             "nchar"])
+    s1 = get_win_str_a(jitter, args.ptr_str1).lower()
+    s2 = get_win_str_a(jitter, args.ptr_str2).lower()
+    s1 = s1[:args.nchar]
+    s2 = s2[:args.nchar]
+    jitter.func_ret_stdcall(ret_ad, cmp(s1, s2))
+
+
+def advapi32_RegCreateKeyW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hkey", "subkey",
+                                             "phandle"])
+    s_subkey = get_win_str_w(jitter, args.subkey).lower() if args.subkey else ""
+
+    ret_hkey = 0
+    ret = 2
+    if args.hkey in winobjs.hkey_handles:
+        ret = 0
+        if s_subkey:
+            ret_hkey = hash(s_subkey) & 0xffffffff
+            winobjs.hkey_handles[ret_hkey] = s_subkey
+        else:
+            ret_hkey = args.hkey
+
+    log.info("RegCreateKeyW(%x, '%s') = (%x,%d)" % (args.hkey, s_subkey, ret_hkey, ret))
+    jitter.vm.set_u32(args.phandle, ret_hkey)
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+def kernel32_GetCurrentDirectoryA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["size","buf"])
+    dir_ = winobjs.cur_dir
+    log.debug("GetCurrentDirectory() = '%s'" % dir_)
+    set_win_str_a(jitter, args.buf, dir_[:args.size-1])
+    ret = len(dir_)
+    if args.size <= len(dir_):
+        ret += 1
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+def advapi32_RegOpenKeyEx(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["hkey", "subkey",
+                                             "reserved", "access",
+                                             "phandle"])
+    s_subkey = get_str(args.subkey).lower() if args.subkey else ""
+
+    ret_hkey = 0
+    ret = 2
+    if args.hkey in winobjs.hkey_handles:
+        if s_subkey:
+            h = hash(s_subkey) & 0xffffffff
+            if h in winobjs.hkey_handles:
+                ret_hkey = h
+                ret = 0
+        else:
+            log.error('unknown skey')
+
+    jitter.vm.set_u32(args.phandle, ret_hkey)
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def advapi32_RegOpenKeyExA(jitter):
+    advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def advapi32_RegOpenKeyExW(jitter):
+    advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def advapi32_RegSetValue(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["hkey", "psubkey",
+                                             "valuetype", "pvalue",
+                                             "vlen"])
+    if args.psubkey:
+        log.info("Subkey %s", get_str(args.psubkey))
+    if args.pvalue:
+        log.info("Value %s", get_str(args.pvalue))
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+def advapi32_RegSetValueEx(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["hkey", "lpvaluename",
+                                             "reserved", "dwtype",
+                                             "lpdata", "cbData"])
+    hkey = winobjs.hkey_handles.get(args.hkey, "unknown HKEY")
+    value_name = get_str(args.lpvaluename) if args.lpvaluename else ""
+    data = get_str(args.lpdata) if args.lpdata else ""
+    log.info("%s('%s','%s'='%s',%x)" % (funcname, hkey, value_name, data, args.dwtype))
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+def advapi32_RegCloseKey(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hkey"])
+    del winobjs.hkey_handles[args.hkey]
+    log.info("RegCloseKey(%x)" % args.hkey)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+def advapi32_RegSetValueExA(jitter):
+    advapi32_RegSetValueEx(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def advapi32_RegSetValueExW(jitter):
+    advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def advapi32_RegSetValueA(jitter):
+    advapi32_RegSetValue(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def advapi32_RegSetValueW(jitter):
+    advapi32_RegSetValue(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_GetThreadLocale(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0x40c)
+
+def kernel32_SetCurrentDirectory(jitter, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['dir'])
+    dir_ = get_str(args.dir)
+    log.debug("SetCurrentDirectory('%s') = 1" % dir_)
+    winobjs.cur_dir = dir_
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+def kernel32_SetCurrentDirectoryW(jitter):
+    return kernel32_SetCurrentDirectory(jitter, lambda addr:get_win_str_w(jitter, addr))
+
+def kernel32_SetCurrentDirectoryA(jitter):
+    return kernel32_SetCurrentDirectory(jitter, lambda addr:get_win_str_a(jitter, addr))
+
+def msvcrt_wcscat(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['ptr_str1', 'ptr_str2'])
+    s1 = get_win_str_w(jitter, args.ptr_str1)
+    s2 = get_win_str_w(jitter, args.ptr_str2)
+    log.info("strcat('%s','%s')" % (s1,s2))
+    set_win_str_w(jitter, args.ptr_str1, s1 + s2)
+    jitter.func_ret_cdecl(ret_ad, args.ptr_str1)
+
+
+def kernel32_GetLocaleInfo(jitter, funcname, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["localeid", "lctype",
+                                             "lplcdata", "cchdata"])
+
+    buf = None
+    ret = 0
+    if args.localeid == 0x40c:
+        if args.lctype == 0x3:
+            buf = "ENGLISH"
+            buf = buf[:args.cchdata - 1]
+            set_str(args.lplcdata, buf)
+            ret = len(buf)
+    else:
+        raise ValueError('unimpl localeid')
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetLocaleInfoA(jitter):
+    kernel32_GetLocaleInfo(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetLocaleInfoW(jitter):
+    kernel32_GetLocaleInfo(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_TlsAlloc(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    winobjs.tls_index += 1
+    jitter.func_ret_stdcall(ret_ad, winobjs.tls_index)
+
+
+def kernel32_TlsFree(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["tlsindex"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_TlsSetValue(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["tlsindex", "tlsvalue"])
+    winobjs.tls_values[args.tlsindex] = args.tlsvalue
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_TlsGetValue(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["tlsindex"])
+    if not args.tlsindex in winobjs.tls_values:
+        raise ValueError("unknown tls val", repr(args.tlsindex))
+    jitter.func_ret_stdcall(ret_ad, winobjs.tls_values[args.tlsindex])
+
+
+def user32_GetKeyboardType(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["typeflag"])
+
+    ret = 0
+    if args.typeflag == 0:
+        ret = 4
+    else:
+        raise ValueError('unimpl keyboard type')
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+class startupinfo(object):
+    """
+        typedef struct _STARTUPINFOA {
+          /* 00000000 */ DWORD  cb;
+          /* 00000004 */ LPSTR  lpReserved;
+          /* 00000008 */ LPSTR  lpDesktop;
+          /* 0000000C */ LPSTR  lpTitle;
+          /* 00000010 */ DWORD  dwX;
+          /* 00000014 */ DWORD  dwY;
+          /* 00000018 */ DWORD  dwXSize;
+          /* 0000001C */ DWORD  dwYSize;
+          /* 00000020 */ DWORD  dwXCountChars;
+          /* 00000024 */ DWORD  dwYCountChars;
+          /* 00000028 */ DWORD  dwFillAttribute;
+          /* 0000002C */ DWORD  dwFlags;
+          /* 00000030 */ WORD   wShowWindow;
+          /* 00000032 */ WORD   cbReserved2;
+          /* 00000034 */ LPBYTE lpReserved2;
+          /* 00000038 */ HANDLE hStdInput;
+          /* 0000003C */ HANDLE hStdOutput;
+          /* 00000040 */ HANDLE hStdError;
+        } STARTUPINFOA, *LPSTARTUPINFOA;
+
+    """
+    # TODO: fill with relevant values
+    # for now, struct is just a placeholder
+    cb = 0x0
+    lpReserved = 0x0
+    lpDesktop = 0x0
+    lpTitle = 0x0
+    dwX = 0x0
+    dwY = 0x0
+    dwXSize = 0x0
+    dwYSize = 0x0
+    dwXCountChars = 0x0
+    dwYCountChars = 0x0
+    dwFillAttribute = 0x0
+    dwFlags = 0x0
+    wShowWindow = 0x0
+    cbReserved2 = 0x0
+    lpReserved2 = 0x0
+    hStdInput = 0x0
+    hStdOutput = 0x0
+    hStdError = 0x0
+
+    def pack(self):
+        return struct.pack('IIIIIIIIIIIIHHIIII',
+                self.cb,
+                self.lpReserved,
+                self.lpDesktop,
+                self.lpTitle,
+                self.dwX,
+                self.dwY,
+                self.dwXSize,
+                self.dwYSize,
+                self.dwXCountChars,
+                self.dwYCountChars,
+                self.dwFillAttribute,
+                self.dwFlags,
+                self.wShowWindow,
+                self.cbReserved2,
+                self.lpReserved2,
+                self.hStdInput,
+                self.hStdOutput,
+                self.hStdError)
+
+
+def kernel32_GetStartupInfo(jitter, funcname, set_str):
+    """
+        void GetStartupInfo(
+          LPSTARTUPINFOW lpStartupInfo
+        );
+
+        Retrieves the contents of the STARTUPINFO structure that was specified
+        when the calling process was created.
+
+        https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getstartupinfow
+
+    """
+    ret_ad, args = jitter.func_args_stdcall(["ptr"])
+    jitter.vm.set_mem(args.ptr, startupinfo().pack())
+    jitter.func_ret_stdcall(ret_ad, args.ptr)
+
+
+def kernel32_GetStartupInfoA(jitter):
+    kernel32_GetStartupInfo(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetStartupInfoW(jitter):
+    kernel32_GetStartupInfo(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+def kernel32_GetCurrentThreadId(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0x113377)
+
+
+def kernel32_InitializeCriticalSection(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(["lpcritic"])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def user32_GetSystemMetrics(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["nindex"])
+
+    ret = 0
+    if args.nindex in [0x2a, 0x4a]:
+        ret = 0
+    else:
+        raise ValueError('unimpl index')
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def wsock32_WSAStartup(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["version", "pwsadata"])
+    jitter.vm.set_mem(args.pwsadata, b"\x01\x01\x02\x02WinSock 2.0\x00")
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def get_current_filetime():
+    """
+    Get current filetime
+    https://msdn.microsoft.com/en-us/library/ms724228
+    """
+    curtime = winobjs.current_datetime
+    unixtime = int(time.mktime(curtime.timetuple()))
+    filetime = (int(unixtime * 1000000 + curtime.microsecond) * 10 +
+                DATE_1601_TO_1970)
+    return filetime
+
+
+def unixtime_to_filetime(unixtime):
+    """
+    Convert unixtime to filetime
+    https://msdn.microsoft.com/en-us/library/ms724228
+    """
+    return (unixtime * 10000000) + DATE_1601_TO_1970
+
+
+def filetime_to_unixtime(filetime):
+    """
+    Convert filetime to unixtime
+    # https://msdn.microsoft.com/en-us/library/ms724228
+    """
+    return int((filetime - DATE_1601_TO_1970) // 10000000)
+
+
+def datetime_to_systemtime(curtime):
+
+    s = struct.pack('HHHHHHHH',
+                    curtime.year,      # year
+                    curtime.month,     # month
+                    curtime.weekday(), # dayofweek
+                    curtime.day,       # day
+                    curtime.hour,      # hour
+                    curtime.minute ,   # minutes
+                    curtime.second,    # seconds
+                    int(curtime.microsecond // 1000),  # millisec
+                    )
+    return s
+
+
+def kernel32_GetSystemTimeAsFileTime(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["lpSystemTimeAsFileTime"])
+
+    current_filetime = get_current_filetime()
+    filetime = struct.pack('II',
+                           current_filetime & 0xffffffff,
+                           (current_filetime>>32) & 0xffffffff)
+
+    jitter.vm.set_mem(args.lpSystemTimeAsFileTime, filetime)
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_GetLocalTime(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["lpsystemtime"])
+    systemtime = datetime_to_systemtime(winobjs.current_datetime)
+    jitter.vm.set_mem(args.lpsystemtime, systemtime)
+    jitter.func_ret_stdcall(ret_ad, args.lpsystemtime)
+
+
+def kernel32_GetSystemTime(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["lpsystemtime"])
+    systemtime = datetime_to_systemtime(winobjs.current_datetime)
+    jitter.vm.set_mem(args.lpsystemtime, systemtime)
+    jitter.func_ret_stdcall(ret_ad, args.lpsystemtime)
+
+
+def kernel32_CreateFileMapping(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["hfile", "lpattr", "flprotect",
+                                             "dwmaximumsizehigh",
+                                             "dwmaximumsizelow", "lpname"])
+
+    if args.hfile == 0xffffffff:
+        # Create null mapping
+        if args.dwmaximumsizehigh:
+            raise NotImplementedError("Untested case")
+        hmap = StringIO("\x00" * args.dwmaximumsizelow)
+        hmap_handle = winobjs.handle_pool.add('filemem', hmap)
+
+        ret = winobjs.handle_pool.add('filemapping', hmap_handle)
+    else:
+        if not args.hfile in winobjs.handle_pool:
+            raise ValueError('unknown handle')
+        ret = winobjs.handle_pool.add('filemapping', args.hfile)
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_CreateFileMappingA(jitter):
+    kernel32_CreateFileMapping(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_CreateFileMappingW(jitter):
+    kernel32_CreateFileMapping(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_MapViewOfFile(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hfile", "flprotect",
+                                             "dwfileoffsethigh",
+                                             "dwfileoffsetlow",
+                                             "length"])
+
+    if not args.hfile in winobjs.handle_pool:
+        raise ValueError('unknown handle')
+    hmap = winobjs.handle_pool[args.hfile]
+    if not hmap.info in winobjs.handle_pool:
+        raise ValueError('unknown file handle')
+
+    hfile_o = winobjs.handle_pool[hmap.info]
+    fd = hfile_o.info
+    fd.seek((args.dwfileoffsethigh << 32) | args.dwfileoffsetlow)
+    data = fd.read(args.length) if args.length else fd.read()
+    length = len(data)
+
+    log.debug('MapViewOfFile len: %x', len(data))
+
+    if not args.flprotect in ACCESS_DICT:
+        raise ValueError('unknown access dw!')
+
+    alloc_addr = winobjs.heap.alloc(jitter, len(data))
+    jitter.vm.set_mem(alloc_addr, data)
+
+    winobjs.handle_mapped[alloc_addr] = (hfile_o, args.dwfileoffsethigh,
+                                         args.dwfileoffsetlow, length)
+
+    jitter.func_ret_stdcall(ret_ad, alloc_addr)
+
+
+def kernel32_UnmapViewOfFile(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['ad'])
+
+    if not args.ad in winobjs.handle_mapped:
+        raise NotImplementedError("Untested case")
+    """
+    hfile_o, dwfileoffsethigh, dwfileoffsetlow, length = winobjs.handle_mapped[ad]
+    off = (dwfileoffsethigh<<32) | dwfileoffsetlow
+    s = jitter.vm.get_mem(ad, length)
+    hfile_o.info.seek(off)
+    hfile_o.info.write(s)
+    hfile_o.info.close()
+    """
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetDriveType(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(['pathname'])
+
+    p = get_str(args.pathname)
+    p = p.upper()
+
+    log.debug('Drive: %r', p)
+
+    ret = 0
+    if p[0] == "C":
+        ret = 3
+
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetDriveTypeA(jitter):
+    kernel32_GetDriveType(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_GetDriveTypeW(jitter):
+    kernel32_GetDriveType(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_GetDiskFreeSpace(jitter, funcname, get_str):
+    ret_ad, args = jitter.func_args_stdcall(["lprootpathname",
+                                             "lpsectorpercluster",
+                                             "lpbytespersector",
+                                             "lpnumberoffreeclusters",
+                                             "lptotalnumberofclusters"])
+    jitter.vm.set_u32(args.lpsectorpercluster, 8)
+    jitter.vm.set_u32(args.lpbytespersector, 0x200)
+    jitter.vm.set_u32(args.lpnumberoffreeclusters, 0x222222)
+    jitter.vm.set_u32(args.lptotalnumberofclusters, 0x333333)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetDiskFreeSpaceA(jitter):
+    kernel32_GetDiskFreeSpace(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr))
+
+
+def kernel32_GetDiskFreeSpaceW(jitter):
+    kernel32_GetDiskFreeSpace(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr))
+
+
+def kernel32_VirtualQuery(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["ad", "lpbuffer", "dwl"])
+
+    all_mem = jitter.vm.get_all_memory()
+    found = None
+    for basead, m in viewitems(all_mem):
+        if basead <= args.ad < basead + m['size']:
+            found = args.ad, m
+            break
+    if not found:
+        raise ValueError('cannot find mem', hex(args.ad))
+
+    if args.dwl != 0x1c:
+        raise ValueError('strange mem len', hex(args.dwl))
+    s = struct.pack('IIIIIII',
+                    args.ad,
+                    basead,
+                    ACCESS_DICT_INV[m['access']],
+                    m['size'],
+                    0x1000,
+                    ACCESS_DICT_INV[m['access']],
+                    0x01000000)
+    jitter.vm.set_mem(args.lpbuffer, s)
+    jitter.func_ret_stdcall(ret_ad, args.dwl)
+
+
+def kernel32_GetProcessAffinityMask(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hprocess",
+                                             "procaffmask",
+                                             "systemaffmask"])
+    jitter.vm.set_u32(args.procaffmask, 1)
+    jitter.vm.set_u32(args.systemaffmask, 1)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def msvcrt_rand(jitter):
+    ret_ad, _ = jitter.func_args_cdecl(0)
+    jitter.func_ret_stdcall(ret_ad, 0x666)
+
+def msvcrt_srand(jitter):
+    ret_ad, _ = jitter.func_args_cdecl(['seed'])
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+def msvcrt_wcslen(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["pwstr"])
+    s = get_win_str_w(jitter, args.pwstr)
+    jitter.func_ret_cdecl(ret_ad, len(s))
+
+def kernel32_SetFilePointer(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "dinstance",
+                                             "p_dinstance_high",
+                                             "movemethod"])
+
+    if args.hwnd == winobjs.module_cur_hwnd:
+        pass
+    elif args.hwnd in winobjs.handle_pool:
+        pass
+    else:
+        raise ValueError('unknown hwnd!')
+
+    # data = None
+    if args.hwnd in winobjs.files_hwnd:
+        winobjs.files_hwnd[winobjs.module_cur_hwnd].seek(args.dinstance, args.movemethod)
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        wh.info.seek(args.dinstance, args.movemethod)
+    else:
+        raise ValueError('unknown filename')
+    jitter.func_ret_stdcall(ret_ad, args.dinstance)
+
+
+def kernel32_SetFilePointerEx(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "dinstance_l",
+                                             "dinstance_h",
+                                             "pnewfileptr",
+                                             "movemethod"])
+    dinstance = args.dinstance_l | (args.dinstance_h << 32)
+    if dinstance:
+        raise ValueError('Not implemented')
+    if args.pnewfileptr:
+        raise ValueError('Not implemented')
+    if args.hwnd == winobjs.module_cur_hwnd:
+        pass
+    elif args.hwnd in winobjs.handle_pool:
+        pass
+    else:
+        raise ValueError('unknown hwnd!')
+
+    # data = None
+    if args.hwnd in winobjs.files_hwnd:
+        winobjs.files_hwnd[winobjs.module_cur_hwnd].seek(dinstance, args.movemethod)
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        wh.info.seek(dinstance, args.movemethod)
+    else:
+        raise ValueError('unknown filename')
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_SetEndOfFile(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['hwnd'])
+    if args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        wh.info.seek(0, 2)
+    else:
+        raise ValueError('unknown filename')
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_FlushFileBuffers(jitter):
+    ret_ad, args = jitter.func_args_stdcall(['hwnd'])
+    if args.hwnd in winobjs.handle_pool:
+        pass
+    else:
+        raise ValueError('unknown filename')
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_WriteFile(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpbuffer",
+                                             "nnumberofbytestowrite",
+                                             "lpnumberofbyteswrite",
+                                             "lpoverlapped"])
+    data = jitter.vm.get_mem(args.lpbuffer, args.nnumberofbytestowrite)
+
+    if args.hwnd == winobjs.module_cur_hwnd:
+        pass
+    elif args.hwnd in winobjs.handle_pool:
+        pass
+    else:
+        raise ValueError('unknown hwnd!')
+
+    if args.hwnd in winobjs.files_hwnd:
+        winobjs.files_hwnd[winobjs.module_cur_hwnd].write(data)
+    elif args.hwnd in winobjs.handle_pool:
+        wh = winobjs.handle_pool[args.hwnd]
+        wh.info.write(data)
+    else:
+        raise ValueError('unknown filename')
+
+    if (args.lpnumberofbyteswrite):
+        jitter.vm.set_u32(args.lpnumberofbyteswrite, len(data))
+
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def user32_IsCharUpperA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["c"])
+    ret = 0 if args.c & 0x20 else 1
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def user32_IsCharLowerA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["c"])
+    ret = 1 if args.c & 0x20 else 0
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+
+def kernel32_GetSystemDefaultLangID(jitter):
+    ret_ad, _ = jitter.func_args_stdcall(0)
+    jitter.func_ret_stdcall(ret_ad, 0x409)  # encglish
+
+
+def msvcrt_malloc(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["msize"])
+    addr = winobjs.heap.alloc(jitter, args.msize)
+    jitter.func_ret_cdecl(ret_ad, addr)
+
+
+def msvcrt_free(jitter):
+    ret_ad, _ = jitter.func_args_cdecl(["ptr"])
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+
+def msvcrt_fseek(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['stream', 'offset', 'orig'])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+    o = winobjs.handle_pool[fd]
+    o.info.seek(args.offset, args.orig)
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+
+def msvcrt_ftell(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["stream"])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+    o = winobjs.handle_pool[fd]
+    off = o.info.tell()
+    jitter.func_ret_cdecl(ret_ad, off)
+
+
+def msvcrt_rewind(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["stream"])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+    o = winobjs.handle_pool[fd]
+    # off = o.info.seek(0, 0)
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+
+def msvcrt_fread(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["buf", "size", "nmemb", "stream"])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+
+    data = winobjs.handle_pool[fd].info.read(args.size * args.nmemb)
+    jitter.vm.set_mem(args.buf, data)
+    jitter.func_ret_cdecl(ret_ad, args.nmemb)
+
+
+def msvcrt_fwrite(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["buf", "size", "nmemb", "stream"])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Unknown file handle!")
+
+    data = jitter.vm.get_mem(args.buf, args.size*args.nmemb)
+    winobjs.handle_pool[fd].info.write(data)
+    jitter.func_ret_cdecl(ret_ad, args.nmemb)
+
+
+def msvcrt_fclose(jitter):
+    ret_ad, args = jitter.func_args_cdecl(['stream'])
+    fd = jitter.vm.get_u32(args.stream + 0x10)
+
+    if not fd in winobjs.handle_pool:
+        raise NotImplementedError("Untested case")
+    o = winobjs.handle_pool[fd]
+    # off = o.info.close()
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+
+def msvcrt_atexit(jitter):
+    ret_ad, _ = jitter.func_args_cdecl(["func"])
+    jitter.func_ret_cdecl(ret_ad, 0)
+
+
+def user32_MessageBoxA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["hwnd", "lptext",
+                                             "lpcaption", "utype"])
+
+    text = get_win_str_a(jitter, args.lptext)
+    caption = get_win_str_a(jitter, args.lpcaption)
+
+    log.info('Caption: %r Text: %r', caption, text)
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def kernel32_myGetTempPath(jitter, set_str):
+    ret_ad, args = jitter.func_args_stdcall(["l", "buf"])
+    l = 'c:\\temp\\'
+    if len(l) < args.l:
+        set_str(args.buf, l)
+    jitter.func_ret_stdcall(ret_ad, len(l))
+
+
+def kernel32_GetTempPathA(jitter):
+    kernel32_myGetTempPath(jitter, lambda addr,value: set_win_str_a(jitter, addr, value))
+
+
+def kernel32_GetTempPathW(jitter):
+    kernel32_myGetTempPath(jitter, lambda addr,value: set_win_str_w(jitter, addr, value))
+
+
+temp_num = 0
+
+
+def kernel32_GetTempFileNameA(jitter):
+    global temp_num
+    ret_ad, args = jitter.func_args_stdcall(["path", "ext", "unique", "buf"])
+
+    temp_num += 1
+    ext = get_win_str_a(jitter, args.ext) if args.ext else 'tmp'
+    path = get_win_str_a(jitter, args.path) if args.path else "xxx"
+    fname = path + "\\" + "temp%.4d" % temp_num + "." + ext
+    jitter.vm.set_mem(args.buf, fname.encode('utf-8'))
+
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+class win32_find_data(object):
+    fileattrib = 0
+    creationtime = 0
+    lastaccesstime = 0
+    lastwritetime = 0
+    filesizehigh = 0
+    filesizelow = 0
+    dwreserved0 = 0
+    dwreserved1 = 0x1337beef
+    cfilename = ""
+    alternamefilename = ""
+
+    def __init__(self, **kargs):
+        for k, v in viewitems(kargs):
+            setattr(self, k, v)
+
+    def toStruct(self, encode_str=encode_win_str_w):
+        s = struct.pack('=IQQQIIII',
+                        self.fileattrib,
+                        self.creationtime,
+                        self.lastaccesstime,
+                        self.lastwritetime,
+                        self.filesizehigh,
+                        self.filesizelow,
+                        self.dwreserved0,
+                        self.dwreserved1)
+        fname = encode_str(self.cfilename) + b'\x00' * MAX_PATH
+        fname = fname[:MAX_PATH]
+        s += fname
+        fname = encode_str(self.alternamefilename) + b'\x00' * 14
+        fname = fname[:14]
+        s += fname
+        return s
+
+
+class find_data_mngr(object):
+
+    def __init__(self):
+        self.patterns = {}
+        self.flist = []
+        # handle number -> (flist index, current index in list)
+        self.handles = {}
+
+    def add_list(self, pattern, flist):
+        index = len(self.flist)
+        self.flist.append(flist)
+
+        self.patterns[pattern] = index
+
+    def findfirst(self, pattern):
+        assert(pattern in self.patterns)
+        findex = self.patterns[pattern]
+        h = len(self.handles) + 1
+        self.handles[h] = [findex, 0]
+        return h
+
+    def findnext(self, h):
+        assert(h in self.handles)
+        findex, index = self.handles[h]
+        if index >= len(self.flist[findex]):
+            return None
+        fname = self.flist[findex][index]
+        self.handles[h][1] += 1
+
+        return fname
+
+def my_FindFirstFile(jitter, pfilepattern, pfindfiledata, get_win_str, encode_str):
+    filepattern = get_win_str(jitter, pfilepattern)
+    h = winobjs.find_data.findfirst(filepattern)
+
+    fname = winobjs.find_data.findnext(h)
+    fdata = win32_find_data(cfilename=fname)
+
+    jitter.vm.set_mem(pfindfiledata, fdata.toStruct(encode_str=encode_str))
+    return h
+
+def kernel32_FindFirstFileA(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pfilepattern", "pfindfiledata"])
+    h = my_FindFirstFile(jitter, args.pfilepattern, args.pfindfiledata,
+                           get_win_str_a, encode_win_str_a)
+    jitter.func_ret_stdcall(ret_ad, h)
+
+def kernel32_FindFirstFileW(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["pfilepattern", "pfindfiledata"])
+    h = my_FindFirstFile(jitter, args.pfilepattern, args.pfindfiledata,
+                           get_win_str_w, encode_win_str_w)
+    jitter.func_ret_stdcall(ret_ad, h)
+
+def kernel32_FindFirstFileExA(jitter):
+    ret_ad, args = jitter.func_args_stdcall([
+        "lpFileName",
+        "fInfoLevelId",
+        "lpFindFileData",
+        "fSearchOp",
+        "lpSearchFilter",
+        "dwAdditionalFlags"])
+    h = my_FindFirstFile(jitter, args.lpFileName, args.lpFindFileData,
+                         get_win_str_a, encode_win_str_a)
+    jitter.func_ret_stdcall(ret_ad, h)
+
+def kernel32_FindFirstFileExW(jitter):
+    ret_ad, args = jitter.func_args_stdcall([
+        "lpFileName",
+        "fInfoLevelId",
+        "lpFindFileData",
+        "fSearchOp",
+        "lpSearchFilter",
+        "dwAdditionalFlags"])
+    h = my_FindFirstFile(jitter, args.lpFileName, args.lpFindFileData,
+                         get_win_str_w, encode_win_str_w)
+    jitter.func_ret_stdcall(ret_ad, h)
+
+def my_FindNextFile(jitter, encode_str):
+    ret_ad, args = jitter.func_args_stdcall(["handle", "pfindfiledata"])
+    fname = winobjs.find_data.findnext(args.handle)
+    if fname is None:
+        winobjs.lastwin32error = 0x12 # ERROR_NO_MORE_FILES
+        ret = 0
+    else:
+        ret = 1
+        fdata = win32_find_data(cfilename=fname)
+        jitter.vm.set_mem(args.pfindfiledata, fdata.toStruct(encode_str=encode_str))
+    jitter.func_ret_stdcall(ret_ad, ret)
+
+kernel32_FindNextFileA = lambda jitter: my_FindNextFile(jitter, encode_win_str_a)
+kernel32_FindNextFileW = lambda jitter: my_FindNextFile(jitter, encode_win_str_w)
+
+def kernel32_GetNativeSystemInfo(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["sys_ptr"])
+    sysinfo = systeminfo()
+    jitter.vm.set_mem(args.sys_ptr, sysinfo.pack())
+    jitter.func_ret_stdcall(ret_ad, 0)
+
+
+def raw2guid(r):
+    o = struct.unpack('IHHHBBBBBB', r)
+    return '{%.8X-%.4X-%.4X-%.4X-%.2X%.2X%.2X%.2X%.2X%.2X}' % o
+
+
+digs = string.digits + string.ascii_lowercase
+
+
+def int2base(x, base):
+    if x < 0:
+        sign = -1
+    elif x == 0:
+        return '0'
+    else:
+        sign = 1
+    x *= sign
+    digits = []
+    while x:
+        digits.append(digs[x % base])
+        x /= base
+    if sign < 0:
+        digits.append('-')
+    digits.reverse()
+    return ''.join(digits)
+
+
+def msvcrt__ultow(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["value", "p", "radix"])
+
+    value = args.value & 0xFFFFFFFF
+    if not args.radix in [10, 16, 20]:
+        raise ValueError("Not tested")
+    s = int2base(value, args.radix)
+    set_win_str_w(jitter, args.p, s)
+    jitter.func_ret_cdecl(ret_ad, args.p)
+
+
+def msvcrt_myfopen(jitter, get_str):
+    ret_ad, args = jitter.func_args_cdecl(["pfname", "pmode"])
+
+    fname = get_str(args.pfname)
+    rw = get_str(args.pmode)
+    log.info("fopen %r, %r", fname, rw)
+
+    if rw in ['r', 'rb', 'wb+','wb','wt']:
+        sb_fname = windows_to_sbpath(fname)
+        h = open(sb_fname, rw)
+        eax = winobjs.handle_pool.add(sb_fname, h)
+        dwsize = 0x20
+        alloc_addr = winobjs.heap.alloc(jitter, dwsize)
+        pp = pck32(0x11112222) + pck32(0) + pck32(0) + pck32(0) + pck32(eax)
+        jitter.vm.set_mem(alloc_addr, pp)
+
+    else:
+        raise ValueError('unknown access mode %s' % rw)
+
+    jitter.func_ret_cdecl(ret_ad, alloc_addr)
+
+
+def msvcrt__wfopen(jitter):
+    msvcrt_myfopen(jitter, lambda addr:get_win_str_w(jitter, addr))
+
+
+def msvcrt_fopen(jitter):
+    msvcrt_myfopen(jitter, lambda addr:get_win_str_a(jitter, addr))
+
+
+def msvcrt_strlen(jitter):
+    ret_ad, args = jitter.func_args_cdecl(["src"])
+
+    s = get_win_str_a(jitter, args.src)
+    jitter.func_ret_cdecl(ret_ad, len(s))
+
+
+def kernel32_QueryPerformanceCounter(jitter):
+    ret_ad, args = jitter.func_args_stdcall(["lpPerformanceCount"])
+    jitter.vm.set_mem(args.lpPerformanceCount, struct.pack('<Q', 0x1))
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_InitializeCriticalSectionEx(jitter):
+    '''
+      LPCRITICAL_SECTION lpCriticalSection,
+      DWORD              dwSpinCount,
+      DWORD              Flags
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection", "dwSpinCount", "Flags"])
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_EnterCriticalSection(jitter):
+    '''
+    void EnterCriticalSection(
+      LPCRITICAL_SECTION lpCriticalSection
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection"])
+    jitter.func_ret_stdcall(ret_ad, 0x0)
+
+
+def kernel32_LeaveCriticalSection(jitter):
+    '''
+    void LeaveCriticalSection(
+      LPCRITICAL_SECTION lpCriticalSection
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection"])
+    jitter.func_ret_stdcall(ret_ad, 0x0)
+
+
+class FLS(object):
+    def __init__(self):
+        self.slots = []
+
+    def kernel32_FlsAlloc(self, jitter):
+        '''
+        DWORD FlsAlloc(
+          PFLS_CALLBACK_FUNCTION lpCallback
+        );
+        '''
+        ret_ad, args = jitter.func_args_stdcall(["lpCallback"])
+        index = len(self.slots)
+        self.slots.append(0x0)
+        jitter.func_ret_stdcall(ret_ad, index)
+
+    def kernel32_FlsSetValue(self, jitter):
+        '''
+        BOOL FlsSetValue(
+          DWORD dwFlsIndex,
+          PVOID lpFlsData
+        );
+        '''
+        ret_ad, args = jitter.func_args_stdcall(["dwFlsIndex", "lpFlsData"])
+        self.slots[args.dwFlsIndex] = args.lpFlsData
+        jitter.func_ret_stdcall(ret_ad, 1)
+
+    def kernel32_FlsGetValue(self, jitter):
+        '''
+        PVOID FlsGetValue(
+          DWORD dwFlsIndex
+        );
+        '''
+        ret_ad, args = jitter.func_args_stdcall(["dwFlsIndex"])
+        jitter.func_ret_stdcall(ret_ad, self.slots[args.dwFlsIndex])
+
+fls = FLS()
+
+
+def kernel32_GetProcessHeap(jitter):
+    '''
+    HANDLE GetProcessHeap();
+    '''
+    ret_ad, args = jitter.func_args_stdcall([])
+    hHeap = 0x67676767
+    jitter.func_ret_stdcall(ret_ad, hHeap)
+
+
+STD_INPUT_HANDLE = 0xfffffff6
+STD_OUTPUT_HANDLE = 0xfffffff5
+STD_ERROR_HANDLE = 0xfffffff4
+
+
+def kernel32_GetStdHandle(jitter):
+    '''
+    HANDLE WINAPI GetStdHandle(
+      _In_ DWORD nStdHandle
+    );
+
+    STD_INPUT_HANDLE (DWORD)-10
+    The standard input device. Initially, this is the console input buffer, CONIN$.
+
+    STD_OUTPUT_HANDLE (DWORD)-11
+    The standard output device. Initially, this is the active console screen buffer, CONOUT$.
+
+    STD_ERROR_HANDLE (DWORD)-12
+    The standard error device. Initially, this is the active console screen buffer, CONOUT$.
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["nStdHandle"])
+    jitter.func_ret_stdcall(ret_ad, {
+        STD_OUTPUT_HANDLE: 1,
+        STD_ERROR_HANDLE: 2,
+        STD_INPUT_HANDLE: 3,
+    }[args.nStdHandle])
+
+
+FILE_TYPE_UNKNOWN = 0x0000
+FILE_TYPE_CHAR = 0x0002
+
+
+def kernel32_GetFileType(jitter):
+    '''
+    DWORD GetFileType(
+      HANDLE hFile
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["hFile"])
+    jitter.func_ret_stdcall(ret_ad, {
+        # STD_OUTPUT_HANDLE
+        1: FILE_TYPE_CHAR,
+        # STD_ERROR_HANDLE
+        2: FILE_TYPE_CHAR,
+        # STD_INPUT_HANDLE
+        3: FILE_TYPE_CHAR,
+    }.get(args.hFile, FILE_TYPE_UNKNOWN))
+
+
+def kernel32_IsProcessorFeaturePresent(jitter):
+    '''
+    BOOL IsProcessorFeaturePresent(
+      DWORD ProcessorFeature
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["ProcessorFeature"])
+    jitter.func_ret_stdcall(ret_ad, {
+        # PF_ARM_64BIT_LOADSTORE_ATOMIC
+        25: False,
+        # PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE
+        24: False,
+        # PF_ARM_EXTERNAL_CACHE_AVAILABLE
+        26: False,
+        # PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE
+        27: False,
+        # PF_ARM_VFP_32_REGISTERS_AVAILABLE
+        18: False,
+        # PF_3DNOW_INSTRUCTIONS_AVAILABLE
+        7: False,
+        # PF_CHANNELS_ENABLED
+        16: True,
+        # PF_COMPARE_EXCHANGE_DOUBLE
+        2: False,
+        # PF_COMPARE_EXCHANGE128
+        14: False,
+        # PF_COMPARE64_EXCHANGE128
+        15: False,
+        # PF_FASTFAIL_AVAILABLE
+        23: False,
+        # PF_FLOATING_POINT_EMULATED
+        1: False,
+        # PF_FLOATING_POINT_PRECISION_ERRATA
+        0: True,
+        # PF_MMX_INSTRUCTIONS_AVAILABLE
+        3: True,
+        # PF_NX_ENABLED
+        12: True,
+        # PF_PAE_ENABLED
+        9: True,
+        # PF_RDTSC_INSTRUCTION_AVAILABLE
+        8: True,
+        # PF_RDWRFSGSBASE_AVAILABLE
+        22: True,
+        # PF_SECOND_LEVEL_ADDRESS_TRANSLATION
+        20: True,
+        # PF_SSE3_INSTRUCTIONS_AVAILABLE
+        13: True,
+        # PF_VIRT_FIRMWARE_ENABLED
+        21: False,
+        # PF_XMMI_INSTRUCTIONS_AVAILABLE
+        6: True,
+        # PF_XMMI64_INSTRUCTIONS_AVAILABLE
+        10: True,
+        # PF_XSAVE_ENABLED
+        17: False,
+    }[args.ProcessorFeature])
+
+
+def kernel32_GetACP(jitter):
+    '''
+    UINT GetACP();
+    '''
+    ret_ad, args = jitter.func_args_stdcall([])
+    # Windows-1252: Latin 1 / Western European  Superset of ISO-8859-1 (without C1 controls).
+    jitter.func_ret_stdcall(ret_ad, 1252)
+
+
+# ref: https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
+VALID_CODE_PAGES = {
+    37,437,500,708,709,710,720,737,775,850,852,855,857,858,860,861,862,863,864,865,866,869,870,874,875,
+    932,936,949,950,1026,1047,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1200,1201,1250,1251,1252,
+    1253,1254,1255,1256,1257,1258,1361,10000,10001,10002,10003,10004,10005,10006,10007,10008,10010,10017,
+    10021,10029,10079,10081,10082,12000,12001,20000,20001,20002,20003,20004,20005,20105,20106,20107,20108,
+    20127,20261,20269,20273,20277,20278,20280,20284,20285,20290,20297,20420,20423,20424,20833,20838,20866,
+    20871,20880,20905,20924,20932,20936,20949,21025,21027,21866,28591,28592,28593,28594,28595,28596,28597,
+    28598,28599,28603,28605,29001,38598,50220,50221,50222,50225,50227,50229,50930,50931,50933,50935,50936,
+    50937,50939,51932,51936,51949,51950,52936,54936,57002,57003,57004,57005,57006,57007,57008,57009,57010,
+    57011,65000,65001
+}
+
+
+def kernel32_IsValidCodePage(jitter):
+    '''
+    BOOL IsValidCodePage(
+      UINT CodePage
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["CodePage"])
+    jitter.func_ret_stdcall(ret_ad, args.CodePage in VALID_CODE_PAGES)
+
+
+def kernel32_GetCPInfo(jitter):
+    '''
+    BOOL GetCPInfo(
+      UINT     CodePage,
+      LPCPINFO lpCPInfo
+    );
+    '''
+    ret_ad, args = jitter.func_args_stdcall(["CodePage", "lpCPInfo"])
+    assert args.CodePage == 1252
+    # ref: http://www.rensselaer.org/dept/cis/software/g77-mingw32/include/winnls.h
+    #define MAX_LEADBYTES       12
+    #define MAX_DEFAULTCHAR	2
+    jitter.vm.set_mem(args.lpCPInfo, struct.pack('<I', 0x1) + b'??' + b'\x00' * 12)
+    jitter.func_ret_stdcall(ret_ad, 1)
+
+
+def kernel32_GetStringTypeW(jitter):
+    """
+        BOOL GetStringTypeW(
+          DWORD                         dwInfoType,
+          _In_NLS_string_(cchSrc)LPCWCH lpSrcStr,
+          int                           cchSrc,
+          LPWORD                        lpCharType
+        );
+
+        Retrieves character type information for the characters in the specified
+        Unicode source string. For each character in the string, the function
+        sets one or more bits in the corresponding 16-bit element of the output
+        array. Each bit identifies a given character type, for example, letter,
+        digit, or neither.
+
+    """
+    # These types support ANSI C and POSIX (LC_CTYPE) character typing
+    # functions.A bitwise-OR of these values is retrieved in the array in the
+    # output buffer when dwInfoType is set to CT_CTYPE1. For DBCS locales, the
+    # type attributes apply to both narrow characters and wide characters. The
+    # Japanese hiragana and katakana characters, and the kanji ideograph
+    # characters all have the C1_ALPHA attribute.
+    CT_TYPE1 = 0x01
+    # TODO handle other types of information
+    # (CT_TYPE2, CT_TYPE3)
+    # for now, they raise NotImplemented
+    CT_TYPE2 = 0x02
+    CT_TYPE3 = 0x03
+
+    C1_UPPER   = 0x0001  # Uppercase
+    C1_LOWER   = 0x0002  # Lowercase
+    C1_DIGIT   = 0x0004  # Decimal digits
+    C1_SPACE   = 0x0008  # Space characters
+    C1_PUNCT   = 0x0010  # Punctuation
+    C1_CNTRL   = 0x0020  # Control characters
+    C1_BLANK   = 0x0040  # Blank characters
+    C1_XDIGIT  = 0x0080  # Hexadecimal digits
+    C1_ALPHA   = 0x0100  # Any linguistic character: alphabetical, syllabary, or ideographic
+    C1_DEFINED = 0x0200  # A defined character, but not one of the other C1_* types
+
+    # the following sets have been generated from the Linux python library curses
+    # e.g., C1_PUNCT_SET = [chr(i) for i in range(256) if curses.ascii.ispunct(chr(i))]
+    C1_PUNCT_SET = ['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',',
+            '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']',
+            '^', '_', '`', '{', '|', '}', '~']
+    C1_CNTRL_SET = ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06',
+            '\x07', '\x08', '\t', '\n', '\x0b', '\x0c', '\r', '\x0e', '\x0f',
+            '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+            '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+            '\x7f']
+    C1_BLANK_SET = ['\t', ' ']
+    C1_XDIGIT_SET = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
+            'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f']
+
+    ret, args = jitter.func_args_stdcall(['dwInfoType', 'lpSrcStr', 'cchSrc',
+        'lpCharType'])
+    s = jitter.vm.get_mem(args.lpSrcStr, args.cchSrc).decode("utf-16")
+    if args.dwInfoType == CT_TYPE1:
+        # iterate over characters from the decoded W string
+        for i, c in enumerate(s):
+            # TODO handle non-ascii characters
+            if not c.isascii():
+                continue
+            val = 0
+            if c.isupper():
+                val |= C1_UPPER
+            if c.islower():
+                val |= C1_LOWER
+            if c.isdigit():
+                val |= C1_DIGIT
+            if c.isspace():
+                val |= C1_SPACE
+            if c in C1_PUNCT_SET:
+                val |= C1_PUNCT
+            if c in C1_CNTRL_SET:
+                val |= C1_CNTRL
+            if c in C1_BLANK_SET:
+                val |= C1_BLANK
+            if c in C1_XDIGIT_SET:
+                val |= C1_XDIGIT
+            if c.isalpha():
+                val |= C1_ALPHA
+            if val == 0:
+                val = C1_DEFINED
+            jitter.vm.set_u16(args.lpCharType + i * 2, val)
+    elif args.dwInfoType == CT_TYPE2:
+        raise NotImplemented
+    elif args.dwInfoType == CT_TYPE3:
+        raise NotImplemented
+    else:
+        raise ValueError("CT_TYPE unknown: %i" % args.dwInfoType)
+    jitter.func_ret_stdcall(ret, 1)
+    return True