xref: /reactos/sdk/lib/rtl/dbgbuffer.c (revision 9b1edcea)
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