CProcessesHandles::CEntry::~CEntry() { NKT_ASSERT(GetLinkedList() == NULL); if (h != NULL && h != INVALID_HANDLE_VALUE && h != NKTHOOKLIB_CurrentProcess) NktNtClose(h); return; }
HRESULT CNktHookInfoImpl::SendCustomMessage(__in my_ssize_t msgCode, __in my_ssize_t msgParam, __in VARIANT_BOOL sync, __out my_ssize_t *pRetVal) { CNktSimpleLockNonReentrant cLock(&nLock); HRESULT hRes; if (nValid == 0) { hRes = E_FAIL; } else if (pRetVal == NULL) { hRes = E_POINTER; } else if (sCallTrampoline.fnSendCustomMessage == NULL) { *pRetVal = 0; hRes = E_FAIL; } else { NKT_ASSERT(sCallTrampoline.lpInternal != NULL); hRes = sCallTrampoline.fnSendCustomMessage(sCallTrampoline.lpInternal, (SIZE_T*)pRetVal, (SIZE_T)msgCode, (SIZE_T)msgParam, Bool_From_VariantBool(sync)); } if (FAILED(hRes) && pRetVal != NULL) *pRetVal = 0; return ::SetupErrorInfoAndReturn(IID_INktHookInfo, hRes); }
VOID CProcessesHandles::CEntry::CMemBlock::ReleaseSlot(__in LPVOID lpAddr) { SIZE_T nOfs; BYTE nMask; NKT_ASSERT((SIZE_T)(LPBYTE)lpAddr >= (SIZE_T)lpBaseAddress); nOfs = (SIZE_T)(LPBYTE)lpAddr - (SIZE_T)lpBaseAddress; NKT_ASSERT((nOfs % nSlotSize) == 0); nOfs /= nSlotSize; NKT_ASSERT((nOfs>>3) < ((65536 / nSlotSize) + 7) >> 3); nMask = 1 << (nOfs & 7); nOfs >>= 3; NKT_ASSERT((lpFreeEntries[nOfs] & nMask) == 0); lpFreeEntries[nOfs] |= nMask; nFreeCount++; return; }
CProcessesHandles::CEntry::CMemBlock::CMemBlock(__in HANDLE _hProc, __in SIZE_T _nSlotSize) : TNktLnkLstNode<CMemBlock>(), CNktNtHeapBaseObj() { SIZE_T nFreeEntriesSize; NKT_ASSERT(_nSlotSize > 0 && _nSlotSize <= 65536); NKT_ASSERT((_nSlotSize && !(_nSlotSize & (_nSlotSize - 1))) != false); //_nSlotSize must be a power of 2 hProc = _hProc; nSlotSize = _nSlotSize; nFreeEntriesSize = ((65536 / nSlotSize) + 7) >> 3; lpFreeEntries = (LPBYTE)NktHookLibHelpers::MemAlloc(nFreeEntriesSize); if (lpFreeEntries != NULL) NktHookLibHelpers::MemSet(lpFreeEntries, 0xFF, nFreeEntriesSize); nFreeCount = 65536 / nSlotSize; lpBaseAddress = NULL; return; }
CNktDvTransportEngine::CNktDvTransportEngine(__in CNktDvTransportEngineCallback *_lpCallback) : CNktFastMutex(), CNktMemMgrObj() { NKT_ASSERT(_lpCallback != NULL); lpCallback = _lpCallback; hIOCP = NULL; dwMePid = ::GetCurrentProcessId(); NktInterlockedExchange(&nMsgId, 0); lpWorkerThreads = NULL; nWorkerThreadsCount = nDispatchersCount = 0; return; }
LPBYTE CProcessesHandles::CEntry::CMemBlock::GetFreeSlot() { SIZE_T i, nIdx, nFreeEntriesSize; if (nFreeCount == 0) return NULL; nFreeEntriesSize = ((65536 / nSlotSize) + 7) >> 3; for (nIdx=0; nIdx<nFreeEntriesSize; nIdx++) { if (lpFreeEntries[nIdx] != 0) break; } NKT_ASSERT(nIdx < nFreeEntriesSize); for (i=0; i<8; i++) { if ((lpFreeEntries[nIdx] & (1<<i)) != 0) break; } NKT_ASSERT(i < 8); lpFreeEntries[nIdx] &= ~(1<<i); nFreeCount--; return lpBaseAddress + ((nIdx<<3) + i) * nSlotSize; }
VOID CProcessesHandles::CEntry::FreeStub(__in LPVOID lpAddr) { TNktLnkLst<CMemBlock>::Iterator it; CMemBlock *lpBlock; for (lpBlock=it.Begin(cMemBlocksList); lpBlock!=NULL; lpBlock=it.Next()) { if (lpBlock->IsAddressInBlock(lpAddr) != FALSE) { lpBlock->ReleaseSlot(lpAddr); return; } } NKT_ASSERT(FALSE); return; };
HRESULT CNktHookInfoImpl::InvalidateCache(__in my_ssize_t hDll) { CNktSimpleLockNonReentrant cLock(&nLock); HRESULT hRes; if (nValid == 0) { hRes = E_FAIL; } else if (sCallTrampoline.fnInvalidateCache == NULL) { hRes = E_FAIL; } else { NKT_ASSERT(sCallTrampoline.lpInternal != NULL); hRes = sCallTrampoline.fnInvalidateCache(sCallTrampoline.lpInternal, (SIZE_T)hDll); } return ::SetupErrorInfoAndReturn(IID_INktHookInfo, hRes); }
HRESULT CNktHookInfoImpl::ThreadIgnore(__in LONG theadId, __in VARIANT_BOOL ignore) { CNktSimpleLockNonReentrant cLock(&nLock); HRESULT hRes; if (nValid == 0) { hRes = E_FAIL; } else if (sCallTrampoline.fnThreadIgnore == NULL) { hRes = E_FAIL; } else { NKT_ASSERT(sCallTrampoline.lpInternal != NULL); hRes = sCallTrampoline.fnThreadIgnore(sCallTrampoline.lpInternal, (DWORD)theadId, Bool_From_VariantBool(ignore)); } return ::SetupErrorInfoAndReturn(IID_INktHookInfo, hRes); }
HRESULT CNktDvProcessMemory::WriteProtected(__in LPVOID lpRemoteDest, __in LPCVOID lpLocalSrc, __in SIZE_T nSize, __deref_out_opt SIZE_T *lpnWritten) { LPBYTE s, d; SIZE_T nToCopy, nWritten, nTotalWritten; DWORD dwOrigProt, dwNewProt, dwDummy; MEMORY_BASIC_INFORMATION sMbi; HANDLE hTempProc; HRESULT hRes; if (lpnWritten != NULL) *lpnWritten = NULL; //check parameters if (nSize == 0) return S_OK; if (lpRemoteDest == NULL || lpLocalSrc == NULL) return E_POINTER; //get process handle hTempProc = GetWriteAccessHandle(); if (hTempProc == NULL) return E_ACCESSDENIED; //do write hRes = S_OK; nTotalWritten = 0; { CNktAutoFastMutex cLock(&GetLockObject()); d = (LPBYTE)lpRemoteDest; s = (LPBYTE)lpLocalSrc; while (nSize > 0) { //check current protection if (::VirtualQueryEx(hTempProc, d, &sMbi, sizeof(sMbi)) == FALSE) { hRes = E_FAIL; break; } //calculate available writable bytes in region nToCopy = 0; if (d >= (LPBYTE)(sMbi.BaseAddress)) { nToCopy = d - (LPBYTE)(sMbi.BaseAddress); nToCopy = (nToCopy < sMbi.RegionSize) ? (sMbi.RegionSize - nToCopy) : 0; } NKT_ASSERT(nToCopy > 0); if (nToCopy == 0) { hRes = E_FAIL; //should not happen break; } //calculate new desired access based on the current one if ((sMbi.Protect & PAGE_GUARD) != 0) { hRes = E_FAIL; //if a page is guarded, do not touch break; } dwNewProt = sMbi.Protect & 0xFF; switch (dwNewProt) { case PAGE_EXECUTE_READWRITE: case PAGE_EXECUTE_WRITECOPY: case PAGE_READWRITE: case PAGE_WRITECOPY: break; case PAGE_EXECUTE_READ: dwNewProt = PAGE_EXECUTE_READWRITE; break; default: dwNewProt = PAGE_READWRITE; break; } dwNewProt |= (sMbi.Protect & 0xFFFFFF00); //add the rest of the flags //if access protection needs a change, do it if (dwNewProt != sMbi.Protect) { if (::VirtualProtectEx(hTempProc, sMbi.BaseAddress, sMbi.RegionSize, dwNewProt, &dwOrigProt) == FALSE) { hRes = E_ACCESSDENIED; break; } } //do partial copy if (nToCopy > nSize) nToCopy = nSize; nWritten = 0; if (bIsLocal == FALSE) { hRes = (::WriteProcessMemory(hTempProc, d, s, nToCopy, &nWritten) != FALSE) ? S_OK : NKT_HRESULT_FROM_LASTERROR(); } else { nWritten = nktTryMemCopy(d, s, nToCopy); hRes = (nToCopy == nWritten) ? S_OK : E_ACCESSDENIED; } nTotalWritten += nWritten; //restore original access if (dwNewProt != sMbi.Protect) ::VirtualProtectEx(hTempProc, sMbi.BaseAddress, sMbi.RegionSize, dwOrigProt, &dwDummy); //check copy operation if (FAILED(hRes) || nToCopy != nWritten) break; //advance buffer s += nWritten; d += nWritten; nSize -= nWritten; } } if (nTotalWritten > 0 && nSize > 0) hRes = NKT_DVERR_PartialCopy; if (FAILED(hRes) && hRes != E_ACCESSDENIED && hRes != NKT_DVERR_PartialCopy) hRes = E_FAIL; if (lpnWritten != NULL) *lpnWritten = nTotalWritten; return hRes; }
VOID CNktDvTransportEngine::WorkerThreadProc(__in SIZE_T nIndex) { CConnection *lpConn; LPOVERLAPPED _lpOvr; CNktDvTransportOverlapped *lpOvr; HRESULT hRes; HANDLE hEventCopy; BOOL b, bReadPending; DWORD dwNumberOfBytes, dwWritten; lpCallback->TEC_WorkerThreadStarted(); //---- for (;;) { lpConn = NULL; lpOvr = NULL; b = ::GetQueuedCompletionStatus(hIOCP, &dwNumberOfBytes, (PULONG_PTR)&lpConn, &_lpOvr, INFINITE); if (lpConn == NULL) { break; //a completion key of 0 is posted to the iocp to request us to shut down... } hRes = (b != FALSE) ? S_OK : NKT_HRESULT_FROM_LASTERROR(); { CConnectionAutoRef cConnAutoRef(lpConn); lpOvr = CNktDvTransportOverlapped::ClassFromOverlapped(_lpOvr); bReadPending = FALSE; if (_lpOvr != NULL) { switch (lpOvr->dwType) { case XTYPE_Read: lpOvr->dwReadedBytes = dwNumberOfBytes; //add to read queue if (SUCCEEDED(hRes)) { lpConn->AddBufferInSortedReadedQueue(lpOvr); hRes = lpConn->SendReadPacket(); //setup read-ahead } NKT_DEBUGPRINTLNA(Nektra::dlTransport, ("%lu) TransportEngine[ReadCompleted]: Ovr=%IXh, " "hRes=%08X", ::GetTickCount(), lpOvr, hRes)); bReadPending = TRUE; break; case XTYPE_WriteRequest: { CNktAutoFastMutex cLock(lpConn); if (SUCCEEDED(hRes)) lpConn->AddBufferInSortedToWriteQueue(lpOvr); while (SUCCEEDED(hRes) && cShutdownMtx.IsSystemActive() != FALSE && cShutdownMtx.IsShuttingDown() == FALSE) { //get next sequenced block to write lpOvr = lpConn->GetBufferFromSortedToWriteQueue(lpConn->nNextWriteOrderToProcess); if (lpOvr == NULL) break; if (lpOvr->dwType != XTYPE_Discard) { lpConn->AddBufferToRwList(lpOvr); //do real write lpOvr->dwType = XTYPE_Write; if (lpOvr->sMsg.sMsgCommon.hProcessedEvent != NULL) { if (::DuplicateHandle(::GetCurrentProcess(), (HANDLE)(lpOvr->sMsg.sMsgCommon.hProcessedEvent), lpConn->hAgentProc, &hEventCopy, 0, FALSE, DUPLICATE_SAME_ACCESS) != FALSE) lpOvr->sMsg.sMsgCommon.hProcessedEvent = NKT_PTR_2_ULONGLONG(hEventCopy); else hRes = NKT_HRESULT_FROM_LASTERROR(); } if (SUCCEEDED(hRes)) { if (::WriteFile(lpConn->hPipe, &(lpOvr->sMsg), lpOvr->dwWrittenBytes, &dwWritten, &(lpOvr->sOvr)) == FALSE) { hRes = NKT_HRESULT_FROM_LASTERROR(); if (hRes == NKT_HRESULT_FROM_WIN32(ERROR_IO_PENDING)) hRes = S_OK; } } if (SUCCEEDED(hRes)) lpConn->AddRef(); else lpConn->FreeBuffer(lpOvr); NKT_DEBUGPRINTLNA(Nektra::dlTransport, ("%lu) TransportEngine[Write]: Ovr=%IXh, " "hRes=%08X", ::GetTickCount(), lpOvr, hRes)); } else { lpConn->FreeBuffer(lpOvr); } NktInterlockedIncrement(&(lpConn->nNextWriteOrderToProcess)); } } break; case XTYPE_Write: if (SUCCEEDED(hRes) && lpOvr->dwWrittenBytes != dwNumberOfBytes) hRes = NKT_HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); NKT_DEBUGPRINTLNA(Nektra::dlTransport, ("%lu) TransportEngine[WriteCompleted]: Ovr=%IXh, " "hRes=%08X", ::GetTickCount(), lpOvr, hRes)); lpConn->FreeBuffer(lpOvr); break; case XTYPE_Discard: lpConn->FreeBuffer(lpOvr); break; default: NKT_ASSERT(FALSE); lpConn->FreeBuffer(lpOvr); break; } } else { hRes = NKT_DVERR_InvalidTransportData; } if (SUCCEEDED(hRes) && bReadPending != FALSE) { while (SUCCEEDED(hRes) && cShutdownMtx.IsSystemActive() != FALSE && cShutdownMtx.IsShuttingDown() == FALSE) { //get next sequenced block readed lpOvr = lpConn->GetBufferFromSortedReadedQueue(lpConn->nNextReadOrderToProcess); if (lpOvr == NULL) break; hRes = DispatchReadedMessage(lpConn, lpOvr); NktInterlockedIncrement(&(lpConn->nNextReadOrderToProcess)); } } if (FAILED(hRes)) { if (cShutdownMtx.IsSystemActive() != FALSE && cShutdownMtx.IsShuttingDown() == FALSE) lpCallback->TEC_OnTransportError(this, lpConn->dwPid, hRes); DisconnectAgent(lpConn->dwPid); } lpConn->Release(); } } //---- lpCallback->TEC_WorkerThreadEnded(); return; }
static HRESULT ProcessFunction(__in FILE *fp, __in WORD nIndex, __in TYPEATTR* lpTypeAttr, __in ITypeInfo *lpTypeInfo, __in SIZE_T nIndent, __in LPWSTR szParentThisW) { CAutoFuncDesc cFuncDesc; CNktStringW cStrTempW; CNktComBStr bstrNames[MAX_FUNCTION_NAMES]; BSTR bstr[MAX_FUNCTION_NAMES]; HRESULT hRes; CNktComVariant vtValue; UINT i, j, nNamesCount; char szBufA[32]; hRes = cFuncDesc.Set(lpTypeInfo, nIndex); EXIT_ON_ERROR(hRes); //Write return type cStrTempW.Empty(); hRes = TypeDescToString(cStrTempW, &(cFuncDesc->elemdescFunc.tdesc), lpTypeInfo, FALSE); EXIT_ON_ERROR(hRes); WRITEINDENT_AND_CHECK(fp, nIndent); WRITEWIDE_AND_CHECK(fp, (LPWSTR)cStrTempW); WRITEANSI_AND_CHECK(fp, " ("); if (lpTypeAttr->typekind != TKIND_DISPATCH) { // Write calling convention switch (cFuncDesc->callconv) { case CC_CDECL: WRITEANSI_AND_CHECK(fp, "_cdecl "); break; case CC_PASCAL: WRITEANSI_AND_CHECK(fp, "__pascal "); break; case CC_STDCALL: WRITEANSI_AND_CHECK(fp, "__stdcall "); break; default: _tprintf_s(_T("Error: Unsupported calling convetion '%04X'.\n"), cFuncDesc->callconv); return E_NOTIMPL; } } else { WRITEANSI_AND_CHECK(fp, "__stdcall "); } WRITEANSI_AND_CHECK(fp, "*"); // Write methodname // // Problem: If a property has the propput or propputref attributes the // 'right hand side' (rhs) is *always* the last parameter and MkTypeLib // strips the parameter name. Thus you will always get 1 less name // back from ::GetNames than normal. // // Thus for the example below // [propput] void Color([in] VARIANT rgb, [in] VARIANT rgb2 ); // without taking this into consderation the output would be // [propput] void Color([in] VARIANT rgb, [in] VARIANT ); // when it should be // [propput] void Color([in] VARIANT rgb, [in] VARIANT rhs ); // // Another weirdness comes from a bug (which will never be fixed) // where optional parameters on property functions were allowed. // Because they were allowed by accident people used them, so they // are still allowed. nNamesCount = 0; hRes = lpTypeInfo->GetNames(cFuncDesc->memid, bstr, MAX_FUNCTION_NAMES, &nNamesCount); EXIT_ON_ERROR(hRes); for (i=0; i<nNamesCount; i++) bstrNames[i].Attach(bstr[i]); // fix for 'rhs' problem if (nNamesCount < (UINT)cFuncDesc->cParams+1) { hRes = bstrNames[nNamesCount++].Set(L"rhs"); EXIT_ON_ERROR(hRes); } NKT_ASSERT(nNamesCount == cFuncDesc->cParams+1); if ((cFuncDesc->invkind & INVOKE_PROPERTYGET) != 0) { WRITEANSI_AND_CHECK(fp, "get_"); } else if ((cFuncDesc->invkind & INVOKE_PROPERTYPUT) != 0) { WRITEANSI_AND_CHECK(fp, "put_"); } else if ((cFuncDesc->invkind & INVOKE_PROPERTYPUTREF) != 0) { WRITEANSI_AND_CHECK(fp, "putref_"); } WRITEBSTR_AND_CHECK(fp, bstrNames[0]); WRITEANSI_AND_CHECK(fp, ")("); if (szParentThisW != NULL) { WRITEWIDE_AND_CHECK(fp, szParentThisW); WRITEANSI_AND_CHECK(fp, "* This"); } // params have the format // [attributes] type paramname // where attributes can be // in, out, optional, string (string is not valid for TKIND_MODULE) for (i=0; i<(UINT)(cFuncDesc->cParams); i++) { if (i > 0 || szParentThisW != NULL) { WRITEANSI_AND_CHECK(fp, ", "); } // type if ((cFuncDesc->lprgelemdescParam[i].tdesc.vt & 0x0FFF) == VT_CARRAY) { // type name[n] cStrTempW.Empty(); hRes = TypeDescToString(cStrTempW, &(cFuncDesc->lprgelemdescParam[i].tdesc.lpadesc->tdescElem), lpTypeInfo, FALSE); EXIT_ON_ERROR(hRes); WRITEWIDE_AND_CHECK(fp, (LPWSTR)cStrTempW); WRITEANSI_AND_CHECK(fp, " "); WRITEBSTR_AND_CHECK(fp, bstrNames[i+1]); for (j=0; j<cFuncDesc->lprgelemdescParam[i].tdesc.lpadesc->cDims; j++) { _snprintf_s(szBufA, X_ARRAYLEN(szBufA), _TRUNCATE, "[%d]", cFuncDesc->lprgelemdescParam[i].tdesc.lpadesc->rgbounds[j].cElements); WRITEANSI_AND_CHECK(fp, szBufA); } } else { cStrTempW.Empty(); hRes = TypeDescToString(cStrTempW, &(cFuncDesc->lprgelemdescParam[i].tdesc), lpTypeInfo, FALSE); EXIT_ON_ERROR(hRes); WRITEWIDE_AND_CHECK(fp, (LPWSTR)cStrTempW); WRITEANSI_AND_CHECK(fp, " "); WRITEBSTR_AND_CHECK(fp, bstrNames[i+1]); } } WRITEANSI_AND_CHECK(fp, ");\r\n"); return S_OK; }
DWORD CNktHookLib::RemoteHook(__inout HOOK_INFO aHookInfo[], __in SIZE_T nCount, __in DWORD dwPid, __in DWORD dwFlags) { DWORD dwOsErr; if (dwPid == 0) return ERROR_INVALID_PARAMETER; if (lpInternals != NULL) { NktHookLib::CNktAutoFastMutex cAutoLock(&(int_data->cMtx)); NktHookLib::CProcessesHandles::CEntryPtr cProcEntry; NktHookLib::CHookEntry *lpHookEntry, **lpNewEntriesList; BYTE aNewCode[0x80 + HOOKENG_MAX_STUB_SIZE]; SIZE_T nSize, nHookIdx; LPBYTE lpPtr; DWORD dw; NTSTATUS nNtStatus; if (aHookInfo == 0 || nCount == 0) return ERROR_INVALID_PARAMETER; for (nHookIdx=0; nHookIdx<nCount; nHookIdx++) { if (aHookInfo[nHookIdx].lpProcToHook == NULL || aHookInfo[nHookIdx].lpNewProcAddr == NULL) return ERROR_INVALID_PARAMETER; aHookInfo[nHookIdx].nHookId = 0; aHookInfo[nHookIdx].lpCallOriginal = NULL; } //get process handle cProcEntry.Attach(int_data->cProcHdrMgr.Get(dwPid)); if (cProcEntry == NULL) return ERROR_ACCESS_DENIED; //create entries for each item lpNewEntriesList = (NktHookLib::CHookEntry**)NktHookLibHelpers::MemAlloc(nCount*sizeof(NktHookLib::CHookEntry*)); if (lpNewEntriesList != NULL) { dwOsErr = NO_ERROR; NktHookLibHelpers::MemSet(lpNewEntriesList, 0, nCount * sizeof(NktHookLib::CHookEntry*)); } else { dwOsErr = ERROR_NOT_ENOUGH_MEMORY; } for (nHookIdx=0; nHookIdx<nCount && dwOsErr==NO_ERROR; nHookIdx++) { //allocate new entry lpHookEntry = new NktHookLib::CHookEntry(cProcEntry); if (lpHookEntry == NULL) { dwOsErr = ERROR_NOT_ENOUGH_MEMORY; continue; } lpNewEntriesList[nHookIdx] = lpHookEntry; lpHookEntry->lpOrigProc = (LPBYTE)(aHookInfo[nHookIdx].lpProcToHook); if ((dwFlags & NKTHOOKLIB_DontSkipInitialJumps) == 0) { lpHookEntry->lpOrigProc = lpHookEntry->SkipJumpInstructions(lpHookEntry->lpOrigProc); if (lpHookEntry->lpOrigProc == NULL) { dwOsErr = ERROR_ACCESS_DENIED; continue; } } lpHookEntry->lpNewProc = (LPBYTE)(aHookInfo[nHookIdx].lpNewProcAddr); //read original stub and create new one dwOsErr = lpHookEntry->CreateStub(int_data->sOptions.bOutputDebug, ((dwFlags & NKTHOOKLIB_DontSkipAnyJumps) == 0) ? TRUE : FALSE); if (dwOsErr != NO_ERROR) continue; //calculate inject code size and offset to data switch (cProcEntry->GetPlatform()) { case NKTHOOKLIB_PRocessPlatformX86: lpHookEntry->nInjCodeAndDataSize = 0x2A + lpHookEntry->nNewStubSize; break; #if defined _M_X64 case NKTHOOKLIB_PRocessPlatformX64: lpHookEntry->nInjCodeAndDataSize = 0x41 + lpHookEntry->nNewStubSize; break; #endif //_M_X64 } //allocate memory for inject code in target process NKT_ASSERT(lpHookEntry->nInjCodeAndDataSize < NKTHOOKLIB_PROCESS_MEMBLOCK_SIZE); lpHookEntry->lpInjCodeAndData = cProcEntry->AllocateStub(lpHookEntry->lpOrigProc); if (lpHookEntry->lpInjCodeAndData == NULL) { dwOsErr = ERROR_NOT_ENOUGH_MEMORY; continue; } //setup code switch (cProcEntry->GetPlatform()) { case NKTHOOKLIB_PRocessPlatformX86: NktHookLibHelpers::MemSet(aNewCode, 0x00, 8); //flags location NktHookLibHelpers::MemSet(aNewCode+0x08, 0x90, 8); //NOPs for hotpatching double hooks aNewCode[0x10] = 0x50; //push eax aNewCode[0x11] = 0xB8; //mov eax, ADDR lpInjCode *((ULONG NKT_UNALIGNED*)(aNewCode+0x12)) = (ULONG)(lpHookEntry->lpInjCodeAndData); aNewCode[0x16] = 0xF7; //test dword ptr [eax], 00000101h aNewCode[0x17] = 0x00; *((ULONG NKT_UNALIGNED*)(aNewCode+0x18)) = 0x00000101; aNewCode[0x1C] = 0x75; //jne @@1 ;if disabled/uninst aNewCode[0x1D] = 0x06; aNewCode[0x1E] = 0x58; //pop eax aNewCode[0x1F] = 0xE9; //jmp hooked proc *((ULONG NKT_UNALIGNED*)(aNewCode+0x20)) = (ULONG)(lpHookEntry->lpNewProc) - (ULONG)(lpHookEntry->lpInjCodeAndData) - 0x24; aNewCode[0x24] = 0x58; //@@1: pop eax lpHookEntry->lpCall2Orig = lpHookEntry->lpInjCodeAndData + 0x25; NktHookLibHelpers::MemCopy(aNewCode+0x25, lpHookEntry->aNewStub, lpHookEntry->nNewStubSize); //new stub aNewCode[0x25+lpHookEntry->nNewStubSize] = 0xE9; //jmp original proc after stub *((ULONG NKT_UNALIGNED*)(aNewCode+0x26+lpHookEntry->nNewStubSize)) = (ULONG)(lpHookEntry->lpOrigProc) + (ULONG)(lpHookEntry->nOriginalStubSize) - (ULONG)(lpHookEntry->lpInjCodeAndData+0x2A+lpHookEntry->nNewStubSize); break; #if defined _M_X64 case NKTHOOKLIB_PRocessPlatformX64: NktHookLibHelpers::MemSet(aNewCode, 0x00, 8); //flags location NktHookLibHelpers::MemSet(aNewCode+0x08, 0x90, 8); //NOPs for hotpatching double hooks aNewCode[0x10] = 0x50; //push rax aNewCode[0x11] = 0x48; //mov rax, ADDR lpInjCode aNewCode[0x12] = 0xB8; *((ULONGLONG NKT_UNALIGNED*)(aNewCode+0x13)) = (ULONGLONG)(lpHookEntry->lpInjCodeAndData); aNewCode[0x1B] = 0xF7; //test dword ptr [rax], 00000101h aNewCode[0x1C] = 0x00; *((ULONG NKT_UNALIGNED*)(aNewCode+0x1D)) = 0x00000101; aNewCode[0x21] = 0x75; //jne @@1 ;if disabled/uninst aNewCode[0x22] = 0x0F; aNewCode[0x23] = 0x58; //pop rax aNewCode[0x24] = 0xFF; //jmp hooked proc aNewCode[0x25] = 0x25; *((ULONG NKT_UNALIGNED*)(aNewCode+0x26)) = 0; *((ULONGLONG NKT_UNALIGNED*)(aNewCode+0x2A)) = (ULONGLONG)(lpHookEntry->lpNewProc); aNewCode[0x32] = 0x58; //@@1: pop rax lpHookEntry->lpCall2Orig = lpHookEntry->lpInjCodeAndData+0x33; NktHookLibHelpers::MemCopy(aNewCode+0x33, lpHookEntry->aNewStub, lpHookEntry->nNewStubSize); //new stub aNewCode[0x33+lpHookEntry->nNewStubSize] = 0xFF; //jmp original proc after stub aNewCode[0x34+lpHookEntry->nNewStubSize] = 0x25; *((ULONG NKT_UNALIGNED*)(aNewCode+0x35+lpHookEntry->nNewStubSize)) = 0; *((ULONGLONG NKT_UNALIGNED*)(aNewCode+0x39+lpHookEntry->nNewStubSize)) = (ULONGLONG)(lpHookEntry->lpOrigProc + lpHookEntry->nOriginalStubSize); break; #endif //_M_X64 } if (NktHookLibHelpers::WriteMem(cProcEntry->GetHandle(), lpHookEntry->lpInjCodeAndData, aNewCode, lpHookEntry->nInjCodeAndDataSize) == FALSE) { dwOsErr = ERROR_ACCESS_DENIED; continue; } //replace original proc with a jump dw = (DWORD)(lpHookEntry->lpInjCodeAndData+8) - (DWORD)(lpHookEntry->lpOrigProc) - 5; lpHookEntry->aJumpStub[0] = 0xE9; //JMP lpHookEntry->aJumpStub[1] = (BYTE)( dw & 0xFF); lpHookEntry->aJumpStub[2] = (BYTE)((dw >> 8) & 0xFF); lpHookEntry->aJumpStub[3] = (BYTE)((dw >> 16) & 0xFF); lpHookEntry->aJumpStub[4] = (BYTE)((dw >> 24) & 0xFF); //set id #if defined _M_IX86 lpHookEntry->nId = (SIZE_T)lpHookEntry ^ 0x34B68363UL; //odd number to avoid result of zero #elif defined _M_X64 lpHookEntry->nId = (SIZE_T)lpHookEntry ^ 0x34B68364A3CE19F3ui64; //odd number to avoid result of zero #endif //done aHookInfo[nHookIdx].nHookId = lpHookEntry->nId; aHookInfo[nHookIdx].lpCallOriginal = lpHookEntry->lpCall2Orig; } //hook new items if (dwOsErr == NO_ERROR) { NktHookLib::CNktThreadSuspend::CAutoResume cAutoResume(&(int_data->cThreadSuspender)); NktHookLib::CNktThreadSuspend::IP_RANGE sIpRanges[MAX_SUSPEND_IPRANGES]; SIZE_T k, k2, nThisRoundSuspCount; MEMORY_BASIC_INFORMATION sMbi; HOOK_INFO sHooks[64]; DWORD dwNewProt, dwOldProt; for (nHookIdx=nThisRoundSuspCount=0; nHookIdx<nCount && dwOsErr==NO_ERROR; ) { if (nThisRoundSuspCount == 0) { //suspend threads nThisRoundSuspCount = (nCount-nHookIdx > MAX_SUSPEND_IPRANGES) ? MAX_SUSPEND_IPRANGES : (nCount-nHookIdx); for (k=0; k<nThisRoundSuspCount; k++) { sIpRanges[k].nStart = (SIZE_T)(lpNewEntriesList[nHookIdx+k]->lpOrigProc); sIpRanges[k].nEnd = sIpRanges[k].nStart + HOOKENG_JUMP_TO_HOOK_SIZE; } dwOsErr = NO_ERROR; if (int_data->sOptions.bSuspendThreads != FALSE) dwOsErr = int_data->cThreadSuspender.SuspendAll(cProcEntry->GetPid(), sIpRanges, nThisRoundSuspCount); if (dwOsErr != NO_ERROR) { err_uninstall_and_destroy: for (nHookIdx=k2=0; nHookIdx<nCount; nHookIdx++) { if (lpNewEntriesList[nHookIdx]->nInstalledCode != 0) { sHooks[k2++].nHookId = lpNewEntriesList[nHookIdx]->nId; if (k2 >= X_ARRAYLEN(sHooks)) { Unhook(sHooks, k2); k2 = 0; } } } if (k2 > 0) Unhook(sHooks, k2); continue; } } for (k=0; k<nThisRoundSuspCount; k++) { k2 = 0; lpPtr = lpNewEntriesList[nHookIdx+k]->lpOrigProc; NktHookLibHelpers::MemSet(&sMbi, 0, sizeof(sMbi)); nNtStatus = NktHookLib::NktNtQueryVirtualMemory(cProcEntry->GetHandle(), lpPtr, MyMemoryBasicInformation, &sMbi, sizeof(sMbi), &k2); dwNewProt = PAGE_EXECUTE_WRITECOPY; if (NT_SUCCESS(nNtStatus)) { switch (sMbi.Protect & 0xFF) { case PAGE_NOACCESS: case PAGE_READONLY: case PAGE_READWRITE: dwNewProt = PAGE_READWRITE; break; case PAGE_WRITECOPY: dwNewProt = PAGE_WRITECOPY; break; case PAGE_EXECUTE: case PAGE_EXECUTE_READ: case PAGE_EXECUTE_READWRITE: dwNewProt = PAGE_EXECUTE_READWRITE; break; } } //change protection if needed if (dwNewProt != (sMbi.Protect & 0xFF)) { dwOldProt = 0; nSize = HOOKENG_JUMP_TO_HOOK_SIZE; nNtStatus = NktHookLib::NktNtProtectVirtualMemory(cProcEntry->GetHandle(), (PVOID*)&lpPtr, &nSize, dwNewProt, &dwOldProt); if (!NT_SUCCESS(nNtStatus)) { dwOsErr = NktHookLib::NktRtlNtStatusToDosError(nNtStatus); if (dwOsErr == 0) dwOsErr = ERROR_NOT_SUPPORTED; int_data->cThreadSuspender.ResumeAll(); goto err_uninstall_and_destroy; } } //replace each entry point k2 = (NktHookLibHelpers::WriteMem(cProcEntry->GetHandle(), lpNewEntriesList[nHookIdx+k]->lpOrigProc, lpNewEntriesList[nHookIdx+k]->aJumpStub, HOOKENG_JUMP_TO_HOOK_SIZE) != FALSE) ? 1 : 0; //restore protection if (dwNewProt != (sMbi.Protect & 0xFF)) { lpPtr = lpNewEntriesList[nHookIdx+k]->lpOrigProc; nSize = HOOKENG_JUMP_TO_HOOK_SIZE; NktHookLib::NktNtProtectVirtualMemory(cProcEntry->GetHandle(), (PVOID*)&lpPtr, &nSize, dwOldProt, &dw); } //check write operation result if (k2 == 0) { dwOsErr = ERROR_ACCESS_DENIED; int_data->cThreadSuspender.ResumeAll(); goto err_uninstall_and_destroy; } //flush instruction cache NktHookLib::NktNtFlushInstructionCache(cProcEntry->GetHandle(), lpNewEntriesList[nHookIdx+k]->lpOrigProc, 32); //mark as installed lpNewEntriesList[nHookIdx+k]->nInstalledCode = 1; } //advance count nHookIdx += nThisRoundSuspCount; //check if we can proceed with the next hook with this nThisRoundSuspCount = 0; for (k=nHookIdx; k<nCount; k++) { k2 = (SIZE_T)(lpNewEntriesList[k]->lpOrigProc); if (int_data->cThreadSuspender.CheckIfThreadIsInRange(k2, k2+HOOKENG_JUMP_TO_HOOK_SIZE) == FALSE) break; nThisRoundSuspCount++; } if (nThisRoundSuspCount == 0) { //resume threads int_data->cThreadSuspender.ResumeAll(); } } } //done... move to the final list or delete on error if (dwOsErr == NO_ERROR) { for (nHookIdx=0; nHookIdx<nCount; nHookIdx++) { lpNewEntriesList[nHookIdx]->dwFlags = dwFlags; if (int_data->sOptions.bOutputDebug != FALSE) { NktHookLibHelpers::DebugPrint("NktHookLib: Hook installed. Proc @ 0x%IX -> 0x%IX (Stub @ 0x%IX) \r\n", (SIZE_T)(lpNewEntriesList[nHookIdx]->lpOrigProc), (SIZE_T)(lpNewEntriesList[nHookIdx]->lpNewProc), (SIZE_T)(lpNewEntriesList[nHookIdx]->lpInjCodeAndData)); } int_data->cHooksList.PushTail(lpNewEntriesList[nHookIdx]); } } else { for (nHookIdx=0; nHookIdx<nCount; nHookIdx++) delete lpNewEntriesList[nHookIdx]; } if (lpNewEntriesList != NULL) NktHookLibHelpers::MemFree(lpNewEntriesList); }
VOID CNktWorkerThread::ThreadProc() { NKT_ASSERT(lpStartRoutine != NULL); lpStartRoutine(this, lpParam); return; }