I'm to trying to call FreeLibraryAndExitThread externally in another process (using CreateRemoteThread) so that I can unload a module I loaded in externally through LoadLibrary.
I understand that whilst CreateRemoteThread takes 1 parameter, you can provide it with a struct of multiple arguments if you need more than one.
If have tried the following which did not unload the module. In fact it seemed to do nothing.
Note I have removed all error checking to keep this post simple and short
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr VirtualAllocEx(IntPtr processHandle, IntPtr baseAddress, int size, int allocationType, int protection);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteProcessMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, int size, int bytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateRemoteThread(IntPtr processHandle, IntPtr threadAttributes, int stackSize, IntPtr startAddress, IntPtr parameter, int creationFlags, int threadId);
private struct FreeLibraryAndExitThreadParameters
{
internal IntPtr ModuleAddress;
internal int ExitCode;
}
var process = Process.GetProcessesByName("notepad")[0];
var freeLibraryAndExitThreadAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibraryAndExitThread");
// Get an instance of the module - dllName is the name of the module I am trying to unload
var module = process.Modules.Cast<ProcessModule>().SingleOrDefault(m => string.Equals(m.ModuleName, dllName, StringComparison.OrdinalIgnoreCase));
var freeLibraryAndExitThreadParameters = new FreeLibraryAndExitThreadParameters { ModuleAddress = module.BaseAddress, ExitCode = 0 };
// This code turns the struct into a byte array
var structureSize = Marshal.SizeOf(freeLibraryAndExitThreadParameters);
var structureBytes = new byte[structureSize];
var buffer = Marshal.AllocHGlobal(structureSize);
Marshal.StructureToPtr(freeLibraryAndExitThreadParameters, buffer, true);
Marshal.Copy(buffer, structureBytes, 0, structureSize);
Marshal.FreeHGlobal(buffer);
// Allocate memory in the remote process with commit and reserve allocation type and PageExecuteReadWrite permissions
var remoteAddress = VirtualAllocEx(process.Handle, IntPtr.Zero, structureSize, 0x01000 | 0x02000, 0x040);
// Write the structure into the remote process
WriteProcessMemory(process.Handle, remoteAddress, buffer, structureSize, 0);
// Finally call CreateRemoteThread to execute the function in the remote process
CreateRemoteThread(process.Handle, IntPtr.Zero, 0, freeLibraryAndExitThreadAddress, remoteAddress, 0, 0);
None of the pinvoke calls are actually failing and I can see that the bytes are being written into memory but nothing seems to happen after the remote thread is created - In my actual code I call WaitForSingleObject and the thread finishes its task also with no problem.
Can someone point out what I'm doing wrong and how I can fix this problem so that I can externally call FreeLibraryAndExitThread in a remote process?
It may be worth mentioning that I can use FreeLibrary with this method - it works fine(removing the struct as it only takes 1 parameter) but I specifically need to use FreeLibraryAndExitThread for the module I need to unload which is why I am not using the simpler FreeLibrary.
formally this is simply, all what we need
CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)FreeLibraryAndExitThread, hmod, 0, 0)
where hmod
is address of module in remote process. address of FreeLibraryAndExitThread
can be take from current process kernel32!FreeLibraryAndExitThread
- until kernel32.dll
is loaded at the same base address in all processes.
that
DECLSPEC_NORETURN
VOID
WINAPI
FreeLibraryAndExitThread(
_In_ HMODULE hLibModule,
_In_ DWORD dwExitCode
);
take 2 parameters in concrete case no problem. as result of call - CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)FreeLibraryAndExitThread, hmod, 0, 0)
the FreeLibraryAndExitThread
will be called via stdcal
l (WINAPI
) calling convention with single parameter - hmod
. the second parameter dwExitCode
will be undefined in this case, but it not play any role - any return code of thread is ok. system not interpret this value. and because this concrete api never return - different in parameter count also not play role.
another question - for what, which sense unload module in remote process. and if module really will be unloaded (the FreeLibrary
call only decrement module load count, so module not always will be unloaded during this call) and after this some code in remote process call code of unloading module - think not need explain what is be in this case