Top / JUNK / ProcessMemoryManager

他プロセスのメモリに対する入出力を管理するクラス

Option Strict On
Imports System.ComponentModel
Imports System.Runtime.ConstrainedExecution
Imports System.Runtime.InteropServices
Imports System.Text

Public Class ProcessMemoryManager
    Inherits CriticalFinalizerObject
    Implements IDisposable

    Private ReadOnly lst As New List(Of ProcessMemory)

    Public Sub New(ByVal dwPorcessId As Integer)
        ProcessId = dwPorcessId
        ProcessHandle = NativeMethods.OpenProcess(dwPorcessId)
        If Environment.Is64BitOperatingSystem Then
            Dim isWow64 As Boolean
            If NativeMethods.IsWow64Process(ProcessHandle, isWow64) Then
                Is64Bit = Not isWow64
            End If
        End If
    End Sub

    Public ReadOnly Property ProcessId As Integer
    Public ReadOnly Property ProcessHandle As IntPtr
    Public ReadOnly Property Is64Bit As Boolean = False

    Public Function Add(ByVal nSize As Integer) As ProcessMemory
        Dim mem As New ProcessMemory(Me, nSize)
        SyncLock Me
            lst.Add(mem)
        End SyncLock
        Return mem
    End Function

    Public Sub Remove(ByVal item As ProcessMemory)
        SyncLock Me
            If lst.Contains(item) Then
                lst.Remove(item)
            End If
        End SyncLock
    End Sub

    Default Public ReadOnly Property Item(ByVal index As Integer) As ProcessMemory
        Get
            SyncLock Me
                Return lst(index)
            End SyncLock
        End Get
    End Property

    Private disposedValue As Boolean

    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            disposedValue = True
            Try
                Dim array As ProcessMemory()
                SyncLock Me
                    array = lst.ToArray()
                End SyncLock
                For i As Integer = 0 To array.Length - 1
                    array(i).Dispose()
                Next
            Catch
            Finally
                NativeMethods.CloseHandle(ProcessHandle)
            End Try
        End If
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
        MyBase.Finalize()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

End Class

