// ithsys.cc
// 8/21/2013 jichi
// Branch: ITH_SYS/SYS.cpp, rev 126
//
// 8/24/2013 TODO:
// - Clean up the code
// - Move my old create remote thread for ITH2 here

#include "ithsys/ithsys.h"
//#include "vnrhook/src/util/growl.h"

//#define ITH_SYS_SECTION L"ITH_SysSection"
#define ITH_THREADMAN_SECTION L"VNR_SYS_THREAD"

// jichi 9/28/2013: Weither use NtThread or RemoteThread
// RemoteThread works on both Windows 7 or Wine, while NtThread does not work on wine
#define ITH_ENABLE_THREADMAN    (!IthIsWindows8OrGreater() && !IthIsWine())
//#define ITH_ENABLE_THREADMAN    true

//#define ITH_ENABLE_WINAPI // jichi: prefer Win32 API to NTDLL API

// Helpers

// jichi 2/3/2015: About GetVersion
// Windows XP SP3: 5.1
// Windows 7: 6.1, 0x1db10106
// Windows 8: 6.2, 0x23f00206
// Windows 10: 6.2, 0x23f00206 (build 9926):

BOOL IthIsWindowsXp()
{
  static BOOL ret = -1; // cached
  if (ret < 0) {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
    DWORD v = ::GetVersion();
    BYTE major = LOBYTE(LOWORD(v));
    //DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));

    // Windows XP = 5.1
    //ret =  major < 6 ? 1 : 0;
    ret = major < 6;
  }
  return ret;
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx
// The same as IsWindows8OrGreater, which I don't know if the function is available to lower Windows.
static BOOL IthIsWindows8OrGreater() // this function is not exported
{
  static BOOL ret = -1; // cached
  if (ret < 0) {
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
    DWORD v = ::GetVersion();
    BYTE major = LOBYTE(LOWORD(v)),
         minor = HIBYTE(LOWORD(v));
    //DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));

    // Windows 8/10 = 6.2
    ret = major > 6 || (major == 6 && minor >= 2);
  }
  return ret;
}

BOOL IthIsWine()
{
  static BOOL ret = -1; // cached
  if (ret < 0) {
    const wchar_t *path;
    wchar_t buffer[MAX_PATH];
    if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
      path = buffer;
      buffer[sz] = 0;
      ::wcscpy(buffer + sz, L"\\winecfg.exe");
    } else
      path = L"C:\\Windows\\System32\\winecfg.exe";
    //ITH_MSG(path);
    ret = ::GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE;
  }
  return ret;
}

// jichi 9/28/2013: prevent parallelization in wine
void IthCoolDown()
{
  // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtDelayExecution.html
  //const LONGLONG timeout = -10000; // in 100ns, i.e. 1ms
  //NtDelayExecution(FALSE, (PLARGE_INTEGER)&timeout);
  //NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
  // Flush the instruction cache line, and prevent wine from rending things in parallel
  if (IthIsWine())
    IthSleep(1); // sleep for 1 ms
  //__asm
  //{
  //  //mov eax,0x2710    // = 10000
  //  mov ecx,time
  //  mul ecx
  //  neg eax
  //  adc edx,0
  //  neg edx
  //  push edx
  //  push eax
  //  push esp
  //  push 0
  //  call dword ptr [NtDelayExecution]
  //  add esp,8
  //}
}

// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//static bool ith_has_section = true;

//#ifdef ITH_WINE
//# include "winddk/winddk.h"
//#endif // ITH_WINE

//#define SEC_BASED 0x200000    // jichi 8/24/2013: emoved

// jichi 10/6/2013
// See: http://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
// See: http://www.codeproject.com/Articles/16598/Get-Your-DLL-s-Path-Name
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define CURRENT_MODULE_HANDLE  ((HINSTANCE)&__ImageBase)
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len)
{ return ::GetModuleFileNameW(CURRENT_MODULE_HANDLE, buf, len); }

// - Global variables -

#ifdef ITH_HAS_HEAP
HANDLE hHeap; // used in ith/common/memory.h
#endif // ITH_HAS_HEAP

DWORD current_process_id;
DWORD debug;
BYTE launch_time[0x10];
LPVOID page;

// jichi 6/12/2015: https://en.wikipedia.org/wiki/Shift_JIS
// Leading table for SHIFT-JIS encoding
BYTE LeadByteTable[0x100] = {
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
  2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
  2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
};

namespace { // unnamed

WCHAR file_path[MAX_PATH] = L"\\??\\";
LPWSTR current_dir;
DWORD page_locale;
HANDLE root_obj,
       dir_obj,
       codepage_section,
       thread_man_section;

BYTE file_info[0x1000];


// - Helper functions -

inline DWORD GetShareMemory()
{
  __asm
  {
    mov eax,fs:[0x30]
    mov eax,[eax+0x4C]
  }
}

inline LARGE_INTEGER *GetTimeBias()
{ __asm mov eax,0x7ffe0020 }


//Get full path of current process.
//inline LPWSTR GetModulePath()
//{
//  __asm
//  {
//    mov eax,fs:[0x30]
//    mov eax,[eax+0xC]
//    mov eax,[eax+0xC]
//    mov eax,[eax+0x28]
//  }
//}

// - Singleton classes -

BYTE normal_routine[0x14] = {
  0x51,0x52,0x64,0x89,0x23,0x55,0xff,0xd0,0x50,0x6a,0xfe,0xff,0x15,0x14,0x00,0x00,0x00
};

BYTE except_routine[0xe0] = {
  0xba,0x08,0x00,0x00,0x00,0x8b,0xc1,0x83,0xe0,0x0f,0x83,0xf8,0x0a,0x72,0x02,0x04,
  0x07,0x04,0x30,0x66,0xab,0xc1,0xc9,0x04,0x4a,0x75,0xea,0xc3,0x00,0x00,0x00,0x00,
  0x8b,0x44,0xe4,0x04,0x31,0xf6,0x8b,0x28,0x8b,0x4c,0xe4,0x0c,0x8b,0x99,0xb8,0x00,
  0x00,0x00,0x81,0xec,0x40,0x02,0x00,0x00,0x8d,0x7c,0xe4,0x40,0x89,0xe0,0x56,0x6a,
  0x1c,0x50,0x56,0x53,0x6a,0xff,0xff,0x15,0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0x98,
  0x89,0xe0,0x50,0x68,0x00,0x02,0x00,0x00,0x57,0x6a,0x02,0x53,0x6a,0xff,0xff,0x15,
  0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0xe6,0x5e,0x0f,0xc1,0xf7,0xfd,0xb0,0x5c,0x66,
  0xf2,0xaf,0x66,0xc7,0x47,0x02,0x3a,0x00,0x89,0xd9,0x2b,0x0c,0xe4,0xe8,0x7e,0xff,
  0xff,0xff,0x47,0x47,0x87,0xfe,0x89,0xe9,0xe8,0x73,0xff,0xff,0xff,0x47,0x47,0x31,
  0xc0,0x89,0x47,0x10,0x6a,0x00,0x57,0x56,0x6a,0x00,0xfc,0xff,0x15,0x1c,0x00,0x00,
  0x00,0x83,0xc8,0xff,0xeb,0xbe
};

// jichi 8/24/2013: Could be initialized using NtMapViewOfSection/ZwMapViewOfSection
// This class cannot have constructor / destructor
struct _ThreadView {
  UINT_PTR mutex,
           count;
  DWORD proc_record[1];
};

class : private _ThreadView { // ThreadStartManager

