/* Copyright (c) 2009, J. "abort" Dijkstra
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY J. Dijkstra ''AS IS'' */
#include "injector.h"
// Constructor
CInjector::CInjector(DWORD dwPid, LPWSTR pwszLibrary, bool fAdjustPrivileges = false, bool fExecuteFunctions = false) {
if (dwPid == 0)
throw string("No Process ID specified");
if (!pwszLibrary)
throw string("No library specified");
// Open the process handle by it's Process ID
hProcess = OpenProcess(INJECT_ACCESS, FALSE, dwPid);
if (!hProcess)
throw string("Failed to open process");
// Calculate memory size for the library string, including terminating null
int nSize = (lstrlenW(pwszLibrary)+1)*sizeof(WCHAR);
if (nSize == sizeof(WCHAR))
throw string("Invalid library specified");
// Try to allocate memory for the library string
try {
this->pwszLibrary = new WCHAR[nSize];
} catch (bad_alloc& e) {
UNREFERENCED_PARAMETER(e);
throw string("Insufficient memory to allocate library string");
}
// Store the library string in the current instance
if (wcscpy_s(this->pwszLibrary, nSize, pwszLibrary))
throw string("Failed to copy library string");
// Adjust privileges if defined and pass on the exceptions
if (fAdjustPrivileges)
{
string szError;
if (!adjustPrivileges(szError))
throw szError;
}
// If the class user wants to execute functions
if (fExecuteFunctions)
{
if (GetFileAttributesW(pwszLibrary) == INVALID_FILE_ATTRIBUTES)
throw string("Failed to retrieve Dynamic Link Library");
hLibrary = LoadLibraryW(pwszLibrary);
if (!hLibrary)
throw string("Failed to load Dynamic Link Library");
this->fExecuteFunctions = true;
}
dwBaseAddress = 0;
}
// Free up memory after destroying the instance
CInjector::~CInjector() {
if (hProcess)
CloseHandle(hProcess);
if (fExecuteFunctions && hLibrary)
FreeLibrary(hLibrary);
delete[] pwszLibrary;
}
// Find the Process ID by executable name
DWORD CInjector::getProcessIdByName(LPWSTR pwszProcess, string& szError) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (!hSnap || hSnap == INVALID_HANDLE_VALUE)
{
szError = "Failed to create snapshot of processes";
return 0;
}
PROCESSENTRY32W pe;
pe.dwSize = sizeof(PROCESSENTRY32W);
DWORD dwPid = 0;
BOOL bResult = Process32FirstW(hSnap, &pe);
while (bResult)
{
if (!lstrcmpW(pe.szExeFile, pwszProcess))
{
dwPid = pe.th32ProcessID;
break;
}
bResult = Process32NextW(hSnap, &pe);
}
CloseHandle(hSnap);
return dwPid;
}
// Adjust the privileges to enable access to processes like windows services
bool CInjector::adjustPrivileges(string& szError) {
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
// Open process token to adjust the privileges
if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
szError = "Failed to open process token";
return false;
}
// Request the current privileges
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
szError = "Failed to lookup current privilege";
return false;
}
// Change current privilege variables
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
// enable the privilege to access system services
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Apply the changed privileges to the current process token
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
szError = "Failed to adjust token privileges";
return false;
}
return true;
}
// Inject the library into the open process
bool CInjector::injectLibrary(string& szError) {
HANDLE hThread; // Remote thread handle
LPWSTR pwszLibString; // Parameter for remote LoadLibrary function
LPVOID pLoadLibrary; // Pointer to remote LoadLibrary function
HINSTANCE hKernelLib; // Instance of kernel library
DWORD dwExitCode; // Remote thread exit code
size_t nSize; // Size of zero terminated library path
// Calculate length of the library string
nSize = (lstrlenW(pwszLibrary)+1)*sizeof(WCHAR);
if (nSize == sizeof(WCHAR))
{
szError = "Failed to retrieve length of library string";
return false;
}
// Retrieve handle to the kernel library
hKernelLib = GetModuleHandleW(KERNEL_LIB);
if (!hKernelLib)
{
szError = "Failed to retrieve kernel32 handle";
return false;
}
// Get the address of the loadlibrary function
pLoadLibrary = (LPVOID)GetProcAddress(hKernelLib, LOADLIBRARY_FUNCTION);
if (!pLoadLibrary)
{
szError = "Failed to retrieve remote load library address";
return false;
}
// Allocate remote memory for the library string
pwszLibString = (LPWSTR)VirtualAllocEx(hProcess, NULL, nSize, MEM_COMMIT, PAGE_READWRITE);
if (!pwszLibString)
{
szError = "Failed to allocate remote memory";
return false;
}
// Write the library string to the allocated remote memory
if (!WriteProcessMemory(hProcess, pwszLibString, (LPVOID)pwszLibrary, nSize, NULL))
{
szError = "Failed to write remote process memory";
return false;
}
// Start a remote loadlibrary thread with the library string as a parameter
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pwszLibString, 0, NULL);
if (!hThread)
{
szError = "Failed to create remote thread";
return false;
}
// Wait for the remote thread to be terminated
if (WaitForSingleObject(hThread, THREAD_WAIT) != WAIT_OBJECT_0)
{
szError = "Failed to wait for thread to exit";
return false;
}
// Retrieve the loadlibrary exit code (which is the address of the library)
if (!GetExitCodeThread(hThread, &dwExitCode))
{
szError = "Failed to retrieve thread exit code";
return false;
}
// Free up the remotely allocated memory
// No need to check for errors as it has no influence on the injection
VirtualFreeEx(hProcess, pwszLibString, 0, MEM_RELEASE);
if (!dwExitCode)
{
szError = "Failed to inject library into process";
return false;
}
// Store the dll base address in the current instance
dwBaseAddress = dwExitCode;
return true;
}
// Retrieve the base address of the injected library
DWORD CInjector::getBaseAddress() {
return dwBaseAddress;
}
// Start a remote library function
DWORD CInjector::startFunction(LPVOID pFunction, LPVOID pParameter, string& szError) {
if (!fExecuteFunctions)
{
szError = "This instance has no remote function execution enabled";
return 0;
}
if (!dwBaseAddress)
{
szError = "Can not execute function as the library is not injected yet";
return 0;
}
HANDLE hThread;
DWORD dwExitCode = 0;
// Calculate the absolute function address of the function
pFunction = (LPVOID)((DWORD)pFunction + (DWORD)dwBaseAddress);
// Start the remote function
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunction, NULL, 0, NULL);
if (!hThread)
{
szError = "Failed to create remote thread";
return 0;
}
// Wait for the remote thread to exit
if (WaitForSingleObject(hThread, THREAD_WAIT) != WAIT_OBJECT_0)
{
szError = "Failed to wait for remote thread";
return 0;
}
// Retrieve the exit code of the remote thread
if (!GetExitCodeThread(hThread, &dwExitCode))
{
szError = "Failed to retrieve thread exit code";
return 0;
}
// Return thread exit code to be able to check for successful execution
return dwExitCode;
}
// Retrieve the relative address of an exported function
LPVOID CInjector::getRelativeAddress(char* pwszExportName, string& szError) {
if (!fExecuteFunctions || !hLibrary)
{
szError = "This instance has no remote function execution enabled";
return NULL;
}
// Retrieve the absolute address of the given function
LPVOID pFunction = GetProcAddress(hLibrary, pwszExportName);
if (!pFunction)
{
szError = "Failed to retrieve DLL Entry Function";
return NULL;
}
// Calculate the relative function address of the function
pFunction = (LPVOID)((DWORD)pFunction - (DWORD)hLibrary);
return pFunction;
}
#ifndef INJECTOR_H_
#define INJECTOR_H_
#define STRICT
#define WIN32_LEAN_AND_MEAN
#define DEFAULTBASE_ADDRESS 268435456
#define KERNEL_LIB L"kernel32.dll"
#define LOADLIBRARY_FUNCTION "LoadLibraryW"
#define INJECT_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
#define THREAD_WAIT 5000
#include <windows.h>
// Required for making a snapshot of the processes
#include <tlhelp32.h>
// Required for errors
#include <string>
using namespace std;
class CInjector {
public:
// Have an instance to store and maintain a handle to the process
CInjector(DWORD dwPid, LPWSTR pwszLibrary, bool fAdjustPrivileges, bool fExecuteFunctions);
// Free some memory
~CInjector();
// Inject the given library in to the open process
bool injectLibrary(string& szError);
// Start a function in the injected library
DWORD startFunction(LPVOID pFunction, LPVOID pParameter, string& szError);
// Get relative address of a certain function
LPVOID getRelativeAddress(char* pwszExportName, string& szError);
// Get the process id by executable name
static DWORD getProcessIdByName(LPWSTR pwszProcess, string& szError);
// Get the base address of the injected library
DWORD getBaseAddress();
private:
HANDLE hProcess; // Process handle
HINSTANCE hLibrary; // The local library instance
DWORD dwBaseAddress; // The injected library's base address
LPWSTR pwszLibrary; // Store the path to the library
bool fExecuteFunctions; // Enable function executing, so we can load it locally
// Adjust privileges to gain access to e.g. winlogon.exe
bool adjustPrivileges(string& szError);
};
#endif