0

TL;DR

I want to implement functions from kernel32.dll in pure C# (ReadProcessMemory and CloseHandle) because kernel32.dll is Windows specific and I need to run them with Mono under GNU/Linux.


I have the following C# code to read contents at a specific address of a given process; it relies on extern functions based on kernel32.dll :

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace MyProject
{
    public enum ProcessAccess : int
    {
        VM_READ = 0x0010,
    }

    class ReadMemory
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess,
            bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress,
            byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        static extern bool CloseHandle(IntPtr hObject);

        static string GetProcessContentAtAddress(string processName, int address, int contentLength) {
            Process process = Process.GetProcessesByName(processName)[0];
            IntPtr processHandle = OpenProcess(ProcessAccess.VM_READ, false, process.Id);

            UIntPtr addressPtr = (UIntPtr)address;
            byte[] buffer = new byte[contentLength];

            int lpNumberOfBytesRead = 0;

            ReadProcessMemory(processHandle, addressPtr, buffer,
                (IntPtr)buffer.Length, ref lpNumberOfBytesRead);

            string contents = BitConverter.ToString(buffer);

            CloseHandle(processHandle);

            return contents;
        }

        static void Main(string[] args)
        {
            // read 4 bytes at address 0x12345678 of my_process
            Console.WriteLine(
                GetProcessContentAtAddress("my_process", 0x12345678, 4)
            );
        }
    }
}

This works under Windows, here is an example of the expected result :

00-04-A8-00

but using Mono on GNU/Linux, kernel32.dll is not present and executing the code returns an error :

[ERROR] FATAL UNHANDLED EXCEPTION:
System.EntryPointNotFoundException: OpenProcess assembly:<unknown assembly> type:<unknown type> member:(null)
  at (wrapper managed-to-native) MyProject.ReadMemory.OpenProcess(MyProject.ProcessAccess,bool,int)
  at MyProject.ReadMemory.GetProcessContentAtAddress (System.String processName, System.Int32 address, System.Int32 contentLength) [0x00029] in <e1ed4e23c9fb45098da80208134b52bb>:0 
  at MyProject.ReadMemory.Main (System.String[] args) [0x00001] in <e1ed4e23c9fb45098da80208134b52bb>:0

My idea was to rewrite in pure C# the 3 extern functions (OpenProcess, ReadProcessMemory and CloseHandle), to avoid relying on kernel32.dll :

static IntPtr OpenProcess(ProcessAccess dwDesiredAccess,
    bool bInheritHandle, int dwProcessId) {
        return System.Diagnostics.Process
            .GetProcessById(dwProcessId).Handle;
}

static bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress,
    byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead) {
        // ???
        return true;
}

static bool CloseHandle(IntPtr hObject) {
    // ???
    return true;
}

OpenProcess appears to work with Mono on GNU/Linux. But, since I don't use dwDesiredAccess and bInheritedHandle, I think it may not work on Windows (I don't have a Windows machine to test it).

I have no idea how to write the other 2 functions (ReadProcessMemory and CloseHandle) in pure C# (or at least that does not rely on kernel32.dll) that will work both in Windows and under GNU/Linux using Mono.

Does anybody have ideas of implementation or just classes that would work to use in these functions? Maybe should I rely on another DLL present on Windows and Mono that already does that job? I am not an expert in C#.

Edit

When replacing [DllImport("kernel32.dll")] to [DllImport("kernel32")], I got a new error when running with Mono under GNU/Linux :

[ERROR] FATAL UNHANDLED EXCEPTION:
System.DllNotFoundException: kernel32 assembly:<unknown assembly> type:<unknown type> member:(null)
  at (wrapper managed-to-native) MyProject.ReadMemory.OpenProcess(MyProject.ProcessAccess,bool,int)
  at MyProject.ReadMemory.GetProcessContentAtAddress (System.String processName, System.Int32 address, System.Int32 contentLength) [0x00013] in <411171bde77146b8b0aa4953209bbc2e>:0 
  at MyProject.ReadMemory.Main (System.String[] args) [0x00001] in <411171bde77146b8b0aa4953209bbc2e>:0

If that could help.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
norbjd
  • 10,166
  • 4
  • 45
  • 80
  • Well, `kernel32.dll` is Windows specific dll, you can't use it on linux – Pavel Anikhouski Apr 26 '20 at 09:33
  • @PavelAnikhouski Thanks for your answer, that's why I try to rewrite it in pure C#. Is it not possible to read the contents at a specific address of another process by not using `kernel32.dll`? I'd like to find replacement that will work under GNU/Linux with Mono. – norbjd Apr 26 '20 at 09:34
  • Look at the error carefully. It is finding the dll but not finding the entry point MyProject.ReadMemory.OpenProcess. The entry point (name of the method) in the dll is simply OpenProcess. You are calling the method incorrectly. – jdweng Apr 26 '20 at 09:37
  • @jdweng Thanks for your answer. How should I call the method then? I was sure the error came from the fact that Mono does not have `kernel32.dll`, thus `[DllImport("kernel32.dll")]` failed to import the function since it's defined as `extern`. – norbjd Apr 26 '20 at 09:39
  • It should work on a window machine (not mac). A mono linux it built on a Windows kernel and has the kernel32.dll. Not sure why you are trying to rewrite the code. It looks like from the error message you are calling your static method instead of the windows dll. – jdweng Apr 26 '20 at 10:00
  • @jdweng Hmm, I see. How should I call `OpenProcess` for example then? If I don't define it as an `static extern` method, how could I call `OpenProcess`? Is there something to import? In every example I've seen, these methods are defined are `static extern` (for example https://stackoverflow.com/questions/34467311/c-sharp-readprocessmemory-how-to-read-a-64-bit-memory-address) with `DLLImport`. – norbjd Apr 26 '20 at 10:20
  • This question is also referring to this : https://stackoverflow.com/questions/11602781/windows-kernel32-functions-in-mono-on-linux. Note that I'm using GNU/Linux, **not Windows**. – norbjd Apr 26 '20 at 10:24
  • @jdweng I edited my question by replacing `[DllImport("kernel32.dll")]` by `[DllImport("kernel32")]` and got a new error when running **with Mono on GNU/Linux**. I've checked and indeed I can't find any `kernel32.dll` on my build Docker container (I'm building it in a container to have a fresh environment). – norbjd Apr 26 '20 at 10:36
  • By removing the dll you may the issue worse. Put it back. See : https://www.mono-project.com/docs/advanced/pinvoke/ – jdweng Apr 26 '20 at 11:59
  • @jdweng I just removed the extension i`.dll` n the `DLLImport` directive. I tried just to see. In any case, it does not work :( Do you have an idea? We can continue the discussion here to avoid extended discussions in comments : https://chat.stackoverflow.com/rooms/212545/discussion-between-norbjd-and-jdweng – norbjd Apr 26 '20 at 12:40
  • You can't call windows API functions on Linux. End of story. – David Heffernan Apr 26 '20 at 17:58
  • @DavidHeffernan Thanks for your comment. That's why I'm trying to rewrite them in pure C#. – norbjd Apr 26 '20 at 19:12
  • You can't write them in C#. Requires system calls. I've found you some duplicates. – David Heffernan Apr 26 '20 at 19:43

0 Answers0