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

问题描述

我正在尝试将 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,false,ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,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,loaderDllPointer,(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,MemoryFreeType.MEM_RELEASE);
    }
}

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

#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,&pcchVersion,&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()] = '\0';

    g_hThread = (HANDLE)_beginthreadex(NULL,ThreadMain,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,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;
        }
    }
}

解决方法

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

  • 通过HANDLE(您可以使用它代替CreateProcess())和Process.GetProcessById()泄漏CreateRemoteThread()的输出。

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

  • 在释放分配的内存之前不等待 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,false,ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,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") + "\0";
            var bytes = Encoding.Unicode.GetBytes(loaderPath);

            var loaderPathPtr = VirtualAllocEx(
                processInfo.hProcess,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,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,loaderDllPointer,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,MemoryFreeType.MEM_RELEASE);
            }
        }
        finally
        {
            CloseHandle(processInfo.hProcess);
        }
    }
}

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