- use inlined probing macros for basic types
[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 ProbeForWriteUlong(UnsafeResultLength);
307 }
308 _SEH_HANDLE
309 {
310 Status = _SEH_GetExceptionCode();
311 }
312 _SEH_END;
313
314 if (!NT_SUCCESS(Status))
315 {
316 return Status;
317 }
318 }
319
320 if (Address >= MmSystemRangeStart)
321 {
322 DPRINT1("Invalid parameter\n");
323 return STATUS_INVALID_PARAMETER;
324 }
325
326 Status = MiQueryVirtualMemory ( ProcessHandle,
327 Address,
328 VirtualMemoryInformationClass,
329 &VirtualMemoryInfo,
330 Length,
331 &ResultLength );
332
333 if (NT_SUCCESS(Status))
334 {
335 if (PreviousMode != KernelMode)
336 {
337 _SEH_TRY
338 {
339 if (ResultLength > 0)
340 {
341 ProbeForWrite(VirtualMemoryInformation,
342 ResultLength,
343 1);
344 RtlCopyMemory(VirtualMemoryInformation,
345 &VirtualMemoryInfo,
346 ResultLength);
347 }
348 if (UnsafeResultLength != NULL)
349 {
350 *UnsafeResultLength = ResultLength;
351 }
352 }
353 _SEH_HANDLE
354 {
355 Status = _SEH_GetExceptionCode();
356 }
357 _SEH_END;
358 }
359 else
360 {
361 if (ResultLength > 0)
362 {
363 RtlCopyMemory(VirtualMemoryInformation,
364 &VirtualMemoryInfo,
365 ResultLength);
366 }
367
368 if (UnsafeResultLength != NULL)
369 {
370 *UnsafeResultLength = ResultLength;
371 }
372 }
373 }
374
375 return(Status);
376 }
377
378
379 NTSTATUS STDCALL
380 MiProtectVirtualMemory(IN PEPROCESS Process,
381 IN OUT PVOID *BaseAddress,
382 IN OUT PULONG NumberOfBytesToProtect,
383 IN ULONG NewAccessProtection,
384 OUT PULONG OldAccessProtection OPTIONAL)
385 {
386 PMEMORY_AREA MemoryArea;
387 PMADDRESS_SPACE AddressSpace;
388 ULONG OldAccessProtection_;
389 NTSTATUS Status;
390
391 *NumberOfBytesToProtect =
392 PAGE_ROUND_UP((*BaseAddress) + (*NumberOfBytesToProtect)) -
393 PAGE_ROUND_DOWN(*BaseAddress);
394 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
395
396 AddressSpace = &Process->AddressSpace;
397
398 MmLockAddressSpace(AddressSpace);
399 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
400 if (MemoryArea == NULL)
401 {
402 MmUnlockAddressSpace(AddressSpace);
403 return STATUS_UNSUCCESSFUL;
404 }
405
406 if (OldAccessProtection == NULL)
407 OldAccessProtection = &OldAccessProtection_;
408
409 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
410 {
411 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
412 *NumberOfBytesToProtect, NewAccessProtection,
413 OldAccessProtection);
414 }
415 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
416 {
417 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
418 *NumberOfBytesToProtect,
419 NewAccessProtection,
420 OldAccessProtection);
421 }
422 else
423 {
424 /* FIXME: Should we return failure or success in this case? */
425 Status = STATUS_SUCCESS;
426 }
427
428 MmUnlockAddressSpace(AddressSpace);
429
430 return Status;
431 }
432
433
434 /* (tMk 2004.II.5)
435 * FUNCTION:
436 * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
437 *
438 */
439 NTSTATUS STDCALL
440 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
441 IN OUT PVOID *UnsafeBaseAddress,
442 IN OUT ULONG *UnsafeNumberOfBytesToProtect,
443 IN ULONG NewAccessProtection,
444 OUT PULONG UnsafeOldAccessProtection)
445 {
446 PEPROCESS Process;
447 ULONG OldAccessProtection;
448 PVOID BaseAddress = NULL;
449 ULONG NumberOfBytesToProtect = 0;
450 KPROCESSOR_MODE PreviousMode;
451 NTSTATUS Status = STATUS_SUCCESS;
452
453 PreviousMode = ExGetPreviousMode();
454
455 if (PreviousMode != KernelMode)
456 {
457 _SEH_TRY
458 {
459 ProbeForWritePointer(UnsafeBaseAddress);
460 ProbeForWriteUlong(UnsafeNumberOfBytesToProtect);
461 ProbeForWriteUlong(UnsafeOldAccessProtection);
462
463 BaseAddress = *UnsafeBaseAddress;
464 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
465 }
466 _SEH_HANDLE
467 {
468 Status = _SEH_GetExceptionCode();
469 }
470 _SEH_END;
471
472 if (!NT_SUCCESS(Status))
473 {
474 return Status;
475 }
476 }
477 else
478 {
479 BaseAddress = *UnsafeBaseAddress;
480 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
481 }
482
483 /* (tMk 2004.II.5) in Microsoft SDK I read:
484 * 'if this parameter is NULL or does not point to a valid variable, the function fails'
485 */
486 if(UnsafeOldAccessProtection == NULL)
487 {
488 return(STATUS_INVALID_PARAMETER);
489 }
490
491 Status = ObReferenceObjectByHandle(ProcessHandle,
492 PROCESS_VM_OPERATION,
493 PsProcessType,
494 UserMode,
495 (PVOID*)(&Process),
496 NULL);
497 if (!NT_SUCCESS(Status))
498 {
499 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
500 return(Status);
501 }
502
503 Status = MiProtectVirtualMemory(Process,
504 &BaseAddress,
505 &NumberOfBytesToProtect,
506 NewAccessProtection,
507 &OldAccessProtection);
508
509 ObDereferenceObject(Process);
510
511 if (PreviousMode != KernelMode)
512 {
513 _SEH_TRY
514 {
515 *UnsafeOldAccessProtection = OldAccessProtection;
516 *UnsafeBaseAddress = BaseAddress;
517 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
518 }
519 _SEH_HANDLE
520 {
521 Status = _SEH_GetExceptionCode();
522 }
523 _SEH_END;
524 }
525 else
526 {
527 *UnsafeOldAccessProtection = OldAccessProtection;
528 *UnsafeBaseAddress = BaseAddress;
529 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
530 }
531
532 return(Status);
533 }
534
535
536 /* (tMk 2004.II.05)
537 * FUNCTION:
538 * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
539 *
540 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
541 */
542 NTSTATUS STDCALL
543 NtReadVirtualMemory(IN HANDLE ProcessHandle,
544 IN PVOID BaseAddress,
545 OUT PVOID Buffer,
546 IN ULONG NumberOfBytesToRead,
547 OUT PULONG NumberOfBytesRead OPTIONAL)
548 {
549 PMDL Mdl;
550 PVOID SystemAddress;
551 KPROCESSOR_MODE PreviousMode;
552 PEPROCESS Process, CurrentProcess;
553 NTSTATUS Status = STATUS_SUCCESS;
554
555 PAGED_CODE();
556
557 PreviousMode = ExGetPreviousMode();
558
559 if(PreviousMode != KernelMode)
560 {
561 _SEH_TRY
562 {
563 ProbeForWrite(Buffer,
564 NumberOfBytesToRead,
565 1);
566 if(NumberOfBytesRead != NULL)
567 {
568 ProbeForWriteUlong(NumberOfBytesRead);
569 }
570 }
571 _SEH_HANDLE
572 {
573 Status = _SEH_GetExceptionCode();
574 }
575 _SEH_END;
576
577 if(!NT_SUCCESS(Status))
578 {
579 return Status;
580 }
581 }
582
583 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
584 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
585 Buffer,NumberOfBytesToRead);
586
587 Status = ObReferenceObjectByHandle(ProcessHandle,
588 PROCESS_VM_READ,
589 NULL,
590 PreviousMode,
591 (PVOID*)(&Process),
592 NULL);
593 if (!NT_SUCCESS(Status))
594 {
595 return(Status);
596 }
597
598 CurrentProcess = PsGetCurrentProcess();
599
600 if (Process == CurrentProcess)
601 {
602 _SEH_TRY
603 {
604 RtlCopyMemory(Buffer, BaseAddress, NumberOfBytesToRead);
605 }
606 _SEH_HANDLE
607 {
608 Status = _SEH_GetExceptionCode();
609 }
610 _SEH_END;
611 }
612 else
613 {
614 Mdl = MmCreateMdl(NULL,
615 Buffer,
616 NumberOfBytesToRead);
617 if(Mdl == NULL)
618 {
619 ObDereferenceObject(Process);
620 return(STATUS_NO_MEMORY);
621 }
622 _SEH_TRY
623 {
624 MmProbeAndLockPages(Mdl,
625 PreviousMode,
626 IoWriteAccess);
627 }
628 _SEH_HANDLE
629 {
630 Status = _SEH_GetExceptionCode();
631 }
632 _SEH_END;
633
634 if(NT_SUCCESS(Status))
635 {
636 KeAttachProcess(&Process->Pcb);
637
638 SystemAddress = MmGetSystemAddressForMdl(Mdl);
639
640 Status = STATUS_SUCCESS;
641 _SEH_TRY {
642 ProbeForRead(BaseAddress, NumberOfBytesToRead, 1);
643 Status = STATUS_PARTIAL_COPY;
644 RtlCopyMemory(SystemAddress, BaseAddress, NumberOfBytesToRead);
645 Status = STATUS_SUCCESS;
646 } _SEH_HANDLE {
647 if(Status != STATUS_PARTIAL_COPY)
648 Status = _SEH_GetExceptionCode();
649 } _SEH_END;
650
651 KeDetachProcess();
652
653 if (Mdl->MappedSystemVa != NULL)
654 {
655 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
656 }
657 MmUnlockPages(Mdl);
658 }
659 ExFreePool(Mdl);
660 }
661
662 ObDereferenceObject(Process);
663
664 if((NT_SUCCESS(Status) || Status == STATUS_PARTIAL_COPY) &&
665 NumberOfBytesRead != NULL)
666 {
667 _SEH_TRY
668 {
669 *NumberOfBytesRead = NumberOfBytesToRead;
670 }
671 _SEH_HANDLE
672 {
673 Status = _SEH_GetExceptionCode();
674 }
675 _SEH_END;
676 }
677
678 return(Status);
679 }
680
681 /* (tMk 2004.II.05)
682 * FUNCTION: THIS function doesn't make a sense...
683 * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
684 */
685 NTSTATUS STDCALL
686 NtUnlockVirtualMemory(HANDLE ProcessHandle,
687 PVOID BaseAddress,
688 ULONG NumberOfBytesToUnlock,
689 PULONG NumberOfBytesUnlocked OPTIONAL)
690 {
691 // AG [08-20-03] : I have *no* idea if this is correct, I just used the
692 // other functions as a template and made a few intelligent guesses...
693
694 NTSTATUS Status;
695 PMDL Mdl;
696 PEPROCESS Process;
697
698 DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
699 "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle,BaseAddress,
700 NumberOfBytesToUnlock, NumberOfBytesUnlocked);
701
702 Status = ObReferenceObjectByHandle(ProcessHandle,
703 PROCESS_VM_WRITE,
704 NULL,
705 UserMode,
706 (PVOID*)(&Process),
707 NULL);
708 if (!NT_SUCCESS(Status))
709 {
710 return(Status);
711 }
712
713 Mdl = MmCreateMdl(NULL,
714 BaseAddress,
715 NumberOfBytesToUnlock);
716 if(Mdl == NULL)
717 {
718 ObDereferenceObject(Process);
719 return(STATUS_NO_MEMORY);
720 }
721
722 ObDereferenceObject(Process);
723
724 if (Mdl->MappedSystemVa != NULL)
725 {
726 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
727 }
728 MmUnlockPages(Mdl);
729 ExFreePool(Mdl);
730
731 *NumberOfBytesUnlocked = NumberOfBytesToUnlock;
732
733 return(STATUS_SUCCESS);
734 }
735
736
737 /* (tMk 2004.II.05)
738 * FUNCTION:
739 * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
740 *
741 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
742 */
743 NTSTATUS STDCALL
744 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
745 IN PVOID BaseAddress,
746 IN PVOID Buffer,
747 IN ULONG NumberOfBytesToWrite,
748 OUT PULONG NumberOfBytesWritten OPTIONAL)
749 {
750 PMDL Mdl;
751 PVOID SystemAddress;
752 PEPROCESS Process;
753 ULONG OldProtection = 0;
754 PVOID ProtectBaseAddress;
755 ULONG ProtectNumberOfBytes;
756 KPROCESSOR_MODE PreviousMode;
757 NTSTATUS CopyStatus, Status = STATUS_SUCCESS;
758
759 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
760 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
761 Buffer,NumberOfBytesToWrite);
762
763 PreviousMode = ExGetPreviousMode();
764
765 if (PreviousMode != KernelMode && NumberOfBytesWritten != NULL)
766 {
767 _SEH_TRY
768 {
769 ProbeForWriteUlong(NumberOfBytesWritten);
770 }
771 _SEH_HANDLE
772 {
773 Status = _SEH_GetExceptionCode();
774 }
775 _SEH_END;
776
777 if (!NT_SUCCESS(Status))
778 {
779 return Status;
780 }
781 }
782
783 Status = ObReferenceObjectByHandle(ProcessHandle,
784 PROCESS_VM_WRITE,
785 NULL,
786 UserMode,
787 (PVOID*)(&Process),
788 NULL);
789 if (!NT_SUCCESS(Status))
790 {
791 return(Status);
792 }
793
794 /* We have to make sure the target memory is writable.
795 *
796 * I am not sure if it is correct to do this in any case, but it has to be
797 * done at least in some cases because you can use WriteProcessMemory to
798 * write into the .text section of a module where memcpy() would crash.
799 * -blight (2005/01/09)
800 */
801 ProtectBaseAddress = BaseAddress;
802 ProtectNumberOfBytes = NumberOfBytesToWrite;
803
804 CopyStatus = STATUS_SUCCESS;
805
806 /* Write memory */
807 if (Process == PsGetCurrentProcess())
808 {
809 Status = MiProtectVirtualMemory(Process,
810 &ProtectBaseAddress,
811 &ProtectNumberOfBytes,
812 PAGE_READWRITE,
813 &OldProtection);
814 if (!NT_SUCCESS(Status))
815 {
816 ObDereferenceObject(Process);
817 return Status;
818 }
819
820 if (PreviousMode != KernelMode)
821 {
822 _SEH_TRY
823 {
824 memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
825 }
826 _SEH_HANDLE
827 {
828 CopyStatus = _SEH_GetExceptionCode();
829 }
830 _SEH_END;
831 }
832 else
833 {
834 memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
835 }
836 }
837 else
838 {
839 /* Create MDL describing the source buffer. */
840 Mdl = MmCreateMdl(NULL,
841 Buffer,
842 NumberOfBytesToWrite);
843 if(Mdl == NULL)
844 {
845 ObDereferenceObject(Process);
846 return(STATUS_NO_MEMORY);
847 }
848
849 /* Make the target area writable. */
850 Status = MiProtectVirtualMemory(Process,
851 &ProtectBaseAddress,
852 &ProtectNumberOfBytes,
853 PAGE_READWRITE,
854 &OldProtection);
855 if (!NT_SUCCESS(Status))
856 {
857 ObDereferenceObject(Process);
858 ExFreePool(Mdl);
859 return Status;
860 }
861
862 /* Map the MDL. */
863 MmProbeAndLockPages(Mdl,
864 UserMode,
865 IoReadAccess);
866
867 /* Copy memory from the mapped MDL into the target buffer. */
868 KeAttachProcess(&Process->Pcb);
869
870 SystemAddress = MmGetSystemAddressForMdl(Mdl);
871 if (PreviousMode != KernelMode)
872 {
873 _SEH_TRY
874 {
875 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
876 }
877 _SEH_HANDLE
878 {
879 CopyStatus = _SEH_GetExceptionCode();
880 }
881 _SEH_END;
882 }
883 else
884 {
885 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
886 }
887
888 KeDetachProcess();
889
890 /* Free the MDL. */
891 if (Mdl->MappedSystemVa != NULL)
892 {
893 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
894 }
895 MmUnlockPages(Mdl);
896 ExFreePool(Mdl);
897 }
898
899 /* Reset the protection of the target memory. */
900 Status = MiProtectVirtualMemory(Process,
901 &ProtectBaseAddress,
902 &ProtectNumberOfBytes,
903 OldProtection,
904 &OldProtection);
905 if (!NT_SUCCESS(Status))
906 {
907 DPRINT1("Failed to reset protection of the target memory! (Status 0x%x)\n", Status);
908 /* FIXME: Should we bugcheck here? */
909 }
910
911 ObDereferenceObject(Process);
912
913 if (NumberOfBytesWritten != NULL)
914 {
915 if (PreviousMode != KernelMode)
916 {
917 _SEH_TRY
918 {
919 *NumberOfBytesWritten = NumberOfBytesToWrite;
920 }
921 _SEH_HANDLE
922 {
923 Status = _SEH_GetExceptionCode();
924 }
925 _SEH_END;
926 }
927 else
928 {
929 *NumberOfBytesWritten = NumberOfBytesToWrite;
930 }
931 }
932
933 return(NT_SUCCESS(CopyStatus) ? Status : CopyStatus);
934 }
935
936 /*
937 * @unimplemented
938 */
939
940 PVOID
941 STDCALL
942 MmGetVirtualForPhysical (
943 IN PHYSICAL_ADDRESS PhysicalAddress
944 )
945 {
946 UNIMPLEMENTED;
947 return 0;
948 }
949
950 /* FUNCTION:
951 * Called from EngSecureMem (subsys\win32k\eng\mem.c)
952 * @unimplemented
953 */
954 PVOID STDCALL
955 MmSecureVirtualMemory (PVOID Address,
956 SIZE_T Length,
957 ULONG Mode)
958 {
959 /* Only works for user space */
960 if (MmHighestUserAddress < Address)
961 {
962 return NULL;
963 }
964
965 UNIMPLEMENTED;
966
967 return 0;
968 }
969
970
971 /* FUNCTION:
972 * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
973 * @unimplemented
974 */
975 VOID STDCALL
976 MmUnsecureVirtualMemory(PVOID SecureMem)
977 {
978 if (NULL == SecureMem)
979 {
980 return;
981 }
982
983 UNIMPLEMENTED;
984 }
985
986
987 /*
988 * @implemented
989 */
990 VOID STDCALL
991 ProbeForRead (IN CONST VOID *Address,
992 IN ULONG Length,
993 IN ULONG Alignment)
994 {
995 ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
996
997 if (Length == 0)
998 return;
999
1000 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
1001 {
1002 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
1003 }
1004 else if ((ULONG_PTR)Address + Length - 1 < (ULONG_PTR)Address ||
1005 (ULONG_PTR)Address + Length - 1 >= (ULONG_PTR)MmUserProbeAddress)
1006 {
1007 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
1008 }
1009 }
1010
1011
1012 /*
1013 * @implemented
1014 */
1015 VOID STDCALL
1016 ProbeForWrite (IN CONST VOID *Address,
1017 IN ULONG Length,
1018 IN ULONG Alignment)
1019 {
1020 volatile CHAR *Current;
1021 PCHAR Last;
1022
1023 ASSERT(Alignment == 1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
1024
1025 if (Length == 0)
1026 return;
1027
1028 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
1029 {
1030 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
1031 }
1032
1033 Last = (PCHAR)((ULONG_PTR)Address + Length - 1);
1034 if ((ULONG_PTR)Last < (ULONG_PTR)Address ||
1035 (ULONG_PTR)Last >= (ULONG_PTR)MmUserProbeAddress)
1036 {
1037 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
1038 }
1039
1040 /* Check for accessible pages */
1041 Current = (CHAR*)Address;
1042 do
1043 {
1044 *Current = *Current;
1045 Current = (CHAR*)((ULONG_PTR)Current + PAGE_SIZE);
1046 } while (Current <= Last);
1047 }
1048
1049 /* EOF */