Is there a way in C# to convert a plain byte array to an object?
e.g given this class:
class Data
{
public int _int1;
public int _int2;
public short _short1;
public long _long1;
}
I want to basically be able to do something like this:
var bytes = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0 };
var obj = (Data)bytes;
You could try marshalling:
Declare the layout of your class as Sequential (and note that you will need to use Pack = 1):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
public int _int1;
public int _int2;
public short _short1;
public long _long1;
}
Marshal the bytes into a new instance of the Data class:
var bytes = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0 };
GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
gcHandle.Free();
// Now data should contain the correct values.
Console.WriteLine(data._int1); // Prints 1
Console.WriteLine(data._int2); // Prints 2
Console.WriteLine(data._short1); // Prints 3
Console.WriteLine(data._long1); // Prints 4
For convenience you could write a static method on Data to do the conversion:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
public int _int1;
public int _int2;
public short _short1;
public long _long1;
public static Data FromBytes(byte[] bytes)
{
GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
gcHandle.Free();
return data;
}
}
...
var data = Data.FromBytes(new byte[] {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0});
If you really wanted to you could write an explicit operator to convert from an array of bytes, to get the syntax in your OP. I would suggest just using Data.FromBytes() which is going to be a lot clearer IMO.
Still, just for completeness:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
public int _int1;
public int _int2;
public short _short1;
public long _long1;
public static explicit operator Data(byte[] bytes)
{
GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
gcHandle.Free();
return data;
}
}
...
var data = (Data)new byte[] {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0};
Use BitConverter.ToInt32/Int16/Int64 methods. You have only have to specify the starting index like:
Data data = new Data();
data._int1 = BitConverter.ToInt32(bytes, 0);
data._int2 = BitConverter.ToInt32(bytes, 4);
data._short1 = BitConverter.ToInt16(bytes, 8);
data._long1 = BitConverter.ToInt64(bytes,10);
Just remember:
BitConverter.ToInt32
The order of bytes in the array must reflect the endianness of the
computer system's architecture;
Here is a way to convert a byte array into an object.
var binaryFormatter = new BinaryFormatter();
using (var ms = new MemoryStream(bytes))
{
object obj = binaryFormatter.Deserialize(ms);
return (Data)obj;
}
There is nothing that will do the conversion in one go.
But you can build on top of BitConverter:
var d = new Data();
var sI32 = sizeof(Int32);
d._int1 = BitConverter.ToInt32(bytes, 0);
d._int2 = BitConverter.ToInt32(bytes, sI32);
d._short1 = BitConverter.ToInt16(bytes, 2*sI32);
…
Related
I'm trying to build a packet structured as:
HEADER OP
Where HEADER size is 4 bytes, and contains total packet length.
OP size is 2 bytes, and contains an integer.
For example, i'm trying to send as OP "3".
So packet should become {2, 0, 0, 0, 3, 0}
Where 2, 0, 0, 0 is header and 3, 0 is OP (padded to the left)
You'll need the following two methods to perform conversions to and from byte arrays. I am assuming this is all Little Endian.
/// <summary>
/// Converts the supplied object to a byte array.
/// </summary>
public static byte[] ToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
/// <summary>
/// Maps the supplied byte array onto a structure of the specified type.
/// </summary>
public static T ToStructure<T>(byte[] byteArray)
{
GCHandle h = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
T result = (T)Marshal.PtrToStructure(h.AddrOfPinnedObject(), typeof(T));
h.Free();
return result;
}
Now you just need a structure that will hold your data. It will look something like this:
[StructLayout(LayoutKind.Explicit)]
public struct PacketHeader
{
public UInt32 HEADER;
public UInt16 OP;
}
And then all you have to do is populate an instance of PacketHeader with data, and convert it to a byte array like this:
var packetHeader = new PacketHeader
{
HEADER=2,
OP=3
};
var bytes = ToByteArray(packetHeader);
To reverse the process:
var packetHeader p FromByteArray<PacketHeader>(bytes);
I collect some large log infos using a C# tool. Therefore I searched for a way to compress that giant string and I found this snippet to do the trick:
public static string CompressString(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
var memoryStream = new MemoryStream();
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
{
gZipStream.Write(buffer, 0, buffer.Length);
}
memoryStream.Position = 0;
var compressedData = new byte[memoryStream.Length];
memoryStream.Read(compressedData, 0, compressedData.Length);
var gZipBuffer = new byte[compressedData.Length + 4];
Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length);
Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4);
return Convert.ToBase64String(gZipBuffer);
}
After my logging action the C# tool sends this compressed String to a node.js REST interface which writes it into a database.
Now (in my naive understanding of compression) I thought that I could simply use something like the follwoing code on nodejs side to uncompress it:
zlib.gunzip(Buffer.from(compressedLogMessage, 'base64'), function(err, uncompressedLogMessage) {
if(err) {
console.error(err);
}
else {
console.log(uncompressedLogMessage.toString('utf-8'));
}
});
But I get the error:
{ Error: incorrect header check
at Zlib._handle.onerror (zlib.js:370:17) errno: -3, code: 'Z_DATA_ERROR' }
It seems that the compression method does not match with the uncompression function. I expect that anyone with compression/uncompression knowledge could maybe see the issue(s) immediately.
What could I change or improve to make the uncompression work?
Thanks a lot!
========== UPDATE ===========
It seems that message receiving and base64 decoding works..
Using CompressString("Hello World") results in:
// before compression
"Hello World"
// after compression before base64 encoding
new byte[] { 11, 0, 0, 0, 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 243, 72, 205, 201, 201, 87, 8, 207, 47, 202, 73, 1, 0, 86, 177, 23, 74, 11, 0, 0, 0 }
// after base64 encoding
CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=
And on node js side:
// after var buf = Buffer.from('CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=', 'base64');
{"buf":{"type":"Buffer","data":[11,0,0,0,31,139,8,0,0,0,0,0,0,3,243,72,205,201,201,87,8,207,47,202,73,1,0,86,177,23,74,11,0,0,0]}}
// after zlib.gunzip(buf, function(err, dezipped) { ... }
{ Error: incorrect header check
at Zlib._handle.onerror (zlib.js:370:17) errno: -3, code: 'Z_DATA_ERROR' }
=============== Update 2 ==================
#01binary's answer was correct! That's the working solution:
function toArrayBuffer(buffer) {
var arrayBuffer = new ArrayBuffer(buffer.length);
var view = new Uint8Array(arrayBuffer);
for (var i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return arrayBuffer;
}
// Hello World (compressed with C#) => CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=
var arrayBuffer = toArrayBuffer(Buffer.from('CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=', 'base64'))
var zlib = require('zlib');
zlib.gunzip(Buffer.from(arrayBuffer, 4), function(err, uncompressedMessage) {
if(err) {
console.log(err)
}
else {
console.log(uncompressedMessage.toString()) // Hello World
}
});
The snippet you found appears to write 4 extra bytes to the beginning of the output stream, containing the "uncompressed" size of the original data. The original author must have assumed that logic on the receiving end is going to read those 4 bytes, know that it needs to allocate a buffer of that size, and pass the rest of the stream (at +4 offset) to gunzip.
If you are using this signature on the Node side:
https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
...then pass a byte offset of 4. The first two bytes of your gzip stream should be { 0x1F, 0x8b }, and you can see in your array that those two bytes start at offset 4. A simple example of the zlib header can be found here:
Zlib compression incompatibile C vs C# implementations
The PInkove part was taken from some SO answer (sorry, I've lost the link to original).
Below is the complete program. The output is false.
using System;
using System.Runtime.InteropServices;
namespace Memcpy
{
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(Array b1, Array b2, long count);
public static bool CompareArrays(Array b1, Array b2)
{
// Validate buffers are the same length.
// This also ensures that the count does not exceed the length of either buffer.
return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}
static void Main(string[] args)
{
var array1 = new int[,]
{
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
};
var array2 = new int[,]
{
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
};
Console.WriteLine(CompareArrays(array1, array2));
}
}
}
If I change arrays' size to 4x4 the output turns true
Why does memcmp behave this way?
Your code has many problems. Here is what I can see:
The msvcrt.dll C++ runtime is subject to change, so you any code that relies on it risks breaking in the future.
The final parameter of memcmp has type size_t. That is the same size as a pointer and so maps to UIntPtr. Remember that C# long is 64 bits wide.
The use of Array in a p/invoke is dubious at best. Who knows what that marshals as? I expect it marshals as a pointer to the internal representation of the class. It for sure does not marshal as a pointer to the beginning of the array.
memcpy expects the number of bytes to compare, not the length of the array. You need to multiply the length of the array by the size of the elements to get the number of bytes.
To make your program work you will need to tackle these issues. Leaving aside the use of msvcrt, you can correct your code like this:
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(IntPtr b1, IntPtr b2, UIntPtr count);
public static bool CompareArrays(Array b1, Array b2)
{
if (b1.Length != b2.Length)
return false;
if (!b1.GetType().GetElementType().Equals(b2.GetType().GetElementType()))
return false;
GCHandle gch1 = GCHandle.Alloc(b1, GCHandleType.Pinned);
try
{
GCHandle gch2 = GCHandle.Alloc(b2, GCHandleType.Pinned);
try
{
return memcmp(gch1.AddrOfPinnedObject(), gch2.AddrOfPinnedObject(),
(UIntPtr)(b1.Length * Marshal.SizeOf(b1.GetType().GetElementType()))) == 0;
}
finally
{
gch2.Free();
}
}
finally
{
gch1.Free();
}
}
This code is surely not very efficient, not to mention it being very hacky. I suggest that you stick to pure .net code.
I need to run a stored procedure from code. One of the input parameters is rowVersion of the table. rowVersion is a byte array ( {0, 0, 0, 0, 0, 0, 13, 191} that's 0x0000000000000DBF in db). So if to add rowVersion this way :
cmd.Parameters.AddWithValue("#myPKRowversion", 0x0000000000000DBF);
my sp is working. But when I'm adding it like here:
uint a = 0x0000000000000DBF;
cmd.Parameters.AddWithValue("#myPKRowversion", a);
or if I convert byte Array to string like:
string a = "0x0000000000000DBF";
cmd.Parameters.AddWithValue("#myPKRowversion", a);
my sp is not working.
What should I do to make my sp work?
I suggest you add it as a byte array. For example:
byte[] bytes = new byte[] { 0, 0, 0, 0, 0, 0, 13, 191 };
cmd.Parameters.Add("#myPKRowVersion", SqlDbType.Binary).Value = bytes;
If you're trying to specify bytes, the most natural type is a byte array...
I know there are many tutorials out there showing you how to use the "ProcessMemoryReader" functions. But this problems seems to be unique or not solved yet.
For quite a while I've been digging into other people's code to find a way to use multiple offsets.
And I thought that using multiple offsets was the problem for me, but I think it's a problem with the fact that my offset value is bigger than 255.
The game I'm trying to get the memory values from is called "Assault Cube".
As I wasn't sure whether I got the right offset values I googled what others results where.
They seem to be exactly the same:
http://cheatengine.org/tables/moreinfo.php?tid=1142 (You can view the .ct file with notepad if you don't have cheat engine installed.)
Here is my code, using the ProcessMemoryReader.cs.
private void timer1_Tick(object sender, EventArgs e)
{
int bytesread;
int pointerbase;
byte[] memory;
Process[] myprocess = Process.GetProcessesByName("ac_client");
if (myprocess.Length != 0)
{
preader.ReadProcess = myprocess[0];
preader.OpenProcess();
//Ammo
memory = preader.ReadProcessMemory((IntPtr)0x4DF73C, 4, out bytesread);
pointerbase = BitConverter.ToInt32(memory, 0);
pointerbase += 0x00; //0 // 14 // 378
byte[] memory1 = preader.ReadProcessMemory((IntPtr)pointerbase, 4, out bytesread);
int pointerbase1 = BitConverter.ToInt32(memory1, 0);
pointerbase1 += 0x14; //0 // 14 // 378
byte[] memory2 = preader.ReadProcessMemory((IntPtr)pointerbase1, 4, out bytesread);
int pointerbase2 = BitConverter.ToInt32(memory2, 0);
pointerbase2 += 0x378; //00 // 14 // 378
byte[] memory3 = preader.ReadProcessMemory((IntPtr)pointerbase2, 4, out bytesread);
int valueis = BitConverter.ToInt32(memory3, 0);
label1.Text = valueis.ToString();
}
Though with a single pointer the process works fine, for example:
//HP
memory = preader.ReadProcessMemory((IntPtr)0x4DF73C, 4, out bytesread);
pointerbase = BitConverter.ToInt32(memory, 0);
pointerbase += 0xf4;
byte[] memory1 = preader.ReadProcessMemory((IntPtr)pointerbase, 4, out bytesread);
int valueis = BitConverter.ToInt32(memory1, 0);
label2.Text = valueis.ToString();
So that works, it's pretty straight forward what's happening here, but I can't figure how to read the Ammo code with the multiple offsets.
I'm not familiar with CheatEngine and it's table format, but I do not get the impression it's pointing to the memory addresses that you are using.
You read 4 bytes at 0x4DF73C, which is used as the new memory address for the next read. This is repeated a few times. Basically, you're reading information from a pointer to a pointer to a pointer. Are you sure this is what is intended?
There's no reason whatsoever that an offset value greater than 255 would be a problem.
Use FindDMAAddy to walk the pointer chain for you, here is a working example, make sure you run as admin:
public static IntPtr FindDMAAddy(IntPtr hProc, IntPtr ptr, int[] offsets)
{
var buffer = new byte[IntPtr.Size];
foreach (int i in offsets)
{
ReadProcessMemory(hProc, ptr, buffer, buffer.Length, out
var read);
ptr = (IntPtr.Size == 4) ? IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), i) : ptr = IntPtr.Add(new IntPtr(BitConverter.ToInt64(buffer, 0)), i);
}
return ptr;
}
var modBase = GetModuleBaseAddress(proc.Id, "ac_client.exe");
var ammoAddr = FindDMAAddy(hProc, (IntPtr)(modBase + 0x10f4f4), new int[] { 0x374, 0x14, 0 });
Console.WriteLine("Ammo address " + "0x" + ammoAddr.ToString("X"));
int newAmmo = 1337;
byte[] buffer = new byte[4];
ReadProcessMemory(proc.Handle, ammoAddr, buffer, 4, out _);
Console.WriteLine("Ammo value " + BitConverter.ToInt32(buffer, 0).ToString());
WriteProcessMemory(hProc, ammoAddr, newAmmo, 4, out _);