【问题标题】:Write / read process memory写/读进程内存
【发布时间】:2020-10-25 11:38:05
【问题描述】:

如何写入/读取任何进程的内存?

据我了解,我必须使用 WriteProcessMemory / ReadProcessMemory winapi 函数。

这是我的开始:

Add-Type -TypeDefinition '
using System;
using System.Runtime.InteropServices;

public class winapi{
    [DllImport("kernel32.dll")]public static extern Boolean WriteProcessMemory(
        IntPtr hProcess,
        IntPtr lpBaseAddress,
        IntPtr lpBuffer,
        UInt32 nSize,
        ref UInt32 lpNumberOfBytesWritten
    );
    [DllImport("kernel32.dll")]public static extern Boolean ReadProcessMemory( 
        IntPtr hProcess, 
        IntPtr lpBaseAddress,
        IntPtr lpBuffer,
        UInt32 dwSize, 
        ref UInt32 lpNumberOfBytesRead
    );
}
'

[IntPtr]$mem = [Runtime.InteropServices.Marshal]::AllocHGlobal(4)

$dataToWrite = "ABC"

[IntPtr]$hProcess = $pid
[IntPtr]$lpBaseAddress = $mem
[IntPtr]$lpBuffer
[UInt32]$nSize
[UInt32]$BytesWritten

$CallResult = [winapi]::WriteProcessMemory(
    $hProcess,
    $lpBaseAddress,
    $lpBuffer,
    $nSize,
    [ref]$BytesWritten
)
$CallResult

[IntPtr]$hProcess = $pid
[IntPtr]$lpBaseAddress  = $mem
[UInt32]$ReadSize = 4
[IntPtr]$lpBuffer = [Runtime.InteropServices.Marshal]::AllocHGlobal($ReadSize)
[UInt32]$BytesRead = 0 

$CallResult = [winapi]::ReadProcessMemory(
    $hProcess,
    $lpBaseAddress,
    $lpBuffer,
    $ReadSize,
    [ref]$BytesRead
)
$CallResult

# As I understand, here I should get "ABC"

[Runtime.InteropServices.Marshal]::FreeHGlobal($mem)
[Runtime.InteropServices.Marshal]::FreeHGlobal($lpBuffer)

我需要读取一些进程内存/写入进程内存。

如何正确做?

【问题讨论】:

  • 在底部的FreeHGlobal() 调用之前添加$destArray = [byte[]]::new($BytesRead);[System.Runtime.InteropServices.Marshal]::Copy($lpBuffer, $destArray, 0, $BytesRead) - 将存储在非托管$lpBuffer 中的读取内存复制到$destArray - 但它不会对应于$dataToWrite,因为你实际上并没有写任何东西 :)
  • “如何正确操作?” 呃,第一步是搞清楚,读/写哪个地址。那是困难的部分。你问的问题是微不足道的部分,已经被问了数百次了。

标签: .net powershell winapi


【解决方案1】:

让我们一次拿一个。

WriteProcessMemory

​​>

为了在some process中将something写入somewhere,您需要传递正确的参数:

lpBuffer

lpBuffer 参数必须指向需要写入的数据。

有几种方法可以生成指向现有对象的安全指针,我更喜欢这种方法:

$dataToWrite = "ABC"

# Encode data in byte array 
$dataArray   = [Text.Encoding]::Unicode.GetBytes($dataToWrite)

# Pin the object in memory (ensures the garbage collector doesn't move the source data around)
$pinnedArray = [Runtime.InteropServices.GCHandle]::Alloc($dataArray, [Runtime.InteropServices.GCHandleType]::Pinned)

# Now we can obtain a safe pointer to the array
[IntPtr]$lpBuffer = $pinnedArray.AddrOfPinnedObject()

但更简单的方法是首先声明 lpBufferbyte[] 并让运行时处理所有这些(无字符串编码):

[DllImport("kernel32.dll")]public static extern Boolean WriteProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    byte[] lpBuffer,
    UInt32 nSize,
    ref UInt32 lpNumberOfBytesWritten
);

hProcess

对于hProcess 参数,您需要一个进程句柄 - 直接传递拥有进程的PID 是行不通的。

对于当前进程(即正在执行的PowerShell宿主应用程序),可以使用Get-Process

# Obtain handle to current process
$hProcess = (Get-Process -Id $PID).Handle

但是对于一个不同的进程,你需要调用Kernel32!OpenProcess() - this answer 有一个很好的例子来说明如何导入和声明访问标志参数类型:

[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
    ProcessAccessFlags dwDesiredAccess, 
    [MarshalAs(UnmanagedType.Bool)]
    bool bInheritHandle,
    int dwProcessId);
[Flags]
public enum ProcessAccessFlags : uint
{
    All = 0x001F0FFF,
    Terminate = 0x00000001,
    CreateThread = 0x00000002,
    VMOperation = 0x00000008,
    VMRead = 0x00000010,
    VMWrite = 0x00000020,
    DupHandle = 0x00000040,
    SetInformation = 0x00000200,
    QueryInformation = 0x00000400,
    Synchronize = 0x00100000
}

lpBaseAddress

[Marshal]::AllocHGlobal($size) 只会在写入当前进程时帮助您。

对于不同进程中的分配,您需要将通过OpenProcess() 获得的可写进程句柄传递给Kernel32!VirtualAllocEx()

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true) ]
public extern IntPtr VirtualAllocEx(
    IntPtr hProcess,
    IntPtr lpAddress,
    UInt32 dwSize,
    AllocationType flAllocationType,
    MemoryProtection flProtect);
[Flags]
public enum AllocationType
{
     Commit = 0x1000,
     Reserve = 0x2000,
     Decommit = 0x4000,
     Release = 0x8000,
     Reset = 0x80000,
     Physical = 0x400000,
     TopDown = 0x100000,
     WriteWatch = 0x200000,
     LargePages = 0x20000000
}

[Flags]
public enum MemoryProtection
{
     Execute = 0x10,
     ExecuteRead = 0x20,
     ExecuteReadWrite = 0x40,
     ExecuteWriteCopy = 0x80,
     NoAccess = 0x01,
     ReadOnly = 0x02,
     ReadWrite = 0x04,
     WriteCopy = 0x08,
     GuardModifierflag = 0x100,
     NoCacheModifierflag = 0x200,
     WriteCombineModifierflag = 0x400
}

VirtualAllocEx 将分配的基地址写入lpAddress 指针成功。


读取进程内存

最后,要读取结果数据,您需要将lpBuffer 参数引用的数据复制到一个数组中:

$CallResult = [winapi]::ReadProcessMemory(
    $hProcess,
    $lpBaseAddress,
    $lpBuffer,
    $ReadSize,
    [ref]$BytesRead
)

if($CallResult){
  $destArray = [byte[]]::new($BytesRead)
  [System.Runtime.InteropServices.Marshal]::Copy($lpBuffer, $destArray, 0, $BytesRead)

  $stringRead = [Text.Encoding]::Unicode.GetString($destArray)
  Write-Host "Read data from process memory: $stringRead"
}

【讨论】:

    猜你喜欢
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多