Calling a function in an injected DLL? - c++

Using C++, I have an application which creates a remote process and injects a DLL into it. Is there a way to get the remote application to execute a function exported from the DLL, from the application which created it? And is it possible to send parameters to that function? Please note that I am trying to stay away from doing anything within DllMain.

Note:
For a much better answer, please see my update posted below!
Okay so here's how I was able to accomplish this:
BOOL RemoteLibraryFunction( HANDLE hProcess, LPCSTR lpModuleName, LPCSTR lpProcName, LPVOID lpParameters, SIZE_T dwParamSize, PVOID *ppReturn )
{
LPVOID lpRemoteParams = NULL;
LPVOID lpFunctionAddress = GetProcAddress(GetModuleHandleA(lpModuleName), lpProcName);
if( !lpFunctionAddress ) lpFunctionAddress = GetProcAddress(LoadLibraryA(lpModuleName), lpProcName);
if( !lpFunctionAddress ) goto ErrorHandler;
if( lpParameters )
{
lpRemoteParams = VirtualAllocEx( hProcess, NULL, dwParamSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !lpRemoteParams ) goto ErrorHandler;
SIZE_T dwBytesWritten = 0;
BOOL result = WriteProcessMemory( hProcess, lpRemoteParams, lpParameters, dwParamSize, &dwBytesWritten);
if( !result || dwBytesWritten < 1 ) goto ErrorHandler;
}
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpFunctionAddress, lpRemoteParams, NULL, NULL );
if( !hThread ) goto ErrorHandler;
DWORD dwOut = 0;
while(GetExitCodeThread(hThread, &dwOut)) {
if(dwOut != STILL_ACTIVE) {
*ppReturn = (PVOID)dwOut;
break;
}
}
return TRUE;
ErrorHandler:
if( lpRemoteParams ) VirtualFreeEx( hProcess, lpRemoteParams, dwParamSize, MEM_RELEASE );
return FALSE;
}
//...
CStringA targetDll = "injected.dll"
// Inject the target library into the remote process
PVOID lpReturn = NULL;
RemoteLibraryFunction( hProcess, "kernel32.dll", "LoadLibraryA", targetDll.GetBuffer(MAX_PATH), targetDll.GetLength(), &lpReturn );
HMODULE hInjected = reinterpret_cast<HMODULE>( lpReturn );
// Call our exported function
lpReturn = NULL;
RemoteLibraryFunction( hProcess, targetDll, "Initialize", NULL, 0, &lpReturn );
BOOL RemoteInitialize = reinterpret_cast<BOOL>( lpReturn );
This can also be used to send parameters to a remote function via a pointer to a struct or union, and gets around having to write anything in DllMain.

