C# 主机应用在 PInvoke C/C++ DLL 调用退出时关闭 (-1);



我有一个 C# 应用程序,它在 VisualC 中调用一个编译的 DLL,当执行exit(-1(方法时关闭整个过程;在 DLL 内。

我没有本机DLL的源代码,我知道DLL是进程的一部分,然后退出(n(;关闭主机进程。

我收到了这样的建议:

  • 修改本机 DLL。
  • 使用应用程序域(太多了 依赖关系和应用程序过于广泛(。
  • 重新实现本机 DLL(它太广泛了(。

问题是:

  • 是否可以在 C# 托管应用程序中捕获exit(n(并阻止整个应用程序关闭?
  • 或者在调用本机 DLL 的方法中使用某些属性?

没有干净的方法来挂接进程退出。一种解决方案是修补 DLL 二进制文件以删除该调用并将其替换为 nop 或其他内容。这是我的建议。

否则,只是为了好玩,下面是一些使用 Windows API 上的钩子来执行此操作的示例代码。我不建议使用它,因为它可能会有一些副作用(您的电话(。它使用 MinHook 二进制文件进行地面低级黑客/钩子工作。

以下是一些调用exit的 C dll 代码:

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

这是 C# 调用方

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
}

最新更新