C#: Search a byte[] array in another process's memory - c#

How is it possible to search for a byte[] array in the memory of another process and then get the address at the place where the byte[] array is located?
I want to write a byte array into the memory of another process(WriteProcessMemory()).One of the parameters of that call is uint Address.Well I want to get the address by searching a byte array into the process.
For example I search for {0xEB ,0x20,0x68,0x21,0x27,0x65, ??, 0x21,0x64,0xA1}
We assume that this array is placed only at one place in the memory of the process I'd like to write memory to.
To get that address,I have to search for that byte array.
Is it possible to be done in C#?
EDIT: This is for native applications,NOT .NET. No need to downvote my question,there are components for C++ that do this,I just want to do it in C#.
Thanks for understanding!

Is it possible to be done in C#?
Everithing is possible in c#(or any other languge), u just need to fiind how;
Hard coding here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out] byte[] lpBuffer,
int dwSize,
out int lpNumberOfBytesRead
);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
static void Main(string[] args)
{
Process[] procs = Process.GetProcessesByName("explorer");
if (procs.Length <= 0) //proces not found
return; //can replace with exit nag(message)+exit;
IntPtr p = OpenProcess(0x10 | 0x20, true, procs[0].Id); //0x10-read 0x20-write
uint PTR = 0x0; //begin of memory
byte[] bit2search1 = {0xEB ,0x20,0x68,0x21,0x27,0x65}; //your bit array until ??
int k = 1; //numer of missing array (??)
byte[] bit2search2 = {0x21,0x64,0xA1};//your bit array after ??
byte[] buff = new byte[bit2search1.Length+1+bit2search2.Length]; //your array lenght;
int bytesReaded;
bool finded = false;
while (PTR != 0xFF000000) //end of memory // u can specify to read less if u know he does not fill it all
{
ReadProcessMemory(p, (IntPtr)PTR, buff, buff.Length, out bytesReaded);
if (SpecialByteCompare(buff, bit2search1,bit2search2,k))
{
//do your stuff
finded = true;
break;
}
PTR += 0x1;
}
if (!finded)
Console.WriteLine("sry no byte array found");
}
private static bool SpecialByteCompare(byte[] b1, byte[] b2, byte[] b3, int k) //readed memory, first byte array, second byte array, number of missing byte's
{
if (b1.Length != (b2.Length + k + b3.Length))
return false;
for (int i = 0; i < b2.Length; i++)
{
if (b1[i] != b2[i])
return false;
}
for (int i = 0; i < b3.Length; i++)
{
if (b1[b2.Length + k + i] != b3[i])
return false;
}
return true;
}
}
}

I guess you could use the ReadProcessMemory Windows API call. There's even a premade P/Invoke signature for it so you don't need to bother with manually crafting it. You page through the memory of the process, search through it for your pattern and you're done.

You'll want to use these APIs:
[DllImport("Kernel32.Dll")]
public static extern uint VirtualQueryEx(IntPtr ProcessHandle, uint Address, ref MEMORY_BASIC_INFORMATION MemInfo, int MemInfoLength);
[DllImport("Kernel32.Dll")]
public static extern bool ReadProcessMemory(IntPtr ProcessHandle, uint Address, byte[] Buffer, uint Size, ref uint BytesRead);
[DllImport("Kernel32.Dll")]
public static extern bool WriteProcessMemory(IntPtr ProcessHandle, uint Address, byte[] Buffer, uint Size, ref uint BytesRead);
pinvoke.net is a great resource for Windows API calls. I wrote a trainer for GTA: Vice City that uses these calls if you want to check out the code on sourceforge. The code isn't pretty, it was a long time ago and I just threw it together, but there are helper classes for enumerating memory regions for a process and searching for certain bytes or strings.

This may help you find the right way:
private static int GetMemoryAddressOfString(byte[] searchedBytes, Process p)
{
//List<int> addrList = new List<int>();
int addr = 0;
int speed = 1024*64;
for (int j = 0x400000; j < 0x7FFFFFFF; j+= speed)
{
ManagedWinapi.ProcessMemoryChunk mem = new ProcessMemoryChunk(p, (IntPtr)j, speed + searchedBytes.Length);
byte[] bigMem = mem.Read();
for (int k = 0; k < bigMem.Length - searchedBytes.Length; k++)
{
bool found = true;
for (int l = 0; l < searchedBytes.Length; l++)
{
if(bigMem[k+l] != searchedBytes[l])
{
found = false;
break;
}
}
if(found)
{
addr = k+j;
break;
}
}
if (addr != 0)
{
//addrList.Add(addr);
//addr = 0;
break;
}
}
//return addrList;
return addr;
}

