windows-nt/Source/XPSP1/NT/sdktools/debuggers/dbgsvc/dbgsvc.cpp

3742 lines
91 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//----------------------------------------------------------------------------
//
// Low-level debugging service interface implementations.
//
// Copyright (C) Microsoft Corporation, 2000-2001.
//
//----------------------------------------------------------------------------
#include "pch.hpp"
#include <time.h>
#include <comsvcs.h>
#include "dbgsvc.hpp"
#ifndef NT_NATIVE
// #include <winbasep.h>
extern "C" {
BOOL
WINAPI
CloseProfileUserMapping(
VOID
);
};
// winbasep.h
#else
#define CloseProfileUserMapping()
#endif
// SYSTEM_PROCESS_INFORMATION can change in size, requiring
// different offsets to get to thread information.
#define NT4_SYSTEM_PROCESS_INFORMATION_SIZE 136
#define W2K_SYSTEM_PROCESS_INFORMATION_SIZE 184
#define SYSTEM_PROCESS_NAME "System Process"
#define SYSTEM_PROCESS_NAME_W L"System Process"
#define PEBLESS_PROCESS_NAME "System"
ULONG g_UserServicesUninitialized;
//----------------------------------------------------------------------------
//
// UserDebugServices.
//
//----------------------------------------------------------------------------
UserDebugServices::UserDebugServices(void)
{
m_Refs = 1;
m_Initialized = FALSE;
}
UserDebugServices::~UserDebugServices(void)
{
}
STDMETHODIMP
UserDebugServices::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
HRESULT Status;
*Interface = NULL;
Status = S_OK;
if (DbgIsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
DbgIsEqualIID(InterfaceId, __uuidof(IUserDebugServices)))
{
*Interface = (IUserDebugServices *)this;
}
else
{
Status = E_NOINTERFACE;
}
if (Status == S_OK)
{
AddRef();
}
return Status;
}
STDMETHODIMP_(ULONG)
UserDebugServices::AddRef(
THIS
)
{
return InterlockedIncrement((PLONG)&m_Refs);
}
STDMETHODIMP_(ULONG)
UserDebugServices::Release(
THIS
)
{
LONG Refs = InterlockedDecrement((PLONG)&m_Refs);
if (Refs == 0)
{
delete this;
}
return Refs;
}
HRESULT
UserDebugServices::Initialize(
THIS_
OUT PULONG Flags
)
{
m_Initialized = TRUE;
*Flags = 0;
return S_OK;
}
HRESULT
UserDebugServices::Uninitialize(
THIS_
IN BOOL Global
)
{
m_Initialized = FALSE;
if (Global)
{
g_UserServicesUninitialized++;
}
return S_OK;
}
HRESULT
UserDebugServices::Initialize(PSTR Identity, PVOID* Interface)
{
HRESULT Status;
ULONG Flags;
if ((Status = Initialize(&Flags)) != S_OK)
{
return Status;
}
*Interface = (IUserDebugServices*)this;
return S_OK;
}
void
UserDebugServices::Finalize(void)
{
// Take a reference on this object for the RPC client
// thread to hold.
AddRef();
}
void
UserDebugServices::Uninitialize(void)
{
// Directly destroy the client object rather than releasing
// as the remote client may have exited without politely
// cleaning up references.
delete this;
}
//----------------------------------------------------------------------------
//
// LiveUserDebugServices.
//
//----------------------------------------------------------------------------
// This global instance is intended for direct use only
// by routines which need a temporary local service instance.
LiveUserDebugServices g_LiveUserDebugServices(FALSE);
LiveUserDebugServices::LiveUserDebugServices(BOOL Remote)
{
m_Remote = Remote;
m_EventProcessId = 0;
m_ContextSize = 0;
m_SysProcInfoSize = 0;
m_PlatformId = VER_PLATFORM_WIN32s;
m_DebugObject = NULL;
}
LiveUserDebugServices::~LiveUserDebugServices(void)
{
if (m_DebugObject != NULL)
{
g_NtDllCalls.NtClose(m_DebugObject);
}
}
HRESULT
GetOsVerInfo(LPOSVERSIONINFOW OsVersionInfo, PBOOL WideCsd)
{
*WideCsd = TRUE;
ZeroMemory(OsVersionInfo, sizeof(*OsVersionInfo));
OsVersionInfo->dwOSVersionInfoSize = sizeof(*OsVersionInfo);
#ifdef NT_NATIVE
NTSTATUS NtStatus;
if (!NT_SUCCESS(NtStatus = RtlGetVersion(OsVersionInfo)))
{
return HRESULT_FROM_NT(NtStatus);
}
#else
if (!GetVersionExW(OsVersionInfo))
{
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
{
OSVERSIONINFOA InfoA;
// Must be Win9x.
ZeroMemory(&InfoA, sizeof(InfoA));
InfoA.dwOSVersionInfoSize = sizeof(InfoA);
if (!::GetVersionExA(&InfoA))
{
return WIN32_LAST_STATUS();
}
OsVersionInfo->dwMajorVersion = InfoA.dwMajorVersion;
OsVersionInfo->dwMinorVersion = InfoA.dwMinorVersion;
OsVersionInfo->dwBuildNumber = InfoA.dwBuildNumber;
OsVersionInfo->dwPlatformId = InfoA.dwPlatformId;
memcpy(OsVersionInfo->szCSDVersion, InfoA.szCSDVersion,
sizeof(InfoA.szCSDVersion));
*WideCsd = FALSE;
}
else
{
return WIN32_LAST_STATUS();
}
}
#endif
return S_OK;
}
HRESULT
LiveUserDebugServices::Initialize(
THIS_
OUT PULONG Flags
)
{
HRESULT Status;
OSVERSIONINFOW OsVersionInfo;
BOOL WideCsd;
if ((Status = GetOsVerInfo(&OsVersionInfo, &WideCsd)) != S_OK)
{
return Status;
}
ULONG BaseFlags;
if ((Status = UserDebugServices::Initialize(&BaseFlags)) != S_OK)
{
return Status;
}
m_PlatformId = OsVersionInfo.dwPlatformId;
// System structures may change size depending on the OS
// version. Pick the right size to use later.
if (m_PlatformId == VER_PLATFORM_WIN32_NT)
{
if (OsVersionInfo.dwBuildNumber <= 1381)
{
m_SysProcInfoSize = NT4_SYSTEM_PROCESS_INFORMATION_SIZE;
}
else if (OsVersionInfo.dwBuildNumber <= 2195)
{
m_SysProcInfoSize = W2K_SYSTEM_PROCESS_INFORMATION_SIZE;
}
else
{
m_SysProcInfoSize = sizeof(SYSTEM_PROCESS_INFORMATION);
}
}
// If the direct NT debugging APIs are available use them
// as they offer more flexibility.
if (g_NtDllCalls.DbgUiSetThreadDebugObject != NULL)
{
// The NtWait/Continue APIs do not automatically manage
// process and thread handles so the caller must close them.
BaseFlags |= DBGSVC_CLOSE_PROC_THREAD_HANDLES;
m_UseDebugObject = TRUE;
}
else
{
m_UseDebugObject = FALSE;
}
*Flags = BaseFlags | DBGSVC_GENERIC_CODE_BREAKPOINTS;
return S_OK;
}
HRESULT
LiveUserDebugServices::Uninitialize(
THIS_
IN BOOL Global
)
{
HRESULT Status;
if ((Status = UserDebugServices::Uninitialize(Global)) != S_OK)
{
return Status;
}
m_Remote = FALSE;
m_EventProcessId = 0;
m_ContextSize = 0;
m_PlatformId = VER_PLATFORM_WIN32s;
if (m_DebugObject != NULL)
{
::CloseHandle(m_DebugObject);
m_DebugObject = NULL;
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetTargetInfo(
THIS_
OUT PULONG MachineType,
OUT PULONG NumberProcessors,
OUT PULONG PlatformId,
OUT PULONG BuildNumber,
OUT PULONG CheckedBuild,
OUT PSTR CsdString,
IN ULONG CsdStringSize,
OUT PSTR BuildString,
IN ULONG BuildStringSize
)
{
HRESULT Status;
OSVERSIONINFOW OsVersionInfo;
BOOL WideCsd;
if ((Status = GetOsVerInfo(&OsVersionInfo, &WideCsd)) != S_OK)
{
return Status;
}
ULONG ProcArch, NumProc;
#ifdef NT_NATIVE
NTSTATUS NtStatus;
SYSTEM_BASIC_INFORMATION BasicInfo;
SYSTEM_PROCESSOR_INFORMATION ProcInfo;
if (!NT_SUCCESS(NtStatus =
NtQuerySystemInformation(SystemBasicInformation,
&BasicInfo, sizeof(BasicInfo),
NULL)) ||
!NT_SUCCESS(NtStatus =
NtQuerySystemInformation(SystemProcessorInformation,
&ProcInfo, sizeof(ProcInfo),
NULL)))
{
return HRESULT_FROM_NT(NtStatus);
}
ProcArch = ProcInfo.ProcessorArchitecture;
NumProc = BasicInfo.NumberOfProcessors;
#else
SYSTEM_INFO SystemInfo;
::GetSystemInfo(&SystemInfo);
ProcArch = SystemInfo.wProcessorArchitecture;
NumProc = SystemInfo.dwNumberOfProcessors;
#endif
switch(ProcArch)
{
case PROCESSOR_ARCHITECTURE_INTEL:
*MachineType = IMAGE_FILE_MACHINE_I386;
if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
if (OsVersionInfo.dwBuildNumber <= 1381)
{
m_ContextSize = sizeof(X86_CONTEXT);
}
else
{
m_ContextSize = sizeof(X86_NT5_CONTEXT);
}
}
else if ((OsVersionInfo.dwBuildNumber & 0xffff) <= 1998)
{
// Win9x prior to Win98SE didn't support the extended context.
m_ContextSize = sizeof(X86_CONTEXT);
}
else
{
m_ContextSize = sizeof(X86_NT5_CONTEXT);
}
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
*MachineType = IMAGE_FILE_MACHINE_ALPHA;
// The "NT5" is a misnomer, this context
// applies to all versions.
m_ContextSize = sizeof(ALPHA_NT5_CONTEXT);
break;
case PROCESSOR_ARCHITECTURE_ALPHA64:
*MachineType = IMAGE_FILE_MACHINE_AXP64;
m_ContextSize = sizeof(ALPHA_NT5_CONTEXT);
break;
case PROCESSOR_ARCHITECTURE_IA64:
*MachineType = IMAGE_FILE_MACHINE_IA64;
m_ContextSize = sizeof(IA64_CONTEXT);
break;
default:
return E_UNEXPECTED;
}
*NumberProcessors = NumProc;
*PlatformId = OsVersionInfo.dwPlatformId;
*BuildNumber = OsVersionInfo.dwBuildNumber;
*CheckedBuild = 0;
if (WideCsd)
{
if (!WideCharToMultiByte(CP_ACP, 0, OsVersionInfo.szCSDVersion, -1,
CsdString, CsdStringSize, NULL, NULL))
{
CsdString[0] = 0;
}
}
else
{
CsdString[0] = 0;
strncat(CsdString, (PSTR)OsVersionInfo.szCSDVersion, CsdStringSize);
}
BuildString[0] = 0;
#ifndef NT_NATIVE
if (VER_PLATFORM_WIN32_NT == OsVersionInfo.dwPlatformId)
{
HKEY hkey = NULL;
TCHAR sz[40] = {0};
DWORD dwType;
DWORD dwSize = sizeof(sz);
if (ERROR_SUCCESS ==
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows NT\\CurrentVersion",
0,
KEY_READ,
&hkey))
{
if (ERROR_SUCCESS ==
RegQueryValueEx(hkey,
"CurrentType",
NULL,
&dwType,
(PUCHAR) sz,
&dwSize))
{
if (*sz)
{
_strlwr(sz);
if (strstr(sz, "checked"))
{
*CheckedBuild = 0xC;
}
}
}
RegCloseKey(hkey);
}
if (OsVersionInfo.dwBuildNumber > 2195)
{
char RawString[128];
// Look up the file version string for a system DLL to
// try and get the build lab information.
strcpy(RawString, "kernel32.dll version: ");
GetFileStringFileInfo("kernel32.dll", "FileVersion",
RawString + strlen(RawString),
sizeof(RawString) - strlen(RawString));
strncat(BuildString, RawString, BuildStringSize);
}
}
#endif // #ifndef NT_NATIVE
return S_OK;
}
BOOL
X86CpuId(
IN ULONG SubFunction,
OUT PULONG EaxRegister,
OUT PULONG EbxRegister,
OUT PULONG EcxRegister,
OUT PULONG EdxRegister
)
{
#ifdef _X86_
ULONG _Eax;
ULONG _Ebx;
ULONG _Ecx;
ULONG _Edx;
__asm
{
mov eax, SubFunction
__emit 0x0F
__emit 0xA2 ;; CPUID
mov _Eax, eax
mov _Ebx, ebx
mov _Ecx, ecx
mov _Edx, edx
}
*EaxRegister = _Eax;
*EbxRegister = _Ebx;
*EcxRegister = _Ecx;
*EdxRegister = _Edx;
return TRUE;
#else
return FALSE;
#endif // #ifdef _X86_
}
BOOL
Ia64CpuId(ULONG Reg, PULONG64 Val)
{
// XXX drewb - How should this be implemented?
#if defined(_IA64_) && defined(IA64_INLINE_ASSEMBLY)
ULONG64 _Val;
__asm mov t0, Reg;
__asm mov _Val, cpuid[t0];
*Val = _Val;
return TRUE;
#else
return FALSE;
#endif
}
STDMETHODIMP
LiveUserDebugServices::GetProcessorId(
THIS_
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG BufferUsed
)
{
if (BufferSize < sizeof(DEBUG_PROCESSOR_IDENTIFICATION_ALL))
{
return E_INVALIDARG;
}
ZeroMemory(Buffer, sizeof(DEBUG_PROCESSOR_IDENTIFICATION_ALL));
ULONG ProcArch, ProcLevel, ProcRevision;
#ifdef NT_NATIVE
NTSTATUS NtStatus;
SYSTEM_PROCESSOR_INFORMATION ProcInfo;
if (!NT_SUCCESS(NtStatus =
NtQuerySystemInformation(SystemProcessorInformation,
&ProcInfo, sizeof(ProcInfo),
NULL)))
{
return HRESULT_FROM_NT(NtStatus);
}
ProcArch = ProcInfo.ProcessorArchitecture;
ProcLevel = ProcInfo.ProcessorLevel;
ProcRevision = ProcInfo.ProcessorRevision;
#else
SYSTEM_INFO SystemInfo;
::GetSystemInfo(&SystemInfo);
ProcArch = SystemInfo.wProcessorArchitecture;
ProcLevel = SystemInfo.wProcessorLevel;
ProcRevision = SystemInfo.wProcessorRevision;
#endif
PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id =
(PDEBUG_PROCESSOR_IDENTIFICATION_ALL)Buffer;
ULONG64 Val;
switch(ProcArch)
{
case PROCESSOR_ARCHITECTURE_INTEL:
*BufferUsed = sizeof(DEBUG_PROCESSOR_IDENTIFICATION_X86);
Id->X86.Family = ProcLevel;
Id->X86.Model = (ProcRevision >> 8) & 0xf;
Id->X86.Stepping = ProcRevision & 0xf;
if (ProcLevel >= 5)
{
ULONG Eax, Ebx, Ecx, Edx;
if (X86CpuId(0, &Eax, &Ebx, &Ecx, &Edx))
{
*(PULONG)(Id->X86.VendorString + 0 * sizeof(ULONG)) = Ebx;
*(PULONG)(Id->X86.VendorString + 1 * sizeof(ULONG)) = Edx;
*(PULONG)(Id->X86.VendorString + 2 * sizeof(ULONG)) = Ecx;
}
}
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
*BufferUsed = sizeof(DEBUG_PROCESSOR_IDENTIFICATION_ALPHA);
Id->Alpha.Type = ProcLevel;
Id->Alpha.Revision = ProcRevision;
break;
case PROCESSOR_ARCHITECTURE_IA64:
*BufferUsed = sizeof(DEBUG_PROCESSOR_IDENTIFICATION_IA64);
Id->Ia64.Model = ProcLevel;
Id->Ia64.Revision = ProcRevision;
if (Ia64CpuId(3, &Val))
{
Id->Ia64.ArchRev = (ULONG)((Val >> 32) & 0xff);
Id->Ia64.Family = (ULONG)((Val >> 24) & 0xff);
Ia64CpuId(0, (PULONG64)
(Id->Ia64.VendorString + 0 * sizeof(ULONG64)));
Ia64CpuId(1, (PULONG64)
(Id->Ia64.VendorString + 1 * sizeof(ULONG64)));
}
break;
case PROCESSOR_ARCHITECTURE_AMD64:
*BufferUsed = sizeof(DEBUG_PROCESSOR_IDENTIFICATION_AMD64);
Id->Amd64.Family = ProcLevel;
Id->Amd64.Model = (ProcRevision >> 8) & 0xf;
Id->Amd64.Stepping = ProcRevision & 0xf;
break;
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetFileVersionInformation(
THIS_
IN PCSTR File,
IN PCSTR Item,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG VerInfoSize
)
{
#ifndef NT_NATIVE
PVOID AllInfo = GetAllFileVersionInfo((PSTR)File);
if (AllInfo == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT Status;
PVOID Val;
UINT ValSize;
if (VerQueryValue(AllInfo, (PSTR)Item, &Val, &ValSize))
{
Status = FillDataBuffer(Val, ValSize,
Buffer, BufferSize, VerInfoSize);
}
else
{
Status = WIN32_LAST_STATUS();
}
free(AllInfo);
return Status;
#else // #ifndef NT_NATIVE
return E_UNEXPECTED;
#endif // #ifndef NT_NATIVE
}
HRESULT
GetNtSystemProcessInformation(PSYSTEM_PROCESS_INFORMATION* ProcInfo)
{
NTSTATUS NtStatus;
PVOID Buffer;
SIZE_T BufferSize = 8192;
for (;;)
{
Buffer = NULL;
NtStatus = g_NtDllCalls.
NtAllocateVirtualMemory(NtCurrentProcess(),
&Buffer, 0, &BufferSize,
MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(NtStatus))
{
return HRESULT_FROM_NT(NtStatus);
}
NtStatus = g_NtDllCalls.
NtQuerySystemInformation(SystemProcessInformation,
Buffer, (ULONG)BufferSize, NULL);
if (NT_SUCCESS(NtStatus))
{
break;
}
g_NtDllCalls.NtFreeVirtualMemory(NtCurrentProcess(),
&Buffer, &BufferSize, MEM_RELEASE);
if (NtStatus == STATUS_INFO_LENGTH_MISMATCH)
{
BufferSize += 8192;
}
else
{
return HRESULT_FROM_NT(NtStatus);
}
}
*ProcInfo = (PSYSTEM_PROCESS_INFORMATION)Buffer;
return S_OK;
}
HRESULT
NtGetProcessIds(PULONG Ids, ULONG Count, PULONG ActualCount)
{
HRESULT Status;
PSYSTEM_PROCESS_INFORMATION ProcessInfo, ProcInfoBuffer;
if ((Status = GetNtSystemProcessInformation(&ProcInfoBuffer)) != S_OK)
{
return Status;
}
ULONG TotalOffset;
ULONG ProcessCount;
ProcessInfo = ProcInfoBuffer;
TotalOffset = 0;
ProcessCount = 0;
for (;;)
{
if (ProcessCount < Count)
{
Ids[ProcessCount] = (ULONG)(ULONG_PTR)ProcessInfo->UniqueProcessId;
}
ProcessCount++;
if (ProcessInfo->NextEntryOffset == 0)
{
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
((PUCHAR)ProcInfoBuffer + TotalOffset);
}
if (ActualCount != NULL)
{
*ActualCount = ProcessCount;
}
SIZE_T MemSize;
g_NtDllCalls.NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID*)&ProcInfoBuffer, &MemSize,
MEM_RELEASE);
return Status;
}
HRESULT
W9xGetProcessIds(PULONG Ids, ULONG Count, PULONG ActualCount)
{
#ifndef NT_NATIVE
HRESULT Status;
HANDLE Snap;
Snap = g_Kernel32Calls.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Snap == INVALID_HANDLE_VALUE)
{
return WIN32_LAST_STATUS();
}
ULONG ProcessCount = 0;
for (;;)
{
PROCESSENTRY32 Proc;
BOOL Succ;
Proc.dwSize = sizeof(Proc);
if (ProcessCount == 0)
{
Succ = g_Kernel32Calls.Process32First(Snap, &Proc);
}
else
{
Succ = g_Kernel32Calls.Process32Next(Snap, &Proc);
}
if (!Succ)
{
break;
}
if (ProcessCount < Count)
{
Ids[ProcessCount] = Proc.th32ProcessID;
}
ProcessCount++;
}
if (ActualCount != NULL)
{
*ActualCount = ProcessCount;
}
CloseHandle(Snap);
return S_OK;
#else
return E_UNEXPECTED;
#endif
}
STDMETHODIMP
LiveUserDebugServices::GetProcessIds(
THIS_
OUT OPTIONAL /* size_is(Count) */ PULONG Ids,
IN ULONG Count,
OUT OPTIONAL PULONG ActualCount
)
{
HRESULT Status;
// Allow privileged enumeration.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
switch(m_PlatformId)
{
case VER_PLATFORM_WIN32_NT:
return NtGetProcessIds(Ids, Count, ActualCount);
case VER_PLATFORM_WIN32_WINDOWS:
return W9xGetProcessIds(Ids, Count, ActualCount);
default:
return E_UNEXPECTED;
}
}
HRESULT
NtGetPidByExe(PCSTR ExeName, ULONG Flags, PULONG Id)
{
HRESULT Status;
// Rather than converting each process name to ANSI and
// comparing, convert the incoming name to Unicode so
// only one conversion is needed.
WCHAR WideName[MAX_PATH];
BOOL WideHasPath;
if (!MultiByteToWideChar(CP_ACP, 0, ExeName, -1,
WideName, sizeof(WideName) / sizeof(WCHAR)))
{
return WIN32_LAST_STATUS();
}
// Check if the given name has path components.
WideHasPath =
wcschr(WideName, '\\') != NULL ||
wcschr(WideName, '/') != NULL ||
(WideName[0] && WideName[1] == ':');
PSYSTEM_PROCESS_INFORMATION ProcessInfo, ProcInfoBuffer;
if ((Status = GetNtSystemProcessInformation(&ProcInfoBuffer)) != S_OK)
{
return Status;
}
ULONG TotalOffset;
ULONG FoundId;
ProcessInfo = ProcInfoBuffer;
TotalOffset = 0;
FoundId = DEBUG_ANY_ID;
Status = E_NOINTERFACE;
for (;;)
{
PWSTR ImageName;
if (ProcessInfo->ImageName.Buffer == NULL)
{
ImageName = SYSTEM_PROCESS_NAME_W;
}
else
{
ImageName = ProcessInfo->ImageName.Buffer;
}
if ((Flags & DEBUG_GET_PROC_FULL_MATCH) == 0 &&
!WideHasPath)
{
PWSTR Slash;
Slash = wcsrchr(ImageName, '\\');
if (Slash == NULL)
{
Slash = wcsrchr(ImageName, '/');
}
if (Slash != NULL)
{
ImageName = Slash + 1;
}
}
if (!_wcsicmp(ImageName, WideName))
{
if ((Flags & DEBUG_GET_PROC_ONLY_MATCH) &&
FoundId != DEBUG_ANY_ID)
{
Status = S_FALSE;
break;
}
Status = S_OK;
FoundId = (ULONG)(ULONG_PTR)ProcessInfo->UniqueProcessId;
*Id = FoundId;
if ((Flags & DEBUG_GET_PROC_ONLY_MATCH) == 0)
{
break;
}
}
if (ProcessInfo->NextEntryOffset == 0)
{
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
((PUCHAR)ProcInfoBuffer + TotalOffset);
}
SIZE_T MemSize;
g_NtDllCalls.NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID*)&ProcInfoBuffer, &MemSize,
MEM_RELEASE);
return Status;
}
HRESULT
W9xGetPidByExe(PCSTR ExeName, ULONG Flags, PULONG Id)
{
#ifndef NT_NATIVE
HRESULT Status;
HANDLE Snap;
Snap = g_Kernel32Calls.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Snap == INVALID_HANDLE_VALUE)
{
return WIN32_LAST_STATUS();
}
// Check if the given name has path components.
BOOL HasPath =
strchr(ExeName, '\\') != NULL ||
strchr(ExeName, '/') != NULL ||
(ExeName[0] && ExeName[1] == ':');
ULONG FoundId = DEBUG_ANY_ID;
BOOL First = TRUE;
for (;;)
{
PROCESSENTRY32 Proc;
BOOL Succ;
Proc.dwSize = sizeof(Proc);
if (First)
{
Succ = g_Kernel32Calls.Process32First(Snap, &Proc);
First = FALSE;
}
else
{
Succ = g_Kernel32Calls.Process32Next(Snap, &Proc);
}
if (!Succ)
{
break;
}
PSTR ImageName = Proc.szExeFile;
if ((Flags & DEBUG_GET_PROC_FULL_MATCH) == 0 &&
!HasPath)
{
PSTR Slash;
Slash = strrchr(ImageName, '\\');
if (Slash == NULL)
{
Slash = strrchr(ImageName, '/');
}
if (Slash != NULL)
{
ImageName = Slash + 1;
}
}
if (!_stricmp(ImageName, ExeName))
{
if ((Flags & DEBUG_GET_PROC_ONLY_MATCH) &&
FoundId != DEBUG_ANY_ID)
{
Status = S_FALSE;
break;
}
Status = S_OK;
FoundId = Proc.th32ProcessID;
*Id = FoundId;
if ((Flags & DEBUG_GET_PROC_ONLY_MATCH) == 0)
{
break;
}
}
}
CloseHandle(Snap);
return S_OK;
#else
return E_UNEXPECTED;
#endif
}
STDMETHODIMP
LiveUserDebugServices::GetProcessIdByExecutableName(
THIS_
IN PCSTR ExeName,
IN ULONG Flags,
OUT PULONG Id
)
{
HRESULT Status;
// Allow privileged enumeration.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
switch(m_PlatformId)
{
case VER_PLATFORM_WIN32_NT:
return NtGetPidByExe(ExeName, Flags, Id);
case VER_PLATFORM_WIN32_WINDOWS:
return W9xGetPidByExe(ExeName, Flags, Id);
default:
return E_UNEXPECTED;
}
}
HRESULT
ConvertProcessUnicodeString(HANDLE Process,
PUNICODE_STRING UniString,
PSTR* AnsiString)
{
HRESULT Status;
PSTR Ansi = NULL;
PWSTR Wide = NULL;
SIZE_T Done;
Wide = new WCHAR[UniString->Length + 1];
if (Wide == NULL)
{
return E_OUTOFMEMORY;
}
Ansi = new CHAR[UniString->Length + 1];
if (Ansi == NULL)
{
Status = E_OUTOFMEMORY;
goto Exit;
}
if (!::ReadProcessMemory(Process, UniString->Buffer, Wide,
(UniString->Length + 1) * sizeof(WCHAR), &Done))
{
Status = WIN32_LAST_STATUS();
goto Exit;
}
if (Done != (UniString->Length + 1) * sizeof(WCHAR))
{
Status = E_FAIL;
goto Exit;
}
if (!WideCharToMultiByte(CP_ACP, 0, Wide, UniString->Length + 1,
Ansi, UniString->Length + 1, NULL, NULL))
{
Status = WIN32_LAST_STATUS();
goto Exit;
}
*AnsiString = Ansi;
Ansi = NULL;
Status = S_OK;
Exit:
delete Ansi;
delete Wide;
return Status;
}
#ifndef NT_NATIVE
HRESULT
NtGetServiceStatus(PULONG NumServices,
LPENUM_SERVICE_STATUS_PROCESS* ServiceStatus)
{
SC_HANDLE Scm;
Scm = g_Advapi32Calls.OpenSCManagerA(NULL, NULL,
SC_MANAGER_CONNECT |
SC_MANAGER_ENUMERATE_SERVICE);
if (!Scm)
{
return WIN32_LAST_STATUS();
}
HRESULT Status;
LPENUM_SERVICE_STATUS_PROCESS Info;
ULONG InfoSize = 8 * 1024;
ULONG ExtraNeeded;
ULONG Resume;
ULONG Loop = 0;
//
// First pass through the loop allocates from an initial guess.
// If that isn't sufficient, we make another pass and allocate
// what is actually needed. Things may have changed due to
// other machine changes, so loop around a few times before
// giving up.
//
for (;;)
{
Info = (LPENUM_SERVICE_STATUS_PROCESS)malloc(InfoSize);
if (!Info)
{
Status = E_OUTOFMEMORY;
break;
}
Resume = 0;
if (!g_Advapi32Calls.EnumServicesStatusExA(Scm,
SC_ENUM_PROCESS_INFO,
SERVICE_WIN32,
SERVICE_ACTIVE,
(LPBYTE)Info,
InfoSize,
&ExtraNeeded,
NumServices,
&Resume,
NULL))
{
free(Info);
if (Loop > 2 || GetLastError() != ERROR_MORE_DATA)
{
Status = WIN32_LAST_STATUS();
break;
}
}
else
{
*ServiceStatus = Info;
Status = S_OK;
break;
}
InfoSize += ExtraNeeded;
Loop++;
}
CloseServiceHandle(Scm);
return Status;
}
HRESULT
NtGetProcessServiceNames(HRESULT RetStatus, ULONG ProcessId,
PSTR* Description, ULONG* DescriptionSize,
PULONG ActualDescriptionSize, PBOOL Any)
{
HRESULT Status;
if (!g_Advapi32Calls.EnumServicesStatusExA ||
!g_Advapi32Calls.OpenSCManagerA)
{
return RetStatus;
}
ULONG i, NumServices;
LPENUM_SERVICE_STATUS_PROCESS ServiceStatus;
BOOL AnyServices = FALSE;
if ((Status = NtGetServiceStatus(&NumServices, &ServiceStatus)) != S_OK)
{
// If we can't get the service status just leave the
// string unchanged and do not consider it a serious error.
return RetStatus;
}
for (i = 0; i < NumServices; i++)
{
if (ServiceStatus[i].ServiceStatusProcess.dwProcessId != ProcessId ||
!ServiceStatus[i].lpServiceName ||
!ServiceStatus[i].lpServiceName[0])
{
continue;
}
PSTR Intro;
if (AnyServices)
{
Intro = ",";
}
else if (*Any)
{
Intro = " Services: ";
}
else
{
Intro = "Services: ";
}
RetStatus = AppendToStringBuffer(RetStatus, Intro, !*Any,
Description, DescriptionSize,
ActualDescriptionSize);
RetStatus = AppendToStringBuffer(RetStatus,
ServiceStatus[i].lpServiceName, FALSE,
Description, DescriptionSize,
ActualDescriptionSize);
*Any = TRUE;
AnyServices = TRUE;
}
free(ServiceStatus);
return RetStatus;
}
HRESULT
NtGetProcessMtsPackageNames(HRESULT RetStatus, ULONG ProcessId,
PSTR* Description, ULONG* DescriptionSize,
PULONG ActualDescriptionSize, PBOOL Any)
{
HRESULT Status;
// Load and initialize ole32.dll so we can call CoCreateInstance.
if ((Status = InitDynamicCalls(&g_Ole32CallsDesc)) != S_OK ||
(Status = InitDynamicCalls(&g_OleAut32CallsDesc)) != S_OK ||
(Status = g_Ole32Calls.
CoInitializeEx(NULL, COINIT_MULTITHREADED)) != S_OK)
{
// Just leave things unchanged on failure.
return RetStatus;
}
IMtsGrp* MtsGrp = NULL;
long Packages;
long i;
BOOL AnyPackages = FALSE;
if ((Status = g_Ole32Calls.
CoCreateInstance(CLSID_MtsGrp, NULL, CLSCTX_ALL,
__uuidof(IMtsGrp), (void **)&MtsGrp)) != S_OK ||
(Status = MtsGrp->Refresh()) != S_OK ||
(Status = MtsGrp->get_Count(&Packages)) != S_OK)
{
goto Exit;
}
for (i = 0; i < Packages; i++)
{
IUnknown* Unk;
IMtsEvents* Events;
BSTR Name;
ULONG Pid;
if ((Status = MtsGrp->Item(i, &Unk)) != S_OK)
{
continue;
}
Status = Unk->QueryInterface(IID_IMtsEvents, (void **)&Events);
Unk->Release();
if (Status != S_OK)
{
continue;
}
Status = Events->GetProcessID((PLONG)&Pid);
if (Status == S_OK && Pid == ProcessId)
{
Status = Events->get_PackageName(&Name);
}
Events->Release();
if (Status != S_OK || Pid != ProcessId)
{
continue;
}
char NameA[MAX_PATH];
int Conv;
Conv = WideCharToMultiByte(CP_ACP, 0, Name, -1, NameA, sizeof(NameA),
NULL, NULL);
g_OleAut32Calls.SysFreeString(Name);
if (Conv > 0)
{
PSTR Intro;
if (AnyPackages)
{
Intro = ",";
}
else if (*Any)
{
Intro = " MTS Packages: ";
}
else
{
Intro = "MTS Packages: ";
}
RetStatus = AppendToStringBuffer(RetStatus, Intro, !*Any,
Description, DescriptionSize,
ActualDescriptionSize);
RetStatus = AppendToStringBuffer(RetStatus, NameA, FALSE,
Description, DescriptionSize,
ActualDescriptionSize);
*Any = TRUE;
AnyPackages = TRUE;
}
}
Exit:
if (MtsGrp)
{
MtsGrp->Release();
}
g_Ole32Calls.CoUninitialize();
return RetStatus;
}
#endif // #ifndef NT_NATIVE
HRESULT
NtGetProcDesc(ULONG ProcessId, ULONG Flags,
PSTR ExeName, ULONG ExeNameSize, PULONG ActualExeNameSize,
PSTR Description, ULONG DescriptionSize,
PULONG ActualDescriptionSize)
{
HRESULT Status;
if (ProcessId == 0)
{
// This is base system process so fake the description.
Status = FillStringBuffer(SYSTEM_PROCESS_NAME, 0,
ExeName, ExeNameSize, ActualExeNameSize);
FillStringBuffer("", 0,
Description, DescriptionSize, ActualDescriptionSize);
return Status;
}
NTSTATUS NtStatus;
HANDLE Process;
OBJECT_ATTRIBUTES ObjAttr;
CLIENT_ID ClientId;
ClientId.UniqueThread = NULL;
ClientId.UniqueProcess = (HANDLE)(ULONG_PTR)ProcessId;
InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL, NULL);
NtStatus = g_NtDllCalls.NtOpenProcess(&Process, PROCESS_ALL_ACCESS,
&ObjAttr, &ClientId);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
goto EH_Exit;
}
PROCESS_BASIC_INFORMATION ProcBasic;
ULONG Done;
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(Process, ProcessBasicInformation,
&ProcBasic, sizeof(ProcBasic), &Done);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
goto EH_Process;
}
if (Done != sizeof(ProcBasic))
{
Status = E_FAIL;
goto EH_Process;
}
if (ProcBasic.PebBaseAddress == 0)
{
// This process has no PEB so fake the description.
Status = FillStringBuffer(PEBLESS_PROCESS_NAME, 0,
ExeName, ExeNameSize, ActualExeNameSize);
FillStringBuffer("", 0,
Description, DescriptionSize, ActualDescriptionSize);
goto EH_Process;
}
PEB Peb;
SIZE_T DoneSize;
if (!::ReadProcessMemory(Process, ProcBasic.PebBaseAddress,
&Peb, sizeof(Peb), &DoneSize))
{
Status = WIN32_LAST_STATUS();
goto EH_Process;
}
if (DoneSize != sizeof(Peb))
{
Status = E_FAIL;
goto EH_Process;
}
RTL_USER_PROCESS_PARAMETERS Params;
if (!::ReadProcessMemory(Process, Peb.ProcessParameters,
&Params, sizeof(Params), &DoneSize))
{
Status = WIN32_LAST_STATUS();
goto EH_Process;
}
if (DoneSize != sizeof(Params))
{
Status = E_FAIL;
goto EH_Process;
}
if (Params.ImagePathName.Buffer != NULL)
{
PSTR AnsiImage, ImageName;
if ((Status = ConvertProcessUnicodeString(Process,
&Params.ImagePathName,
&AnsiImage)) != S_OK)
{
goto EH_Process;
}
if (Flags & DEBUG_PROC_DESC_NO_PATHS)
{
ImageName = strrchr(AnsiImage, '\\');
if (ImageName == NULL)
{
ImageName = strrchr(AnsiImage, '/');
}
if (ImageName == NULL)
{
ImageName = AnsiImage;
}
else
{
ImageName++;
}
}
else
{
ImageName = AnsiImage;
}
Status = FillStringBuffer(ImageName, 0,
ExeName, ExeNameSize, ActualExeNameSize);
delete AnsiImage;
}
else
{
Status = FillStringBuffer(SYSTEM_PROCESS_NAME, 0,
ExeName, ExeNameSize, ActualExeNameSize);
}
#ifndef NT_NATIVE
if ((Description && DescriptionSize) || ActualDescriptionSize)
{
BOOL Any = FALSE;
Status = NtGetProcessServiceNames(Status, ProcessId,
&Description, &DescriptionSize,
ActualDescriptionSize, &Any);
Status = NtGetProcessMtsPackageNames(Status, ProcessId,
&Description, &DescriptionSize,
ActualDescriptionSize, &Any);
if (!Any)
{
if (FillStringBuffer("", 0,
Description, DescriptionSize,
ActualDescriptionSize) == S_FALSE)
{
Status = S_FALSE;
}
}
}
else
#endif // #ifndef NT_NATIVE
{
FillStringBuffer("", 0,
Description, DescriptionSize, ActualDescriptionSize);
}
EH_Process:
g_NtDllCalls.NtClose(Process);
EH_Exit:
return Status;
}
HRESULT
W9xGetProcDesc(ULONG ProcessId, ULONG Flags,
PSTR ExeName, ULONG ExeNameSize, PULONG ActualExeNameSize,
PSTR Description, ULONG DescriptionSize,
PULONG ActualDescriptionSize)
{
#ifndef NT_NATIVE
HRESULT Status;
HANDLE Snap;
Snap = g_Kernel32Calls.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Snap == INVALID_HANDLE_VALUE)
{
return WIN32_LAST_STATUS();
}
BOOL First = TRUE;
Status = E_NOINTERFACE;
for (;;)
{
PROCESSENTRY32 Proc;
BOOL Succ;
Proc.dwSize = sizeof(Proc);
if (First)
{
Succ = g_Kernel32Calls.Process32First(Snap, &Proc);
First = FALSE;
}
else
{
Succ = g_Kernel32Calls.Process32Next(Snap, &Proc);
}
if (!Succ)
{
break;
}
if (Proc.th32ProcessID == ProcessId)
{
PSTR AnsiImage = Proc.szExeFile;
PSTR ImageName;
if (Flags & DEBUG_PROC_DESC_NO_PATHS)
{
ImageName = strrchr(AnsiImage, '\\');
if (ImageName == NULL)
{
ImageName = strrchr(AnsiImage, '/');
}
if (ImageName == NULL)
{
ImageName = AnsiImage;
}
else
{
ImageName++;
}
}
else
{
ImageName = AnsiImage;
}
Status = FillStringBuffer(ImageName, 0,
ExeName, ExeNameSize, ActualExeNameSize);
break;
}
}
CloseHandle(Snap);
// Win9x doesn't have services and we don't have to
// worry about IIS so there's currently nothing we provide
// as a description.
FillStringBuffer("", 0,
Description, DescriptionSize, ActualDescriptionSize);
return Status;
#else
return E_UNEXPECTED;
#endif
}
STDMETHODIMP
LiveUserDebugServices::GetProcessDescription(
THIS_
IN ULONG ProcessId,
IN ULONG Flags,
OUT OPTIONAL PSTR ExeName,
IN ULONG ExeNameSize,
OUT OPTIONAL PULONG ActualExeNameSize,
OUT OPTIONAL PSTR Description,
IN ULONG DescriptionSize,
OUT OPTIONAL PULONG ActualDescriptionSize
)
{
HRESULT Status;
// Allow privileged access.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
switch(m_PlatformId)
{
case VER_PLATFORM_WIN32_NT:
return NtGetProcDesc(ProcessId, Flags, ExeName, ExeNameSize,
ActualExeNameSize, Description, DescriptionSize,
ActualDescriptionSize);
case VER_PLATFORM_WIN32_WINDOWS:
return W9xGetProcDesc(ProcessId, Flags, ExeName, ExeNameSize,
ActualExeNameSize, Description, DescriptionSize,
ActualDescriptionSize);
default:
return E_UNEXPECTED;
}
}
HRESULT
InsertUserThread(PUSER_THREAD_INFO Threads, ULONG Index,
HRESULT Status, ULONG ThreadId, HANDLE ThreadHandle,
PUSER_THREAD_INFO PrevThreads, ULONG PrevInfoCount)
{
// Suspend the thread immediately to try and keep the
// process state as static as we can.
if (::SuspendThread(ThreadHandle) == -1)
{
Status = WIN32_LAST_STATUS();
::CloseHandle(ThreadHandle);
}
if (Status != S_OK)
{
while (Index-- > 0)
{
::ResumeThread(OS_HANDLE(Threads[Index].Handle));
::CloseHandle(OS_HANDLE(Threads[Index].Handle));
}
return Status;
}
Threads[Index].Handle = SERVICE_HANDLE(ThreadHandle);
Threads[Index].Id = ThreadId;
Threads[Index].Reserved = 0;
//
// Search for this thread in any previous information.
//
if (PrevThreads == NULL)
{
return S_OK;
}
ULONG i;
Status = S_FALSE;
for (i = 0; i < PrevInfoCount; i++)
{
if (PrevThreads[i].Id == ThreadId)
{
// Found a match.
Status = S_OK;
break;
}
}
return Status;
}
HRESULT
NtGetProcThreads(ULONG ProcessId, PUSER_THREAD_INFO Threads,
ULONG InfoCount, PULONG ThreadCount,
ULONG SysProcInfoSize,
PUSER_THREAD_INFO PrevThreads, ULONG PrevInfoCount)
{
HRESULT Status;
PSYSTEM_PROCESS_INFORMATION ProcessInfo, ProcInfoBuffer;
if ((Status = GetNtSystemProcessInformation(&ProcInfoBuffer)) != S_OK)
{
return Status;
}
ULONG TotalOffset;
ProcessInfo = ProcInfoBuffer;
TotalOffset = 0;
for (;;)
{
if (ProcessInfo->UniqueProcessId == (HANDLE)(ULONG_PTR)ProcessId ||
ProcessInfo->NextEntryOffset == 0)
{
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
((PUCHAR)ProcInfoBuffer + TotalOffset);
}
if (ProcessInfo->UniqueProcessId == (HANDLE)(ULONG_PTR)ProcessId)
{
if (ThreadCount != NULL)
{
*ThreadCount = ProcessInfo->NumberOfThreads;
}
if (ProcessInfo->NumberOfThreads < InfoCount)
{
InfoCount = ProcessInfo->NumberOfThreads;
}
// If the last iteration returned a different number
// of threads there's a mismatch so we need to return S_FALSE.
Status = (PrevThreads != NULL &&
PrevInfoCount != ProcessInfo->NumberOfThreads) ?
S_FALSE : S_OK;
PSYSTEM_THREAD_INFORMATION ThreadInfo = (PSYSTEM_THREAD_INFORMATION)
((PUCHAR)ProcessInfo + SysProcInfoSize);
for (ULONG i = 0; i < InfoCount; i++)
{
NTSTATUS NtStatus;
OBJECT_ATTRIBUTES ObjAttr;
HANDLE Thread;
HRESULT SingleStatus;
InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL, NULL);
NtStatus = g_NtDllCalls.
NtOpenThread(&Thread, THREAD_ALL_ACCESS, &ObjAttr,
&ThreadInfo->ClientId);
SingleStatus = InsertUserThread
(Threads, i, CONV_NT_STATUS(NtStatus),
(ULONG)(ULONG_PTR)ThreadInfo->ClientId.UniqueThread,
Thread, PrevThreads, PrevInfoCount);
if (SingleStatus == S_FALSE)
{
// Inserted thread didn't match so return S_FALSE.
Status = S_FALSE;
}
else if (SingleStatus != S_OK)
{
Status = SingleStatus;
break;
}
ThreadInfo++;
}
}
else
{
Status = E_NOINTERFACE;
}
SIZE_T MemSize;
g_NtDllCalls.NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID*)&ProcInfoBuffer, &MemSize,
MEM_RELEASE);
return Status;
}
// These functions are in the minidump library and are
// not really public functions, but we need them so
// just extern them here.
#ifdef _X86_
extern "C" BOOL WinInitialize(void);
extern "C" HANDLE WINAPI WinOpenThread(DWORD dwAccess, BOOL bInheritHandle,
DWORD ThreadId);
#else
#define WinInitialize() FALSE
#define WinOpenThread(dwAccess, bInheritHandle, ThreadId) NULL
#endif
HRESULT
W9xGetProcThreads(ULONG ProcessId, PUSER_THREAD_INFO Threads,
ULONG InfoCount, PULONG ThreadCount,
PUSER_THREAD_INFO PrevThreads, ULONG PrevInfoCount)
{
#ifndef NT_NATIVE
HRESULT Status;
HANDLE Snap;
if (!WinInitialize())
{
return WIN32_LAST_STATUS();
}
Snap = g_Kernel32Calls.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,
ProcessId);
if (Snap == INVALID_HANDLE_VALUE)
{
return WIN32_LAST_STATUS();
}
BOOL First = TRUE;
ULONG NumThreads = 0;
Status = S_OK;
for (;;)
{
THREADENTRY32 Thread;
BOOL Succ;
Thread.dwSize = sizeof(Thread);
if (First)
{
Succ = g_Kernel32Calls.Thread32First(Snap, &Thread);
First = FALSE;
}
else
{
Succ = g_Kernel32Calls.Thread32Next(Snap, &Thread);
}
if (!Succ)
{
break;
}
if (Thread.th32OwnerProcessID == ProcessId)
{
if (NumThreads < InfoCount)
{
HRESULT SingleStatus;
HANDLE Handle = WinOpenThread(THREAD_ALL_ACCESS, FALSE,
Thread.th32ThreadID);
SingleStatus = InsertUserThread
(Threads, NumThreads, CONV_W32_STATUS(Handle != NULL),
Thread.th32ThreadID, Handle, PrevThreads, PrevInfoCount);
if (SingleStatus == S_FALSE)
{
// Inserted thread didn't match so return S_FALSE.
Status = S_FALSE;
}
else if (SingleStatus != S_OK)
{
Status = SingleStatus;
break;
}
}
NumThreads++;
}
}
if (ThreadCount != NULL)
{
*ThreadCount = NumThreads;
}
if (Status == S_OK)
{
// If no threads were found the process must be invalid.
if (NumThreads == 0)
{
Status = E_NOINTERFACE;
}
else if (PrevThreads != NULL && NumThreads != PrevInfoCount)
{
// Thread count didn't match so return S_FALSE.
Status = S_FALSE;
}
}
CloseHandle(Snap);
return Status;
#else
return E_UNEXPECTED;
#endif
}
STDMETHODIMP
LiveUserDebugServices::GetProcessInfo(
THIS_
IN ULONG ProcessId,
OUT OPTIONAL PULONG64 Handle,
OUT OPTIONAL /* size_is(InfoCount) */ PUSER_THREAD_INFO Threads,
IN ULONG InfoCount,
OUT OPTIONAL PULONG ThreadCount
)
{
HANDLE Process;
HRESULT Status;
// Enable the privilege that allows the user to debug
// another process.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
if (Handle != NULL)
{
// This should always be a real process ID so there's
// no need to look for the special CSR value.
Process = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ProcessId);
if (Process == NULL)
{
return WIN32_LAST_STATUS();
}
*Handle = SERVICE_HANDLE(Process);
}
else
{
Process = NULL;
}
if (Threads != NULL || ThreadCount != NULL)
{
PUSER_THREAD_INFO PrevThreads;
ULONG PrevInfoCount;
ULONG _ThreadCount;
//
// We need to enumerate the threads in the process.
// This is a difficult thing to get right as
// the thread state for the process can continuously
// change. In order to try and get a clean snapshot
// of the thread state we iteratively enumerate until
// we get two consecutive snapshots that match.
//
// We suspend enumerated threads immediately to
// reduce churn from inside the process itself.
// We can't do anything about external processes so
// the enumeration could still get stale right after
// we return but by stopping everything in the process
// itself we do what we can.
//
// If the caller is just getting the count and
// not the actual thread information we don't bother
// iterating as there's no expectation that the
// thread state will be the same from one call to
// the next so there's no need to do the extra work.
//
if (Threads != NULL)
{
// Allocate an array to hold previous results. This
// can always be the same size as the return array
// because if there are more threads than can fit in
// the return array the snapshot will be wrong anyway
// so we just return without doing comparisons.
PrevThreads = new USER_THREAD_INFO[InfoCount];
if (PrevThreads == NULL)
{
Status = E_OUTOFMEMORY;
goto EH_CloseProc;
}
}
else
{
PrevThreads = NULL;
}
PrevInfoCount = 0;
for (;;)
{
switch(m_PlatformId)
{
case VER_PLATFORM_WIN32_NT:
Status = NtGetProcThreads(ProcessId, Threads, InfoCount,
&_ThreadCount, m_SysProcInfoSize,
PrevThreads, PrevInfoCount);
break;
case VER_PLATFORM_WIN32_WINDOWS:
Status = W9xGetProcThreads(ProcessId, Threads, InfoCount,
&_ThreadCount, PrevThreads,
PrevInfoCount);
break;
default:
Status = E_UNEXPECTED;
break;
}
//
// We can clean up any previous information now.
//
ULONG i;
for (i = 0; i < PrevInfoCount; i++)
{
::ResumeThread(OS_HANDLE(PrevThreads[i].Handle));
::CloseHandle(OS_HANDLE(PrevThreads[i].Handle));
}
if (Status != S_FALSE ||
_ThreadCount > InfoCount)
{
// The snapshot either matched the previous
// snapshot or there was an error. Also,
// if the snapshot overflowed the return array
// quit and give the caller the option of
// calling again when they notice they didn't
// get a complete snapshot.
break;
}
// There was a snapshot mismatch so loop again
// with this snapshot as the previous data.
PrevInfoCount = _ThreadCount;
if (PrevInfoCount > InfoCount)
{
PrevInfoCount = InfoCount;
}
RtlCopyMemory(PrevThreads, Threads,
PrevInfoCount * sizeof(*PrevThreads));
}
if (ThreadCount != NULL)
{
*ThreadCount = _ThreadCount;
}
delete PrevThreads;
EH_CloseProc:
if (Status != S_OK && Process != NULL)
{
::CloseHandle(Process);
}
}
else
{
Status = S_OK;
}
return Status;
}
HRESULT
ProcessIdToHandle(ULONG ProcessId, PHANDLE Process)
{
if (ProcessId == CSRSS_PROCESS_ID)
{
if (g_NtDllCalls.CsrGetProcessId != NULL)
{
ProcessId = (ULONG)(ULONG_PTR)g_NtDllCalls.CsrGetProcessId();
}
else
{
*Process = NULL;
return S_OK;
}
}
*Process = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ProcessId);
if (*Process == NULL)
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
NTSTATUS
CreateDebugObject(PHANDLE Object)
{
if (*Object != NULL)
{
return STATUS_SUCCESS;
}
OBJECT_ATTRIBUTES Attr;
InitializeObjectAttributes(&Attr, NULL, 0, NULL, g_AllAccessSecDesc);
return g_NtDllCalls.NtCreateDebugObject(Object, DEBUG_ALL_ACCESS,
&Attr, DEBUG_KILL_ON_CLOSE);
}
HRESULT
LiveUserDebugServices::SysGetProcessOptions(HANDLE Process, PULONG Options)
{
NTSTATUS NtStatus;
ULONG Flags;
if (m_PlatformId == VER_PLATFORM_WIN32_NT)
{
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(Process, ProcessDebugFlags,
&Flags, sizeof(Flags), NULL);
}
else
{
NtStatus = STATUS_INVALID_INFO_CLASS;
}
if (NtStatus == STATUS_INVALID_INFO_CLASS)
{
// The system doesn't support control over the
// debug flags. In the attach case this means
// the flags will be DEBUG_ONLY_THIS_PROCESS.
*Options = DEBUG_PROCESS_ONLY_THIS_PROCESS;
NtStatus = STATUS_SUCCESS;
}
else if (NT_SUCCESS(NtStatus))
{
*Options = 0;
if ((Flags & PROCESS_DEBUG_INHERIT) == 0)
{
*Options = DEBUG_PROCESS_ONLY_THIS_PROCESS;
}
}
return CONV_NT_STATUS(NtStatus);
}
HRESULT
LiveUserDebugServices::OpenDebugActiveProcess(ULONG ProcessId,
HANDLE Process)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT ||
!m_UseDebugObject)
{
return E_NOTIMPL;
}
// We're going to open the process's existing debug
// object and use it so we can't already have a debug object.
if (Process == NULL || m_DebugObject != NULL)
{
return E_UNEXPECTED;
}
NTSTATUS NtStatus;
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(Process, ProcessDebugObjectHandle,
&m_DebugObject, sizeof(m_DebugObject), NULL);
if (!NT_SUCCESS(NtStatus))
{
return HRESULT_FROM_NT(NtStatus);
}
return S_OK;
}
HRESULT
LiveUserDebugServices::CreateDebugActiveProcess(ULONG ProcessId,
HANDLE Process)
{
if (m_UseDebugObject)
{
if (Process == NULL)
{
return E_FAIL;
}
if (g_NtDllCalls.NtDebugActiveProcess == NULL)
{
return E_NOTIMPL;
}
NTSTATUS NtStatus;
NtStatus = CreateDebugObject(&m_DebugObject);
if (NT_SUCCESS(NtStatus))
{
NtStatus = g_NtDllCalls.NtDebugActiveProcess(Process,
m_DebugObject);
if (NT_SUCCESS(NtStatus))
{
g_NtDllCalls.DbgUiIssueRemoteBreakin(Process);
}
}
if (!NT_SUCCESS(NtStatus))
{
return HRESULT_FROM_NT(NtStatus);
}
}
#ifndef NT_NATIVE
else if (!::DebugActiveProcess(ProcessId))
{
return WIN32_LAST_STATUS();
}
#else
else
{
return E_UNEXPECTED;
}
#endif
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::AttachProcess(
THIS_
IN ULONG ProcessId,
IN ULONG AttachFlags,
OUT PULONG64 ProcessHandle,
OUT PULONG ProcessOptions
)
{
HRESULT Status;
// Enable the privilege that allows the user to debug
// another process.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
HANDLE Process;
if (ProcessId == CSRSS_PROCESS_ID)
{
CloseProfileUserMapping();
}
if ((Status = ProcessIdToHandle(ProcessId, &Process)) != S_OK)
{
return Status;
}
if ((Status = SysGetProcessOptions(Process, ProcessOptions)) != S_OK)
{
if (Process != NULL)
{
::CloseHandle(Process);
}
return Status;
}
if (AttachFlags & DEBUG_ATTACH_EXISTING)
{
Status = OpenDebugActiveProcess(ProcessId, Process);
}
else
{
Status = CreateDebugActiveProcess(ProcessId, Process);
}
if (Status != S_OK)
{
if (Process != NULL)
{
::CloseHandle(Process);
}
return Status;
}
*ProcessHandle = SERVICE_HANDLE(Process);
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::DetachProcess(
THIS_
IN ULONG ProcessId
)
{
HRESULT Status;
//
// A ProcessId of zero means that the caller is just
// checking for detach support and no actual detach
// should occur.
//
if (m_UseDebugObject)
{
if (g_NtDllCalls.NtRemoveProcessDebug == NULL)
{
return E_NOTIMPL;
}
// Check for the query before checking the debug
// object as the query may come in early.
if (ProcessId == 0)
{
return S_OK;
}
if (m_DebugObject == NULL)
{
return E_UNEXPECTED;
}
HANDLE Process;
if ((Status = ProcessIdToHandle(ProcessId, &Process)) != S_OK)
{
return Status;
}
if (Process == NULL)
{
return E_FAIL;
}
NTSTATUS NtStatus;
NtStatus = g_NtDllCalls.
NtRemoveProcessDebug(Process, m_DebugObject);
Status = CONV_NT_STATUS(NtStatus);
::CloseHandle(Process);
}
else
{
if (g_Kernel32Calls.DebugActiveProcessStop == NULL)
{
return E_NOTIMPL;
}
if (ProcessId == 0)
{
return S_OK;
}
if (!g_Kernel32Calls.DebugActiveProcessStop(ProcessId))
{
return WIN32_LAST_STATUS();
}
}
return S_OK;
}
#ifdef NT_NATIVE
NTSTATUS
NtSimpleCreateProcess(PCSTR CommandLine, ULONG CreateFlags,
HANDLE DebugObject, PPROCESS_INFORMATION RetInfo)
{
NTSTATUS Status;
ANSI_STRING Ansi;
UNICODE_STRING RawAppName, AppName;
UNICODE_STRING WideCmdLine;
PRTL_USER_PROCESS_PARAMETERS Params;
RTL_USER_PROCESS_INFORMATION Info;
if (CreateFlags & DEBUG_ONLY_THIS_PROCESS)
{
// The hacked way of controlling debug inheritance
// is via the low bit of the debug object handle.
// If the bit is set it means do not inherit.
DebugObject = (HANDLE)((ULONG_PTR)DebugObject | 1);
}
//
// This is a simple interface, so assume the first
// space-delimited token is the executable to run.
//
PCSTR ExeStart, ExeEnd;
ExeStart = CommandLine;
while (*ExeStart == ' ' || *ExeStart == '\t')
{
ExeStart++;
}
if (*ExeStart == 0)
{
return STATUS_INVALID_PARAMETER;
}
ExeEnd = ExeStart;
while (*ExeEnd && !(*ExeEnd == ' ' || *ExeEnd == '\t'))
{
ExeEnd++;
}
Ansi.Buffer = (PSTR)ExeStart;
Ansi.Length = (USHORT)(ExeEnd - ExeStart);
Ansi.MaximumLength = Ansi.Length;
Status = RtlAnsiStringToUnicodeString(&RawAppName, &Ansi, TRUE);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = RtlDosPathNameToNtPathName_U(RawAppName.Buffer, &AppName,
NULL, NULL);
if (!NT_SUCCESS(Status))
{
goto EH_RawAppName;
}
RtlInitAnsiString(&Ansi, CommandLine);
Status = RtlAnsiStringToUnicodeString(&WideCmdLine, &Ansi, TRUE);
if (!NT_SUCCESS(Status))
{
goto EH_AppName;
}
Status = RtlCreateProcessParameters(&Params, &AppName, NULL, NULL,
&WideCmdLine, NULL, NULL, NULL,
NULL, NULL);
if (!NT_SUCCESS(Status))
{
goto EH_WideCmdLine;
}
Info.Length = sizeof(Info);
Status = RtlCreateUserProcess(&AppName, OBJ_CASE_INSENSITIVE,
Params, NULL, NULL, NULL, FALSE,
DebugObject, NULL, &Info);
RtlDestroyProcessParameters(Params);
if (NT_SUCCESS(Status))
{
RetInfo->dwProcessId = HandleToUlong(Info.ClientId.UniqueProcess);
RetInfo->dwThreadId = HandleToUlong(Info.ClientId.UniqueThread);
RetInfo->hProcess = Info.Process;
RetInfo->hThread = Info.Thread;
if ((CreateFlags & CREATE_SUSPENDED) == 0)
{
NtResumeThread(Info.Thread, NULL);
}
}
EH_WideCmdLine:
RtlFreeUnicodeString(&WideCmdLine);
EH_AppName:
RtlFreeUnicodeString(&AppName);
EH_RawAppName:
RtlFreeUnicodeString(&RawAppName);
return Status;
}
#endif // #ifdef NT_NATIVE
#define DHEAP_ENV "_NO_DEBUG_HEAP"
STDMETHODIMP
LiveUserDebugServices::CreateProcess(
THIS_
IN PSTR CommandLine,
IN ULONG CreateFlags,
OUT PULONG ProcessId,
OUT PULONG ThreadId,
OUT PULONG64 ProcessHandle,
OUT PULONG64 ThreadHandle
)
{
HRESULT Status;
// Enable the privilege that allows the user to debug
// another process.
if ((Status = EnableDebugPrivilege()) != S_OK)
{
return Status;
}
// The system looks at the environment variable
// _NO_DEBUG_HEAP to determine whether the new
// process should use the debug heap or not. If
// the caller has requested the normal heap
// set this environment variable so that it's
// inherited.
if (CreateFlags & DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP)
{
::SetEnvironmentVariable(DHEAP_ENV, "1");
// Turn off this flag since it's not meaningful
// to CreateProcess itself.
CreateFlags &= ~DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP;
}
PROCESS_INFORMATION ProcInfo;
#ifndef NT_NATIVE
HANDLE OldDebugObject;
BOOL SetOldDebugObject = FALSE;
Status = S_OK;
if (m_UseDebugObject)
{
//
// Set up this thread's debug object to the one that
// we're using so that our debug object is used when
// debugging the new process. This lets us continue
// to use the normal Win32 CreateProcess call rather
// than trying to go through NtCreateProcessEx and
// guarantees we get all the Win32 process creation logic.
//
if (g_NtDllCalls.DbgUiSetThreadDebugObject == NULL)
{
Status = E_NOTIMPL;
}
else
{
NTSTATUS NtStatus;
OldDebugObject = g_NtDllCalls.DbgUiGetThreadDebugObject();
NtStatus = CreateDebugObject(&m_DebugObject);
if (NT_SUCCESS(NtStatus))
{
g_NtDllCalls.DbgUiSetThreadDebugObject(m_DebugObject);
SetOldDebugObject = TRUE;
}
else
{
Status = HRESULT_FROM_NT(NtStatus);
}
}
}
if (Status == S_OK)
{
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
if (!::CreateProcess(NULL, CommandLine, NULL, NULL, TRUE,
CreateFlags, NULL, NULL,
&StartupInfo, &ProcInfo))
{
Status = WIN32_LAST_STATUS();
}
else
{
Status = S_OK;
}
}
if (SetOldDebugObject)
{
g_NtDllCalls.DbgUiSetThreadDebugObject(OldDebugObject);
}
#else // #ifndef NT_NATIVE
if (!m_UseDebugObject)
{
Status = E_UNEXPECTED;
}
else
{
NTSTATUS NtStatus;
NtStatus = CreateDebugObject(&m_DebugObject);
if (NT_SUCCESS(NtStatus))
{
NtStatus = NtSimpleCreateProcess(CommandLine, CreateFlags,
m_DebugObject, &ProcInfo);
}
Status = CONV_NT_STATUS(NtStatus);
}
#endif // #ifndef NT_NATIVE
// Clear the special debug heap variable so it
// isn't inadvertently used somewhere else.
::SetEnvironmentVariable(DHEAP_ENV, NULL);
if (Status == S_OK)
{
*ProcessId = ProcInfo.dwProcessId;
*ThreadId = ProcInfo.dwThreadId;
*ProcessHandle = SERVICE_HANDLE(ProcInfo.hProcess);
*ThreadHandle = SERVICE_HANDLE(ProcInfo.hThread);
}
return Status;
}
STDMETHODIMP
LiveUserDebugServices::TerminateProcess(
THIS_
IN ULONG64 Process,
IN ULONG ExitCode
)
{
if (!::TerminateProcess(OS_HANDLE(Process), ExitCode))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::AbandonProcess(
THIS_
IN ULONG64 Process
)
{
HRESULT Status;
//
// In order to abandon a process but still leave it
// as being debugged we need to get the process's
// debug object and duplicate it into the debuggee
// process. This gives the debuggee process itself
// a reference to its debug object, creating a circle
// that will keep the process alive and in the debugged
// state.
//
// This circular reference will also mean that the
// process must be manually killed. This may be
// something interesting to address at some point.
//
if (m_DebugObject == NULL)
{
return E_NOTIMPL;
}
HANDLE Dup;
if (!::DuplicateHandle(GetCurrentProcess(), m_DebugObject,
OS_HANDLE(Process), &Dup, 0, FALSE,
DUPLICATE_SAME_ACCESS))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetProcessExitCode(
THIS_
IN ULONG64 Process,
OUT PULONG ExitCode
)
{
if (!::GetExitCodeProcess(OS_HANDLE(Process), ExitCode))
{
return WIN32_LAST_STATUS();
}
return *ExitCode == STILL_ACTIVE ? S_FALSE : S_OK;
}
STDMETHODIMP
LiveUserDebugServices::CloseHandle(
THIS_
IN ULONG64 Handle
)
{
if (Handle == 0)
{
return S_FALSE;
}
if (!::CloseHandle(OS_HANDLE(Handle)))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::SetProcessOptions(
THIS_
IN ULONG64 Process,
IN ULONG Options
)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
{
return E_NOTIMPL;
}
NTSTATUS NtStatus;
ULONG NtFlags = 0;
if ((Options & DEBUG_PROCESS_ONLY_THIS_PROCESS) == 0)
{
NtFlags |= PROCESS_DEBUG_INHERIT;
}
NtStatus = g_NtDllCalls.
NtSetInformationProcess(OS_HANDLE(Process), ProcessDebugFlags,
&NtFlags, sizeof(NtFlags));
if (NtStatus == STATUS_INVALID_INFO_CLASS)
{
return E_NOTIMPL;
}
else
{
return CONV_NT_STATUS(NtStatus);
}
}
STDMETHODIMP
LiveUserDebugServices::SetDebugObjectOptions(
THIS_
IN ULONG64 DebugObject,
IN ULONG Options
)
{
if (DebugObject == 0)
{
if (m_DebugObject == NULL)
{
if (g_Kernel32Calls.DebugSetProcessKillOnExit == NULL)
{
return E_NOTIMPL;
}
if (!g_Kernel32Calls.
DebugSetProcessKillOnExit((Options &
DEBUG_PROCESS_DETACH_ON_EXIT) == 0))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
DebugObject = SERVICE_HANDLE(m_DebugObject);
}
if (g_NtDllCalls.NtSetInformationDebugObject == NULL)
{
return E_NOTIMPL;
}
NTSTATUS NtStatus;
ULONG NtFlags = 0;
if ((Options & DEBUG_PROCESS_DETACH_ON_EXIT) == 0)
{
NtFlags |= DEBUG_KILL_ON_CLOSE;
}
NtStatus = g_NtDllCalls.
NtSetInformationDebugObject(OS_HANDLE(DebugObject), DebugObjectFlags,
&NtFlags, sizeof(NtFlags), NULL);
return CONV_NT_STATUS(NtStatus);
}
STDMETHODIMP
LiveUserDebugServices::GetProcessDebugObject(
THIS_
IN ULONG64 Process,
OUT PULONG64 DebugObject
)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
{
return E_NOTIMPL;
}
NTSTATUS NtStatus;
HANDLE ObjHandle;
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(OS_HANDLE(Process), ProcessDebugObjectHandle,
&ObjHandle, sizeof(ObjHandle), NULL);
if (!NT_SUCCESS(NtStatus))
{
return HRESULT_FROM_NT(NtStatus);
}
*DebugObject = SERVICE_HANDLE(ObjHandle);
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::DuplicateHandle(
THIS_
IN ULONG64 InProcess,
IN ULONG64 InHandle,
IN ULONG64 OutProcess,
IN ULONG DesiredAccess,
IN ULONG Inherit,
IN ULONG Options,
OUT PULONG64 OutHandle
)
{
HANDLE Dup;
if (!::DuplicateHandle(OS_HANDLE(InProcess), OS_HANDLE(InHandle),
OS_HANDLE(OutProcess), &Dup,
DesiredAccess, Inherit, Options))
{
return WIN32_LAST_STATUS();
}
*OutHandle = SERVICE_HANDLE(Dup);
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::ReadVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
SIZE_T SizeRead;
if (!::ReadProcessMemory(OS_HANDLE(Process),
(LPCVOID)(ULONG_PTR)Offset,
Buffer, BufferSize, &SizeRead))
{
return WIN32_LAST_STATUS();
}
if (BytesRead != NULL)
{
*BytesRead = (ULONG)SizeRead;
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::WriteVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesWritten
)
{
SIZE_T SizeWritten;
if (!::WriteProcessMemory(OS_HANDLE(Process),
(LPVOID)(ULONG_PTR)Offset,
Buffer, BufferSize, &SizeWritten))
{
return WIN32_LAST_STATUS();
}
if (BytesWritten != NULL)
{
*BytesWritten = (ULONG)SizeWritten;
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::QueryVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BufferUsed
)
{
if (BufferSize < sizeof(MEMORY_BASIC_INFORMATION))
{
return E_INVALIDARG;
}
if (BufferUsed != NULL)
{
*BufferUsed = sizeof(MEMORY_BASIC_INFORMATION);
}
if (!::VirtualQueryEx(OS_HANDLE(Process),
(LPCVOID)(ULONG_PTR)Offset,
(PMEMORY_BASIC_INFORMATION)Buffer,
sizeof(MEMORY_BASIC_INFORMATION)))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::ProtectVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN ULONG64 Size,
IN ULONG NewProtect,
OUT PULONG OldProtect
)
{
BOOL Status = ::VirtualProtectEx(OS_HANDLE(Process),
(PVOID)(ULONG_PTR)Offset, (SIZE_T)Size,
NewProtect, OldProtect);
return CONV_W32_STATUS(Status);
}
STDMETHODIMP
LiveUserDebugServices::AllocVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN ULONG64 Size,
IN ULONG Type,
IN ULONG Protect,
OUT PULONG64 AllocOffset
)
{
PVOID Addr = ::VirtualAllocEx(OS_HANDLE(Process), (PVOID)(ULONG_PTR)Offset,
(SIZE_T)Size, Type, Protect);
if (Addr == NULL)
{
return WIN32_LAST_STATUS();
}
*AllocOffset = (ULONG64)(LONG64)(ULONG_PTR)Addr;
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::FreeVirtual(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN ULONG64 Size,
IN ULONG Type
)
{
BOOL Status = ::VirtualFreeEx(OS_HANDLE(Process), (PVOID)(ULONG_PTR)Offset,
(SIZE_T)Size, Type);
return CONV_W32_STATUS(Status);
}
STDMETHODIMP
LiveUserDebugServices::ReadHandleData(
THIS_
IN ULONG64 Process,
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
{
return E_NOTIMPL;
}
HANDLE Dup = NULL;
if (DataType != DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT &&
!::DuplicateHandle(OS_HANDLE(Process), OS_HANDLE(Handle),
GetCurrentProcess(), &Dup, 0, FALSE,
DUPLICATE_SAME_ACCESS))
{
return WIN32_LAST_STATUS();
}
ULONG64 NtBuffer[1024 / sizeof(ULONG64)];
ULONG Used = 0;
NTSTATUS NtStatus;
HRESULT Status = S_OK;
switch(DataType)
{
case DEBUG_HANDLE_DATA_TYPE_BASIC:
Used = sizeof(DEBUG_HANDLE_DATA_BASIC);
if (Buffer == NULL)
{
break;
}
if (BufferSize < Used)
{
Status = E_INVALIDARG;
break;
}
POBJECT_BASIC_INFORMATION NtBasic;
NtBasic = (POBJECT_BASIC_INFORMATION)NtBuffer;
NtStatus = g_NtDllCalls.NtQueryObject(Dup, ObjectBasicInformation,
NtBasic, sizeof(*NtBasic), NULL);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
break;
}
PDEBUG_HANDLE_DATA_BASIC Basic;
Basic = (PDEBUG_HANDLE_DATA_BASIC)Buffer;
Basic->TypeNameSize = NtBasic->TypeInfoSize / sizeof(WCHAR);
Basic->ObjectNameSize = NtBasic->NameInfoSize / sizeof(WCHAR);
Basic->Attributes = NtBasic->Attributes;
Basic->GrantedAccess = NtBasic->GrantedAccess;
Basic->HandleCount = NtBasic->HandleCount;
Basic->PointerCount = NtBasic->PointerCount;
break;
case DEBUG_HANDLE_DATA_TYPE_TYPE_NAME:
POBJECT_TYPE_INFORMATION NtType;
NtType = (POBJECT_TYPE_INFORMATION)NtBuffer;
NtStatus = g_NtDllCalls.NtQueryObject(Dup, ObjectTypeInformation,
NtType, sizeof(NtBuffer), NULL);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
break;
}
if (NtType->TypeName.Buffer == NULL)
{
Used = 1;
if (Buffer != NULL && BufferSize > 0)
{
*(PCHAR)Buffer = 0;
}
break;
}
Used = NtType->TypeName.Length / sizeof(WCHAR) + 1;
NtType->TypeName.Buffer[Used - 1] = 0;
if (Buffer != NULL &&
WideCharToMultiByte(CP_ACP, 0, NtType->TypeName.Buffer,
-1, (LPSTR)Buffer, BufferSize,
NULL, NULL) == 0)
{
Status = WIN32_LAST_STATUS();
break;
}
break;
case DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME:
POBJECT_NAME_INFORMATION NtName;
NtName = (POBJECT_NAME_INFORMATION)NtBuffer;
NtStatus = g_NtDllCalls.NtQueryObject(Dup, ObjectNameInformation,
NtName, sizeof(NtBuffer), NULL);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
break;
}
if (NtName->Name.Buffer == NULL)
{
Used = 1;
if (Buffer != NULL && BufferSize > 0)
{
*(PCHAR)Buffer = 0;
}
break;
}
Used = NtName->Name.Length / sizeof(WCHAR) + 1;
NtName->Name.Buffer[Used - 1] = 0;
if (Buffer != NULL &&
WideCharToMultiByte(CP_ACP, 0, NtName->Name.Buffer,
-1, (LPSTR)Buffer, BufferSize,
NULL, NULL) == 0)
{
Status = WIN32_LAST_STATUS();
break;
}
break;
case DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT:
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(OS_HANDLE(Process), ProcessHandleCount,
Buffer, BufferSize, &Used);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
}
break;
}
if (DataSize != NULL)
{
*DataSize = Used;
}
if (Dup != NULL)
{
::CloseHandle(Dup);
}
return Status;
}
STDMETHODIMP
LiveUserDebugServices::SuspendThreads(
THIS_
IN ULONG Count,
IN /* size_is(Count) */ PULONG64 Threads,
OUT OPTIONAL /* size_is(Count) */ PULONG SuspendCounts
)
{
ULONG i;
HRESULT Status;
Status = S_OK;
for (i = 0; i < Count; i++)
{
ULONG OldCount = ::SuspendThread(OS_HANDLE(Threads[i]));
if (OldCount == -1)
{
Status = WIN32_LAST_STATUS();
}
if (SuspendCounts != NULL)
{
SuspendCounts[i] = OldCount + 1;
}
}
return Status;
}
STDMETHODIMP
LiveUserDebugServices::ResumeThreads(
THIS_
IN ULONG Count,
IN /* size_is(Count) */ PULONG64 Threads,
OUT OPTIONAL /* size_is(Count) */ PULONG SuspendCounts
)
{
ULONG i;
HRESULT Status;
Status = S_OK;
for (i = 0; i < Count; i++)
{
ULONG OldCount = ::ResumeThread(OS_HANDLE(Threads[i]));
if (OldCount == -1)
{
Status = WIN32_LAST_STATUS();
}
if (SuspendCounts != NULL)
{
SuspendCounts[i] = OldCount - 1;
}
}
return Status;
}
STDMETHODIMP
LiveUserDebugServices::GetContext(
THIS_
IN ULONG64 Thread,
IN ULONG Flags,
IN ULONG FlagsOffset,
OUT PVOID Context,
IN ULONG ContextSize,
OUT OPTIONAL PULONG ContextUsed
)
{
if (ContextSize < m_ContextSize)
{
return E_INVALIDARG;
}
if (ContextUsed != NULL)
{
*ContextUsed = m_ContextSize;
}
// Some platforms have alignment requirements for
// context information, so just get data into a
// local context structure, which presumably the
// compiler will properly align, and then copy
// it into the output buffer.
#ifndef _X86_
CONTEXT _LocalContext;
PCONTEXT LocalContext = &_LocalContext;
#else
PCONTEXT LocalContext = (PCONTEXT)Context;
#endif
// Initialize context flags here rather than making Context
// IN OUT to avoid sending a full CONTEXT just for a
// ULONG's worth of flags.
*(PULONG)((PUCHAR)LocalContext + FlagsOffset) = Flags;
if (!::GetThreadContext(OS_HANDLE(Thread), LocalContext))
{
return WIN32_LAST_STATUS();
}
#ifndef _X86_
memcpy(Context, LocalContext, m_ContextSize);
#endif
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::SetContext(
THIS_
IN ULONG64 Thread,
IN PVOID Context,
IN ULONG ContextSize,
OUT OPTIONAL PULONG ContextUsed
)
{
if (ContextSize < m_ContextSize)
{
return E_INVALIDARG;
}
if (ContextUsed != NULL)
{
*ContextUsed = m_ContextSize;
}
// Some platforms have alignment requirements for
// context information, so just get data into a
// local context structure, which presumably the
// compiler will properly align.
#ifndef _X86_
CONTEXT _LocalContext;
PCONTEXT LocalContext = &_LocalContext;
memcpy(LocalContext, Context, m_ContextSize);
#else
PCONTEXT LocalContext = (PCONTEXT)Context;
#endif
if (!::SetThreadContext(OS_HANDLE(Thread), LocalContext))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetProcessDataOffset(
THIS_
IN ULONG64 Process,
OUT PULONG64 Offset
)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
{
// XXX drewb - Equivalent?
return E_NOTIMPL;
}
NTSTATUS NtStatus;
PROCESS_BASIC_INFORMATION ProcessInformation;
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(OS_HANDLE(Process),
ProcessBasicInformation,
&ProcessInformation,
sizeof(ProcessInformation),
NULL);
*Offset = (ULONG64)(ProcessInformation.PebBaseAddress);
return CONV_NT_STATUS(NtStatus);
}
STDMETHODIMP
LiveUserDebugServices::GetThreadDataOffset(
THIS_
IN ULONG64 Thread,
OUT PULONG64 Offset
)
{
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
{
// XXX drewb - Equivalent?
return E_NOTIMPL;
}
NTSTATUS NtStatus;
THREAD_BASIC_INFORMATION ThreadInformation;
NtStatus = g_NtDllCalls.
NtQueryInformationThread(OS_HANDLE(Thread),
ThreadBasicInformation,
&ThreadInformation,
sizeof(ThreadInformation),
NULL);
*Offset = (ULONG64)(ThreadInformation.TebBaseAddress);
return CONV_NT_STATUS(NtStatus);
}
STDMETHODIMP
LiveUserDebugServices::DescribeSelector(
THIS_
IN ULONG64 Thread,
IN ULONG Selector,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BufferUsed
)
{
#ifdef _X86_
if (BufferSize < sizeof(LDT_ENTRY))
{
return E_INVALIDARG;
}
if (BufferUsed != NULL)
{
*BufferUsed = sizeof(LDT_ENTRY);
}
#endif
if (!::GetThreadSelectorEntry(OS_HANDLE(Thread), Selector,
(LPLDT_ENTRY)Buffer))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetCurrentTimeDateN(
THIS_
OUT PULONG64 TimeDate
)
{
// On NT only: *TimeDate = USER_SHARED_DATA->SystemTime;
*TimeDate = TimeDateStampToFileTime((ULONG)time(NULL));
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetCurrentSystemUpTimeN(
THIS_
OUT PULONG64 UpTime
)
{
// On NT only: *UpTime = USER_SHARED_DATA->InterruptTime;
*UpTime = TimeToFileTime(GetTickCount() / 1000);
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::GetProcessUpTimeN(
THIS_
IN ULONG64 Process,
OUT PULONG64 UpTime
)
{
if (m_PlatformId == VER_PLATFORM_WIN32_NT)
{
NTSTATUS NtStatus;
KERNEL_USER_TIMES KernelUserTimes;
NtStatus = g_NtDllCalls.
NtQueryInformationProcess(OS_HANDLE(Process),
ProcessTimes,
&KernelUserTimes,
sizeof(KernelUserTimes),
NULL);
if (NT_SUCCESS(NtStatus))
{
ULONG64 SystemUpTime;
GetCurrentTimeDateN(&SystemUpTime);
*UpTime = SystemUpTime - KernelUserTimes.CreateTime.QuadPart;
}
return CONV_NT_STATUS(NtStatus);
}
else
{
return E_NOTIMPL;
}
}
STDMETHODIMP
LiveUserDebugServices::RequestBreakIn(
THIS_
IN ULONG64 Process
)
{
if (g_Kernel32Calls.DebugBreakProcess != NULL)
{
if (!g_Kernel32Calls.DebugBreakProcess(OS_HANDLE(Process)))
{
return WIN32_LAST_STATUS();
}
}
else if (g_NtDllCalls.DbgUiIssueRemoteBreakin != NULL)
{
NTSTATUS Status;
Status = g_NtDllCalls.DbgUiIssueRemoteBreakin(OS_HANDLE(Process));
return CONV_NT_STATUS(Status);
}
else
{
HANDLE Thread;
DWORD ThreadId;
LPTHREAD_START_ROUTINE BreakFn;
#if defined(_WIN64)
BreakFn = (LPTHREAD_START_ROUTINE)g_NtDllCalls.DbgBreakPoint;
#else
BreakFn = (LPTHREAD_START_ROUTINE)g_Kernel32Calls.DebugBreak;
#endif
Thread =
::CreateRemoteThread(OS_HANDLE(Process), NULL, 0, BreakFn,
NULL, 0, &ThreadId);
if (Thread != NULL)
{
::CloseHandle(Thread);
}
else
{
return WIN32_LAST_STATUS();
}
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::WaitForEvent(
THIS_
IN ULONG Timeout,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BufferUsed
)
{
if (BufferSize < sizeof(DEBUG_EVENT))
{
return E_INVALIDARG;
}
if (BufferUsed != NULL)
{
*BufferUsed = sizeof(DEBUG_EVENT);
}
LPDEBUG_EVENT Event = (LPDEBUG_EVENT)Buffer;
HRESULT Status = E_NOTIMPL;
if (m_DebugObject == NULL)
{
#ifndef NT_NATIVE
if (!::WaitForDebugEvent(Event, Timeout))
{
if (GetLastError() == ERROR_SEM_TIMEOUT)
{
Status = S_FALSE;
}
else
{
Status = WIN32_LAST_STATUS();
}
}
else
{
Status = S_OK;
}
#endif
}
else if (g_NtDllCalls.NtWaitForDebugEvent != NULL &&
g_NtDllCalls.DbgUiConvertStateChangeStructure != NULL)
{
NTSTATUS NtStatus;
LARGE_INTEGER NtTimeout;
DBGUI_WAIT_STATE_CHANGE StateChange;
Win32ToNtTimeout(Timeout, &NtTimeout);
NtStatus = g_NtDllCalls.NtWaitForDebugEvent(m_DebugObject, FALSE,
&NtTimeout, &StateChange);
if (NtStatus == STATUS_TIMEOUT)
{
Status = S_FALSE;
}
else if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
}
else
{
NtStatus = g_NtDllCalls.
DbgUiConvertStateChangeStructure(&StateChange, Event);
// If the conversion fails we'll lose an event, but
// there's nothing else that can be done. Conversion
// failures will only occur in out-of-resource situations
// so normal debugging will not be affected.
Status = CONV_NT_STATUS(NtStatus);
}
}
if (Status != S_OK)
{
return Status;
}
m_EventProcessId = Event->dwProcessId;
m_EventThreadId = Event->dwThreadId;
#ifdef DBG_WAITFOREVENT
g_NtDllCalls.DbgPrint("Event %d for %X.%X\n",
Event->dwDebugEventCode, Event->dwProcessId,
Event->dwThreadId);
#endif
// If this is responding to a remote request then
// we can't return file handles.
if (m_Remote)
{
switch(Event->dwDebugEventCode)
{
case CREATE_PROCESS_DEBUG_EVENT:
::CloseHandle(Event->u.CreateProcessInfo.hFile);
Event->u.CreateProcessInfo.hFile = NULL;
break;
case LOAD_DLL_DEBUG_EVENT:
::CloseHandle(Event->u.LoadDll.hFile);
Event->u.LoadDll.hFile = NULL;
break;
}
}
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::ContinueEvent(
THIS_
IN ULONG ContinueStatus
)
{
#ifdef DBG_WAITFOREVENT
g_NtDllCalls.DbgPrint("Continue event for %X.%X\n",
m_EventProcessId, m_EventThreadId);
#endif
if (m_EventProcessId == 0)
{
return E_UNEXPECTED;
}
if (m_DebugObject != NULL && g_NtDllCalls.NtDebugContinue != NULL)
{
NTSTATUS NtStatus;
CLIENT_ID ClientId;
ClientId.UniqueProcess = UlongToHandle(m_EventProcessId);
ClientId.UniqueThread = UlongToHandle(m_EventThreadId);
NtStatus = g_NtDllCalls.NtDebugContinue(m_DebugObject, &ClientId,
ContinueStatus);
if (!NT_SUCCESS(NtStatus))
{
return HRESULT_FROM_NT(NtStatus);
}
}
#ifndef NT_NATIVE
else if (!::ContinueDebugEvent(m_EventProcessId, m_EventThreadId,
ContinueStatus))
{
return WIN32_LAST_STATUS();
}
#else
else
{
return E_UNEXPECTED;
}
#endif
m_EventProcessId = 0;
return S_OK;
}
STDMETHODIMP
LiveUserDebugServices::InsertCodeBreakpoint(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN ULONG MachineType,
OUT PVOID Storage,
IN ULONG StorageSize
)
{
// Generic breakpoint support is used so this method
// does not do anything.
return E_UNEXPECTED;
}
STDMETHODIMP
LiveUserDebugServices::RemoveCodeBreakpoint(
THIS_
IN ULONG64 Process,
IN ULONG64 Offset,
IN ULONG MachineType,
IN PVOID Storage,
IN ULONG StorageSize
)
{
// Generic breakpoint support is used so this method
// does not do anything.
return E_UNEXPECTED;
}
STDMETHODIMP
LiveUserDebugServices::GetFunctionTableListHead(
THIS_
IN ULONG64 Process,
OUT PULONG64 Offset
)
{
if (!g_NtDllCalls.RtlGetFunctionTableListHead)
{
*Offset = 0;
return E_NOINTERFACE;
}
else
{
*Offset = (ULONG64)(ULONG_PTR)
g_NtDllCalls.RtlGetFunctionTableListHead();
return S_OK;
}
}
STDMETHODIMP
LiveUserDebugServices::GetOutOfProcessFunctionTable(
THIS_
IN ULONG64 Process,
IN PSTR Dll,
IN ULONG64 Table,
IN OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG TableSize
)
{
#if !defined(NT_NATIVE) && defined(OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME)
HRESULT Status;
NTSTATUS NtStatus;
HMODULE DllHandle;
POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK Callback;
ULONG Entries;
PRUNTIME_FUNCTION Functions;
if ((DllHandle = LoadLibrary(Dll)) == NULL)
{
return WIN32_LAST_STATUS();
}
Callback = (POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)GetProcAddress
(DllHandle, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME);
if (!Callback)
{
Status = WIN32_LAST_STATUS();
goto Exit;
}
NtStatus = Callback(OS_HANDLE(Process), (PVOID)(ULONG_PTR)Table,
&Entries, &Functions);
if (!NT_SUCCESS(NtStatus))
{
Status = HRESULT_FROM_NT(NtStatus);
goto Exit;
}
if (Functions == NULL)
{
Status = E_NOINTERFACE;
goto Exit;
}
Status = FillDataBuffer(Functions, Entries * sizeof(RUNTIME_FUNCTION),
Buffer, BufferSize, TableSize);
// RtlProcessHeap turns into a TEB reference so it doesn't
// need to (and can't) be a dynamic reference.
g_NtDllCalls.RtlFreeHeap(RtlProcessHeap(), 0, Functions);
Exit:
FreeLibrary(DllHandle);
return Status;
#else
return E_UNEXPECTED;
#endif
}
//----------------------------------------------------------------------------
//
// Generated RPC proxies and stubs.
//
//----------------------------------------------------------------------------
// Generated headers.
#include "dbgsvc_p.hpp"
#include "dbgsvc_s.hpp"
#include "dbgsvc_p.cpp"
#include "dbgsvc_s.cpp"