-3

I want to call a function in another process in the context of the same process with parameters. For this, the CreateRemoteThread() function seems to do the job. A code example can be found e.g. here.

Note: I don't want to implement the other provided solution since this one seems nice and to the point.

When it comes to passing parameters to the function, the parameters are only received as garbage.

My function takes an int and a double as parameters:

DLL_EXPORT void show_message_dialog(const int first_value, const double second_value)
{
    const auto text = std::to_string(first_value);
    const auto caption = std::to_string(second_value);
    MessageBoxA(nullptr, text.c_str(), caption.c_str(), MB_ICONINFORMATION);
}

As far as I understood it, one needs to define a struct holding the parameters and then passing the address of the struct like this:

struct my_parameters
{
    int i;
    double d;
};

auto params = my_parameters{3, 1.1};
const auto process_id = get_process_id_from_process_name("...");
auto * const process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
RemoteLibraryFunction(process_handle, "my_injected_dll.dll", "show_message_dialog", &params, sizeof(my_parameters), nullptr);

The message box quite clearly shows the wrong values but my exported DLL function is implemented correctly:

Is there anything wrong with the code or is there a recommended approach to debug this issue on an assembly level? The registers do not contain the right values.

BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
  • Your `show_message_dialog` function doesn't have `struct my_parameters *` as its (only) argument. – ssbssa Apr 09 '21 at 14:12
  • 3
    I don't understand how this struct turns into two arguments. You seem to have omitted a fair amount of code. – David Heffernan Apr 09 '21 at 14:20
  • The injector would need to load the DLL into the target process, then copy the struct data into the remote process, then call a remote function that can move that struct data into the call stack and then call the target DLL function. There is a lot of work involved in doing all of that. Much more than this question suggests. – Remy Lebeau Apr 09 '21 at 18:47
  • @DavidHeffernan: I'm not omitting any relevant code, I'm referring to the `RemoteLibraryFunction()` posted on the link above. It handles writing the arguments into the target process. If that isn't sufficient or wrong, it would be great to have a full example with writing the struct data into the call stack. If multiple arguments like in my example export function are not supported, that's good to know too. I just want to have a generic remote library function with parameter support to cover at least the basic parameter types. I don't understand the reason for the downvotes. – BullyWiiPlaza Apr 09 '21 at 20:29
  • We don't know what you mean by `.....` so, no, there isn't enough information. We don't know which function you are injecting. – David Heffernan Apr 09 '21 at 20:36
  • @DavidHeffernan: I injected a `DLL` earlier with exported functions such as the `show_message_dialog()` function. Now I just want to call the exported function(s) with parameters assuming I can already get the address of the exported function(s) from the injector process or any other C++ process. However, I updated my code to add more details to the function call to `RemoteLibraryFunction()`. – BullyWiiPlaza Apr 09 '21 at 20:48
  • OK, it's clear now that your entire problem is explained by the first comment above. – David Heffernan Apr 09 '21 at 21:51
  • @DavidHeffernan: Okay but I still want to support any type of function with multiple arguments, not just the ones accepting a struct as their single argument. I don't have control over every function I may want to call. – BullyWiiPlaza Apr 09 '21 at 21:57
  • 1
    Do you understand how function calls are implemented on your envisioned target architecture(s)? – IInspectable Apr 10 '21 at 07:07
  • Pass all the arguments in a struct then unpack them and call the target function. – David Heffernan Apr 10 '21 at 07:16

2 Answers2

1

The message box produces incorrect values. As mentioned in the comments, you cannot modify the parameter list of the function, but use code similar to the following:

DLL_EXPORT void show_message_dialog(LPVOID *myparam)
{
    my_parameters *p = (my_parameters *)myparam
    const auto text = std::to_string(p->i);
    const auto caption = std::to_string(p->d);
    MessageBoxA(nullptr, text.c_str(), caption.c_str(), MB_ICONINFORMATION);
}

but I still want to support any type of function with multiple arguments

You don't have a better way, you need to add the added parameters to the struct, and then get the corresponding other data through the structure in the function.

struct my_parameters
{
    int i;
    double d;
    char c;
    std::string s;
    //other data
};

DLL_EXPORT void show_message_dialog(LPVOID *myparam)
{
    my_parameters* p = (my_parameters*)myparam;
    int i = p->i;
    double d = p->d;
    char c = p->c;
    std::string s = p->s;
    ......
}
Zeus
  • 3,703
  • 3
  • 7
  • 20
0

Another way I found which allows you to truly specify any amount of parameters is to implement shellcode for setting up the parameters and the call to the function address while of course respecting the calling conventions of the platform and then the shellcode will be the function to be executed by CreateRemoteThread() instead of the actual target function. This method is more painful to fully implement but may be quite viable for some "edge cases" like only needing integer arguments.

BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
  • I am glad you have got your solution and thanks for your sharing, I would appreciate it if you mark them as answer and this will be beneficial to other community. – Zeus Apr 16 '21 at 08:26