Been wanting to do this for a VERY long time... This is a super simple (like as easy as you can get) DLL injector that I wrote to do some testing. Here we go!
1: #include "stdafx.h"
2: #include <Windows.h>
3: #include <iostream>
4: #include <TlHelp32.h>
5:
6: using namespace std;
7:
8: int main()
9: {
10: HANDLE hHandle = NULL;
11: HANDLE hSnapshot;
12: PROCESSENTRY32 entry;
13: entry.dwSize = sizeof(entry);
14: hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
15: if (Process32First(hSnapshot, &entry) == TRUE)
16: {
17: while (Process32Next(hSnapshot, &entry) == TRUE)
18: {
19: if (_tcscmp(entry.szExeFile, TEXT("chrome.exe")) == 0)
20: {
21: hHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
22: break;
23: }
24: }
25: }
26: if (!hHandle)
27: {
28: printf("OpenProcess() Failed!\n");
29: return 1;
30: }
31: else
32: {
33: printf("Program (PID: %d) opened successfully\n", entry.th32ProcessID);
34:
35: const char* path = "C:\\misc\\Dll1.dll";
36: LPVOID cave = VirtualAllocEx(hHandle, NULL, 25, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
37: SIZE_T* bytesWritten = NULL;
38: BOOL successWriting = WriteProcessMemory(hHandle, cave, path, 25, bytesWritten);
39: if (successWriting)
40: {
41: LPVOID loadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
42: if (!loadLibraryAddress)
43: {
44: DWORD dw = GetLastError();
45: cout << "Error: " << dw << endl;
46: }
47: HANDLE remoteThread = CreateRemoteThread(hHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)loadLibraryAddress, cave, NULL, NULL);
48: }
49: else
50: {
51: DWORD dw = GetLastError();
52: cout << "Error: " << dw << endl;
53: }
54: }
55: cin.get();
56: }
All the way up to line 33 is almost a direct steal from the Firefall Auto-Trigger tool I wrote a while ago. It finds a process by executable name, and obtains a handle to it with OpenProcess(). For this one, I used
PROCESS_ALL_ACCESS to be able to write to it and start remote threads.
The interesting parts are line 35 and on. Line 35 is just the path to a DLL on the system. The DLL I wrote just pops up a message box saying "It worked!". 36 allocates enough memory to store that string. Important to note to use the PAGE_EXECUTE_READWRITE permission so the code can be executed. Line 38 writes that path into the newly allocated memory.
If it was successful, line 41 gets the address of LoadLibraryA from kernel32.dll in the DLL injector process. Since ASLR only randomizes on boot up, it'll be the same for every process, meaning the location of LoadLibraryA is the same in the DLL injector as it is in the target process.
Line 47 is the magic. It'll start a remote thread that just calls LoadLibraryA, with the argument of my DLL path. LoadLibraryA will import my DLL. The DLL is written so that every time it's imported, it'll execute a block of code. That code is the message box.
There's lots that can be fixed with this. Trimming down permissions from PROCESS_ALL_ACCESS to only what's needed, dynamically figuring out how much size is needed for the VirtualAlloc by the length of the path, etc...
One thing interesting that I found out, is that I couldn't inject into built-in Windows processes like Calculator or Notepad. It just kept giving me permission denied. I tried using both 32-bit and 64-bit versions of the injector, but it still wouldn't work. When I tried chrome.exe on a hunch, it worked no problem. I'm still investigating why this is an issue.