C
C#14mo ago
Popzi

❔ Reading Memory comes out backwards?

I'm learning assembly / how to read and write from it and having an issue reading memory as hex values... it for some reason comes out backwards? The program I'm reading from is 64-bit
const int PROCESS_WM_READ = 0x0010;
[DllImport("kernel32.dll")]
static extern bool ReadProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

Process P = Process.GetProcessesByName(ProcessName)[0]; // Process
IntPtr PH = OpenProcess(PROCESS_WM_READ, false, P.Id); // ProcessHandle
IntPTr BA = P.MainModule.BaseAddress; // BaseAddress

int bytesRead = 0; // Keep track of how many bytes we've read
byte[] buffer = new byte[4]; // Prepare a byte array which we'll populate as we read
ReadProcessMemory((int) this.PH , 0x2445BC56E00, buffer, buffer.Length, ref bytesRead);
Console.WriteLine(BitConverter.ToString(buffer));

>>
54-B1-01-00
const int PROCESS_WM_READ = 0x0010;
[DllImport("kernel32.dll")]
static extern bool ReadProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

Process P = Process.GetProcessesByName(ProcessName)[0]; // Process
IntPtr PH = OpenProcess(PROCESS_WM_READ, false, P.Id); // ProcessHandle
IntPTr BA = P.MainModule.BaseAddress; // BaseAddress

int bytesRead = 0; // Keep track of how many bytes we've read
byte[] buffer = new byte[4]; // Prepare a byte array which we'll populate as we read
ReadProcessMemory((int) this.PH , 0x2445BC56E00, buffer, buffer.Length, ref bytesRead);
Console.WriteLine(BitConverter.ToString(buffer));

>>
54-B1-01-00
It should be 1B154, as that's what Decimel 110932 is in Hex. 110932 is the decimal value I'm trying to read from memory. Whats going on here?
35 Replies
mtreit
mtreit14mo ago
Endian-ness
mtreit
mtreit14mo ago
Endianness
In computing, endianness is the order or sequence of bytes of a word of digital data in computer memory. Endianness is primarily expressed as big-endian (BE) or little-endian (LE). A big-endian system stores the most significant byte of a word at the smallest memory address and the least significant byte at the largest. A little-endian system, ...
Popzi
Popzi14mo ago
Interesting, is the TL;DR to just reverse the buffer after reading it?
mtreit
mtreit14mo ago
You can use that Also in theory the code could run on both big endian and little endian machines so you might want to check and only reverse if it doesn't match what you want. https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.islittleendian?view=net-7.0
Popzi
Popzi14mo ago
Popzi.exe has crashed. Erm, yes. Information, thank you. I appreciate it. So like, basically, I need to do something to not have it do that Surely I'm not the first person to run into this and there's a simple answer?
mtreit
mtreit14mo ago
Simple answer to what exactly? I pointed to the method you can use to reverse the endianness if for some reason you want to treat the value as a different endianness from the system you are currently running on.
Popzi
Popzi14mo ago
Right, thank you! I've just gotta figure out how to use this method 🙂
mtreit
mtreit14mo ago
There are also a lot of helper methods that you can use if you are trying to read a buffer directly into a numeric variable: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives?view=net-7.0
BinaryPrimitives Class (System.Buffers.Binary)
Reads bytes as primitives with specific endianness.
MODiX
MODiX14mo ago
mtreit#6470
REPL Result: Success
using System.Buffers.Binary;

var value = 110932;
var buffer = new byte[4];
BinaryPrimitives.WriteInt32BigEndian(buffer, value);
var bigendian = Convert.ToHexString(buffer);
Console.WriteLine(bigendian);

BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
var littleendian = Convert.ToHexString(buffer);
Console.WriteLine(littleendian);
using System.Buffers.Binary;

var value = 110932;
var buffer = new byte[4];
BinaryPrimitives.WriteInt32BigEndian(buffer, value);
var bigendian = Convert.ToHexString(buffer);
Console.WriteLine(bigendian);

BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
var littleendian = Convert.ToHexString(buffer);
Console.WriteLine(littleendian);
Console Output
0001B154
54B10100
0001B154
54B10100
Compile: 619.415ms | Execution: 67.695ms | React with ❌ to remove this embed.
Popzi
Popzi14mo ago
Well there we go, I was just about to ask how to actually use it, granted the MS docs are.......... the most helpful unhelpful documentation to put it nicely 🙂
mtreit
mtreit14mo ago
If you are starting with a buffer you would use the inverse methods: ReadInt32BigEndian for instance.
MODiX
MODiX14mo ago
mtreit#6470
REPL Result: Success
using System.Buffers.Binary;

var buffer = new byte[4] { 0x54, 0xB1, 0x01, 0x00 };

var bigEndian = BinaryPrimitives.ReadInt32BigEndian(buffer);
var littleEndian = BinaryPrimitives.ReadInt32LittleEndian(buffer);

Console.WriteLine($"BE: {bigEndian} ({bigEndian.ToString("X")})");
Console.WriteLine($"LE: {littleEndian} ({littleEndian.ToString("X")})");
using System.Buffers.Binary;

var buffer = new byte[4] { 0x54, 0xB1, 0x01, 0x00 };

var bigEndian = BinaryPrimitives.ReadInt32BigEndian(buffer);
var littleEndian = BinaryPrimitives.ReadInt32LittleEndian(buffer);