  enum {
    ADDR0 = 0xD
    , ADDR1 = 0x48
    , ADDR2 = 0x60
    , ADDR3 = 0x9D
  };

public:
  LPVOID GetProcAddr(HANDLE hProc)
  {
    AcquireLock();
    DWORD pid,addr,len;
    if (hProc == NtCurrentProcess())
      pid = ::current_process_id;
    else {
      PROCESS_BASIC_INFORMATION info;
      NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), &len);
      pid=info.uUniqueProcessId;
    }
    pid >>= 2;
    for (UINT_PTR i = 0; i < count; i++)
      if (pid == (proc_record[i] & 0xfff)) {
        addr = proc_record[i] & ~0xfff;
        ReleaseLock();
        return (LPVOID)addr;
      }
    len = 0x1000;
    NtAllocateVirtualMemory(hProc, (PVOID *)(proc_record + count), 0, &len,
        MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    DWORD base = proc_record[count];
    proc_record[count] |= pid;
    union {
      LPVOID buffer;
      DWORD b;
    };
    b = base;
    LPVOID fun_table[3];
    *(DWORD *)(normal_routine + ADDR0) += base;
    NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0);
    *(DWORD *)(normal_routine + ADDR0) -= base;
    b += 0x14;
    fun_table[0] = NtTerminateThread;
    fun_table[1] = NtQueryVirtualMemory;
    fun_table[2] = MessageBoxW;
    NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0);
    b += 0xc;
    *(DWORD *)(except_routine + ADDR1) += base;
    *(DWORD *)(except_routine + ADDR2) += base;
    *(DWORD *)(except_routine + ADDR3) += base;
    NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0);
    *(DWORD *)(except_routine + ADDR1) -= base;
    *(DWORD *)(except_routine + ADDR2) -= base;
    *(DWORD *)(except_routine + ADDR3) -= base;
    count++;
    ReleaseLock();
    return (LPVOID)base;
  }
  void ReleaseProcessMemory(HANDLE hProc)
  {
    DWORD pid,addr,len;
    AcquireLock();
    if (hProc==NtCurrentProcess())
      pid = ::current_process_id;
    else {
      PROCESS_BASIC_INFORMATION info;
      NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len);
      pid = info.uUniqueProcessId;
    }
    pid >>= 2;
    //NtWaitForSingleObject(thread_man_mutex,0,0);
    for (UINT_PTR i = 0; i < count; i++) {
      if ((proc_record[i]&0xfff) == pid) {
        addr = proc_record[i] & ~0xfff;
        DWORD size=0x1000;
        NtFreeVirtualMemory(hProc, (PVOID *)&addr, &size, MEM_RELEASE);
        count--;
        for (UINT_PTR j = i; j < count; j++)
          proc_record[j] = proc_record[j + 1];
        proc_record[count] = 0;
        ReleaseLock();
        //NtReleaseMutant(thread_man_mutex,0);
        return;
      }
    }
    ReleaseLock();
    //NtReleaseMutant(thread_man_mutex,0);
  }
  void CheckProcessMemory()
  {
    UINT_PTR i, j, flag, addr;
    DWORD len;
    CLIENT_ID id;
    OBJECT_ATTRIBUTES oa = {};
    HANDLE hProc;
    BYTE buffer[8];
    AcquireLock();
    id.UniqueThread = 0;
    oa.uLength = sizeof(oa);
    for (i = 0; i < count ; i++) {
      id.UniqueProcess = (proc_record[i]&0xfff)<<2;
      addr = proc_record[i] & ~0xfff;
      flag = 0;
      if (NT_SUCCESS(NtOpenProcess(&hProc, PROCESS_VM_OPERATION|PROCESS_VM_READ, &oa, &id))) {
        if (NT_SUCCESS(NtReadVirtualMemory(hProc, (PVOID)addr, buffer, 8, &len)))
          if (::memcmp(buffer, normal_routine, 4) == 0)
            flag = 1;
        NtClose(hProc);
      }
      if (flag == 0) {
        for (j = i; j < count; j++)
          proc_record[j] = proc_record[j + 1];
        count--;
        i--;
      }
    }
    ReleaseLock();
  }
  void AcquireLock()
  {
    LONG *p = (LONG *)&mutex;
    while (_interlockedbittestandset(p,0))
      YieldProcessor();
  }
  void ReleaseLock()
  {
    LONG *p = (LONG*)&mutex;
    _interlockedbittestandreset(p, 0);
  }
} *thread_man_ = nullptr; // global singleton

} // unnamed namespace

// - API functions -

extern "C" {

void FreeThreadStart(HANDLE hProc)
{
  if (thread_man_)
    ::thread_man_->ReleaseProcessMemory(hProc);
}

void CheckThreadStart()
{
  if (thread_man_)
    ::thread_man_->CheckProcessMemory();

    // jichi 2/2/2015: This function is only used to wait for injected threads vnrhost.
    // Sleep for 100 ms to wait for remote thread to start
    //IthSleep(100);
    //IthCoolDown();
}

void IthSleep(int time)
{
  __asm
  {
    mov eax,0x2710 // jichi = 10000
    mov ecx,time
    mul ecx
    neg eax
    adc edx,0
    neg edx
    push edx
    push eax
    push esp
    push 0
    call dword ptr [NtDelayExecution]
    add esp,8
  }
}

void IthSystemTimeToLocalTime(LARGE_INTEGER *time)
{ time->QuadPart -= GetTimeBias()->QuadPart; }

int FillRange(LPCWSTR name, DWORD *lower, DWORD *upper)
{
  PLDR_DATA_TABLE_ENTRY it;
  LIST_ENTRY *begin;
  __asm
  {
    mov eax,fs:[0x30]
    mov eax,[eax+0xc]
    mov eax,[eax+0xc]
    mov it,eax
    mov begin,eax
  }

  while (it->SizeOfImage) {
    if (::_wcsicmp(it->BaseDllName.Buffer, name) == 0) {
      *lower = *upper = (DWORD)it->DllBase;
      MEMORY_BASIC_INFORMATION info = {};
      DWORD l,size;
      size = 0;
      do {
        NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)(*upper), MemoryBasicInformation, &info, sizeof(info), &l);
        if (info.Protect&PAGE_NOACCESS) {
          it->SizeOfImage=size;
          break;
        }
        size += info.RegionSize;
        *upper += info.RegionSize;
      } while (size < it->SizeOfImage);
      return 1;
    }
    it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
    if (it->InLoadOrderModuleList.Flink == begin)
      break;
  }
  return 0;
}

DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
{
  __asm
  {
    mov eax,search_length
alloc:
    push 0
    sub eax,1
    jnz alloc

    mov edi,search
    mov edx,search_length
    mov ecx,1
    xor esi,esi
build_table:
    mov al,byte ptr [edi+esi]
    cmp al,byte ptr [edi+ecx]
    sete al
    test esi,esi
    jz pre
    test al,al
    jnz pre
    mov esi,[esp+esi*4-4]
    jmp build_table
pre:
    test al,al
    jz write_table
    inc esi
write_table:
    mov [esp+ecx*4],esi

    inc ecx
    cmp ecx,edx
    jb build_table

    mov esi,base
    xor edx,edx
    mov ecx,edx
matcher:
    mov al,byte ptr [edi+ecx]
    cmp al,byte ptr [esi+edx]
    sete al
    test ecx,ecx
    jz match
    test al,al
    jnz match
    mov ecx, [esp+ecx*4-4]
    jmp matcher
match:
    test al,al
    jz pre2
    inc ecx
    cmp ecx,search_length
    je finish
pre2:
    inc edx
    cmp edx,base_length // search_length
    jb matcher
    mov edx,search_length
    dec edx
finish:
    mov ecx,search_length
    sub edx,ecx
    lea eax,[edx+1]
    lea ecx,[ecx*4]
    add esp,ecx
  }
}

// jichi 2/5/2014: '?' = 0xff
// See: http://sakuradite.com/topic/124
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard) // KMP
{
  __asm
  {
    // jichi 2/5/2014 BEGIN
    mov bl,wildcard
    // jichi 2/5/2014 END
    mov eax,search_length
alloc:
    push 0
    sub eax,1
    jnz alloc // jichi 2/5/2014: this will also set %eax to zero

    mov edi,search
    mov edx,search_length
    mov ecx,1
    xor esi,esi
build_table:
    mov al,byte ptr [edi+esi]
    cmp al,byte ptr [edi+ecx]
    sete al
    test esi,esi
    jz pre
    test al,al
    jnz pre
    mov esi,[esp+esi*4-4]
    jmp build_table
pre:
    test al,al
    jz write_table
    inc esi
write_table:
    mov [esp+ecx*4],esi

    inc ecx
    cmp ecx,edx
    jb build_table

    mov esi,base
    xor edx,edx
    mov ecx,edx
matcher:
    mov al,byte ptr [edi+ecx] // search
    // jichi 2/5/2014 BEGIN
    mov bh,al // save loaded byte to reduce cache access. %ah is not used and always zero
    cmp al,bl // %bl is the wildcard byte
    sete al
    test al,al
    jnz wildcard_matched
    mov al,bh // restore the loaded byte
    // jichi 2/5/2014 END
    cmp al,byte ptr [esi+edx] // base
    sete al
    // jichi 2/5/2014 BEGIN
wildcard_matched:
    // jichi 2/5/2014 END
    test ecx,ecx
    jz match
    test al,al
    jnz match
    mov ecx, [esp+ecx*4-4]
    jmp matcher
match:
    test al,al
    jz pre2
    inc ecx
    cmp ecx,search_length
    je finish
pre2:
    inc edx
    cmp edx,base_length // search_length
    jb matcher
    mov edx,search_length
    dec edx
finish:
    mov ecx,search_length
    sub edx,ecx
    lea eax,[edx+1]
    lea ecx,[ecx*4]
    add esp,ecx
  }
}

DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size)
{
  DWORD r;
  MEMORY_BASIC_INFORMATION info;
  NtQueryVirtualMemory(NtCurrentProcess(), const_cast<LPVOID>(mem), MemoryBasicInformation, &info, sizeof(info), &r);
  if (base)
    *base = (DWORD)info.BaseAddress;
  if (size)
    *size = info.RegionSize;
  return (info.Type&PAGE_NOACCESS) == 0;
}

// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// SJIS->Unicode. 'mb' must be null-terminated. 'wc' should have enough space ( 2*strlen(mb) is safe).
//#ifdef ITH_WINE
//int MB_WC(char *mb, wchar_t *wc)
//{ return mbstowcs(wc, mb, 0x100); }
//
//#else
int MB_WC(char *mb, wchar_t *wc)
{
  __asm
  {
    mov esi,mb
    mov edi,wc
    mov edx,page
    lea ebx,LeadByteTable
    add edx,0x220
    push 0
_mb_translate:
    movzx eax,word ptr [esi]
    test al,al
    jz _mb_fin
    movzx ecx,al
    xlat
    test al,1
    cmovnz cx, word ptr [ecx*2+edx-0x204]
    jnz _mb_next
    mov cx,word ptr [ecx*2+edx]
    mov cl,ah
    mov cx, word ptr [ecx*2+edx]
_mb_next:
    mov [edi],cx
    add edi,2
    movzx eax,al
    add esi,eax
    inc dword ptr [esp]
    jmp _mb_translate
_mb_fin:
    pop eax
  }
}

// Count characters of 'mb' string. 'mb_length' is max length.
// jichi 9/25/2013: This function is not used
//int MB_WC_count(char *mb, int mb_length)
//{
//  __asm
//  {
//    xor eax,eax
//    xor edx,edx
//    mov esi,mb
//    mov edi,mb_length
//    lea ebx,LeadByteTable
//_mbc_count:
//    mov dl,byte ptr [esi]
//    test dl,dl
//    jz _mbc_finish
//    movzx ecx, byte ptr [ebx+edx]
//    add esi,ecx
//    inc eax
//    sub edi,ecx
//    ja _mbc_count
//_mbc_finish:
//  }
//}

// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// Unicode->SJIS. Analogous to MB_WC.
//#ifdef ITH_WINE
//int WC_MB(wchar_t *wc, char *mb)
//{ return wcstombs(mb, wc, 0x100); }
//
//#else
int WC_MB(wchar_t *wc, char *mb)
{
  __asm
  {
    mov esi,wc
    mov edi,mb
    mov edx,page
    add edx,0x7c22
    xor ebx,ebx
_wc_translate:
    movzx eax,word ptr [esi]
    test eax,eax
    jz _wc_fin
    mov cx,word ptr [eax*2+edx]
    test ch,ch
    jz _wc_single
    mov [edi+ebx],ch
    inc ebx
_wc_single:
    mov [edi+ebx],cl
    inc ebx
    add esi,2
    jmp _wc_translate
_wc_fin:
    mov eax,ebx
  }
}

//Initialize environment for NT native calls. Not thread safe so only call it once in one module.
//1. Create new heap. Future memory requests are handled by this heap.
//Destroying this heap will completely release all dynamically allocated memory, thus prevent memory leaks on unload.
//2. Create handle to root directory of process objects (section/event/mutex/semaphore).
//NtCreate* calls will use this handle as base directory.
//3. Load SJIS code page. First check for Japanese locale. If not then load from 'C_932.nls' in system folder.
//MB_WC & WC_MB use this code page for translation.
//4. Locate current NT path (start with \??\).
//NtCreateFile requires full path or a root handle. But this handle is different from object.
//5. Map shared memory for ThreadStartManager into virtual address space.
//This will allow IthCreateThread function properly.
BOOL IthInitSystemService()
{
  PPEB peb;
  //NTSTATUS status;
  DWORD size;
  ULONG LowFragmentHeap;
  UNICODE_STRING us;
  OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  IO_STATUS_BLOCK ios;
  HANDLE codepage_file;
  LARGE_INTEGER sec_size = {0x1000, 0};
  __asm
  {
    mov eax,fs:[0x18]
    mov ecx,[eax+0x20]
    mov eax,[eax+0x30]
    mov peb,eax
    mov current_process_id,ecx
  }
  debug = peb->BeingDebugged;
  LowFragmentHeap = 2;

#ifdef ITH_HAS_HEAP
  ::hHeap = RtlCreateHeap(0x1002, 0, 0, 0, 0, 0);
  RtlSetHeapInformation(::hHeap, HeapCompatibilityInformation, &LowFragmentHeap, sizeof(LowFragmentHeap));
#endif // ITH_HAS_HEAP

  LPWSTR t = nullptr,   // jichi: path to system32, such as "c:\windows\system32"
         obj = nullptr; // jichi: path to current kernel session, such as "Sessions\\1\\BaseNamedObjects"
  // jichi 9/22/2013: This would crash wine with access violation exception.
  if (!IthIsWine()) {
    // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
    //   t = L"C:\\Windows\\system32";
    //   obj = L"\\Sessions\\1\\BaseNamedObjects";
    // On Windows XP
    //   t = L"C:\\WINDOWS\\system32";
    //   obj = L"\\BaseNamedObjects";
    MEMORY_BASIC_INFORMATION info;
    if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), peb->ReadOnlySharedMemoryBase, MemoryBasicInformation, &info, sizeof(info), &size)))
      return FALSE;
    DWORD base = (DWORD)peb->ReadOnlySharedMemoryBase;
    DWORD end = base + info.RegionSize - 0x40;
    static WCHAR system32[] = L"system32";
    for (;base < end; base += 2)
      if (::memcmp((PVOID)base, system32, 0x10) == 0) {
        t = (LPWSTR)base;
        while (*t-- != L':');
        obj = (LPWSTR)base;
        while (*obj != L'\\') obj++;
        break;
      }
    if (base == end)
      return FALSE;
  }
  //ITH_MSG(t);
  //ITH_MSG(obj);

  LDR_DATA_TABLE_ENTRY *ldr_entry = (LDR_DATA_TABLE_ENTRY*)peb->Ldr->InLoadOrderModuleList.Flink;

  // jichi 7/12/2015: This will fail when the file path is a remote path such as:
  // Original remote file path: \\??\\\\\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
  // Correct UNC path: \\??\\\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
  //RtlInitUnicodeString(&us, L"\\??\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
  //WCHAR file_path[MAX_PATH] = L"\\??\\";
  LPCWSTR modulePath = ldr_entry->FullDllName.Buffer;
  if (modulePath[0] == '\\' && modulePath[1] == '\\') { // This is a remote path
    ::file_path[4] = 'U';
    ::file_path[5] = 'N';
    ::file_path[6] = 'C';
    ::wcscpy(::file_path + 7, modulePath + 1);
  } else
    ::wcscpy(::file_path + 4, modulePath);

  current_dir = ::wcsrchr(::file_path, L'\\') + 1;
  *current_dir = 0;

  //GROWL(::file_path);
  RtlInitUnicodeString(&us, ::file_path);

  if (!NT_SUCCESS(NtOpenFile(&dir_obj,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,
      &oa,&ios,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT)))
    return FALSE;

  // jichi 9/22/2013: Get kernel object session ID
  // See: http://www.brianbondy.com/blog/id/100/
  // It seems that on sessionId is 0 on Windows XP, and 1 on Windows Vista and later
  // I assume that sessionId is in [0,9]
  // For ChuSingura46+1 on Windows 7
  //    obj = L"\\Sessions\\1\\BaseNamedObjects";
  // On Windows XP
  //    obj = L"\\BaseNamedObjects";
  //ITH_MSG(obj);
  {
    if (obj)
      RtlInitUnicodeString(&us, obj);
    else { // jichi ITH is on Wine
      // Get session ID in PEB
      // See: http://msdn.microsoft.com/en-us/library/bb432286%28v=vs.85%29.aspx
      DWORD sessionId = peb->SessionId;
      if (!sessionId) // Windows XP
        RtlInitUnicodeString(&us, L"\\BaseNamedObjects");
      else { // Windows Vista +
        wchar_t path[] = L"\\Sessions\\0\\BaseNamedObjects";
        path[10] += (wchar_t)sessionId; // replace 0 with the session ID
        RtlInitUnicodeString(&us, path);
      }
    }
  }

  if (!NT_SUCCESS(NtOpenDirectoryObject(&::root_obj, READ_CONTROL|0xf, &oa)))
    return FALSE;

  ::page = peb->InitAnsiCodePageData;

  enum { CP932 = 932 };

  // jichi 9/23/2013: Access violation on Wine
  if (IthIsWine())
    // One wine, there is no C_932.nls
    //page_locale = 0x4e4; // 1252, English
    //page_locale = GetACP(); // This will return 932 when LC_ALL=ja_JP.UTF-8 on wine
    // Always set locale to CP932 on Wine, since C_932.nls could be missing.
    ::page_locale = CP932;
  else
    ::page_locale = *(DWORD *)page >> 16;

  if (::page_locale == CP932) {
    oa.hRootDirectory = ::root_obj;
    oa.uAttributes |= OBJ_OPENIF;
  } else { // Unreachable or wine
//#ifdef ITH_WINE
//    // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
//    //t = L"C:\\Windows\\system32";
//    wchar_t buffer[MAX_PATH];
//    if (!t) { // jichi 9/22/2013: ITH is one wine
//      if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
//        buffer[sz] = 0;
//        t = buffer;
//      } else
//        t = L"C:\\Windows\\System32"; // jichi 9/29/2013: sth is wrong here
//    }
//#endif // ITH_WINE

    ::wcscpy(::file_path + 4, t);
    t = ::file_path;
    while(*++t);
    if (*(t-1)!=L'\\')
      *t++=L'\\';
    ::wcscpy(t,L"C_932.nls");
    RtlInitUnicodeString(&us, ::file_path);
    if (!NT_SUCCESS(NtOpenFile(&codepage_file, FILE_READ_DATA, &oa, &ios,FILE_SHARE_READ,0)))
      return FALSE;
    oa.hRootDirectory = ::root_obj;
    oa.uAttributes |= OBJ_OPENIF;
    RtlInitUnicodeString(&us, L"JPN_CodePage");
    if (!NT_SUCCESS(NtCreateSection(&codepage_section, SECTION_MAP_READ,
        &oa,0, PAGE_READONLY, SEC_COMMIT, codepage_file)))
      return FALSE;
    NtClose(codepage_file);
    size = 0;
    ::page = nullptr;
    if (!NT_SUCCESS(NtMapViewOfSection(::codepage_section, NtCurrentProcess(),
        &::page,
        0, 0, 0, &size, ViewUnmap, 0,
        PAGE_READONLY)))
      return FALSE;
  }
  if (ITH_ENABLE_THREADMAN) {
    RtlInitUnicodeString(&us, ITH_THREADMAN_SECTION);
    if (!NT_SUCCESS(NtCreateSection(&thread_man_section, SECTION_ALL_ACCESS, &oa, &sec_size,
        PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0)))
      return FALSE;
    size = 0;
    // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Section/NtMapViewOfSection.html
    thread_man_ = nullptr;
    if (!NT_SUCCESS(NtMapViewOfSection(thread_man_section, NtCurrentProcess(),
       (LPVOID *)&thread_man_,
       0,0,0, &size, ViewUnmap, 0,
       PAGE_EXECUTE_READWRITE)))
      return FALSE;
  }
  return TRUE;
}