Public Class ProcessMemory
    Inherits SafeHandle

    Private ReadOnly hProcess As IntPtr

    Protected Friend Sub New(ByVal mgr As ProcessMemoryManager, ByVal nSize As Integer)
        MyBase.New(IntPtr.Zero, True)
        Manager = mgr
        Size = nSize
        hProcess = mgr.ProcessHandle
        handle = NativeMethods.VirtualAllocEx(hProcess, nSize)
    End Sub

    Public ReadOnly Property BaseAddress As IntPtr
        Get
            Return handle
        End Get
    End Property

    Public ReadOnly Property Manager As ProcessMemoryManager
    Public ReadOnly Property Size As Integer

    Public Function Read(ByVal lpAddress As IntPtr, ByVal nSize As Integer) As Integer
        Return Read(lpAddress, nSize, 0)
    End Function

    Public Function Read(ByVal lpAddress As IntPtr, ByVal nSize As Integer, ByVal nOffset As Integer) As Integer
        Return NativeMethods.ReadProcessMemory(hProcess, handle + nOffset, lpAddress, nSize)
    End Function

    Public Function ReadString() As String
        Return ReadString(0)
    End Function

    Public Function ReadString(ByVal nOffset As Integer) As String
        Dim nSize As Integer = Size - nOffset
        Dim lpaddr As IntPtr = Marshal.AllocCoTaskMem(nSize)
        Try
            Read(lpaddr, nSize, nOffset)
            Return Marshal.PtrToStringUni(lpaddr)
        Catch
            Throw
        Finally
            Marshal.FreeCoTaskMem(lpaddr)
        End Try
    End Function

    Public Function ReadStringA() As String
        Return ReadStringA(0)
    End Function

    Public Function ReadStringA(ByVal nOffset As Integer) As String
        Dim nSize As Integer = Size - nOffset
        Dim lpaddr As IntPtr = Marshal.AllocCoTaskMem(nSize)
        Try
            Read(lpaddr, nSize, nOffset)
            Return Marshal.PtrToStringAnsi(lpaddr)
        Catch
            Throw
        Finally
            Marshal.FreeCoTaskMem(lpaddr)
        End Try
    End Function

    Public Sub Read(Of T As Class)(target As T)
        Dim typ As Type = target.GetType()
        Dim gch As GCHandle = GCHandle.Alloc(target, GCHandleType.Pinned)
        Try
            Read(gch.AddrOfPinnedObject(), Marshal.SizeOf(typ))
        Catch
            Throw
        Finally
            gch.Free()
        End Try
    End Sub

    Public Function ReadBytes() As Byte()
        Return ReadBytes(Size)
    End Function

    Public Function ReadBytes(ByVal nSize As Integer) As Byte()
        Return ReadBytes(nSize, 0)
    End Function

    Public Function ReadBytes(ByVal nSize As Integer, ByVal nOffset As Integer) As Byte()
        Dim target As Byte() = New Byte(nSize - 1) {}
        NativeMethods.ReadProcessMemory(hProcess, handle + nOffset, target)
        Return target
    End Function

    Public Function ReadClass(Of T As {Class, New})() As T
        Dim target As New T()
        Read(target)
        Return target
    End Function

    Public Function ReadStructure(Of T As Structure)() As T
        Dim nSize As Integer = Marshal.SizeOf(GetType(T))
        Dim lpaddr As IntPtr = Marshal.AllocCoTaskMem(nSize)
        Try
            Read(lpaddr, nSize)
            Return DirectCast(Marshal.PtrToStructure(lpaddr, GetType(T)), T)
        Catch
            Throw
        Finally
            Marshal.FreeCoTaskMem(lpaddr)
        End Try
    End Function

    Public Function Write(ByVal lpAddress As IntPtr, ByVal nSize As Integer) As Integer
        Return Write(lpAddress, nSize, 0)
    End Function

    Public Function Write(ByVal lpAddress As IntPtr, ByVal nSize As Integer, ByVal nOffset As Integer) As Integer
        Return NativeMethods.WriteProcessMemory(hProcess, handle + nOffset, lpAddress, nSize)
    End Function

    Public Function Write(ByVal value As Byte()) As Integer
        Return NativeMethods.WriteProcessMemory(hProcess, handle, value)
    End Function

    Public Function Write(ByVal value As Byte(), ByVal nOffset As Integer) As Integer
        Return NativeMethods.WriteProcessMemory(hProcess, handle + nOffset, value)
    End Function

    Public Function Write(ByVal value As String) As Integer
        Return NativeMethods.WriteProcessMemory(hProcess, handle, value)
    End Function

    Public Function Write(ByVal value As String, ByVal nOffset As Integer) As Integer
        Return NativeMethods.WriteProcessMemory(hProcess, handle + nOffset, value)
    End Function

    Public Function WriteA(ByVal value As String) As Integer
        Return WriteA(value, 0)
    End Function

    Public Function WriteA(ByVal value As String, ByVal nOffset As Integer) As Integer
        Dim buffer As Byte() = Encoding.GetEncoding("shift_jis").GetBytes(value)
        Return Write(buffer, nOffset)
    End Function

    Public Function Write(Of T)(ByVal target As T) As Integer
        Return Write(target, 0)
    End Function

    Public Function Write(Of T)(ByVal target As T, ByVal nOffset As Integer) As Integer
        Dim nSize As Integer = Marshal.SizeOf(target.GetType())
        Dim gch As GCHandle = GCHandle.Alloc(target, GCHandleType.Pinned)
        Try
            Return Write(gch.AddrOfPinnedObject(), nSize, nOffset)
        Catch
            Throw
        Finally
            gch.Free()
        End Try
    End Function

    Public Overrides ReadOnly Property IsInvalid As Boolean
        Get
            Return handle = IntPtr.Zero
        End Get
    End Property

    Protected Overrides Function ReleaseHandle() As Boolean
        If IsInvalid Then
            Return True
        End If
        Dim ret As Boolean = NativeMethods.VirtualFreeEx(hProcess, handle)
        SetHandle(IntPtr.Zero)
        Return ret
    End Function

    Protected Overrides Sub Dispose(disposing As Boolean)
        MyBase.Dispose(disposing)
        If disposing Then
            Manager.Remove(Me)
        End If
    End Sub