Is it possible to be done in C#?
Yes. But very hard. It is hard from a native application where there is no impedance mismatched with the unmanaged view of processes and their memory maps you will need to use.
Considerations:
You will need permission to open the process to get a handle.
While the virtual memory space of a 32bit process is from two to four GB in size (depending on host OS and /3GB switch), much of this address range will not be allocated, and reading it will cause a page fault. You really need to find out what pages are allocated and for what to avoid lots of invalid page accesses.
Suggestions:
Do you really really need to do this? Seriously this will be hard.
Consider doing a native application, this will avoid working across the native/managed fence (this could include a native library with a managed driver application).
Do you really need to do this?
Consider doing the work inside the target process. This will require some cleverness (documented) to inject a thread, but should then be much faster.
Start by reading up on Windows how process memory works (start with Windows Internals and (can't recall its name in the latest edition) Jeffrey Richter's book on Win32 application development.
Do you really need to do this? There must be something simpler... could you automated a debugger?

Related

ReadProcessMemory not working with Dolphin emulator

I'm trying to get access to some of the values in the memory of an emulation of Smash Bros Melee running in Dolphin. The Dolphin debug mode says that the address of the value I want is 0x80C6BA10 (I'm assuming that it means 0x00C6BA10, because 0x80C6BA10 goes over the integer limit, and it had previously referred to the same address as 0x00C6BA10). But when I pass this into the ReadProcessMemory function, I get a byte array that is just [0,0,0,0] (I'm using 4 bytes because the value I'm trying to get is an 8 digit hexadecimal number).
I tried to use OllyDbg to check if the address is right, but for some reason Dolphin doesn't work with OllyDbg, maybe because it isn't 64 bit? I've also tried using the unchecked() method in order to pass 0x80C6BA10 instead of 0x00C6BA10.
public class Program
{
//Constant that says we want to just read memory
const int PROCESS_WM_READ = 0x0010;
// **** Imports the functions used to get memory data ****
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool
bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int
lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
static void Main(string[] args)
{
//Gets process by finding the first process with the name dolphin
Process melee = Process.GetProcessesByName("Dolphin")[0];
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false,
melee.Id);
int bytesRead = 0;
//The list that will be populated by the data we find in the
memory
byte[] buffer = new byte[4];
//Reads the memory at the specified location
ReadProcessMemory((int)processHandle, 0x00C6BA10, buffer,
buffer.Length, ref bytesRead);
//Converts byte array into floating point number -- not currently
being used
float memoryValue = BitConverter.ToSingle(buffer, 0);
foreach (byte b in buffer)
{
Console.WriteLine(b);
}
Console.ReadLine();
}
}
I expect the output to be anything but 0, but what I get back is an array of 0s. I think part of the issue might be that I'm trying to get memory values from a process that is running as a sub-process of Dolphin.

(lib)mpg123 use of function mpg123_index in C#

