1 /* 2 * PROJECT: ReactOS system libraries 3 * LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0) 4 * PURPOSE: RTL_DEBUG_INFORMATION implementation 5 * COPYRIGHT: Copyright James Tabor 6 * Copyright 2020 Mark Jansen (mark.jansen@reactos.org) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <rtl.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* FUNCTIONS *****************************************************************/ 17 18 PVOID 19 NTAPI 20 RtlpDebugBufferCommit(_Inout_ PRTL_DEBUG_INFORMATION Buffer, 21 _In_ SIZE_T Size) 22 { 23 ULONG Remaining = Buffer->CommitSize - Buffer->OffsetFree; 24 PVOID Result; 25 NTSTATUS Status; 26 27 if (Size > MAXLONG) 28 return NULL; 29 30 if (Remaining < Size) 31 { 32 PVOID Buf; 33 SIZE_T CommitSize; 34 35 Buf = (PVOID)((ULONG_PTR)Buffer->ViewBaseClient + Buffer->CommitSize); 36 CommitSize = Size - Remaining; 37 38 /* this is not going to end well.. */ 39 if (CommitSize > MAXLONG) 40 return NULL; 41 42 Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE); 43 if (!NT_SUCCESS(Status)) 44 return NULL; 45 46 Buffer->CommitSize += CommitSize; 47 Remaining = Buffer->CommitSize - Buffer->OffsetFree; 48 /* Sanity check */ 49 ASSERT(Remaining >= Size); 50 if (Remaining < Size) 51 return NULL; 52 } 53 54 Result = (PBYTE)Buffer->ViewBaseClient + Buffer->OffsetFree; 55 Buffer->OffsetFree += Size; 56 57 return Result; 58 } 59 60 61 /* 62 * @unimplemented 63 */ 64 PRTL_DEBUG_INFORMATION 65 NTAPI 66 RtlCreateQueryDebugBuffer(_In_ ULONG Size, 67 _In_ BOOLEAN EventPair) 68 { 69 NTSTATUS Status; 70 PRTL_DEBUG_INFORMATION Buf = NULL; 71 SIZE_T AllocationSize = Size ? Size : 0x400 * PAGE_SIZE; 72 SIZE_T CommitSize = sizeof(*Buf); 73 74 /* Reserve the memory */ 75 Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &AllocationSize, MEM_RESERVE, PAGE_READWRITE); 76 if (!NT_SUCCESS(Status)) 77 return NULL; 78 79 /* Commit the first data, CommitSize is updated with the actual committed data */ 80 Status = NtAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Buf, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE); 81 if (!NT_SUCCESS(Status)) 82 { 83 RtlDestroyQueryDebugBuffer(Buf); 84 return NULL; 85 } 86 87 /* Fill out the minimum data required */ 88 Buf->ViewBaseClient = Buf; 89 Buf->ViewSize = (ULONG)AllocationSize; 90 Buf->CommitSize = CommitSize; 91 Buf->OffsetFree = sizeof(*Buf); 92 93 return Buf; 94 } 95 96 /* 97 * @unimplemented 98 */ 99 NTSTATUS 100 NTAPI 101 RtlDestroyQueryDebugBuffer(_In_ PRTL_DEBUG_INFORMATION Buf) 102 { 103 NTSTATUS Status = STATUS_SUCCESS; 104 SIZE_T ViewSize = 0; 105 106 if (NULL != Buf) 107 { 108 Status = NtFreeVirtualMemory(NtCurrentProcess(), 109 (PVOID*)&Buf, 110 &ViewSize, 111 MEM_RELEASE); 112 } 113 if (!NT_SUCCESS(Status)) 114 { 115 DPRINT1("RtlDQDB: Failed to free VM!\n"); 116 } 117 return Status; 118 } 119 120 /* 121 * Based on lib/epsapi/enum/modules.c by KJK::Hyperion. 122 */ 123 NTSTATUS 124 NTAPI 125 RtlpQueryRemoteProcessModules(HANDLE ProcessHandle, 126 IN PRTL_PROCESS_MODULES Modules OPTIONAL, 127 IN ULONG Size OPTIONAL, 128 OUT PULONG ReturnedSize) 129 { 130 PROCESS_BASIC_INFORMATION pbiInfo; 131 PPEB_LDR_DATA ppldLdrData; 132 LDR_DATA_TABLE_ENTRY lmModule; 133 PLIST_ENTRY pleListHead; 134 PLIST_ENTRY pleCurEntry; 135 136 PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL; 137 NTSTATUS Status = STATUS_SUCCESS; 138 ULONG UsedSize = sizeof(ULONG); 139 ANSI_STRING AnsiString; 140 PCHAR p; 141 142 DPRINT("RtlpQueryRemoteProcessModules Start\n"); 143 144 /* query the process basic information (includes the PEB address) */ 145 Status = NtQueryInformationProcess(ProcessHandle, 146 ProcessBasicInformation, 147 &pbiInfo, 148 sizeof(PROCESS_BASIC_INFORMATION), 149 NULL); 150 151 if (!NT_SUCCESS(Status)) 152 { 153 /* failure */ 154 DPRINT("NtQueryInformationProcess 1 0x%lx\n", Status); 155 return Status; 156 } 157 158 if (Modules == NULL || Size == 0) 159 { 160 Status = STATUS_INFO_LENGTH_MISMATCH; 161 } 162 else 163 { 164 Modules->NumberOfModules = 0; 165 ModulePtr = &Modules->Modules[0]; 166 Status = STATUS_SUCCESS; 167 } 168 169 /* get the address of the PE Loader data */ 170 Status = NtReadVirtualMemory(ProcessHandle, 171 &(pbiInfo.PebBaseAddress->Ldr), 172 &ppldLdrData, 173 sizeof(ppldLdrData), 174 NULL); 175 176 if (!NT_SUCCESS(Status)) 177 { 178 /* failure */ 179 DPRINT("NtReadVirtualMemory 1 0x%lx\n", Status); 180 return Status; 181 } 182 183 184 /* head of the module list: the last element in the list will point to this */ 185 pleListHead = &ppldLdrData->InLoadOrderModuleList; 186 187 /* get the address of the first element in the list */ 188 Status = NtReadVirtualMemory(ProcessHandle, 189 &(ppldLdrData->InLoadOrderModuleList.Flink), 190 &pleCurEntry, 191 sizeof(pleCurEntry), 192 NULL); 193 194 if (!NT_SUCCESS(Status)) 195 { 196 /* failure */ 197 DPRINT("NtReadVirtualMemory 2 0x%lx\n", Status); 198 return Status; 199 } 200 201 while(pleCurEntry != pleListHead) 202 { 203 UNICODE_STRING Unicode; 204 WCHAR Buffer[256 * sizeof(WCHAR)]; 205 206 /* read the current module */ 207 Status = NtReadVirtualMemory(ProcessHandle, 208 CONTAINING_RECORD(pleCurEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks), 209 &lmModule, 210 sizeof(LDR_DATA_TABLE_ENTRY), 211 NULL); 212 213 if (!NT_SUCCESS(Status)) 214 { 215 /* failure */ 216 DPRINT( "NtReadVirtualMemory 3 0x%lx\n", Status); 217 return Status; 218 } 219 220 /* Import module name from remote Process user space. */ 221 Unicode.Length = lmModule.FullDllName.Length; 222 Unicode.MaximumLength = lmModule.FullDllName.MaximumLength; 223 Unicode.Buffer = Buffer; 224 225 Status = NtReadVirtualMemory(ProcessHandle, 226 lmModule.FullDllName.Buffer, 227 Unicode.Buffer, 228 Unicode.Length, 229 NULL); 230 231 if (!NT_SUCCESS(Status)) 232 { 233 /* failure */ 234 DPRINT( "NtReadVirtualMemory 3 0x%lx\n", Status); 235 return Status; 236 } 237 238 DPRINT(" Module %wZ\n", &Unicode); 239 240 if (UsedSize > Size) 241 { 242 Status = STATUS_INFO_LENGTH_MISMATCH; 243 } 244 else if (Modules != NULL) 245 { 246 ModulePtr->Section = 0; 247 ModulePtr->MappedBase = NULL; // FIXME: ?? 248 ModulePtr->ImageBase = lmModule.DllBase; 249 ModulePtr->ImageSize = lmModule.SizeOfImage; 250 ModulePtr->Flags = lmModule.Flags; 251 ModulePtr->LoadOrderIndex = 0; // FIXME: ?? 252 ModulePtr->InitOrderIndex = 0; // FIXME: ?? 253 ModulePtr->LoadCount = lmModule.LoadCount; 254 255 AnsiString.Length = 0; 256 AnsiString.MaximumLength = 256; 257 AnsiString.Buffer = ModulePtr->FullPathName; 258 RtlUnicodeStringToAnsiString(&AnsiString, 259 &Unicode, 260 FALSE); 261 262 p = strrchr(ModulePtr->FullPathName, '\\'); 263 if (p != NULL) 264 ModulePtr->OffsetToFileName = (USHORT)(p - ModulePtr->FullPathName + 1); 265 else 266 ModulePtr->OffsetToFileName = 0; 267 268 ModulePtr++; 269 Modules->NumberOfModules++; 270 } 271 UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION); 272 273 /* address of the next module in the list */ 274 pleCurEntry = lmModule.InLoadOrderLinks.Flink; 275 } 276 277 if (ReturnedSize != 0) 278 *ReturnedSize = UsedSize; 279 280 DPRINT("RtlpQueryRemoteProcessModules End\n"); 281 282 /* success */ 283 return (STATUS_SUCCESS); 284 } 285 286 /* 287 * @unimplemented 288 */ 289 NTSTATUS 290 NTAPI 291 RtlQueryProcessDebugInformation(IN ULONG ProcessId, 292 IN ULONG DebugInfoMask, 293 IN OUT PRTL_DEBUG_INFORMATION Buf) 294 { 295 NTSTATUS Status = STATUS_SUCCESS; 296 ULONG Pid = (ULONG)(ULONG_PTR) NtCurrentTeb()->ClientId.UniqueProcess; 297 298 Buf->Flags = DebugInfoMask; 299 Buf->OffsetFree = sizeof(RTL_DEBUG_INFORMATION); 300 301 DPRINT("QueryProcessDebugInformation Start\n"); 302 303 /* 304 Currently ROS can not read-only from kenrel space, and doesn't 305 check for boundaries inside kernel space that are page protected 306 from every one but the kernel. aka page 0 - 2 307 */ 308 if (ProcessId <= 1) 309 { 310 Status = STATUS_ACCESS_VIOLATION; 311 } 312 else 313 if (Pid == ProcessId) 314 { 315 if (DebugInfoMask & RTL_DEBUG_QUERY_MODULES) 316 { 317 PRTL_PROCESS_MODULES Mp; 318 ULONG ReturnSize = 0; 319 320 /* I like this better than the do & while loop. */ 321 Status = LdrQueryProcessModuleInformation(NULL, 322 0, 323 &ReturnSize); 324 325 Mp = RtlpDebugBufferCommit(Buf, ReturnSize); 326 if (!Mp) 327 { 328 DPRINT1("RtlQueryProcessDebugInformation: Unable to commit %u\n", ReturnSize); 329 } 330 331 Status = LdrQueryProcessModuleInformation(Mp, 332 ReturnSize, 333 &ReturnSize); 334 if (!NT_SUCCESS(Status)) 335 { 336 return Status; 337 } 338 339 Buf->Modules = Mp; 340 } 341 342 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAPS) 343 { 344 PRTL_PROCESS_HEAPS Hp; 345 ULONG HSize; 346 347 Hp = (PRTL_PROCESS_HEAPS)((PUCHAR)Buf + Buf->OffsetFree); 348 HSize = sizeof(RTL_PROCESS_HEAPS); 349 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_TAGS) 350 { 351 // TODO 352 } 353 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_BLOCKS) 354 { 355 // TODO 356 } 357 Buf->Heaps = Hp; 358 Buf->OffsetFree = Buf->OffsetFree + HSize; 359 360 } 361 362 if (DebugInfoMask & RTL_DEBUG_QUERY_LOCKS) 363 { 364 PRTL_PROCESS_LOCKS Lp; 365 ULONG LSize; 366 367 Lp = (PRTL_PROCESS_LOCKS)((PUCHAR)Buf + Buf->OffsetFree); 368 LSize = sizeof(RTL_PROCESS_LOCKS); 369 Buf->Locks = Lp; 370 Buf->OffsetFree = Buf->OffsetFree + LSize; 371 } 372 373 DPRINT("QueryProcessDebugInformation end\n"); 374 DPRINT("QueryDebugInfo : 0x%lx\n", Buf->OffsetFree); 375 } 376 else 377 { 378 HANDLE hProcess; 379 CLIENT_ID ClientId; 380 OBJECT_ATTRIBUTES ObjectAttributes; 381 382 Buf->TargetProcessHandle = NtCurrentProcess(); 383 384 ClientId.UniqueThread = 0; 385 ClientId.UniqueProcess = (HANDLE)(ULONG_PTR)ProcessId; 386 InitializeObjectAttributes(&ObjectAttributes, 387 NULL, 388 0, 389 NULL, 390 NULL); 391 392 Status = NtOpenProcess(&hProcess, 393 (PROCESS_ALL_ACCESS), 394 &ObjectAttributes, 395 &ClientId ); 396 if (!NT_SUCCESS(Status)) 397 { 398 return Status; 399 } 400 401 if (DebugInfoMask & RTL_DEBUG_QUERY_MODULES) 402 { 403 PRTL_PROCESS_MODULES Mp; 404 ULONG ReturnSize = 0; 405 406 Status = RtlpQueryRemoteProcessModules(hProcess, 407 NULL, 408 0, 409 &ReturnSize); 410 411 Mp = RtlpDebugBufferCommit(Buf, ReturnSize); 412 if (!Mp) 413 { 414 DPRINT1("RtlQueryProcessDebugInformation: Unable to commit %u\n", ReturnSize); 415 } 416 417 Status = RtlpQueryRemoteProcessModules(hProcess, 418 Mp, 419 ReturnSize , 420 &ReturnSize); 421 if (!NT_SUCCESS(Status)) 422 { 423 return Status; 424 } 425 426 Buf->Modules = Mp; 427 } 428 429 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAPS) 430 { 431 PRTL_PROCESS_HEAPS Hp; 432 ULONG HSize; 433 434 Hp = (PRTL_PROCESS_HEAPS)((PUCHAR)Buf + Buf->OffsetFree); 435 HSize = sizeof(RTL_PROCESS_HEAPS); 436 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_TAGS) 437 { 438 // TODO 439 } 440 if (DebugInfoMask & RTL_DEBUG_QUERY_HEAP_BLOCKS) 441 { 442 // TODO 443 } 444 Buf->Heaps = Hp; 445 Buf->OffsetFree = Buf->OffsetFree + HSize; 446 447 } 448 449 if (DebugInfoMask & RTL_DEBUG_QUERY_LOCKS) 450 { 451 PRTL_PROCESS_LOCKS Lp; 452 ULONG LSize; 453 454 Lp = (PRTL_PROCESS_LOCKS)((PUCHAR)Buf + Buf->OffsetFree); 455 LSize = sizeof(RTL_PROCESS_LOCKS); 456 Buf->Locks = Lp; 457 Buf->OffsetFree = Buf->OffsetFree + LSize; 458 } 459 460 DPRINT("QueryProcessDebugInformation end\n"); 461 DPRINT("QueryDebugInfo : 0x%lx\n", Buf->OffsetFree); 462 } 463 464 return Status; 465 } 466 467 /* EOL */ 468