End Class

Friend Class NativeMethods

    Private Class ExternDll
        Public Const Kernel32 As String = "kernel32.dll"
    End Class

    Private Class Win32ApiException
        Inherits Win32Exception

        Private ReadOnly _Message As String

        Public Sub New(ByVal apiName As String)
            MyBase.New()
            MyBase.Source = apiName
            _Message = String.Format("{0} API が異常終了しました。({1}) {2}", MyBase.Source, MyBase.NativeErrorCode, MyBase.Message)
        End Sub

        Public Overrides ReadOnly Property Message As String
            Get
                Return _Message
            End Get
        End Property

        Public Overrides Property Source As String
            Get
                Return MyBase.Source
            End Get
            Set(value As String)

            End Set
        End Property

    End Class

    Private Const PROCESS_VM_OPERATION As Integer = &H8
    Private Const PROCESS_VM_READ As Integer = &H10
    Private Const PROCESS_VM_WRITE As Integer = &H20

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Public Shared Function IsWow64Process(ByVal hProcess As IntPtr, ByRef lpSystemInfo As Boolean) As Boolean
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function OpenProcess(
                            ByVal dwDesiredAccess As Integer,
                            ByVal bInheritHandle As Boolean,
                            ByVal dwProcessId As Integer) As IntPtr
    End Function

    Public Shared Function OpenProcess(ByVal dwProcessId As Integer) As IntPtr
        Const dwDesiredAccess As Integer = PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE
        Dim hProcess As IntPtr = OpenProcess(dwDesiredAccess, False, dwProcessId)
        If hProcess = IntPtr.Zero Then
            Throw New Win32ApiException("OpenProcess")
        End If
        Return hProcess
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Public Shared Function CloseHandle(ByVal hObject As IntPtr) As Boolean
    End Function

    Private Const MEM_RESERVE As Integer = &H2000
    Private Const MEM_RELEASE As Integer = &H8000
    Private Const MEM_COMMIT As Integer = &H1000
    Private Const PAGE_READWRITE As Integer = &H4

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function VirtualAllocEx(
                            ByVal hProcess As IntPtr,
                            ByVal lpAddress As IntPtr,
                            ByVal dwSize As IntPtr,
                            ByVal flAllocationType As Integer,
                            ByVal flProtect As Integer) As IntPtr
    End Function

    Public Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal nSize As Integer) As IntPtr
        Const flAllocationType As Integer = MEM_RESERVE Or MEM_COMMIT
        Dim dwSize As New IntPtr(nSize)
        Dim handle As IntPtr = VirtualAllocEx(hProcess, IntPtr.Zero, dwSize, flAllocationType, PAGE_READWRITE)
        If handle = IntPtr.Zero Then
            Throw New Win32ApiException("VirtualAllocEx")
        End If
        Return handle
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function VirtualFreeEx(
                            ByVal hProcess As IntPtr,
                            ByVal lpAddress As IntPtr,
                            ByVal dwSize As IntPtr,
                            ByVal dwFreeType As Integer) As Boolean
    End Function

    Public Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr) As Boolean
        Return VirtualFreeEx(hProcess, lpAddress, IntPtr.Zero, MEM_RELEASE)
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function ReadProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As IntPtr,
                            ByVal dwSize As IntPtr,
                            ByRef numberOfBytesRead As IntPtr) As Boolean
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function ReadProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As StringBuilder,
                            ByVal dwSize As IntPtr,
                            ByRef numberOfBytesRead As IntPtr) As Boolean
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function ReadProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As Byte(),
                            ByVal dwSize As IntPtr,
                            ByRef numberOfBytesRead As IntPtr) As Boolean
    End Function

    Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As IntPtr, ByVal nSize As Integer) As Integer
        Dim numberOfBytesRead As IntPtr
        Dim ret As Boolean = ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, New IntPtr(nSize), numberOfBytesRead)
        If Not ret Then
            Throw New Win32ApiException("ReadProcessMemory")
        End If
        Return numberOfBytesRead.ToInt32()
    End Function

    Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As StringBuilder, ByVal nSize As Integer) As Integer
        Dim numberOfBytesRead As IntPtr
        Dim ret As Boolean = ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, New IntPtr(nSize), numberOfBytesRead)
        If Not ret Then
            Throw New Win32ApiException("ReadProcessMemory")
        End If
        Return numberOfBytesRead.ToInt32()
    End Function

    Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As Byte()) As Integer
        Dim numberOfBytesRead As IntPtr
        Dim ret As Boolean = ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, New IntPtr(lpBuffer.Length), numberOfBytesRead)
        If Not ret Then
            Throw New Win32ApiException("ReadProcessMemory")
        End If
        Return numberOfBytesRead.ToInt32()
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function WriteProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As IntPtr,
                            ByVal dwSize As IntPtr,
                            ByRef lpNumberOfBytesWritten As IntPtr) As Boolean
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function WriteProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As StringBuilder,
                            ByVal dwSize As IntPtr,
                            ByRef lpNumberOfBytesWritten As IntPtr) As Boolean
    End Function

    <DllImport(ExternDll.Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
    Private Shared Function WriteProcessMemory(
                            ByVal hProcess As IntPtr,
                            ByVal lpBaseAddress As IntPtr,
                            ByVal lpBuffer As Byte(),
                            ByVal dwSize As IntPtr,
                            ByRef lpNumberOfBytesWritten As IntPtr) As Boolean
    End Function

    Public Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As IntPtr, ByVal nSize As Integer) As Integer
        Dim numberOfBytesWritten As IntPtr
        Dim ret As Boolean = WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, New IntPtr(nSize), numberOfBytesWritten)
        If Not ret Then
            Throw New Win32ApiException("WriteProcessMemory")
        End If
        Return numberOfBytesWritten.ToInt32()
    End Function

    Public Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal value As String) As Integer
        Dim numberOfBytesWritten As IntPtr
        Dim sb As New StringBuilder(value.Length + 1)
        sb.Append(value)
        sb.Append(vbNullChar)
        Dim ret As Boolean = WriteProcessMemory(hProcess, lpBaseAddress, sb, New IntPtr(sb.Length * 2), numberOfBytesWritten)
        If Not ret Then
            Throw New Win32ApiException("WriteProcessMemory")
        End If
        Return numberOfBytesWritten.ToInt32()
    End Function

    Public Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal value As Byte()) As Integer
        Dim numberOfBytesWritten As IntPtr
        Dim ret As Boolean = WriteProcessMemory(hProcess, lpBaseAddress, value, New IntPtr(value.Length * 2), numberOfBytesWritten)
        If Not ret Then
            Throw New Win32ApiException("WriteProcessMemory")
        End If
        Return numberOfBytesWritten.ToInt32()
    End Function

