LONG LhSetACL( HOOK_ACL* InAcl, BOOL InIsExclusive, ULONG* InThreadIdList, ULONG InThreadCount) { /* Description: This method is used internally to provide a generic interface to either the global or local hook ACLs. Parameters: - InAcl NULL if you want to set the global ACL. Any LOCAL_HOOK_INFO::LocalACL to set the hook specific ACL. - InIsExclusive TRUE if all listed thread shall be excluded from interception, FALSE otherwise - InThreadIdList An array of thread IDs. If you specific zero for an entry in this array, it will be automatically replaced with the calling thread ID. - InThreadCount The count of entries listed in the thread ID list. This value must not exceed MAX_ACE_COUNT! */ ULONG Index; ASSERT(IsValidPointer(InAcl, sizeof(HOOK_ACL)),L"acl.c - IsValidPointer(InAcl, sizeof(HOOK_ACL))"); if(InThreadCount > MAX_ACE_COUNT) return STATUS_INVALID_PARAMETER_2; if(!IsValidPointer(InThreadIdList, InThreadCount * sizeof(ULONG))) return STATUS_INVALID_PARAMETER_1; for(Index = 0; Index < InThreadCount; Index++) { if(InThreadIdList[Index] == 0) InThreadIdList[Index] = GetCurrentThreadId(); } // set ACL... InAcl->IsExclusive = InIsExclusive; InAcl->Count = InThreadCount; RtlCopyMemory(InAcl->Entries, InThreadIdList, InThreadCount * sizeof(ULONG)); return STATUS_SUCCESS; }
EASYHOOK_NT_EXPORT LhBarrierGetReturnAddress(PVOID* OutValue) { /* Description: Is expected to be called inside a hook handler. Otherwise it will fail with STATUS_NOT_SUPPORTED. The method retrieves the return address of the hook handler. This is usually the instruction behind the "CALL" which invoked the hook. The calling module determination is based on this method. */ NTSTATUS NtStatus; LPTHREAD_RUNTIME_INFO Runtime; if(!IsValidPointer(OutValue, sizeof(PVOID))) THROW(STATUS_INVALID_PARAMETER, L"Invalid result storage specified."); if(!TlsGetCurrentValue(&Unit.TLS, &Runtime)) THROW(STATUS_NOT_SUPPORTED, L"The caller is not inside a hook handler."); if(Runtime->Current != NULL) *OutValue = Runtime->Current->RetAddress; else THROW(STATUS_NOT_SUPPORTED, L"The caller is not inside a hook handler.NULL"); RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT LhBarrierEndStackTrace(PVOID InBackup) { /* Description: Is expected to be called inside a hook handler. Otherwise it will fail with STATUS_NOT_SUPPORTED. You have to pass the backup pointer obtained with LhBarrierBeginStackTrace(). */ NTSTATUS NtStatus; PVOID* AddrOfRetAddr; if(!IsValidPointer(InBackup, 1)) THROW(STATUS_INVALID_PARAMETER, L"The given stack backup pointer is invalid."); FORCE(LhBarrierGetAddressOfReturnAddress(&AddrOfRetAddr)); *AddrOfRetAddr = InBackup; RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT LhBarrierGetCallback(PVOID* OutValue) { /* Description: Is expected to be called inside a hook handler. Otherwise it will fail with STATUS_NOT_SUPPORTED. The method retrieves the callback initially passed to the related LhInstallHook() call. */ NTSTATUS NtStatus; LPTHREAD_RUNTIME_INFO Runtime; if(!IsValidPointer(OutValue, sizeof(PVOID))) THROW(STATUS_INVALID_PARAMETER, L"Invalid result storage specified."); if(!TlsGetCurrentValue(&Unit.TLS, &Runtime)) THROW(STATUS_NOT_SUPPORTED, L"The caller is not inside a hook handler."); if(Runtime->Current != NULL) *OutValue = Runtime->Callback; else THROW(STATUS_NOT_SUPPORTED, L"The caller is not inside a hook handler. NULL"); RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
void MultiMBC1MemoryRule::PerformWrite(u16 address, u8 value) { switch (address & 0xE000) { case 0x0000: { bool previous = m_bRamEnabled; m_bRamEnabled = ((value & 0x0F) == 0x0A); if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) { (*m_pRamChangedCallback)(); } break; } case 0x2000: { if (m_iMode == 0) { m_iFinalROMBank = (m_iCurrentROMBank & 0x1F) ? m_iCurrentROMBank : (m_iCurrentROMBank | 1); m_iFinalROMBank &= (m_pCartridge->GetROMBankCount() - 1); } else { int rombank = ((m_iCurrentROMBank >> 1) & 0x30) | (m_iCurrentROMBank & 0xF); m_iFinalROMBank = (rombank & 0x1F) ? rombank : (rombank | 1); } break; } case 0x4000: { m_iCurrentROMBank = ((value << 5) & 0x60) | (m_iCurrentROMBank & 0x1F); SetRomBank(); break; } case 0x6000: { m_iMode = value & 0x01; SetRomBank(); break; } case 0xA000: { if (m_bRamEnabled) { m_pMemory->Load(address, value); } else { Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); } break; } default: { m_pMemory->Load(address, value); break; } } }
void PhysicsManager::SetPhysicsCallback(PhysicsCallbackGeneric* pCallback) { m_pPhysicsCallback = pCallback; if (IsValidPointer(m_pDynamicsWorld)) { m_pDynamicsWorld->setInternalTickCallback(&PhysicsManager::__PhysicsCallback); } }
EASYHOOK_NT_EXPORT DbgGetProcessIdByHandle( HANDLE InProcessHandle, ULONG* OutProcessId) { /* Description: Translates the given process handle back to its process ID if possible. Parameters: - InProcessHandle A process handle with PROCESS_QUERY_INFORMATION access. - OutProcessId Receives the process ID. */ PROCESS_BASIC_INFORMATION ProcInfo; NTSTATUS NtStatus; if(!IsValidPointer(OutProcessId, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_2, L"The given process ID storage is invalid."); if(ZwQueryInformationProcess != NULL) { // use deprecated API FORCE(ZwQueryInformationProcess( InProcessHandle, 0 /* ProcessBasicInformation */, &ProcInfo, sizeof(ProcInfo), NULL)); *OutProcessId = (ULONG)ProcInfo.UniqueProcessId; } else { ASSERT(ZwGetProcessId != NULL); // use new support API if((*OutProcessId = ZwGetProcessId(InProcessHandle)) == 0) THROW(STATUS_INVALID_PARAMETER_1, L"Invalid process handle or improper privileges."); } RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT LhGetHookBypassAddress(TRACED_HOOK_HANDLE InHook, PVOID** OutAddress) { /* Description: Retrieves the address to bypass the hook. Using the returned value to call the original function bypasses all thread safety measures and must be used with care. This function should be called each time the address is required to ensure the hook and associated memory is still valid at the time of use. CAUTION: This must be used with extreme caution. If the hook is uninstalled and pending hooks removed, the address returned by this function will no longer point to valid memory and attempting to use the address will result in unexpected behaviour, most likely crashing the process. Parameters: - InHook The hook to retrieve the relocated entry point for. - OutAddress Upon successfully retrieving the hook details this will contain the address of the relocated function entry point. This address can be used to call the original function from outside of a hook while still bypassing the hook. Returns: STATUS_SUCCESS - OutAddress will contain the result STATUS_INVALID_PARAMETER_1 - the hook is invalid STATUS_INVALID_PARAMETER_3 - the target pointer is invalid */ NTSTATUS NtStatus; PLOCAL_HOOK_INFO Handle; if (!LhIsValidHandle(InHook, &Handle)) THROW(STATUS_INVALID_PARAMETER_1, L"The given hook handle is invalid or already disposed."); if (!IsValidPointer(OutAddress, sizeof(PVOID*))) THROW(STATUS_INVALID_PARAMETER_3, L"Invalid pointer for result storage."); *OutAddress = (PVOID*)Handle->OldProc; RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT DbgGetThreadIdByHandle( HANDLE InThreadHandle, ULONG* OutThreadId) { /* Description: Translates the given thread handle back to its thread ID if possible. Parameters: - InThreadHandle A thread handle with THREAD_QUERY_INFORMATION access. - OutThreadId Receives the thread ID. */ THREAD_BASIC_INFORMATION ThreadInfo; NTSTATUS NtStatus; if(!IsValidPointer(OutThreadId, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_2, L"Invalid TID storage specified."); if(ZwQueryInformationThread != NULL) { // use deprecated API FORCE(ZwQueryInformationThread( InThreadHandle, 0 /* ThreadBasicInformation */, &ThreadInfo, sizeof(ThreadInfo), NULL)); *OutThreadId = ThreadInfo.ClientId.UniqueThread; } else { ASSERT(ZwGetThreadId != NULL); // use new support API if((*OutThreadId = ZwGetThreadId(InThreadHandle)) == 0) THROW(STATUS_INVALID_PARAMETER_1, L"Invalid thread handler or improper privileges."); } RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT RhGetProcessToken( ULONG InProcessId, HANDLE* OutToken) { /* Description: This method is intended for the managed layer and has no special advantage in an unmanaged environment! Parameters: - InProcessId The target process shall be accessible with PROCESS_QUERY_INFORMATION. - OutToken The identity token for the session the process was created in. */ HANDLE hProc = NULL; NTSTATUS NtStatus; if(!IsValidPointer(OutToken, sizeof(HANDLE))) THROW(STATUS_INVALID_PARAMETER_2, L"The given token storage is invalid."); if((hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, InProcessId)) == NULL) { if(GetLastError() == ERROR_ACCESS_DENIED) THROW(STATUS_ACCESS_DENIED, L"The given process is not accessible.") else THROW(STATUS_NOT_FOUND, L"The given process does not exist."); } if(!OpenProcessToken(hProc, TOKEN_READ, OutToken)) THROW(STATUS_INTERNAL_ERROR, L"Unable to query process token."); RETURN(STATUS_SUCCESS); THROW_OUTRO: FINALLY_OUTRO: { if(hProc != NULL) CloseHandle(hProc); return NtStatus; } }
EASYHOOK_NT_EXPORT LhIsProcessIntercepted( TRACED_HOOK_HANDLE InHook, ULONG InProcessID, BOOL* OutResult) #endif { /* Description: This method will negotiate whether a given thread passes the ACLs and would invoke the related hook handler. Refer to the source code of Is[Thread/Process]Intercepted() for more information about the implementation. */ NTSTATUS NtStatus; PLOCAL_HOOK_INFO Handle; if(!LhIsValidHandle(InHook, &Handle)) THROW(STATUS_INVALID_PARAMETER_1, L"The given hook handle is invalid or already disposed."); if(!IsValidPointer(OutResult, sizeof(BOOL))) THROW(STATUS_INVALID_PARAMETER_3, L"Invalid pointer for result storage."); #ifndef DRIVER *OutResult = IsThreadIntercepted(&Handle->LocalACL, InThreadID); #else *OutResult = IsProcessIntercepted(&Handle->LocalACL, InProcessID); #endif RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT LhBarrierPointerToModule( PVOID InPointer, MODULE_INFORMATION* OutModule) { /* Description: Translates the given pointer (likely a method) to its owning module if possible. Parameters: - InPointer A method pointer to be translated. - OutModule Receives the owner of a given method. Returns: STATUS_NOT_FOUND No matching module could be found. */ UCHAR* Pointer = (UCHAR*)InPointer; NTSTATUS NtStatus; BOOL CanTryAgain = TRUE; MODULE_INFORMATION* List; if(!IsValidPointer(OutModule, sizeof(MODULE_INFORMATION))) THROW(STATUS_INVALID_PARAMETER_2, L"The given module storage is invalid."); LABEL_TRY_AGAIN: RtlAcquireLock(&GlobalHookLock); { List = LhModuleArray; // walk through process modules while(List != NULL) { if((Pointer >= List->BaseAddress) && (Pointer <= List->BaseAddress + List->ImageSize)) { *OutModule = *List; RtlReleaseLock(&GlobalHookLock); RETURN; } List = List->Next; } } RtlReleaseLock(&GlobalHookLock); if((InPointer == NULL) || (InPointer == (PVOID)~0)) { // this pointer does not belong to any module... } else { // unable to find calling module... FORCE(LhUpdateModuleInformation()); if(CanTryAgain) { CanTryAgain = FALSE; goto LABEL_TRY_AGAIN; } } THROW(STATUS_NOT_FOUND, L"Unable to determine module."); THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
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; } }
void MBC5MemoryRule::PerformWrite(u16 address, u8 value) { switch (address & 0xE000) { case 0x0000: { if (m_pCartridge->GetRAMSize() > 0) { bool previous = m_bRamEnabled; m_bRamEnabled = ((value & 0x0F) == 0x0A); if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) { (*m_pRamChangedCallback)(); } } break; } case 0x2000: { if (address < 0x3000) { m_iCurrentROMBank = value | (m_iCurrentROMBankHi << 8); } else { m_iCurrentROMBankHi = value & 0x01; m_iCurrentROMBank = (m_iCurrentROMBank & 0xFF) | (m_iCurrentROMBankHi << 8); } m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; break; } case 0x4000: { m_iCurrentRAMBank = value & 0x0F; m_iCurrentRAMBank &= (m_pCartridge->GetRAMBankCount() - 1); m_CurrentRAMAddress = m_iCurrentRAMBank * 0x2000; break; } case 0x6000: { Log("--> ** Attempting to write on invalid address %X %X", address, value); break; } case 0xA000: { if (m_bRamEnabled) { m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress] = value; } else { Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); } break; } default: { m_pMemory->Load(address, value); break; } } }
EASYHOOK_NT_EXPORT RtlCreateSuspendedProcess( WCHAR* InEXEPath, WCHAR* InCommandLine, ULONG InCustomFlags, ULONG* OutProcessId, ULONG* OutThreadId) { /* Description: Creates a suspended process with the given parameters. This is only intended for the managed layer. Parameters: - InEXEPath A relative or absolute path to the EXE file of the process being created. - InCommandLine Optional command line parameters passed to the newly created process. - InCustomFlags Additional process creation flags. - OutProcessId Receives the PID of the newly created process. - OutThreadId Receives the initial TID of the newly created process. */ STARTUPINFO StartInfo; PROCESS_INFORMATION ProcessInfo; WCHAR FullExePath[MAX_PATH + 1]; WCHAR FullCommandLine[MAX_PATH + 1]; WCHAR CurrentDir[MAX_PATH + 1]; WCHAR* FilePart; NTSTATUS NtStatus; // must be executed before any THROW or RETURN! RtlZeroMemory(&StartInfo, sizeof(StartInfo)); RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); if(!IsValidPointer(OutProcessId, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_3, L"The given process ID storage is invalid."); if(!IsValidPointer(OutThreadId, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_4, L"The given thread ID storage is invalid."); // parse path if(!RtlFileExists(InEXEPath)) THROW(STATUS_INVALID_PARAMETER_1, L"The given process file does not exist."); if(GetFullPathName(InEXEPath, MAX_PATH, CurrentDir, &FilePart) > MAX_PATH) THROW(STATUS_INVALID_PARAMETER_1, L"Full path information exceeds MAX_PATH characters."); // compute current directory... RtlCopyMemory(FullExePath, CurrentDir, sizeof(FullExePath)); swprintf_s(FullCommandLine, MAX_PATH, L"\"%s\" \"%s\"", FullExePath, InCommandLine); *FilePart = 0; // create suspended process StartInfo.cb = sizeof(StartInfo); StartInfo.wShowWindow = TRUE; if(!CreateProcessW( FullExePath, FullCommandLine, NULL, NULL, FALSE, InCustomFlags | CREATE_SUSPENDED, NULL, CurrentDir, &StartInfo, &ProcessInfo)) THROW(STATUS_INVALID_PARAMETER, L"Unable to start process; please check the given parameters."); *OutProcessId = ProcessInfo.dwProcessId; *OutThreadId = ProcessInfo.dwThreadId; RETURN; THROW_OUTRO: FINALLY_OUTRO: { if(ProcessInfo.hProcess != NULL) CloseHandle(ProcessInfo.hProcess); if(ProcessInfo.hThread != NULL) CloseHandle(ProcessInfo.hThread); return NtStatus; } }
EASYHOOK_NT_EXPORT RhCreateAndInject( WCHAR* InEXEPath, WCHAR* InCommandLine, ULONG InProcessCreationFlags, ULONG InInjectionOptions, WCHAR* InLibraryPath_x86, WCHAR* InLibraryPath_x64, PVOID InPassThruBuffer, ULONG InPassThruSize, ULONG* OutProcessId) { /* Description: Creates a suspended process and immediately injects the user library. This is done BEFORE any of the usual process initialization is called. When the injection is made, NO thread has actually executed any instruction so far... It is just like your library entry point is the first thing executed in such a process and you can allow the original execution to take place by calling RhWakeUpProcess() in the injected library. But even that is no requirement for the process to work... Parameters: - InEXEPath A relative or absolute path to the EXE file of the process being created. - InCommandLine Optional command line parameters passed to the newly created process. - InProcessCreationFlags Custom process creation flags. - 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. - 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. - OutProcessId Receives the PID of the newly created process. */ ULONG ProcessId = 0; ULONG ThreadId = 0; HANDLE hProcess = NULL; NTSTATUS NtStatus; if(!IsValidPointer(OutProcessId, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_8, L"The given process ID storage is invalid."); // all other parameters are validate by called APIs... FORCE(RtlCreateSuspendedProcess(InEXEPath, InCommandLine, InProcessCreationFlags, &ProcessId, &ThreadId)); // inject library FORCE(RhInjectLibrary( ProcessId, ThreadId, InInjectionOptions, InLibraryPath_x86, InLibraryPath_x64, InPassThruBuffer, InPassThruSize)); *OutProcessId = ProcessId; RETURN; THROW_OUTRO: { if(ProcessId != 0) { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); TerminateProcess(hProcess, 0); CloseHandle(hProcess); } } FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_INTERNAL NtCreateThreadEx( HANDLE InProcess, LPTHREAD_START_ROUTINE InRemoteThreadStart, void* InRemoteCallback, BOOLEAN InCreateSuspended, HANDLE* OutThread) { /* Description: Only intended for Vista and later... Will return NULL for all others which should use CreateRemoteThread() and services instead! In contrast to RtlCreateUserThread() this one will fortunately setup a proper activation context stack, which is required to load the NET framework and many other common APIs. This is why RtlCreateUserThread() can't be used for Windows XP ,for example, even if it would replace the windows service approach which is required in order to get CreateRemoteThread() working. Injection through WOW64 boundaries is still not directly supported and requires a WOW64 bypass helper process. Parameters: - InProcess A target process opened with PROCESS_ALL_ACCESS. - InRemoteThreadStart The method executed by the remote thread. Must be valid in the context of the given process. - InRemoteCallback An uninterpreted callback passed to the remote start routine. Must be valid in the context of the given process. - OutThread Receives a handle to the remote thread. This handle is valid in the calling process. Returns: STATUS_NOT_SUPPORTED Only Windows Vista and later supports NtCreateThreadEx, all other platforms will return this error code. */ HANDLE hRemoteThread; NTSTATUS NtStatus; NtCreateThreadEx_PROC* VistaCreateThread; if(!IsValidPointer(OutThread, sizeof(HANDLE))) THROW(STATUS_INVALID_PARAMETER_4, L"The given handle storage is invalid."); // this will only work for vista and later... if((VistaCreateThread = (NtCreateThreadEx_PROC*)GetProcAddress(hNtDll, "NtCreateThreadEx")) == NULL) THROW(STATUS_NOT_SUPPORTED, L"NtCreateThreadEx() is not supported."); FORCE(VistaCreateThread( &hRemoteThread, 0x1FFFFF, // all access NULL, InProcess, (LPTHREAD_START_ROUTINE)InRemoteThreadStart, InRemoteCallback, InCreateSuspended, 0, NULL, NULL, NULL )); *OutThread = hRemoteThread; RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT RhIsX64Process( ULONG InProcessId, BOOL* OutResult) { /* Description: Detects the bitness of a given process. Parameters: - InProcessId The calling process must have PROCESS_QUERY_INFORMATION access to the process represented by this ID. - OutResult Is set to TRUE if the given process is running under 64-Bit, FALSE otherwise. */ BOOL IsTarget64Bit = FALSE; HANDLE hProc = NULL; IsWow64Process_PROC* pIsWow64Process; NTSTATUS NtStatus; #ifndef _M_X64 GetNativeSystemInfo_PROC* pGetNativeSystemInfo; SYSTEM_INFO SysInfo; #endif if(!IsValidPointer(OutResult, sizeof(BOOL))) THROW(STATUS_INVALID_PARAMETER_2, L"The given result storage is invalid."); // open target process if((hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, InProcessId)) == NULL) { if(GetLastError() == ERROR_ACCESS_DENIED) THROW(STATUS_ACCESS_DENIED, L"The given process is not accessible.") else THROW(STATUS_NOT_FOUND, L"The given process does not exist."); } // if WOW64 is not available then target must be 32-bit pIsWow64Process = (IsWow64Process_PROC*)GetProcAddress(hKernel32, "IsWow64Process"); #ifdef _M_X64 // if the target is not WOW64, then it is 64-bit if(!pIsWow64Process(hProc, &IsTarget64Bit)) THROW(STATUS_INTERNAL_ERROR, L"Unable to detect wether target process is 64-bit or not."); IsTarget64Bit = !IsTarget64Bit; #else IsTarget64Bit = FALSE; if(pIsWow64Process != NULL) { // check if we are running on a 32-bit OS pGetNativeSystemInfo = (GetNativeSystemInfo_PROC*)GetProcAddress(hKernel32, "GetNativeSystemInfo"); if(pGetNativeSystemInfo != NULL) { pGetNativeSystemInfo(&SysInfo); if(SysInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { // if not, then and only then a 32-bit process will be marked as WOW64 process! if(!pIsWow64Process(hProc, &IsTarget64Bit)) THROW(STATUS_INTERNAL_ERROR, L"Unable to detect wether target process is 64-bit or not."); IsTarget64Bit = !IsTarget64Bit; } } } #endif *OutResult = IsTarget64Bit; RETURN(STATUS_SUCCESS); THROW_OUTRO: FINALLY_OUTRO: { if(hProc != NULL) CloseHandle(hProc); return NtStatus; } }
void MBC2MemoryRule::PerformWrite(u16 address, u8 value) { switch (address & 0xE000) { case 0x0000: { if (!(address & 0x0100)) { bool previous = m_bRamEnabled; m_bRamEnabled = ((value & 0x0F) == 0x0A); if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) { (*m_pRamChangedCallback)(); } } else { Log("--> ** Attempting to write on invalid register %X %X", address, value); } break; } case 0x2000: { if (address & 0x0100) { m_iCurrentROMBank = value & 0x0F; if (m_iCurrentROMBank == 0) m_iCurrentROMBank = 1; m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; } else { Log("--> ** Attempting to write on invalid register %X %X", address, value); } break; } case 0x4000: case 0x6000: { Log("--> ** Attempting to write on invalid address %X %X", address, value); break; } case 0xA000: { if (address < 0xA200) { if (m_bRamEnabled) { m_pMemory->Load(address, value & 0x0F); } else { Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); } } else { Log("--> ** Attempting to write on invalid RAM %X %X", address, value); } break; } default: { m_pMemory->Load(address, value); break; } } }
EASYHOOK_NT_EXPORT DbgHandleToObjectName( HANDLE InNamedHandle, PUNICODE_STRING OutNameBuffer, ULONG InBufferSize, ULONG* OutRequiredSize) { /* Description: Queries the kernel space name of a named object. This is only possible if the handle refers to a named object of course. Parameters: - InNamedHandle A valid file, event, section, etc. - OutNameBuffer A buffer large enough to hold the kernel space object name. To query the required size in bytes, set this parameter to NULL. - InBufferSize The maximum size in bytes the given buffer can hold. - OutRequiredSize Receives the required size in bytes. This parameter can be NULL. */ ULONG RequiredSize; NTSTATUS NtStatus; if((InNamedHandle == NULL) || (InNamedHandle == INVALID_HANDLE_VALUE)) THROW(STATUS_INVALID_PARAMETER_1, L"The given handle is invalid."); if(!IsValidPointer(OutNameBuffer, InBufferSize)) { // determine required length if(InBufferSize != 0) THROW(STATUS_INVALID_PARAMETER_3, L"If no buffer is specified, the buffer size is expected to be zero."); if(OutRequiredSize == NULL) THROW(STATUS_INVALID_PARAMETER_4, L"If no buffer is specified, you are expected to query the required size."); } if((NtStatus = ZwQueryObject(InNamedHandle, ObjectNameInformation, NULL, 0, &RequiredSize)) != STATUS_INFO_LENGTH_MISMATCH) FORCE(NtStatus); if(IsValidPointer(OutNameBuffer, InBufferSize)) { // query string if(InBufferSize < RequiredSize) THROW(STATUS_BUFFER_TOO_SMALL, L"The given buffer is not long enough to hold all the data."); FORCE(ZwQueryObject(InNamedHandle, ObjectNameInformation, OutNameBuffer, InBufferSize, &RequiredSize)); } if(IsValidPointer(OutRequiredSize, sizeof(ULONG))) *OutRequiredSize = RequiredSize; RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
void MBC1MemoryRule::PerformWrite(u16 address, u8 value) { switch (address & 0xE000) { case 0x0000: { if (m_pCartridge->GetRAMSize() > 0) { bool previous = m_bRamEnabled; m_bRamEnabled = ((value & 0x0F) == 0x0A); if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) { (*m_pRamChangedCallback)(); } } break; } case 0x2000: { if (m_iMode == 0) { m_iCurrentROMBank = (value & 0x1F) | (m_HigherRomBankBits << 5); } else { m_iCurrentROMBank = value & 0x1F; } if (m_iCurrentROMBank == 0x00 || m_iCurrentROMBank == 0x20 || m_iCurrentROMBank == 0x40 || m_iCurrentROMBank == 0x60) m_iCurrentROMBank++; m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; break; } case 0x4000: { if (m_iMode == 1) { m_iCurrentRAMBank = value & 0x03; m_iCurrentRAMBank &= (m_pCartridge->GetRAMBankCount() - 1); m_CurrentRAMAddress = m_iCurrentRAMBank * 0x2000; } else { m_HigherRomBankBits = value & 0x03; m_iCurrentROMBank = (m_iCurrentROMBank & 0x1F) | (m_HigherRomBankBits << 5); if (m_iCurrentROMBank == 0x00 || m_iCurrentROMBank == 0x20 || m_iCurrentROMBank == 0x40 || m_iCurrentROMBank == 0x60) m_iCurrentROMBank++; m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; } break; } case 0x6000: { if ((m_pCartridge->GetRAMSize() != 3) && (value & 0x01)) { Log("--> ** Attempting to change MBC1 to mode 1 with incorrect RAM banks %X %X", address, value); } else { m_iMode = value & 0x01; } break; } case 0xA000: { if (m_bRamEnabled) { if (m_iMode == 0) { if ((m_pCartridge->GetRAMSize() == 1) && (address >= 0xA800)) { // only 2KB of ram Log("--> ** Attempting to write on invalid RAM %X %X", address, value); } m_pRAMBanks[address - 0xA000] = value; } else m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress] = value; } else { Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); } break; } default: { m_pMemory->Load(address, value); break; } } }
EASYHOOK_NT_EXPORT LhUninstallHook(TRACED_HOOK_HANDLE InHandle) { /* Description: Removes the given hook. To also release associated resources, you will have to call LhWaitForPendingRemovals(). In any case your hook handler will never be executed again, after calling this method. Parameters: - InHandle A traced hook handle. If the hook is already removed, this method will still return STATUS_SUCCESS. */ LOCAL_HOOK_INFO* Hook = NULL; LOCAL_HOOK_INFO* List; LOCAL_HOOK_INFO* Prev; NTSTATUS NtStatus; BOOLEAN IsAllocated = FALSE; if(!IsValidPointer(InHandle, sizeof(HOOK_TRACE_INFO))) return FALSE; RtlAcquireLock(&GlobalHookLock); { if((InHandle->Link != NULL) && LhIsValidHandle(InHandle, &Hook)) { InHandle->Link = NULL; if(Hook->HookProc != NULL) { Hook->HookProc = NULL; IsAllocated = TRUE; } } if(!IsAllocated) { RtlReleaseLock(&GlobalHookLock); RETURN; } // remove from global list List = GlobalHookListHead.Next; Prev = &GlobalHookListHead; while(List != NULL) { if(List == Hook) { Prev->Next = Hook->Next; break; } List = List->Next; } // add to removal list Hook->Next = GlobalRemovalListHead.Next; GlobalRemovalListHead.Next = Hook; } RtlReleaseLock(&GlobalHookLock); RETURN(STATUS_SUCCESS); //THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }
EASYHOOK_NT_EXPORT LhBarrierCallStackTrace( PVOID* OutMethodArray, ULONG InMaxMethodCount, ULONG* OutMethodCount) { /* Description: Creates a call stack trace and translates all method entries back into their owning modules. Parameters: - OutMethodArray An array receiving the methods on the call stack. - InMaxMethodCount The length of the method array. - OutMethodCount The actual count of methods on the call stack. This will never be greater than 64. Returns: STATUS_NOT_IMPLEMENTED Only supported since Windows XP. */ NTSTATUS NtStatus; PVOID Backup = NULL; if(InMaxMethodCount > 64) THROW(STATUS_INVALID_PARAMETER_2, L"At maximum 64 modules are supported."); if(!IsValidPointer(OutMethodArray, InMaxMethodCount * sizeof(PVOID))) THROW(STATUS_INVALID_PARAMETER_1, L"The given module buffer is invalid."); if(!IsValidPointer(OutMethodCount, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_3, L"Invalid module count storage."); FORCE(LhBarrierBeginStackTrace(&Backup)); #ifndef DRIVER if(RtlCaptureStackBackTrace == NULL) RtlCaptureStackBackTrace = (PROC_RtlCaptureStackBackTrace*)GetProcAddress(hKernel32, "RtlCaptureStackBackTrace"); if(RtlCaptureStackBackTrace == NULL) THROW(STATUS_NOT_IMPLEMENTED, L"This method requires Windows XP or later."); #endif *OutMethodCount = RtlCaptureStackBackTrace(1, 32, OutMethodArray, NULL); RETURN; THROW_OUTRO: FINALLY_OUTRO: { if(Backup != NULL) LhBarrierEndStackTrace(Backup); return NtStatus; } }
void MBC3MemoryRule::PerformWrite(u16 address, u8 value) { switch (address & 0xE000) { case 0x0000: { if (m_pCartridge->GetRAMSize() > 0) { bool previous = m_bRamEnabled; m_bRamEnabled = ((value & 0x0F) == 0x0A); if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled) { (*m_pRamChangedCallback)(); } } m_bRTCEnabled = ((value & 0x0F) == 0x0A); break; } case 0x2000: { m_iCurrentROMBank = value & 0x7F; if (m_iCurrentROMBank == 0) m_iCurrentROMBank = 1; m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1); m_CurrentROMAddress = m_iCurrentROMBank * 0x4000; break; } case 0x4000: { if ((value >= 0x08) && (value <= 0x0C)) { // RTC if (m_pCartridge->IsRTCPresent() && m_bRTCEnabled) { m_RTCRegister = value; m_iCurrentRAMBank = -1; } else { Log("--> ** Attempting to select RTC register when RTC is disabled or not present %X %X", address, value); } } else if (value <= 0x03) { m_iCurrentRAMBank = value; m_iCurrentRAMBank &= (m_pCartridge->GetRAMBankCount() - 1); m_CurrentRAMAddress = m_iCurrentRAMBank * 0x2000; } else { Log("--> ** Attempting to select unkwon register %X %X", address, value); } break; } case 0x6000: { if (m_pCartridge->IsRTCPresent()) { // RTC Latch if ((m_iRTCLatch == 0x00) && (value == 0x01)) { UpdateRTC(); m_iRTCLatchedSeconds = m_iRTCSeconds; m_iRTCLatchedMinutes = m_iRTCMinutes; m_iRTCLatchedHours = m_iRTCHours; m_iRTCLatchedDays = m_iRTCDays; m_iRTCLatchedControl = m_iRTCControl; } if ((value == 0x00) || (value == 0x01)) { m_iRTCLatch = value; } } break; } case 0xA000: { if (m_iCurrentRAMBank >= 0) { if (m_bRamEnabled) { m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress] = value; } else { Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value); } } else if (m_pCartridge->IsRTCPresent() && m_bRTCEnabled) { m_RTCLastTime = static_cast<s32>(m_pCartridge->GetCurrentRTC()); switch (m_RTCRegister) { case 0x08: m_iRTCSeconds = value; break; case 0x09: m_iRTCMinutes = value; break; case 0x0A: m_iRTCHours = value; break; case 0x0B: m_iRTCDays = value; break; case 0x0C: if (m_iRTCControl & 0x80) m_iRTCControl = 0x80 | value; else m_iRTCControl = value; break; } } else { Log("--> ** Attempting to write on RTC when RTC is disabled or not present %X %X", address, value); } break; } default: { m_pMemory->Load(address, value); break; } } }
EASYHOOK_NT_EXPORT LhEnumModules( HMODULE* OutModuleArray, ULONG InMaxModuleCount, ULONG* OutModuleCount) { /* Description: For performance reasons, only the module base addresses are returned. You may then loop through the array and use LhBarrierPointerToModule() to query each module information. Parameters: - OutModuleArray An array receiveing module pointers. Set to NULL to only query "OutModuleCount". - InMaxModuleCount The maximum count of modules that the given buffer can hold. - OutModuleCount The actual count of modules loaded into the current process or into the kernel, depending on the caller's context. This pointer must be specified if no module buffer is passed; if one is passed, this parameter is optional. */ ULONG ModIndex = 0; NTSTATUS NtStatus; MODULE_INFORMATION* List; if(IsValidPointer(OutModuleArray, InMaxModuleCount * sizeof(PVOID))) { // loop through the module list... RtlAcquireLock(&GlobalHookLock); { if(IsValidPointer(OutModuleCount, sizeof(ULONG))) *OutModuleCount = LhModuleCount; List = LhModuleArray; // walk through process modules while(List != NULL) { if(ModIndex > InMaxModuleCount) { RtlReleaseLock(&GlobalHookLock); THROW(STATUS_BUFFER_TOO_SMALL, L"The given buffer was filled but could not hold all modules."); } OutModuleArray[ModIndex++] = (HMODULE)List->BaseAddress; List = List->Next; } } RtlReleaseLock(&GlobalHookLock); } else { // return module count... if(!IsValidPointer(OutModuleCount, sizeof(ULONG))) THROW(STATUS_INVALID_PARAMETER_3, L"If no buffer is specified you need to pass a module count storage."); *OutModuleCount = LhModuleCount; } RETURN; THROW_OUTRO: FINALLY_OUTRO: return NtStatus; }