/* * PROJECT: ReactOS system libraries * LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0) * PURPOSE: RTL_DEBUG_INFORMATION implementation * COPYRIGHT: Copyright James Tabor * Copyright 2020 Mark Jansen (mark.jansen@reactos.org) */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include /* FUNCTIONS *****************************************************************/ PVOID NTAPI RtlpDebugBufferCommit(_Inout_ PRTL_DEBUG_INFORMATION Buffer, _In_ SIZE_T Size) { ULONG Remaining = Buffer->CommitSize - Buffer->OffsetFree; PVOID Result; NTSTATUS Status; if (Size > MAXLONG) return NULL; if (Remaining < Size) { PVOID Buf; SIZE_T CommitSize; Buf = (PVOID)((ULONG_PTR)Buffer->ViewBaseClient + Buffer->CommitSize); CommitSize = Size - Remaining; /* this is not going to end well.. */ if (CommitSize > MAXLONG) return NULL; Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return NULL; Buffer->CommitSize += CommitSize; Remaining = Buffer->CommitSize - Buffer->OffsetFree; /* Sanity check */ ASSERT(Remaining >= Size); if (Remaining < Size) return NULL; } Result = (PBYTE)Buffer->ViewBaseClient + Buffer->OffsetFree; Buffer->OffsetFree += Size; return Result; } /* * @unimplemented */ PRTL_DEBUG_INFORMATION NTAPI RtlCreateQueryDebugBuffer(_In_ ULONG Size, _In_ BOOLEAN EventPair) { NTSTATUS Status; PRTL_DEBUG_INFORMATION Buf = NULL; SIZE_T AllocationSize = Size ? Size : 0x400 * PAGE_SIZE; SIZE_T CommitSize = sizeof(*Buf); /* Reserve the memory */ Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &AllocationSize, MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(Status)) return NULL; /* Commit the first data, CommitSize is updated with the actual committed data */ Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { RtlDestroyQueryDebugBuffer(Buf); return NULL; } /* Fill out the minimum data required */ Buf->ViewBaseClient = Buf; Buf->ViewSize = (ULONG)AllocationSize; Buf->CommitSize = CommitSize; Buf->OffsetFree = sizeof(*Buf); return Buf; } /* * @unimplemented */ NTSTATUS NTAPI RtlDestroyQueryDebugBuffer(_In_ PRTL_DEBUG_INFORMATION Buf) { NTSTATUS Status = STATUS_SUCCESS; SIZE_T ViewSize = 0; if (NULL != Buf) { Status = NtFreeVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, &ViewSize, MEM_RELEASE); } if (!NT_SUCCESS(Status)) { DPRINT1("RtlDQDB: Failed to free VM!\n"); } return Status; } /* * Based on lib/epsapi/enum/modules.c by KJK::Hyperion. */ NTSTATUS NTAPI RtlpQueryRemoteProcessModules(HANDLE ProcessHandle, IN PRTL_PROCESS_MODULES Modules OPTIONAL, IN ULONG Size OPTIONAL, OUT PULONG ReturnedSize) { PROCESS_BASIC_INFORMATION pbiInfo; PPEB_LDR_DATA ppldLdrData; LDR_DATA_TABLE_ENTRY lmModule; PLIST_ENTRY pleListHead; PLIST_ENTRY pleCurEntry; PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL; NTSTATUS Status = STATUS_SUCCESS; ULONG UsedSize = sizeof(ULONG); ANSI_STRING AnsiString; PCHAR p; DPRINT("RtlpQueryRemoteProcessModules Start\n"); /* query the process basic information (includes the PEB address) */ Status = NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &pbiInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL); if (!NT_SUCCESS(Status)) { /* failure */ DPRINT("NtQueryInformationProcess 1 0x%lx\n", Status); return Status; } if (Modules == NULL || Size == 0) { Status = STATUS_INFO_LENGTH_MISMATCH; } else { Modules->NumberOfModules = 0; ModulePtr = &Modules->Modules[0]; Status = STATUS_SUCCESS; } /* get the address of the PE Loader data */ Status = NtReadVirtualMemory(ProcessHandle, &(pbiInfo.PebBaseAddress->Ldr), &ppldLdrData, sizeof(ppldLdrData), NULL); if (!NT_SUCCESS(Status)) { /* failure */ DPRINT("NtReadVirtualMemory 1 0x%lx\n", Status); return Status; } /* head of the module list: the last element in the list will point to this */ pleListHead = &ppldLdrData->InLoadOrderModuleList; /* get the address of the first element in the list */ Status = NtReadVirtualMemory(ProcessHandle, &(ppldLdrData->InLoadOrderModuleList.Flink), &pleCurEntry, sizeof(pleCurEntry), NULL); if (!NT_SUCCESS(Status)) { /* failure */ DPRINT("NtReadVirtualMemory 2 0x%lx\n", Status); return Status; } while(pleCurEntry != pleListHead) { UNICODE_STRING Unicode; WCHAR Buffer[256 * sizeof(WCHAR)]; /* read the current module */ Status = NtReadVirtualMemory(ProcessHandle, CONTAINING_RECORD(pleCurEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks), &lmModule, sizeof(LDR_DATA_TABLE_ENTRY), NULL); if (!NT_SUCCESS(Status)) { /* failure */ DPRINT( "NtReadVirtualMemory 3 0x%lx\n", Status); return Status; } /* Import module name from remote Process user space. */ Unicode.Length = lmModule.FullDllName.Length; Unicode.MaximumLength = lmModule.FullDllName.MaximumLength; Unicode.Buffer = Buffer; Status = NtReadVirtualMemory(ProcessHandle, lmModule.FullDllName.Buffer, Unicode.Buffer, Unicode.Length, NULL); if (!NT_SUCCESS(Status)) { /* failure */ DPRINT( "NtReadVirtualMemory 3 0x%lx\n", Status); return Status; } DPRINT(" Module %wZ\n", &Unicode); if (UsedSize > Size) { Status = STATUS_INFO_LENGTH_MISMATCH; } else if (Modules != NULL) { ModulePtr->Section = 0; ModulePtr->MappedBase = NULL; // FIXME: ?? ModulePtr->ImageBase = lmModule.DllBase; ModulePtr->ImageSize = lmModule.SizeOfImage; ModulePtr->Flags = lmModule.Flags; ModulePtr->LoadOrderIndex = 0; // FIXME: ?? ModulePtr->InitOrderIndex = 0; // FIXME: ?? ModulePtr->LoadCount = lmModule.LoadCount; AnsiString.Length = 0; AnsiString.MaximumLength = 256; AnsiString.Buffer = ModulePtr->FullPathName; RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, FALSE); p = strrchr(ModulePtr->FullPathName, '\\'); if (p != NULL) ModulePtr->OffsetToFileName = (USHORT)(p - ModulePtr->FullPathName + 1); else ModulePtr->OffsetToFileName = 0; ModulePtr++; Modules->NumberOfModules++; } UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION); /* address of the next module in the list */ pleCurEntry = lmModule.InLoadOrderLinks.Flink; } if (ReturnedSize != 0) *ReturnedSize = UsedSize; DPRINT("RtlpQueryRemoteProcessModules End\n"); /* success */ return (STATUS_SUCCESS); } /* * @unimplemented */ NTSTATUS NTAPI RtlQueryProcessDebugInformation(IN ULONG ProcessId, IN ULONG DebugInfoMask, IN OUT PRTL_DEBUG_INFORMATION Buf) { NTSTATUS Status = STATUS_SUCCESS; ULONG Pid = (ULONG)(ULONG_PTR) NtCurrentTeb()->ClientId.UniqueProcess; Buf->Flags = DebugInfoMask; Buf->OffsetFree = sizeof(RTL_DEBUG_INFORMATION); DPRINT("QueryProcessDebugInformation Start\n"); /* Currently ROS can not read-only from kenrel space, and doesn't check for boundaries inside kernel space that are page protected from every one but the kernel. aka page 0 - 2 */ if (ProcessId <= 1) { Status = STATUS_ACCESS_VIOLATION; } else if (Pid == ProcessId) { if (DebugInfoMask & RTL_DEBUG_QUERY_MODULES) { PRTL_PROCESS_MODULES Mp; ULONG ReturnSize = 0; /* I like this better than the do & while loop. */ Status = LdrQueryProcessModuleInformation(NULL, 0, &ReturnSize); Mp = RtlpDebugBufferCommit(Buf, ReturnSize); if (!Mp) { DPRINT1("RtlQueryProcessDebugInformation: Unable to commit %u\n", ReturnSize); } Status = LdrQueryProcessModuleInformation(Mp, ReturnSize, &ReturnSize); if (!NT_SUCCESS(Status)) { return Status; } Buf->Modules = Mp; } if (DebugInfoMask & RTL_DEBUG_QUERY_HEAPS) { PRTL_PROCESS_HEAPS Hp; ULONG HSize; Hp = (PRTL_PROCESS_HEAPS)((PUCHAR)Buf + Buf->OffsetFree); HSize = sizeof(RTL_PROCESS_HEAPS); if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_TAGS) { // TODO } if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_BLOCKS) { // TODO } Buf->Heaps = Hp; Buf->OffsetFree = Buf->OffsetFree + HSize; } if (DebugInfoMask & RTL_DEBUG_QUERY_LOCKS) { PRTL_PROCESS_LOCKS Lp; ULONG LSize; Lp = (PRTL_PROCESS_LOCKS)((PUCHAR)Buf + Buf->OffsetFree); LSize = sizeof(RTL_PROCESS_LOCKS); Buf->Locks = Lp; Buf->OffsetFree = Buf->OffsetFree + LSize; } DPRINT("QueryProcessDebugInformation end\n"); DPRINT("QueryDebugInfo : 0x%lx\n", Buf->OffsetFree); } else { HANDLE hProcess; CLIENT_ID ClientId; OBJECT_ATTRIBUTES ObjectAttributes; Buf->TargetProcessHandle = NtCurrentProcess(); ClientId.UniqueThread = 0; ClientId.UniqueProcess = (HANDLE)(ULONG_PTR)ProcessId; InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = NtOpenProcess(&hProcess, (PROCESS_ALL_ACCESS), &ObjectAttributes, &ClientId ); if (!NT_SUCCESS(Status)) { return Status; } if (DebugInfoMask & RTL_DEBUG_QUERY_MODULES) { PRTL_PROCESS_MODULES Mp; ULONG ReturnSize = 0; Status = RtlpQueryRemoteProcessModules(hProcess, NULL, 0, &ReturnSize); Mp = RtlpDebugBufferCommit(Buf, ReturnSize); if (!Mp) { DPRINT1("RtlQueryProcessDebugInformation: Unable to commit %u\n", ReturnSize); } Status = RtlpQueryRemoteProcessModules(hProcess, Mp, ReturnSize , &ReturnSize); if (!NT_SUCCESS(Status)) { return Status; } Buf->Modules = Mp; } if (DebugInfoMask & RTL_DEBUG_QUERY_HEAPS) { PRTL_PROCESS_HEAPS Hp; ULONG HSize; Hp = (PRTL_PROCESS_HEAPS)((PUCHAR)Buf + Buf->OffsetFree); HSize = sizeof(RTL_PROCESS_HEAPS); if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_TAGS) { // TODO } if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_BLOCKS) { // TODO } Buf->Heaps = Hp; Buf->OffsetFree = Buf->OffsetFree + HSize; } if (DebugInfoMask & RTL_DEBUG_QUERY_LOCKS) { PRTL_PROCESS_LOCKS Lp; ULONG LSize; Lp = (PRTL_PROCESS_LOCKS)((PUCHAR)Buf + Buf->OffsetFree); LSize = sizeof(RTL_PROCESS_LOCKS); Buf->Locks = Lp; Buf->OffsetFree = Buf->OffsetFree + LSize; } DPRINT("QueryProcessDebugInformation end\n"); DPRINT("QueryDebugInfo : 0x%lx\n", Buf->OffsetFree); } return Status; } /* EOL */