I would like to use Span and stackalloc to allocate an array of struct and pass it to an interop call. Is it possible to retrieve a pointer (IntPtr) from the Span without being unsafe ?
Here is how i did it without unsafe:
i just changed lpBuffer to ref byte instead of byte[] (for c++ user COULD represent it as uint8_t*)
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(nint hProcess, nuint lpBaseAddress, ref byte lpBuffer, int dwSize, out int lpNumberOfBytesRead);
Span<byte> bytes = stackalloc byte[8];
// Get reference to first byte in the span (for c++ user we COULD represent it as `uint8_t*`)
ref byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `ref MemoryMarshal.AsRef<byte>(bytes)` to the function directly
bool success = Win32.ReadProcessMemory(_processHandle, address, ref bytesReference, cSize, out int numberOfBytesRead);
lpBuffer could be in byte instead of ref byte as that will allow you to use ReadOnlySpan
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(nint hProcess, nuint lpBaseAddress, in byte lpBuffer, int dwSize, out int lpNumberOfBytesWritten);
ReadOnlySpan<byte> bytes = stackalloc byte[8];
ref readonly byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `in MemoryMarshal.AsRef<byte>(bytes)` to the function directly
Win32.WriteProcessMemory(_processHandle, address, in bytesReference, bytes.Length, out int numberOfBytesWritten);
Notes:
[+] You could use GetPinnableReference instead of MemoryMarshal.AsRef but it is not intended to be called by user code.
[+] ReadProcessMemory Windows API function.
[+] ReadProcessMemory(kernel32) pinvoke Original implementation.
Related
Deleted my old post and decided to reupload with more direct questions.
I'm trying to write bytes to a memory address for my C# menu, writing singular a singular byte or int isn't an issue. I have an issue trying to write multiple bytes.
The code below is writing a singular byte to increase the player speed to 555, which works perfectly fine.
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
byte[] memory = { 555 };
WriteProcessMemory(Game.hProc, Player.PlayerSpeedOffSet, memory, memory.Length, out _);
The part I'm having issues with is when I try and write multiple bytes to an address.
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
byte[] memory = { 90, 90, 90 };
WriteProcessMemory(Game.hProc, Player.UnlimitedAmmoOffSet, memory, memory.Length, out _);
"90 90 90" being unlimted ammo and to disable it, i'd write to the same address with "89 50 04".
This worked just fine with memory.dll so the handle and offset is just fine, it's just something to do with my writing to memory.
Look at the definition of the function WriteProcessMemory:
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
Note the type of the nSize parameter, it's SIZE_T. How wide is this type? You can look it up here: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
typedef ULONG_PTR SIZE_T;
Okay, then what is the width of ULONG_PTR? The same documentation tells:
#if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned long ULONG_PTR;
#endif
So, its either 32 bits or 64 bits wide, depending on whether the process calling this function is a 32-bit or 64-bit process. (In Windows, unsigned long is 32-bit wide.)
Now look at your P/Invoke definition:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
You have defined nSize as a 32-bit integer. If your program is compiled to run as a 32-bit process, you got lucky and the 32-bit integer matches the 32-bit SIZE_T.
But if your program is running as a 64-bit process, your 32-bit integer does not match the 64-bit SIZE_T.
I hope using IntPtr instead of int for the nSize parameter should fix your issue.
I read card SD like file. I use this method:
public static int ReadUSBDisk(ref byte[] buffer, int sector, string fileName)
{
ushort count = 10;
bool retValue;
int address = 512 * sector;
if (!IsUSBOpen())
hUSBDisk = OpenUSBDisk(fileName);
if ((int)hUSBDisk == -1)
return NISH_ERROR;
SetFilePointer(hUSBDisk, address
, 0,
EMoveMethod.Begin);
retValue = ReadFile(hUSBDisk, buffer, 512, ref count, IntPtr.Zero);
if (retValue)
return NISH_NO_ERROR;
else
return NISH_ERROR;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(IntPtr handle,
byte[] buffer, ushort toRead, ref ushort read, IntPtr lpOverLapped);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetFilePointer(
IntPtr hFile,
int lDistanceToMove,
int lpDistanceToMoveHigh,
EMoveMethod dwMoveMethod);
But I need to read from more space than the int. But change lDistanceToMove to long couses throw error:
A call to PInvoke '(className) :: SetFilePointer' has upset the
balance of the stack. Probable cause is a mismatch between the managed
PInvoke signature and the unmanaged target signature. Check that the
calling convention and signature parameters of the PInvoke function
match the unmanaged target signature.
Honestly, I don't know exactly how it works. Maybe someone can tell me if I can change it to read from long space in some way.
Given a method with this signature in a DLL
int32_t __stdcall ndUDSReadDataByIdentifier(
TD1 *diagRef,
uint16_t ID,
uint8_t dataOut[],
int32_t *len,
int8_t *success);
How does the C# external call look like?
I tried:
[DllImport("nidiagcs.dll")]
public static extern Int32 ndUDSReadDataByIdentifier(
ref TD1 diagRef,
[MarshalAs(UnmanagedType.U2)] UInt16 ID,
byte[] dataOut,
[MarshalAs(UnmanagedType.U4)] ref Int32 len,
[MarshalAs(UnmanagedType.U1)] ref byte success);
The call is executed but the dataOut is not filled.
Okay I found the solution.
[DllImport("nidiagcs.dll", CallingConvention = CallingConvention.StdCall)]
private static extern Int32 ndUDSReadDataByIdentifier(
ref TD1 diagRef,
UInt16 ID,
Byte[] dataOut,
ref Int32 len,
out byte success);
This is the correct way to call the function. It was a mistake from my side. One needs to supply a buffer array in dataOut and the according size of the buffer in len. I always set len to 0 which makes the library think the dataOut array has a size of zero so nothing is returned.
Thanks for everyones help!
My program need control a hardware. Vendor provide a DLL which is design for C/C++ language.
Most of functions passed my test beside below function:
int32 ni845xSpiWriteRead(NiHandle DeviceHandle,
NiHandle ConfigurationHandle,
uInt32 WriteSize,
uInt8 * pWriteData,
uInt32 * pReadSize,
uInt8 * pReadData);
Document of this function is here:
Inputs NiHandle DeviceHandle
Device handle returned from ni845xOpen.
NiHandle ConfigurationHandle
The configuration handle returned from ni845xSpiConfigurationOpen.
uInt32 WriteSize
The number of bytes to write. This must be nonzero.
uInt8 * pWriteData
The data bytes to be written.
Outputs uInt32 * pReadSize
A pointer to the amount of bytes read.
uInt8 * pReadData
A pointer to an array of bytes where the bytes that have been read
are stored.
My code in C# is here:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
IntPtr pWriteData, //In
IntPtr pReadSize, //Out
IntPtr pReadData //Out
);
I always got AccessViolationException exception. I am guessing that is caused by the pointer input/output parameter.
The code of invoke Swrapper.ni845xSpiWriteRead() i here:
public void WriteData(int length, int[] writeArray)
{
byte[] writeDate = new byte[8];
int writeSize = writeDate.Length;
try
{
//Define pointers
IntPtr writeDataPointer = Marshal.AllocHGlobal(writeDate.Length);
IntPtr readDataSizePointer = Marshal.AllocHGlobal(writeDate.Length);
IntPtr readDataPointer = Marshal.AllocHGlobal(writeDate.Length);
//Copy value to write data pointer
Marshal.Copy(writeDate, 0, writeDataPointer, writeDate.Length);
int state = Ni845xNativeMethods.ni845xSpiWriteRead(_niHandle, _niConfigrationHandle, writeSize, writeDataPointer,readDataSizePointer,readDataPointer);
this.CheckStatus(state);
}
catch (Exception)
{
throw;
}
}
Most likely the p/invoke should be:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
uint WriteSize,
[In] byte[] WriteData,
out uint ReadSize,
[Out] byte[] ReadData
);
You'll need to allocate a byte[] of sufficient length to use as the ReadData parameter. Presumably you know how to do this.
I've taken at face value the statement that ReadSize is an output parameter. However, if it is both in and out, then declare it as ref.
The most likely thing is it's just the Out size parameter. So:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
IntPtr pWriteData, //In
out uint ReadSize, //Out
IntPtr pReadData //Out
);
This is similar to #Marius, but the handles should be fine as integers. The trick is to get the size returned as an integer, so either 'out uint' or 'ref uint' to do that.
Much better if we could see the calling code.
Having seen the code, you could also try something like this.
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
[MarshalAs(UnmanagedType.LPArray)] long [] writeData, //In
out uint ReadSize, //Out
[MarshalAs(UnmanagedType.LPArray), Out] readData //Out
);
This avoids all that Marshal.Copy stuff. Make sure the readData buffer is big enough.
I think both of the existing answers are somewhat incorrect, so I will add another (probably also incorrect) answer.
If DeviceHandle and ConfigurationHandle are truly HANDLE types (or other pointer types) then you should use IntPtr as the type for P/Invoke.
Given the C function declaration, the ReadData buffer must be allocated by the caller. Also, it is likely that ReadSize must be initialized to the size of the buffer and will be set to the actual number of bytes read during the function call.
I think this may come close to working:
class Program
{
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
UInt32 WriteSize,
[MarshalAs(UnmanagedType.LPArray)][In] Byte[] pWriteData,
ref UInt32 ReadSize,
[MarshalAs(UnmanagedType.LPArray)][Out] Byte[] pReadData);
static void Main(string[] args)
{
IntPtr DeviceHandle = (IntPtr)123;
IntPtr ConfigurationHandle = (IntPtr)456;
Byte[] WriteData = new Byte[2324];
Byte[] ReadData = new Byte[8274];
UInt32 ReadSize = (UInt32)ReadData.Length;
int result = ni845xSpiWriteRead(DeviceHandle,
ConfigurationHandle,
(UInt32) WriteData.Length,
WriteData,
ref ReadSize,
ReadData);
}
}
Change your calling convention from Cdecl to stdCall and it will work now.
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
I'm trying to make a memory scanner in C#. I heard that I need the API functions WriteProcessMemory and ReadProcessMemory.
I called them :
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
UInt32 nSize,
ref UInt32 lpNumberOfBytesRead
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
uint nSize,
out UIntPtr lpNumberOfBytesWritten);
All the examples for memory scanners I found on Google were very hard to understand. I've read a lot of articles saying that to find value addr you need to search at any memory byte. Now I need to get the program to open the process for reading and writing from memory.
Can I do that with GetProcessByName?
Yes, like so:
Process[] process = Process.GetProcessesByName("cmd");
byte[] memory = new byte[255];
uint bytesRead =0;
bool succes = ReadProcessMemory(
process[0].Handle,
process[0].MainModule.BaseAddress ,
memory ,
(uint) memory.Length ,
ref bytesRead);
You have to check if success is true. If not you have read nothing.