简体   繁体   中英

Running native code with CreateRemoteThread

A bit of background, this is my first ever question on stack overflow, so if it is difficult to answer, because of the quality of my question, I'm sorry.

Running on: Windows 11 10.0.22000 Build 22000, Intel x64, C++20, Mono 4.5, Visual Studio 2022 (if you need to know something else, then ask)

I have been struggling on this problem for a few days. I've been using the C++ Mono API to try to inject a method from a C# Class Library (DLL) into another process. It's been going great for a while now, I've managed to get the MonoMethod object from the method I want to inject, and I have found multiple ways of executing it, such as:

mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc)

mono_method_get_unmanaged_thunk(MonoMethod* method) (which returns a C/C++ function pointer)

mono_compile_method(MonoMethod* method) (I will come back to this in a moment)

but the problem is, that none of these have let me execute it remotely. I think the best shot I have with is the mono_compile_method(MonoMethod* method) , since the documentation tells me This JIT-compiles the method, and returns the pointer to the native code produced.

The Docs: http://docs.go-mono.com/

mono_runtime_invoke and mono_method_get_unmanaged_thunk: http://docs.go-mono.com/?link=xhtml%3Adeploy%2Fmono-api-methods.html (scroll down)

mono_compile_method is in the 'Unsorted' section in the left.

I thought I could just allocate some memory in the target process using VirtualAllocEx and WriteProcessMemory to somehow put the code inside the target program's address space and then execute it using CreateRemoteThread .

The VirtualAllocEx and WriteProcessMemory always succeed, same as the CreateRemoteThread , no errors are thrown, but it's only ever done one thing, which is crash the target process due to Access Violation .

This is my attempt (injection code):

MonoAssembly* Manager::Inject(const char* dllpath, char *ass, UINT len)
{

    MonoClass* classs;
    MonoMethod* method;

    MonoDomain* domain = mono_jit_init("ass");

    
    MonoAssembly* asse = mono_domain_assembly_open(domain, dllpath);
    if (!asse)
    {
        std::cout << "Failed to open assembly.";
        return NULL;
    }

    MonoImage* monoImage = mono_assembly_get_image(asse);
    if (!monoImage)
    {
        std::cout << "Failed to get image from assembly.";
        return NULL;
    }
    classs = mono_class_from_name(monoImage, this->namespaceName.c_str(), this->className.c_str());
    if (!classs)
    {
        std::cout << "Failed to get class from name.";
        return NULL;
    }
    
    method = mono_class_get_method_from_name(classs, this->method.c_str(), -1);
    if (!method)
    {
        std::cout << "Failed to get method from name.";
        return NULL;
    }

    
    MonoObject* excPtr = nullptr;

    int size = 1024 * 8;
    void* code;
    code = mono_compile_method(method);

    if (handle)
    {

        void* loc = VirtualAllocEx(handle, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        

        if (!loc)
        {
            std::cout << "VirtualAllocEx failed # " << GetLastError();
            return NULL;
        }


        SIZE_T written = 0;


        if (!WriteProcessMemory(handle, loc, code, size, &written))
        {
            std::cout << "WriteProcessMemory failed # " << GetLastError();
            return NULL;
        }
        std::cout << written << " bytes written.\n";


        HANDLE h = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)loc, NULL, 0, NULL);

        if (!h)
        {
            std::cout << "Failed to create remote thread # " << GetLastError();
            return NULL;
        }


        DWORD waitResult = WaitForSingleObject(h, -1);

        if (waitResult == WAIT_FAILED)
        {
            std::cout << "Failed to wait for remote thread. # " << GetLastError();
            return NULL;
        }


        DWORD error;

        if (!GetExitCodeThread(h, &error))
        {
            std::cout << "Failed to get thread exit code. # " << GetLastError();
            return NULL;
        }


        if (error == 3221225477)
        {
            std::cout << "An access violation occurred while executing.";
            return NULL;
        }


    } 
    else
    {
        std::cout << "Invalid handle! # " << GetLastError();
        return NULL;
    }
        


    CloseHandle(handle);
    return asse;

}

This is my Dll which contains the method which will be injected:

using System;

namespace TestDll
{
    public class Class1
    {
        static void PrintText()
        {
            Console.WriteLine("HELLO FROM DLL");
        }
    }
}

This is the code for my test target application:


using System;

namespace TestTarget
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "";
            while(true)
            {
                s = Console.ReadLine();
                if(s == "stop")
                {
                    break;
                }
            }
        }
    }
}

This is the message TestTarget throws when I try to inject:

The program '[9636] TestTarget.exe' has exited with code 3221225477 (0xc0000005) 'Access violation'.

I know the mono_compile_method works, because if I try to cast the return value to a function pointer and call it:

void (*code)() = (void(*)())mono_compile_method(method);
code();

It works as expected, and prints

HELLO FROM DLL

So, it says it returns a pointer to native code (which if I'm right should be directly executable by the CPU). I'm wondering why won't it work when I use it on CreateRemoteThread, since I can call it as a function pointer. I think I am calling it wrong/I wrote the pointer to the native code to the memory, which points to the native code in my process, but god knows what in the target process, since processes have their own address spaces.

If what I'm trying to do (inject the method into a remote process) is not possible like this, then please try and supply a corresponding technique. Thanks, if you have any questions about my question (:D), then please ask.

You need to write injected data and code(not function pointer as you pointed) to the remote process. There is an example of Three Ways to Inject . Be noticed The CreateRemoteThread & WriteProcessMemory Technique .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM