- Implement VirtualMemoryInformationClass of MemorySectionName for NtQueryVirtualMemory.
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/virtual.c
5 * PURPOSE: Implementing operations on virtual memory.
6 *
7 * PROGRAMMERS: David Welch
8 */
9
10 /* INCLUDE ********************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MI_MAPPED_COPY_PAGES 16
17 #define MI_POOL_COPY_BYTES 512
18 #define MI_MAX_TRANSFER_SIZE 64 * 1024
19 #define TAG_VM TAG('V', 'm', 'R', 'w')
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 _SEH_DEFINE_LOCALS(MiGetExceptionInfo)
24 {
25 volatile BOOLEAN HaveBadAddress;
26 volatile ULONG_PTR BadAddress;
27 };
28
29 _SEH_FILTER(MiGetExceptionInfo)
30 {
31 _SEH_ACCESS_LOCALS(MiGetExceptionInfo);
32 EXCEPTION_POINTERS *ExceptionInfo = _SEH_GetExceptionPointers();
33 PEXCEPTION_RECORD ExceptionRecord;
34 PAGED_CODE();
35
36 /* Assume default */
37 _SEH_VAR(HaveBadAddress) = FALSE;
38
39 /* Get the exception record */
40 ExceptionRecord = ExceptionInfo->ExceptionRecord;
41
42 /* Look at the exception code */
43 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
44 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
45 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
46 {
47 /* We can tell the address if we have more than one parameter */
48 if (ExceptionRecord->NumberParameters > 1)
49 {
50 /* Return the address */
51 _SEH_VAR(HaveBadAddress) = TRUE;
52 _SEH_VAR(BadAddress) = ExceptionRecord->ExceptionInformation[1];
53 }
54 }
55
56 /* Continue executing the next handler */
57 return EXCEPTION_EXECUTE_HANDLER;
58 }
59
60 NTSTATUS
61 NTAPI
62 MiDoMappedCopy(IN PEPROCESS SourceProcess,
63 IN PVOID SourceAddress,
64 IN PEPROCESS TargetProcess,
65 OUT PVOID TargetAddress,
66 IN SIZE_T BufferSize,
67 IN KPROCESSOR_MODE PreviousMode,
68 OUT PSIZE_T ReturnSize)
69 {
70 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
71 PMDL Mdl = (PMDL)MdlBuffer;
72 SIZE_T TotalSize, CurrentSize, RemainingSize;
73 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
74 BOOLEAN PagesLocked;
75 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
76 PVOID MdlAddress;
77 KAPC_STATE ApcState;
78 _SEH_DECLARE_LOCALS(MiGetExceptionInfo);
79 NTSTATUS Status = STATUS_SUCCESS;
80 PAGED_CODE();
81
82 /* Calculate the maximum amount of data to move */
83 TotalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
84 if (BufferSize <= TotalSize) TotalSize = BufferSize;
85 CurrentSize = TotalSize;
86 RemainingSize = BufferSize;
87
88 /* Loop as long as there is still data */
89 while (RemainingSize > 0)
90 {
91 /* Check if this transfer will finish everything off */
92 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
93
94 /* Attach to the source address space */
95 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
96
97 /* Reset state for this pass */
98 MdlAddress = NULL;
99 PagesLocked = FALSE;
100 FailedInMoving = FALSE;
101 ASSERT(FailedInProbe == FALSE);
102
103 /* Protect user-mode copy */
104 _SEH_TRY
105 {
106 /* If this is our first time, probe the buffer */
107 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
108 {
109 /* Catch a failure here */
110 FailedInProbe = TRUE;
111
112 /* Do the probe */
113 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
114
115 /* Passed */
116 FailedInProbe = FALSE;
117 }
118
119 /* Initialize and probe and lock the MDL */
120 MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
121 MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
122 PagesLocked = TRUE;
123
124 /* Now map the pages */
125 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
126 KernelMode,
127 MmCached,
128 NULL,
129 FALSE,
130 HighPagePriority);
131 if (!MdlAddress)
132 {
133 /* Use our SEH handler to pick this up */
134 FailedInMapping = TRUE;
135 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
136 }
137
138 /* Now let go of the source and grab to the target process */
139 KeUnstackDetachProcess(&ApcState);
140 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
141
142 /* Check if this is our first time through */
143 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
144 {
145 /* Catch a failure here */
146 FailedInProbe = TRUE;
147
148 /* Do the probe */
149 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
150
151 /* Passed */
152 FailedInProbe = FALSE;
153 }
154
155 /* Now do the actual move */
156 FailedInMoving = TRUE;
157 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
158 }
159 _SEH_EXCEPT(MiGetExceptionInfo)
160 {
161 /* Detach from whoever we may be attached to */
162 KeUnstackDetachProcess(&ApcState);
163
164 /* Check if we had mapped the pages */
165 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
166
167 /* Check if we had locked the pages */
168 if (PagesLocked) MmUnlockPages(Mdl);
169
170 /* Check if we failed during the probe or mapping */
171 if ((FailedInProbe) || (FailedInMapping))
172 {
173 /* Exit */
174 Status = _SEH_GetExceptionCode();
175 _SEH_YIELD(return Status);
176 }
177
178 /* Otherwise, we failed probably during the move */
179 *ReturnSize = BufferSize - RemainingSize;
180 if (FailedInMoving)
181 {
182 /* Check if we know exactly where we stopped copying */
183 if (_SEH_VAR(HaveBadAddress))
184 {
185 /* Return the exact number of bytes copied */
186 *ReturnSize = _SEH_VAR(BadAddress) - (ULONG_PTR)SourceAddress;
187 }
188 }
189
190 /* Return partial copy */
191 Status = STATUS_PARTIAL_COPY;
192 }
193 _SEH_END;
194
195 /* Check for SEH status */
196 if (Status != STATUS_SUCCESS) return Status;
197
198 /* Detach from target */
199 KeUnstackDetachProcess(&ApcState);
200
201 /* Unmap and unlock */
202 MmUnmapLockedPages(MdlAddress, Mdl);
203 MmUnlockPages(Mdl);
204
205 /* Update location and size */
206 RemainingSize -= CurrentSize;
207 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
208 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
209 }
210
211 /* All bytes read */
212 *ReturnSize = BufferSize;
213 return STATUS_SUCCESS;
214 }
215
216 NTSTATUS
217 NTAPI
218 MiDoPoolCopy(IN PEPROCESS SourceProcess,
219 IN PVOID SourceAddress,
220 IN PEPROCESS TargetProcess,
221 OUT PVOID TargetAddress,
222 IN SIZE_T BufferSize,
223 IN KPROCESSOR_MODE PreviousMode,
224 OUT PSIZE_T ReturnSize)
225 {
226 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
227 SIZE_T TotalSize, CurrentSize, RemainingSize;
228 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
229 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
230 PVOID PoolAddress;
231 KAPC_STATE ApcState;
232 _SEH_DECLARE_LOCALS(MiGetExceptionInfo);
233 NTSTATUS Status = STATUS_SUCCESS;
234 PAGED_CODE();
235
236 /* Calculate the maximum amount of data to move */
237 TotalSize = MI_MAX_TRANSFER_SIZE;
238 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
239 CurrentSize = TotalSize;
240 RemainingSize = BufferSize;
241
242 /* Check if we can use the stack */
243 if (BufferSize <= MI_POOL_COPY_BYTES)
244 {
245 /* Use it */
246 PoolAddress = (PVOID)StackBuffer;
247 }
248 else
249 {
250 /* Allocate pool */
251 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, TAG_VM);
252 if (!PoolAddress) ASSERT(FALSE);
253 HavePoolAddress = TRUE;
254 }
255
256 /* Loop as long as there is still data */
257 while (RemainingSize > 0)
258 {
259 /* Check if this transfer will finish everything off */
260 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
261
262 /* Attach to the source address space */
263 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
264
265 /* Reset state for this pass */
266 FailedInMoving = FALSE;
267 ASSERT(FailedInProbe == FALSE);
268
269 /* Protect user-mode copy */
270 _SEH_TRY
271 {
272 /* If this is our first time, probe the buffer */
273 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
274 {
275 /* Catch a failure here */
276 FailedInProbe = TRUE;
277
278 /* Do the probe */
279 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
280
281 /* Passed */
282 FailedInProbe = FALSE;
283 }
284
285 /* Do the copy */
286 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
287
288 /* Now let go of the source and grab to the target process */
289 KeUnstackDetachProcess(&ApcState);
290 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
291
292 /* Check if this is our first time through */
293 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
294 {
295 /* Catch a failure here */
296 FailedInProbe = TRUE;
297
298 /* Do the probe */
299 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
300
301 /* Passed */
302 FailedInProbe = FALSE;
303 }
304
305 /* Now do the actual move */
306 FailedInMoving = TRUE;
307 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
308 }
309 _SEH_EXCEPT(MiGetExceptionInfo)
310 {
311 /* Detach from whoever we may be attached to */
312 KeUnstackDetachProcess(&ApcState);
313
314 /* Check if we had allocated pool */
315 if (HavePoolAddress) ExFreePool(PoolAddress);
316
317 /* Check if we failed during the probe */
318 if (FailedInProbe)
319 {
320 /* Exit */
321 Status = _SEH_GetExceptionCode();
322 _SEH_YIELD(return Status);
323 }
324
325 /* Otherwise, we failed probably during the move */
326 *ReturnSize = BufferSize - RemainingSize;
327 if (FailedInMoving)
328 {
329 /* Check if we know exactly where we stopped copying */
330 if (_SEH_VAR(HaveBadAddress))
331 {
332 /* Return the exact number of bytes copied */
333 *ReturnSize = _SEH_VAR(BadAddress) - (ULONG_PTR)SourceAddress;
334 }
335 }
336
337 /* Return partial copy */
338 Status = STATUS_PARTIAL_COPY;
339 }
340 _SEH_END;
341
342 /* Check for SEH status */
343 if (Status != STATUS_SUCCESS) return Status;
344
345 /* Detach from target */
346 KeUnstackDetachProcess(&ApcState);
347
348 /* Update location and size */
349 RemainingSize -= CurrentSize;
350 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
351 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
352 }
353
354 /* Check if we had allocated pool */
355 if (HavePoolAddress) ExFreePool(PoolAddress);
356
357 /* All bytes read */
358 *ReturnSize = BufferSize;
359 return STATUS_SUCCESS;
360 }
361
362 NTSTATUS
363 NTAPI
364 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
365 IN PVOID SourceAddress,
366 IN PEPROCESS TargetProcess,
367 OUT PVOID TargetAddress,
368 IN SIZE_T BufferSize,
369 IN KPROCESSOR_MODE PreviousMode,
370 OUT PSIZE_T ReturnSize)
371 {
372 NTSTATUS Status;
373 PEPROCESS Process = SourceProcess;
374
375 /* Don't accept zero-sized buffers */
376 if (!BufferSize) return STATUS_SUCCESS;
377
378 /* If we are copying from ourselves, lock the target instead */
379 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
380
381 /* Acquire rundown protection */
382 if (!ExAcquireRundownProtection(&Process->RundownProtect))
383 {
384 /* Fail */
385 return STATUS_PROCESS_IS_TERMINATING;
386 }
387
388 /* See if we should use the pool copy */
389 if (BufferSize > MI_POOL_COPY_BYTES)
390 {
391 /* Use MDL-copy */
392 Status = MiDoMappedCopy(SourceProcess,
393 SourceAddress,
394 TargetProcess,
395 TargetAddress,
396 BufferSize,
397 PreviousMode,
398 ReturnSize);
399 }
400 else
401 {
402 /* Do pool copy */
403 Status = MiDoPoolCopy(SourceProcess,
404 SourceAddress,
405 TargetProcess,
406 TargetAddress,
407 BufferSize,
408 PreviousMode,
409 ReturnSize);
410 }
411
412 /* Release the lock */
413 ExReleaseRundownProtection(&Process->RundownProtect);
414 return Status;
415 }
416
417 NTSTATUS FASTCALL
418 MiQueryVirtualMemory(IN HANDLE ProcessHandle,
419 IN PVOID Address,
420 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
421 OUT PVOID VirtualMemoryInformation,
422 IN SIZE_T Length,
423 OUT PSIZE_T ResultLength)
424 {
425 NTSTATUS Status;
426 PEPROCESS Process;
427 MEMORY_AREA* MemoryArea;
428 PMM_AVL_TABLE AddressSpace;
429
430 Status = ObReferenceObjectByHandle(ProcessHandle,
431 PROCESS_QUERY_INFORMATION,
432 NULL,
433 UserMode,
434 (PVOID*)(&Process),
435 NULL);
436
437 if (!NT_SUCCESS(Status))
438 {
439 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
440 return(Status);
441 }
442
443 AddressSpace = &Process->VadRoot;
444
445 MmLockAddressSpace(AddressSpace);
446 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
447 switch(VirtualMemoryInformationClass)
448 {
449 case MemoryBasicInformation:
450 {
451 PMEMORY_BASIC_INFORMATION Info =
452 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
453 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
454 {
455 MmUnlockAddressSpace(AddressSpace);
456 ObDereferenceObject(Process);
457 return(STATUS_INFO_LENGTH_MISMATCH);
458 }
459
460 if (MemoryArea == NULL)
461 {
462 Info->Type = 0;
463 Info->State = MEM_FREE;
464 Info->Protect = PAGE_NOACCESS;
465 Info->AllocationProtect = 0;
466 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
467 Info->AllocationBase = NULL;
468 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
469 Status = STATUS_SUCCESS;
470 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
471 }
472 else
473 {
474 switch(MemoryArea->Type)
475 {
476 case MEMORY_AREA_VIRTUAL_MEMORY:
477 case MEMORY_AREA_PEB_OR_TEB:
478 Status = MmQueryAnonMem(MemoryArea, Address, Info,
479 ResultLength);
480 break;
481
482 case MEMORY_AREA_SECTION_VIEW:
483 Status = MmQuerySectionView(MemoryArea, Address, Info,
484 ResultLength);
485 break;
486
487 case MEMORY_AREA_NO_ACCESS:
488 Info->Type = MEM_PRIVATE;
489 Info->State = MEM_RESERVE;
490 Info->Protect = MemoryArea->Protect;
491 Info->AllocationProtect = MemoryArea->Protect;
492 Info->BaseAddress = MemoryArea->StartingAddress;
493 Info->AllocationBase = MemoryArea->StartingAddress;
494 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
495 (ULONG_PTR)MemoryArea->StartingAddress;
496 Status = STATUS_SUCCESS;
497 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
498 break;
499
500 case MEMORY_AREA_SHARED_DATA:
501 Info->Type = MEM_PRIVATE;
502 Info->State = MEM_COMMIT;
503 Info->Protect = MemoryArea->Protect;
504 Info->AllocationProtect = MemoryArea->Protect;
505 Info->BaseAddress = MemoryArea->StartingAddress;
506 Info->AllocationBase = MemoryArea->StartingAddress;
507 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
508 (ULONG_PTR)MemoryArea->StartingAddress;
509 Status = STATUS_SUCCESS;
510 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
511 break;
512
513 case MEMORY_AREA_SYSTEM:
514 Info->Type = 0;
515 Info->State = MEM_COMMIT;
516 Info->Protect = MemoryArea->Protect;
517 Info->AllocationProtect = MemoryArea->Protect;
518 Info->BaseAddress = MemoryArea->StartingAddress;
519 Info->AllocationBase = MemoryArea->StartingAddress;
520 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
521 (ULONG_PTR)MemoryArea->StartingAddress;
522 Status = STATUS_SUCCESS;
523 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
524 break;
525
526 case MEMORY_AREA_KERNEL_STACK:
527 Info->Type = 0;
528 Info->State = MEM_COMMIT;
529 Info->Protect = MemoryArea->Protect;
530 Info->AllocationProtect = MemoryArea->Protect;
531 Info->BaseAddress = MemoryArea->StartingAddress;
532 Info->AllocationBase = MemoryArea->StartingAddress;
533 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
534 (ULONG_PTR)MemoryArea->StartingAddress;
535 Status = STATUS_SUCCESS;
536 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
537 break;
538
539 case MEMORY_AREA_PAGED_POOL:
540 Info->Type = 0;
541 Info->State = MEM_COMMIT;
542 Info->Protect = MemoryArea->Protect;
543 Info->AllocationProtect = MemoryArea->Protect;
544 Info->BaseAddress = MemoryArea->StartingAddress;
545 Info->AllocationBase = MemoryArea->StartingAddress;
546 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
547 (ULONG_PTR)MemoryArea->StartingAddress;
548 Status = STATUS_SUCCESS;
549 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
550 break;
551
552 default:
553 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
554 Status = STATUS_UNSUCCESSFUL;
555 *ResultLength = 0;
556 }
557 }
558 break;
559 }
560
561 default:
562 {
563 Status = STATUS_INVALID_INFO_CLASS;
564 *ResultLength = 0;
565 break;
566 }
567 }
568
569 MmUnlockAddressSpace(AddressSpace);
570 ObDereferenceObject(Process);
571
572 return Status;
573 }
574
575 NTSTATUS STDCALL
576 MiProtectVirtualMemory(IN PEPROCESS Process,
577 IN OUT PVOID *BaseAddress,
578 IN OUT PSIZE_T NumberOfBytesToProtect,
579 IN ULONG NewAccessProtection,
580 OUT PULONG OldAccessProtection OPTIONAL)
581 {
582 PMEMORY_AREA MemoryArea;
583 PMM_AVL_TABLE AddressSpace;
584 ULONG OldAccessProtection_;
585 NTSTATUS Status;
586
587 *NumberOfBytesToProtect =
588 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
589 PAGE_ROUND_DOWN(*BaseAddress);
590 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
591
592 AddressSpace = &Process->VadRoot;
593
594 MmLockAddressSpace(AddressSpace);
595 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
596 if (MemoryArea == NULL)
597 {
598 MmUnlockAddressSpace(AddressSpace);
599 return STATUS_UNSUCCESSFUL;
600 }
601
602 if (OldAccessProtection == NULL)
603 OldAccessProtection = &OldAccessProtection_;
604
605 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
606 {
607 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
608 *NumberOfBytesToProtect, NewAccessProtection,
609 OldAccessProtection);
610 }
611 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
612 {
613 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
614 *NumberOfBytesToProtect,
615 NewAccessProtection,
616 OldAccessProtection);
617 }
618 else
619 {
620 /* FIXME: Should we return failure or success in this case? */
621 Status = STATUS_CONFLICTING_ADDRESSES;
622 }
623
624 MmUnlockAddressSpace(AddressSpace);
625
626 return Status;
627 }
628
629 /* PUBLIC FUNCTIONS ***********************************************************/
630
631 /*
632 * @unimplemented
633 */
634 PVOID
635 NTAPI
636 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
637 {
638 UNIMPLEMENTED;
639 return 0;
640 }
641
642 /*
643 * @unimplemented
644 */
645 PVOID
646 NTAPI
647 MmSecureVirtualMemory(IN PVOID Address,
648 IN SIZE_T Length,
649 IN ULONG Mode)
650 {
651 UNIMPLEMENTED;
652 return NULL;
653 }
654
655 /*
656 * @unimplemented
657 */
658 VOID
659 NTAPI
660 MmUnsecureVirtualMemory(IN PVOID SecureMem)
661 {
662 UNIMPLEMENTED;
663 }
664
665 /* SYSTEM CALLS ***************************************************************/
666
667 NTSTATUS
668 NTAPI
669 NtReadVirtualMemory(IN HANDLE ProcessHandle,
670 IN PVOID BaseAddress,
671 OUT PVOID Buffer,
672 IN SIZE_T NumberOfBytesToRead,
673 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
674 {
675 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
676 PEPROCESS Process;
677 NTSTATUS Status = STATUS_SUCCESS;
678 SIZE_T BytesRead = 0;
679 PAGED_CODE();
680
681 /* Check if we came from user mode */
682 if (PreviousMode != KernelMode)
683 {
684 /* Validate the read addresses */
685 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
686 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
687 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
688 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
689 {
690 /* Don't allow to write into kernel space */
691 return STATUS_ACCESS_VIOLATION;
692 }
693
694 /* Enter SEH for probe */
695 _SEH_TRY
696 {
697 /* Probe the output value */
698 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
699 }
700 _SEH_HANDLE
701 {
702 /* Get exception code */
703 Status = _SEH_GetExceptionCode();
704 }
705 _SEH_END;
706
707 /* Return if we failed */
708 if (!NT_SUCCESS(Status)) return Status;
709 }
710
711 /* Reference the process */
712 Status = ObReferenceObjectByHandle(ProcessHandle,
713 PROCESS_VM_READ,
714 PsProcessType,
715 PreviousMode,
716 (PVOID*)(&Process),
717 NULL);
718 if (NT_SUCCESS(Status))
719 {
720 /* Do the copy */
721 Status = MmCopyVirtualMemory(Process,
722 BaseAddress,
723 PsGetCurrentProcess(),
724 Buffer,
725 NumberOfBytesToRead,
726 PreviousMode,
727 &BytesRead);
728
729 /* Dereference the process */
730 ObDereferenceObject(Process);
731 }
732
733 /* Check if the caller sent this parameter */
734 if (NumberOfBytesRead)
735 {
736 /* Enter SEH to guard write */
737 _SEH_TRY
738 {
739 /* Return the number of bytes read */
740 *NumberOfBytesRead = BytesRead;
741 }
742 _SEH_HANDLE
743 {
744 /* Handle exception */
745 Status = _SEH_GetExceptionCode();
746 }
747 _SEH_END;
748 }
749
750 /* Return status */
751 return Status;
752 }
753
754 NTSTATUS
755 NTAPI
756 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
757 IN PVOID BaseAddress,
758 IN PVOID Buffer,
759 IN SIZE_T NumberOfBytesToWrite,
760 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
761 {
762 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
763 PEPROCESS Process;
764 NTSTATUS Status = STATUS_SUCCESS;
765 ULONG BytesWritten = 0;
766 PAGED_CODE();
767
768 /* Check if we came from user mode */
769 if (PreviousMode != KernelMode)
770 {
771 /* Validate the read addresses */
772 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
773 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
774 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
775 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
776 {
777 /* Don't allow to write into kernel space */
778 return STATUS_ACCESS_VIOLATION;
779 }
780
781 /* Enter SEH for probe */
782 _SEH_TRY
783 {
784 /* Probe the output value */
785 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
786 }
787 _SEH_HANDLE
788 {
789 /* Get exception code */
790 Status = _SEH_GetExceptionCode();
791 }
792 _SEH_END;
793
794 /* Return if we failed */
795 if (!NT_SUCCESS(Status)) return Status;
796 }
797
798 /* Reference the process */
799 Status = ObReferenceObjectByHandle(ProcessHandle,
800 PROCESS_VM_WRITE,
801 PsProcessType,
802 PreviousMode,
803 (PVOID*)&Process,
804 NULL);
805 if (NT_SUCCESS(Status))
806 {
807 /* Do the copy */
808 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
809 Buffer,
810 Process,
811 BaseAddress,
812 NumberOfBytesToWrite,
813 PreviousMode,
814 &BytesWritten);
815
816 /* Dereference the process */
817 ObDereferenceObject(Process);
818 }
819
820 /* Check if the caller sent this parameter */
821 if (NumberOfBytesWritten)
822 {
823 /* Enter SEH to guard write */
824 _SEH_TRY
825 {
826 /* Return the number of bytes read */
827 *NumberOfBytesWritten = BytesWritten;
828 }
829 _SEH_HANDLE
830 {
831 /* Handle exception */
832 Status = _SEH_GetExceptionCode();
833 }
834 _SEH_END;
835 }
836
837 /* Return status */
838 return Status;
839 }
840
841 NTSTATUS
842 NTAPI
843 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
844 IN OUT PVOID *UnsafeBaseAddress,
845 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
846 IN ULONG NewAccessProtection,
847 OUT PULONG UnsafeOldAccessProtection)
848 {
849 PEPROCESS Process;
850 ULONG OldAccessProtection;
851 PVOID BaseAddress = NULL;
852 SIZE_T NumberOfBytesToProtect = 0;
853 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
854 NTSTATUS Status = STATUS_SUCCESS;
855
856 /* Check if we came from user mode */
857 if (PreviousMode != KernelMode)
858 {
859 /* Enter SEH for probing */
860 _SEH_TRY
861 {
862 /* Validate all outputs */
863 ProbeForWritePointer(UnsafeBaseAddress);
864 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
865 ProbeForWriteUlong(UnsafeOldAccessProtection);
866
867 /* Capture them */
868 BaseAddress = *UnsafeBaseAddress;
869 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
870 }
871 _SEH_HANDLE
872 {
873 /* Get exception code */
874 Status = _SEH_GetExceptionCode();
875 }
876 _SEH_END;
877
878 /* Return on exception */
879 if (!NT_SUCCESS(Status)) return Status;
880 }
881 else
882 {
883 /* Capture directly */
884 BaseAddress = *UnsafeBaseAddress;
885 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
886 }
887
888 /* Catch illegal base address */
889 if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
890
891 /* Catch illegal region size */
892 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
893 {
894 /* Fail */
895 return STATUS_INVALID_PARAMETER_3;
896 }
897
898 /* 0 is also illegal */
899 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
900
901 /* Get a reference to the process */
902 Status = ObReferenceObjectByHandle(ProcessHandle,
903 PROCESS_VM_OPERATION,
904 PsProcessType,
905 PreviousMode,
906 (PVOID*)(&Process),
907 NULL);
908 if (!NT_SUCCESS(Status)) return Status;
909
910 /* Do the actual work */
911 Status = MiProtectVirtualMemory(Process,
912 &BaseAddress,
913 &NumberOfBytesToProtect,
914 NewAccessProtection,
915 &OldAccessProtection);
916
917 /* Release reference */
918 ObDereferenceObject(Process);
919
920 /* Enter SEH to return data */
921 _SEH_TRY
922 {
923 /* Return data to user */
924 *UnsafeOldAccessProtection = OldAccessProtection;
925 *UnsafeBaseAddress = BaseAddress;
926 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
927 }
928 _SEH_HANDLE
929 {
930 /* Catch exception */
931 Status = _SEH_GetExceptionCode();
932 }
933 _SEH_END;
934
935 /* Return status */
936 return Status;
937 }
938
939 NTSTATUS STDCALL
940 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
941 IN PVOID Address,
942 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
943 OUT PVOID VirtualMemoryInformation,
944 IN SIZE_T Length,
945 OUT PSIZE_T UnsafeResultLength)
946 {
947 NTSTATUS Status = STATUS_SUCCESS;
948 SIZE_T ResultLength = 0;
949 KPROCESSOR_MODE PreviousMode;
950 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
951 UNICODE_STRING ModuleFileName;
952 PMEMORY_SECTION_NAME SectionName = NULL;
953 union
954 {
955 MEMORY_BASIC_INFORMATION BasicInfo;
956 }
957 VirtualMemoryInfo;
958
959 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
960 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
961 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
962 VirtualMemoryInformationClass,VirtualMemoryInformation,
963 Length,ResultLength);
964
965 PreviousMode = ExGetPreviousMode();
966
967 if (PreviousMode != KernelMode && UnsafeResultLength != NULL)
968 {
969 _SEH_TRY
970 {
971 ProbeForWriteSize_t(UnsafeResultLength);
972 }
973 _SEH_HANDLE
974 {
975 Status = _SEH_GetExceptionCode();
976 }
977 _SEH_END;
978
979 if (!NT_SUCCESS(Status))
980 {
981 return Status;
982 }
983 }
984
985 if (Address >= MmSystemRangeStart)
986 {
987 DPRINT1("Invalid parameter\n");
988 return STATUS_INVALID_PARAMETER;
989 }
990
991 /* FIXME: Move this inside MiQueryVirtualMemory */
992 if (VirtualMemoryInformationClass == MemorySectionName)
993 {
994 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
995 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
996
997 if (NT_SUCCESS(Status))
998 {
999 SectionName = VirtualMemoryInformation;
1000 if (PreviousMode != KernelMode)
1001 {
1002 _SEH_TRY
1003 {
1004 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1005 SectionName->SectionFileName.MaximumLength = Length;
1006 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1007
1008 if (UnsafeResultLength != NULL)
1009 {
1010 *UnsafeResultLength = ModuleFileName.Length;
1011 }
1012 }
1013 _SEH_HANDLE
1014 {
1015 Status = _SEH_GetExceptionCode();
1016 }
1017 _SEH_END;
1018 }
1019 else
1020 {
1021 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1022 SectionName->SectionFileName.MaximumLength = Length;
1023 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1024
1025 if (UnsafeResultLength != NULL)
1026 {
1027 *UnsafeResultLength = ModuleFileName.Length;
1028 }
1029 }
1030 }
1031 return Status;
1032 }
1033 else
1034 {
1035 Status = MiQueryVirtualMemory(ProcessHandle,
1036 Address,
1037 VirtualMemoryInformationClass,
1038 &VirtualMemoryInfo,
1039 Length,
1040 &ResultLength);
1041 }
1042
1043 if (NT_SUCCESS(Status))
1044 {
1045 if (PreviousMode != KernelMode)
1046 {
1047 _SEH_TRY
1048 {
1049 if (ResultLength > 0)
1050 {
1051 ProbeForWrite(VirtualMemoryInformation,
1052 ResultLength,
1053 1);
1054 RtlCopyMemory(VirtualMemoryInformation,
1055 &VirtualMemoryInfo,
1056 ResultLength);
1057 }
1058 if (UnsafeResultLength != NULL)
1059 {
1060 *UnsafeResultLength = ResultLength;
1061 }
1062 }
1063 _SEH_HANDLE
1064 {
1065 Status = _SEH_GetExceptionCode();
1066 }
1067 _SEH_END;
1068 }
1069 else
1070 {
1071 if (ResultLength > 0)
1072 {
1073 RtlCopyMemory(VirtualMemoryInformation,
1074 &VirtualMemoryInfo,
1075 ResultLength);
1076 }
1077
1078 if (UnsafeResultLength != NULL)
1079 {
1080 *UnsafeResultLength = ResultLength;
1081 }
1082 }
1083 }
1084
1085 return(Status);
1086 }
1087
1088 NTSTATUS
1089 NTAPI
1090 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1091 IN PVOID BaseAddress,
1092 IN SIZE_T NumberOfBytesToLock,
1093 OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
1094 {
1095 UNIMPLEMENTED;
1096 if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
1097 return STATUS_SUCCESS;
1098 }
1099
1100 NTSTATUS
1101 NTAPI
1102 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1103 IN PVOID BaseAddress,
1104 IN SIZE_T NumberOfBytesToUnlock,
1105 OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
1106 {
1107 UNIMPLEMENTED;
1108 if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
1109 return STATUS_SUCCESS;
1110 }
1111
1112 NTSTATUS
1113 NTAPI
1114 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1115 IN OUT PVOID *BaseAddress,
1116 IN OUT PSIZE_T NumberOfBytesToFlush,
1117 OUT PIO_STATUS_BLOCK IoStatusBlock)
1118 {
1119 UNIMPLEMENTED;
1120 return STATUS_SUCCESS;
1121 }
1122
1123 /* EOF */