Ejecting after injecting DLL from running process - c++

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.

Related

CreateRemoteThread + LoadLibraryA doesn't do anything despite succeeding

Despite the fact that memory allocation/write, finding LoadLibraryA address and creating a remote thread return valid (not NULL) results, absolutely nothing happens after that (mainly, the DllMain of the loaded DLL doesn't seem to get called).
#define PROC_NAME L"TestConsole.exe"
#define DLL_NAME "TestLib.dll\0"
HANDLE GetProcessByName(const wchar_t* name);
int main()
{
const char dllName[] = DLL_NAME;
int dllNameSize = strlen(dllName) + 1;
HANDLE process = GetProcessByName(PROC_NAME);
LPVOID allocMem = VirtualAllocEx(process, NULL, dllNameSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(process, allocMem, dllName, dllNameSize, NULL);
// Just to make sure
char buff[20];
ReadProcessMemory(process, allocMem, buff, dllNameSize, NULL);
printf("Data: %s\n", buff);
LPVOID libraryAddress =
(LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
HANDLE remoteThread = CreateRemoteThread(process, NULL, NULL, (LPTHREAD_START_ROUTINE)libraryAddress, allocMem, NULL, NULL);
}
HANDLE GetProcessByName(const wchar_t* name)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (wcscmp(entry.szExeFile, name) == 0)
{
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ParentProcessID);
}
}
}
return NULL;
}
Things I know/checked:
The thread gets created and a valid (not null) handle is returned. Despite it nothing happens.
I'm pretty sure that it's not DLL's fault. It's extremely simple, simply prints to console when it gets loaded and it works correctly when used simply with CreateThread().
Injector, DLL and the app to which I'm injecting are all 64 bit. If I chose any other platform (for all 3) everything works the same except for CreateRemoteThread(), which now fails.
The entry.th32ParentProcessID is the identifier of the process that created this process (its parent process). which means you did inject into the parent process of the target process (explorer.exe in my test). You should use entry.th32ProcessID instead.
In addition, the open permission PROCESS_ALL_ACCESS used in OpenProcess is too large, you only need to use what the CreateRemoteThread document requires: PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ

Dll injection - LoadLibraryA fails

I'm trying to inject a dll into a process. The dll does nothing except return TRUE.
I attached a debugger to the process that I want to inject into and confirmed that LoadLibraryA is called correctly but returns NULL.
Now I think that this might have something to do with my dll's dependencies. So I checked them and found out that it requires vcruntime140.dll.
The process that I want to inject my dll into does not load that dll.
#include "pch.h"
extern "C" int __stdcall APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
#include "Source.h"
const char* DllName = "InjectMe.dll";
int main()
{
DWORD processID = 0;
printf("Process ID: ");
scanf_s("%i", &processID);
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
if (handle == nullptr) {
printf("Process could not be opened.");
return -1;
}
LPVOID memDllName = VirtualAllocEx(handle, nullptr, strlen(DllName) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
assert(memDllName != nullptr);
assert(WriteProcessMemory(handle, memDllName, DllName, strlen(DllName) + 1, nullptr));
LPVOID loadLibraryAddr = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
assert(loadLibraryAddr != nullptr);
HANDLE thread = CreateRemoteThreadEx(handle, nullptr, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddr, memDllName, CREATE_SUSPENDED, nullptr, nullptr);
assert(thread != nullptr);
ResumeThread(thread);
DWORD returnCode = WaitForSingleObject(thread, 5000);
CloseHandle(thread);
if (returnCode == WAIT_TIMEOUT) {
printf("DLL was not loaded. Thread timed out.");
return -1;
}
else if (returnCode == WAIT_OBJECT_0) {
printf("DLL was successfully injected into the process.");
}
CloseHandle(handle);
std::cin.get();
return 0;
}
You must use a full file path not a relative file path when calling LoadLibrary()
const char* DllName = "InjectMe.dll";
needs to be changed to something like this
const char* DllName = "c:\\Users\User\\Desktop\\InjectMe.dll";
Also make sure you run as administrator if OpenProcess fails or sometimes you also need to use SeDebugPrivelage
In order to test if it is a pathing issue, try the following. Keep the
const char* DllName = "InjectMe.dll";
Then put the InjectMe.dll and your .exe in the same directory and try to run your exe. If the dll is loaded successfully, then it is a pathing issue.
To work around that, you can either specify the full path like GuidedHacking said, OR you can put your InjectMe.dll in the same directory as the .vcxproj and .cpp files (not where the .sln file is)

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.

Calling a function in an injected DLL?

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! =)

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.