bool __stdcall KdComDispatcher::KdSendPacket(ULONG PacketType, PKD_BUFFER FirstBuffer, PKD_BUFFER SecondBuffer, PKD_CONTEXT KdContext) { m_PacketLogger.OnSendReceivePacket(g_pReporter->GetStatusPointer()->LogAllPackets != 0, true, PacketType, FirstBuffer, SecondBuffer, KdContext); if (!m_RandomValidContextRecord.GetSize() && (PacketType == 2)) if (FirstBuffer && FirstBuffer->pData) if (*((unsigned *)FirstBuffer->pData) == DbgKdReadControlSpaceApi) if (SecondBuffer && SecondBuffer->Length && SecondBuffer->pData) { m_RandomValidContextRecord.EnsureSize(SecondBuffer->Length); memcpy(m_RandomValidContextRecord.GetData(), SecondBuffer->pData, SecondBuffer->Length); m_RandomValidContextRecord.SetSize(SecondBuffer->Length); } #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION g_pReporter->GetStatusPointer()->OSDetected = true; #endif #ifdef KDCLIENT_ENABLE_TRACE_ASSIST switch (PacketType) { case KdPacketType3: if (FirstBuffer && SecondBuffer && (*((ULONG *)FirstBuffer->pData) == 0x3230)) { FixVerifierUnicodeReport(SecondBuffer); if (g_pReporter->GetStatusPointer()->TraceAssistUpdatePending) { m_TraceAssistant.ReloadParams(); g_pReporter->GetStatusPointer()->TraceAssistUpdatePending = false; } if (m_TraceAssistant.TraceLine((char *)SecondBuffer->pData, SecondBuffer->Length)) { #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION g_pReporter->GetStatusPointer()->BytesSent += (FirstBuffer->Length + SecondBuffer->Length + sizeof(KD_PACKET_HEADER) + 1); g_pReporter->GetStatusPointer()->PacketsSent++; #endif return true; } } break; } #endif ASSERT(FirstBuffer); KD_PACKET_HEADER header; unsigned checksum = KdpComputeChecksum(FirstBuffer->pData, FirstBuffer->Length); unsigned totalLength = FirstBuffer->Length; if (SecondBuffer) { totalLength += SecondBuffer->Length; checksum += KdpComputeChecksum(SecondBuffer->pData, SecondBuffer->Length); } header.Signature = '0000'; header.PacketType = (USHORT)PacketType; header.Checksum = checksum; header.TotalDataLength = totalLength; KdCompNumberRetries = KdCompRetryCount; if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom)) { TCHAR tsz[512]; _sntprintf(tsz, __countof(tsz), _T("Sending normal packet (type = %d, id = %08X, len = %d)...\r\n"), header.PacketType, KdCompNextPacketIdToSend, header.TotalDataLength); g_pReporter->LogLineIfEnabled(tsz); } unsigned RetryNumber = 0; for (;;) { //SIMPLIFY: The classical KdCompRetryCount-related scheme is replaced by infinite retrying with //fallback to VM. In case we get looped here (inside GuestRPC dispatcher), VMWare hangs. //if (!KdCompNumberRetries) if (RetryNumber) { /*bool bCanDrop = false; switch (PacketType) { case KdPacketType3: bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3230; break; case KdPacketType7: bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3031; break; case KdPacketType11: bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3430; break; } if (bCanDrop)*/ { //As returning false will result in automatic packet retrying, we do not loop here forever any more, if a packet send failed. //Instead, we return false and wait for this call to be retried by KDVMWARE.DLL. KD_DEBUGGER_NOT_PRESENT = TRUE; KdResetPacketNumbering(true); if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom)) g_pReporter->LogLineIfEnabled(_T("Acknowledgment timeout\r\n")); return false; } } if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll)) { TCHAR tsz[512]; _sntprintf(tsz, __countof(tsz), _T("KdSendPacket(): sending (%d)\r\n"), header.PacketType); g_pReporter->LogLineIfEnabled(tsz); } header.PacketID = KdCompNextPacketIdToSend; m_Pipe.Send(&header, sizeof(header)); m_Pipe.Send(FirstBuffer->pData, FirstBuffer->Length); if (SecondBuffer && SecondBuffer->Length) m_Pipe.Send(SecondBuffer->pData, SecondBuffer->Length); unsigned char ch = 0xAA; m_Pipe.Send(&ch, 1); #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected(); #endif switch (KdReceivePacket(KdPacketAcknowledge, NULL, NULL, NULL, KdContext, NULL)) { case KD_RECV_CODE_TIMEOUT: KdCompNumberRetries--; RetryNumber++; break; case KD_RECV_CODE_OK: KdCompNextPacketIdToSend &= 0xFFFFFFF7; KdCompRetryCount = KdContext->RetryCount; if (++m_PacketsSentFromLastReset >= KdMinPacketCountForSuccessfulLink) m_SequentialResetCount = 0; if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom)) g_pReporter->LogLineIfEnabled(_T("Packet acknowledged\r\n")); #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION g_pReporter->GetStatusPointer()->BytesSent += (header.TotalDataLength + sizeof(KD_PACKET_HEADER) + 1); g_pReporter->GetStatusPointer()->PacketsSent++; #endif return true; } } }
//WARNING! This code is experimental and serves as a workaround for MS debug engine hanging, when a remote machine stops responding. //The main idea is to send a 'NTOSKRNL unloaded' message when a VM process is terminating, or DLL is unloading. //Everything works perfectly, if the target is running at that time, however, if the target is stopped, the message will not be //processed normally, as the debugger does not expect it. Simple resending does not help, as too many copies of that message //may drive WinDbg into a permanent 'out of sync' state. This code tries to detect, how and when to send such message. void KdComDispatcher::SimulateWindowsTermination() { SystemTerminationPacket packet; InitializeSystemTerminationPacket(packet); m_Notifier.SignalSessionTerminationAndWaitForAck(); m_PacketLogger.OnWindowsTerminationSimulated(); KD_BUFFER first = {0,}, second = {0,}; first.Length = sizeof(packet); first.pData = (PUCHAR)packet; KD_CONTEXT ctx = {0,}; bool RequireAdditionalTerminationPacket = false; while (!KdSendPacket(7, &first, &second, &ctx)) { if (!m_Pipe.IsClientConnected()) { m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected"); return; } RequireAdditionalTerminationPacket = true; //Target is not running and debugger is not ready to receive a packet. We'll repeat it later. } //Packet type 2 always has buffer 1 of size 0x38 static char szBuf1[0x38], szBuf2[KdMaxBufferSize]; ULONG unused = 0; BazisLib::DateTime dtStart = BazisLib::DateTime::Now(); const int NoControlPacketTimeout = 1500; for (;;) { if (!m_Pipe.IsClientConnected()) { m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected"); return; } first.MaxLength = sizeof(szBuf1); first.pData = (PUCHAR)szBuf1; second.MaxLength = sizeof(szBuf2); second.pData = (PUCHAR)szBuf2; KD_RECV_CODE code = KdReceivePacket(2, &first, &second, &unused, &ctx, NULL); if (!m_Pipe.IsClientConnected()) { m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected"); return; } if (code == KD_RECV_CODE_TIMEOUT) { if ((BazisLib::DateTime::Now() - dtStart).GetTotalMilliseconds() < NoControlPacketTimeout) continue; m_PacketLogger.OnWindowsTerminationSimDone("timeout"); return; } if (code != KD_RECV_CODE_OK) continue; if ((*((unsigned *)szBuf1) == DbgKdContinueApi2) || (*((unsigned *)szBuf1) == DbgKdContinueApi)) { if (RequireAdditionalTerminationPacket) { first.Length = sizeof(packet); first.pData = (PUCHAR)packet; if (!KdSendPacket(7, &first, &second, &ctx)) { m_PacketLogger.OnWindowsTerminationSimDone("type 7 packet not acknowledged"); return; } RequireAdditionalTerminationPacket = false; continue; } m_PacketLogger.OnWindowsTerminationSimDone("continue command received"); return; } ((unsigned *)szBuf1)[2] = 0; //Set ReturnStatus in DBGKD_MANIPULATE_STATE64 to STATUS_SUCCESS if ((*((unsigned *)szBuf1) == DbgKdReadControlSpaceApi)) { second.Length = (USHORT)m_RandomValidContextRecord.GetSize(); second.pData = (PUCHAR)m_RandomValidContextRecord.GetData(); } if ((*((unsigned *)szBuf1) == DbgKdSetContextApi)) { second.Length = 0; second.pData = NULL; } //If we have received a type 2 packet, other than 'continue', simply echo the packet back. //Packet-level layer of WinDbg will return a successful status code, while higher level will //most likely treat it as an error. However, then WinDbg will receive our termination packet //and will disconnect from session. KdSendPacket(2, &first, &second, &ctx); RequireAdditionalTerminationPacket = true; } m_PacketLogger.OnWindowsTerminationSimDone("unexpected"); }
BOOLEAN NTAPI KdpPromptString(IN PSTRING PromptString, IN PSTRING ResponseString) { STRING Data, Header; DBGKD_DEBUG_IO DebugIo; ULONG Length = PromptString->Length; KDSTATUS Status; /* Copy the string to the message buffer */ RtlCopyMemory(KdpMessageBuffer, PromptString->Buffer, PromptString->Length); /* Make sure we don't exceed the KD Packet size */ if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) { /* Normalize length */ Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO); } /* Build the packet header */ DebugIo.ApiNumber = DbgKdGetStringApi; DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel; DebugIo.Processor = KeGetCurrentPrcb()->Number; DebugIo.u.GetString.LengthOfPromptString = Length; DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength; Header.Length = sizeof(DBGKD_DEBUG_IO); Header.Buffer = (PCHAR)&DebugIo; /* Build the data */ Data.Length = PromptString->Length; Data.Buffer = KdpMessageBuffer; /* Send the packet */ KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext); /* Set the maximum lengths for the receive */ Header.MaximumLength = sizeof(DBGKD_DEBUG_IO); Data.MaximumLength = sizeof(KdpMessageBuffer); /* Enter receive loop */ do { /* Get our reply */ Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &Length, &KdpContext); /* Return TRUE if we need to resend */ if (Status == KdPacketNeedsResend) return TRUE; /* Loop until we succeed */ } while (Status != KdPacketReceived); /* Don't copy back a larger response than there is room for */ Length = min(Length, ResponseString->MaximumLength); /* Copy back the string and return the length */ RtlCopyMemory(ResponseString->Buffer, KdpMessageBuffer, Length); ResponseString->Length = (USHORT)Length; /* Success; we don't need to resend */ return FALSE; }