Make this Readprocessmemory code a lot more efficient - c#

Introduction
Currently I am using someone's class to read bytes in memory (via pointers). The code I have so far works perfectly, I don't really want to change the way the class is setup if possible (because it works), but hopefully some minor tweaks can be made to the class and my code to make it highly efficient.
What I achieve at the moment:
Start off with a pointer address in memory, reads a byte at that address, adds the data to an array, adds 1 to the original address so we can now read the next address (eg original address is 24004, once the byte is stored, increment 1 and next address to read becomes 24005).
Reads the byte at the next address (24005), adds this to the same array, adds 1 to that address (next to read becomes 24006).
And so forth for a fixed number of iterations (approximately 10,000).
The problem:
Making 10,000 calls to readprocessmemory one after the other causes a system delay of 20 seconds whilst it goes about its business.
What I hope can be achieved:
Perform Readprocessmemory once only, specifying 10,000 bytes of data to be read (instead of just a byte at a time for 10,000 iterations), save this to the array in the same format as I had before with individual bytes (I am aware therefore that instead of array {0} (1) (2).. etc.. I will now just have array {0}, so I would imagine I would need an efficient means of splitting this very large number out into 10,000 numbers (in another array)) The data stored at each address are integers. So for 5 addresses: array {12345} becomes {1}{2}{3}{4}{5}. Where 1 2 3 4 or 5 could just as well be 1 200 40 43 or 20, for example.
So ideally, if I could wave my newbie wand, it would look something like this (class below as well as what I have so far):
Iteration code:
private void label1_Click(object sender, EventArgs e)
{
int[] valuesSeperated[200];
List<byte> PreArray = new List<byte>();
Process[] test = Process.GetProcessesByName("MyProcess"); //Get process handle
int baseAddress = test[0].MainModule.BaseAddress.ToInt32(); //Get base address
byte ReadX = MyClass.ReadPointerByte("MyProcess", BaseAddress, new int[] { 0xc, 0x0, 0x2 }); //call memory reading function (including memory offsets)
PreArray.Add(ReadX);
byte[] PreArrayToInt = PreArray.ToArray();
int[] MYConvertedBytes = PreArray ToInt.Select(x => (int)x).ToArray();
foreach (int i in MYConvertedBytes)
{
valuesSeperated // (don't really know what to do here, if the read was successful I would have a long number at [0], so now need to separate these as if I had read each one in memory one at a time.
}
//new array with 10,000 values created.
}
Class:
[DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
private static extern byte ReadProcessMemoryByte(int Handle, int Address, ref byte Value, int Size, ref int BytesRead);
public static byte ReadPointerByte(string EXENAME, int Pointer, int[] Offset)
{
byte Value = 0;
checked
{
try
{
Process[] Proc = Process.GetProcessesByName(EXENAME);
if (Proc.Length != 0)
{
int Bytes = 0;
int Handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Proc[0].Id);
if (Handle != 0)
{
foreach (int i in Offset)
{
ReadProcessMemoryInteger((int)Handle, Pointer, ref Pointer, 4, ref Bytes);
Pointer += i;
}
ReadProcessMemoryByte((int)Handle, Pointer, ref Value, 2, ref Bytes);
CloseHandle(Handle);
}
}
}
catch
{ }
}
return Value;
}