//Release resources allocated by IthInitSystemService.
//After destroying the heap, all memory allocated by ITH module is returned to system.
void IthCloseSystemService()
{
  if (::page_locale != 0x3a4) {
    NtUnmapViewOfSection(NtCurrentProcess(), ::page);
    NtClose(::codepage_section);
  }
  if (ITH_ENABLE_THREADMAN) {
    NtUnmapViewOfSection(NtCurrentProcess(), ::thread_man_);
    NtClose(::thread_man_section);
  }
  NtClose(::root_obj);
#ifdef ITH_HAS_HEAP
  RtlDestroyHeap(::hHeap);
#endif // ITH_HAS_HEAP
}

//Check for existence of a file in current folder. Thread safe after init.
//For ITH main module, it's ITH folder. For target process it's the target process's current folder.
BOOL IthCheckFile(LPCWSTR file)
{
  //return PathFileExistsW(file);   // jichi: need Shlwapi.lib

  //return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
  //return GetFileAttributesW(file) != INVALID_FILE_ATTRIBUTES; // jichi: does not consider the current app's path

  // jichi 9/22/2013: Following code does not work in Wine
  // See: http://stackoverflow.com/questions/3828835/how-can-we-check-if-a-file-exists-or-not-using-win32-program
   //WIN32_FIND_DATA FindFileData;
   //HANDLE handle = FindFirstFileW(file, &FindFileData);
   //if (handle != INVALID_HANDLE_VALUE) {
   //  FindClose(handle);
   //  return TRUE;
   //}
   //return FALSE;
  if (IthIsWine()) {
    HANDLE hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
    if (hFile != INVALID_HANDLE_VALUE) {
      CloseHandle(hFile);
      return TRUE;
    } else if (!wcschr(file, L':')) { // jichi: this is relative path
      // jichi 9/22/2013: Change current directory to the same as main module path
      // Otherwise NtFile* would not work for files with relative paths.
      if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
        if (const wchar_t *base = wcsrchr(path, L'\\')) {
          size_t dirlen = base - path + 1;
          if (dirlen + wcslen(file) < MAX_PATH) {
            wchar_t buf[MAX_PATH];
            wcsncpy(buf, path, dirlen);
            wcscpy(buf + dirlen, file);
            return IthCheckFile(buf);
          }
        }
    }
  } else { // not wine
    HANDLE hFile;
    IO_STATUS_BLOCK isb;
    UNICODE_STRING us;
    RtlInitUnicodeString(&us, file);
    OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, 0, 0, 0};
    // jichi 9/22/2013: Following code does not work in Wine
    if (NT_SUCCESS(NtCreateFile(&hFile, FILE_READ_DATA, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) {
      NtClose(hFile);
      return TRUE;
    }
  }
  return FALSE;
  //return IthGetFileInfo(file,file_info);
  //wcscpy(current_dir,file);
}

//Check for existence of files in current folder.
//Unlike IthCheckFile, this function allows wildcard character.
BOOL IthFindFile(LPCWSTR file)
{
  NTSTATUS status;
  HANDLE h;
  UNICODE_STRING us;
  OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  us.Buffer = const_cast<LPWSTR>(file);
  LPCWSTR path = wcsrchr(file, L'\\');
  if (path) {
    us.Length = (path - file) << 1;
    us.MaximumLength = us.Length;
  } else {
    us.Length = 0;
    us.MaximumLength = 0;
  }
  IO_STATUS_BLOCK ios;
  if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
      &oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
    BYTE info[0x400];
    if (path)
      RtlInitUnicodeString(&us, path + 1);
    else
      RtlInitUnicodeString(&us, file);
    status = NtQueryDirectoryFile(h,0,0,0,&ios,info,0x400,FileBothDirectoryInformation,TRUE,&us,TRUE);
    NtClose(h);
    return NT_SUCCESS(status);
  }
  return FALSE;
}
//Analogous to IthFindFile, but return detail information in 'info'.
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size)
{
  NTSTATUS status;
  HANDLE h;
  UNICODE_STRING us;
  LPCWSTR path = wcsrchr(file, L'\\');
  us.Buffer = const_cast<LPWSTR>(file);
  if (path) {
    us.Length = (path - file) << 1;
    us.MaximumLength = us.Length;
  } else {
    us.Length = 0;
    us.MaximumLength = 0;
  }
  //RtlInitUnicodeString(&us,file);
  OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  IO_STATUS_BLOCK ios;
  if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
      &oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
    RtlInitUnicodeString(&us,file);
    status = NtQueryDirectoryFile(h,0,0,0,&ios,info,size,FileBothDirectoryInformation,0,&us,0);
    status = NT_SUCCESS(status);
    NtClose(h);
  } else
    status = FALSE;
  return status;
}