I'm using mpg123 inside my project to decode mp3. I spoke with the developer (Thomas Orgis) about a performance issue when seeking and he had a great idea: Scan the file and afterwards set the scanned index to the playback handle.
So I want to use mpg123_index and mpg123_set_index inside my C# project and use a wrapper around libmpg123.
The wrapper is not self written, I also contacted the developer, but he seems to be unavailable. So maybe someone has knowledge of pointer programming, I also did some things myself, but currently I only get a AccessViolationException when calling the method.
Here some Code:
Wrapper specification (maybe this is wrong?!):
[DllImport(Mpg123Dll)]public static extern int mpg123_index(IntPtr mh, IntPtr offsets, IntPtr step, IntPtr fill);
Specification on Website (C):
https://www.mpg123.de/api/group__mpg123__seek.shtml#gae1d174ac632ec72df7dead94c04865fb
MPG123_EXPORT int mpg123_index ( mpg123_handle * mh, off_t **
offsets, off_t * step, size_t * fill )
Give access to the frame index table that is managed for seeking. You
are asked not to modify the values... Use mpg123_set_index to set the
seek index
Parameters
mh handle
offsets pointer to the index array
step one index byte offset advances this many MPEG frames
fill number of recorded index offsets; size of the array
Returns
MPG123_OK on success
My try to use the function in C#:
IntPtr pIndex = IntPtr.Zero;
IntPtr pFill = IntPtr.Zero;
IntPtr pStep = IntPtr.Zero;
if (MPGImport.mpg123_index(this.mp3Handle,pIndex,pStep,pFill) != (int)MPGImport.mpg123_errors.MPG123_OK))
{
log.error("Failed!");
}
So, has anyone an idea, how to use the function properly on C# side? From the author of mpg123 I just got the information, that the pIndex will be a pointer to the internal index. So this pointer will be changed by this function.
Thanks for your help.
Sincerely
Sven
Edit:
Now the following code almost works:
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_index(IntPtr mh, long **offsets, long *step, ulong *fill);
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_set_index(IntPtr mh, long* offsets, long step, ulong fill);
public static Boolean CopyIndex(IntPtr _sourceHandle,IntPtr _destinationHandle)
{
Boolean copiedIndex = false;
unsafe
{
long* offsets = null;
long step = 0;
ulong fill = 0;
int result = MPGImport.mpg123_index(_sourceHandle, &offsets, &step, &fill);
if (result == (int)MPGImport.mpg123_errors.MPG123_OK)
{
result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill);
if (result == (int)mpg123_errors.MPG123_OK)
{
copiedIndex = true;
}
}
}
return copiedIndex;
}
On result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill); the result is -1, which is MPG123_ERR = -1, /**< Generic Error */.
Working code:
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_index(IntPtr mh, long **offsets, long *step, ulong *fill);
[DllImport(Mpg123Dll)]public unsafe static extern int mpg123_set_index(IntPtr mh, long* offsets, long step, ulong fill);
public static Boolean CopyIndex(IntPtr _sourceHandle,IntPtr _destinationHandle)
{
Boolean copiedIndex = false;
unsafe
{
long* offsets = null;
long step = 0;
ulong fill = 0;
int result = MPGImport.mpg123_index(_sourceHandle, &offsets, &step, &fill);
if (result == (int)MPGImport.mpg123_errors.MPG123_OK)
{
result = MPGImport.mpg123_set_index(_destinationHandle, offsets, step, fill);
if (result == (int)mpg123_errors.MPG123_OK)
{
copiedIndex = true;
}
}
}
return copiedIndex;
}
_sourceHandle and _destinationHandle may not be IntPtr.Zero, that was my mistake. Thanks for Stargateur, really nice help!

Evaluate if drive is in use

