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
RtlpDebugBufferCommit(_Inout_ PRTL_DEBUG_INFORMATION Buffer,_In_ SIZE_T Size)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
RtlCreateQueryDebugBuffer(_In_ ULONG Size,_In_ BOOLEAN EventPair)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
RtlDestroyQueryDebugBuffer(_In_ PRTL_DEBUG_INFORMATION Buf)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
RtlpQueryRemoteProcessModules(HANDLE ProcessHandle,IN PRTL_PROCESS_MODULES Modules OPTIONAL,IN ULONG Size OPTIONAL,OUT PULONG ReturnedSize)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
RtlQueryProcessDebugInformation(IN ULONG ProcessId,IN ULONG DebugInfoMask,IN OUT PRTL_DEBUG_INFORMATION Buf)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