//Check for existence of a file with full NT path(start with \??\).
BOOL IthCheckFileFullPath(LPCWSTR file)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us, file);
  OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  HANDLE hFile;
  IO_STATUS_BLOCK isb;
  if (NT_SUCCESS(NtCreateFile(&hFile,FILE_READ_DATA,&oa,&isb,0,0,FILE_SHARE_READ,FILE_OPEN,0,0,0))) {
    NtClose(hFile);
    return TRUE;
  } else
    return FALSE;
}
//Create or open file in current folder. Analogous to Win32 CreateFile.
//option: GENERIC_READ / GENERIC_WRITE.
//share: FILE_SHARE_READ / FILE_SHARE_WRITE / FILE_SHARE_DELETE. 0 for exclusive access.
//disposition: FILE_OPEN / FILE_OPEN_IF.
//Use FILE_OPEN instead of OPEN_EXISTING and FILE_OPEN_IF for CREATE_ALWAYS.
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us, name);
  OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0 };
  HANDLE hFile;
  IO_STATUS_BLOCK isb;
  return NT_SUCCESS(NtCreateFile(&hFile,
      option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
      &oa,&isb,0,0,share,disposition,
      FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
    hFile : INVALID_HANDLE_VALUE;
}
//Create a directory file in current folder.
HANDLE IthCreateDirectory(LPCWSTR name)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us,name);
  OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  HANDLE hFile;
  IO_STATUS_BLOCK isb;
  return NT_SUCCESS(NtCreateFile(&hFile,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,&oa,&isb,0,0,
      FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN_IF,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT,0,0)) ?
     hFile : INVALID_HANDLE_VALUE;
}

HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us,name);
  if (dir == 0) dir = dir_obj;
  OBJECT_ATTRIBUTES oa = {sizeof(oa), dir, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  HANDLE hFile;
  IO_STATUS_BLOCK isb;
  return NT_SUCCESS(NtCreateFile(&hFile,
      option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
      &oa,&isb,0,0,share,disposition,
      FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
    hFile : INVALID_HANDLE_VALUE;
}

//Analogous to IthCreateFile, but with full NT path.
HANDLE IthCreateFileFullPath(LPCWSTR path, DWORD option, DWORD share, DWORD disposition)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us,path);
  OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
  HANDLE hFile;
  IO_STATUS_BLOCK isb;
  return NT_SUCCESS(NtCreateFile(&hFile,
      option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
      &oa,&isb,0,0,share,disposition,
      FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
    hFile : INVALID_HANDLE_VALUE;
}

//Create section object for sharing memory between processes.
//Similar to CreateFileMapping.
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right)
{
// jichi 9/25/2013: GENERIC_ALL does NOT work one wine
// See ZwCreateSection: http://msdn.microsoft.com/en-us/library/windows/hardware/ff566428%28v=vs.85%29.aspx
//#ifdef ITH_WINE
  enum { DesiredAccess = SECTION_ALL_ACCESS };
//#else
//  enum { DesiredAccess = GENERIC_ALL }; // jichi 9/25/2013: not sure whhy ITH is usin GENERIC_ALL
//#endif // ITH_WINE
#define eval    (NT_SUCCESS(NtCreateSection(&hSection, DesiredAccess, poa, &s, \
      right, SEC_COMMIT, 0)) ? hSection : INVALID_HANDLE_VALUE)
  HANDLE hSection;
  LARGE_INTEGER s = {size, 0};
  OBJECT_ATTRIBUTES *poa = nullptr;
  // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
  // is pointed to freed object on the stack?! This will crash wine!
  if (name) {
    UNICODE_STRING us;
    RtlInitUnicodeString(&us, name);
    OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us,OBJ_OPENIF,0,0};
    poa = &oa;
    return eval;
  } else
    return eval;
#undef retval
}

//Create event object. Similar to CreateEvent.
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset, DWORD init_state)
{
#define eval  (NT_SUCCESS(NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, poa, auto_reset, init_state)) ? \
     hEvent : INVALID_HANDLE_VALUE)
  HANDLE hEvent;
  OBJECT_ATTRIBUTES *poa = nullptr;
  // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
  // is pointed to freed object on the stack?! This will crash wine!
  if (name) {
    UNICODE_STRING us;
    RtlInitUnicodeString(&us,name);
    OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
    poa = &oa;
    return eval;
  } else
    return eval;
#undef eval
}

HANDLE IthOpenEvent(LPCWSTR name)
{
  UNICODE_STRING us;
  RtlInitUnicodeString(&us, name);
  OBJECT_ATTRIBUTES oa = { sizeof(oa), root_obj, &us, 0, 0, 0 };
  HANDLE hEvent;
  return NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &oa)) ?
     hEvent : INVALID_HANDLE_VALUE;
}

void IthSetEvent(HANDLE hEvent) { NtSetEvent(hEvent, 0); }

void IthResetEvent(HANDLE hEvent) { NtClearEvent(hEvent); }

//Create mutex object. Similar to CreateMutex.
//If 'exist' is not null, it will be written 1 if mutex exist.
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist)
{
#ifdef ITH_ENABLE_WINAPI
  HANDLE ret = ::CreateMutexW(nullptr, InitialOwner, name);
  if (exist)
    *exist = ret == INVALID_HANDLE_VALUE || ::GetLastError() == ERROR_ALREADY_EXISTS;
  return ret;
#else
#define eval    NtCreateMutant(&hMutex, MUTEX_ALL_ACCESS, poa, InitialOwner)
  UNICODE_STRING us;
  HANDLE hMutex;
  NTSTATUS status;
  OBJECT_ATTRIBUTES *poa = nullptr;
  // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
  // is pointed to freed object on the stack?! This will crash wine!
  if (name) {
    //GROWL(name);
    RtlInitUnicodeString(&us, name);
    OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
    poa = &oa;
    status = eval;
    //GROWL_DWORD(status);
  } else
    status = eval;
  if (NT_SUCCESS(status)) {
    if (exist)
      *exist = status == STATUS_OBJECT_NAME_EXISTS;
    return hMutex;
  } else
    return INVALID_HANDLE_VALUE;
#undef eval
#endif // ITH_ENABLE_WINAPI
}

