Textractor_test/vnr/ithsys/ithsys.cc

1560 lines
46 KiB
C++
Raw Normal View History

// 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;
::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;
// 일본어 코드 페이지 C_932 이 SysWow64에 없음
// 64bit에서 (32bit) 로 실행할 경우 SysWow64로 리다이렉트 되는 것 해제
PVOID OldValue;
Wow64DisableWow64FsRedirection(&OldValue);
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;
}
*/