Lets assume the remote thread procedure look like this:
DWORD __stdcall ThreadProc (void *pData) {
ThreadData *p = (ThreadData*)pData; // Contains function references and strings
p->MessageBoxW(NULL, p->Message, p->Title, MB_OK);
}
Then everything works fine and p->MessageBoxW(...) shows a message box as expected. But I don't want to call GetProcAddress for every function I use in the remote thread, so I thought I could create a function export within my module (EXE file creating the remote thread), so that the remote thread just calls LoadLibraryW to load my EXE file as module into the target process's address space and GetProcAddress to get the exported function's address in order to call it.
typedef void (__stdcall *_Test) ();
extern "C" void __stdcall Test () {
return;
}
DWORD __stdcall ThreadProc (void *pData) {
ThreadData *p = (ThreadData*)pData; // Contains function references and strings
HMODULE hLib = p->LoadLibraryW(p->LibPath);
_Test pTest = (_Test)p->GetProcAddress(hLib, p->ProcName);
pTest();
p->FreeLibrary(hLib);
return NULL;
}
This still works fine. But as soon as I change the exported function to
extern "C" void __stdcall Test () {
MessageBoxW(NULL, L"Message", L"Title", MB_OK);
return;
}
the target process suddenly crashes. Doesn't LoadLibrary resolve intermodular references? Is it possible to load my module into the target process's address space so that the exported function can be coded without passing all function addresses to it?
Additional information: For everyone copying the code, I had to disable incremental linking, build as release and add a module definition file to ensure that Test is exported as Test and not as _Test#SoMeJuNk. Just prepending __declspec(dllexport) didn't work for some reason. The module definition file looks like this
EXPORTS
Test#0
The ThreadData structure looks like this
typedef struct tagThreadData {
typedef BOOL (__stdcall *_FreeLibrary) (HMODULE);
typedef FARPROC (__stdcall *_GetProcAddress) (HMODULE, PSTR);
typedef HMODULE (__stdcall *_LoadLibraryW) (LPWSTR);
typedef DWORD (__stdcall *_MessageBoxW) (HWND, LPWSTR, LPWSTR, DWORD);
_FreeLibrary FreeLibrary;
_GetProcAddress GetProcAddress;
_LoadLibraryW LoadLibraryW;
_MessageBoxW MessageBoxW;
WCHAR LibPath[100];
WCHAR Message[30];
CHAR ProcName[10];
WCHAR Title[30];
} ThreadData, *PThreadData;
I came up with a temporary solution: Putting all remote code into an actual DLL. But putting the code into a DLL isn't my target, so if someone comes up with a clever solution, where the EXE file is the injector as well as the module being injected, I will mark the new answer as right.
Even though there are many tutorials on how to inject an actual DLL into another process's address space, I still give away my solution. I wrote my original solution only for UNICODE and 64-Bit, but I tried my best to make it work for both ASCII and UNICODE and 32-bit and 64-bit. But lets get started...
First of all, an explanation of the basic steps
Obtain handle to the target process with at least the following access rights
PROCESS_CREATE_THREAD
PROCESS_QUERY_INFORMATION
PROCESS_VM_OPERATION
PROCESS_VM_WRITE
PROCESS_VM_READ
Allocate memory for the remote thread procedure and the data and function pointers needed for loading the target dll and its "entrypoint" (I don't mean the actual entrypoint DllMain, but a function designed to be called from within the remote thread)
PVOID pThread = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
Copy remote thread procedure and important data over to the target process
WriteProcessMemory(hProc, pThread, ThreadProc, ThreadProcLen, NULL);
WriteProcessMemory(hProc, pParam, &data, sizeof(ThreadData), NULL);
Create remote thread. This thread will load the target dll into the target process's address space and calls its "entrypoint"
HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (PTHREAD_START_ROUTINE)pThread, pParam, NULL, NULL);
Optional: Wait until the thread returns
WaitForSingleObject(hThread, INFINITE);
DWORD threadExitCode;
GetExitCodeThread(hThread, &threadExitCode);
Close thread handle, release memory, Close process handle
CloseHandle(hThread);
VirtualFreeEx(hProc, pThread, 4096, MEM_RELEASE);
CloseHandle(hProc);
So here's my ThreadProc and ThreadData structure. ThreadProc is the remote thread procedure being called by CreateRemoteThread and should LoadLibrary the target dll, so it can call the target dll's "entrypoint". The ThreadData structure contains the addresses of LoadLibrary, GetProcAddress and FreeLibrary, the target dll's path TargetDll and the name of the "entrypoint" DllEntry.
typedef struct {
typedef BOOL (__stdcall *_FreeLibrary) (HMODULE);
typedef FARPROC (__stdcall *_GetProcAddress) (HMODULE, LPCH);
typedef HMODULE (__stdcall *_LoadLibrary) (LPTSTR);
typedef void (__stdcall *_DllEntry) ();
_LoadLibrary LoadLibrary;
TCHAR TargetDll[MAX_PATH];
_GetProcAddress GetProcAddress;
CHAR DllEntry[50]; // Some entrypoint designed to be
// called from the remote thread
_FreeLibrary FreeLibrary;
} ThreadData, *PThreadData;
// ThreadProcLen should be smaller than 3400, because ThreadData can
// take up to 644 bytes unless you change the length of TargetDll or
// DllEntry
#define ThreadProcLen (ULONG_PTR)2048
#define SPY_ERROR_OK (DWORD)0
#define SPY_ERROR_LOAD_LIB (DWORD)1
#define SPY_ERROR_GET_PROC (DWORD)2
DWORD ThreadProc (PVOID pParam) {
DWORD err = SPY_ERROR_OK;
PThreadData p = (PThreadData)pParam;
// Load dll to be injected
HMODULE hLib = p->LoadLibrary(p->TargetDll);
if (hLib == NULL)
return SPY_ERROR_LOAD_LIB;
// Obtain "entrypoint" of dll (not DllMain)
ThreadData::_DllEntry pDllEntry = (ThreadData::_DllEntry)p->GetProcAddress(hLib, p->DllEntry);
if (pDllEntry != NULL)
// Call dll's "entrypoint"
pDllEntry();
else
err = SPY_ERROR_GET_PROC;
// Free dll
p->FreeLibrary(hLib);
return err;
}
Then there's the actual code injecting the remote thread procedure into the target process's address space
int main(int argc, char* argv[]) {
// DWORD pid = atoi(argv[1]);
// Open process
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProc != NULL) {
// Allocate memory in the target process's address space
PVOID pThread = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pThread != NULL) {
PVOID pParam = (PVOID)((ULONG_PTR)pThread + ThreadProcLen);
// Initialize data to be passed to the remote thread
ThreadData data;
HMODULE hLib = LoadLibrary(TEXT("KERNEL32.DLL"));
data.LoadLibrary = (ThreadData::_LoadLibrary)GetProcAddress(hLib, "LoadLibrary");
data.GetProcAddress = (ThreadData::_GetProcAddress)GetProcAddress(hLib, "GetProcAddress");
data.FreeLibrary = (ThreadData::_FreeLibrary)GetProcAddress(hLib, "FreeLibrary");
FreeLibrary(hLib);
_tcscpy_s(data.TargetDll, TEXT("...")); // Insert path of target dll
strcpy_s(data.DllEntry, "NameOfTheDllEntry"); // Insert name of dll's "entrypoint"
// Write procedure and data into the target process's address space
WriteProcessMemory(hProc, pThread, ThreadProc, ThreadProcLen, NULL);
WriteProcessMemory(hProc, pParam, &data, sizeof(ThreadData), NULL);
// Create remote thread (ThreadProc)
HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (PTHREAD_START_ROUTINE)pThread, pParam, NULL, NULL);
if (hThread != NULL) {
// Wait until remote thread has finished
if (WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0) {
DWORD threadExitCode;
// Evaluate exit code
if (GetExitCodeThread(hThread, &threadExitCode) != FALSE) {
// Evaluate exit code
} else {
// The thread's exit code couldn't be obtained
}
} else {
// Thread didn't finish for some unknown reason
}
// Close thread handle
CloseHandle(hThread);
}
// Deallocate memory
VirtualFreeEx(hProc, pThread, 4096, MEM_RELEASE);
} else {
// Couldn't allocate memory in the target process's address space
}
// Close process handle
CloseHandle(hProc);
}
return 0;
}
The dll being injected has a real entrypoint DllMain that is called, when LoadLibrary loads the target dll into the target process's address space, and another "entrypoint" NameOfTheDllEntry called by the remote thread procedure (if it can be located in the first place)
// Module.def:
// LIBRARY NameOfDllWithoutExtension
// EXPORTS
// NameOfTheDllEntry
__declspec(dllexport) void __stdcall NameOfTheDllEntry () {
// Because the library is actually loaded in the target process's address
// space, there's no need for obtaining pointers to every function.
// I didn't try libraries other than kernel32.dll and user32.dll, but they
// should be working as well as long as the dll itself references them
// Do stuff
return;
}
BOOL APIENTRY DllMain (HMODULE hLib, DWORD reason, PVOID) {
if (reason == DLL_PROCESS_ATTACH)
DisableThreadLibraryCalls(hLib); // Optional
return TRUE;
}
Related
I'm trying to hook the DLL onto a notepad process, and then unhook it. When hooked, the DLL should cause the notepad to create a hidden file whenever the user clicks "Save As" (code for this is not shown). When unhooked, that should not be the case.
However, for some reason, while I got the message "DLL unhooking from process", the DLL still is not unhooked from the notepad process, and I know this because the notepad still creates the additional file when it should not have done that.
There are no error messages on the return values whatsover (at least none that I know of), so I removed most return value checks.
Hook
HANDLE hThread;
char * pid = argv[1];
DWORD user_pid = atoi(pid);
LPCSTR Dllpath = "C:\\Users\\xxx\\Desktop....\\MyDll.dll"
LPVOID pDllPath; // Address in remote process where Dllpath will be copied to.
HMODULE hKernel32 = GetModuleHandle("Kernel32");
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, user_pid);
char * command = argv[2];
if (strcmp(command,"hook") == 0){
SIZE_T bytesWritten = 0;
//Allocate memory to target process, and write dll to the allocated memory.
pDllPath = VirtualAllocEx(hProcess, NULL,strlen(DllPath)+1, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
// Write DLL hook name
WriteProcessMemory(hProcess, pDllPath, (LPCVOID)DllPath, strlen(Dllpath)+1,&bytesWritten);
// Load Dll to remote process
hThread = CreateRemoteThread(hProcess, NULL,0,(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA"), pDllPath,0,NULL);
WaitForSingleObject(hThread, INFINITE);
//Clean up
CloseHandle(hThread);
VirtualFreeEx(hProcess, pDllPath, strlen(DllPath+1, MEM_RELEASE);
else if (strcmp(command,"unhook")==0){
InlineUnhook(); //Call unhook inside the dll itself
}
}
Unhook (inside the dll itself)
HANDLE __stdcall InlineUnhook(){
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
LoadLibrary("C:\\Users\\xxx\\Desktop...\\MyDll.dll);
HMODULE hLibModule = GetModuleHandleA ("C:\\Users\\xxx\\Desktop...\\MyDll.dll);
HANDLE hThread = CreateRemoteThread(hProcess, NULL,0,(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "FreeLibraryAndExitThread"), (void *)(hLibModule,0),0,NULL);
if (hThread == NULL){
OutputDebugStringA("CreateRemoteThread failed.");
return -1;
}
else{
WaitForSingleObject(hThread, INFINITE);
//Clean up
CloseHandle(hThread);
OutputDebugStringA("DLL unhooking from process...");
return 0;
}
}
Your injector is calling InlineUnhook() directly, so it will act on the instance of the DLL that is loaded in the injector process, not the hooked process.
FreeLibraryAndExitThread() is not compatible with CreateRemoteThread(), so you can't use a remote thread to call it directly, like you can with LoadLibraryA().
Inside of the DLL itself, there is no need for it to call OpenProcess(), LoadLibrary(), or CreateRemoteThread() for itself. The DLL can simply call FreeLibraryAndExitThread() directly, like any other local function.
HINSTANCE hThisInst;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
hThisInst = hinstDLL;
...
return 1;
}
void __stdcall InlineUnhook()
{
FreeLibraryAndExitThread(hThisInst, 0);
}
Your injector will have to use a remote thread to call InlineUnhook() within the context of the hooked process, rather than calling it directly. That means you need to:
export InlineUnhook() from the DLL.
find the address of the loaded DLL within the hooked process. If your DLL is 32bit being loaded into a 32bit target process, that address can be obtained from GetExitCodeThread() when CreateRemoteThread() is done calling LoadLibraryA(). Otherwise, you will have to go hunting for the loaded address afterwards, such as by EnumProcessModules() or CreateToolhelp32Snapshot(TH32CS_SNAPMODULE).
find the address of the exported InlineUnhook() within the hooked process. Use LoadLibrary() and GetProcAddress() inside the injector to calculate the offset of InlineUnhook() within the DLL, and then apply that offset to the address of the loaded DLL within the hooked process.
use CreateRemoteThread() to call InlineUnhook() at that calculated address. You will have to change the signature of InlineUnhook() to be compatible with CreateRemoteThread(), eg:
DWORD __stdcall InlineUnhook(LPVOID)
{
FreeLibraryAndExitThread(hThisInst, 0);
return 1;
}
That's because your InlineUnhook call above calls the copy of the dll that is loaded into your injection process, not the one in the target process.
Here is some code this is supposed to inject my DLL and run it in notepad.exe but as the title states the CreateRemoteThread call returns null
MyGetProcessId works just fine I made it and checked its results to see if the pid was right and it was.
#define DLL_PATH "C:\\Users\\tkina\\Desktop\\3\\Dll1\\Debug\\Dll1.dll"
#include <Windows.h>
#include <iostream>
#include <tlhelp32.h>
DWORD MyGetProcessId(LPCTSTR ProcessName);
int main()
{
TCHAR Buffer[MAX_PATH];
DWORD err;
// Get full path of DLL to inject
DWORD pathLen = GetFullPathName(TEXT("mydll.dll"), MAX_PATH, Buffer, NULL);
PVOID addrLoadLibrary = (PVOID)GetProcAddress(GetModuleHandle(Buffer), "LoadLibraryA");
DWORD pID = MyGetProcessId(TEXT("Notepad.exe"));
// Open remote process
HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
if (!proc)
{
std::cout << "Could not open the process!\n";
system("pause");
}
// Get a pointer to memory location in remote process,
// big enough to store DLL path
PVOID memAddr = (PVOID)VirtualAllocEx(proc, 0, strlen(DLL_PATH)+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == memAddr) {
err = GetLastError();
return 0;
}
// Write DLL name to remote process memory
BOOL check = WriteProcessMemory(proc, memAddr, (LPVOID)DLL_PATH, strlen(DLL_PATH) + 1, NULL);
if (0 == check) {
err = GetLastError();
return 0;
}
// Open remote thread, while executing LoadLibrary
// with parameter DLL name, will trigger DLLMain
HANDLE hRemote = CreateRemoteThread(proc, 0, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("Kernel32.dll"),
"LoadLibraryA"), (LPVOID)memAddr, 0,0);
if (NULL == hRemote) {
err = GetLastError();
return 0;
}
WaitForSingleObject(hRemote, INFINITE);
check = CloseHandle(hRemote);
VirtualFreeEx(proc, memAddr, strlen(DLL_PATH) + 1, MEM_RELEASE);
system("pause");
return 0;
}
The call to GetLastError returned 5.
The lpStartAddress parameter of the function CreateRemoteProcess requires the address of the function in the virtual address space of the target process (notepad.exe). However, you are passing the address of this function in the address space of the injector program.
This wouldn't be a problem if the address of the function were the same in the virtual address space of both processes. In current versions of Windows, kernel32.dll is loaded to the same address for all 32-bit processes and it is also loaded to the same address for all 64-bit processes. However, the address it is loaded to is different for 32-bit and 64-bit processes. Therefore, the address of the function LoadLibraryA in kernel32.dll will also be different if one process is 32-bit and the other is 64-bit.
By passing the address of LoadLibraryA in its own address space to the call to CreateRemoteThread, your injector program is assuming that kernel32.dll is loaded to the same address in both its address space and in the address space of the target program (notepad.exe). However, as stated above, this assumption is only true if both processes are 32-bit or both are 64-bit.
Judging by your comments in the comments section, it seems that your injector program is 32-bit whereas the target process (notepad.exe) is 64-bit. Therefore, to fix this problem, you should change the build target in Visual Studio from "x86" (32-bit) to "x64" (64-bit).
Another problem is that the DLL that you are injecting must also be 64-bit. As stated in this StackOverflow question, it is not possible to load a 32-bit DLL as executable code into a 64-bit process.
I've been trying to figure out what's wrong for so long.
I've seen some people assign:
GetProcAddress(GetModuleHandle("KERNEL32.dll"), "LoadLibraryA")
And I wonder if that's what I have to do, but I just don't understand what that line of code does exactly. It has nothing to do with MY dll function, so why load it?
Main (console application A.K.A injector):
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
char* dllPath = "C:\\Users\\Kalist\\Desktop\\Projects\\DLL\\bin\\Debug\\DLL.dll";
typedef DWORD (WINAPI *pThreadFunc)();
char* ProcToInject = "calc.exe";
int main(){
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE procSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
DWORD procID;
if(procSnap){
if(Process32First(procSnap, &pe32)){
do{
if(!strcmp(pe32.szExeFile, ProcToInject)){
procID = pe32.th32ProcessID;
break;
}
}while(Process32Next(procSnap, &pe32));
}
CloseHandle(procSnap);
}
HANDLE procAccess = OpenProcess(PROCESS_ALL_ACCESS, false, procID);
void* memSpace = VirtualAllocEx(procAccess, NULL, strlen(dllPath)+1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(procAccess, memSpace, dllPath, strlen(dllPath)+1, NULL);
HINSTANCE getLibadd = LoadLibrary(dllPath);
pThreadFunc pThreadFuncVar = (pThreadFunc)GetProcAddress(getLibadd, "threadFunc");
CreateRemoteThread(procAccess, NULL, 0, (LPTHREAD_START_ROUTINE)pThreadFuncVar, memSpace, 0, NULL);
CloseHandle(procAccess);
}
DLL remote process:
#include <iostream>
#include <windows.h>
extern "C" DWORD WINAPI threadFunc(){
MessageBox(0, "Injection worked!", "Injection message", MB_OK);
return 0;
}
The problem with your code is that pThreadFuncVar contains the address of threadFunc in your injector process. However, your Dll.dll is not even loaded in the target process. Even if your dll were loaded, it would likely not be loaded at the same address, so the pThreadFuncVar address would still be meaningless in the target process.
Only a few essentials modules, like KERNEL32, are loaded at the same address in every process. So, if you use the address of LoadLibraryA for CreateRemoteThread, it will load the dll from the path which you copied into the target process's memory. This will in turn call the dll attach procedure of your dll, which is where you want to put the MessageBox call.
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.
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).