HANDLE IthOpenMutex(LPCWSTR name)
{
#ifdef ITH_ENABLE_WINAPI
  return ::OpenMutexW(MUTEX_ALL_ACCESS, FALSE, name);
#else
  UNICODE_STRING us;
  RtlInitUnicodeString(&us, name);
  OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, 0, 0, 0};
  HANDLE hMutex;
  if (NT_SUCCESS(NtOpenMutant(&hMutex, MUTEX_ALL_ACCESS, &oa)))
    return hMutex;
  else
    return INVALID_HANDLE_VALUE;
#endif // ITH_ENABLE_WINAPI
}

BOOL IthReleaseMutex(HANDLE hMutex)
{ return NT_SUCCESS(NtReleaseMutant(hMutex, 0)); }

//Create new thread. 'hProc' must have following right.
//PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE.
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc)
{
  HANDLE hThread;
  // jichi 9/27/2013: NtCreateThread is not implemented in Wine 1.7
  if (thread_man_) { // Windows XP
    // jichi 9/29/2013: Reserved && commit stack size
    // See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx
    // See: http://msdn.microsoft.com/en-us/library/ms810627.aspx
    enum { DEFAULT_STACK_LIMIT = 0x400000 };
    enum { DEFAULT_STACK_COMMIT = 0x10000 };
    enum { PAGE_SIZE = 0x1000 };
    CLIENT_ID id;
    LPVOID protect;
    USER_STACK stack = {};
    CONTEXT ctx = {CONTEXT_FULL};
    DWORD size = DEFAULT_STACK_LIMIT,
          commit = DEFAULT_STACK_COMMIT;
    if (!NT_SUCCESS(NtAllocateVirtualMemory(hProc, &stack.ExpandableStackBottom, 0, &size, MEM_RESERVE, PAGE_READWRITE)))
      return INVALID_HANDLE_VALUE;

    stack.ExpandableStackBase = (char *)stack.ExpandableStackBottom + size;
    stack.ExpandableStackLimit = (char *)stack.ExpandableStackBase - commit;
    size = PAGE_SIZE;
    commit += size;
    protect = (char *)stack.ExpandableStackBase - commit;
    NtAllocateVirtualMemory(hProc, &protect, 0, &commit, MEM_COMMIT, PAGE_READWRITE);
    DWORD oldAccess; // jichi 9/29/2013: unused
    NtProtectVirtualMemory(hProc, &protect, &size, PAGE_READWRITE|PAGE_GUARD, &oldAccess);
    ctx.SegGs = 0;
    ctx.SegFs = 0x38;
    ctx.SegEs = 0x20;
    ctx.SegDs = 0x20;
    ctx.SegSs = 0x20;
    ctx.SegCs = 0x18;
    ctx.EFlags = 0x3000;
    ctx.Eip = (DWORD)thread_man_->GetProcAddr(hProc);
    ctx.Eax = (DWORD)start_addr;
    ctx.Ecx = ctx.Eip + 0x40;
    ctx.Edx = 0xffffffff;
    ctx.Esp = (DWORD)stack.ExpandableStackBase - 0x10;
    ctx.Ebp = param;

     // NTSYSAPI
     // NTSTATUS
     // NTAPI
     // NtCreateThread(
     //   _Out_ PHANDLE             ThreadHandle,
     //   _In_  ACCESS_MASK         DesiredAccess,
     //   _In_  POBJECT_ATTRIBUTES  ObjectAttributes,
     //   _In_  HANDLE              ProcessHandle,
     //   _Out_ PCLIENT_ID          ClientId,
     //   _In_  PCONTEXT            ThreadContext,
     //   _In_  PUSER_STACK         UserStack,
     //   _In_  BOOLEAN             CreateSuspended
     // );
    if (NT_SUCCESS(NtCreateThread(
        &hThread, // _Out_ PHANDLE             ThreadHandle,
        THREAD_ALL_ACCESS, // _In_ ACCESS_MASK DesiredAccess,
        nullptr,  // _In_  POBJECT_ATTRIBUTES  ObjectAttributes,
        hProc,    // _In_  HANDLE              ProcessHandle,
        &id,      // _Out_ PCLIENT_ID          ClientId,
        &ctx,     // _In_  PCONTEXT            ThreadContext,
        &stack,   // _In_  PUSER_STACK         UserStack,
        TRUE      // _In_  BOOLEAN             CreateSuspended
      ))) {
      // On x64 Windows, NtCreateThread in ntdll calls NtCreateThread in ntoskrnl via WOW64,
      // which maps 32-bit system call to the correspond 64-bit version.
      // This layer doesn't correctly copy whole CONTEXT structure, so we must set it manually
      // after the thread is created.
      // On x86 Windows, this step is not necessary.
      NtSetContextThread(hThread, &ctx);
      NtResumeThread(hThread, 0);
    } else
      hThread = INVALID_HANDLE_VALUE;

  } else {
    // jichi 9/27/2013: CreateRemoteThread works on both Wine and Windows 7
    // Use CreateRemoteThread instead
    // FIXME 10/5/2031: Though sometimes works, CreateRemoteThread randomly crashes on wine.
    // See:
    // - http://www.unknowncheats.me/forum/c-and-c/64775-createremotethread-dll-injection.html
    // - http://source.winehq.org/WineAPI/CreateRemoteThread.html
    // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682437%28v=vs.85%29.aspx
    // HANDLE WINAPI CreateRemoteThread(
    //   _In_   HANDLE hProcess,
    //   _In_   LPSECURITY_ATTRIBUTES lpThreadAttributes,
    //   _In_   SIZE_T dwStackSize,
    //   _In_   LPTHREAD_START_ROUTINE lpStartAddress,
    //   _In_   LPVOID lpParameter,
    //   _In_   DWORD dwCreationFlags,
    //   _Out_  LPDWORD lpThreadId
    // );
    //ITH_TRY {
    if (hProc == INVALID_HANDLE_VALUE)
      hProc = GetCurrentProcess();
    //DWORD dwThreadId;
    hThread = CreateRemoteThread(
      hProc,        // _In_ HANDLE hProcess,
      nullptr,      // _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
      0,            // _In_ SIZE_T dwStackSize,
      (LPTHREAD_START_ROUTINE)start_addr,  // _In_ LPTHREAD_START_ROUTINE lpStartAddress,
      (LPVOID)param,  // _In_ LPVOID lpParameter,
      0,            //STACK_SIZE_PARAM_IS_A_RESERVATION  // _In_ DWORD dwCreationFlags,
      nullptr       // _Out_ LPDWORD lpThreadId
    );
    if (!hThread)   // jichi: this function returns nullptr instead of -1
      hThread = INVALID_HANDLE_VALUE;
    //} ITH_EXCEPT {
    //  ITH_WARN(L"exception");
    //  hThread = INVALID_HANDLE_VALUE;
    //}
  }
  /*
  else {
    // jichi 9/29/2013: Also work on Wine and Windows 7
    // See: http://waleedassar.blogspot.com/2012/06/createremotethread-vs.html
    CLIENT_ID id;
    //DWORD size = DEFAULT_STACK_LIMIT,
    //      commit = DEFAULT_STACK_COMMIT;
    DWORD reserve = 0,
          commit = 0;
    // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlCreateUserThread.html
    //   NTSYSAPI
    //   NTSTATUS
    //   NTAPI
    //   RtlCreateUserThread(
    //     IN HANDLE ProcessHandle,
    //     IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
    //     IN BOOLEAN CreateSuspended,
    //     IN ULONG StackZeroBits,
    //     IN OUT PULONG StackReserved,
    //     IN OUT PULONG StackCommit,
    //     IN PVOID StartAddress,
    //     IN PVOID StartParameter OPTIONAL,
    //     OUT PHANDLE ThreadHandle,
    //     OUT PCLIENT_ID ClientID);
    if (!NT_SUCCESS(RtlCreateUserThread(
        hProc,        // HANDLE hProcess,
        nullptr,      // IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
        FALSE,        // IN BOOLEAN CreateSuspended,
        0,            // IN ULONG StackZeroBits,
        &reserve,     // IN OUT PULONG StackReserved,
        &commit,      // IN OUT PULONG StackCommit,
        (LPVOID)start_addr, // IN PVOID StartAddress,
        (LPVOID)param,// IN PVOID StartParameter OPTIONAL,
        &hThread,     // OUT PHANDLE ThreadHandle,
        &id           // OUT PCLIENT_ID ClientID
      )))
      hThread = INVALID_HANDLE_VALUE;
  }
  */
  return hThread;
}

