Приложение хоста C # закрывается, когда вызов DLL PInvoke C / C ++ завершается (-1); - PullRequest
2 голосов
/ 18 октября 2019

У меня есть приложение C #, которое PInvokes скомпилировало DLL в VisualC, которая закрывает весь процесс при выполнении метода exit (-1) ;внутри DLL.

У меня нет исходного кода нативной DLL, и я понимаю, что DLL является частью процесса, и exit (n); закрывает хост-процесс.

Я получил такие рекомендации, как:

  • Изменить собственную DLL.
  • Использовать домен приложений (слишком много зависимостей, и приложениеслишком обширный).
  • Переопределить нативную DLL (она слишком обширна).

Вопрос:

  • Естьможно перехватить exit (n); в управляемом приложении C # и предотвратить закрытие всего приложения?
  • Или использовать какой-либо атрибут в методе, который вызывает собственную DLL?

1 Ответ

0 голосов
/ 18 октября 2019

Нет чистого способа перехватить выход из процесса. Одним из решений является исправление двоичного файла DLL, чтобы удалить этот вызов и заменить его nop или чем-то другим. Это было бы моей рекомендацией.

В противном случае, просто для удовольствия, вот пример кода, который использует ловушку для Windows API, чтобы сделать это. Я не рекомендую использовать его, поскольку он может иметь некоторые побочные эффекты (ваш звонок). Он использует MinHook двоичные файлы для наземного низкоуровневого хака / хука.

Вот код C dll, который вызывает exit:

extern "C" void _stdcall CallExit()
{
  printf("Let's try an exit...\n");
  exit(1234);
  printf("I survived this!\n");
}

Издесь C # caller

class Program
{
    static void Main()
    {
        using (var hook = new MinHook())
        {
            // hook ExitProcess which is the Windows API underneath exit.
            var fake1 = hook.CreateHook<ExitProcess>("kernel32.dll", nameof(ExitProcess), Fake);
            hook.EnableHook(fake1);

            // on recent Windows, we must also hook CorExitProcess
            // because exit (defined in ucrtbased) always try it before calling ExitProcess
            // and if you only hook ExitProcess, the process hangs for some reason
            var fake2 = hook.CreateHook<CorExitProcess>("mscoree.dll", nameof(CorExitProcess), Fake);
            hook.EnableHook(fake2);

            CallExit();
            Console.ReadLine();

            hook.DisableHook(fake1);
            hook.DisableHook(fake2);
        }
    }

    static void Fake(int exitCode)
    {
        Console.WriteLine("Hmmm... nope, I want to live forever. Exit code: " + exitCode);
    }

    private delegate void ExitProcess(int uExitCode);
    private delegate void CorExitProcess(int uExitCode);

    [DllImport("MyDll.dll")]
    private static extern void CallExit();
}

При запуске (проверено на Windows 10) это отображает это (по какой-то причине CorExitProcess вызывается дважды)

Let's try an exit...
Hmmm... nope, I want to live forever. Exit code: 1234
Hmmm... nope, I want to live forever. Exit code: 1234
Hmmm... nope, I want to live forever. Exit code: 1234
I survived this!

А вот некоторыеC # код утилиты взаимодействия для использования MinHook в C #:

public sealed class MinHook : IDisposable
{
    private static readonly Lazy<IntPtr> _module = new Lazy<IntPtr>(HookNativeProcs, true);
    public static string NativeDllPath { get; private set; }

    private IntPtr _handle;

    public MinHook()
    {
        var hook = _module.Value;
        CheckError(_MH_Initialize());
    }

    public IntPtr CreateHook<T>(string libraryName, string procName, T detour)
    {
        if (libraryName == null)
            throw new ArgumentNullException(nameof(libraryName));

        if (procName == null)
            throw new ArgumentNullException(nameof(procName));

        var module = LoadLibrary(libraryName);
        if (module == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        var address = GetProcAddress(module, procName);
        if (address == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        var ptr = Marshal.GetFunctionPointerForDelegate<T>(detour);
        CheckError(_MH_CreateHook(address, ptr, out IntPtr original));
        return address;
    }

    public void EnableHook(IntPtr hook)
    {
        if (hook == IntPtr.Zero)
            throw new ArgumentException(null, nameof(hook));

        CheckError(_MH_EnableHook(hook));
    }

    public void DisableHook(IntPtr hook)
    {
        if (hook == IntPtr.Zero)
            throw new ArgumentException(null, nameof(hook));

        CheckError(_MH_DisableHook(hook));
    }

    public void RemoveHook(IntPtr hook)
    {
        if (hook == IntPtr.Zero)
            throw new ArgumentException(null, nameof(hook));

        CheckError(_MH_RemoveHook(hook));
    }

    public void Dispose()
    {
        var handle = Interlocked.Exchange(ref _handle, IntPtr.Zero);
        if (handle != IntPtr.Zero)
        {
            CheckError(_MH_Uninitialize());
        }
    }

    private Exception CheckError(MH_STATUS status, bool throwOnError = true)
    {
        if (status == MH_STATUS.MH_OK)
            return null;

        var ex = new Exception(status.ToString());
        if (throwOnError)
            throw ex;

        return ex;
    }

    // with this code, we support AnyCpu targets
    private static IEnumerable<string> PossibleNativePaths
    {
        get
        {
            string bd = AppDomain.CurrentDomain.BaseDirectory;
            string rsp = AppDomain.CurrentDomain.RelativeSearchPath;
            string bitness = IntPtr.Size == 8 ? "64" : "86";
            bool searchRsp = rsp != null && bd != rsp;

            // look for an env variable
            string env = GetEnvironmentVariable("MINHOOK_X" + bitness + "_DLL");
            if (env != null)
            {
                // full path?
                if (Path.IsPathRooted(env))
                {
                    yield return env;
                }
                else
                {
                    // relative path?
                    yield return Path.Combine(bd, env);
                    if (searchRsp)
                        yield return Path.Combine(rsp, env);
                }
            }

            // look in appdomain path
            string name = "minhook.x" + bitness + ".dll";
            yield return Path.Combine(bd, name);
            if (searchRsp)
                yield return Path.Combine(rsp, name);

            name = "minhook.dll";
            yield return Path.Combine(bd, name); // last resort, hoping the bitness's right, we do not recommend it
            if (searchRsp)
                yield return Path.Combine(rsp, name);
        }
    }

    private static string GetEnvironmentVariable(string name)
    {
        try
        {
            string value = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
            if (value != null)
                return value;

            value = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User);
            if (value != null)
                return value;

            return Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Machine);
        }
        catch
        {
            // probably an access denied, continue
            return null;
        }
    }

