//---------------------------------------------------------------------------- // // Low-level debugging service interface implementations. // // Copyright (C) Microsoft Corporation, 2000-2001. // //---------------------------------------------------------------------------- #include "pch.hpp" #include #include #include "dbgsvc.hpp" #ifndef NT_NATIVE // #include 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"