I'd like to evaluate wether a drive is in use (if I'm not mistaken this means that some read/write stuff is happening with the drive) using C#. I wouldn't mind for a solution using bash scripts or similiar either, as I could use them in a C# application. I already found a question regarding bash scripts here, but couldn't solve my problem with the answers given.
I considered to use the DriveInfo class already, however it didn't seem to have anything useful for me. I wondered wether I could use the IsReady property from DriveInfo, because I guessed that it wouldn't be ready while it is read/written, but this attempt seems rather botchy to me.
However I still tried it:
private static bool IsDriveInUse ()
{
var drive = DriveInfo.GetDrives ().FirstOrDefault(info => info.Name.StartsWith(DRIVE_LETTER.ToString()));
return drive != null && !drive.IsReady;
}
But it didn't work (it returned false while I played music from my drive).
An optimal solution for me would be a function that tells me wether the drive was in use in a specific span of time (let's stick to the name IsDriveInUse). That means that if the time was for example 60 seconds, IsDriveInUse should return true if 5 seconds before the function call content from the drive was read and false if there was no read/write action in the passed 60 seconds.
EDIT To specify what exactly I mean by in use, I'll try to explain what I'm trying to do. I am writing a tool, which automatically spins down my hard drive, when it's been idle or when I press a keyboard shortcut. I managed to spin it down programmatically (even though either the windows integrated tool nor other tools I found were able to do that, but that's another problem). However, it now spins down the hard drive every minute, regardless of wether it's currently in use or not. That means, if I play music from my hard drive, it's still spinned down, just to spin up directly after it, which doesn't decrease noise development.
I hope this clarified the matter.
EDIT I now tried using the FSCTL_LOCK_VOLUME control code (I couldn't find a value for IOCTL_DISK_PERFORMANCE), but it still returned false for IsDriveInUse() while I was playing music. Furthermore it caused windows to directly spin the drive up again as I spinned it down (probably because the releasing made Windows think that something was using the drive). This is what I tried:
public class DriveManager
{
public const int FSCTL_LOCK_VOLUME = 0x00090018;
public const int FSCTL_UNLOCK_VOLUME = 0x0009001c;
[DllImport ("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateFile (
string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[return: MarshalAs (UnmanagedType.Bool)]
[DllImport ("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DeviceIoControl (
[In] SafeFileHandle hDevice,
[In] int dwIoControlCode, [In] IntPtr lpInBuffer,
[In] int nInBufferSize, [Out] IntPtr lpOutBuffer,
[In] int nOutBufferSize, out int lpBytesReturned,
[In] IntPtr lpOverlapped);
public static SafeFileHandle CreateFileR (string device)
{
string str = device.EndsWith (#"\") ? device.Substring (0, device.Length - 1) : device;
return new SafeFileHandle (
CreateFile (#"\\.\" + str, WinntConst.GENERIC_READ, WinntConst.FILE_SHARE_READ, IntPtr.Zero,
WinntConst.OPEN_EXISTING, WinntConst.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
}
internal class WinntConst
{
// Fields
internal static uint FILE_ATTRIBUTE_NORMAL = 0x80;
internal static uint FILE_SHARE_READ = 1;
internal static uint GENERIC_READ = 0x80000000;
internal static uint OPEN_EXISTING = 3;
}
public static bool IsDriveInUse (string deviceName)
{
var handle = CreateFileR (deviceName);
var buffer = Marshal.AllocHGlobal (sizeof (int));
try
{
return
DeviceIoControl (handle,
FSCTL_LOCK_VOLUME,
IntPtr.Zero,
0,
buffer,
sizeof(int),
out var bytesReturned,
IntPtr.Zero
);
}
finally
{
var sessionId = Marshal.ReadInt32 (buffer);
Marshal.FreeHGlobal (buffer);
handle.Close ();
}
}
And the implementation:
private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($#"{DRIVE_LETTER}:\");
Maybe it helps to see the part in which I'm spinning the disc down as well (I used Smartmontools for this):
internal static class Program
{
private const string PROGRAM_PATH = #"External\smartctl.exe";
private const string ARGUMENTS_SHUTDOWN = #"-s standby,now {0}:";
private const char DRIVE_LETTER = 'd';
public static void Main (string [] args)
{
InitializeHotKey ();
Console.WriteLine ("Hotkey registered!");
while (true)
{
Thread.Sleep (60000);
if (!IsDriveInUse ())
ShutDownDrive (true);
}
}
private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($#"{DRIVE_LETTER}:\");
private static void InitializeHotKey ()
{
HotKeyManager.RegisterHotKey (Keys.D, KeyModifiers.Alt | KeyModifiers.Control);
HotKeyManager.HotKeyPressed += HotKeyPressed;
}
private static void HotKeyPressed (object sender, HotKeyEventArgs hotKeyEventArgs) => ShutDownDrive (true);
private static void ShutDownDrive (bool withDialog = false)
{
Process process;
(process = new Process
{
StartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = PROGRAM_PATH,
Arguments = string.Format (ARGUMENTS_SHUTDOWN, DRIVE_LETTER)
}
}).Start ();
process.WaitForExit ();
process.Close ();
if (withDialog)
Console.WriteLine ("Drive shut down!");
}
}
Perhaps you could use the Windows Performance Counter relevant to your drive ?
"Disk Read/sec" seems quite relevant for what youhave in mind.
In .Net, the counters are available via System.Diagnostics.PerformanceCounter
see there :
https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx

PInvoke ReadFile kernel32: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I'm trying to make kernel32's ReadFile work since native c# alternatives are slow as...
The following code was working a couple of times, then suddenly only gives "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I figured a restart of the server would make it work a couple times before getting the same error, but it seems I was wrong there, it happens no matter what I do now..
Here's my code:
using System.Runtime.InteropServices;
private WinFileIO.WinFileIO _winFile;
_winFile = new WinFileIO.WinFileIO(new byte[bufferSize]);
_winFile.OpenForReading(_fileInfo.FullName);
public WinFileIO(Array Buffer)
{
PinBuffer(Buffer);
pHandleRead = IntPtr.Zero;
pHandleWrite = IntPtr.Zero;
bufferSize = Buffer.GetLength(0);
}
private void PinBuffer(Array Buffer)
{
UnpinBuffer();
gchBuf = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
IntPtr pAddr = Marshal.UnsafeAddrOfPinnedArrayElement(Buffer, 0);
// pBuffer is the pointer used for all of the I/O functions in this class.
pBuffer = (void*)pAddr.ToPointer();
}
public void UnpinBuffer()
{
if (gchBuf.IsAllocated)
gchBuf.Free();
}
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe System.IntPtr CreateFile
(
string FileName, // file name
uint DesiredAccess, // access mode
uint ShareMode, // share mode
uint SecurityAttributes, // Security Attributes
uint CreationDisposition, // how to create
uint FlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
private unsafe static extern bool ReadFile
(
int hFile,
byte[] arraypBuffer,
int NumberOfBytesToRead,
ref int lpNumberOfBytesToRead,
int* ptr
);
[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe bool SetFilePointer
(
System.IntPtr hObject,
int lDistanceToMove,
ref int lpDistanceToMoveHigh,
EMoveMethod dwMoveMethod
);
public void OpenForReading(string FileName)
{
Close(true, false);
pHandleRead = CreateFile(FileName, 3, 3, 0, OPEN_EXISTING, 0, 0);
if (pHandleRead == System.IntPtr.Zero)
{
Win32Exception WE = new Win32Exception();
ApplicationException AE = new ApplicationException("WinFileIO:OpenForReading - Could not open file " +
FileName + " - " + WE.Message);
throw AE;
}
}
public void MoveOffset(int moveDistance, EMoveMethod moveMethod)
{
if (pHandleRead != System.IntPtr.Zero)
{
int moveDistanceHigh = 0;
SetFilePointer(pHandleRead, moveDistance, ref moveDistanceHigh, moveMethod);
}
}
public unsafe Tuple<int, byte[]> ReadChunk(int bufferSize, int offset)
{
int bytesRead = 0;
byte[] bufBytes = new byte[bufferSize];
MoveOffset(offset, EMoveMethod.Begin);
int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));
if (ReadFile(pointer, bufBytes, bufferSize, ref bytesRead, null))
{
byte[] outBytes = new byte[bytesRead];
Array.Copy(bufBytes, outBytes, bytesRead);
return new Tuple<int, byte[]>(bytesRead, outBytes);
}
return null;
}
I think that's all the relevant code.
I'm guessing it has something to do with the CreateFile and ReadFile-signatures not being fully compatible (IntPtr vs. int),
the Marshal.PtrToStructure for the IntPtr not pointing where it should, or memory not being freed or something?
The fact that it didn't work for a couple tries after a reboot confuses me though.
Anyone spot anything obvious or have any suggestions I can look into?
Thanks
Edit: As you might notice, this is kind of a mishmash of different approaches, I'm not using the pinned buffer anymore as I struggled to get the contents of the buffer read like I would want to.
Edit2: The stacktrace says this is the problem:
at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)
Your pinvoke declarations are bad, get good ones from pinvoke.net
The 1st argument of ReadFile() is a handle, the one you got from CreateFile(). It is IntPtr. You dug yourself a hole by trying to convert the IntPtr to int, the Marshal.PtrToStructure() call is not correct. And will almost always bomb with an AVE, a handle is not a pointer.
Fix the [DllImport] declaration and use the IntPtr you got from CreateFile() directly. And don't forget that System.IO.FileStream is the .NET wrapper for these winapi functions, you only ever need to pinvoke CreateFile() if you need to open a handle to a device instead of a file. You then still use the FileStream constructor that takes a handle and use its Read() method to call ReadFile().
This code
int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));
does not do what you think it does. This code attempts to treat pHandleRead as a pointer to int. But that's not what you want at all. The code that does what you want is:
int iHandleRead = pHandleRead.ToInt32();
In fact you only find yourself writing that code because your p/invoke declarations are poor. Your ReadFile declaration should receive the file handle as IntPtr and not as int. If it did so then you would not need the code above at all and would pass pHandleRead.
More general points:
I do not understand why you have chosen to use unsafe code here. Nothing at all here requires that.
I also think it unlikely that this code, if you make it work, will perform better than the managed equivalent.

Working with pointer offsets in C#

I've been working on an application that use API to get events from the Windows event log. I'm stuck on pointer offsets at the moment. The specific struct I'm using is EVENTLOGRECORD (see: http://msdn.microsoft.com/en-us/library/aa363646(v=vs.85).aspx). My C# struct is defined as:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
internal struct EVENTLOGRECORD
{
internal UInt32 Length;
internal UInt32 Reserved;
internal UInt32 RecordNumber;
internal UInt32 TimeGenerated;
internal UInt32 TimeWritten;
internal UInt32 EventID;
internal UInt16 EventType;
internal UInt16 NumStrings;
internal UInt16 EventCategory;
internal UInt16 ReservedFlags;
internal UInt32 ClosingRecordNumber;
internal UInt32 StringOffset;
internal UInt32 UserSidLength;
internal UInt32 UserSidOffset;
internal UInt32 DataLength;
internal UInt32 DataOffset;
}
My ReadEventLog function is declared as:
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "ReadEventLog")]
internal static extern Boolean ReadEventLog(IntPtr hEventLog, EVT_READ_FLAGS dwReadFlags, UInt32 dwRecordOffset, IntPtr lpBuffer, UInt32 nNumberOfBytesToRead, out UInt32 pnBytesRead, out UInt32 pnMinNumberOfBytesNeeded);
I'm able to get the struct filled with data and I can get to SourceName and ComputerName sections by using IntPtr.Add. Example:
IntPtr pSrc = IntPtr.Add(pRecord, Marshal.SizeOf(typeof(EVENTLOGRECORD)));
string sSrc = Marshal.PtrToStringAuto(pSrc);
Console.WriteLine("source: {0}\n", sSrc);
IntPtr pComp = IntPtr.Add(pSrc, (sSrc.Length * 2) + 2);
string sComp = Marshal.PtrToStringAuto(pComp);
Console.WriteLine("computer: {0}\n", sComp);
My issue is trying to get the Strings portion from the struct. I can't seem to figure out what the correct offsets will be. I can do it in C++, but I can't seem to make it work in C#. Here's a snippet of what I use in C++ (elr is (EVENTLOGRECORD*)pRecord):
char* strings = (LPSTR)((LPBYTE) elr + elr->StringOffset);
while (elr->NumStrings)
{
wprintf(L"String: %s\n", strings);
strings += (wcslen((wchar_t*)strings) * sizeof(wchar_t)) + sizeof(wchar_t);
elr->NumStrings--;
}
Hopefully someone can help explain what I'm missing. I'd also be curious if there are any alternatives to IntPtr.Add since that require .NET 4.0. I'm not an expert with p/invoke by any means. Thanks.
It you use Marshal.PtrToStructure() to copy the first part of your block of data to a EVENTLOGRECORD, then you should be able to just do something like:
EVENTLOGRECORD record;
... Copy the ptr into record ...
IntPtr pStrings = IntPtr.Add(pRecord, (record.StringOffset * 2));
I'd be happy to get this going for you, but I'm too lazy to do all the other p/invoke bit which get as far as being able to make the ReadEventLog call.
After some time away from coding to clear the brain, I finally have a solution. I was incorrectly making the offset too large since I was taking the size of the struct (EVENTLOGRECORD), adding it to StringOffset, then adding that to the existing object. All I really needed to do was add the offset to the existing IntPtr (pRecord). Not sure why that didn't click before.
So simply doing:
int offset = ((int)(((EVENTLOGRECORD)o).StringOffset));
IntPtr pStrings = IntPtr.Add(pRecord, offset);
string sString = Marshal.PtrToStringAuto(pStrings);
Console.WriteLine("string : {0}\n", sString);
..is enough to get the string. Thanks for the assists though. I apparently lack rep to mark suggestions as helpful. Guess I just needed time away from this.
It might be too late to ask. Why are you not using the System.Diagnostics.EventLog class of .NET?
As far as I can see you have problems with the EventLog class. Did you try the EventLogReader class which does work for Windows Versions Vista and later?
With Reflector you can also find out how they do the marshalling. It looks like that it is not possible to let the marshaller do the complete work. They do read the data intoa a simple byte array and store it inside an EventLogEntry class which does read from the byte array directly the required data.
To read the replacement strings they do in the EventLogEntry class. The databuf member is the byte array from the native ReadEventLog call.
[MonitoringDescription("LogEntryReplacementStrings")]
public string[] ReplacementStrings
{
get
{
string[] strArray = new string[this.ShortFrom(this.dataBuf, this.bufOffset + 0x1a)];
int index = 0;
int offset = this.bufOffset + this.IntFrom(this.dataBuf, this.bufOffset + 0x24);
StringBuilder builder = new StringBuilder();
while (index < strArray.Length)
{
char ch = this.CharFrom(this.dataBuf, offset);
if (ch != '\0')
{
builder.Append(ch);
}
else
{
strArray[index] = builder.ToString();
index++;
builder = new StringBuilder();
}
offset += 2;
}
return strArray;
}
}
Yours,
Alois Kraus

Categories