In the process of creating a thread function RtlCreateUserThread

 RtlCreateUserThread(hProcess, NULL, true, 0, 0, 0, (PVOID)GetProcAddress(hModule, function), NULL, &hThread, &cid); 

In the case of functions without parameters, such as ExitProcess from kernel32 , everything works fine, but when I try to transfer, for example, MessageBox from user32.dll , I don’t know how to pass the parameters of this function (3rd from the end RtlCreateUserThread parameter) such as parent, text, type etc. How to pass parameters ( PVOID StartParameter )?

Update

Tried to do so, declared function

 void message(){ MessageBoxA(NULL, "text", "aption", MB_OK); } 

And caused RtlCreateUserThread(hProcess, NULL, true, 0, 0, 0, &message, NULL, &hThread, &cid);

Process crashed

Doesn’t have the appropriate access

Update 2

Challenge doing in someone else's process. What is needed for now is just for common development. As for the rights, it is strange that before the call I receive an access token with TOKEN_ALL_ACCESS OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken); And I open the process with PROCESS_ALL_ACCESS HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);

  • one
    create your own wrapper function. It will receive a pointer to the structure with arguments. Inside, it will simply call the desired function and substitute the correct arguments in the right places. - KoVadim
  • And why do you need such a low-level function? Use normal runtime. Or do you call her in someone else's process? - VladD
  • And why do you use undocumented function? There, as it turns out, the third argument is not just a bool, but one-byte one. - KoVadim
  • And there is. Here are the EXTERN_C NTSTATUS WINAPI RtlCreateUserThread( HANDLE hProcess, SECURITY_DESCRIPTOR* pSec, BOOLEAN fCreateSuspended, ULONG StackZeroBits, SIZE_T* StackReserved, SIZE_T* StackCommit, void* StartAddress, void* Parametr, HANDLE* pThreadHandle, CLIENT_ID* pResult); As for the function, again for the sake of interest - Louis Cauchy

1 answer 1

And why are you not comfortable with the CreateRemoteThread function?

Well, firstly, you can pass only one parameter, so the function being started must have only one parameter. And to win it does not work.

Secondly, the CreateRemoteThread function has one subtlety, I am sure that with RtlCreateUserThread the situation is the same. The fact is that the StartAddress pointer must be the address of the function being started in the remote process space. That is, you should know in advance at what address the function being started is located in the remote process space. You call RtlCreateUserThread(hProcess, NULL, true, 0, 0, 0, &message, NULL, &hThread, &cid); specify the message() address from the space of the calling process. Therefore, there is a fall. For the same reason, it makes no sense to give the pointer to the local memory as a parameter.

But why then does not fall in the case of ExitProcces ? Yes, because ExitProcses in all processes is located at the same address - this is how the loader works. When a new process is started, it pulls the kernel32.dll library into the very first address space (or one of the first, I don’t remember) since system calls will not work without it. But this behavior of the loader is not specified and there is no guarantee that tomorrow MS will not change the logic and start kernel32.dll to start differently each time.

By the way, the CreateRemoteThread function is used when implementing a wonderful hacking dll implementation technique for intercepting process calls.