Some advices:
Do not call OpenProcess / CloseHandle inside your "managed wrapper" (ReadPointerByte, or whatever...), you can use the Process.Handle property.
To avoid possible errors related with page's permissions, you might need to wrap calls to ReadProcessMemory with VirtualProtectEx (one to 'unprotect', allowing reads, and another to 'restore' the previous protection).
Some code:
// from http://www.pinvoke.net/default.aspx/kernel32.readprocessmemory
[DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
private static extern bool ReadProcessMemory(IntPtr Handle, IntPtr Address,
[Out] byte[] Arr, int Size, out int BytesRead);
public static byte[] ReadBytes(IntPtr hnd, IntPtr Pointer, int Length)
{
byte[] Arr = new byte[Length];
int Bytes = 0;
if(!ReadProcessMemory(hnd, Pointer, Arr, Length, out Bytes)){
// Throw exception ...
}
// Check if Bytes == Length ...
return Arr;
}
private void button1_Click(object sender, EventArgs e)
{
//Get process handle
Process[] test = Process.GetProcessesByName("notepad++");
//Get base address
IntPtr baseAddress = test[0].MainModule.BaseAddress;
int bytesToRead = 16;
int[] valuesSeparated = new int[bytesToRead / 4];
byte[] ret = ReadBytes(test[0].Handle, baseAddress, bytesToRead);
// Interpret ret as you like ...
// Convert ret to int[]
valuesSeparated = ....
}
[The iteration process]
You have two options here:
Get a full snapshot of the target process's memory into a byte[], and perform your algorithm in this array
You can exec a readprocessmemory for each iteration. I believe this is your best option.
Regardless of targe-process-memory-reading-technique, your algorithm is this (I'm having a real hard time translating your text into an implementation...):
Input: BaseAddress of process, Offsets
Output: byte[] Ret
Steps:
1: LastPtr = BaseAddress
2: IdxOffset = 0
3: Offset = Offsets[IdxOffset]
4: LastValue = Memory[LastPtr + Offset]
5: Ret.Add(LastValue)
6: IdxOffset++
7: LastPtr = LastValue // Is this it?????
8: If IdxOffset < Offsets.Length then return else goto 3

Related

How to copy float* to IntPtr?

Short description of my task:
As parameter of my function I got some buffer (IntPtr).
I need to extract some information from this buffer and copy information to audioFrame.AudioBuffer buffer (IntPtr).
Problem:
Needed information placed in channelData[c] (float*), I need to copy this information to destStart (IntPtr).
Code:
private void SomeFunc(IntPtr buffer)
{
...
AudioFrame audioFrame; // audioFrame.AudioBuffer is IntPtr
...
unsafe
{
float** channelData = (float**)buffer.ToPointer();
for (int c = 0; c < 2; c++)
{
IntPtr destStart = new IntPtr(audioFrame.AudioBuffer.ToInt64() + (c * audioFrame.ChannelStride));
Marshal.Copy(channelData[c], 0, destStart, audioFrame.NumSamples); ///< problem in this line, channelData[c] is float*
}
}
...
}
Edit
Little more context: I got this buffer from CEF (https://github.com/cefsharp/CefSharp). In fact this function work as callback. When I got new audio data I need to send this data throught NDI (https://www.ndi.tv/)
AudioFrame is wrapper over the NDI structure
public struct audio_frame_v2_t
{
// The sample-rate of this buffer
public int sample_rate;
// The number of audio channels
public int no_channels;
// The number of audio samples per channel
public int no_samples;
// The timecode of this frame in 100ns intervals
public Int64 timecode;
// The audio data
public IntPtr p_data;
// The inter channel stride of the audio channels, in bytes
public int channel_stride_in_bytes;
// Per frame metadata for this frame. This is a NULL terminated UTF8 string that should be
// in XML format. If you do not want any metadata then you may specify NULL here.
public IntPtr p_metadata;
// This is only valid when receiving a frame and is specified as a 100ns time that was the exact
// moment that the frame was submitted by the sending side and is generated by the SDK. If this
// value is NDIlib_recv_timestamp_undefined then this value is not available and is NDIlib_recv_timestamp_undefined.
public Int64 timestamp;
}
Perhaps consider spans here:
var fromSpan = new Span<float>(channelData[c], audioFrame.NumSamples);
var toSpan = new Span<float>(destStart.ToPointer(), audioFrame.NumSamples);
fromSpan.CopyTo(toSpan);
Or Buffer.MemoryCopy:
var size = sizeof(float) * audioFrame.NumSamples;
Buffer.MemoryCopy(channelData[c], destStart.ToPointer(), size, size);
(note that in both cases it would be better to include knowledge of the actual buffer sizes if you have it, to avoid buffer overflow scenarios; I've just assumed the sizes are valid, for simplicity; there's also Unsafe.CopyBlock which works a lot like Buffer.MemoryCopy)

Enumerating the shell with Next > 1

All sample codes anywhere on the net just don't question the habit of passing 1 as the first argument to an enumeration interface Next() call. Yet the documentation clearly promises that more than one items can be obtained in one call. As it can be seen in this code fragment, doing so would tremendously speed up the process of counting the files in a folder (actually, the fundamentally similar WPD interface works that way).
string FolderPath = #"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\<Phone-USB-ID>\Internal storage\Pictures\Test";
SHCreateItemFromParsingName(FolderPath, IntPtr.Zero, typeof(IShellItem).GUID, out IShellItem item);
item.BindToHandler(IntPtr.Zero, BHID_SFObject, typeof(IShellFolder).GUID, out IShellFolder folder);
folder.EnumObjects(IntPtr.Zero, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, out IEnumIDList list);
uint count = 0;
try {
do {
//var ObjectIDs = new ObjectIDLargeArray();
//var pidl = Marshal.AllocHGlobal(Marshal.SizeOf(ObjectIDs));
//Marshal.StructureToPtr(ObjectIDs, pidl, true);
//var pidl = Marshal.AllocCoTaskMem(100 * IntPtr.Size);
int hr = list.Next(100, out var pidl, out uint fetched); // <<<<<
if (hr == 0)
count += fetched;
if (fetched == 0)
break;
//Marshal.FreeHGlobal(pidl);
Marshal.FreeCoTaskMem(pidl);
}
while (true);
}
catch (Exception e) {
Console.WriteLine(e);
}
[StructLayout(LayoutKind.Sequential)]
internal class ObjectIDLargeArray {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public IntPtr[] IDs;
}
The documentation and the usage on the web is not entirely clear about whether the caller has to allocate the buffer for the IDs returned but as can be seen from the code, I tried all approaches, both a specific array and an allocated block of memory. For very small values like 2 and 3, the shell might simply return garbage in fetched. For anything larger, an access violation exception.
Just to put it into a perspective of why it's important: with a newly connected phone, no previous caching, counting the files in a folder of 500 pictures takes about 15 (!) seconds. The second time with data already cached, 3 to 4. Needless to say, even the second is unacceptable for a mere file count but 15 seconds are absolutely stupid. With the otherwise mediocre speeds of WPD but with a 100-step count, a few hundred ms at most.
Solved courtesy of the new C# 7.2 feature, in parameters:
internal interface IEnumIDList {
[PreserveSig]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
HResult Next(uint celt, in IntPtr rgelt, out uint pceltFetched);
}
That makes it play all right with both the preallocated array and the single value case. It's not really that much faster, unfortunately...

C# - Read Write Process Memory , Pointers and Offsets Gamer Server Management Tool

I am coding a server manager for a game and got stuck in pointers and offsets in C#. Old server manager is written in vb6 and for some reason does not work on my windows so i decided to code some basic functions of it in C#.
I have pointer and offsets values of everything that is necessary for now i am writing only to get all player names.
Player pointer = 96C290 Player name Offset = +20
Offset of +668 give me the next player pointer, and adding +20 to next player should give me next player name and so on.
Reading First Player Name
public static IntPtr BASE_ADDR = new IntPtr(0x96C290);
public static IntPtr OFFSET_NAME = new IntPtr(0x20);
const int PROCESS_WM_READ = 0x0010;
public static void Read()
{
Process process = Process.GetProcessesByName("gameprocessname")[0];
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false, process.Id);
//defining data structures
int bytesRead = 0;
byte[] buffer = new byte[4];
//Reading Base Address pointer value
ReadProcessMemory((int)processHandle,(int)BASE_ADDR, buffer, 4, ref bytesRead);
IntPtr myBaseAddress = new IntPtr(BitConverter.ToInt32(buffer, 0));
//Adding offset of 20 to original base pointer address
IntPtr namePointer = BASE_ADDR;
namePointer = IntPtr.Add(namePointer,(int)OFFSET_NAME);
//Getting memory address of name pointer
ReadProcessMemory((int)processHandle, (int)namePointer,buffer, 4, ref bytesRead);
IntPtr playerNameAddress = new IntPtr(BitConverter.ToInt32(buffer, 0));
//reading name from name address . ASCII and Unicode
byte[] playerNameBuffer = new byte[256];
ReadProcessMemory((int)processHandle, (int)playerNameAddress, buffer, 256, ref bytesRead);
string name = Encoding.Default.GetString(playerNameBuffer);
MessageBox.Show(name);
}
I am not getting player name . The scripts written in VB6 are operational.
I am Using Win8.1 64bit and the game is very old 32 bit application.
What could be the problem ? i used cheat engine to view values manually but it also shows me nothing. Pointer and Offset values are correct.
I have used different offsets values but got none of them return the right values.
The questions is **If i am doing something wrong on coding end ? or it is the OS 64bit issue. **
I didn't test the program (because I don't have/know the game) but I see that you read myBaseAddress then you don't use it, may be the problem is in the following lines
IntPtr namePointer = BASE_ADDR;
namePointer = IntPtr.Add(namePointer,(int)OFFSET_NAME);
that should be
IntPtr namePointer = myBaseAddress ;
namePointer = IntPtr.Add(namePointer,(int)OFFSET_NAME);

C# unmanaged function calls, Possible data loss, Ascii Vs. Unicode, Chinese folder names

So here is the deal, Im working a C# application that calls into a legacy C++ dll, which in turns loops through a directory pulling back the names of certain directories, i.e directories that have .lib, I have a directory with the following 3 folders: Default.Lib,中文文本帧的文件.lib,我们的.lib.
As you can see we have some chinese folder names, a string is built in memory by the c++ code as you can see below, it use strcat to build it in memory. however when control is returned back to the c# code, it appears part of that data is lost and the only two folders left are the first two. Default.Lib,中文文本帧的文件.lib, something with 我们的.lib gets lost in translation, I would greatly appreciate any insights anyone may have. thanks.
C# code snippet
lock (padLock)
{
ConnectSign(service);
int size = MaxFileListSize * 100;
byte[] mem = new byte[size];
string finalList;
int used = size;
int fileCount = 0;
string library = "*";
string extension = "*";
V7_FILE_LIST_TYPE type = V7_FILE_LIST_TYPE.LibraryList;
fixed (byte* listbytes = mem)
{
int error = NativeMethods.GetFileDirInfo(sign, type, fileServer, library, extension, &fileCount, listbytes, &used);
if (error != 0)
throw new V7ResponseException(error, sign, service, "GetFileDirInfo");
}
finalList = Encoding.Default.GetString(mem, 0, (int)used);
string[] libraryArray = finalList.Split(new char[] { '\n', '\0' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < libraryArray.Length; i++)
{
int index = libraryArray[i].LastIndexOf(".lib", StringComparison.OrdinalIgnoreCase);
if (index > 0)
libraryArray[i] = libraryArray[i].Substring(0, index);
//libraryArray[i] = libraryArray[i].Trim().ToLower(CultureInfo.CurrentCulture).Replace(".lib", string.Empty);
}
return libraryArray;
}
[DllImport("V7SSRpc.dll", CharSet = CharSet.Ansi, EntryPoint = "V7ssGetFileDirInfo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int GetFileDirInfo(string sign, V7_FILE_LIST_TYPE type, string fileServer, string library, string extension, int* fileCount, byte* files, int* bytesUsed);
*****************************C++ DLL code--------------------------------------
//--------------------------------------------------------------------
// RETURN :
//
// PARAMS : eListType
// szServer
// szLib
// szExt
// *pdwFileCnt
// *pbyFileBuf
// *pdwFileBufSize
//
// REMARKS:
//
BOOL CVSign::apiGetFileDirInfo(V7_FILE_LIST_TYPE eListType, LPCSTR szServer, LPCSTR szLib, LPCSTR szExt,
DWORD *pdwFileCnt, char *pbyFileBuf, DWORD *pdwFileBufSize) const
{
BOOL bReturn=TRUE;
CString sServer(szServer);
CString sLib(szLib);
CString sExt(szExt);
CString sFileInfo, sTemp;
CStringArray asFiles;
CFileStatus status;
CV7Files V7Files;
DWORD dwBufUsed=0;
// SOME OTHER LOGIC (not posted)
USES_CONVERSION;
//CoInitialize(NULL);
//AVIFileInit();
CString sFilePath;
CV7SequenceFile V7Seq;
CV7FileInfo fileInfo;
// go through list of files and build the buffer with file names and other info
for (nFile=0; nFile<nFiles; nFile++)
{
// MORE OBSCURED LOGIC
sFileInfo += _T("\n");
// add file info to buffer
int nLen = sFileInfo.GetLength();
if (dwBufUsed+nLen<*pdwFileBufSize)
{
strcat(pbyFileBuf, T2CA(sFileInfo)); //<--- THIS IS THE IMPORTANT PART
int nTemp = sFileInfo.GetLength();
dwBufUsed += nTemp;
}
else
{
*pdwFileBufSize = 0;
AVIFileExit();
CoUninitialize();
return FALSE;
}
} // end for files
//AVIFileExit();
//CoUninitialize();
*pdwFileBufSize = dwBufUsed;
return bReturn;
} // end apiGetFileDirInfo()
I suspect the problem is that you're specifying Charset=CharSet.Ansi, so the default marshaling behavior is to convert the returned string to ANSI. That's going to cause a problem.
You probably want to specify string Charset=CharSet.Unicode, and possibly specify custom marshaling for some strings. See http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor5 for information on how to change the string marshaling behavior for individual parameters.

How can I pass a pointer to an integer in C#

I have a C API with the signature:
int GetBuffer(char* buffer, int* maxSize)
In C, I will call it this way:
char buffer[4096];
int maxSize = 4096;
GetBuffer(buffer, &maxSize);
maxSize is set to the buffer size, and set with the actual size filled.
I need to call it from C#. How do I do that under "safe mode"?
One option is simply to use C# pointer types - this requires unsafe block (or modifier on method/class), and compiling with /unsafe:
[DllImport(...)]
static extern int GetBuffer(byte* buffer, ref int maxSize);
Buffer can be allocated in several different ways. One would be to use a pinned heap array:
fixed (byte* buffer = new byte[4096])
{
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
}
Another is to use stackalloc, though this is only feasible for small buffers:
byte* buffer = stackalloc byte[4096];
int maxSize = 4096;
GetBuffer(buffer, ref maxSize);
This particular approach is virtually identical to your C code in terms of performance and allocation patterns.
Another option altogether is to use marshaling for heap arrays, and avoid pointers entirely.
[DllImport(...)]
static extern int GetBuffer([Out] byte[] buffer, ref int maxSize);
byte[] buffer = new byte[4096];
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
This should work without unsafe code.
extern int GetBuffer(IntPtr buffer, ref int bufSize);
// ...
byte[] buf = new byte[kBufSize];
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); // possibly expensive call
IntPtr p = handle.AddrOfPinnedObject();
int size = buf.Length;
int ret = GetBuffer(p, ref size);
handle.Free();
You need to use what is called P\Invoke, and generate a function declaration that to reference the C function in the Dll from C#.
However, you have to be very careful when passing buffers in/out of unmanaged code. The framework will take care of some things for you but you may need to ensure that memory that you pass into the unmanaged call doesn't get moved by the Garbage collector.
[DllImport("Kernel32.dll", SetLastError=true)]
static extern Int32 GetBuffer(byte[] buffer,ref Int32 maxSize);
And to use it:
byte[] myBuf = new myBuf[4096];
Int32 maxSize = myBuf.Length;
GetBuffer(myBuf, ref maxSize);
Having a handle to the pointer doesn't fit the "safe mode" model at all; if the resource isn't managed by the Framework, it is unsafe.
One easy and safe option is to create a simple class that wraps the value, or any value like the following code:
public class Value<T> where T: struct
{
public static implicit operator T(Value<T> val)
{
return val.Value;
}
private T _value;
public Value(T value)
{
_value = value;
}
public Value() : this(default)
{
}
public T Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public override string ToString()
{
return _value.ToString();
}
}
Passing on instances of this class instead of the value itself works almost like working with pointers.

Categories

Resources