3742 lines
91 KiB
C++
3742 lines
91 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// 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"
|