This is a VERY BAD way to implement code injection.
For one thing, your calculation of the create()
function's byte size is not guaranteed to work the way you are expecting. It relies on GetTargetProcessId()
being located in memory immediately after create()
, but that is not guaranteed by the compiler/linker. They may be in reverse order, or they may have other code in between them.
Second, even if you could copy the raw bytes of the entire function into the remote process like you are attempting, the create()
function will still likely fail because:
it is declared wrong to begin with. It does not match the signature that LPTHREAD_START_ROUTINE
is expecting. In particular, its return value and calling convention are both wrong, which will cause mismanagement of the call stack.
create()
calls CreateFileW()
and WriteFile()
statically, so it relies on the loaded memory address of kernel32.dll
within YOUR PROCESS's address space, which may be different than the loaded address within the TARGET PROCESS's address space. This is especially important if Address Space Layout Randomization is enabled.
There are much cleaner and safer ways to implement code injection, like moving your create()
code into a separate DLL and then using CreateRemoteThread()
to load that DLL into the target process. When the DLL is loaded into the target process, its DLL_PROCESS_ATTACH
handler can then call its create()
code normally without using ugly hacks.
You inject a DLL by using LoadLibrary()
as the remote thread procedure, and a (remote-allocated) pointer to the DLL filename as the thread parameter.
Use GetProcAddress()
within your own process to get a pointer to LoadLibrary()
within your process. If ASLR is not enabled, and kernel32.dll
is not re-based in the target process, you can pass that LoadLibrary()
pointer directly to CreateRemoteThread()
as the thread procedure, since the load address of kernel32.dll
, and thus the address of LoadLibrary()
, will be the same in both processes.
However, if ALSR or rebasing is involved, then you will have to adjust the LoadLibrary()
pointer before passing it to CreateRemoteThread()
. You can use GetModuleHandle()
to get the load address of kernel32.dll
within your own process. To get the load address of kernel32.dll
within the target process, use CreateToolhelp32Snapshot(TH32CS_SNAPMODULE)
/Module32First()
/Module32Next()
, or EnumProcessModules()
or EnumProcessModulesEx()
. If the two addresses differ, adjust the LoadLibrary()
pointer by the difference before passing it to CreateRemoteThread()
.
Using LoadLibrary()
in this manner works because the signature of LoadLibrary()
is compatible with LPTHREAD_START_ROUTINE
. This also has the added benefit that the return value of LoadLibrary()
becomes the thread's exit code, so your app can call CreateRemoteThread()
, wait for the thread to terminate when LoadLibrary()
exits, and then grab the exit code to determine if the injection was successful or not (but you cannot access the actual error code if LoadLibrary()
fails).
Try something like this:
DLL:
BOOL create()
{
HANDLE hFile = CreateFileW(L"C:\CodeInjectTest.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
const char *lpBuffer = "if you see this file, then the CodeInjectTest has succeed
";
DWORD dNumberOfByteToWrite;
WriteFile(hFile, lpBuffer, strlen(lpBuffer), &dNumberOfByteToWrite, NULL);
CloseHandle(hFile);
return TRUE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
// your injected code here...
return create();
}
return TRUE;
}
EXE:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <shlwapi.h>
DWORD GetTargetProcessId(const wchar_t *target)
{
DWORD TargetProcessId = 0;
PROCESSENTRY32 pe32 = {0};
pe32.dwSize = sizeof(pe32);
HANDLE hHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hHandle == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot failed with error %u
", GetLastError());
return 0;
}
if (!Process32First(hHandle, &pe32))
{
if (GetLastError() != ERROR_NO_MORE_FILES)
printf("Process32First failed with error %u
", GetLastError());
}
else
{
do
{
printf("[%u] %ws
", pe32.th32ProcessID, pe32.szExeFile);
if (wcscmp(pe32.szExeFile, target) == 0)
{
TargetProcessId = pe32.th32ProcessID;
break;
}
if (!Process32Next(hHandle, &pe32))
{
if (GetLastError() != ERROR_NO_MORE_FILES)
printf("Process32Next failed with error %u
", GetLastError());
break;
}
}
while (true);
}
CloseHandle(hHandle);
return TargetProcessId;
}
LPVOID GetTargetProcAddress(HANDLE hProcess, const wchar_t *szWantedModule, const char *szProcName)
{
// note, there is a very interesting gotcha in a comment to this answer:
//
// Would ASLR cause friction for the address with DLL injection?
// http://stackoverflow.com/a/8569008/65863
//
// "The address of the module may not change but that does not make
// what the OP is doing safe! Consider the case where your app is
// running with shims enabled (something which you do not control!)
// or even the case where some other pieces of software which also
// performs EAT hooks is running in your process (again, not something
// you control). In that case, GetProcAddress could return a pointer
// to a function in another module to what you're expecting/asking,
// including one which is not loaded in the process which you're going
// to call CreateRemoteThread on, in that case the target will crash."
//
// you probably won't run into this very often, if ever, but you should
// be aware of it nonetheless!
HANDLE hLocalMod = GetModuleHandleW(szWantedModule);
LPVOID lpProcAddress = GetProcAddress(hLocalMod, szProcName);
if (!lpProcAddress)
{
printf("GetProcAddress failed with error %u
", GetLastError());
return NULL;
}
// return lpProcAddress;
HANDLE hRemoteMod = NULL;
wchar_t szModName[MAX_PATH];
HMODULE hMods[1024];
DWORD cbNeeded;
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
printf("EnumProcessModules failed with error %u
", GetLastError());
return NULL;
}
cbNeeded /= sizeof(HMODULE);
for (DWORD i = 0; i < cbNeeded; ++i)
{
if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(wchar_t)))
{
if (wcscmp(PathFindFileNameW(szModName), szWantedModule) == 0)
{
hRemoteMod = hMods[i];
break;
}
}
}
if (!hRemoteMod)
{
printf("Cannot find %ws in remote process
", szWantedModule);
return NULL;
}
if (hLocalMod != hRemoteMod)
lpProcAddress = (LPVOID)((INT_PTR)hRemoteMod - (INT_PTR)hLocalMod);
return lpProcAddress;
}
int main()
{
DWORD TargetProcessId = GetTargetProcessId(L"notepad.exe");
if (TargetProcessId == 0)
{
system("PAUSE");
return -1;
}
printf("TargetProcessId: %u
", TargetProcessId);
HANDLE hTargetHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, TargetProcessId);
if (!hTargetHandle)
{
printf("OpenProcess failed with error %u
", GetLastError());
system("PAUSE");
return -1;
}
LPVOID lpRemoteProcAddress = GetTargetProcAddress(hTargetHandle, L"kernel32.dll", "LoadLibraryW");
if (!lpRemoteProcAddress)
{
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
const wchar_t lpFilename[] = L"C:\CodeInjectTest.dll";
DWORD dwBufferSize = sizeof(lpFilename);
LPVOID lpRemoteBuffer = VirtualAllocEx(hTargetHandle, NULL, dwBufferSize, MEM_COMMIT, PAGE_READWRITE);
if (!lpRemoteBuffer)
{
printf("VirtualAllocEx failed with error %u
", GetLastError());
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
if (!WriteProcessMemory(hTargetHandle, lpRemoteBuffer, lpFilename, dwBufferSize, NULL);
{
printf("WriteProcessMemory failed with error %u
", GetLastError());
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
DWORD dwThreadId;
HANDLE hRemoteThreadHandle = CreateRemoteThread(hTargetHandle, NULL, NULL, lpRemoteProcAddress, lpRemoteBuffer, 0, &dwThreadId);
if (!hRemoteThreadHandle)
{
printf("CreateRemoteThread failed with error %u
", GetLastError());
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}
WaitForSingleObject(hRemoteThreadHandle, INFINITE);
DWORD dwExitCode;
GetExitCodeThread(hRemoteThreadHandle, &dwExitCode);
CloseHandle(hRemoteThreadHandle);
VirtualFreeEx(hTargetHandle, lpRemoteBuffer, 0, MEM_RELEASE);
if (dwExitCode == 0)
{
printf("Remote LoadLibrary failed
");
CloseHandle(hTargetHandle);
system("PAUSE");
return -1;
}