03-26-2012, 10:13 PM
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
Module1.vb
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:
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 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.
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:
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 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.