windows-nt/Source/XPSP1/NT/base/win32/client/debug.c

1260 lines
30 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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
}