    private static IntPtr HookNativeProcs()
    {
        var path = PossibleNativePaths.FirstOrDefault(p => File.Exists(p));
        if (path == null)
            throw new Exception("Cannot determine native MinHook dll path. Process is running " + (IntPtr.Size == 8 ? "64" : "32") + "-bit.");

        NativeDllPath = path;
        var module = LoadLibrary(path);
        if (module == IntPtr.Zero)
            throw new Exception("Cannot load native MinHook dll from path '" + path + "'. Process is running " + (IntPtr.Size == 8 ? "64" : "32") + "-bit.", new Win32Exception(Marshal.GetLastWin32Error()));

        _MH_Initialize = LoadProc<MH_Initialize>(module);
        _MH_Uninitialize = LoadProc<MH_Uninitialize>(module);
        _MH_CreateHook = LoadProc<MH_CreateHook>(module);
        _MH_RemoveHook = LoadProc<MH_RemoveHook>(module);
        _MH_EnableHook = LoadProc<MH_EnableHook>(module);
        _MH_DisableHook = LoadProc<MH_DisableHook>(module);
        return module;
    }

    private static T LoadProc<T>(IntPtr module, string name = null)
    {
        if (name == null)
        {
            name = typeof(T).Name;
        }

        var address = GetProcAddress(module, name);
        if (address == IntPtr.Zero)
            throw new Exception("Cannot load library function '" + name + "' from '" + NativeDllPath + "'. Please make sure MinHook is the latest one.", new Win32Exception(Marshal.GetLastWin32Error()));

        return (T)(object)Marshal.GetDelegateForFunctionPointer(address, typeof(T));
    }

    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_Initialize();
    private static MH_Initialize _MH_Initialize;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_Uninitialize();
    private static MH_Uninitialize _MH_Uninitialize;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_CreateHook(IntPtr pTarget, IntPtr pDetour, out IntPtr ppOriginal);
    private static MH_CreateHook _MH_CreateHook;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_RemoveHook(IntPtr pTarget);
    private static MH_RemoveHook _MH_RemoveHook;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_EnableHook(IntPtr pTarget);
    private static MH_EnableHook _MH_EnableHook;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate MH_STATUS MH_DisableHook(IntPtr pTarget);
    private static MH_DisableHook _MH_DisableHook;
}

public enum MH_STATUS
{
    // Unknown error. Should not be returned.
    MH_UNKNOWN = -1,

    // Successful.
    MH_OK = 0,

    // MinHook is already initialized.
    MH_ERROR_ALREADY_INITIALIZED,

    // MinHook is not initialized yet, or already uninitialized.
    MH_ERROR_NOT_INITIALIZED,

    // The hook for the specified target function is already created.
    MH_ERROR_ALREADY_CREATED,

    // The hook for the specified target function is not created yet.
    MH_ERROR_NOT_CREATED,

    // The hook for the specified target function is already enabled.
    MH_ERROR_ENABLED,

    // The hook for the specified target function is not enabled yet, or already
    // disabled.
    MH_ERROR_DISABLED,

    // The specified pointer is invalid. It points the address of non-allocated
    // and/or non-executable region.
    MH_ERROR_NOT_EXECUTABLE,

    // The specified target function cannot be hooked.
    MH_ERROR_UNSUPPORTED_FUNCTION,

    // Failed to allocate memory.
    MH_ERROR_MEMORY_ALLOC,

    // Failed to change the memory protection.
    MH_ERROR_MEMORY_PROTECT,

    // The specified module is not loaded.
    MH_ERROR_MODULE_NOT_FOUND,

    // The specified function is not found.
    MH_ERROR_FUNCTION_NOT_FOUND
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...