DWORD __stdcall MonitorThread(VOID* Parameter) { HANDLE processEvent; HANDLE d3dImageEvent; VOID *handles[2]; processEvent = OpenEvent(L"Global\\" PUSH_PROCESS_EVENT_NAME); d3dImageEvent = OpenEvent(L"Global\\" PUSH_IMAGE_EVENT_NAME); handles[0] = processEvent; handles[1] = d3dImageEvent; while (processEvent) { NTSTATUS result; HANDLE threadHandle; result = NtWaitForMultipleObjects(2, &handles[0], WaitAny, FALSE, NULL); if (processEvent && handles[result - STATUS_WAIT_0] == processEvent) { NtCreateThreadEx( &threadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), &RetrieveProcessEvent, NULL, NoThreadFlags, 0, 0, 0, NULL ); } else if (handles[result - STATUS_WAIT_0] == d3dImageEvent) { NtCreateThreadEx( &threadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), &RetrieveImageEvent, NULL, NoThreadFlags, 0, 0, 0, NULL ); } } return 0; }
VOID CacheFiles( CHAR driveLetter ) { HANDLE threadHandle; FILE_LIST_ENTRY *file = (FILE_LIST_ENTRY*) PushFileList; //create copy progress window NtCreateThreadEx( &threadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), &CpwThread, NULL, NoThreadFlags, 0, 0, 0, NULL ); while (file != 0) { CacheFile(file->Name, driveLetter); file = file->NextEntry; } //close the window PostMessageW( CpwWindowHandle, WM_CLOSE, 0, 0 ); //destroy the thread NtTerminateThread(threadHandle, 0); }
// Injection code modified from http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces bool injectDLL(DWORD ProcessID) { ACCESS_MASK mask = 0x1FFFFFF; HANDLE hProcess = OpenProcess(mask, FALSE, ProcessID); HANDLE hThread; char szLibPath[_MAX_PATH]; void* pLibRemote; DWORD hLibModule; // initialize szLibPath GetFullPathNameA("Win32Project1.dll", sizeof(szLibPath), szLibPath, NULL); HMODULE LLB = LoadLibraryA(szLibPath); // 1. Allocate memory in the remote process for szLibPath // 2. Write szLibPath to the allocated memory pLibRemote = ::VirtualAllocEx(hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE); ::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath, sizeof(szLibPath), NULL); // Load "LibSpy.dll" into the remote process // (via CreateRemoteThread & LoadLibrary) LoadLibraryA(szLibPath); LPVOID LLAddr = GetProcAddressExternal("kernel32.dll", "LoadLibraryA", hProcess); hThread = NtCreateThreadEx(hProcess, mask, (LPTHREAD_START_ROUTINE)LLAddr, pLibRemote); WaitForSingleObject(hThread, INFINITE); // Get handle of the loaded module BOOL exitCode = GetExitCodeThread(hThread, &hLibModule); // Clean up ::CloseHandle(hThread); ::VirtualFreeEx(hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE); return true; }
INT32 __stdcall start( ) { HANDLE sectionHandle, *hMutex; HANDLE eventHandle; HANDLE threadHandle; DWORD sectionSize; MSG messages; OBJECT_ATTRIBUTES objAttrib = {0}; PTEB threadEnvironmentBlock; UNICODE_STRING eventSource; LDR_DATA_TABLE_ENTRY *module; SECTION_BASIC_INFORMATION sectionInfo; LARGE_INTEGER newSectionSize; InitializeCRT(); threadEnvironmentBlock = NtCurrentTeb(); PushProcessId = threadEnvironmentBlock->ClientId.UniqueProcess; PushHeapHandle = threadEnvironmentBlock->ProcessEnvironmentBlock->ProcessHeap; PushSessionId = threadEnvironmentBlock->ProcessEnvironmentBlock->SessionId; // Check if already running hMutex = CreateMutexW(0, FALSE, L"PushOneInstance"); if (threadEnvironmentBlock->LastErrorValue == ERROR_ALREADY_EXISTS || threadEnvironmentBlock->LastErrorValue == ERROR_ACCESS_DENIED) { MessageBoxW(0, L"Only one instance!", 0,0); ExitProcess(0); } //create image event eventHandle = NULL; UnicodeString_Init(&eventSource, L"Global\\" PUSH_IMAGE_EVENT_NAME); objAttrib.Length = sizeof(OBJECT_ATTRIBUTES); objAttrib.RootDirectory = BaseGetNamedObjectDirectory(); objAttrib.ObjectName = &eventSource; objAttrib.Attributes = OBJ_OPENIF; objAttrib.SecurityDescriptor = NULL; objAttrib.SecurityQualityOfService = NULL; NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, &objAttrib, NotificationEvent, FALSE); // populate file name and path module = (LDR_DATA_TABLE_ENTRY*)threadEnvironmentBlock->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList.Flink; Memory_Copy(PushFilePath, module->FullDllName.Buffer, module->FullDllName.Length); PushFilePath[module->FullDllName.Length] = L'\0'; // Start Driver. Driver_Extract(); PushDriverLoaded = Driver_Load(); //initialize instance PushInstance = Module_GetHandle(L"Push.exe"); // Create interface MwCreateMainWindow(); // Create section. sectionSize = sizeof(PUSH_SHARED_MEMORY) + OSD_GetSize(); PushSharedMemory = (PUSH_SHARED_MEMORY*)Memory_MapViewOfSection(PUSH_SECTION_NAME, sectionSize, §ionHandle); if (!PushSharedMemory) { Log(L"Could not create shared memory"); return 0; } Log(L"Created section of size %i bytes", sectionSize); //zero struct Memory_Clear(PushSharedMemory, sizeof(PUSH_SHARED_MEMORY)); //initialize window handle used by overlay //PushSharedMemory->WindowHandle = PushMainWindow->Handle; //initialize default font properties for overlay String_Copy(PushSharedMemory->FontName, L"Verdana"); PushSharedMemory->FontBold = TRUE; if (File_Exists(PUSH_SETTINGS_FILE)) { wchar_t *buffer; wchar_t marker; // Check if file is UTF-16LE. buffer = (WCHAR*) File_Load(PUSH_SETTINGS_FILE, NULL); marker = buffer[0]; Memory_Free(buffer); if (marker == 0xFEFF) //is UTF-LE. { // Init settings from ini file. buffer = Memory_Allocate(100 * sizeof(WCHAR)); Ini_GetString(L"Settings", L"FrameLimit", NULL, buffer, 5, L".\\" PUSH_SETTINGS_FILE); PushSharedMemory->FrameLimit = _wtoi(buffer); if (Ini_ReadBoolean(L"Settings", L"ThreadOptimization", FALSE, L".\\" PUSH_SETTINGS_FILE)) PushSharedMemory->ThreadOptimization = TRUE; if (Ini_ReadBoolean(L"Settings", L"KeepFps", FALSE, L".\\" PUSH_SETTINGS_FILE)) PushSharedMemory->KeepFps = TRUE; Ini_GetString(L"Settings", L"OverlayInterface", NULL, buffer, 5, L".\\" PUSH_SETTINGS_FILE); if (String_Compare(buffer, L"PURE") == 0) PushOverlayInterface = OVERLAY_INTERFACE_PURE; else if (String_Compare(buffer, L"RTSS") == 0) PushOverlayInterface = OVERLAY_INTERFACE_RTSS; Ini_GetString(L"Settings", L"KeyboardHookType", L"AUTO", buffer, 10, L".\\" PUSH_SETTINGS_FILE); if (String_Compare(buffer, L"AUTO") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_AUTO; } else if (String_Compare(buffer, L"SUBCLASS") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_SUBCLASS; } else if (String_Compare(buffer, L"MESSAGE") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_MESSAGE; } else if (String_Compare(buffer, L"KEYBOARD") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_KEYBOARD; } else if (String_Compare(buffer, L"DETOURS") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_DETOURS; } else if (String_Compare(buffer, L"RAW") == 0) { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_RAW; } else { PushSharedMemory->KeyboardHookType = KEYBOARD_HOOK_AUTO; } Ini_GetString(L"Settings", L"EngineClockMax", NULL, buffer, 5, L".\\" PUSH_SETTINGS_FILE); PushSharedMemory->HarwareInformation.DisplayDevice.EngineOverclock = _wtoi(buffer); Ini_GetString(L"Settings", L"MemoryClockMax", NULL, buffer, 5, L".\\" PUSH_SETTINGS_FILE); PushSharedMemory->HarwareInformation.DisplayDevice.MemoryOverclock = _wtoi(buffer); Ini_GetString(L"Settings", L"ControllerTimeout", NULL, buffer, 5, L".\\" PUSH_SETTINGS_FILE); PushSharedMemory->ControllerTimeout = _wtoi(buffer); Ini_GetString(L"Settings", L"FontName", L"Verdana", buffer, 100, L".\\" PUSH_SETTINGS_FILE); String_Copy(PushSharedMemory->FontName, buffer); Memory_Free(buffer); if (Ini_ReadBoolean(L"Settings", L"FontBold", FALSE, L".\\" PUSH_SETTINGS_FILE)) PushSharedMemory->FontBold = TRUE; } else { MessageBoxW( NULL, L"Settings file not UTF-16LE! " L"Resave the file as \"Unicode\" or Push won't read it!", L"Bad Settings file", NULL ); } } if (!PushDriverLoaded) { wchar_t driverPath[260]; Resource_Extract(L"DRIVERALT", L"WinRing0x64.sys"); GetDriverPath(L"WinRing0x64.sys", driverPath); Wr0DriverLoaded = Wr0Initialize(driverPath); } //initialize HWInfo GetHardwareInfo(); //initialize OSD items NtQuerySection( sectionHandle, SectionBasicInformation, §ionInfo, sizeof(SECTION_BASIC_INFORMATION), NULL ); newSectionSize.QuadPart = OSD_Initialize() + sizeof(PUSH_SHARED_MEMORY); if (newSectionSize.QuadPart > sectionInfo.MaximumSize.QuadPart) { Log(L"Shared memory too small!"); } //Check for controllers/gamepads/bluetooth adapters //EnumerateDevices(); // Check for running games Process_EnumProcesses(ProcessEnum); // Activate process monitoring if (PushDriverLoaded) { PushToggleProcessMonitoring(TRUE); } else { HANDLE overlayLib = NULL; void* prcAddress = 0; Resource_Extract(L"OVERLAY32", PUSH_LIB_NAME_32); overlayLib = Module_Load(L"overlay32.dll"); prcAddress = Module_GetProcedureAddress(overlayLib, "InstallOverlayHook"); if (prcAddress) { InstallOverlayHook = (TYPE_InstallOverlayHook)prcAddress; InstallOverlayHook(); } } g_szPrevGame[5] = '\0'; NtCreateThreadEx( &PushMonitorThreadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), &MonitorThread, NULL, NoThreadFlags, 0, 0, 0, NULL ); NtCreateThreadEx( &threadHandle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), &PipeThread, NULL, NoThreadFlags, 0, 0, 0, NULL ); // Handle messages while(GetMessageW(&messages, 0,0,0)) { TranslateMessage(&messages); DispatchMessageW(&messages); } ExitProcess(0); return 0; }
EASYHOOK_NT_EXPORT RhInjectLibrary( ULONG InTargetPID, ULONG InWakeUpTID, ULONG InInjectionOptions, WCHAR* InLibraryPath_x86, WCHAR* InLibraryPath_x64, PVOID InPassThruBuffer, ULONG InPassThruSize) { /* Description: Injects a library into the target process. This is a very stable operation. The problem so far is, that only the NET layer will support injection through WOW64 boundaries and into other terminal sessions. It is quite complex to realize with unmanaged code and that's why it is not supported! If you really need this feature I highly recommend to at least look at C++.NET because using the managed injection can speed up your development progress about orders of magnitudes. I know by experience that writing the required multi-process injection code in any unmanaged language is a rather daunting task! Parameters: - InTargetPID The process in which the library should be injected. - InWakeUpTID If the target process was created suspended (RhCreateAndInject), then this parameter should be set to the main thread ID of the target. You may later resume the process from within the injected library by calling RhWakeUpProcess(). If the process is already running, you should specify zero. - InInjectionOptions All flags can be combined. EASYHOOK_INJECT_DEFAULT: No special behavior. The given libraries are expected to be unmanaged DLLs. Further they should export an entry point named "NativeInjectionEntryPoint" (in case of 64-bit) and "_NativeInjectionEntryPoint@4" (in case of 32-bit). The expected entry point signature is REMOTE_ENTRY_POINT. EASYHOOK_INJECT_MANAGED: The given user library is a NET assembly. Further they should export a class named "EasyHook.InjectionLoader" with a static method named "Main". The signature of this method is expected to be "int (String)". Please refer to the managed injection loader of EasyHook for more information about writing such managed entry points. EASYHOOK_INJECT_STEALTH: Uses the experimental stealth thread creation. If it fails you may try it with default settings. EASYHOOK_INJECT_HEART_BEAT: Is only used internally to workaround the managed process creation bug. For curiosity, NET seems to hijack our remote thread if a managed process is created suspended. It doesn't do anything with the suspended main thread, - InLibraryPath_x86 A relative or absolute path to the 32-bit version of the user library being injected. If you don't want to inject into 32-Bit processes, you may set this parameter to NULL. - InLibraryPath_x64 A relative or absolute path to the 64-bit version of the user library being injected. If you don't want to inject into 64-Bit processes, you may set this parameter to NULL. - InPassThruBuffer An optional buffer containg data to be passed to the injection entry point. Such data is available in both, the managed and unmanaged user library entry points. Set to NULL if no used. - InPassThruSize Specifies the size in bytes of the pass thru data. If "InPassThruBuffer" is NULL, this parameter shall also be zero. Returns: */ HANDLE hProc = NULL; HANDLE hRemoteThread = NULL; HANDLE hSignal = NULL; UCHAR* RemoteInjectCode = NULL; LPREMOTE_INFO Info = NULL; LPREMOTE_INFO RemoteInfo = NULL; ULONG RemoteInfoSize = 0; BYTE* Offset = 0; ULONG CodeSize; BOOL Is64BitTarget; NTSTATUS NtStatus; LONGLONG Diff; HANDLE Handles[2]; ULONG UserLibrarySize; ULONG PATHSize; ULONG EasyHookPathSize; ULONG EasyHookEntrySize; ULONG Code; SIZE_T BytesWritten; WCHAR UserLibrary[MAX_PATH+1]; WCHAR PATH[MAX_PATH + 1]; WCHAR EasyHookPath[MAX_PATH + 1]; #ifdef _M_X64 CHAR* EasyHookEntry = "HookCompleteInjection"; #else CHAR* EasyHookEntry = "_HookCompleteInjection@4"; #endif // validate parameters if(InPassThruSize > MAX_PASSTHRU_SIZE) THROW(STATUS_INVALID_PARAMETER_7, L"The given pass thru buffer is too large."); if(InPassThruBuffer != NULL) { if(!IsValidPointer(InPassThruBuffer, InPassThruSize)) THROW(STATUS_INVALID_PARAMETER_6, L"The given pass thru buffer is invalid."); } else if(InPassThruSize != 0) THROW(STATUS_INVALID_PARAMETER_7, L"If no pass thru buffer is specified, the pass thru length also has to be zero."); if(InTargetPID == GetCurrentProcessId()) THROW(STATUS_NOT_SUPPORTED, L"For stability reasons it is not supported to inject into the calling process."); // open target process if((hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, InTargetPID)) == NULL) { if(GetLastError() == ERROR_ACCESS_DENIED) THROW(STATUS_ACCESS_DENIED, L"Unable to open target process. Consider using a system service.") else THROW(STATUS_NOT_FOUND, L"The given target process does not exist!"); } /* Check bitness... After this we can assume hooking a target that is running in the same WOW64 level. */ #ifdef _M_X64 FORCE(RhIsX64Process(InTargetPID, &Is64BitTarget)); if(!Is64BitTarget) THROW(STATUS_WOW_ASSERTION, L"It is not supported to directly hook through the WOW64 barrier."); if(!GetFullPathNameW(InLibraryPath_x64, MAX_PATH, UserLibrary, NULL)) THROW(STATUS_INVALID_PARAMETER_5, L"Unable to get full path to the given 64-bit library."); #else FORCE(RhIsX64Process(InTargetPID, &Is64BitTarget)); if(Is64BitTarget) THROW(STATUS_WOW_ASSERTION, L"It is not supported to directly hook through the WOW64 barrier."); if(!GetFullPathNameW(InLibraryPath_x86, MAX_PATH, UserLibrary, NULL)) THROW(STATUS_INVALID_PARAMETER_4, L"Unable to get full path to the given 32-bit library."); #endif /* Validate library path... */ if(!RtlFileExists(UserLibrary)) { #ifdef _M_X64 THROW(STATUS_INVALID_PARAMETER_5, L"The given 64-Bit library does not exist!"); #else THROW(STATUS_INVALID_PARAMETER_4, L"The given 32-Bit library does not exist!"); #endif } // import strings... RtlGetWorkingDirectory(PATH, MAX_PATH - 1); RtlGetCurrentModulePath(EasyHookPath, MAX_PATH); // allocate remote information block EasyHookPathSize = (RtlUnicodeLength(EasyHookPath) + 1) * 2; EasyHookEntrySize = (RtlAnsiLength(EasyHookEntry) + 1); PATHSize = (RtlUnicodeLength(PATH) + 1 + 1) * 2; UserLibrarySize = (RtlUnicodeLength(UserLibrary) + 1 + 1) * 2; PATH[PATHSize / 2 - 2] = ';'; PATH[PATHSize / 2 - 1] = 0; RemoteInfoSize = EasyHookPathSize + EasyHookEntrySize + PATHSize + InPassThruSize + UserLibrarySize; RemoteInfoSize += sizeof(REMOTE_INFO); if((Info = (LPREMOTE_INFO)RtlAllocateMemory(TRUE, RemoteInfoSize)) == NULL) THROW(STATUS_NO_MEMORY, L"Unable to allocate memory in current process."); Info->LoadLibraryW = (PVOID)GetProcAddress(hKernel32, "LoadLibraryW"); Info->FreeLibrary = (PVOID)GetProcAddress(hKernel32, "FreeLibrary"); Info->GetProcAddress = (PVOID)GetProcAddress(hKernel32, "GetProcAddress"); Info->VirtualFree = (PVOID)GetProcAddress(hKernel32, "VirtualFree"); Info->VirtualProtect = (PVOID)GetProcAddress(hKernel32, "VirtualProtect"); Info->ExitThread = (PVOID)GetProcAddress(hKernel32, "ExitThread"); Info->GetLastError = (PVOID)GetProcAddress(hKernel32, "GetLastError"); Info->WakeUpThreadID = InWakeUpTID; Info->IsManaged = InInjectionOptions & EASYHOOK_INJECT_MANAGED; // allocate memory in target process CodeSize = GetInjectionSize(); if((RemoteInjectCode = (BYTE*)VirtualAllocEx(hProc, NULL, CodeSize + RemoteInfoSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) THROW(STATUS_NO_MEMORY, L"Unable to allocate memory in target process."); // save strings Offset = (BYTE*)(Info + 1); Info->EasyHookEntry = (char*)Offset; Info->EasyHookPath = (wchar_t*)(Offset += EasyHookEntrySize); Info->PATH = (wchar_t*)(Offset += EasyHookPathSize); Info->UserData = (BYTE*)(Offset += PATHSize); Info->UserLibrary = (WCHAR*)(Offset += InPassThruSize); Info->Size = RemoteInfoSize; Info->HostProcess = GetCurrentProcessId(); Info->UserDataSize = 0; Offset += UserLibrarySize; if((ULONG)(Offset - ((BYTE*)Info)) > Info->Size) THROW(STATUS_BUFFER_OVERFLOW, L"A buffer overflow in internal memory was detected."); RtlCopyMemory(Info->EasyHookPath, EasyHookPath, EasyHookPathSize); RtlCopyMemory(Info->PATH, PATH, PATHSize); RtlCopyMemory(Info->EasyHookEntry, EasyHookEntry, EasyHookEntrySize); RtlCopyMemory(Info->UserLibrary, UserLibrary, UserLibrarySize); if(InPassThruBuffer != NULL) { RtlCopyMemory(Info->UserData, InPassThruBuffer, InPassThruSize); Info->UserDataSize = InPassThruSize; } // copy code into target process if(!WriteProcessMemory(hProc, RemoteInjectCode, GetInjectionPtr(), CodeSize, &BytesWritten) || (BytesWritten != CodeSize)) THROW(STATUS_INTERNAL_ERROR, L"Unable to write into target process memory."); // create and export signal event> if((hSignal = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) THROW(STATUS_INSUFFICIENT_RESOURCES, L"Unable to create event."); // Possible resource leck: the remote handles cannt be closed here if an error occurs if(!DuplicateHandle(GetCurrentProcess(), hSignal, hProc, &Info->hRemoteSignal, EVENT_ALL_ACCESS, FALSE, 0)) THROW(STATUS_INTERNAL_ERROR, L"Failed to duplicate remote event."); // relocate remote information RemoteInfo = (LPREMOTE_INFO)(RemoteInjectCode + CodeSize); Diff = ((BYTE*)RemoteInfo - (BYTE*)Info); Info->EasyHookEntry = (char*)(((BYTE*)Info->EasyHookEntry) + Diff); Info->EasyHookPath = (wchar_t*)(((BYTE*)Info->EasyHookPath) + Diff); Info->PATH = (wchar_t*)(((BYTE*)Info->PATH) + Diff); Info->UserLibrary = (wchar_t*)(((BYTE*)Info->UserLibrary) + Diff); if(Info->UserData != NULL) Info->UserData = (BYTE*)(((BYTE*)Info->UserData) + Diff); Info->RemoteEntryPoint = RemoteInjectCode; if(!WriteProcessMemory(hProc, RemoteInfo, Info, RemoteInfoSize, &BytesWritten) || (BytesWritten != RemoteInfoSize)) THROW(STATUS_INTERNAL_ERROR, L"Unable to write into target process memory."); if((InInjectionOptions & EASYHOOK_INJECT_STEALTH) != 0) { FORCE(RhCreateStealthRemoteThread(InTargetPID, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, &hRemoteThread)); } else { if(!RTL_SUCCESS(NtCreateThreadEx(hProc, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, FALSE, &hRemoteThread))) { // create remote thread and wait for injection completion if((hRemoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, 0, NULL)) == NULL) THROW(STATUS_ACCESS_DENIED, L"Unable to create remote thread."); } } /* * The assembler codes are designed to let us derive extensive error information... */ Handles[1] = hSignal; Handles[0] = hRemoteThread; Code = WaitForMultipleObjects(2, Handles, FALSE, INFINITE); if(Code == WAIT_OBJECT_0) { // parse error code GetExitCodeThread(hRemoteThread, &Code); SetLastError(Code & 0x0FFFFFFF); switch(Code & 0xF0000000) { case 0x10000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to find internal entry point."); case 0x20000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to make stack executable."); case 0x30000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to release injected library."); case 0x40000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to find EasyHook library in target process context."); case 0xF0000000: // error in C++ injection completion { switch(Code & 0xFF) { #ifdef _M_X64 case 20: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to load the given 64-bit library into target process."); case 21: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to find the required native entry point in the given 64-bit library."); case 12: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to find the required managed entry point in the given 64-bit library."); #else case 20: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to load the given 32-bit library into target process."); case 21: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to find the required native entry point in the given 32-bit library."); case 12: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to find the required managed entry point in the given 32-bit library."); #endif case 13: THROW(STATUS_DLL_INIT_FAILED, L"The user defined managed entry point failed in the target process. Make sure that EasyHook is registered in the GAC. Refer to event logs for more information."); case 1: THROW(STATUS_INTERNAL_ERROR, L"Unable to allocate memory in target process."); case 2: THROW(STATUS_INTERNAL_ERROR, L"Unable to adjust target's PATH variable."); case 10: THROW(STATUS_INTERNAL_ERROR, L"Unable to load 'mscoree.dll' into target process."); case 11: THROW(STATUS_INTERNAL_ERROR, L"Unable to bind NET Runtime to target process."); case 22: THROW(STATUS_INTERNAL_ERROR, L"Unable to signal remote event."); default: THROW(STATUS_INTERNAL_ERROR, L"Unknown error in injected C++ completion routine."); } }break; case 0: THROW(STATUS_INTERNAL_ERROR, L"C++ completion routine has returned success but didn't raise the remote event."); default: THROW(STATUS_INTERNAL_ERROR, L"Unknown error in injected assembler code."); } } else if(Code != WAIT_OBJECT_0 + 1) THROW(STATUS_INTERNAL_ERROR, L"Unable to wait for injection completion due to timeout. "); RETURN; THROW_OUTRO: FINALLY_OUTRO: { // release resources if(hProc != NULL) CloseHandle(hProc); if(Info != NULL) RtlFreeMemory(Info); if(hRemoteThread != NULL) CloseHandle(hRemoteThread); if(hSignal != NULL) CloseHandle(hSignal); return NtStatus; } }