Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Project] Write To Another Process' Memory Block
#1
So I created a quick little project to show how you can read and write to another process' memory block/address.

It was a project I was testing out because this is similar how game trainers work, and I was curious on figuring out on my own. We've got several API's in use here and being called on, the older methods use Marshal instead from what I know.

Here's my full project source:
Form1.vb
Code:
Option Strict On
Imports System.Text
Imports System.Runtime.InteropServices

Public Class Form1

    Sub New()
        InitializeComponent()
        TextBox1.Text = "1080"
        TextBox2.Text = "my string value"
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        If Not NotepadRunning() Then MessageBox.Show("You need to make sure that notepad.exe is open for this to work", "Notepad Not Running", _
            MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
    End Sub

    'WRITE TO MEMORY
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim Hnd As IntPtr = CheckProcess(AccessFlags.PROCESS_ALL)
        If Hnd <> IntPtr.Zero Then
            If BadValues() Then Exit Sub
            Dim MyProps As New MyProperties

            Dim WriteValues As String
            With MyProps
                .intProperty = CInt(TextBox1.Text)
                .strProperty = TextBox2.Text
                .dateProperty = CDate(DateTimePicker1.Value)
                WriteValues = String.Format("{0}`{1}`{2}", .intProperty.ToString, .strProperty, .dateProperty.ToString)
            End With

            Dim vBuffer As Long = Nothing
            DataAddr = &H100B90

            For i As Integer = 0 To WriteValues.Length - 1
                WriteProcessMemory(Hnd, DataAddr + i, Asc(WriteValues.Substring(i, 1)), 1, 0)
            Next
            TextLen = WriteValues.Length
            CloseHandle(Hnd)
        Else
            MessageBox.Show("Could not write to memory", "Write Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End If
    End Sub

    'READ FROM MEMORY
    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim Hnd As IntPtr = CheckProcess(AccessFlags.PROCESS_VMREAD)
        If Hnd <> IntPtr.Zero Then
            Dim vBuffer As Long, ByteData As String = String.Empty

            For i = 0 To TextLen - 1
                ReadProcessMemory(Hnd, DataAddr + i, vBuffer, 1, 0)
                ByteData &= ChrW(CInt(vBuffer))
            Next

            TextBox3.Clear()
            TextBox3.Text = String.Join(Environment.NewLine, ByteData.Split("`"c))
            CloseHandle(Hnd)
        Else
            MessageBox.Show("Could not read from memory", "Read Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End If
    End Sub

End Class

Module1.vb
Code:
Option Strict On

Imports System.Diagnostics
Imports System.Runtime.InteropServices

Module Module1
    Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As AccessFlags, _
                                                                ByVal bInheritHandle As Integer, _
                                                                ByVal dwProcessId As Integer) As IntPtr

    Public Enum AccessFlags As UInteger
        PROCESS_ALL = &H1F0FFF
        PROCESS_TERMINATE = &H1
        PROCESS_CREATETHREAD = &H2
        PROCESS_VMOPERATION = &H8
        PROCESS_VMREAD = &H10
        PROCESS_VMWRITE = &H20
        PROCESS_DUPHANDLE = &H40
        PROCESS_SETINFORMATION = &H200
        PROCESS_QUERYINFORMATION = &H400
        PROCESS_SYNCHRONIZE = &H100000
    End Enum

    Public Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As IntPtr, _
                                                                ByVal lpBaseAddress As Integer, _
                                                                ByRef lpBuffer As Integer, _
                                                                ByVal nSize As UInt32, _
                                                                ByRef lpNumberOfBytesWritten As Integer) As Boolean

    Public Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As IntPtr, _
                                                                ByVal lpBaseAddress As Integer, _
                                                                ByRef lpBuffer As Long, _
                                                                ByVal nSize As Integer, _
                                                                ByRef lpNumberOfBytesWritten As Integer) As Boolean

    Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As Boolean

    Public Declare Function VirtualQueryEx Lib "kernel32" Alias "VirtualQueryEx" (ByVal hProcess As IntPtr, _
                                                                                  ByVal lpAddress As IntPtr, _
                                                                                  ByVal lpBuffer As MEMORY_BASIC_INFORMATION, _
                                                                                  ByVal dwLength As Long) As Long

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure MEMORY_BASIC_INFORMATION
        Public BaseAddress As IntPtr
        Public AllocationBase As IntPtr
        Public AllocationProtect As UInteger
        Public RegionSize As IntPtr
        Public State As UInteger
        Public Protect As UInteger
        Public Type As UInteger
    End Structure

    Public DataAddr As Integer
    Public TextLen As Integer

    Public Function NotepadRunning() As Boolean
        If Process.GetProcessesByName("notepad").Length = 0 Then
            MessageBox.Show("Notepad is currently not running...", "Not Running", _
                            MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
            Return False
        End If
        Return True
    End Function

    Public Function CheckProcess(AccessFlag As AccessFlags) As IntPtr
        If Not NotepadRunning() Then Return IntPtr.Zero

        Static NotepadPID As Integer, NotepadHandle As IntPtr
        If NotepadPID = 0 Then NotepadPID = Process.GetProcessesByName("notepad")(0).Id
        NotepadHandle = OpenProcess(AccessFlag, 0, NotepadPID)

        If NotepadHandle = IntPtr.Zero Then
            MessageBox.Show("Failed to open notepad process, handle initialized to Zero.", "Load Failed", _
                            MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
            Return IntPtr.Zero
        End If
        Return NotepadHandle
    End Function

    Public Function BadValues() As Boolean
        For Each Ctrl As Control In Form1.Controls
            If TryCast(Ctrl, TextBox) IsNot Nothing Then
                If String.IsNullOrEmpty(Ctrl.Text) And Ctrl.Name <> "TextBox3" Then
                    MessageBox.Show("You have an input field that is null, please make sure that you fill in all values", "Null Input", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
                    Return True
                End If
            End If
        Next
        If Not IsNumeric(Form1.TextBox1.Text) Then
            MessageBox.Show("The integer input text field must have a valid integral input, please make sure that the input is valid", "Invalid Input", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
            Return True
        End If
        Return False
    End Function
End Module

Public Class MyProperties
    Public Property strProperty() As String
    Public Property intProperty() As Integer
    Public Property dateProperty() As Date
End Class

I make sure to use the proper Process Access Flags for the Write and Read methods, I found out for me that setting it to ALL flags was the only one that worked for writing to the memory data address, but the READ enumeration flag worked and was all I needed for reading back from that data address.

Here's a bit of a preview:
[Image: 8WdSU.png]

This is an address in memory (a particular memory block) for the notepad.exe process modified.

I write the values in all as byte values but by parsing each data that we're writing into a string equivalent before casting it over to a byte value to write to a data address in the loop starting from the first address in the particular memory block we're writing to.

Edit: Wow, I never noticed how large that block size was while I was writing this out lol. I'm using a program/tool called HeapMemView there, it's a nice tool to have.

DISCLAIMER: You can damage and/or BSOD your computer by attempting this if you don't know what you're doing, so be forewarned.

Note: One last thing I forgot to mention and I believe it's why i'm debugging in a x64 bit debug process for my application, but you can't read/write to a x64 bit module/process if your running from a x86 (32 bit) process. I had those issues when creating my Process Detective application, which is the reasoning behind creating both a 32 bit and 64 bit version for my output.

This doesn't show a devised way of finding a memory block allocated to that process dynamically. That's a bit of a different story, but it does show interesting phenomena of how you can read back data in ASCII values from a string of bytes in memory for another process, and I found it interesting enough to share with you guys...

You'd need to get a hold of where the heap memory base address is for the process and find a memory block being used as reserved memory and to which you know you can write to for the data to stay there and not get overwritten if you plan on sharing memory, otherwise it's fairly easy to write to a data address. Writing to a guessed data address though can be risky if you aren't sure you're writing to the proper location that you wanted to write to... And the hard thing about that is that you would need to use a constant reference point in memory to ensure that you've found the correct address. As the base address and memory block sizes change every time you open the process.

Finding the biggest memory block though is what I wanted to test out next and read back where the base address for it is, then loop through each byte to find a string of bytes not containing any data at the moment.

The way i've written this though seems very efficient to me, I check for the process and return a process handle by reference through my function if the check is validated. So really it's taking full use of a function being called Smile We also make sure that the notepad we're opening and closing is the same notepad process and not some other open notepad process by keeping the same PID that we call by the function.

If anyone is actually interested in this, just make a new thread or ask your questions here and I can explain things for you.
Reply
#2
No more coders here... So sad lol. I know i'll get little replies on some of the threads I make here, but it's worth to share anyways...
Reply
#3
Thanks for sharing your full project.
I hope it can help in future if I start coding a Game Trainer. Tongue
If you are willing to join SF Webmasters.
Reply
#4
It's a start, you'd need to know what to look for in memory though and how to find it which is unfortunately where this thread i've posted cuts it short lol. That's a whole new story as well, and I would hope whoever uses this project to assist them in writing to memory would make that part bulletproof as there is potential to damage your system if you do something wrong. Best to read back where you're writing to, and not actually write the data to that address until you're sure it's the proper location. That's what I would do, comment out the WriteProcessMemory and see what your data address is if you're finding a spot in memory dynamically.

I still need to do that... But I have my eye on a new API which should help me read regions in memory.
Reply
#5
Thanks for the share, but this is still a little bit to advanced for me.
Reply
#6
(03-31-2012, 03:24 AM)Modestep Wrote: Thanks for the share, but this is still a little bit to advanced for me.

You could learn Unsure

Anything you don't understand, then just ask, that's what I posted this thread for Smile

(Along with showing you guys something interesting)
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Source] List Process'/Kill Process ♱ RedTube ♱ 3 1,499 11-25-2012, 10:39 PM
Last Post: ƃu∀ ıʞƃu∀
  [VB.Net] Local/Remote TCP info from process [Source] The-One 5 3,819 08-11-2012, 05:18 PM
Last Post: Kenneth
  Process Detective (32 & 64 bit binaries) AceInfinity 3 1,489 03-23-2012, 12:09 PM
Last Post: AceInfinity
  Kill A Process [C#] wchar_t 15 5,004 11-16-2011, 06:27 AM
Last Post: TalishHF
  [Vb.net] Project Dancing [Source] [Have fun] [Lollll project] ★ASI_F★™ 0 1,276 06-19-2011, 07:42 PM
Last Post: ★ASI_F★™

Forum Jump:


Users browsing this thread: 1 Guest(s)