MmCopyVirtualMemory 失败,代码正确

MmCopyVirtualMemory failing, code is correct

我的情况是 MmCopyVirtualMemory 几乎总是(%99 的时间)returns STATUS_PARTIAL_COPY。 (我在 Ring0 驱动程序中运行)

我尝试了很多不同的方法,比如使用不同的变量大小和类型、不同的地址等...总是 returns STATUS_PARTIAL_COPY.

在线也没有任何帮助,这不是一个真正常见的错误。

错误描述:

{Partial Copy} Due to protection conflicts not all the requested bytes could be copied.

我读取进程内存的方式:

DWORD64 Read(DWORD64 SourceAddress, SIZE_T Size)
{

 SIZE_T Bytes;
 NTSTATUS Status = STATUS_SUCCESS;
 DWORD64 TempRead;

 DbgPrintEx(0, 0, "\nRead Address:%p\n", SourceAddress); // Always Prints     Correct Address
 DbgPrintEx(0, 0, "Read szAddress:%x\n", Size); // Prints Correct Size 8 bytes

 Status = MmCopyVirtualMemory(Process, SourceAddress, PsGetCurrentProcess(), &TempRead, Size, KernelMode, &Bytes);

 DbgPrintEx(0, 0, "Read Bytes:%x\n", Bytes); // Copied bytes - prints 0
 DbgPrintEx(0, 0, "Read Output:%p\n", TempRead); // prints 0 as expected since it failed

 if (!NT_SUCCESS(Status))
 {
      DbgPrintEx(0, 0, "Read Failed:%p\n", Status);
      return NULL;
 }

 return TempRead;
}

我如何使用它的示例:

NetMan = Read(BaseAddr + NET_MAN, sizeof(DWORD64));
//BaseAddr is a DWORD64 and NetMan is also a DWORD64

我已经检查了我的代码很多次了,所有这些似乎都是正确的。

在调查 MiDoPoolCopy(MmCopyVirtualMemoy 称之为)之后,它似乎在移动操作期间失败了:

// MiDoPoolCopy function
// Probe to make sure that the specified buffer is accessible in
// the target process.

//Wont execute (supplied KernelMode)
if ((InVa == FromAddress) && (PreviousMode != KernelMode)){ 
   Probing = TRUE;
   ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
   Probing = FALSE;
}

//Failing either here, copying inside the target process's address space to     the buffer
RtlCopyMemory (PoolArea, InVa, AmountToMove); 

KeUnstackDetachProcess (&ApcState);

KeStackAttachProcess (&ToProcess->Pcb, &ApcState); 
//
// Now operating in the context of the ToProcess.
//

//Wont execute (supplied KernelMode)
 if ((InVa == FromAddress) && (PreviousMode != KernelMode)){ 
    Probing = TRUE;
    ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
    Probing = FALSE;
}

Moving = TRUE;

//or failing here - moving from the Target Process to Source (target process->Kernel)
RtlCopyMemory (OutVa, PoolArea, AmountToMove);

这是返回的 SEH STATUS_PARTIAL_COPY(包含在 try 和 except 中)

//(wrapped in try and except)
//
// If the failure occurred during the move operation, determine
// which move failed, and calculate the number of bytes
// actually moved.
//

*NumberOfBytesRead = BufferSize - LeftToMove;
if (Moving == TRUE) {
    //
    // The failure occurred writing the data.
    //

    if (ExceptionAddressConfirmed == TRUE) {
       *NumberOfBytesRead = (SIZE_T)((ULONG_PTR)(BadVa -         (ULONG_PTR)FromAddress));
     }
}
return STATUS_PARTIAL_COPY;

MmCopyVirtualMemory(未记录的结构)

NTSTATUS NTAPI MmCopyVirtualMemory
(
    PEPROCESS SourceProcess,
    PVOID SourceAddress,
    PEPROCESS TargetProcess,
    PVOID TargetAddress,
    SIZE_T BufferSize,
    KPROCESSOR_MODE PreviousMode,
    PSIZE_T ReturnSize
);

这是 MmCopyVirtualMemory 和 MiDoPoolCopy 的源代码: https://lacicloud.net/custom/open/leaks/Windows%20Leaked%20Source/wrk-v1.2/base/ntos/mm/readwrt.c

任何帮助将不胜感激,我已经坚持了很长时间...

我知道这是旧的,但因为我遇到了同样的问题并修复了它,也许将来其他人会偶然发现这个问题。

阅读您的代码,问题在于您要复制数据的目标地址。您正在使用单个定义的 DWORD64,这就是当 运行 函数是一个简单变量(寄存器或堆栈)而不是您可以写入的内存区域时。

解决方案是提供一个正确的缓冲区,您之前使用 malloc 为其分配了内存。

示例(你想读取一个 DWORD64,基于你的代码并进行一些调整):

DWORD64* buffer = malloc(sizeof(DWORD64))
Status = MmCopyVirtualMemory(Process, (void*)SourceAddress, PsGetCurrentProcess(), (void*)buffer, sizeof(DWORD64), UserMode, &Bytes);

不要忘记在使用后释放缓冲区以防止内存泄漏。