CreateRemoteThread 运行没有错误,但没有任何反应

CreateRemoteThread runs with no error but nothing happens

我正在尝试将 C++ dll 注入到进程中,并且正在使用 C# 控制台应用程序 运行 它。我的问题是程序 运行 已经完成,但似乎什么也没有发生,写入成功并且没有出现 win32 错误。

 class Program
{
    static void Main()
    {
        var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var bootstrapperSettingsFilePath = Path.Combine(currentFolder, "bootstrapperSettings.json");
        var bootstrapperSettings = JsonConvert.DeserializeObject<BootstrapperSettings>(File.ReadAllText(bootstrapperSettingsFilePath));

        var startupInfo = new STARTUPINFO();

        CreateProcess(
            bootstrapperSettings.TargetPath,
            null,
            IntPtr.Zero,
            IntPtr.Zero,
            false,
            ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,
            IntPtr.Zero,
            null, 
            ref startupInfo,
            out PROCESS_INFORMATION processInfo);

        Thread.Sleep(1000);
        var processHandle = Process.GetProcessById((int)processInfo.dwProcessId).Handle;
        var loaderPath = Path.Combine(currentFolder, "Loader.dll");
        var loaderPathPtr = VirtualAllocEx(
            processHandle, 
            (IntPtr)0, 
            loaderPath.Length, 
            MemoryAllocationType.MEM_COMMIT, 
            MemoryProtectionType.PAGE_EXECUTE_READWRITE);
        Thread.Sleep(500);

        int error = Marshal.GetLastWin32Error();
        if (error > 0)
            throw new InvalidOperationException($"Failed to allocate memory for Loader.dll, error code: {error}");
        var bytes = Encoding.Unicode.GetBytes(loaderPath);
        var bytesWritten = 0;
        WriteProcessMemory(processHandle, loaderPathPtr, bytes, bytes.Length, ref bytesWritten);
        Thread.Sleep(1000);

        error = Marshal.GetLastWin32Error();
        if (error > 0 || bytesWritten == 0)
            throw new InvalidOperationException($"Failed to write Loader.dll into the process, error code: {error}");

        var loaderDllPointer = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryW");

        Thread.Sleep(1000);

        error = Marshal.GetLastWin32Error();
        if (error > 0)
            throw new InvalidOperationException($"Failed to get memory address to Loader.dll in the process, error code: {error}");

        CreateRemoteThread(processHandle, (IntPtr)null, (IntPtr)0, loaderDllPointer, loaderPathPtr, 0, (IntPtr)null);

        Thread.Sleep(1000);

        error = Marshal.GetLastWin32Error();
        if (error > 0)
            throw new InvalidOperationException($"Failed to create remote thread to start execution of Loader.dll in the process, error code: {error}");

        VirtualFreeEx(processHandle, loaderPathPtr, 0, MemoryFreeType.MEM_RELEASE);
    }
}

还有 C++。这负责在 WPF 应用程序上调用方法并在 CLR 启动和 运行ning 后初始化它。我希望至少点击其中一个消息框调用,但没有显示任何内容。

#define WIN32_LEAN_AND_MEAN
#define FOR_DOTNET_4
#include <Windows.h>
#include <process.h>
#include <string>
#ifdef FOR_DOTNET_4
#include <metahost.h>
#else
#include <mscoree.h>
#endif
#include "CorError.h"

#pragma comment( lib, "mscoree" )

#define LOAD_DLL_FILE_NAME L"InjectionTest1.exe"
#define NAMESPACE_AND_CLASS L"InjectionTest1.Loader"
#define MAIN_METHOD L"Load"
#define MAIN_METHOD_ARGS L"NONE"

HMODULE g_myDllModule = NULL;

ICLRMetaHostPolicy* g_pMetaHost = NULL;
ICLRRuntimeInfo* g_pRuntimeInfo = NULL;
ICLRRuntimeHost* g_clrHost = NULL;

HANDLE g_hThread = NULL;
wchar_t* dllLocation = NULL;

#define MB(s) MessageBoxW(NULL, s, NULL, MB_OK);

