【问题标题】:CreateRemoteThread runs with no error but nothing happensCreateRemoteThread 运行没有错误,但没有任何反应
【发布时间】:2021-09-12 22:31:17
【问题描述】:

我正在尝试将 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 启动并运行后对其进行初始化。我希望至少在其中点击一个消息框调用,但没有显示任何内容。

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

    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;
        }
    }
}

【问题讨论】:

    标签: c# c++ memory process


    【解决方案1】:

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

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

    • 没有在远程进程中分配足够的字节来保存loaderPath 字符串,也没有将足够的字节复制到远程进程中。分配内存时需要将loaderPath.Length 乘以2(System.Char 的大小)。更好的解决方案是在调用VirtualAllocEx() 之前调用Encoding.Unicode.GetString(),然后分配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,
                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") + "\0";
                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()

    【讨论】:

    • 谢谢。使用上面的示例进行一些小的更改,我现在不再传递 C# 部分,而是得到:无法在进程中创建远程线程以开始执行 Loader.dll,错误代码:0
    • 阅读 CreateRemoteThread,听起来引发上述异常的 if 检查实际上是无效的,因为我没有将指针作为 lpThreadId 传递,因此不会返回任何内容。不幸的是,删除它会让我得到相同的结果,即 C# 应用程序以没有来自 C++ 部分的交互结束。
    • CreateRemoteThread()lpThreadId 参数是可选的,可以为空。但是,您不应该收到错误代码 0。你的DLLImport 声明CreateRemoteThread() 是什么样子的,它上面是否设置了SetLastError=true
    • 我已将导入添加到我的原始帖子中。
    • @JamieBrunton 您的大部分DllImport 语句都没有指定SetLastError=true,因此在调用这些函数时Marshal.GetLastWin32Error() 将不起作用。你需要修复它,然后当CreateRemoteThread() 失败时你应该得到一个真正的错误代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多