So after some elaborate testing, it would seem that my previous answer is anything but foolproof(or even 100% functional, for that matter), and is prone to crashes. After giving it some thought, I've decided to take an entirely different approach to this... using Interprocess Communication.
Be aware... this method utilizes code in DllMain.
So don't go overboard, and be sure to follow safe practices when doing this, so that you don't end up in a deadlock...
Most notably, the Win32 API offers the following useful functions:
CreateFileMapping
MapViewOfFile
OpenFileMapping
With the use of these, we can simply tell our Launcher process exactly where our remote init function resides, straight from the injected dll itself...
dllmain.cpp:
// Data struct to be shared between processes
struct TSharedData
{
DWORD dwOffset = 0;
HMODULE hModule = nullptr;
LPDWORD lpInit = nullptr;
};
// Name of the exported function you wish to call from the Launcher process
#define DLL_REMOTEINIT_FUNCNAME "RemoteInit"
// Size (in bytes) of data to be shared
#define SHMEMSIZE sizeof(TSharedData)
// Name of the shared file map (NOTE: Global namespaces must have the SeCreateGlobalPrivilege privilege)
#define SHMEMNAME "Global\\InjectedDllName_SHMEM"
static HANDLE hMapFile;
static LPVOID lpMemFile;
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
TSharedData data;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
// Get a handle to our file map
hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, SHMEMSIZE, SHMEMNAME);
if (hMapFile == nullptr) {
MessageBoxA(nullptr, "Failed to create file mapping!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
return FALSE;
}
// Get our shared memory pointer
lpMemFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (lpMemFile == nullptr) {
MessageBoxA(nullptr, "Failed to map shared memory!", "DLL_PROCESS_ATTACH", MB_OK | MB_ICONERROR);
return FALSE;
}
// Set shared memory to hold what our remote process needs
memset(lpMemFile, 0, SHMEMSIZE);
data.hModule = hModule;
data.lpInit = LPDWORD(GetProcAddress(hModule, DLL_REMOTEINIT_FUNCNAME));
data.dwOffset = DWORD(data.lpInit) - DWORD(data.hModule);
memcpy(lpMemFile, &data, sizeof(TSharedData));
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
// Tie up any loose ends
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
break;
}
return TRUE;
UNREFERENCED_PARAMETER(lpReserved);
}
Then, from our Launcher application, we will do the usual CreateProcess + VirtualAllocEx + CreateRemoteThread trick to inject our Dll, making sure to pass in a pointer to a proper SECURITY_DESCRIPTOR as the 3rd parameter to CreateProcess, as well as passing the CREATE_SUSPENDED flag in the 6th parameter.
This is to help ensure that your child process will have the proper privileges to read and write to a global shared memory namespace, though there are also other ways to achieve this (or you could test without the global path altogether).
The CREATE_SUSPENDED flag will ensure that the dllmain entry point function would have finished writing to our shared memory before other libraries are loaded, which allows easier local hooking later on...
Injector.cpp:
SECURITY_ATTRIBUTES SecAttr, *pSec = nullptr;
SECURITY_DESCRIPTOR SecDesc;
if (InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION) &&
SetSecurityDescriptorDacl(&SecDesc, TRUE, PACL(nullptr), FALSE))
{
SecAttr.nLength = sizeof(SecAttr);
SecAttr.lpSecurityDescriptor = &SecDesc;
SecAttr.bInheritHandle = TRUE;
pSec = &SecAttr;
}
CreateProcessA(szTargetExe, nullptr, pSec, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
After injecting the DLL into the target process, all you need to do is use the same (more or less) file mapping code from your DLL project into your Launcher project (except for the part where you set the shared memory's contents, of course).
Then, calling your remote function is just a simple matter of:
// Copy from shared memory
TSharedData data;
memcpy(&data, lpMemFile, SHMEMSIZE);
// Clean up
UnmapViewOfFile(lpMemFile);
CloseHandle(hMapFile);
// Call the remote function
DWORD dwThreadId = 0;
auto hThread = CreateRemoteThread(hProcess, nullptr, 0, LPTHREAD_START_ROUTINE(data.lpInit), nullptr, 0, &dwThreadId);
Then you can ResumeThread on the target process's main thread, or from your remote function.
As an added bonus... Using this form of communication can also open up several doors for our Launcher process, as it can now directly communicate with the target process.
But again, be sure that you don't do too much in DllMain and, if at all possible, simply use your remote init function (where it is also safe to use named mutexes, for example) to create a separate shared memory map and continue communication from there.
Hope this helps someone! =)

Related

why openprocess function return different handle each time?

I want to get the process and thread handles about some games to inject dll, and I used OpenProcess() and OpenThread() to obtain these handles. But I found that I just get different handles each time I use these functions. And they are useless for me because they arent the true handles. Please tell me how I can get the true handles?
Thanks for your answers and comments! And I found that I did not describe my problem very well. Sorry.
Actually, if i used CreateProcess() funtion to launch a process and get handles from parameter lpProcessInformation pi. I could inject my dll into game through these handles named pi.hProcess and pi.hThread. And these handles seem like would not change during the program's runtime.
But if I got handles from OpenProcess() and OpenThread(), the process handle and thread handle were not same as the handle from CreateProcess() even though I got them in same run from a process.
So I thought that the handle from pi is the true handle, and the handle from OpenProcess() are fake. I dont know why they are different and why only handles from pi can work well.
Please tell me the difference about handles from OpenProcess() and
CreateProcess(). Or how I can get the handles same as CreateProcess() through PID.
This is the code how inject dll. And ony handles from pi.hProcess and pi.hThread can work.
void KInject::InjectDll(HANDLE hProcess, HANDLE hThread, ULONG_PTR param){
QueueUserAPC(
(PAPCFUNC)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"),
hThread,
(ULONG_PTR)param
);
}
void KInject::Inject2(HANDLE hProcess, HANDLE hThread, const char* szDLL ){
SIZE_T len = strlen(szDLL) + 1;
PVOID param = VirtualAllocEx(hProcess, NULL, len, MEM_COMMIT | MEM_TOP_DOWN /*MEM_RESERVE*/, PAGE_READWRITE);
if (param != NULL)
{
SIZE_T ret;
if (WriteProcessMemory(hProcess, param, szDLL, len, &ret)) {
InjectDll(hProcess, hThread, (ULONG_PTR)param );
}
}
}
This is the code how i get handles.
#include <Windows.h>
#include "tlhelp32.h"
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
HWND hq = FindWindow(NULL, "Temp");
RECT rect;
DWORD dwThreadID;
DWORD dwProcessId;
GetWindowThreadProcessId(hq, &dwProcessId);
GetWindowRect(hq, &rect);
DWORD a = GetWindowThreadProcessId(hq, &dwProcessId);
THREADENTRY32 te32 = { sizeof(te32) };
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (Thread32First(hThreadSnap, &te32))
{
do {
if (dwProcessId == te32.th32OwnerProcessID)
{
dwThreadID = te32.th32ThreadID;
break;
}
} while (Thread32Next(hThreadSnap, &te32));
}
CloseHandle(hThreadSnap);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);
CloseHandle(hThread);
CloseHandle(hProcess);
return 0;
}
There is nothing wrong with the API in this regard. Their return values are just what they are supposed to be, i.e. "handles" to the actual processes and threads. Exactly the same way as when you open a file, you get a handle to it, and if you open the same file multiple times, you may get different handles.
Having said that, just in the same way that files do have a more permanent name—which is their paths—processes and threads also do have a more permanent name and its called their "ID".
You can use the Win32 functions GetProcessId(HANDLE handle) and GetThreadId(HANDLE handle) to get to these more permanent identifiers.