unsigned __stdcall ThreadMain(void* pParam)
{
    MB(L"Test");
    HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHostPolicy, IID_ICLRMetaHostPolicy, (LPVOID*)&g_pMetaHost);

    if (FAILED(hr))
    {
        MB(L"Could not create instance of ICLRMetaHost");
        return 1;
    }
    
    DWORD pcchVersion = 0;
    DWORD dwConfigFlags = 0;

    hr = g_pMetaHost->GetRequestedRuntime(METAHOST_POLICY_HIGHCOMPAT,
        dllLocation, NULL,
        NULL, &pcchVersion,
        NULL, NULL,
        &dwConfigFlags,
        IID_ICLRRuntimeInfo,
        (LPVOID*)&g_pRuntimeInfo);

    if (FAILED(hr))
    {
        if (hr == E_POINTER)
        {
            MB(L"Could not get an instance of ICLRRuntimeInfo -- E_POINTER");
        }
        else if (hr == E_INVALIDARG)
        {
            MB(L"Could not get an instance of ICLRRuntimeInfo -- E_INVALIDARG");
        }
        else
        {
            wchar_t buff[1024];
            wsprintf(buff, L"Could not get an instance of ICLRRuntimeInfo -- hr = 0x%lx -- Is DomainManager.dll present?", hr);
            MB(buff);
        }

        return 1;
    }
    
    hr = g_pRuntimeInfo->BindAsLegacyV2Runtime();

    if (FAILED(hr))
    {
        MB(L"Failed to bind as legacy v2 runtime! (.NET 3.5 Mixed-Mode Support)");
        return 1;
    }

    hr = g_pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&g_clrHost);

    if (FAILED(hr))
    {
        MB(L"Could not get an instance of ICLRRuntimeHost!");
        return 1;
    }

    hr = g_clrHost->Start();

    if (FAILED(hr))
    {
        MB(L"Failed to start the CLR!");
        return 1;
    }

    DWORD dwRet = 0;
    hr = g_clrHost->ExecuteInDefaultAppDomain(dllLocation, NAMESPACE_AND_CLASS, MAIN_METHOD, MAIN_METHOD_ARGS, &dwRet);

    if (FAILED(hr))
    {
        MB(L"Failed to execute in the default app domain!");


        switch (hr)
        {
        case HOST_E_CLRNOTAVAILABLE:
            MB(L"CLR Not available");
            break;

        case HOST_E_TIMEOUT:
            MB(L"Call timed out");
            break;

        case HOST_E_NOT_OWNER:
            MB(L"Caller does not own lock");
            break;

        case HOST_E_ABANDONED:
            MB(L"An event was canceled while a blocked thread or fiber was waiting on it");
            break;

        case E_FAIL:
            MB(L"Unspecified catastrophic failure");
            break;

        default:
            char buff[128];
            sprintf(buff, "Result is: 0x%lx", hr);
            MessageBoxA(NULL, buff, "Info", 0);
            break;
        }

        return 1;
    }

    return 0;
}

void LoadClr()
{
    wchar_t buffer[255];
    if (!GetModuleFileNameW(g_myDllModule, buffer, 255))
        return;

    std::wstring modulePath(buffer);
    modulePath = modulePath.substr(0, modulePath.find_last_of('\') + 1);
    modulePath = modulePath.append(LOAD_DLL_FILE_NAME);
    dllLocation = new wchar_t[modulePath.length() + 1];
    MB((modulePath).c_str());
    wcscpy(dllLocation, modulePath.c_str());
    dllLocation[modulePath.length()] = '[=11=]';

    g_hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadMain, NULL, 0, NULL);
}

BOOL WINAPI DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
    g_myDllModule = hDll;
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        MB(L"Loading");
        LoadClr();
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        if (g_clrHost)
        {
            g_clrHost->Stop();
            g_clrHost->Release();
        }
        
        if (g_hThread)
        {
            TerminateThread(g_hThread, 0);
            CloseHandle(g_hThread);
        }
    }

    return TRUE;
}

进口:

using System;
using System.Runtime.InteropServices;

namespace Bootstrapper
{
    static class WinImports
    {
        [DllImport("kernel32.dll")]
        internal static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            ProcessCreationFlag dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool CloseHandle(IntPtr hHandle);


        [DllImport("kernel32.dll")]
        internal static extern IntPtr VirtualAllocEx(
            IntPtr hProcess,
            IntPtr dwAddress,
            int nSize,
            MemoryAllocationType dwAllocationType,
            MemoryProtectionType dwProtect);

