• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

E:/PROJECTS/cvsed/mixed/VIRTUA~1/rpcdispatch/kdcomdisp.cpp

Go to the documentation of this file.
00001 
00007 #include "stdafx.h"
00008 #include "kdcomdisp.h"
00009 #include "reporter.h"
00010 
00011 /*
00012         The code in this module implements the KDCOM protocol (protocol used to debug kernel over COM port).
00013         When some things are simplified/changed compared to full protocol description, the comment SIMPLIFY:
00014         is added.
00015 */
00016 
00017 
00018 KD_RECV_CODE KdComDispatcher::KdCompReceivePacketLeader(ULONG /*PacketType*/,
00019                                                                                                             ULONG *pSignature,
00020                                                                                                             PKD_CONTEXT pContext)
00021 {
00022         ASSERT(pContext && pSignature);
00023         unsigned repeatCount = 0;
00024         char ch = 0, lastCh = 0;
00025         bool BreakInRequested = false;
00026         for (;;)
00027         {
00028                 if (m_Pipe.Receive(&ch, 1) != 1)
00029                 {
00030                         if (BreakInRequested)
00031                         {
00032                                 pContext->BreakInRequested = TRUE;
00033                                 return KD_RECV_CODE_FAILED;
00034                         }
00035                         else
00036                         {
00037                                 unsigned err = m_Pipe.GetLastOperationError();
00038                                 switch (err)
00039                                 {
00040                                 case ERROR_PIPE_LISTENING:
00041                                 case ERROR_PIPE_NOT_CONNECTED:
00042                                 case ERROR_NO_DATA:
00043                                         Sleep(10);
00044                                         break;
00045                                 }
00046                                 return KD_RECV_CODE_TIMEOUT;
00047                         }
00048                 }
00049                 switch (ch)
00050                 {
00051                 case '0':
00052                 case 'i':
00053                         break;
00054                 case 'b':
00055                         BreakInRequested = true;
00056                         //no break!
00057                 default:
00058                         g_pReporter->GetStatusPointer()->BytesDropped++;
00059                         repeatCount = 0;
00060                         continue;
00061                 }
00062 
00063                 if (!repeatCount)
00064                 {
00065                         lastCh = ch;
00066                         repeatCount++;
00067                         continue;
00068                 }
00069 
00070                 if (ch != lastCh)
00071                         repeatCount = 1, lastCh = ch;
00072                 else
00073                 {
00074                         if (++repeatCount >= 4)
00075                         {
00076                                 if (BreakInRequested)
00077                                         pContext->BreakInRequested = TRUE;
00078                                 *pSignature = (ch == '0') ? '0000' : 'iiii';
00079                                 KD_DEBUGGER_NOT_PRESENT = false;
00080                                 return KD_RECV_CODE_OK;
00081                         }
00082                 }
00083         }
00084 }
00085 
00086 void KdComDispatcher::KdpSendControlPacket(ULONG PacketType, ULONG NextPacketId)
00087 {
00088         static char __unused[sizeof(KD_PACKET_HEADER) == 16];
00089         static unsigned LastSentID = 0;
00090         KD_PACKET_HEADER hdr;
00091         hdr.Signature = 'iiii';
00092         hdr.PacketType = (USHORT)PacketType;
00093         hdr.TotalDataLength = 0;
00094         if (NextPacketId)
00095                 hdr.PacketID = LastSentID = NextPacketId;
00096         else
00097                 hdr.PacketID = LastSentID;
00098         hdr.Checksum = 0;
00099         if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00100         {
00101                         TCHAR tsz[512];
00102                         _sntprintf(tsz, __countof(tsz), _T("Sending control packet (type = %d, id = %08X)... "),
00103                                         hdr.PacketType,
00104                                         hdr.PacketID);
00105                         g_pReporter->LogLineIfEnabled(tsz);
00106         }
00107         m_Pipe.Send(&hdr, sizeof(hdr));
00108         if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00109         {
00110                 g_pReporter->LogLineIfEnabled(_T("ok\r\n"));
00111         }
00112 }
00113         
00114 KD_RECV_CODE __stdcall KdComDispatcher::KdReceivePacket(ULONG PacketType,
00115                                                                                                                 PKD_BUFFER FirstBuffer,
00116                                                                                                                 PKD_BUFFER SecondBuffer,
00117                                                                                                                 PULONG PayloadBytes,
00118                                                                                                                 PKD_CONTEXT KdContext)
00119 {
00120 /*#ifdef _DEBUG
00121         if (PacketType == 0x1234)
00122         {
00123                 memset(FirstBuffer->pData, '+', FirstBuffer->MaxLength);
00124                 memset(SecondBuffer->pData, '*', SecondBuffer->MaxLength);
00125                 FirstBuffer->Length = FirstBuffer->MaxLength;
00126                 SecondBuffer->Length = SecondBuffer->MaxLength;
00127                 *PayloadBytes = SecondBuffer->MaxLength;
00128                 return KD_RECV_CODE_OK;
00129         }
00130 #endif*/
00131         if (0)
00132                 SimulateWindowsTermination();
00133 
00134         ASSERT(KdContext);
00135 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00136         g_pReporter->GetStatusPointer()->OSDetected = true;
00137 #endif
00138 
00139         if (KdTraceEnabled)
00140         {
00141                 TCHAR tsz[512];
00142                 switch (PacketType)
00143                 {
00144                 case KdPacketAcknowledge:
00145                         if (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom)
00146                         {
00147                                 _sntprintf(tsz, __countof(tsz), _T("KdReceivePacket(%d)\r\n"),
00148                                                         PacketType);
00149                                 g_pReporter->LogLineIfEnabled(tsz);
00150                         }
00151                         break;
00152                 case KdCheckForAnyPacket:
00153                         if (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll)
00154                                 g_pReporter->LogLineIfEnabled(_T("KdReceivePacket(): checking for new packets\r\n"));
00155                         break;
00156                 }
00157         }
00158 
00159 
00160         if (PacketType == KdCheckForAnyPacket)
00161         {
00162                 bool hasData = m_Pipe.HasDataInBuffer();
00163 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00164                 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00165 #endif
00166                 if (!hasData)
00167                         return KD_RECV_CODE_TIMEOUT;
00168                 char ch = 0;
00169                 if (!m_Pipe.Receive(&ch, 1))
00170                         return KD_RECV_CODE_TIMEOUT;
00171                 if (ch != 'b')
00172                         return KD_RECV_CODE_TIMEOUT;
00173                 return KD_RECV_CODE_OK;
00174         }
00175 
00176         if (FirstBuffer)
00177                 FirstBuffer->Length = 0;
00178         if (SecondBuffer)
00179                 SecondBuffer->Length = 0;
00180         
00181         for (;;)
00182         {
00183                 KD_PACKET_HEADER header = {0,};
00184                 KD_RECV_CODE status = KdCompReceivePacketLeader(PacketType, &header.Signature, KdContext);
00185 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00186                 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00187 #endif
00188                 if (status != KD_RECV_CODE_TIMEOUT)
00189                         KdCompNumberRetries = KdCompRetryCount;
00190                 if (status != KD_RECV_CODE_OK)
00191                 {
00192                         if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll))
00193                         {
00194                                 TCHAR tsz[512];
00195                                 _sntprintf(tsz, __countof(tsz), _T("KdCompReceivePacketLeader(): %s\r\n"), (status == KD_RECV_CODE_TIMEOUT) ? _T("timeout") : _T("error"));
00196                                 g_pReporter->LogLineIfEnabled(tsz);
00197                         }
00198                         return status;
00199                 }
00200                 size_t size = sizeof(header) - sizeof(header.Signature);
00201                 //SIMPLIFY: We do not have 'failed' condition any more. Only timeout, so receive the rest of the packet in a single call.
00202                 if (m_Pipe.Receive(&header.PacketType, size, false) != size)
00203                         return KD_RECV_CODE_TIMEOUT;
00204 
00205                 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00206                 {
00207                         TCHAR tsz[512];
00208                         _sntprintf(tsz, __countof(tsz), _T("Got packet (expected %d) (signature = %08X, id = %08X, type = %d, data = %d, checksum = %d)\r\n"),
00209                                                 PacketType,
00210                                                 header.Signature,
00211                                                 header.PacketID,
00212                                                 header.PacketType,
00213                                                 header.TotalDataLength,
00214                                                 header.Checksum);
00215                         g_pReporter->LogLineIfEnabled(tsz);
00216                 }
00217 
00218                 //SIMPLIFY: Originally this check should be performed just after receiving the packet type
00219                 if ((header.Signature == 'iiii') && (header.PacketType == KdPacketRetryRequest))
00220                         return KD_RECV_CODE_FAILED;
00221                 
00222                 if (header.Signature == 'iiii')
00223                 {
00224                         switch (header.PacketType)
00225                         {
00226                         case KdPacketAcknowledge:
00227                                 if ((PacketType == 4) && (header.PacketID == (KdCompNextPacketIdToSend & 0xFFFFF7FF)))
00228                                 {
00229                                         KdCompNextPacketIdToSend ^= 1;
00230                                         return KD_RECV_CODE_OK;
00231                                 }
00232                                 break;
00233                         case KdPacketResynchronize:
00234                                 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00235                                 {
00236                                         TCHAR tsz[512];
00237                                         _sntprintf(tsz, __countof(tsz), _T("Resync (got %d, sent %d, seq cnt %d)\r\n"),
00238                                                         m_PacketsGotFromLastReset,
00239                                                         m_PacketsSentFromLastReset,
00240                                                         m_SequentialResetCount);
00241                                         g_pReporter->LogLineIfEnabled(tsz);
00242                                 }
00243                                 if ((m_PacketsGotFromLastReset <= KdMinPacketCountForSuccessfulLink) && (m_PacketsSentFromLastReset <= KdMinPacketCountForSuccessfulLink))
00244                                         m_SequentialResetCount++;
00245                                 KdResetPacketNumbering(false);
00246                                 /*
00247                                         The following line serves as a workaround for the 'resync bounce' bug.
00248                                         The KDCOM protocol was originally designed for normal, hardware COM ports. It means, that
00249                                         an operating system that gained control over the COM port did not receive the previously
00250                                         sent data. In case of named pipes, the following scenario usually takes place:
00251                                         1) KD opens the pipe and starts sending RESET packets every second
00252                                         2) OS loads and starts receiving packets
00253                                         3) OS receives N RESET packets and sends N RESET acknowledgments
00254                                         4) KD receives the first RESET ack packet and starts normal operation (sends something)
00255                                         5) OS receives normal packet
00256                                         6) KD receives next RESET ack, performs resynchronization and... sends a RESET packet
00257                                         7) OS responds with a RESET ack
00258                                         8) KD reads another previous RESET ack and continues
00259                                         9) ...
00260                                         A) KD receives another RESET from OS and goes to step 6
00261                                         The loop is never broken, the RESET packets continue being bounced from KD to OS and back
00262 
00263                                         By discarding all buffered data from the named pipe when a RESET occurs, the problem is solved.
00264                                 */
00265                                 m_Pipe.DiscardBufferedData();
00266                                 KdpSendControlPacket(KdPacketResynchronize, 0);
00267 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00268                                 g_pReporter->GetStatusPointer()->ResyncCount++;
00269 #endif
00270                                 return KD_RECV_CODE_FAILED;
00271                         case KdPacketRetryRequest:
00272                                 return KD_RECV_CODE_FAILED;
00273                         }
00274                 }
00275                 else    //header.Signature == '0000'
00276                 {
00277                         if (PacketType != KdPacketAcknowledge)
00278                         {
00279                                 if (header.TotalDataLength <= KdMaxBufferSize)
00280                                 {
00281                                         if (header.TotalDataLength >= FirstBuffer->MaxLength)
00282                                         {
00283                                                 *PayloadBytes = header.TotalDataLength - FirstBuffer->MaxLength;
00284                                                 if (m_Pipe.Receive(FirstBuffer->pData, FirstBuffer->MaxLength, false) == FirstBuffer->MaxLength)
00285                                                 {
00286                                                         FirstBuffer->Length = FirstBuffer->MaxLength;
00287                                                         if (!(*PayloadBytes) || (m_Pipe.Receive(SecondBuffer->pData, *PayloadBytes, false) == *PayloadBytes))
00288                                                         {
00289                                                                 SecondBuffer->Length = (USHORT)*PayloadBytes;
00290                                                                 unsigned char ch = 0;
00291                                                                 if (m_Pipe.Receive(&ch, 1) && (ch == 0xAA))
00292                                                                 {
00293                                                                         if (PacketType != header.PacketType)
00294                                                                         {
00295                                                                                 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00296                                                                                 continue;
00297                                                                         }
00298                                                                         else if ((header.PacketID == 0x80800000) || (header.PacketID == 0x80800001))
00299                                                                         {
00300                                                                                 if (header.PacketID != KdCompPacketIdExpected)
00301                                                                                 {
00302                                                                                         KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00303                                                                                         continue;
00304                                                                                 }
00305                                                                         }
00306                                                                         int checksum = KdpComputeChecksum(FirstBuffer->pData, FirstBuffer->Length);
00307                                                                         checksum += KdpComputeChecksum(SecondBuffer->pData, SecondBuffer->Length);
00308                                                                         if (checksum == header.Checksum)
00309                                                                         {
00310                                                                                 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00311                                                                                 KdCompPacketIdExpected ^= 1;
00312                                                                                 if (++m_PacketsGotFromLastReset >= KdMinPacketCountForSuccessfulLink)
00313                                                                                         m_SequentialResetCount = 0;
00314 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00315                                                                                 g_pReporter->GetStatusPointer()->BytesReceived += header.TotalDataLength + sizeof(KD_PACKET_HEADER) + 1;
00316                                                                                 g_pReporter->GetStatusPointer()->PacketsReceived++;
00317 #endif
00318                                                                                 m_PacketLogger.OnSendReceivePacket(g_pReporter->GetStatusPointer()->LogAllPackets != 0,
00319                                                                                         false,
00320                                                                                         PacketType,
00321                                                                                         FirstBuffer,
00322                                                                                         SecondBuffer,
00323                                                                                         KdContext);
00324 
00325                                                                                 return KD_RECV_CODE_OK;
00326                                                                         }
00327                                                                 }
00328                                                         }
00329 
00330                                                 }
00331                                         }
00332                                 }
00333                                 KdpSendControlPacket(KdPacketRetryRequest, 0);
00334                                 continue;
00335                         }
00336                         if (header.PacketID == KdCompPacketIdExpected)
00337                         {
00338                                 KdpSendControlPacket(KdPacketRetryRequest, 0);
00339                                 KdCompNextPacketIdToSend ^= 1;
00340                                 return KD_RECV_CODE_OK;
00341                         }
00342                         else
00343                         {
00344                                 KdpSendControlPacket(KdPacketAcknowledge, header.PacketID);
00345                                 continue;
00346                         }
00347                 }
00348         }
00349 
00350         return KD_RECV_CODE_TIMEOUT;
00351 }
00352 
00353 static bool FixVerifierUnicodeReport(PKD_BUFFER pDataBuffer)
00354 {
00355         char *pText = (char *)pDataBuffer->pData;
00356         char *pZero = NULL;
00357         char *pEnd = pText + pDataBuffer->Length;
00358         for (int i = 0; i < pDataBuffer->Length; i++)
00359                 if (!pText[i])
00360                 {
00361                         pZero = &pText[i];
00362                         break;
00363                 }
00364         if (!pZero)
00365                 return false;
00366         if (!strstr(pText, "Driver Verifier:"))
00367                 return false;
00368 
00369         char *pSrc = pZero + 1;
00370         while (pSrc < pEnd)
00371         {
00372                 if (!pSrc[0])
00373                 {
00374                         pSrc++;
00375                         continue;
00376                 }
00377                 *pZero = *pSrc;
00378 
00379                 pZero++;
00380                 pSrc++;
00381         }
00382         //We cannot modify the length of the packet, as it seems to be specified somewhere else additionally.
00383         //Instead, we exploit the same effect of chopping of all string characters past first NULL char.
00384         if (pZero < pEnd)
00385                 *pZero = 0;     
00386         return true;
00387 }
00388 
00389 bool __stdcall KdComDispatcher::KdSendPacket(ULONG PacketType,
00390                                                                                          PKD_BUFFER FirstBuffer,
00391                                                                                          PKD_BUFFER SecondBuffer,
00392                                                                                          PKD_CONTEXT KdContext)
00393 {
00394         m_PacketLogger.OnSendReceivePacket(g_pReporter->GetStatusPointer()->LogAllPackets != 0,
00395                                                                            true,
00396                                                                            PacketType,
00397                                                                            FirstBuffer,
00398                                                                            SecondBuffer,
00399                                                                            KdContext);
00400 
00401 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00402         g_pReporter->GetStatusPointer()->OSDetected = true;
00403 #endif
00404 #ifdef KDCLIENT_ENABLE_TRACE_ASSIST
00405         switch (PacketType)
00406         {
00407         case KdPacketType3:
00408                 if (FirstBuffer && SecondBuffer && (*((ULONG *)FirstBuffer->pData) == 0x3230))
00409                 {
00410                         FixVerifierUnicodeReport(SecondBuffer);
00411                         if (g_pReporter->GetStatusPointer()->TraceAssistUpdatePending)
00412                         {
00413                                 m_TraceAssistant.ReloadParams();
00414                                 g_pReporter->GetStatusPointer()->TraceAssistUpdatePending = false;                              
00415                         }
00416                         if (m_TraceAssistant.TraceLine((char *)SecondBuffer->pData, SecondBuffer->Length))
00417                         {
00418 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00419                                 g_pReporter->GetStatusPointer()->BytesSent += (FirstBuffer->Length + SecondBuffer->Length + sizeof(KD_PACKET_HEADER) + 1);
00420                                 g_pReporter->GetStatusPointer()->PacketsSent++;
00421 #endif
00422                                 return true;
00423                         }
00424                 }
00425                 break;
00426         }
00427 #endif
00428 
00429         ASSERT(FirstBuffer);
00430         KD_PACKET_HEADER header;
00431         unsigned checksum = KdpComputeChecksum(FirstBuffer->pData, FirstBuffer->Length);
00432         unsigned totalLength = FirstBuffer->Length;
00433         if (SecondBuffer)
00434         {
00435                 totalLength += SecondBuffer->Length;
00436                 checksum += KdpComputeChecksum(SecondBuffer->pData, SecondBuffer->Length);
00437         }
00438         
00439         header.Signature = '0000';
00440         header.PacketType = (USHORT)PacketType;
00441         header.Checksum = checksum;
00442         header.TotalDataLength = totalLength;
00443         KdCompNumberRetries = KdCompRetryCount;
00444 
00445         if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00446         {
00447                         TCHAR tsz[512];
00448                         _sntprintf(tsz, __countof(tsz), _T("Sending normal packet (type = %d, id = %08X, len = %d)...\r\n"),
00449                                         header.PacketType,
00450                                         KdCompNextPacketIdToSend,
00451                                         header.TotalDataLength);
00452                         g_pReporter->LogLineIfEnabled(tsz);
00453         }
00454 
00455         unsigned RetryNumber = 0;
00456 
00457         for (;;)
00458         {
00459                 //SIMPLIFY: The classical KdCompRetryCount-related scheme is replaced by infinite retrying with
00460                 //fallback to VM. In case we get looped here (inside GuestRPC dispatcher), VMWare hangs.
00461                 //if (!KdCompNumberRetries)
00462                 if (RetryNumber)
00463                 {
00464                         /*bool bCanDrop = false;
00465                         switch (PacketType)
00466                         {
00467                         case KdPacketType3:
00468                                 bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3230;
00469                                 break;
00470                         case KdPacketType7:
00471                                 bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3031;
00472                                 break;
00473                         case KdPacketType11:
00474                                 bCanDrop = *((ULONG *)FirstBuffer->pData) == 0x3430;
00475                                 break;
00476                         }
00477                         if (bCanDrop)*/
00478                         {
00479                                 //As returning false will result in automatic packet retrying, we do not loop here forever any more, if a packet send failed.
00480                                 //Instead, we return false and wait for this call to be retried by KDVMWARE.DLL.
00481                                 KD_DEBUGGER_NOT_PRESENT = TRUE;
00482                                 KdResetPacketNumbering(true);
00483                                 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00484                                         g_pReporter->LogLineIfEnabled(_T("Acknowledgment timeout\r\n"));
00485                                 return false;
00486                         }
00487                 }
00488 
00489                 if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdComAll))
00490                 {
00491                                 TCHAR tsz[512];
00492                                 _sntprintf(tsz, __countof(tsz), _T("KdSendPacket(): sending (%d)\r\n"),
00493                                                 header.PacketType);
00494                                 g_pReporter->LogLineIfEnabled(tsz);
00495                 }
00496 
00497                 header.PacketID = KdCompNextPacketIdToSend;
00498                 m_Pipe.Send(&header, sizeof(header));
00499                 m_Pipe.Send(FirstBuffer->pData, FirstBuffer->Length);
00500                 if (SecondBuffer && SecondBuffer->Length)
00501                         m_Pipe.Send(SecondBuffer->pData, SecondBuffer->Length);
00502                 unsigned char ch = 0xAA;
00503                 m_Pipe.Send(&ch, 1);
00504 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00505                 g_pReporter->GetStatusPointer()->DebuggerConnected = m_Pipe.IsClientConnected();
00506 #endif
00507                 switch (KdReceivePacket(KdPacketAcknowledge, NULL, NULL, NULL, KdContext))
00508                 {
00509                 case KD_RECV_CODE_TIMEOUT:
00510                         KdCompNumberRetries--;
00511                         RetryNumber++;
00512                         break;
00513                 case KD_RECV_CODE_OK:
00514                         KdCompNextPacketIdToSend &= 0xFFFFFFF7;
00515                     KdCompRetryCount = KdContext->RetryCount;
00516                         if (++m_PacketsSentFromLastReset >= KdMinPacketCountForSuccessfulLink)
00517                                 m_SequentialResetCount = 0;
00518                         if (KdTraceEnabled && (g_pReporter->GetStatusPointer()->DebugLevel >= DebugLevelTraceKdCom))
00519                                 g_pReporter->LogLineIfEnabled(_T("Packet acknowledged\r\n"));
00520 #ifdef KDCLIENT_REPORT_PERFORMANCE_INFORMATION
00521                         g_pReporter->GetStatusPointer()->BytesSent += (header.TotalDataLength + sizeof(KD_PACKET_HEADER) + 1);
00522                         g_pReporter->GetStatusPointer()->PacketsSent++;
00523 #endif
00524                         return true;
00525                 }
00526         }
00527 }
00528 
00529 void KdComDispatcher::ReportProtocolVersionError(int GuestVersion, int HostVersion)
00530 {
00531         g_pReporter->GetStatusPointer()->ProtocolMismatchStatus = MAKELONG(GuestVersion, HostVersion);
00532 }
00533 
00534 //-------------------------------------------------------------------------------------------------------------------------------------
00535 
00536 typedef unsigned SystemTerminationPacket[60];
00537 
00538 typedef struct _DBGKD_LOAD_SYMBOLS64
00539 {
00540         ULONG PathNameLength;
00541         ULONG64 BaseOfDll;
00542         ULONG64 ProcessId;
00543         ULONG CheckSum;
00544         ULONG SizeOfImage;
00545         BOOLEAN UnloadSymbols;
00546 } DBGKD_LOAD_SYMBOLS64, *PDBGKD_LOAD_SYMBOLS64;
00547 
00548 static void InitializeSystemTerminationPacket(SystemTerminationPacket Packet)
00549 {
00550         memset(Packet, -1, sizeof(SystemTerminationPacket));
00551         Packet[0] = 0x00003031; //DbgKdLoadSymbolsStateChange
00552         Packet[1] = 6;                  //ProcessorLevel
00553         Packet[2] = 1;                  //NumberOfProcessors
00554 
00555         //Thread and ProgramCounter fields are left uninitialized, as this should not matter when signalling Windows termination.
00556 
00557         const int kDbgkdLoadSymbols64Offset = 8;
00558 
00559         PDBGKD_LOAD_SYMBOLS64 pSym = (PDBGKD_LOAD_SYMBOLS64)&Packet[kDbgkdLoadSymbols64Offset];
00560 
00561         pSym->PathNameLength = 0;
00562         pSym->BaseOfDll = -1;
00563         pSym->ProcessId = 0;
00564         pSym->CheckSum = 0;
00565         pSym->SizeOfImage = 0;
00566         pSym->UnloadSymbols = TRUE;     
00567 }
00568 
00569 #define DbgKdContinueApi                    0x00003136
00570 #define DbgKdContinueApi2                   0x0000313C
00571 
00572 //WARNING! This code is experimental and serves as a workaround for MS debug engine hanging, when a remote machine stops responding.
00573 //The main idea is to send a 'NTOSKRNL unloaded' message when a VM process is terminating, or DLL is unloading.
00574 //Everything works perfectly, if the target is running at that time, however, if the target is stopped, the message will not be
00575 //processed normally, as the debugger does not expect it. Simple resending does not help, as too many copies of that message
00576 //may drive WinDbg into a permanent 'out of sync' state. This code tries to detect, how and when to send such message.
00577 void KdComDispatcher::SimulateWindowsTermination()
00578 {
00579         SystemTerminationPacket packet;
00580         InitializeSystemTerminationPacket(packet);
00581 
00582         m_PacketLogger.OnWindowsTerminationSimulated();
00583         
00584         KD_BUFFER first = {0,}, second = {0,};
00585         first.Length = sizeof(packet);
00586         first.pData = (PUCHAR)packet;
00587         KD_CONTEXT ctx = {0,};
00588 
00589         bool RequireAdditionalTerminationPacket = false;
00590 
00591         while (!KdSendPacket(7, &first, &second, &ctx))
00592         {
00593                 if (!m_Pipe.IsClientConnected())
00594                 {
00595                         m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00596                         return;
00597                 }
00598                 RequireAdditionalTerminationPacket = true;      //Target is not running and debugger is not ready to receive a packet. We'll repeat it later.
00599         }
00600 
00601         //Packet type 2 always has buffer 1 of size 0x38
00602         static char szBuf1[0x38], szBuf2[KdMaxBufferSize];
00603 
00604         ULONG unused = 0;
00605 
00606         for (;;)
00607         {
00608                 if (!m_Pipe.IsClientConnected())
00609                 {
00610                         m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00611                         return;
00612                 }
00613 
00614                 first.MaxLength = sizeof(szBuf1);
00615                 first.pData = (PUCHAR)szBuf1;
00616                 second.MaxLength = sizeof(szBuf2);
00617                 second.pData = (PUCHAR)szBuf2;
00618 
00619                 KD_RECV_CODE code = KdReceivePacket(2, &first, &second, &unused, &ctx);
00620                 if (!m_Pipe.IsClientConnected())
00621                 {
00622                         m_PacketLogger.OnWindowsTerminationSimDone("debugger disconnected");
00623                         return;
00624                 }
00625 
00626                 if (code == KD_RECV_CODE_TIMEOUT)
00627                 {
00628                         m_PacketLogger.OnWindowsTerminationSimDone("timeout");
00629                         return;
00630                 }
00631                 if (code != KD_RECV_CODE_OK)
00632                         continue;
00633 
00634                 if ((*((unsigned *)szBuf1) == DbgKdContinueApi2) || (*((unsigned *)szBuf1) == DbgKdContinueApi))
00635                 {
00636                         if (RequireAdditionalTerminationPacket)
00637                         {
00638                                 first.Length = sizeof(packet);
00639                                 first.pData = (PUCHAR)packet;
00640                                 if (!KdSendPacket(7, &first, &second, &ctx))
00641                                 {
00642                                         m_PacketLogger.OnWindowsTerminationSimDone("type 7 packet not acknowledged");
00643                                         return;
00644                                 }
00645                                 RequireAdditionalTerminationPacket = false;
00646                                 continue;
00647                         }
00648                         m_PacketLogger.OnWindowsTerminationSimDone("continue command received");
00649                         return;
00650                 }
00651 
00652                 //If we have received a type 2 packet, other than 'continue', simply echo the packet back.
00653                 //Packet-level layer of WinDbg will return a successful status code, while higher level will
00654                 //most likely treat it as an error. However, then WinDbg will receive our termination packet
00655                 //and will disconnect from session.
00656                 KdSendPacket(2, &first, &second, &ctx);
00657                 RequireAdditionalTerminationPacket = true;
00658         }
00659         m_PacketLogger.OnWindowsTerminationSimDone("unexpected");
00660 }