Перепишите код с использованием внешних функций `kernel32.dll` на чистом C# для работы с Mono

TL;DR

Я хочу реализовать функции из kernel32.dll на чистом C# (ReadProcessMemory и CloseHandle), потому что kernel32.dll зависит от Windows, и мне нужно запускать их в Mono под GNU/Linux.


У меня есть следующий код С# для чтения содержимого по определенному адресу данного процесса; он опирается на extern функций, основанных на 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)
            );
        }
    }
}

Это работает под Windows, вот пример ожидаемого результата:

00-04-A8-00

но при использовании Mono в GNU/Linux kernel32.dll отсутствует и выполняется код возвращает ошибку:

[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

Моя идея состояла в том, чтобы переписать на чистом C# 3 функции extern (OpenProcess, ReadProcessMemory и CloseHandle), чтобы не полагаться на 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 работает с Mono в GNU/Linux. Но, поскольку я не использую dwDesiredAccess и bInheritedHandle, я думаю, что это может не работать в Windows (у меня нет компьютера с Windows, чтобы проверить это).

Я понятия не имею, как написать две другие функции (ReadProcessMemory и CloseHandle) на чистом C# (или, по крайней мере, не зависящем от kernel32.dll), которые будут работать как в Windows , так и под GNU/Linux с использованием Mono .

Есть ли у кого-нибудь идеи реализации или просто классы, которые можно было бы использовать в этих функциях? Может быть, мне следует полагаться на другую DLL, присутствующую в Windows и Mono, которая уже выполняет эту работу? Я не эксперт в C#.

Редактировать

При замене [DllImport("kernel32.dll")] на [DllImport("kernel32")] я получил новую ошибку при работе с Mono под 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

Если бы это могло помочь.


person norbjd    schedule 26.04.2020    source источник
comment
Ну, kernel32.dll - это dll для Windows, вы не можете использовать ее в Linux.   -  person Pavel Anikhouski    schedule 26.04.2020
comment
@PavelAnikhouski Спасибо за ваш ответ, поэтому я пытаюсь переписать его на чистом C #. Нельзя ли прочитать содержимое по определенному адресу другого процесса, не используя kernel32.dll? Я хотел бы найти замену, которая будет работать под GNU/Linux с Mono.   -  person norbjd    schedule 26.04.2020
comment
Посмотрите внимательно на ошибку. Он находит dll, но не находит точку входа MyProject.ReadMemory.OpenProcess. Точка входа (имя метода) в dll — это просто OpenProcess. Вы неправильно вызываете метод.   -  person jdweng    schedule 26.04.2020
comment
@jdweng Спасибо за ваш ответ. Как мне тогда вызвать метод? Я был уверен, что ошибка возникла из-за того, что в Mono нет kernel32.dll, поэтому [DllImport("kernel32.dll")] не удалось импортировать функцию, поскольку она определена как extern.   -  person norbjd    schedule 26.04.2020
comment
Он должен работать на машине с окном (не Mac). Монолинукс построен на ядре Windows и содержит kernel32.dll. Не уверен, почему вы пытаетесь переписать код. Похоже, что из сообщения об ошибке вы вызываете свой статический метод вместо Windows dll.   -  person jdweng    schedule 26.04.2020
comment
@jdweng Хм, понятно. Как мне тогда вызвать OpenProcess например? Если я не определяю его как метод static extern, как я могу вызвать OpenProcess? Есть что импортировать? В каждом примере, который я видел, эти методы определены как static extern (например, stackoverflow.com/questions/34467311/) с DLLImport.   -  person norbjd    schedule 26.04.2020
comment
Этот вопрос также относится к этому: > stackoverflow.com/questions/11602781/. Обратите внимание, что я использую GNU/Linux, не Windows.   -  person norbjd    schedule 26.04.2020
comment
@jdweng Я отредактировал свой вопрос, заменив [DllImport("kernel32.dll")] на [DllImport("kernel32")], и получил новую ошибку при запуске с Mono в GNU/Linux. Я проверил и действительно не могу найти kernel32.dll в моем контейнере Docker для сборки (я строю его в контейнере, чтобы иметь свежую среду).   -  person norbjd    schedule 26.04.2020
comment
Удалив dll, вы можете усугубить проблему. Положить его обратно. См.: mono-project.com/docs/advanced/pinvoke.   -  person jdweng    schedule 26.04.2020
comment
@jdweng Я только что удалил расширение i.dll в директиве DLLImport. Я пытался просто посмотреть. В любом случае не получается :( У вас есть идея? Можем продолжить обсуждение здесь, чтобы избежать затянувшихся дискуссий в комментариях: chat.stackoverflow.com/rooms/212545/   -  person norbjd    schedule 26.04.2020
comment
Вы не можете вызывать функции Windows API в Linux. Конец истории.   -  person David Heffernan    schedule 26.04.2020
comment
@DavidHeffernan Спасибо за ваш комментарий. Вот почему я пытаюсь переписать их на чистом C#.   -  person norbjd    schedule 26.04.2020
comment
Вы не можете написать их на C#. Требуются системные вызовы. Я нашел вам несколько дубликатов.   -  person David Heffernan    schedule 26.04.2020