Introduction
RmThread is a project that I made based on one of three ideas from the Robert Kuster article, "Three Ways to Inject Your Code into Another Process". However, I didn't use any code. I wanted to learn about it, searched over the net, and got influenced by the CreateRemoteThread
& LoadLibrary
technique. The rest was a prescription of "quite right function calls" and MSDN.
The project is useful for someone who need to run some code in a neighbor process, but doesn't want to worry about remote thread technique development. He wants just to write the code to run remotely. The demo project, RmThread.exe, works precisely to do the dirty thing. You say what process to run and the DLL to load, and it starts the process and loads the DLL in the process context. The rest remains in the DLL code.
To write your DLL, there's a demo project that uses a technique that I created to run some code starting from the DllMain
execution (apart from another DLL function calling) and doesn't be a slave of its limitations (like: you can safely call only kernel32.dll functions).
Using the code
There're three useful functions that could be called from your program:
HANDLE CreateAndGetProcessGodHandle(LPCTSTR lpApplicationName,
LPTSTR lpCommandLine);
HMODULE RemoteLoadLibrary(HANDLE hProcess, LPCTSTR lpFileName);
BOOL RemoteFreeLibrary(HANDLE hProcess, HMODULE hModule);
Here's a simplified main routine showing how simple it is to use the functions:
hProc = CreateAndGetProcessGodHandle(tzProgPath, tzProgArgs);
if( hProc != NULL )
{
HMODULE hDll = RemoteLoadLibrary(hProc, tzDllPath);
if( hDll != NULL )
RemoteFreeLibrary(hProc, hDll);
CloseHandle(hProc);
}
The most complicated thing maybe what to do when your DLL is loaded. Considering that when it is called in your entry point, the DLL code has to follow some boring rules to avoid boring things like access violations (for more, read the DllMain
help at MSDN). I made an "alternative execution", creating a thread in the DllMain
function:
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call, LPVOID lpReserved) {
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
{
DWORD dwThrId;
BOOL bRes =
DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&g_hThrDllMain,
0,
FALSE,
0);
if( bRes == FALSE ) break;
CloseHandle(CreateThread(NULL,
0,
RmThread,
(LPVOID) LoadLibrary(g_tzModuleName),
0,
&dwThrId));
}
break;
The tasks for our newly created thread, by its turn are, to wait for the DllMain
finalization (we'd have its handle stored in g_hThrDllMain
), to do what it must to do (the all this stuff purpose), and to get back, releasing at the same time the DLL handle allocated for itself:
DWORD WINAPI RmThread(LPVOID lpParameter) {
HMODULE hDll = (HMODULE) lpParameter;
LPCTSTR ptzMsg =
_T("Congratulations! Did you call RmThread.dll successfully!");
WaitForSingleObject(g_hThrDllMain, INFINITE);
MessageBox(NULL,
ptzMsg,
g_tzModuleName,
MB_OK | MB_ICONINFORMATION);
FreeLibraryAndExitThread(hDll, 0);
}
The TODO mark is where your code must be written (you can pull out the MessageBox
, if you want). Like DllMain
code was previously executed, this part of the code is free to do what it wants to do in the neighbor process context.
An interesting detail is the requirement to call FreeLibraryAndExitThread
. In another way, if you call FreeLibrary
and then return, the code to be executed after that (a simple ret
assembly instruction) would be in an invalid memory address (the DLL code is no more loaded at the address space). So, the result could not be so delightful like the final solution.
Points of Interest
A boring problem that you can find is: if the DLL couldn't be loaded successfully, how can I get the error code? Unfortunately, there's no trivial way to get the error code returned from LoadLibrary
call. In order for the thread to start and finish in this API function, the LastError
code is lost (there's a LastError
for each thread in the process). Any idea?
History
- 2004.06.11 - 1.0.0.1 version released.
- 2004.06.13 - Support to VC6 project.
- 2006.08.17 - Support for VS2005.