End Class

使い方

Public Structure TestStructure
    Public hoge As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Class TestClass
    Public hoge As Integer
End Class

Sub Main()
    Using manager As New ProcessMemoryManager(14532),
          pm As ProcessMemory = manager.Add(nLength)

        Dim writeArray As Byte() = New Byte(nLength - 1) {}
        For i As Integer = writeArray.GetLowerBound(0) To writeArray.GetUpperBound(0)
            writeArray(i) = i
        Next
        pm.Write(writeArray)

        Dim readArray As Byte() = pm.ReadBytes()
        For i As Integer = readArray.GetLowerBound(0) To readArray.GetUpperBound(0)
            Debug.Assert(readArray(i) = writeArray(i))
        Next

        Dim writeStr As String = "ABCDEFG"
        pm.Write(writeStr)
        Debug.Assert(pm.ReadString() = writeStr)

        Dim writeClass As New TestClass
        writeClass.hoge = 456
        pm.Write(writeClass)

        Dim readClass As TestClass = pm.ReadClass(Of TestClass)
        Debug.Assert(writeClass.hoge = readClass.hoge)

        Dim writeSt As New TestStructure
        writeSt.hoge = 123
        pm.Write(writeSt)

        Dim readSt As TestStructure = pm.ReadStructure(Of TestStructure)
        Debug.Assert(readSt.hoge = writeSt.hoge)
    End Using
End Sub



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2020-03-05 (木) 12:31:26 (31d)