Injected DLL and calling a function using CreateRemoteThread causes "has stopped working", what happens?

I`m trying to inject a DLL in a process and call a exported function in my DLL.
The DLL is injected alright with that code:
HANDLE Proc;
char buf[50] = { 0 };
LPVOID RemoteString, LoadLibAddy;
if (!pID)
return false;
Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
if (!Proc)
{
sprintf_s(buf, "OpenProcess() failed: %d", GetLastError());
printf(buf);
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
// Allocate space in the process for our DLL
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write the string name of our DLL in the memory allocated
WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL);
// Load our DLL
HANDLE hThread = CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL);
The module of my DLL is created OK, like you see in that image of Process Hacker (BootstrapDLL.exe):
My exported functions is ok too, like you see in the list of functions exported on Process Hacker (ImplantDotNetAssembly):
The problems, I think, happens on the offset calculation to get the address of the "ImplantDotNetAssembly", because everything above is alright and when I do the calculation I get the address of the "ImplantDotNetAssembly", but when I call CreateRemoteThread again to call it, the window "Has stopped working..." of the windows is showed and the process stoped. What`s happening?
Here is the code of the calculation of the offset:
DWORD_PTR hBootstrap = GetRemoteModuleHandle(ProcId, L"BootstrapDLL.exe");
DWORD_PTR offset = GetFunctionOffset(L"C:\\Users\\Acaz\\Documents\\Visual Studio 2013\\Projects\\Contoso\\Debug\\BootstrapDLL.exe", "ImplantDotNetAssembly");
DWORD_PTR fnImplant = hBootstrap + offset;
HANDLE hThread2 = CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)fnImplant, NULL, 0, NULL);
Here are the functions GetRemoteModuleHandle and GetFunctionOffset:
DWORD_PTR GetFunctionOffset(const wstring& library, const char* functionName)
{
// load library into this process
HMODULE hLoaded = LoadLibrary(library.c_str());
// get address of function to invoke
void* lpInject = GetProcAddress(hLoaded, functionName);
// compute the distance between the base address and the function to invoke
DWORD_PTR offset = (DWORD_PTR)lpInject - (DWORD_PTR)hLoaded;
// unload library from this process
FreeLibrary(hLoaded);
// return the offset to the function
return offset;
}
DWORD_PTR GetRemoteModuleHandle(const int processId, const wchar_t* moduleName)
{
MODULEENTRY32 me32;
HANDLE hSnapshot = INVALID_HANDLE_VALUE;
// get snapshot of all modules in the remote process
me32.dwSize = sizeof(MODULEENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
// can we start looking?
if (!Module32First(hSnapshot, &me32))
{
CloseHandle(hSnapshot);
return 0;
}
// enumerate all modules till we find the one we are looking for or until every one of them is checked
while (wcscmp(me32.szModule, moduleName) != 0 && Module32Next(hSnapshot, &me32));
// close the handle
CloseHandle(hSnapshot);
// check if module handle was found and return it
if (wcscmp(me32.szModule, moduleName) == 0)
return (DWORD_PTR)me32.modBaseAddr;
return 0;
}
If someone know what is happening, I'll be very grateful!
I cant`t even debug the "has stopped work.." error. When I clik in the DEBUG button on the window, the error throw again and everything stop.
Thank you.
NEVER inject managed assemblies. If for some reason you must inject code into another process, use native code with either NO C library or a STATIC C library.

How to inject a DLL into a Delphi program

I have a legacy application, which contains a grid with data I need to extract.
I don't have the code for that application and it is impossible to get the data out of it with normal means (like programmatically selecting all cells and copying them into clipboard).
So I decided to use DLL injection as described in section "II. The CreateRemoteThread & LoadLibrary Technique" at
http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
My plan is
To load a DLL into the address space of the legacy application.
Make the DLL read the data from the grid and write them out (e. g. via a named pipe).
The first step is to inject the DLL into the address space of the legacy application (step a) above).
I've written following code for that:
int InjectDll (HANDLE hProcess);
int _tmain(int argc, _TCHAR* argv[])
{
printf("DllInjector\n");
/**
* Find out PID of the legacy application (START)
*/
HWND windowHandle = FindWindowW(NULL, L"FORMSSSSS");
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
DWORD delphiAppProcessId = *processID;
/**
* Find out PID of the legacy application (END)
*/
printf("Process ID of legacy app: %lu\n", delphiAppProcessId);
// Now we need the handle of the legacy app
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, delphiAppProcessId);
if (hProcess != NULL)
{
printf("Found handle, ready for injection\n");
int result = InjectDll(hProcess);
CloseHandle( hProcess );
printf("Injection complete, result=%d\n", result);
}
else
{
printf("Handle not found\n");
}
system("pause");
return 0;
}
int InjectDll( HANDLE hProcess )
{
HANDLE hThread;
const char* const szLibPath = "D:\\mycompany\\SampleDll\\Debug\\SampleDll.dll";
void* pLibRemote = 0; // the address (in the remote process) where
// szLibPath will be copied to;
DWORD hLibModule = 0; // base adress of loaded module (==HMODULE);
HMODULE hKernel32 = ::GetModuleHandle(L"Kernel32");
// 1. Allocate memory in the remote process for szLibPath
// 2. Write szLibPath to the allocated memory
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE );
if( pLibRemote == NULL )
return false;
::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL);
// Load "LibSpy.dll" into the remote process
// (via CreateRemoteThread & LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryA"),
pLibRemote, 0, NULL );
if( hThread == NULL )
goto JUMP;
::WaitForSingleObject( hThread, INFINITE );
// Get handle of loaded module
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
JUMP:
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
if( hLibModule == NULL ) // (1)
return false;
// Unload "LibSpy.dll" from the remote process
// (via CreateRemoteThread & FreeLibrary)
hThread = ::CreateRemoteThread( hProcess,
NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"FreeLibrary"),
(void*)hLibModule,
0, NULL );
if( hThread == NULL ) // failed to unload
return false;
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
// return value of remote FreeLibrary (=nonzero on success)
return hLibModule;
}
Some comments:
The legacy program has the title "FORMSSSSS".
The sample DLL has following DllMain method:
-
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
{
OutputDebugStringA("DllMain called: ");
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
OutputDebugStringA("DLL_PROCESS_ATTACH\n");
case DLL_THREAD_ATTACH:
OutputDebugStringA("DLL_THREAD_ATTACH\n");
case DLL_THREAD_DETACH:
OutputDebugStringA("DLL_THREAD_DETACH\n");
case DLL_PROCESS_DETACH:
OutputDebugStringA("DLL_PROCESS_DETACH\n");
break;
}
return TRUE;
}
When it is called, a text is written into the standard output of the application.
When I run the program above (the one with _tmain method), I expect to see the text
DllMain called: DLL_PROCESS_ATTACH
in the console output (it means that the DLL injection was successful).
But it doesn't happen.
One potential cause is that the PID of the legacy application is determined incorrectly:
HWND windowHandle = FindWindowW(NULL, L"FORMSSSSS");
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
DWORD delphiAppProcessId = *processID;
But the value delphiAppProcessId is the same as the PID displayed in the task manager, so I can exclude this potential bug.
Using the debugger I found out that the execution stops at the line with comment (1):
JUMP:
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
if( hLibModule == NULL ) // (1)
return false;
What do I need to change in order for the sample DLL to be injected into the address space of the application with title "FORMSSSSS" ?
Update, 16.09.2012:
I replaced all occurrences of
sizeof(szLibPath)
by pathLength, where
const int pathLength = strlen(szLibPath)+1;
Now, in
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
// return value of remote FreeLibrary (=nonzero on success)
return hLibModule;
}
hLibModule is nonzero, which means that the injection was successful.
But I still can't see the log output of the sample DLL in the output of the program.
Update, 16.09.2012 (2):
When I
a) add a call to AllocConsole() in DllMain of the sample DLL,
b) rebuild it and
c) execute the injecting program,
then a console window appears, which has the same icon as the Delphi application.
When I remove AllocConsole from the DllMain function, and execute the injecting application, the console window does not appear.
So the injection might actually work.
The biggest problem that I can see is that sizeof(szLibPath) evaluates to the size of a pointer. Use strlen(szLibPath)+1 instead.
For sure that means that your injection will fail because the path that LoadLibraryA receives will be truncated. There may be other problems, but that's the place to start.

Calling function in injected DLL

I want to call a function in a remote process of an injected DLL that I've made.
I have successfully injected my DLL with:
CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"), pLibRemote, 0, NULL);
The DllMain is executed and the DLL is running in a stand-by mode. What I would like to do is somehow call the remotely loaded DLL in order to do some work.
I have tried exporting the function like this:
extern "C" __declspec(dllexport) void MyFunc(void)
and then executing the function like this:
CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("mydll"), "MyFunc"), NULL, 0, NULL);
but it results in a crash.
How can I solve this?
Calling GetModuleHandle as you have will get the base of the DLL as it is mapped into your process (if at all). So what you need to do is first make sure to export the function in the DLL. You can do as you have done or create a .def file as shown here. Thereafter:
In Theory
Inject the DLL to the target process and get the base address it was loaded at
Inject the DLL to the current process. Use GetProcAddress to find the offset between the exported function and the base of the DLL.
Add this offset to the base address obtained from step 1. CreateRemoteThread at this location.
In Practice
When doing your DLL injection, it is possible for you to get the base that your DLL is loaded into the target.
HMODULE hInjected;
hThread = CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)( GetProcAddress( hMod,
"LoadLibraryW" ) ), lpAddress, 0, NULL );
// Locate address our payload was loaded
if( hThread != 0 ) {
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, ( LPDWORD )&hInjected );
CloseHandle( hThread );
}
hInjected will be the base of the injected DLL. I then have another function:
void* GetPayloadExportAddr( LPCWSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName ) {
// Load payload in our own virtual address space
HMODULE hLoaded = LoadLibrary( lpPath );
if( hLoaded == NULL ) {
return NULL;
} else {
void* lpFunc = GetProcAddress( hLoaded, lpFunctionName );
DWORD dwOffset = (char*)lpFunc - (char*)hLoaded;
FreeLibrary( hLoaded );
return (DWORD)hPayloadBase + dwOffset;
}
}
What this does is first load the payload into our own virtual address space. Afterwards,
we can use GetProcAddress to get the address of the exported function. From this, we can get the offset of the function from the base of the DLL. Adding this offset to the hInjected we got earlier will tell us where the CreateRemoteThread call should be made. So you could make a call like so:
BOOL InitPayload( HANDLE hProcess, LPCWSTR lpPath, HMODULE hPayloadBase, HWND hwndDlg ) {
void* lpInit = GetPayloadExportAddr( lpPath, hPayloadBase, "Init" );
if( lpInit == NULL ) {
return FALSE;
} else {
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,
lpInit, hwndDlg, 0, NULL );
if( hThread == NULL ) {
return FALSE;
} else {
CloseHandle( hThread );
}
}
return TRUE;
}
This is all code that is ripped out of an old project I have. You're welcome to take the code and do whatever you want with it but I know if I were to rewrite the code now, I would do a lot of things differently.
Mike's answer works if you are injecting a 32bit DLL into a 32bit process.
If you want to inject a 64bit DLL into a 64bit process, you can't get the base address of the DLL from GetExitCodeThread since it will only give you the lower 32bits of the 64bit address.
To get the correct address in this case, you must write a block of code into the process that calls LoadLibrary (storing the result at a specific location in process memory), execute this block of code (using CreateRemoteThread), and then read back the address from that location using ReadProcessMemory.
You can find more details for this here (including PowerShell and ASM code) here:
http://clymb3r.wordpress.com/2013/05/26/implementing-remote-loadlibrary-and-remote-getprocaddress-using-powershell-and-assembly/
You can then compute the offset to your exported function the same way Mike describes, but watch out to store the difference in a 64bit value, and not in a DWORD (which is 32bit).

Ejecting after injecting DLL from running process

I wrote this function to inject DLL into running process:
DLL_Results CDLL_Loader::InjectDll()
{
DWORD ThreadTeminationStatus;
LPVOID VirtualMem;
HANDLE hProcess, hRemoteThread;
HMODULE hModule;
if (!isInit())
return NOT_INIT;
if (isInjected())
return DLL_ALREADY_HOOKED;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
if (hProcess == NULL)
return PROCESS_ERROR_OPEN;
VirtualMem = VirtualAllocEx (hProcess, NULL, strlen(DllFilePath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (VirtualMem == NULL)
return PROCESS_ERRORR_VALLOC;
if (WriteProcessMemory(hProcess, (LPVOID)VirtualMem, DllFilePath, strlen(DllFilePath), NULL) == 0)
{
VirtualFreeEx(hProcess, NULL, (size_t)strlen(DllFilePath), MEM_RESERVE|MEM_COMMIT);
CloseHandle(hProcess);
return PROCESS_ERROR_WRITE;
}
hModule = GetModuleHandle(L"kernel32.dll");
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA"),
(LPVOID)VirtualMem, 0, NULL);
if (hRemoteThread == NULL)
{
FreeLibrary(hModule);
VirtualFreeEx(hProcess, NULL, (size_t)strlen(DllFilePath), MEM_RESERVE | MEM_COMMIT);
CloseHandle(hProcess);
return PROCESS_ERROR_CREATE_RTHREAD;
}
WaitForSingleObject(hRemoteThread, INFINITE);
GetExitCodeThread(hRemoteThread, &ThreadTeminationStatus);
FreeLibrary(hModule);
VirtualFreeEx(hProcess, NULL, (size_t)strlen(DllFilePath), MEM_RESERVE | MEM_COMMIT);
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
injected = true;
return DLLHOOK_OK;
}
And It works great, but when i was trying to eject the dll i was unable to find information about unhooking.. i was trying to build some function to do it and i think i'm close
this is what i've got so far:
is that the right way? if so what parameter should i pass in createRemoteThread instade of VirtualMem (That was used in the injecting function)...
DLL_Results CDLL_Loader::EjectDll()
{
DWORD ThreadTeminationStatus;
HANDLE hProcess, hRemoteThread;
HMODULE hModule;
if (isInjected())
return DLLEJECT_OK;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
if (hProcess == NULL)
return PROCESS_ERROR_OPEN;
hModule = GetModuleHandle(L"kernel32.dll");
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary"),
/*(LPVOID)VirtualMem <- What do i need to send here?*/, 0, NULL);
if (hRemoteThread != NULL)
{
WaitForSingleObject(hRemoteThread, INFINITE);
GetExitCodeThread(hRemoteThread, &ThreadTeminationStatus);
}
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
injected = false;
return DLLEJECT_OK;
}
On 32-bit systems, the value of ThreadTeminationStatus after GetExitCodeThread contains the return value of LoadLibraryA in the remote process.
This is the module handle of the newly loaded dll.
You can use it as the parameter to FreeLibrary in the remote thread.
If you want to use the code on 64-bit Windows, the thread exit code is truncated to a 32-bit DWORD, so it's unusable.
You have to create a callable routine in the remote process (as Necrolis suggested) or resort to finding the module base of the DLL via psapi or the Toolhelp API (CreateToolhelp32Snapshot, Module32First, Module32Next).
You need to pass it the HANDLE of the dll you injected, else you can pass it VirtualMem but then your remote thread routine would need to be:
DWORD WINAPI UnloadDll(void* pMem)
{
FreeLibrary(GetModuleHandleA((const char*)pMem));
return 0;
}
However, generally the dll you inject should unload itself (see how DllMain works), either manually or automatically when the host is closed.