/*++ Copyright (c) 1990 Microsoft Corporation Module Name: debug.c Abstract: This module implements Win32 Debug APIs Author: Mark Lucovsky (markl) 06-Feb-1991 Revision History: --*/ #include "basedll.h" #pragma hdrstop #define TmpHandleHead ((PTMPHANDLES *) (&NtCurrentTeb()->DbgSsReserved[0])) // // This structure is used to preserve the strange mechanisms used by win2k and nt4 to close the handles to open processes, // threads and main image file. // typedef struct _TMPHANDLES { struct _TMPHANDLES *Next; HANDLE Thread; HANDLE Process; DWORD dwProcessId; DWORD dwThreadId; BOOLEAN DeletePending; } TMPHANDLES, *PTMPHANDLES; VOID SaveThreadHandle ( DWORD dwProcessId, DWORD dwThreadId, HANDLE HandleToThread) /*++ Routine Description: This function saves away a handle to a thread in a thread specific list so we can close it later when the thread termination message is continued. Arguments: dwProcessId - Process ID of threads process dwThreadId - Thread ID of thread handle HandleToThread - Handle to be closed later Return Value: None. --*/ { PTMPHANDLES Tmp; Tmp = RtlAllocateHeap (RtlProcessHeap(), 0, sizeof (TMPHANDLES)); if (Tmp != NULL) { Tmp->Thread = HandleToThread; Tmp->Process = NULL; Tmp->dwProcessId = dwProcessId; Tmp->dwThreadId = dwThreadId; Tmp->DeletePending = FALSE; Tmp->Next = *TmpHandleHead; *TmpHandleHead = Tmp; } } VOID SaveProcessHandle ( DWORD dwProcessId, HANDLE HandleToProcess ) /*++ Routine Description: This function saves away a handle to a process and file in a thread specific list so we can close it later when the process termination message is continued. Arguments: dwProcessId - Process ID of threads process HandleToProcess - Handle to be closed later HandleToFile - Handle to be closed later Return Value: None. --*/ { PTMPHANDLES Tmp; Tmp = RtlAllocateHeap (RtlProcessHeap(), 0, sizeof (TMPHANDLES)); if (Tmp != NULL) { Tmp->Process = HandleToProcess; Tmp->Thread = NULL; Tmp->dwProcessId = dwProcessId; Tmp->dwThreadId = 0; Tmp->DeletePending = FALSE; Tmp->Next = *TmpHandleHead; *TmpHandleHead = Tmp; } } VOID MarkThreadHandle ( DWORD dwThreadId ) /*++ Routine Description: This function marks a saved thread handle so that the next time this thread is continued we close its handle Arguments: dwThreadId - Thread ID of thread handle Return Value: None. --*/ { PTMPHANDLES Tmp; Tmp = *TmpHandleHead; while (Tmp != NULL) { if (Tmp->dwThreadId == dwThreadId) { Tmp->DeletePending = TRUE; break; } Tmp = Tmp->Next; } } VOID MarkProcessHandle ( DWORD dwProcessId ) { PTMPHANDLES Tmp; Tmp = *TmpHandleHead; while (Tmp != NULL) { if (Tmp->dwProcessId == dwProcessId && Tmp->dwThreadId == 0) { Tmp->DeletePending = TRUE; break; } Tmp = Tmp->Next; } } VOID RemoveHandles ( DWORD dwThreadId, DWORD dwProcessId ) /*++ Routine Description: This function closes marked handles for this process and thread id Arguments: dwProcessId - Process ID of threads process dwThreadId - Thread ID of thread handle Return Value: None. --*/ { PTMPHANDLES Tmp, *Last; Last = TmpHandleHead; Tmp = *Last; while (Tmp != NULL) { if (Tmp->DeletePending) { if (Tmp->dwProcessId == dwProcessId || Tmp->dwThreadId == dwThreadId) { if (Tmp->Thread != NULL) { CloseHandle (Tmp->Thread); } if (Tmp->Process != NULL) { CloseHandle (Tmp->Process); } *Last = Tmp->Next; RtlFreeHeap (RtlProcessHeap(), 0, Tmp); Tmp = *Last; continue; } } Last = &Tmp->Next; Tmp = Tmp->Next; } } VOID CloseAllProcessHandles ( DWORD dwProcessId ) /*++ Routine Description: This function closes all saved handles when we stop debugging a single process Arguments: dwProcessId - Process ID of threads process Return Value: None. --*/ { PTMPHANDLES Tmp, *Last; Last = TmpHandleHead; Tmp = *Last; while (Tmp != NULL) { if (Tmp->dwProcessId == dwProcessId) { if (Tmp->Thread != NULL) { CloseHandle (Tmp->Thread); } if (Tmp->Process != NULL) { CloseHandle (Tmp->Process); } *Last = Tmp->Next; RtlFreeHeap (RtlProcessHeap(), 0, Tmp); Tmp = *Last; continue; } Last = &Tmp->Next; Tmp = Tmp->Next; } } BOOL APIENTRY IsDebuggerPresent( VOID ) /*++ Routine Description: This function returns TRUE if the current process is being debugged and FALSE if not. Arguments: None. Return Value: None. --*/ { return NtCurrentPeb()->BeingDebugged; } BOOL APIENTRY CheckRemoteDebuggerPresent( IN HANDLE hProcess, OUT PBOOL pbDebuggerPresent ) /*++ Routine Description: This function determines whether the remote process is being debugged. Arguments: hProcess - handle to the process pbDebuggerPresent - supplies a buffer to receive the result of the check TRUE - remote process is being debugged FALSE - remote process is not being debugged Return Value: TRUE - The function succeeded. FALSE - The function fail. Extended error status is available using GetLastError. --*/ { HANDLE hDebugPort; NTSTATUS Status; if( (hProcess == NULL) || (pbDebuggerPresent == NULL) ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } Status = NtQueryInformationProcess( hProcess, ProcessDebugPort, (PVOID)(&hDebugPort), sizeof(hDebugPort), NULL ); if( !NT_SUCCESS(Status) ) { BaseSetLastNTError( Status ); return FALSE; } *pbDebuggerPresent = (hDebugPort != NULL); return TRUE; } //#ifdef i386 //#pragma optimize("",off) //#endif // i386 VOID APIENTRY DebugBreak( VOID ) /*++ Routine Description: This function causes a breakpoint exception to occur in the caller. This allows the calling thread to signal the debugger forcing it to take some action. If the process is not being debugged, the standard exception search logic is invoked. In most cases, this will cause the calling process to terminate (due to an unhandled breakpoint exception). Arguments: None. Return Value: None. --*/ { DbgBreakPoint(); } //#ifdef i386 //#pragma optimize("",on) //#endif // i386 VOID APIENTRY OutputDebugStringW( LPCWSTR lpOutputString ) /*++ Routine Description: UNICODE thunk to OutputDebugStringA --*/ { UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; NTSTATUS Status; RtlInitUnicodeString(&UnicodeString,lpOutputString); Status = RtlUnicodeStringToAnsiString(&AnsiString,&UnicodeString,TRUE); if ( !NT_SUCCESS(Status) ) { AnsiString.Buffer = ""; } OutputDebugStringA(AnsiString.Buffer); if ( NT_SUCCESS(Status) ) { RtlFreeAnsiString(&AnsiString); } } #define DBWIN_TIMEOUT 10000 HANDLE CreateDBWinMutex(VOID) { SECURITY_ATTRIBUTES SecurityAttributes; SECURITY_DESCRIPTOR sd; NTSTATUS Status; SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY authWorld = SECURITY_WORLD_SID_AUTHORITY; PSID psidSystem = NULL, psidAdmin = NULL, psidEveryone = NULL; PACL pAcl = NULL; DWORD cbAcl, aceIndex; HANDLE h = NULL; DWORD i; // // Get the system sid // Status = RtlAllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidSystem); if (!NT_SUCCESS(Status)) goto Exit; // // Get the Admin sid // Status = RtlAllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin); if (!NT_SUCCESS(Status)) goto Exit; // // Get the World sid // Status = RtlAllocateAndInitializeSid(&authWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone); if (!NT_SUCCESS(Status)) goto Exit; // // Allocate space for the ACL // cbAcl = sizeof(ACL) + 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + RtlLengthSid(psidSystem) + RtlLengthSid(psidAdmin) + RtlLengthSid(psidEveryone); pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl); if (!pAcl) { goto Exit; } Status = RtlCreateAcl(pAcl, cbAcl, ACL_REVISION); if (!NT_SUCCESS(Status)) goto Exit; // // Add Aces. // Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE, psidEveryone); if (!NT_SUCCESS(Status)) goto Exit; Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidSystem); if (!NT_SUCCESS(Status)) goto Exit; Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidAdmin); if (!NT_SUCCESS(Status)) goto Exit; Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); if (!NT_SUCCESS(Status)) goto Exit; Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, pAcl, FALSE); if (!NT_SUCCESS(Status)) goto Exit; SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttributes.bInheritHandle = TRUE; SecurityAttributes.lpSecurityDescriptor = &sd; i = 0; while (1) { h = OpenMutex (READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE, TRUE, "DBWinMutex"); if (h != NULL) { break; } h = CreateMutex(&SecurityAttributes, FALSE, "DBWinMutex"); if (h != NULL || GetLastError () != ERROR_ACCESS_DENIED || i++ > 100) { break; } } Exit: if (psidSystem) { RtlFreeSid(psidSystem); } if (psidAdmin) { RtlFreeSid(psidAdmin); } if (psidEveryone) { RtlFreeSid(psidEveryone); } if (pAcl) { GlobalFree (pAcl); } return h; } VOID APIENTRY OutputDebugStringA( IN LPCSTR lpOutputString ) /*++ Routine Description: This function allows an application to send a string to its debugger for display. If the application is not being debugged, but the system debugger is active, the system debugger displays the string. Otherwise, this function has no effect. Arguments: lpOutputString - Supplies the address of the debug string to be sent to the debugger. Return Value: None. --*/ { ULONG_PTR ExceptionArguments[2]; // // Raise an exception. If APP is being debugged, the debugger // will catch and handle this. Otherwise, kernel debugger is // called. // try { ExceptionArguments[0]=strlen(lpOutputString)+1; ExceptionArguments[1]=(ULONG_PTR)lpOutputString; RaiseException(DBG_PRINTEXCEPTION_C,0,2,ExceptionArguments); } except(EXCEPTION_EXECUTE_HANDLER) { // // We caught the debug exception, so there's no user-mode // debugger. If there is a DBWIN running, send the string // to it. If not, use DbgPrint to send it to the kernel // debugger. DbgPrint can only handle 511 characters at a // time, so force-feed it. // char szBuf[512]; size_t cchRemaining; LPCSTR pszRemainingOutput; HANDLE SharedFile = NULL; LPSTR SharedMem = NULL; HANDLE AckEvent = NULL; HANDLE ReadyEvent = NULL; static HANDLE DBWinMutex = NULL; static BOOLEAN CantGetMutex = FALSE; // // look for DBWIN. // if (!DBWinMutex && !CantGetMutex) { DBWinMutex = CreateDBWinMutex(); if (!DBWinMutex) CantGetMutex = TRUE; } if (DBWinMutex) { WaitForSingleObject(DBWinMutex, INFINITE); SharedFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, "DBWIN_BUFFER"); if (SharedFile) { SharedMem = MapViewOfFile( SharedFile, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0); if (SharedMem) { AckEvent = OpenEvent(SYNCHRONIZE, FALSE, "DBWIN_BUFFER_READY"); if (AckEvent) { ReadyEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_DATA_READY"); } } } if (!ReadyEvent) { ReleaseMutex(DBWinMutex); } } try { pszRemainingOutput = lpOutputString; cchRemaining = strlen(pszRemainingOutput); while (cchRemaining > 0) { int used; if (ReadyEvent && WaitForSingleObject(AckEvent, DBWIN_TIMEOUT) == WAIT_OBJECT_0) { *((DWORD *)SharedMem) = GetCurrentProcessId(); used = (int)((cchRemaining < 4095 - sizeof(DWORD)) ? cchRemaining : (4095 - sizeof(DWORD))); RtlCopyMemory(SharedMem+sizeof(DWORD), pszRemainingOutput, used); SharedMem[used+sizeof(DWORD)] = 0; SetEvent(ReadyEvent); } else { used = (int)((cchRemaining < sizeof(szBuf) - 1) ? cchRemaining : (int)(sizeof(szBuf) - 1)); RtlCopyMemory(szBuf, pszRemainingOutput, used); szBuf[used] = 0; DbgPrint("%s", szBuf); } pszRemainingOutput += used; cchRemaining -= used; } } except(STATUS_ACCESS_VIOLATION == GetExceptionCode()) { DbgPrint("\nOutputDebugString faulted during output\n"); } if (AckEvent) { CloseHandle(AckEvent); } if (SharedMem) { UnmapViewOfFile(SharedMem); } if (SharedFile) { CloseHandle(SharedFile); } if (ReadyEvent) { CloseHandle(ReadyEvent); ReleaseMutex(DBWinMutex); } } } BOOL APIENTRY WaitForDebugEvent( LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds ) /*++ Routine Description: A debugger waits for a debug event to occur in one of its debuggees using WaitForDebugEvent: Upon successful completion of this API, the lpDebugEvent structure contains the relevant information of the debug event. Arguments: lpDebugEvent - Receives information specifying the type of debug event that occured. dwMilliseconds - A time-out value that specifies the relative time, in milliseconds, over which the wait is to be completed. A timeout value of 0 specified that the wait is to timeout immediately. This allows an application to test for debug events A timeout value of -1 specifies an infinite timeout period. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed (or timed out). Extended error status is available using GetLastError. --*/ { NTSTATUS Status; DBGUI_WAIT_STATE_CHANGE StateChange; LARGE_INTEGER TimeOut; PLARGE_INTEGER pTimeOut; pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds); again: Status = DbgUiWaitStateChange(&StateChange,pTimeOut); if ( Status == STATUS_ALERTED || Status == STATUS_USER_APC) { goto again; } if ( !NT_SUCCESS(Status) && Status != DBG_UNABLE_TO_PROVIDE_HANDLE ) { BaseSetLastNTError(Status); return FALSE; } if ( Status == STATUS_TIMEOUT ) { SetLastError(ERROR_SEM_TIMEOUT); return FALSE; } Status = DbgUiConvertStateChangeStructure (&StateChange, lpDebugEvent); if (!NT_SUCCESS (Status)) { BaseSetLastNTError(Status); return FALSE; } switch (lpDebugEvent->dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT : // // Save away thread handle for later cleanup. // SaveThreadHandle (lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId, lpDebugEvent->u.CreateThread.hThread); break; case CREATE_PROCESS_DEBUG_EVENT : SaveProcessHandle (lpDebugEvent->dwProcessId, lpDebugEvent->u.CreateProcessInfo.hProcess); SaveThreadHandle (lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId, lpDebugEvent->u.CreateProcessInfo.hThread); break; case EXIT_THREAD_DEBUG_EVENT : MarkThreadHandle (lpDebugEvent->dwThreadId); break; case EXIT_PROCESS_DEBUG_EVENT : MarkThreadHandle (lpDebugEvent->dwThreadId); MarkProcessHandle (lpDebugEvent->dwProcessId); break; case OUTPUT_DEBUG_STRING_EVENT : case RIP_EVENT : case EXCEPTION_DEBUG_EVENT : break; case LOAD_DLL_DEBUG_EVENT : break; case UNLOAD_DLL_DEBUG_EVENT : break; default: return FALSE; } return TRUE; } BOOL APIENTRY ContinueDebugEvent( DWORD dwProcessId, DWORD dwThreadId, DWORD dwContinueStatus ) /*++ Routine Description: A debugger can continue a thread that previously reported a debug event using ContinueDebugEvent. Upon successful completion of this API, the specified thread is continued. Depending on the debug event previously reported by the thread certain side effects occur. If the continued thread previously reported an exit thread debug event, the handle that the debugger has to the thread is closed. If the continued thread previously reported an exit process debug event, the handles that the debugger has to the thread and to the process are closed. Arguments: dwProcessId - Supplies the process id of the process to continue. The combination of process id and thread id must identify a thread that has previously reported a debug event. dwThreadId - Supplies the thread id of the thread to continue. The combination of process id and thread id must identify a thread that has previously reported a debug event. dwContinueStatus - Supplies the continuation status for the thread reporting the debug event. dwContinueStatus Values: DBG_CONTINUE - If the thread being continued had previously reported an exception event, continuing with this value causes all exception processing to stop and the thread continues execution. For any other debug event, this continuation status simply allows the thread to continue execution. DBG_EXCEPTION_NOT_HANDLED - If the thread being continued had previously reported an exception event, continuing with this value causes exception processing to continue. If this is a first chance exception event, then structured exception handler search/dispatch logic is invoked. Otherwise, the process is terminated. For any other debug event, this continuation status simply allows the thread to continue execution. DBG_TERMINATE_THREAD - After all continue side effects are processed, this continuation status causes the thread to jump to a call to ExitThread. The exit code is the value DBG_TERMINATE_THREAD. DBG_TERMINATE_PROCESS - After all continue side effects are processed, this continuation status causes the thread to jump to a call to ExitProcess. The exit code is the value DBG_TERMINATE_PROCESS. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; CLIENT_ID ClientId; ClientId.UniqueProcess = (HANDLE)LongToHandle(dwProcessId); ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId); Status = DbgUiContinue(&ClientId,(NTSTATUS)dwContinueStatus); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } RemoveHandles (dwThreadId, dwProcessId); return TRUE; } HANDLE ProcessIdToHandle ( IN DWORD dwProcessId ) { OBJECT_ATTRIBUTES oa; HANDLE Process; CLIENT_ID ClientId; NTSTATUS Status; if (dwProcessId == -1) { ClientId.UniqueProcess = CsrGetProcessId (); } else { ClientId.UniqueProcess = LongToHandle(dwProcessId); } ClientId.UniqueThread = NULL; InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL); Status = NtOpenProcess (&Process, PROCESS_SET_PORT|PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION| PROCESS_VM_WRITE|PROCESS_VM_READ, &oa, &ClientId); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); Process = NULL; } return Process; } BOOL APIENTRY DebugActiveProcess( DWORD dwProcessId ) /*++ Routine Description: This API allows a debugger to attach to an active process and debug the process. The debugger specifies the process that it wants to debug through the process id of the target process. The debugger gets debug access to the process as if it had created the process with the DEBUG_ONLY_THIS_PROCESS creation flag. The debugger must have approriate access to the calling process such that it can open the process for PROCESS_ALL_ACCESS. For Dos/Win32 this never fails (the process id just has to be a valid process id). For NT/Win32 this check can fail if the target process was created with a security descriptor that denies the debugger approriate access. Once the process id check has been made and the system determines that a valid debug attachment is being made, this call returns success to the debugger. The debugger is then expected to wait for debug events. The system will suspend all threads in the process and feed the debugger debug events representing the current state of the process. The system will feed the debugger a single create process debug event representing the process specified by dwProcessId. The lpStartAddress field of the create process debug event is NULL. For each thread currently part of the process, the system will send a create thread debug event. The lpStartAddress field of the create thread debug event is NULL. For each DLL currently loaded into the address space of the target process, the system will send a LoadDll debug event. The system will arrange for the first thread in the process to execute a breakpoint instruction after it is resumed. Continuing this thread causes the thread to return to whatever it was doing prior to the debug attach. After all of this has been done, the system resumes all threads within the process. When the first thread in the process resumes, it will execute a breakpoint instruction causing an exception debug event to be sent to the debugger. All future debug events are sent to the debugger using the normal mechanism and rules. Arguments: dwProcessId - Supplies the process id of a process the caller wants to debug. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { HANDLE Process; NTSTATUS Status, Status1; // // Connect to dbgss as a user interface // Status = DbgUiConnectToDbg (); if (!NT_SUCCESS (Status)) { BaseSetLastNTError (Status); return FALSE; } // // Convert the process ID to a handle // Process = ProcessIdToHandle (dwProcessId); if (Process == NULL) { return FALSE; } Status = DbgUiDebugActiveProcess (Process); if (!NT_SUCCESS (Status)) { Status1 = NtClose (Process); ASSERT (NT_SUCCESS (Status1)); BaseSetLastNTError (Status); return FALSE; } Status1 = NtClose (Process); ASSERT (NT_SUCCESS (Status1)); return TRUE; } BOOL APIENTRY DebugActiveProcessStop( DWORD dwProcessId ) /*++ Routine Description: Arguments: dwProcessId - Supplies the process id of a process the caller wants to stop debugging. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { HANDLE Process, Thread; NTSTATUS Status; NTSTATUS Status1; DWORD ThreadId; Process = ProcessIdToHandle (dwProcessId); if (Process == NULL) { return FALSE; } // // Tell dbgss we have finished with this process. // CloseAllProcessHandles (dwProcessId); Status = DbgUiStopDebugging (Process); Status1 = NtClose (Process); ASSERT (NT_SUCCESS (Status1)); if (!NT_SUCCESS(Status)) { SetLastError(ERROR_ACCESS_DENIED); return FALSE; } return TRUE; } BOOL APIENTRY DebugBreakProcess ( IN HANDLE Process ) /*++ Routine Description: This functions creates a thread inside the target process that issues a break. Arguments: Process - Handle to process Return Value: TRUE - The operation was successful FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; Status = DbgUiIssueRemoteBreakin (Process); if (NT_SUCCESS (Status)) { return TRUE; } else { BaseSetLastNTError (Status); return FALSE; } } BOOL APIENTRY DebugSetProcessKillOnExit ( IN BOOL KillOnExit ) /*++ Routine Description: This functions sets the action to be performed when the debugging thread dies Arguments: KillOnExit - TRUE: Kill debugged processes on exit, FALSE: Detatch on debug exit Return Value: TRUE - The operation was successful FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { HANDLE DebugHandle; ULONG Flags; NTSTATUS Status; DebugHandle = DbgUiGetThreadDebugObject (); if (DebugHandle == NULL) { BaseSetLastNTError (STATUS_INVALID_HANDLE); return FALSE; } if (KillOnExit) { Flags = DEBUG_KILL_ON_CLOSE; } else { Flags = 0; } Status = NtSetInformationDebugObject (DebugHandle, DebugObjectFlags, &Flags, sizeof (Flags), NULL); if (!NT_SUCCESS (Status)) { BaseSetLastNTError (Status); return FALSE; } return TRUE; } BOOL APIENTRY GetThreadSelectorEntry( HANDLE hThread, DWORD dwSelector, LPLDT_ENTRY lpSelectorEntry ) /*++ Routine Description: This function is used to return a descriptor table entry for the specified thread corresponding to the specified selector. This API is only functional on x86 based systems. For non x86 based systems. A value of FALSE is returned. This API is used by a debugger so that it can convert segment relative addresses to linear virtual address (since this is the only format supported by ReadProcessMemory and WriteProcessMemory. Arguments: hThread - Supplies a handle to the thread that contains the specified selector. The handle must have been created with THREAD_QUERY_INFORMATION access. dwSelector - Supplies the selector value to lookup. The selector value may be a global selector or a local selector. lpSelectorEntry - If the specified selector is contained withing the threads descriptor tables, this parameter returns the selector entry corresponding to the specified selector value. This data can be used to compute the linear base address that segment relative addresses refer to. Return Value: TRUE - The operation was successful FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { #if defined(i386) DESCRIPTOR_TABLE_ENTRY DescriptorEntry; NTSTATUS Status; DescriptorEntry.Selector = dwSelector; Status = NtQueryInformationThread( hThread, ThreadDescriptorTableEntry, &DescriptorEntry, sizeof(DescriptorEntry), NULL ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return FALSE; } *lpSelectorEntry = DescriptorEntry.Descriptor; return TRUE; #else BaseSetLastNTError(STATUS_NOT_SUPPORTED); return FALSE; #endif // i386 }