-
Notifications
You must be signed in to change notification settings - Fork 6
/
launcher.cpp.in
229 lines (187 loc) · 8.46 KB
/
launcher.cpp.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/**
* @file
*
* @author OmniBlade
*
* @brief An executable launcher that will inject a dll into the launched process.
*
* @copyright SetSail is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version
* 2 of the License, or (at your option) any later version.
* A full copy of the GNU General Public License can be found in
* LICENSE
*/
#include "sha.h"
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <windows.h>
// Use WinMain and don't make a console window.
#pragma comment(linker, "/subsystem:windows /ENTRY:WinMainCRTStartup")
// Define the point to stall at while the DLL loads, normally the programs entry point.
#define EXE_ENTRY @SETSAIL_ENTRYPOINT@
// Define the EXE to launch and the DLL to inject.
#define EXE_NAME "@SETSAIL_EXENAME@"
#define DLL_NAME "@SETSAIL_DLLNAME@"
// Define the hash to compare against.
#define EXE_HASH "@SETSAIL_HASH@"
char *Make_Args(const char *args)
{
const char *argstart = args;
static char argstring[_MAX_PATH * 2];
// Stick the exe name on the front of the arg string.
std::snprintf(argstring, sizeof(argstring), "%s %s", EXE_NAME, argstart);
return argstring;
}
void Check_Hash()
{
SHAEngine sha;
char buffer[1024];
char hash[41];
std::ifstream in(EXE_NAME, std::ifstream::in | std::ifstream::binary);
// Get the hash of the binary.
do {
in.read(buffer, sizeof(buffer));
sha.Hash(buffer, in.gcount());
} while (in.gcount() == sizeof(buffer));
sha.Print_Result(hash);
// If we don't actually have a hash to compare against, let the user know what it should be.
// Intended to provide the hash for the developer to include in the top level CMakeLists.txt
// on the first build if the SHA1 hash isn't known by othre means.
if (strcmp(EXE_HASH, "") == 0) {
std::cout << hash << std::endl;
MessageBoxA(NULL, hash, "Define SETSAIL_HASH in CMakeLists.txt!", MB_OK | MB_SERVICE_NOTIFICATION);
std::exit(1);
}
// Let the user know if the binary they have is not the expected version and then bail.
if(strcmp(EXE_HASH, hash) != 0) {
std::cout << hash << " does not match expected " << EXE_HASH << std::endl;
MessageBoxA(NULL, "Your " EXE_NAME " hash does not match expected.\nCheck you have the correct version.", "Hash mismatch!", MB_OK | MB_SERVICE_NOTIFICATION);
std::exit(1);
}
}
// Based on code from http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
bool Inject_Dll(const char *dllname, HANDLE hProcess)
{
HANDLE hThread;
char szLibPath[_MAX_PATH]; // Buffer to hold the name of the DLL (including full path!)
void *pLibRemote; // The address (in the remote process) where szLibPath will be copied to.
DWORD hLibModule; // Base address of loaded module.
HMODULE hKernel32 = GetModuleHandleA("Kernel32"); // For the LoadLibraryA func.
GetFullPathNameA(dllname, _MAX_PATH, szLibPath, NULL);
std::ifstream in(dllname, std::ifstream::in | std::ifstream::binary);
IMAGE_NT_HEADERS exe_header;
in.seekg(offsetof(IMAGE_DOS_HEADER, e_lfanew), std::ifstream::cur); // seek to NE address pointer
DWORD neptr;
in.read(reinterpret_cast<char *>(&neptr), sizeof(neptr));
in.seekg(neptr, std::ifstream::beg);
in.read(reinterpret_cast<char *>(&exe_header), sizeof(exe_header));
if (!(exe_header.FileHeader.Characteristics & IMAGE_FILE_DLL)) {
// printf("NE char is %x",exe_header.FileHeader.Characteristics);
std::cout << "NE char is: " << std::hex << exe_header.FileHeader.Characteristics << std::endl;
throw "dll file does not have correct format";
}
// 1. Allocate memory in the remote process for szLibPath
// 2. Write szLibPath to the allocated memory
pLibRemote = VirtualAllocEx(hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pLibRemote, (void *)szLibPath, sizeof(szLibPath), NULL);
// Load "dll" into the remote process by passing LoadLibraryA as the function
// to run as a thread with CreateRemoteThread. Pass copied name of DLL as
// the arguments to the function.
hThread = CreateRemoteThread(
hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA"), pLibRemote, 0, NULL);
// Wait for the DLL to load and return.
WaitForSingleObject(hThread, INFINITE);
// Get handle of the loaded module
GetExitCodeThread(hThread, &hLibModule);
// Clean up
CloseHandle(hThread);
VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);
// LoadLibrary return is 0 on failure.
return hLibModule != 0;
}
// Based on code snippet from https://opcode0x90.wordpress.com/2011/01/15/injecting-dll-into-process-on-load/
void Inject_Loader(const char *path, const char *dllname, char *args)
{
STARTUPINFOA StartupInfo = {0};
PROCESS_INFORMATION ProcessInformation;
DWORD oldProtect;
DWORD oldProtect2;
char oldBytes[2];
char checkBytes[2];
static const char patchBytes[2] = {'\xEB', '\xFE'}; // JMP $-2
SIZE_T memwritten;
SIZE_T memread;
// initialize the structures
StartupInfo.cb = sizeof(StartupInfo);
// attempt to load the specified target in suspended state
if (CreateProcessA(path, args, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &StartupInfo, &ProcessInformation)) {
HANDLE hProcess(ProcessInformation.hProcess);
// wait for the process to done
try {
// locate the entry point
LPVOID entry = (LPVOID)EXE_ENTRY;
// patch the entry point with infinite loop
VirtualProtectEx(hProcess, entry, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
ReadProcessMemory(hProcess, entry, oldBytes, 2, &memread);
WriteProcessMemory(hProcess, entry, patchBytes, 2, &memwritten);
VirtualProtectEx(hProcess, entry, 2, oldProtect, &oldProtect2);
// resume the main thread
ResumeThread(ProcessInformation.hThread);
// wait until the thread stuck at entry point
CONTEXT context;
std::memset(&context, 0, sizeof(context));
for (unsigned int i = 0; i < 50 && context.Eip != (DWORD)entry; ++i) {
// patience.
Sleep(100);
// read the thread context
context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(ProcessInformation.hThread, &context);
}
if (context.Eip != (DWORD)entry) {
// wait timed out, we never got to the entry point :/
throw "entry point blockade timed out";
}
// inject DLL payload into remote process
if (!Inject_Dll(dllname, hProcess)) {
throw "dll failed to load";
}
// pause and restore original entry point unless DLL init overwrote
// it already.
SuspendThread(ProcessInformation.hThread);
VirtualProtectEx(hProcess, entry, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
ReadProcessMemory(hProcess, entry, checkBytes, 2, &memread);
// Check entry point is still patched to infinite loop. We don't
// want to mess up any patching the DLL did.
if (std::memcmp(checkBytes, patchBytes, 2) == 0) {
WriteProcessMemory(hProcess, entry, oldBytes, 2, &memwritten);
}
VirtualProtectEx(hProcess, entry, 2, oldProtect, &oldProtect2);
// MessageBox(NULL, "Attach debugger or continue.", "game.dat Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
// you are ready to go
ResumeThread(ProcessInformation.hThread);
} catch (...) {
// terminate the newly spawned process
TerminateProcess(hProcess, -1);
// rethrow the exception to top-level handler
throw;
}
} else {
// are you sure this is a valid target ?
throw "unable to load the specified executable";
}
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
#ifndef __WATCOMC__
AttachConsole(ATTACH_PARENT_PROCESS);
#endif
Check_Hash();
Inject_Loader(EXE_NAME, DLL_NAME, Make_Args(lpCmdLine));
Sleep(1000);
return 0;
}