- Implemented MEMORY_AREA_PEB_OR_TEB and MEMORY_AREA_PAGED_POOL for MiQueryVirtualMemory.
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/virtual.c
6 * PURPOSE: Implementing operations on virtual memory.
7 *
8 * PROGRAMMERS: David Welch
9 */
10
11 /* INCLUDE *****************************************************************/
12
13 #include <ntoskrnl.h>
14
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* FUNCTIONS *****************************************************************/
19
20 NTSTATUS STDCALL
21 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
22 IN PVOID BaseAddress,
23 IN ULONG NumberOfBytesToFlush,
24 OUT PULONG NumberOfBytesFlushed OPTIONAL)
25 /*
26 * FUNCTION: Flushes virtual memory to file
27 * ARGUMENTS:
28 * ProcessHandle = Points to the process that allocated the virtual
29 * memory
30 * BaseAddress = Points to the memory address
31 * NumberOfBytesToFlush = Limits the range to flush,
32 * NumberOfBytesFlushed = Actual number of bytes flushed
33 * RETURNS: Status
34 */
35 {
36 /* This should be implemented once we support network filesystems */
37 DPRINT("NtFlushVirtualMemory is UNIMPLEMENTED\n");
38 return(STATUS_SUCCESS);
39 }
40
41
42 NTSTATUS STDCALL
43 MiLockVirtualMemory(HANDLE ProcessHandle,
44 PVOID BaseAddress,
45 ULONG NumberOfBytesToLock,
46 PULONG NumberOfBytesLocked,
47 PObReferenceObjectByHandle pObReferenceObjectByHandle,
48 PMmCreateMdl pMmCreateMdl,
49 PObDereferenceObject pObDereferenceObject,
50 PMmProbeAndLockPages pMmProbeAndLockPages,
51 PExFreePool pExFreePool)
52 {
53 PEPROCESS Process;
54 NTSTATUS Status;
55 PMDL Mdl;
56
57 Status = pObReferenceObjectByHandle(ProcessHandle,
58 PROCESS_VM_WRITE,
59 NULL,
60 UserMode,
61 (PVOID*)(&Process),
62 NULL);
63 if (!NT_SUCCESS(Status))
64 return(Status);
65
66 Mdl = pMmCreateMdl(NULL,
67 BaseAddress,
68 NumberOfBytesToLock);
69 if (Mdl == NULL)
70 {
71 pObDereferenceObject(Process);
72 return(STATUS_NO_MEMORY);
73 }
74
75 pMmProbeAndLockPages(Mdl,
76 UserMode,
77 IoWriteAccess);
78
79 pExFreePool(Mdl);
80
81 pObDereferenceObject(Process);
82
83 *NumberOfBytesLocked = NumberOfBytesToLock;
84 return(STATUS_SUCCESS);
85 }
86
87
88 NTSTATUS STDCALL
89 NtLockVirtualMemory(HANDLE ProcessHandle,
90 PVOID BaseAddress,
91 ULONG NumberOfBytesToLock,
92 PULONG NumberOfBytesLocked)
93 {
94 DPRINT("NtLockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
95 "NumberOfBytesToLock %d, NumberOfBytesLocked %x)\n",
96 ProcessHandle,
97 BaseAddress,
98 NumberOfBytesToLock,
99 NumberOfBytesLocked);
100
101 return MiLockVirtualMemory(ProcessHandle,
102 BaseAddress,
103 NumberOfBytesToLock,
104 NumberOfBytesLocked,
105 ObReferenceObjectByHandle,
106 MmCreateMdl,
107 ObfDereferenceObject,
108 MmProbeAndLockPages,
109 ExFreePool);
110 }
111
112
113 NTSTATUS FASTCALL
114 MiQueryVirtualMemory (IN HANDLE ProcessHandle,
115 IN PVOID Address,
116 IN CINT VirtualMemoryInformationClass,
117 OUT PVOID VirtualMemoryInformation,
118 IN ULONG Length,
119 OUT PULONG ResultLength)
120 {
121 NTSTATUS Status;
122 PEPROCESS Process;
123 MEMORY_AREA* MemoryArea;
124 PMADDRESS_SPACE AddressSpace;
125
126 if (Address < MmSystemRangeStart)
127 {
128 Status = ObReferenceObjectByHandle(ProcessHandle,
129 PROCESS_QUERY_INFORMATION,
130 NULL,
131 UserMode,
132 (PVOID*)(&Process),
133 NULL);
134
135 if (!NT_SUCCESS(Status))
136 {
137 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
138 return(Status);
139 }
140 AddressSpace = &Process->AddressSpace;
141 }
142 else
143 {
144 AddressSpace = MmGetKernelAddressSpace();
145 }
146 MmLockAddressSpace(AddressSpace);
147 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
148 switch(VirtualMemoryInformationClass)
149 {
150 case MemoryBasicInformation:
151 {
152 PMEMORY_BASIC_INFORMATION Info =
153 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
154 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
155 {
156 MmUnlockAddressSpace(AddressSpace);
157 ObDereferenceObject(Process);
158 return(STATUS_INFO_LENGTH_MISMATCH);
159 }
160
161 if (MemoryArea == NULL)
162 {
163 Info->Type = 0;
164 Info->State = MEM_FREE;
165 Info->Protect = PAGE_NOACCESS;
166 Info->AllocationProtect = 0;
167 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
168 Info->AllocationBase = NULL;
169 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
170 Status = STATUS_SUCCESS;
171 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
172 }
173 else
174 {
175 switch(MemoryArea->Type)
176 {
177 case MEMORY_AREA_VIRTUAL_MEMORY:
178 case MEMORY_AREA_PEB_OR_TEB:
179 Status = MmQueryAnonMem(MemoryArea, Address, Info,
180 ResultLength);
181 break;
182 case MEMORY_AREA_SECTION_VIEW:
183 Status = MmQuerySectionView(MemoryArea, Address, Info,
184 ResultLength);
185 break;
186 case MEMORY_AREA_NO_ACCESS:
187 Info->Type = 0;
188 Info->State = MEM_FREE;
189 Info->Protect = MemoryArea->Attributes;
190 Info->AllocationProtect = MemoryArea->Attributes;
191 Info->BaseAddress = MemoryArea->StartingAddress;
192 Info->AllocationBase = MemoryArea->StartingAddress;
193 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
194 (ULONG_PTR)MemoryArea->StartingAddress;
195 Status = STATUS_SUCCESS;
196 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
197 break;
198 case MEMORY_AREA_SHARED_DATA:
199 Info->Type = 0;
200 Info->State = MEM_COMMIT;
201 Info->Protect = MemoryArea->Attributes;
202 Info->AllocationProtect = MemoryArea->Attributes;
203 Info->BaseAddress = MemoryArea->StartingAddress;
204 Info->AllocationBase = MemoryArea->StartingAddress;
205 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
206 (ULONG_PTR)MemoryArea->StartingAddress;
207 Status = STATUS_SUCCESS;
208 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
209 break;
210 case MEMORY_AREA_SYSTEM:
211 Info->Type = 0;
212 Info->State = MEM_COMMIT;
213 Info->Protect = MemoryArea->Attributes;
214 Info->AllocationProtect = MemoryArea->Attributes;
215 Info->BaseAddress = MemoryArea->StartingAddress;
216 Info->AllocationBase = MemoryArea->StartingAddress;
217 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
218 (ULONG_PTR)MemoryArea->StartingAddress;
219 Status = STATUS_SUCCESS;
220 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
221 break;
222 case MEMORY_AREA_KERNEL_STACK:
223 Info->Type = 0;
224 Info->State = MEM_COMMIT;
225 Info->Protect = MemoryArea->Attributes;
226 Info->AllocationProtect = MemoryArea->Attributes;
227 Info->BaseAddress = MemoryArea->StartingAddress;
228 Info->AllocationBase = MemoryArea->StartingAddress;
229 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
230 (ULONG_PTR)MemoryArea->StartingAddress;
231 Status = STATUS_SUCCESS;
232 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
233 break;
234 case MEMORY_AREA_PAGED_POOL:
235 Info->Type = 0;
236 Info->State = MEM_COMMIT;
237 Info->Protect = MemoryArea->Attributes;
238 Info->AllocationProtect = MemoryArea->Attributes;
239 Info->BaseAddress = MemoryArea->StartingAddress;
240 Info->AllocationBase = MemoryArea->StartingAddress;
241 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
242 (ULONG_PTR)MemoryArea->StartingAddress;
243 Status = STATUS_SUCCESS;
244 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
245 break;
246 default:
247 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
248 Status = STATUS_UNSUCCESSFUL;
249 *ResultLength = 0;
250 }
251 }
252 break;
253 }
254
255 default:
256 {
257 Status = STATUS_INVALID_INFO_CLASS;
258 *ResultLength = 0;
259 break;
260 }
261 }
262
263 MmUnlockAddressSpace(AddressSpace);
264 if (Address < MmSystemRangeStart)
265 {
266 ObDereferenceObject(Process);
267 }
268
269 return Status;
270 }
271
272 /* (tMk 2004.II.4)
273 * FUNCTION:
274 * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
275 *
276 */
277 NTSTATUS STDCALL
278 NtQueryVirtualMemory (IN HANDLE ProcessHandle,
279 IN PVOID Address,
280 IN CINT VirtualMemoryInformationClass,
281 OUT PVOID VirtualMemoryInformation,
282 IN ULONG Length,
283 OUT PULONG UnsafeResultLength)
284 {
285 NTSTATUS Status = STATUS_SUCCESS;
286 ULONG ResultLength = 0;
287 KPROCESSOR_MODE PreviousMode;
288 union
289 {
290 MEMORY_BASIC_INFORMATION BasicInfo;
291 }
292 VirtualMemoryInfo;
293
294 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
295 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
296 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
297 VirtualMemoryInformationClass,VirtualMemoryInformation,
298 Length,ResultLength);
299
300 PreviousMode = ExGetPreviousMode();
301
302 if (PreviousMode != KernelMode && UnsafeResultLength != NULL)
303 {
304 _SEH_TRY
305 {
306 ProbeForWrite(UnsafeResultLength,
307 sizeof(ULONG),
308 sizeof(ULONG));
309 }
310 _SEH_HANDLE
311 {
312 Status = _SEH_GetExceptionCode();
313 }
314 _SEH_END;
315
316 if (!NT_SUCCESS(Status))
317 {
318 return Status;
319 }
320 }
321
322 if (Address >= MmSystemRangeStart)
323 {
324 DPRINT1("Invalid parameter\n");
325 return STATUS_INVALID_PARAMETER;
326 }
327
328 Status = MiQueryVirtualMemory ( ProcessHandle,
329 Address,
330 VirtualMemoryInformationClass,
331 &VirtualMemoryInfo,
332 Length,
333 &ResultLength );
334
335 if (NT_SUCCESS(Status))
336 {
337 if (PreviousMode != KernelMode)
338 {
339 _SEH_TRY
340 {
341 if (ResultLength > 0)
342 {
343 ProbeForWrite(VirtualMemoryInformation,
344 ResultLength,
345 1);
346 RtlCopyMemory(VirtualMemoryInformation,
347 &VirtualMemoryInfo,
348 ResultLength);
349 }
350 if (UnsafeResultLength != NULL)
351 {
352 *UnsafeResultLength = ResultLength;
353 }
354 }
355 _SEH_HANDLE
356 {
357 Status = _SEH_GetExceptionCode();
358 }
359 _SEH_END;
360 }
361 else
362 {
363 if (ResultLength > 0)
364 {
365 RtlCopyMemory(VirtualMemoryInformation,
366 &VirtualMemoryInfo,
367 ResultLength);
368 }
369
370 if (UnsafeResultLength != NULL)
371 {
372 *UnsafeResultLength = ResultLength;
373 }
374 }
375 }
376
377 return(Status);
378 }
379
380
381 NTSTATUS STDCALL
382 MiProtectVirtualMemory(IN PEPROCESS Process,
383 IN OUT PVOID *BaseAddress,
384 IN OUT PULONG NumberOfBytesToProtect,
385 IN ULONG NewAccessProtection,
386 OUT PULONG OldAccessProtection OPTIONAL)
387 {
388 PMEMORY_AREA MemoryArea;
389 PMADDRESS_SPACE AddressSpace;
390 ULONG OldAccessProtection_;
391 NTSTATUS Status;
392
393 *NumberOfBytesToProtect =
394 PAGE_ROUND_UP((*BaseAddress) + (*NumberOfBytesToProtect)) -
395 PAGE_ROUND_DOWN(*BaseAddress);
396 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
397
398 AddressSpace = &Process->AddressSpace;
399
400 MmLockAddressSpace(AddressSpace);
401 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
402 if (MemoryArea == NULL)
403 {
404 MmUnlockAddressSpace(AddressSpace);
405 return STATUS_UNSUCCESSFUL;
406 }
407
408 if (OldAccessProtection == NULL)
409 OldAccessProtection = &OldAccessProtection_;
410
411 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
412 {
413 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
414 *NumberOfBytesToProtect, NewAccessProtection,
415 OldAccessProtection);
416 }
417 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
418 {
419 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
420 *NumberOfBytesToProtect,
421 NewAccessProtection,
422 OldAccessProtection);
423 }
424 else
425 {
426 /* FIXME: Should we return failure or success in this case? */
427 Status = STATUS_SUCCESS;
428 }
429
430 MmUnlockAddressSpace(AddressSpace);
431
432 return Status;
433 }
434
435
436 /* (tMk 2004.II.5)
437 * FUNCTION:
438 * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
439 *
440 */
441 NTSTATUS STDCALL
442 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
443 IN OUT PVOID *UnsafeBaseAddress,
444 IN OUT ULONG *UnsafeNumberOfBytesToProtect,
445 IN ULONG NewAccessProtection,
446 OUT PULONG UnsafeOldAccessProtection)
447 {
448 PEPROCESS Process;
449 ULONG OldAccessProtection;
450 PVOID BaseAddress = NULL;
451 ULONG NumberOfBytesToProtect = 0;
452 KPROCESSOR_MODE PreviousMode;
453 NTSTATUS Status = STATUS_SUCCESS;
454
455 PreviousMode = ExGetPreviousMode();
456
457 if (PreviousMode != KernelMode)
458 {
459 _SEH_TRY
460 {
461 ProbeForWrite(UnsafeBaseAddress,
462 sizeof(PVOID),
463 sizeof(ULONG));
464 ProbeForWrite(UnsafeBaseAddress,
465 sizeof(ULONG),
466 sizeof(ULONG));
467 ProbeForWrite(UnsafeOldAccessProtection,
468 sizeof(ULONG),
469 sizeof(ULONG));
470
471 BaseAddress = *UnsafeBaseAddress;
472 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
473 }
474 _SEH_HANDLE
475 {
476 Status = _SEH_GetExceptionCode();
477 }
478 _SEH_END;
479
480 if (!NT_SUCCESS(Status))
481 {
482 return Status;
483 }
484 }
485 else
486 {
487 BaseAddress = *UnsafeBaseAddress;
488 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
489 }
490
491 /* (tMk 2004.II.5) in Microsoft SDK I read:
492 * 'if this parameter is NULL or does not point to a valid variable, the function fails'
493 */
494 if(UnsafeOldAccessProtection == NULL)
495 {
496 return(STATUS_INVALID_PARAMETER);
497 }
498
499 Status = ObReferenceObjectByHandle(ProcessHandle,
500 PROCESS_VM_OPERATION,
501 PsProcessType,
502 UserMode,
503 (PVOID*)(&Process),
504 NULL);
505 if (!NT_SUCCESS(Status))
506 {
507 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
508 return(Status);
509 }
510
511 Status = MiProtectVirtualMemory(Process,
512 &BaseAddress,
513 &NumberOfBytesToProtect,
514 NewAccessProtection,
515 &OldAccessProtection);
516
517 ObDereferenceObject(Process);
518
519 if (PreviousMode != KernelMode)
520 {
521 _SEH_TRY
522 {
523 *UnsafeOldAccessProtection = OldAccessProtection;
524 *UnsafeBaseAddress = BaseAddress;
525 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
526 }
527 _SEH_HANDLE
528 {
529 Status = _SEH_GetExceptionCode();
530 }
531 _SEH_END;
532 }
533 else
534 {
535 *UnsafeOldAccessProtection = OldAccessProtection;
536 *UnsafeBaseAddress = BaseAddress;
537 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
538 }
539
540 return(Status);
541 }
542
543
544 /* (tMk 2004.II.05)
545 * FUNCTION:
546 * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
547 *
548 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
549 */
550 NTSTATUS STDCALL
551 NtReadVirtualMemory(IN HANDLE ProcessHandle,
552 IN PVOID BaseAddress,
553 OUT PVOID Buffer,
554 IN ULONG NumberOfBytesToRead,
555 OUT PULONG NumberOfBytesRead OPTIONAL)
556 {
557 PMDL Mdl;
558 PVOID SystemAddress;
559 KPROCESSOR_MODE PreviousMode;
560 PEPROCESS Process, CurrentProcess;
561 NTSTATUS Status = STATUS_SUCCESS;
562
563 PAGED_CODE();
564
565 PreviousMode = ExGetPreviousMode();
566
567 if(PreviousMode != KernelMode)
568 {
569 _SEH_TRY
570 {
571 ProbeForWrite(Buffer,
572 NumberOfBytesToRead,
573 1);
574 if(NumberOfBytesRead != NULL)
575 {
576 ProbeForWrite(NumberOfBytesRead,
577 sizeof(ULONG),
578 sizeof(ULONG));
579 }
580 }
581 _SEH_HANDLE
582 {
583 Status = _SEH_GetExceptionCode();
584 }
585 _SEH_END;
586
587 if(!NT_SUCCESS(Status))
588 {
589 return Status;
590 }
591 }
592
593 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
594 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
595 Buffer,NumberOfBytesToRead);
596
597 Status = ObReferenceObjectByHandle(ProcessHandle,
598 PROCESS_VM_READ,
599 NULL,
600 PreviousMode,
601 (PVOID*)(&Process),
602 NULL);
603 if (!NT_SUCCESS(Status))
604 {
605 return(Status);
606 }
607
608 CurrentProcess = PsGetCurrentProcess();
609
610 if (Process == CurrentProcess)
611 {
612 _SEH_TRY
613 {
614 RtlCopyMemory(Buffer, BaseAddress, NumberOfBytesToRead);
615 }
616 _SEH_HANDLE
617 {
618 Status = _SEH_GetExceptionCode();
619 }
620 _SEH_END;
621 }
622 else
623 {
624 Mdl = MmCreateMdl(NULL,
625 Buffer,
626 NumberOfBytesToRead);
627 if(Mdl == NULL)
628 {
629 ObDereferenceObject(Process);
630 return(STATUS_NO_MEMORY);
631 }
632 _SEH_TRY
633 {
634 MmProbeAndLockPages(Mdl,
635 PreviousMode,
636 IoWriteAccess);
637 }
638 _SEH_HANDLE
639 {
640 Status = _SEH_GetExceptionCode();
641 }
642 _SEH_END;
643
644 if(NT_SUCCESS(Status))
645 {
646 KeAttachProcess(&Process->Pcb);
647
648 SystemAddress = MmGetSystemAddressForMdl(Mdl);
649
650 Status = STATUS_SUCCESS;
651 _SEH_TRY {
652 ProbeForRead(BaseAddress, NumberOfBytesToRead, 1);
653 Status = STATUS_PARTIAL_COPY;
654 RtlCopyMemory(SystemAddress, BaseAddress, NumberOfBytesToRead);
655 Status = STATUS_SUCCESS;
656 } _SEH_HANDLE {
657 if(Status != STATUS_PARTIAL_COPY)
658 Status = _SEH_GetExceptionCode();
659 } _SEH_END;
660
661 KeDetachProcess();
662
663 if (Mdl->MappedSystemVa != NULL)
664 {
665 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
666 }
667 MmUnlockPages(Mdl);
668 }
669 ExFreePool(Mdl);
670 }
671
672 ObDereferenceObject(Process);
673
674 if((NT_SUCCESS(Status) || Status == STATUS_PARTIAL_COPY) &&
675 NumberOfBytesRead != NULL)
676 {
677 _SEH_TRY
678 {
679 *NumberOfBytesRead = NumberOfBytesToRead;
680 }
681 _SEH_HANDLE
682 {
683 Status = _SEH_GetExceptionCode();
684 }
685 _SEH_END;
686 }
687
688 return(Status);
689 }
690
691 /* (tMk 2004.II.05)
692 * FUNCTION: THIS function doesn't make a sense...
693 * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
694 */
695 NTSTATUS STDCALL
696 NtUnlockVirtualMemory(HANDLE ProcessHandle,
697 PVOID BaseAddress,
698 ULONG NumberOfBytesToUnlock,
699 PULONG NumberOfBytesUnlocked OPTIONAL)
700 {
701 // AG [08-20-03] : I have *no* idea if this is correct, I just used the
702 // other functions as a template and made a few intelligent guesses...
703
704 NTSTATUS Status;
705 PMDL Mdl;
706 PEPROCESS Process;
707
708 DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
709 "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle,BaseAddress,
710 NumberOfBytesToUnlock, NumberOfBytesUnlocked);
711
712 Status = ObReferenceObjectByHandle(ProcessHandle,
713 PROCESS_VM_WRITE,
714 NULL,
715 UserMode,
716 (PVOID*)(&Process),
717 NULL);
718 if (!NT_SUCCESS(Status))
719 {
720 return(Status);
721 }
722
723 Mdl = MmCreateMdl(NULL,
724 BaseAddress,
725 NumberOfBytesToUnlock);
726 if(Mdl == NULL)
727 {
728 ObDereferenceObject(Process);
729 return(STATUS_NO_MEMORY);
730 }
731
732 ObDereferenceObject(Process);
733
734 if (Mdl->MappedSystemVa != NULL)
735 {
736 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
737 }
738 MmUnlockPages(Mdl);
739 ExFreePool(Mdl);
740
741 *NumberOfBytesUnlocked = NumberOfBytesToUnlock;
742
743 return(STATUS_SUCCESS);
744 }
745
746
747 /* (tMk 2004.II.05)
748 * FUNCTION:
749 * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
750 *
751 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
752 */
753 NTSTATUS STDCALL
754 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
755 IN PVOID BaseAddress,
756 IN PVOID Buffer,
757 IN ULONG NumberOfBytesToWrite,
758 OUT PULONG NumberOfBytesWritten OPTIONAL)
759 {
760 PMDL Mdl;
761 PVOID SystemAddress;
762 PEPROCESS Process;
763 ULONG OldProtection = 0;
764 PVOID ProtectBaseAddress;
765 ULONG ProtectNumberOfBytes;
766 KPROCESSOR_MODE PreviousMode;
767 NTSTATUS CopyStatus, Status = STATUS_SUCCESS;
768
769 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
770 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
771 Buffer,NumberOfBytesToWrite);
772
773 PreviousMode = ExGetPreviousMode();
774
775 if (PreviousMode != KernelMode && NumberOfBytesWritten != NULL)
776 {
777 _SEH_TRY
778 {
779 ProbeForWrite(NumberOfBytesWritten,
780 sizeof(ULONG),
781 sizeof(ULONG));
782 }
783 _SEH_HANDLE
784 {
785 Status = _SEH_GetExceptionCode();
786 }
787 _SEH_END;
788
789 if (!NT_SUCCESS(Status))
790 {
791 return Status;
792 }
793 }
794
795 Status = ObReferenceObjectByHandle(ProcessHandle,
796 PROCESS_VM_WRITE,
797 NULL,
798 UserMode,
799 (PVOID*)(&Process),
800 NULL);
801 if (!NT_SUCCESS(Status))
802 {
803 return(Status);
804 }
805
806 /* We have to make sure the target memory is writable.
807 *
808 * I am not sure if it is correct to do this in any case, but it has to be
809 * done at least in some cases because you can use WriteProcessMemory to
810 * write into the .text section of a module where memcpy() would crash.
811 * -blight (2005/01/09)
812 */
813 ProtectBaseAddress = BaseAddress;
814 ProtectNumberOfBytes = NumberOfBytesToWrite;
815
816 CopyStatus = STATUS_SUCCESS;
817
818 /* Write memory */
819 if (Process == PsGetCurrentProcess())
820 {
821 Status = MiProtectVirtualMemory(Process,
822 &ProtectBaseAddress,
823 &ProtectNumberOfBytes,
824 PAGE_READWRITE,
825 &OldProtection);
826 if (!NT_SUCCESS(Status))
827 {
828 ObDereferenceObject(Process);
829 return Status;
830 }
831
832 if (PreviousMode != KernelMode)
833 {
834 _SEH_TRY
835 {
836 memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
837 }
838 _SEH_HANDLE
839 {
840 CopyStatus = _SEH_GetExceptionCode();
841 }
842 _SEH_END;
843 }
844 else
845 {
846 memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
847 }
848 }
849 else
850 {
851 /* Create MDL describing the source buffer. */
852 Mdl = MmCreateMdl(NULL,
853 Buffer,
854 NumberOfBytesToWrite);
855 if(Mdl == NULL)
856 {
857 ObDereferenceObject(Process);
858 return(STATUS_NO_MEMORY);
859 }
860
861 /* Make the target area writable. */
862 Status = MiProtectVirtualMemory(Process,
863 &ProtectBaseAddress,
864 &ProtectNumberOfBytes,
865 PAGE_READWRITE,
866 &OldProtection);
867 if (!NT_SUCCESS(Status))
868 {
869 ObDereferenceObject(Process);
870 ExFreePool(Mdl);
871 return Status;
872 }
873
874 /* Map the MDL. */
875 MmProbeAndLockPages(Mdl,
876 UserMode,
877 IoReadAccess);
878
879 /* Copy memory from the mapped MDL into the target buffer. */
880 KeAttachProcess(&Process->Pcb);
881
882 SystemAddress = MmGetSystemAddressForMdl(Mdl);
883 if (PreviousMode != KernelMode)
884 {
885 _SEH_TRY
886 {
887 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
888 }
889 _SEH_HANDLE
890 {
891 CopyStatus = _SEH_GetExceptionCode();
892 }
893 _SEH_END;
894 }
895 else
896 {
897 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
898 }
899
900 KeDetachProcess();
901
902 /* Free the MDL. */
903 if (Mdl->MappedSystemVa != NULL)
904 {
905 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
906 }
907 MmUnlockPages(Mdl);
908 ExFreePool(Mdl);
909 }
910
911 /* Reset the protection of the target memory. */
912 Status = MiProtectVirtualMemory(Process,
913 &ProtectBaseAddress,
914 &ProtectNumberOfBytes,
915 OldProtection,
916 &OldProtection);
917 if (!NT_SUCCESS(Status))
918 {
919 DPRINT1("Failed to reset protection of the target memory! (Status 0x%x)\n", Status);
920 /* FIXME: Should we bugcheck here? */
921 }
922
923 ObDereferenceObject(Process);
924
925 if (NumberOfBytesWritten != NULL)
926 {
927 if (PreviousMode != KernelMode)
928 {
929 _SEH_TRY
930 {
931 *NumberOfBytesWritten = NumberOfBytesToWrite;
932 }
933 _SEH_HANDLE
934 {
935 Status = _SEH_GetExceptionCode();
936 }
937 _SEH_END;
938 }
939 else
940 {
941 *NumberOfBytesWritten = NumberOfBytesToWrite;
942 }
943 }
944
945 return(NT_SUCCESS(CopyStatus) ? Status : CopyStatus);
946 }
947
948 /*
949 * @unimplemented
950 */
951
952 PVOID
953 STDCALL
954 MmGetVirtualForPhysical (
955 IN PHYSICAL_ADDRESS PhysicalAddress
956 )
957 {
958 UNIMPLEMENTED;
959 return 0;
960 }
961
962 /* FUNCTION:
963 * Called from EngSecureMem (subsys\win32k\eng\mem.c)
964 * @unimplemented
965 */
966 PVOID STDCALL
967 MmSecureVirtualMemory (PVOID Address,
968 SIZE_T Length,
969 ULONG Mode)
970 {
971 /* Only works for user space */
972 if (MmHighestUserAddress < Address)
973 {
974 return NULL;
975 }
976
977 UNIMPLEMENTED;
978
979 return 0;
980 }
981
982
983 /* FUNCTION:
984 * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
985 * @unimplemented
986 */
987 VOID STDCALL
988 MmUnsecureVirtualMemory(PVOID SecureMem)
989 {
990 if (NULL == SecureMem)
991 {
992 return;
993 }
994
995 UNIMPLEMENTED;
996 }
997
998
999 /*
1000 * @implemented
1001 */
1002 VOID STDCALL
1003 ProbeForRead (IN CONST VOID *Address,
1004 IN ULONG Length,
1005 IN ULONG Alignment)
1006 {
1007 ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
1008
1009 if (Length == 0)
1010 return;
1011
1012 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
1013 {
1014 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
1015 }
1016 else if ((ULONG_PTR)Address + Length - 1 < (ULONG_PTR)Address ||
1017 (ULONG_PTR)Address + Length - 1 >= (ULONG_PTR)MmUserProbeAddress)
1018 {
1019 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
1020 }
1021 }
1022
1023
1024 /*
1025 * @implemented
1026 */
1027 VOID STDCALL
1028 ProbeForWrite (IN CONST VOID *Address,
1029 IN ULONG Length,
1030 IN ULONG Alignment)
1031 {
1032 volatile CHAR *Current;
1033 PCHAR Last;
1034
1035 ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
1036
1037 if (Length == 0)
1038 return;
1039
1040 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
1041 {
1042 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
1043 }
1044
1045 Last = (PCHAR)((ULONG_PTR)Address + Length - 1);
1046 if ((ULONG_PTR)Last < (ULONG_PTR)Address ||
1047 (ULONG_PTR)Last >= (ULONG_PTR)MmUserProbeAddress)
1048 {
1049 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
1050 }
1051
1052 /* Check for accessible pages */
1053 Current = (CHAR*)Address;
1054 do
1055 {
1056 *Current = *Current;
1057 Current = (CHAR*)((ULONG_PTR)Current + PAGE_SIZE);
1058 } while (Current <= Last);
1059 }
1060
1061 /* EOF */