Console.WriteLine($"BE: {bigEndian} ({bigEndian.ToString("X")})");
Console.WriteLine($"LE: {littleEndian} ({littleEndian.ToString("X")})");
Console Output
BE: 1420886272 (54B10100)
LE: 110932 (1B154)
BE: 1420886272 (54B10100)
LE: 110932 (1B154)
Compile: 711.954ms | Execution: 121.206ms | React with ❌ to remove this embed.
Popzi
Popzi14mo ago
Blah, this is messy lol ,Int64 Int32, Int, UsInt64, IntPtr which are all the same but totally not the same things and now big and small edians that go back to front Does C# know how to read memory? I just wanna say read this address here and be given the hex value of the address 😐 I wonder if I'm better off doing this in C++, which is something I'd have never thought I'd say
mtreit
mtreit14mo ago
Memory is memory, the issues you are having with Endianness won't change if you switch how you read that memory.
Popzi
Popzi14mo ago
I see, am just learning more now... Big Endian is already superior to everything else since it makes the most sense xd... Down the rabbit hole I go
mtreit
mtreit14mo ago
Aa an aside, the original note that coined the term endian for how bytes are ordered is kind of a fun read: https://www.rfc-editor.org/ien/ien137.txt
DKMK100
DKMK10014mo ago
I think you're misunderstanding the problem The problem isn't really related to the coding language Both C# and c++ can only abstract this away by the type system which prevents reading bytes on their own. If you're gonna read plain bytes as useful info, you need to know what info they represent Including stuff like endianness No coding language can fix that except by doing what c# is ALREADY doing: remember that for you via type system You're the one choosing to interact with the bytes without it, and this problem is a natural concequence of that. Someone even showed you a helpful function to remove the busywork of choosing to interpret these yourself, so you only have the actual problem left to deal with
Popzi
Popzi14mo ago
👍 Understood and yeah, I was just getting a little overwhelmed, went from the basics of reading memory to a PHD in physical memory and their real-world quirks very fast lol - I understand why it is the way it is now and am continuing to understand it 🙂 and thank you both for your help also! I appreciate it
DKMK100
DKMK10014mo ago
Hope you manage to get it working! Good luck!
Popzi
Popzi14mo ago
Ty! 🙂 So I managed to get it to read memory and spit out Hex in the right Endian, which is fantastic 😄 I'm just now looking at Writing back to memory and wonder if there's any Endians to worry about, or if C# takes care of that for us?
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);

public void Write(Int64 Address, byte[] HexDataBytes)
{
int bytesWritten = 0;
WriteProcessMemory((int) this.PH, Address, HexDataBytes, HexDataBytes.Length, ref bytesWritten);
Console.WriteLine("Wrote to memory. Bytes written: " + bytesWritten + " Buffer Length: " + HexDataBytes.Length);
}

>> Write(0x2445BC56E00, new byte[] { 0x00, 0x01, 0xB1, 0x56 });
Wrote to memory. Bytes written: 0 Buffer Length: 4
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);

public void Write(Int64 Address, byte[] HexDataBytes)
{
int bytesWritten = 0;
WriteProcessMemory((int) this.PH, Address, HexDataBytes, HexDataBytes.Length, ref bytesWritten);
Console.WriteLine("Wrote to memory. Bytes written: " + bytesWritten + " Buffer Length: " + HexDataBytes.Length);
}

>> Write(0x2445BC56E00, new byte[] { 0x00, 0x01, 0xB1, 0x56 });
Wrote to memory. Bytes written: 0 Buffer Length: 4
The hex I'm trying to write, is just what was read + 2, so 1B154 + 2 = 1B156
mtreit
mtreit14mo ago
Bytes are written in exactly the order you pass them in as. There is no automatic conversion from one order to another when you are dealing with actual byte arrays / buffers.
Popzi
Popzi14mo ago
Perfect, understood - So my error must lie somewhere within my handling of WriteProcessMemory it seems 🤔 granted it's not writing the buffer
mtreit
mtreit14mo ago
Is WriteProcessMemory returning false?
Popzi
Popzi14mo ago
Yes
mtreit
mtreit14mo ago
If so you need to call GetLastError and see what error is happening... Writing directly to process memory is...the kind of thing I would not expect to work in many cases.
i love cat(mull-rom splines)
invalid access to memory location time
Popzi
Popzi14mo ago
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);
Console.Write(Marshal.GetLastWin32Error());
>>
0
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(int hProcess, Int64 lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesWritten);
Console.Write(Marshal.GetLastWin32Error());
>>
0
Which is success according to MS docs, so I'm assuming I've done it wrong
[DllImport("kernel32.dll")]
static extern uint GetLastError();
...
Console.Write(GetLastError());
[DllImport("kernel32.dll")]
static extern uint GetLastError();
...
Console.Write(GetLastError());
also throwing 0 🤔
i love cat(mull-rom splines)
check your openprocess flags
Popzi
Popzi14mo ago
Ah, it has to be called literally right after the WriteProcessMemory call - Now showing error 5 🙂 Let's see! Access is denied. 😎 being ran as admin
i love cat(mull-rom splines)
all the subsequent functions that call setlasterror will clear all the previous error codes so yes, getlasterror will only return the last function's results
mtreit
mtreit14mo ago
Being admin doesn't matter if you opened the process handle without the necessary flags that allow writing to memory
Popzi
Popzi14mo ago
const int PROCESS_WM_READ = 0x0010;
this.PH = OpenProcess(PROCESS_WM_READ, false, P.Id);
const int PROCESS_WM_READ = 0x0010;
this.PH = OpenProcess(PROCESS_WM_READ, false, P.Id);
Aha, so that's what this does 😄 Ty! Gave it full access (0x1F0FFF) - We're writing bytes! Awesome!!
i love cat(mull-rom splines)
you should really be using a source gen wrapper cswin32 is fine
Accord
Accord14mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.