2020-09-30 17:12:29 +02:00

987 lines
29 KiB
C

/*++
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
#include "ntdbg.h"
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;
}
//#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
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.
--*/
{
DWORD 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]=(DWORD)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;
//
// look for DBWIN.
//
if (!DBWinMutex) {
SECURITY_ATTRIBUTES SecurityAttributes;
SECURITY_DESCRIPTOR SecurityDescriptor;
PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
NTSTATUS Status;
//
// Create a descriptor with a NULL DACL instead of letting
// it use the default.
//
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.bInheritHandle = TRUE;
SecurityAttributes.lpSecurityDescriptor = &SecurityDescriptor;
Status = RtlCreateSecurityDescriptor (
&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
if (NT_SUCCESS(Status)) {
Status = RtlSetDaclSecurityDescriptor (
&SecurityDescriptor,
TRUE,
NULL,
FALSE
);
if (NT_SUCCESS(Status)) {
pSecurityAttributes = &SecurityAttributes;
}
}
DBWinMutex = CreateMutex(pSecurityAttributes, FALSE, "DBWinMutex");
}
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 = (cchRemaining < 4095 - sizeof(DWORD)) ?
cchRemaining : (4095 - sizeof(DWORD));
RtlCopyMemory(SharedMem+sizeof(DWORD),
pszRemainingOutput,
used);
SharedMem[used+sizeof(DWORD)] = 0;
SetEvent(ReadyEvent);
}
else {
used = (cchRemaining < sizeof(szBuf) - 1) ?
cchRemaining : (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;
THREAD_BASIC_INFORMATION ThreadBasicInfo;
HANDLE hThread;
OBJECT_ATTRIBUTES Obja;
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;
}
lpDebugEvent->dwProcessId = (DWORD)StateChange.AppClientId.UniqueProcess;
lpDebugEvent->dwThreadId = (DWORD)StateChange.AppClientId.UniqueThread;
switch ( StateChange.NewState ) {
case DbgCreateThreadStateChange :
lpDebugEvent->dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
lpDebugEvent->u.CreateThread.hThread =
StateChange.StateInfo.CreateThread.HandleToThread;
lpDebugEvent->u.CreateThread.lpStartAddress =
(LPTHREAD_START_ROUTINE)StateChange.StateInfo.CreateThread.NewThread.StartAddress;
Status = NtQueryInformationThread(
StateChange.StateInfo.CreateThread.HandleToThread,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
NULL
);
if (!NT_SUCCESS(Status)) {
lpDebugEvent->u.CreateThread.lpThreadLocalBase = NULL;
}
else {
lpDebugEvent->u.CreateThread.lpThreadLocalBase = ThreadBasicInfo.TebBaseAddress;
}
break;
case DbgCreateProcessStateChange :
lpDebugEvent->dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT;
lpDebugEvent->u.CreateProcessInfo.hProcess =
StateChange.StateInfo.CreateProcessInfo.HandleToProcess;
lpDebugEvent->u.CreateProcessInfo.hThread =
StateChange.StateInfo.CreateProcessInfo.HandleToThread;
lpDebugEvent->u.CreateProcessInfo.hFile =
StateChange.StateInfo.CreateProcessInfo.NewProcess.FileHandle;
lpDebugEvent->u.CreateProcessInfo.lpBaseOfImage =
StateChange.StateInfo.CreateProcessInfo.NewProcess.BaseOfImage;
lpDebugEvent->u.CreateProcessInfo.dwDebugInfoFileOffset =
StateChange.StateInfo.CreateProcessInfo.NewProcess.DebugInfoFileOffset;
lpDebugEvent->u.CreateProcessInfo.nDebugInfoSize =
StateChange.StateInfo.CreateProcessInfo.NewProcess.DebugInfoSize;
lpDebugEvent->u.CreateProcessInfo.lpStartAddress =
(LPTHREAD_START_ROUTINE)StateChange.StateInfo.CreateProcessInfo.NewProcess.InitialThread.StartAddress;
Status = NtQueryInformationThread(
StateChange.StateInfo.CreateProcessInfo.HandleToThread,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
NULL
);
if (!NT_SUCCESS(Status)) {
lpDebugEvent->u.CreateProcessInfo.lpThreadLocalBase = NULL;
}
else {
lpDebugEvent->u.CreateProcessInfo.lpThreadLocalBase = ThreadBasicInfo.TebBaseAddress;
}
lpDebugEvent->u.CreateProcessInfo.lpImageName = NULL;
lpDebugEvent->u.CreateProcessInfo.fUnicode = 1;
break;
case DbgExitThreadStateChange :
lpDebugEvent->dwDebugEventCode = EXIT_THREAD_DEBUG_EVENT;
lpDebugEvent->u.ExitThread.dwExitCode =
(DWORD)StateChange.StateInfo.ExitThread.ExitStatus;
break;
case DbgExitProcessStateChange :
lpDebugEvent->dwDebugEventCode = EXIT_PROCESS_DEBUG_EVENT;
lpDebugEvent->u.ExitProcess.dwExitCode =
(DWORD)StateChange.StateInfo.ExitProcess.ExitStatus;
break;
case DbgExceptionStateChange :
case DbgBreakpointStateChange :
case DbgSingleStepStateChange :
if ( StateChange.StateInfo.Exception.ExceptionRecord.ExceptionCode ==
DBG_PRINTEXCEPTION_C ) {
lpDebugEvent->dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;
lpDebugEvent->u.DebugString.lpDebugStringData =
(PVOID)StateChange.StateInfo.Exception.ExceptionRecord.ExceptionInformation[1];
lpDebugEvent->u.DebugString.nDebugStringLength =
(WORD)StateChange.StateInfo.Exception.ExceptionRecord.ExceptionInformation[0];
lpDebugEvent->u.DebugString.fUnicode = (WORD)0;
}
else if ( StateChange.StateInfo.Exception.ExceptionRecord.ExceptionCode ==
DBG_RIPEXCEPTION ) {
lpDebugEvent->dwDebugEventCode = RIP_EVENT;
lpDebugEvent->u.RipInfo.dwType =
(DWORD)StateChange.StateInfo.Exception.ExceptionRecord.ExceptionInformation[1];
lpDebugEvent->u.RipInfo.dwError =
(DWORD)StateChange.StateInfo.Exception.ExceptionRecord.ExceptionInformation[0];
}
else {
lpDebugEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
lpDebugEvent->u.Exception.ExceptionRecord =
StateChange.StateInfo.Exception.ExceptionRecord;
lpDebugEvent->u.Exception.dwFirstChance =
StateChange.StateInfo.Exception.FirstChance;
}
break;
case DbgLoadDllStateChange :
lpDebugEvent->dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
lpDebugEvent->u.LoadDll.lpBaseOfDll =
StateChange.StateInfo.LoadDll.BaseOfDll;
lpDebugEvent->u.LoadDll.hFile =
StateChange.StateInfo.LoadDll.FileHandle;
lpDebugEvent->u.LoadDll.dwDebugInfoFileOffset =
StateChange.StateInfo.LoadDll.DebugInfoFileOffset;
lpDebugEvent->u.LoadDll.nDebugInfoSize =
StateChange.StateInfo.LoadDll.DebugInfoSize;
{
//
// pick up the image name
//
InitializeObjectAttributes(&Obja, NULL, 0, NULL, NULL);
Status = NtOpenThread(
&hThread,
THREAD_QUERY_INFORMATION,
&Obja,
&StateChange.AppClientId
);
if ( NT_SUCCESS(Status) ) {
Status = NtQueryInformationThread(
hThread,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
NULL
);
NtClose(hThread);
}
if ( NT_SUCCESS(Status) ) {
lpDebugEvent->u.LoadDll.lpImageName = &ThreadBasicInfo.TebBaseAddress->NtTib.ArbitraryUserPointer;
}
else {
lpDebugEvent->u.LoadDll.lpImageName = NULL;
}
lpDebugEvent->u.LoadDll.fUnicode = 1;
}
break;
case DbgUnloadDllStateChange :
lpDebugEvent->dwDebugEventCode = UNLOAD_DLL_DEBUG_EVENT;
lpDebugEvent->u.UnloadDll.lpBaseOfDll =
StateChange.StateInfo.UnloadDll.BaseAddress;
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)dwProcessId;
ClientId.UniqueThread = (HANDLE)dwThreadId;
Status = DbgUiContinue(&ClientId,(NTSTATUS)dwContinueStatus);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
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, Thread;
NTSTATUS Status;
DWORD ThreadId;
PBASE_API_MSG m;
PBASE_DEBUGPROCESS_MSG a;
//
// Determine if a valid process id has been specified. If
// so than call the server to do the attach.
//
if ( dwProcessId != -1 ) {
Process = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
if ( !Process ) {
return FALSE;
}
//
// Call server to see if we can really debug this process
//
{
BASE_API_MSG mm;
PBASE_DEBUGPROCESS_MSG aa= (PBASE_DEBUGPROCESS_MSG)&mm.u.DebugProcess;
aa->DebuggerClientId = NtCurrentTeb()->ClientId;
aa->dwProcessId = dwProcessId;
aa->AttachCompleteRoutine = NULL;
CsrClientCallServer(
(PCSR_API_MSG)&mm,
NULL,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepDebugProcess
),
sizeof( BASE_DEBUGPROCESS_MSG )
);
if (!NT_SUCCESS((NTSTATUS)mm.ReturnValue)) {
SetLastError(ERROR_ACCESS_DENIED);
CloseHandle(Process);
return FALSE;
}
}
CloseHandle(Process);
}
else {
//
// Call server to see if we can really debug this process
//
{
BASE_API_MSG mm;
PBASE_DEBUGPROCESS_MSG aa= (PBASE_DEBUGPROCESS_MSG)&mm.u.DebugProcess;
aa->DebuggerClientId = NtCurrentTeb()->ClientId;
aa->dwProcessId = dwProcessId;
aa->AttachCompleteRoutine = NULL;
CsrClientCallServer(
(PCSR_API_MSG)&mm,
NULL,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepDebugProcess
),
sizeof( BASE_DEBUGPROCESS_MSG )
);
if (!NT_SUCCESS((NTSTATUS)mm.ReturnValue)) {
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
}
}
//
// Connect to dbgss as a user interface
//
Status = DbgUiConnectToDbg();
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
m = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), sizeof(*m));
if ( !m ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
a = (PBASE_DEBUGPROCESS_MSG)&m->u.DebugProcess;
a->DebuggerClientId = NtCurrentTeb()->ClientId;
a->dwProcessId = dwProcessId;
a->AttachCompleteRoutine = (PVOID)BaseAttachComplete;
Thread = CreateThread(
NULL,
0L,
BaseDebugAttachThread,
(LPVOID)m,
0,
&ThreadId
);
if ( !Thread ) {
RtlFreeHeap(RtlProcessHeap(), 0,m);
return FALSE;
}
CloseHandle(Thread);
return TRUE;
}
DWORD
BaseDebugAttachThread(
LPVOID ThreadParameter
)
/*++
Routine Description:
This thread is created as part of the debug attach procedure. It
runs in the context of the attaching debugger. Its basic function
is to call the server and block until the server completes the
attachment. It then exits. This thread is needed because the debugger
making the attach call must be free to service debug events from the
server. Rather than have the server have added complexity of doing
the debug attach procedure asynchronously, we grab a debugger thread
to block.
Arguments:
ThreadParameter - Supplies the address of the base api message Not used.
Return Value:
None.
--*/
{
PBASE_API_MSG m;
NTSTATUS Status;
ULONG Response;
m = (PBASE_API_MSG)ThreadParameter;
CsrClientCallServer( (PCSR_API_MSG)ThreadParameter,
NULL,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepDebugProcess
),
sizeof( BASE_DEBUGPROCESS_MSG )
);
if (!NT_SUCCESS((NTSTATUS)m->ReturnValue)) {
//
// Unexpected attach failure
//
Status =NtRaiseHardError( STATUS_DEBUG_ATTACH_FAILED | 0x10000000,
0,
0,
NULL,
OptionOkCancel,
&Response
);
if ( NT_SUCCESS(Status) && Response == ResponseOk ) {
ExitProcess(Status);
}
}
RtlFreeHeap(RtlProcessHeap(), 0,ThreadParameter);
ExitThread((DWORD)0);
return 0;
}
VOID
BaseAttachComplete(
PCONTEXT Context
)
/*++
Routine Description:
This function is remote called to after a successful debug attach. Its
purpose is to issue a breakpoint and the continue.
Arguments:
Context - Supplies the context record that is to be restored upon
completion of this API.
Return Value:
None.
--*/
{
HANDLE DebugPort;
NTSTATUS Status;
DebugPort = (HANDLE)NULL;
Status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessDebugPort,
(PVOID)&DebugPort,
sizeof(DebugPort),
NULL
);
if ( NT_SUCCESS(Status) && DebugPort ) {
DbgBreakPoint();
}
if ( !Context ) {
ExitThread(0);
ASSERT(FALSE);
}
else {
NtContinue(Context,FALSE);
}
}
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.
--*/
{
#ifdef 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
}