Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1006 lines
26 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. readwrt.c
  5. Abstract:
  6. This module contains the routines which implement the capability
  7. to read and write the virtual memory of a target process.
  8. Author:
  9. Lou Perazzoli (loup) 22-May-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. //
  15. // The maximum amount to try to Probe and Lock is 14 pages, this
  16. // way it always fits in a 16 page allocation.
  17. //
  18. #define MAX_LOCK_SIZE ((ULONG)(14 * PAGE_SIZE))
  19. //
  20. // The maximum to move in a single block is 64k bytes.
  21. //
  22. #define MAX_MOVE_SIZE (LONG)0x10000
  23. //
  24. // The minimum to move is a single block is 128 bytes.
  25. //
  26. #define MINIMUM_ALLOCATION (LONG)128
  27. //
  28. // Define the pool move threshold value.
  29. //
  30. #define POOL_MOVE_THRESHOLD 511
  31. //
  32. // Define forward referenced procedure prototypes.
  33. //
  34. ULONG
  35. MiGetExceptionInfo (
  36. IN PEXCEPTION_POINTERS ExceptionPointers,
  37. IN PLOGICAL ExceptionAddressConfirmed,
  38. IN PULONG_PTR BadVa
  39. );
  40. NTSTATUS
  41. MiDoMappedCopy (
  42. IN PEPROCESS FromProcess,
  43. IN CONST VOID *FromAddress,
  44. IN PEPROCESS ToProcess,
  45. OUT PVOID ToAddress,
  46. IN SIZE_T BufferSize,
  47. IN KPROCESSOR_MODE PreviousMode,
  48. OUT PSIZE_T NumberOfBytesRead
  49. );
  50. NTSTATUS
  51. MiDoPoolCopy (
  52. IN PEPROCESS FromProcess,
  53. IN CONST VOID *FromAddress,
  54. IN PEPROCESS ToProcess,
  55. OUT PVOID ToAddress,
  56. IN SIZE_T BufferSize,
  57. IN KPROCESSOR_MODE PreviousMode,
  58. OUT PSIZE_T NumberOfBytesRead
  59. );
  60. #ifdef ALLOC_PRAGMA
  61. #pragma alloc_text(PAGE,MiGetExceptionInfo)
  62. #pragma alloc_text(PAGE,NtReadVirtualMemory)
  63. #pragma alloc_text(PAGE,NtWriteVirtualMemory)
  64. #pragma alloc_text(PAGE,MiDoMappedCopy)
  65. #pragma alloc_text(PAGE,MiDoPoolCopy)
  66. #pragma alloc_text(PAGE,MmCopyVirtualMemory)
  67. #endif
  68. #define COPY_STACK_SIZE 64
  69. NTSTATUS
  70. NtReadVirtualMemory (
  71. IN HANDLE ProcessHandle,
  72. IN PVOID BaseAddress,
  73. OUT PVOID Buffer,
  74. IN SIZE_T BufferSize,
  75. OUT PSIZE_T NumberOfBytesRead OPTIONAL
  76. )
  77. /*++
  78. Routine Description:
  79. This function copies the specified address range from the specified
  80. process into the specified address range of the current process.
  81. Arguments:
  82. ProcessHandle - Supplies an open handle to a process object.
  83. BaseAddress - Supplies the base address in the specified process
  84. to be read.
  85. Buffer - Supplies the address of a buffer which receives the
  86. contents from the specified process address space.
  87. BufferSize - Supplies the requested number of bytes to read from
  88. the specified process.
  89. NumberOfBytesRead - Receives the actual number of bytes
  90. transferred into the specified buffer.
  91. Return Value:
  92. NTSTATUS.
  93. --*/
  94. {
  95. SIZE_T BytesCopied;
  96. KPROCESSOR_MODE PreviousMode;
  97. PEPROCESS Process;
  98. NTSTATUS Status;
  99. PETHREAD CurrentThread;
  100. PAGED_CODE();
  101. //
  102. // Get the previous mode and probe output argument if necessary.
  103. //
  104. CurrentThread = PsGetCurrentThread ();
  105. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  106. if (PreviousMode != KernelMode) {
  107. if (((PCHAR)BaseAddress + BufferSize < (PCHAR)BaseAddress) ||
  108. ((PCHAR)Buffer + BufferSize < (PCHAR)Buffer) ||
  109. ((PVOID)((PCHAR)BaseAddress + BufferSize) > MM_HIGHEST_USER_ADDRESS) ||
  110. ((PVOID)((PCHAR)Buffer + BufferSize) > MM_HIGHEST_USER_ADDRESS)) {
  111. return STATUS_ACCESS_VIOLATION;
  112. }
  113. if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
  114. try {
  115. ProbeForWriteUlong_ptr (NumberOfBytesRead);
  116. } except(EXCEPTION_EXECUTE_HANDLER) {
  117. return GetExceptionCode();
  118. }
  119. }
  120. }
  121. //
  122. // If the buffer size is not zero, then attempt to read data from the
  123. // specified process address space into the current process address
  124. // space.
  125. //
  126. BytesCopied = 0;
  127. Status = STATUS_SUCCESS;
  128. if (BufferSize != 0) {
  129. //
  130. // Reference the target process.
  131. //
  132. Status = ObReferenceObjectByHandle(ProcessHandle,
  133. PROCESS_VM_READ,
  134. PsProcessType,
  135. PreviousMode,
  136. (PVOID *)&Process,
  137. NULL);
  138. //
  139. // If the process was successfully referenced, then attempt to
  140. // read the specified memory either by direct mapping or copying
  141. // through nonpaged pool.
  142. //
  143. if (Status == STATUS_SUCCESS) {
  144. Status = MmCopyVirtualMemory (Process,
  145. BaseAddress,
  146. PsGetCurrentProcessByThread(CurrentThread),
  147. Buffer,
  148. BufferSize,
  149. PreviousMode,
  150. &BytesCopied);
  151. //
  152. // Dereference the target process.
  153. //
  154. ObDereferenceObject(Process);
  155. }
  156. }
  157. //
  158. // If requested, return the number of bytes read.
  159. //
  160. if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
  161. try {
  162. *NumberOfBytesRead = BytesCopied;
  163. } except(EXCEPTION_EXECUTE_HANDLER) {
  164. NOTHING;
  165. }
  166. }
  167. return Status;
  168. }
  169. NTSTATUS
  170. NtWriteVirtualMemory(
  171. IN HANDLE ProcessHandle,
  172. OUT PVOID BaseAddress,
  173. IN CONST VOID *Buffer,
  174. IN SIZE_T BufferSize,
  175. OUT PSIZE_T NumberOfBytesWritten OPTIONAL
  176. )
  177. /*++
  178. Routine Description:
  179. This function copies the specified address range from the current
  180. process into the specified address range of the specified process.
  181. Arguments:
  182. ProcessHandle - Supplies an open handle to a process object.
  183. BaseAddress - Supplies the base address to be written to in the
  184. specified process.
  185. Buffer - Supplies the address of a buffer which contains the
  186. contents to be written into the specified process
  187. address space.
  188. BufferSize - Supplies the requested number of bytes to write
  189. into the specified process.
  190. NumberOfBytesWritten - Receives the actual number of bytes
  191. transferred into the specified address space.
  192. Return Value:
  193. NTSTATUS.
  194. --*/
  195. {
  196. SIZE_T BytesCopied;
  197. KPROCESSOR_MODE PreviousMode;
  198. PEPROCESS Process;
  199. NTSTATUS Status;
  200. PETHREAD CurrentThread;
  201. PAGED_CODE();
  202. //
  203. // Get the previous mode and probe output argument if necessary.
  204. //
  205. CurrentThread = PsGetCurrentThread ();
  206. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  207. if (PreviousMode != KernelMode) {
  208. if (((PCHAR)BaseAddress + BufferSize < (PCHAR)BaseAddress) ||
  209. ((PCHAR)Buffer + BufferSize < (PCHAR)Buffer) ||
  210. ((PVOID)((PCHAR)BaseAddress + BufferSize) > MM_HIGHEST_USER_ADDRESS) ||
  211. ((PVOID)((PCHAR)Buffer + BufferSize) > MM_HIGHEST_USER_ADDRESS)) {
  212. return STATUS_ACCESS_VIOLATION;
  213. }
  214. if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
  215. try {
  216. ProbeForWriteUlong_ptr(NumberOfBytesWritten);
  217. } except(EXCEPTION_EXECUTE_HANDLER) {
  218. return GetExceptionCode();
  219. }
  220. }
  221. }
  222. //
  223. // If the buffer size is not zero, then attempt to write data from the
  224. // current process address space into the target process address space.
  225. //
  226. BytesCopied = 0;
  227. Status = STATUS_SUCCESS;
  228. if (BufferSize != 0) {
  229. //
  230. // Reference the target process.
  231. //
  232. Status = ObReferenceObjectByHandle(ProcessHandle,
  233. PROCESS_VM_WRITE,
  234. PsProcessType,
  235. PreviousMode,
  236. (PVOID *)&Process,
  237. NULL);
  238. //
  239. // If the process was successfully referenced, then attempt to
  240. // write the specified memory either by direct mapping or copying
  241. // through nonpaged pool.
  242. //
  243. if (Status == STATUS_SUCCESS) {
  244. Status = MmCopyVirtualMemory (PsGetCurrentProcessByThread(CurrentThread),
  245. Buffer,
  246. Process,
  247. BaseAddress,
  248. BufferSize,
  249. PreviousMode,
  250. &BytesCopied);
  251. //
  252. // Dereference the target process.
  253. //
  254. ObDereferenceObject(Process);
  255. }
  256. }
  257. //
  258. // If requested, return the number of bytes read.
  259. //
  260. if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
  261. try {
  262. *NumberOfBytesWritten = BytesCopied;
  263. } except(EXCEPTION_EXECUTE_HANDLER) {
  264. NOTHING;
  265. }
  266. }
  267. return Status;
  268. }
  269. NTSTATUS
  270. MmCopyVirtualMemory(
  271. IN PEPROCESS FromProcess,
  272. IN CONST VOID *FromAddress,
  273. IN PEPROCESS ToProcess,
  274. OUT PVOID ToAddress,
  275. IN SIZE_T BufferSize,
  276. IN KPROCESSOR_MODE PreviousMode,
  277. OUT PSIZE_T NumberOfBytesCopied
  278. )
  279. {
  280. NTSTATUS Status;
  281. PEPROCESS ProcessToLock;
  282. if (BufferSize == 0) {
  283. ASSERT (FALSE); // No one should call with a zero size.
  284. return STATUS_SUCCESS;
  285. }
  286. ProcessToLock = FromProcess;
  287. if (FromProcess == PsGetCurrentProcess()) {
  288. ProcessToLock = ToProcess;
  289. }
  290. //
  291. // Make sure the process still has an address space.
  292. //
  293. if (ExAcquireRundownProtection (&ProcessToLock->RundownProtect) == FALSE) {
  294. return STATUS_PROCESS_IS_TERMINATING;
  295. }
  296. //
  297. // If the buffer size is greater than the pool move threshold,
  298. // then attempt to write the memory via direct mapping.
  299. //
  300. if (BufferSize > POOL_MOVE_THRESHOLD) {
  301. Status = MiDoMappedCopy(FromProcess,
  302. FromAddress,
  303. ToProcess,
  304. ToAddress,
  305. BufferSize,
  306. PreviousMode,
  307. NumberOfBytesCopied);
  308. //
  309. // If the completion status is not a working quota problem,
  310. // then finish the service. Otherwise, attempt to write the
  311. // memory through nonpaged pool.
  312. //
  313. if (Status != STATUS_WORKING_SET_QUOTA) {
  314. goto CompleteService;
  315. }
  316. *NumberOfBytesCopied = 0;
  317. }
  318. //
  319. // There was not enough working set quota to write the memory via
  320. // direct mapping or the size of the write was below the pool move
  321. // threshold. Attempt to write the specified memory through nonpaged
  322. // pool.
  323. //
  324. Status = MiDoPoolCopy(FromProcess,
  325. FromAddress,
  326. ToProcess,
  327. ToAddress,
  328. BufferSize,
  329. PreviousMode,
  330. NumberOfBytesCopied);
  331. //
  332. // Dereference the target process.
  333. //
  334. CompleteService:
  335. //
  336. // Indicate that the vm operation is complete.
  337. //
  338. ExReleaseRundownProtection (&ProcessToLock->RundownProtect);
  339. return Status;
  340. }
  341. ULONG
  342. MiGetExceptionInfo (
  343. IN PEXCEPTION_POINTERS ExceptionPointers,
  344. IN OUT PLOGICAL ExceptionAddressConfirmed,
  345. IN OUT PULONG_PTR BadVa
  346. )
  347. /*++
  348. Routine Description:
  349. This routine examines a exception record and extracts the virtual
  350. address of an access violation, guard page violation, or in-page error.
  351. Arguments:
  352. ExceptionPointers - Supplies a pointer to the exception record.
  353. ExceptionAddressConfirmed - Receives TRUE if the exception address was
  354. reliably detected, FALSE if not.
  355. BadVa - Receives the virtual address which caused the access violation.
  356. Return Value:
  357. EXECUTE_EXCEPTION_HANDLER
  358. --*/
  359. {
  360. PEXCEPTION_RECORD ExceptionRecord;
  361. PAGED_CODE();
  362. //
  363. // If the exception code is an access violation, guard page violation,
  364. // or an in-page read error, then return the faulting address. Otherwise.
  365. // return a special address value.
  366. //
  367. *ExceptionAddressConfirmed = FALSE;
  368. ExceptionRecord = ExceptionPointers->ExceptionRecord;
  369. if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
  370. (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
  371. (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) {
  372. //
  373. // The virtual address which caused the exception is the 2nd
  374. // parameter in the exception information array.
  375. //
  376. // The number of parameters will be zero if an exception handler
  377. // above us (like the one in MmProbeAndLockPages) caught the
  378. // original exception and subsequently just raised status.
  379. // This means the number of bytes copied is zero.
  380. //
  381. if (ExceptionRecord->NumberParameters > 1) {
  382. *ExceptionAddressConfirmed = TRUE;
  383. *BadVa = ExceptionRecord->ExceptionInformation[1];
  384. }
  385. }
  386. return EXCEPTION_EXECUTE_HANDLER;
  387. }
  388. NTSTATUS
  389. MiDoMappedCopy (
  390. IN PEPROCESS FromProcess,
  391. IN CONST VOID *FromAddress,
  392. IN PEPROCESS ToProcess,
  393. OUT PVOID ToAddress,
  394. IN SIZE_T BufferSize,
  395. IN KPROCESSOR_MODE PreviousMode,
  396. OUT PSIZE_T NumberOfBytesRead
  397. )
  398. /*++
  399. Routine Description:
  400. This function copies the specified address range from the specified
  401. process into the specified address range of the current process.
  402. Arguments:
  403. FromProcess - Supplies an open handle to a process object.
  404. FromAddress - Supplies the base address in the specified process
  405. to be read.
  406. ToProcess - Supplies an open handle to a process object.
  407. ToAddress - Supplies the address of a buffer which receives the
  408. contents from the specified process address space.
  409. BufferSize - Supplies the requested number of bytes to read from
  410. the specified process.
  411. PreviousMode - Supplies the previous processor mode.
  412. NumberOfBytesRead - Receives the actual number of bytes
  413. transferred into the specified buffer.
  414. Return Value:
  415. NTSTATUS.
  416. --*/
  417. {
  418. KAPC_STATE ApcState;
  419. SIZE_T AmountToMove;
  420. ULONG_PTR BadVa;
  421. LOGICAL Moving;
  422. LOGICAL Probing;
  423. LOGICAL LockedMdlPages;
  424. CONST VOID *InVa;
  425. SIZE_T LeftToMove;
  426. PSIZE_T MappedAddress;
  427. SIZE_T MaximumMoved;
  428. PMDL Mdl;
  429. PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + (MAX_LOCK_SIZE >> PAGE_SHIFT) + 1];
  430. PVOID OutVa;
  431. LOGICAL MappingFailed;
  432. LOGICAL ExceptionAddressConfirmed;
  433. PAGED_CODE();
  434. MappingFailed = FALSE;
  435. InVa = FromAddress;
  436. OutVa = ToAddress;
  437. MaximumMoved = MAX_LOCK_SIZE;
  438. if (BufferSize <= MAX_LOCK_SIZE) {
  439. MaximumMoved = BufferSize;
  440. }
  441. Mdl = (PMDL)&MdlHack[0];
  442. //
  443. // Map the data into the system part of the address space, then copy it.
  444. //
  445. LeftToMove = BufferSize;
  446. AmountToMove = MaximumMoved;
  447. Probing = FALSE;
  448. //
  449. // Initializing BadVa & ExceptionAddressConfirmed is not needed for
  450. // correctness but without it the compiler cannot compile this code
  451. // W4 to check for use of uninitialized variables.
  452. //
  453. BadVa = 0;
  454. ExceptionAddressConfirmed = FALSE;
  455. #if 0
  456. //
  457. // It is unfortunate that Windows 2000 and all the releases of NT always
  458. // inadvertently returned from this routine detached, as we must maintain
  459. // this behavior even now.
  460. //
  461. KeDetachProcess();
  462. #endif
  463. while (LeftToMove > 0) {
  464. if (LeftToMove < AmountToMove) {
  465. //
  466. // Set to move the remaining bytes.
  467. //
  468. AmountToMove = LeftToMove;
  469. }
  470. KeStackAttachProcess (&FromProcess->Pcb, &ApcState);
  471. MappedAddress = NULL;
  472. LockedMdlPages = FALSE;
  473. Moving = FALSE;
  474. ASSERT (Probing == FALSE);
  475. //
  476. // We may be touching a user's memory which could be invalid,
  477. // declare an exception handler.
  478. //
  479. try {
  480. //
  481. // Probe to make sure that the specified buffer is accessible in
  482. // the target process.
  483. //
  484. if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
  485. Probing = TRUE;
  486. ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
  487. Probing = FALSE;
  488. }
  489. //
  490. // Initialize MDL for request.
  491. //
  492. MmInitializeMdl (Mdl, (PVOID)InVa, AmountToMove);
  493. MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
  494. LockedMdlPages = TRUE;
  495. MappedAddress = MmMapLockedPagesSpecifyCache (Mdl,
  496. KernelMode,
  497. MmCached,
  498. NULL,
  499. FALSE,
  500. HighPagePriority);
  501. if (MappedAddress == NULL) {
  502. MappingFailed = TRUE;
  503. ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
  504. }
  505. //
  506. // Deattach from the FromProcess and attach to the ToProcess.
  507. //
  508. KeUnstackDetachProcess (&ApcState);
  509. KeStackAttachProcess (&ToProcess->Pcb, &ApcState);
  510. //
  511. // Now operating in the context of the ToProcess.
  512. //
  513. if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
  514. Probing = TRUE;
  515. ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
  516. Probing = FALSE;
  517. }
  518. Moving = TRUE;
  519. RtlCopyMemory (OutVa, MappedAddress, AmountToMove);
  520. } except (MiGetExceptionInfo (GetExceptionInformation(),
  521. &ExceptionAddressConfirmed,
  522. &BadVa)) {
  523. //
  524. // If an exception occurs during the move operation or probe,
  525. // return the exception code as the status value.
  526. //
  527. KeUnstackDetachProcess (&ApcState);
  528. if (MappedAddress != NULL) {
  529. MmUnmapLockedPages (MappedAddress, Mdl);
  530. }
  531. if (LockedMdlPages == TRUE) {
  532. MmUnlockPages (Mdl);
  533. }
  534. if (GetExceptionCode() == STATUS_WORKING_SET_QUOTA) {
  535. return STATUS_WORKING_SET_QUOTA;
  536. }
  537. if ((Probing == TRUE) || (MappingFailed == TRUE)) {
  538. return GetExceptionCode();
  539. }
  540. //
  541. // If the failure occurred during the move operation, determine
  542. // which move failed, and calculate the number of bytes
  543. // actually moved.
  544. //
  545. *NumberOfBytesRead = BufferSize - LeftToMove;
  546. if (Moving == TRUE) {
  547. if (ExceptionAddressConfirmed == TRUE) {
  548. *NumberOfBytesRead = (SIZE_T)((ULONG_PTR)BadVa - (ULONG_PTR)FromAddress);
  549. }
  550. }
  551. return STATUS_PARTIAL_COPY;
  552. }
  553. KeUnstackDetachProcess (&ApcState);
  554. MmUnmapLockedPages (MappedAddress, Mdl);
  555. MmUnlockPages (Mdl);
  556. LeftToMove -= AmountToMove;
  557. InVa = (PVOID)((ULONG_PTR)InVa + AmountToMove);
  558. OutVa = (PVOID)((ULONG_PTR)OutVa + AmountToMove);
  559. }
  560. //
  561. // Set number of bytes moved.
  562. //
  563. *NumberOfBytesRead = BufferSize;
  564. return STATUS_SUCCESS;
  565. }
  566. NTSTATUS
  567. MiDoPoolCopy (
  568. IN PEPROCESS FromProcess,
  569. IN CONST VOID *FromAddress,
  570. IN PEPROCESS ToProcess,
  571. OUT PVOID ToAddress,
  572. IN SIZE_T BufferSize,
  573. IN KPROCESSOR_MODE PreviousMode,
  574. OUT PSIZE_T NumberOfBytesRead
  575. )
  576. /*++
  577. Routine Description:
  578. This function copies the specified address range from the specified
  579. process into the specified address range of the current process.
  580. Arguments:
  581. ProcessHandle - Supplies an open handle to a process object.
  582. BaseAddress - Supplies the base address in the specified process
  583. to be read.
  584. Buffer - Supplies the address of a buffer which receives the
  585. contents from the specified process address space.
  586. BufferSize - Supplies the requested number of bytes to read from
  587. the specified process.
  588. PreviousMode - Supplies the previous processor mode.
  589. NumberOfBytesRead - Receives the actual number of bytes
  590. transferred into the specified buffer.
  591. Return Value:
  592. NTSTATUS.
  593. --*/
  594. {
  595. KAPC_STATE ApcState;
  596. SIZE_T AmountToMove;
  597. LOGICAL ExceptionAddressConfirmed;
  598. ULONG_PTR BadVa;
  599. PEPROCESS CurrentProcess;
  600. LOGICAL Moving;
  601. LOGICAL Probing;
  602. CONST VOID *InVa;
  603. SIZE_T LeftToMove;
  604. SIZE_T MaximumMoved;
  605. PVOID OutVa;
  606. PVOID PoolArea;
  607. LONGLONG StackArray[COPY_STACK_SIZE];
  608. ULONG FreePool;
  609. PAGED_CODE();
  610. ASSERT (BufferSize != 0);
  611. //
  612. // Get the address of the current process object and initialize copy
  613. // parameters.
  614. //
  615. CurrentProcess = PsGetCurrentProcess();
  616. InVa = FromAddress;
  617. OutVa = ToAddress;
  618. //
  619. // Allocate non-paged memory to copy in and out of.
  620. //
  621. MaximumMoved = MAX_MOVE_SIZE;
  622. if (BufferSize <= MAX_MOVE_SIZE) {
  623. MaximumMoved = BufferSize;
  624. }
  625. FreePool = FALSE;
  626. if (BufferSize <= sizeof(StackArray)) {
  627. PoolArea = (PVOID)&StackArray[0];
  628. } else {
  629. do {
  630. PoolArea = ExAllocatePoolWithTag (NonPagedPool, MaximumMoved, 'wRmM');
  631. if (PoolArea != NULL) {
  632. FreePool = TRUE;
  633. break;
  634. }
  635. MaximumMoved = MaximumMoved >> 1;
  636. if (MaximumMoved <= sizeof(StackArray)) {
  637. PoolArea = (PVOID)&StackArray[0];
  638. break;
  639. }
  640. } while (TRUE);
  641. }
  642. //
  643. // Initializing BadVa & ExceptionAddressConfirmed is not needed for
  644. // correctness but without it the compiler cannot compile this code
  645. // W4 to check for use of uninitialized variables.
  646. //
  647. BadVa = 0;
  648. ExceptionAddressConfirmed = FALSE;
  649. //
  650. // Copy the data into pool, then copy back into the ToProcess.
  651. //
  652. LeftToMove = BufferSize;
  653. AmountToMove = MaximumMoved;
  654. Probing = FALSE;
  655. #if 0
  656. //
  657. // It is unfortunate that Windows 2000 and all the releases of NT always
  658. // inadvertently returned from this routine detached, as we must maintain
  659. // this behavior even now.
  660. //
  661. KeDetachProcess();
  662. #endif
  663. while (LeftToMove > 0) {
  664. if (LeftToMove < AmountToMove) {
  665. //
  666. // Set to move the remaining bytes.
  667. //
  668. AmountToMove = LeftToMove;
  669. }
  670. KeStackAttachProcess (&FromProcess->Pcb, &ApcState);
  671. Moving = FALSE;
  672. ASSERT (Probing == FALSE);
  673. //
  674. // We may be touching a user's memory which could be invalid,
  675. // declare an exception handler.
  676. //
  677. try {
  678. //
  679. // Probe to make sure that the specified buffer is accessible in
  680. // the target process.
  681. //
  682. if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
  683. Probing = TRUE;
  684. ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
  685. Probing = FALSE;
  686. }
  687. RtlCopyMemory (PoolArea, InVa, AmountToMove);
  688. KeUnstackDetachProcess (&ApcState);
  689. KeStackAttachProcess (&ToProcess->Pcb, &ApcState);
  690. //
  691. // Now operating in the context of the ToProcess.
  692. //
  693. if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
  694. Probing = TRUE;
  695. ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
  696. Probing = FALSE;
  697. }
  698. Moving = TRUE;
  699. RtlCopyMemory (OutVa, PoolArea, AmountToMove);
  700. } except (MiGetExceptionInfo (GetExceptionInformation(),
  701. &ExceptionAddressConfirmed,
  702. &BadVa)) {
  703. //
  704. // If an exception occurs during the move operation or probe,
  705. // return the exception code as the status value.
  706. //
  707. KeUnstackDetachProcess (&ApcState);
  708. if (FreePool) {
  709. ExFreePool (PoolArea);
  710. }
  711. if (Probing == TRUE) {
  712. return GetExceptionCode();
  713. }
  714. //
  715. // If the failure occurred during the move operation, determine
  716. // which move failed, and calculate the number of bytes
  717. // actually moved.
  718. //
  719. *NumberOfBytesRead = BufferSize - LeftToMove;
  720. if (Moving == TRUE) {
  721. //
  722. // The failure occurred writing the data.
  723. //
  724. if (ExceptionAddressConfirmed == TRUE) {
  725. *NumberOfBytesRead = (SIZE_T)((ULONG_PTR)(BadVa - (ULONG_PTR)FromAddress));
  726. }
  727. }
  728. return STATUS_PARTIAL_COPY;
  729. }
  730. KeUnstackDetachProcess (&ApcState);
  731. LeftToMove -= AmountToMove;
  732. InVa = (PVOID)((ULONG_PTR)InVa + AmountToMove);
  733. OutVa = (PVOID)((ULONG_PTR)OutVa + AmountToMove);
  734. }
  735. if (FreePool) {
  736. ExFreePool (PoolArea);
  737. }
  738. //
  739. // Set number of bytes moved.
  740. //
  741. *NumberOfBytesRead = BufferSize;
  742. return STATUS_SUCCESS;
  743. }