        [DllImport("kernel32.dll")]
        internal static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            int dwSize,
            ref int lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr CreateRemoteThread(
            IntPtr hProcess,
            IntPtr lpThreadAttribute,
            IntPtr dwStackSize,
            IntPtr lpStartAddress,
            IntPtr lpParameter,
            uint dwCreationFlags,
            IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        internal static extern bool VirtualFreeEx(
            IntPtr hProcess,
            IntPtr dwAddress,
            int nSize,
            MemoryFreeType dwFreeType);

        internal enum MemoryAllocationType
        {
            MEM_COMMIT = 0x1000
        }

        internal enum MemoryProtectionType
        {
            PAGE_EXECUTE_READWRITE = 0x40
        }

        internal enum MemoryFreeType
        {
            MEM_RELEASE = 0x8000
        }

        internal enum ProcessCreationFlag
        {
            CREATE_DEFAULT_ERROR_MODE = 0x04000000
        }

        internal struct STARTUPINFO
        {
            public uint cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        internal struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }
    }
}

你的注入器代码有很多问题:

  • 通过CreateProcess()(您可以使用它代替Process.GetProcessById())和CreateRemoteThread().

    泄漏HANDLEs输出
  • 没有在远程进程中分配足够的字节来保存 loaderPath 字符串,也没有将足够的字节复制到远程进程中。分配内存时需要将loaderPath.Length乘以2(aSystem.Char的大小)。更好的解决方案是在调用 VirtualAllocEx() 之前调用 Encoding.Unicode.GetString(),然后分配 bytes.length 个字节。但是,请注意 LoadLibraryW() 需要一个以 null 结尾的字符串,但您并未将 null 终止符复制到远程进程中。

  • 在释放分配的内存之前不等待 LoadLibraryW() 实际完成 运行(即,远程线程终止)。

  • 错误处理不正确。

  • Thread.Sleep() 的调用是不必要的,应该删除。

尝试更像这样的东西:

class Program
{
    static void Main()
    {
        var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var bootstrapperSettingsFilePath = Path.Combine(currentFolder, "bootstrapperSettings.json");
        var bootstrapperSettings = JsonConvert.DeserializeObject<BootstrapperSettings>(File.ReadAllText(bootstrapperSettingsFilePath));

        var startupInfo = new STARTUPINFO();

        if (!CreateProcess(
            bootstrapperSettings.TargetPath,
            null,
            IntPtr.Zero,
            IntPtr.Zero,
            false,
            ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,
            IntPtr.Zero,
            null, 
            ref startupInfo,
            out PROCESS_INFORMATION processInfo))
        {
            throw new InvalidOperationException($"Failed to start process, error code: {Marshal.GetLastWin32Error()}");
        }

        CloseHandle(processInfo.hThread);

        try
        {
            var loaderPath = Path.Combine(currentFolder, "Loader.dll") + "[=10=]";
            var bytes = Encoding.Unicode.GetBytes(loaderPath);

            var loaderPathPtr = VirtualAllocEx(
                processInfo.hProcess,
                IntPtr.Zero,
                bytes.Length,
                MemoryAllocationType.MEM_COMMIT,
                MemoryProtectionType.PAGE_EXECUTE_READWRITE);

            if (loaderPathPtr == IntPtr.Zero)
                throw new InvalidOperationException($"Failed to allocate memory for Loader.dll, error code: {Marshal.GetLastWin32Error()}");

            try
            {
                var bytesWritten = 0;
                if (!WriteProcessMemory(processHandle, loaderPathPtr, bytes, bytes.Length, ref bytesWritten))
                {
                    throw new InvalidOperationException($"Failed to write Loader.dll path into the process, error code: {Marshal.GetLastWin32Error()}");
                }

                if (bytesWritten != bytes.Length)
                    throw new InvalidOperationException("Failed to write all bytes of Loader.dll path into the process");

                var loaderDllPointer = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryW");

                if (loaderDllPointer == IntPtr.Zero)
                    throw new InvalidOperationException($"Failed to get memory address to Loader.dll in the process, error code: {Marshal.GetLastWin32Error()}");

                var hRemoteThread = CreateRemoteThread(
                    processInfo.hProcess,
                    IntPtr.Zero,
                    0,
                    loaderDllPointer,
                    loaderPathPtr,
                    0,
                    IntPtr.Zero);

                if (hRemoteThread == IntPtr.Zero)
                {
                    throw new InvalidOperationException($"Failed to create remote thread to start execution of Loader.dll in the process, error code: {Marshal.GetLastWin32Error()}");
                }

                WaitForSingleObject(hRemoteThread, INFINITE);
                CloseHandle(hRemoteThread);
            }
            finally
            {
                VirtualFreeEx(processInfo.hProcess, loaderPathPtr, 0, MemoryFreeType.MEM_RELEASE);
            }
        }
        finally
        {
            CloseHandle(processInfo.hProcess);
        }
    }
}

也就是说,你的 C++ DLL 也是 doing things it shouldn't be doing in DllMain()