So I'm trying to write a DLL Injector in C# (as per previous post). At the moment, it appears to mostly be working. Running as a 32bit process, it can inject a 32bit dll into some other 32bit proces, and then eject it afterwards. Running as a 64bit process, it can inject a 64bit dll into another 64bit process, but the ejecting afterwards doesn't work.
I'm storing the handle retrieved upon injection in a hash-table (Dictionary) and using that handle as the lpParameter when calling CreateRemoteThread with FreeLibrary on ejection. This works fine in x86 world. I can see the Dll being injected in Sysinternals procexp, and then it is removed upon call to EjectLibrary. In x64 however, I can see the dll being injected, but nothing happens on ejection. No processes crash, and no errors are thrown either. Any thoughts on what I'm doing wrong? Relevant code is attached. (For the record, my DllMain simply returns true).
Injection:
Code:
public void InjectLibrary(string libPath)
{
if (!File.Exists(libPath))
throw new FileNotFoundException("Unable to find the library to inject", libPath);
string fullPath = Path.GetFullPath(libPath);
string libName = Path.GetFileName(fullPath);
if (injectedModules.ContainsKey(libName))
throw new Exception(string.Format("The library {0} has already been injected into this process.", libPath));
// declare resources that need to be freed in finally
IntPtr pLibRemote = IntPtr.Zero; // pointer to allocated memory of lib path string
IntPtr hThread = IntPtr.Zero; // handle to thread from CreateRemoteThread
IntPtr pLibFullPathUnmanaged = Marshal.StringToHGlobalUni(fullPath); // unmanaged C-String pointer
try
{
uint sizeUni = (uint)Encoding.Unicode.GetByteCount(fullPath);
// Get Handle to Kernel32.dll and pointer to LoadLibraryW
IntPtr hKernel32 = Imports.GetModuleHandle("Kernel32");
if (hKernel32 == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr hLoadLib = Imports.GetProcAddress(hKernel32, "LoadLibraryW");
if (hLoadLib == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
// allocate memory to the local process for libFullPath
pLibRemote = Imports.VirtualAllocEx(_process.Handle, IntPtr.Zero, sizeUni, AllocationType.Commit, MemoryProtection.ReadWrite);
if (pLibRemote == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
// write libFullPath to pLibPath
int bytesWritten;
if (!Imports.WriteProcessMemory(_process.Handle, pLibRemote, pLibFullPathUnmanaged, sizeUni, out bytesWritten) || bytesWritten != (int)sizeUni)
throw new Win32Exception(Marshal.GetLastWin32Error());
// load dll via call to LoadLibrary using CreateRemoteThread
hThread = Imports.CreateRemoteThread(_process.Handle, IntPtr.Zero, 0, hLoadLib, pLibRemote, 0, IntPtr.Zero);
if (hThread == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (Imports.WaitForSingleObject(hThread, (uint)ThreadWaitValue.Infinite) != (uint)ThreadWaitValue.Object0)
throw new Win32Exception(Marshal.GetLastWin32Error());
// get address of loaded module
IntPtr hLibModule;// = IntPtr.Zero;
if (!Imports.GetExitCodeThread(hThread, out hLibModule))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (hLibModule == IntPtr.Zero)
throw new Exception("Code executed properly, but unable to get an appropriate module handle, possible Win32Exception", new Win32Exception(Marshal.GetLastWin32Error()));
injectedModules.Add(libName, hLibModule);
}
finally
{
Marshal.FreeHGlobal(pLibFullPathUnmanaged); // free unmanaged string
Imports.CloseHandle(hThread); // close thread from CreateRemoteThread
Imports.VirtualFreeEx(_process.Handle, pLibRemote, 0, AllocationType.Release); // Free memory allocated
}
}
Ejection:
Code:
public void EjectLibrary(string libName)
{
string libSearchName = File.Exists(libName) ? Path.GetFileName(Path.GetFullPath(libName)) : libName;
if (!injectedModules.ContainsKey(libSearchName))
throw new Exception("That module has not been injected into the app");
// resources that need to be freed
IntPtr hThread = IntPtr.Zero;
try
{
// get handle to kernel32 and FreeLibrary
IntPtr hKernel32 = Imports.GetModuleHandle("Kernel32");
if (hKernel32 == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr hFreeLib = Imports.GetProcAddress(hKernel32, "FreeLibrary");
if (hFreeLib == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
hThread = Imports.CreateRemoteThread(_process.Handle, IntPtr.Zero, 0, hFreeLib, injectedModules[libSearchName], 0, IntPtr.Zero);
if (hThread == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (Imports.WaitForSingleObject(hThread, (uint)ThreadWaitValue.Infinite) != (uint)ThreadWaitValue.Object0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
Imports.CloseHandle(hThread);
}
}