//Query module export table. Return function address if found.
//Similar to GetProcAddress
DWORD GetExportAddress(DWORD hModule,DWORD hash)
{
  IMAGE_DOS_HEADER *DosHdr;
  IMAGE_NT_HEADERS *NtHdr;
  IMAGE_EXPORT_DIRECTORY *ExtDir;
  UINT uj;
  char* pcExportAddr,*pcFuncPtr,*pcBuffer;
  DWORD dwReadAddr,dwFuncAddr,dwFuncName;
  WORD wOrd;
  DosHdr = (IMAGE_DOS_HEADER*)hModule;
  if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) {
    dwReadAddr=hModule+DosHdr->e_lfanew;
    NtHdr=(IMAGE_NT_HEADERS*)dwReadAddr;
    if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
      pcExportAddr = (char*)((DWORD)hModule+
          (DWORD)NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
      if (!pcExportAddr)
        return 0;
      ExtDir = (IMAGE_EXPORT_DIRECTORY*)pcExportAddr;
      pcExportAddr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNames);

      for (uj = 0; uj < ExtDir->NumberOfNames; uj++) {
        dwFuncName = *(DWORD *)pcExportAddr;
        pcBuffer = (char*)((DWORD)hModule+dwFuncName);
        if (GetHash(pcBuffer) == hash) {
          pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD)));
          wOrd = *(WORD*)pcFuncPtr;
          pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD)));
          dwFuncAddr = *(DWORD *)pcFuncPtr;
          return hModule+dwFuncAddr;
        }
        pcExportAddr += sizeof(DWORD);
      }
    }
  }
  return 0;
}

} // extern "C"

// EOF

/*__declspec(naked) void normal_asm()
{
  __asm
  {
    push ecx
    push edx
    mov fs:[0],esp
    push ebp
    call eax
_terminate:
    push eax
    push -2
    call dword ptr [NtTerminateThread]
  }
}*/

/*
__declspec(naked) void RegToStrAsm()
{
  __asm
  {
    mov edx, 8
_cvt_loop:
    mov eax, ecx
    and eax, 0xF
    cmp eax, 0xA
    jb _below_ten
    add al,7
_below_ten:
    add al,0x30
    stosw
    ror ecx,4
    dec edx
    jne _cvt_loop
    retn
  }
}
__declspec(naked) void except_asm()
{
  __asm
  {
    mov eax,[esp + 4]
    xor esi,esi
    mov ebp,[eax]
    mov ecx,[esp + 0xC]
    mov ebx,[ecx + 0xB8]
    sub esp,0x240
    lea edi,[esp + 0x40]
    mov eax,esp
    push esi
    push 0x1C
    push eax
    push esi
    push ebx
    push -1
    call dword ptr [NtQueryVirtualMemory]
    test eax,eax
    jne _terminate
    mov eax,esp
    push eax
    push 0x200
    push edi
    push 2
    push ebx
    push -1
    call dword ptr [NtQueryVirtualMemory]
    test eax,eax
    jne _terminate
    pop esi
    xadd edi,esi
    std
    mov al,0x5C
    repen scasw
    mov word ptr [edi + 2], 0x3A
    mov ecx,ebx
    sub ecx,[esp]
    call RegToStrAsm
    inc edi
    inc edi
    xchg esi,edi
    mov ecx,ebp
    call RegToStrAsm
    inc edi
    inc edi
    xor eax,eax
    mov [edi + 0x10], eax
    push 0
    push edi
    push esi
    push 0
    call dword ptr [MessageBoxW]
    or eax, -1
    jmp _terminate
  }
}

//Prompt for file name.
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition)
{
  OPENFILENAME ofn = {sizeof(ofn)};       // common dialog box structure
  WCHAR szFile[MAX_PATH];       // buffer for file name
  wcscpy(current_dir,L"ITH_export.txt");
  wcscpy(szFile,file_path);

  //szFile[0]=0;
  ofn.lpstrFile = szFile + 4;
  ofn.nMaxFile = MAX_PATH;
  ofn.lpstrFilter = L"Text\0*.txt";
  BOOL result;
  if (disposition==FILE_OPEN)
    result=GetOpenFileName(&ofn);
  else
    result=GetSaveFileName(&ofn);
  if (result)
  {
    LPWSTR s=szFile+wcslen(szFile) - 4;
    if (_wcsicmp(s,L".txt")!=0) wcscpy(s + 4,L".txt");
    return IthCreateFileFullPath(szFile,option,share,disposition);
  }
  else return INVALID_HANDLE_VALUE;
}
*/
