Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * Given a PID, this program attempts to inject a DLL into the process |
michael@0 | 7 | * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", |
michael@0 | 8 | * must exist alongside this exe. The DLL will then crash the process. |
michael@0 | 9 | */ |
michael@0 | 10 | #include <stdio.h> |
michael@0 | 11 | #include <stdlib.h> |
michael@0 | 12 | #include <string.h> |
michael@0 | 13 | #include <windows.h> |
michael@0 | 14 | |
michael@0 | 15 | int main(int argc, char** argv) |
michael@0 | 16 | { |
michael@0 | 17 | if (argc != 2) { |
michael@0 | 18 | fprintf(stderr, "Usage: crashinject <PID>\n"); |
michael@0 | 19 | return 1; |
michael@0 | 20 | } |
michael@0 | 21 | |
michael@0 | 22 | int pid = atoi(argv[1]); |
michael@0 | 23 | if (pid <= 0) { |
michael@0 | 24 | fprintf(stderr, "Usage: crashinject <PID>\n"); |
michael@0 | 25 | return 1; |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | // find our DLL to inject |
michael@0 | 29 | wchar_t filename[_MAX_PATH]; |
michael@0 | 30 | if (GetModuleFileNameW(nullptr, filename, |
michael@0 | 31 | sizeof(filename) / sizeof(wchar_t)) == 0) |
michael@0 | 32 | return 1; |
michael@0 | 33 | |
michael@0 | 34 | wchar_t* slash = wcsrchr(filename, L'\\'); |
michael@0 | 35 | if (slash == nullptr) |
michael@0 | 36 | return 1; |
michael@0 | 37 | |
michael@0 | 38 | slash++; |
michael@0 | 39 | wcscpy(slash, L"crashinjectdll.dll"); |
michael@0 | 40 | |
michael@0 | 41 | // now find our target process |
michael@0 | 42 | HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, |
michael@0 | 43 | FALSE, |
michael@0 | 44 | pid); |
michael@0 | 45 | if (targetProc == nullptr) { |
michael@0 | 46 | fprintf(stderr, "Error %d opening target process\n", GetLastError()); |
michael@0 | 47 | return 1; |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | /* |
michael@0 | 51 | * This is sort of insane, but we're implementing a technique described here: |
michael@0 | 52 | * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 |
michael@0 | 53 | * |
michael@0 | 54 | * The gist is to use CreateRemoteThread to create a thread in the other |
michael@0 | 55 | * process, but cheat and make the thread function kernel32!LoadLibrary, |
michael@0 | 56 | * so that the only remote data we have to pass to the other process |
michael@0 | 57 | * is the path to the library we want to load. The library we're loading |
michael@0 | 58 | * will then do its dirty work inside the other process. |
michael@0 | 59 | */ |
michael@0 | 60 | HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); |
michael@0 | 61 | // allocate some memory to hold the path in the remote process |
michael@0 | 62 | void* pLibRemote = VirtualAllocEx(targetProc, nullptr, sizeof(filename), |
michael@0 | 63 | MEM_COMMIT, PAGE_READWRITE); |
michael@0 | 64 | if (pLibRemote == nullptr) { |
michael@0 | 65 | fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError()); |
michael@0 | 66 | CloseHandle(targetProc); |
michael@0 | 67 | return 1; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, |
michael@0 | 71 | sizeof(filename), nullptr)) { |
michael@0 | 72 | fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError()); |
michael@0 | 73 | VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); |
michael@0 | 74 | CloseHandle(targetProc); |
michael@0 | 75 | return 1; |
michael@0 | 76 | } |
michael@0 | 77 | // Now create a thread in the target process that will load our DLL |
michael@0 | 78 | HANDLE hThread = CreateRemoteThread( |
michael@0 | 79 | targetProc, nullptr, 0, |
michael@0 | 80 | (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, |
michael@0 | 81 | "LoadLibraryW"), |
michael@0 | 82 | pLibRemote, 0, nullptr); |
michael@0 | 83 | if (hThread == nullptr) { |
michael@0 | 84 | fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError()); |
michael@0 | 85 | VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); |
michael@0 | 86 | CloseHandle(targetProc); |
michael@0 | 87 | return 1; |
michael@0 | 88 | } |
michael@0 | 89 | WaitForSingleObject(hThread, INFINITE); |
michael@0 | 90 | // Cleanup, not that it's going to matter at this point |
michael@0 | 91 | CloseHandle(hThread); |
michael@0 | 92 | VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); |
michael@0 | 93 | CloseHandle(targetProc); |
michael@0 | 94 | |
michael@0 | 95 | return 0; |
michael@0 | 96 | } |