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
/* build.exe — one-click builder for the Lodestone C++ client.
*
* Double-click it (or run it from a terminal) in the project folder. It:
* 1. downloads a portable mingw-w64 toolchain (w64devkit) the first time,
* into a `w64devkit\` subfolder — nothing is installed system-wide;
* 2. compiles the client DLL and the loader EXE with that toolchain;
* 3. checks the DLL imports only the always-present system libraries.
*
* No JDK is needed: jni.h is vendored under third_party/jni. Re-running skips
* the download once w64devkit is present, so a rebuild is just a recompile.
*
* The compiler and flags mirror the Makefile; this file exists so someone with
* a bare Windows box can build without first learning make or installing a
* toolchain. To build this bootstrapper itself (done by the maintainer, on any
* mingw): x86_64-w64-mingw32-gcc -O2 build_bootstrap.c -o build.exe
*/
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Pinned so the build is reproducible; old release assets stay downloadable.
* w64devkit is msvcrt-based, which is why the DLL imports only msvcrt.dll. */
#define DEVKIT_VER "1.23.0"
#define DEVKIT_ZIP "w64devkit-" DEVKIT_VER ".zip"
#define DEVKIT_URL \
"https://github.com/skeeto/w64devkit/releases/download/v" DEVKIT_VER "/" DEVKIT_ZIP
#define GXX "w64devkit\\bin\\g++.exe"
#define GCC "w64devkit\\bin\\gcc.exe"
#define OBJDUMP "w64devkit\\bin\\objdump.exe"
static const char *INCLUDES =
"-Isrc -Isrc/jni_md -Ithird_party/jni "
"-Ithird_party/imgui -Ithird_party/imgui/backends -Ithird_party/minhook/include";
static const char *DEFINES = "-DWIN32_LEAN_AND_MEAN -DNOMINMAX -DUNICODE -D_UNICODE";
static const char *CXXFLAGS = "-std=c++20 -O2 -Wall -Wextra -Wno-unused-parameter -fno-rtti";
static const char *CFLAGS = "-O2 -Wall";
static const char *CXX_SRC[] = {
"src/main.cpp",
"src/overlay.cpp",
"third_party/imgui/imgui.cpp",
"third_party/imgui/imgui_draw.cpp",
"third_party/imgui/imgui_tables.cpp",
"third_party/imgui/imgui_widgets.cpp",
"third_party/imgui/backends/imgui_impl_opengl3.cpp",
};
static const char *C_SRC[] = {
"third_party/minhook/src/hook.c",
"third_party/minhook/src/buffer.c",
"third_party/minhook/src/trampoline.c",
"third_party/minhook/src/hde/hde64.c",
};
#define N_CXX (int)(sizeof(CXX_SRC) / sizeof(*CXX_SRC))
#define N_C (int)(sizeof(C_SRC) / sizeof(*C_SRC))
static int exists(const char *path) {
return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
}
/* Create every directory along a `build/...` path that does not yet exist. */
static void mkdirs(const char *path) {
char t[1024];
strncpy(t, path, sizeof t - 1);
t[sizeof t - 1] = 0;
for (char *p = t + 1; *p; ++p) {
if (*p == '/' || *p == '\\') {
char c = *p;
*p = 0;
CreateDirectoryA(t, NULL);
*p = c;
}
}
CreateDirectoryA(t, NULL);
}
/* Run a command through cmd, echoing it first. Returns its exit code. */
static int run(const char *fmt, ...) {
char cmd[16384];
va_list ap;
va_start(ap, fmt);
vsnprintf(cmd, sizeof cmd, fmt, ap);
va_end(ap);
printf("\n> %s\n", cmd);
fflush(stdout);
return system(cmd);
}
static int fail(const char *what) {
printf("\n[!] %s failed.\n Fix the problem above and run build.exe again.\n", what);
fflush(stdout);
system("pause");
return 1;
}
/* The object file a source compiles to: build/<source>.o */
static void obj_of(const char *src, char *out, size_t n) {
snprintf(out, n, "build/%s.o", src);
}
int main(void) {
/* Work from the folder build.exe lives in, so a double-click (whatever the
* shell's current directory is) still finds the sources. */
char exe[1024];
if (GetModuleFileNameA(NULL, exe, sizeof exe)) {
char *slash = strrchr(exe, '\\');
if (slash) {
*slash = 0;
SetCurrentDirectoryA(exe);
}
}
printf("=== Lodestone C++ builder ===\n");
if (!exists("src\\main.cpp")) {
printf("\n[!] Run build.exe from the project folder (the one with src\\ and\n"
" third_party\\ in it). I do not see src\\main.cpp here.\n");
system("pause");
return 1;
}
/* 1. Toolchain. Downloaded once into .\w64devkit\. */
if (!exists(GXX)) {
printf("\nNo toolchain yet — fetching w64devkit " DEVKIT_VER " (about 73 MB, once).\n");
if (!exists(DEVKIT_ZIP)) {
if (run("powershell -NoProfile -ExecutionPolicy Bypass -Command "
"\"$ProgressPreference='SilentlyContinue'; "
"[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; "
"Invoke-WebRequest -Uri '%s' -OutFile '%s'\"",
DEVKIT_URL, DEVKIT_ZIP))
return fail("Download");
}
printf("\nUnpacking...\n");
if (run("powershell -NoProfile -ExecutionPolicy Bypass -Command "
"\"Expand-Archive -Force '%s' -DestinationPath '.'\"",
DEVKIT_ZIP))
return fail("Unpacking");
if (!exists(GXX))
return fail("Toolchain layout (w64devkit\\bin\\g++.exe not found after unpacking)");
printf("\nToolchain ready.\n");
} else {
printf("\nToolchain already present — skipping download.\n");
}
/* 2. Compile. One object per source; the object tree mirrors the sources. */
char obj[1024];
char objs[12288] = ""; /* the DLL's object list, accumulated for the link */
printf("\nCompiling the client...\n");
for (int i = 0; i < N_CXX; ++i) {
obj_of(CXX_SRC[i], obj, sizeof obj);
mkdirs(obj);
if (run("%s %s %s %s -c %s -o %s", GXX, CXXFLAGS, INCLUDES, DEFINES, CXX_SRC[i], obj))
return fail("Compile");
strcat(objs, obj);
strcat(objs, " ");
}
for (int i = 0; i < N_C; ++i) {
obj_of(C_SRC[i], obj, sizeof obj);
mkdirs(obj);
if (run("%s %s %s %s -c %s -o %s", GCC, CFLAGS, INCLUDES, DEFINES, C_SRC[i], obj))
return fail("Compile");
strcat(objs, obj);
strcat(objs, " ");
}
printf("\nLinking lodestone_client.dll...\n");
if (run("%s %s -o build/lodestone_client.dll "
"-shared -static-libgcc -static-libstdc++ -Wl,--exclude-all-symbols "
"-lopengl32 -lgdi32 -luser32 -lkernel32",
GXX, objs))
return fail("Linking the DLL");
/* 3. The loader — a plain console EXE, no ImGui or MinHook. */
printf("\nBuilding the loader...\n");
obj_of("inject/main.cpp", obj, sizeof obj);
mkdirs(obj);
if (run("%s %s %s %s -c inject/main.cpp -o %s", GXX, CXXFLAGS, INCLUDES, DEFINES, obj))
return fail("Compiling the loader");
if (run("%s %s -o build/lodestone-inject.exe "
"-static-libgcc -static-libstdc++ -lpsapi -luser32 -lkernel32",
GXX, obj))
return fail("Linking the loader");
/* 4. Prove the DLL is injectable: it must import only libraries that are
* always present. A libstdc++-6.dll here means the static link slipped. */
printf("\n--- DLL imports (must NOT list libstdc++-6.dll) ---\n");
fflush(stdout);
run("%s -p build/lodestone_client.dll | findstr /C:\"DLL Name\"", OBJDUMP);
printf("\n=== Done ===\n"
" build\\lodestone_client.dll - inject this\n"
" build\\lodestone-inject.exe - the loader\n");
fflush(stdout);
system("pause");
return 0;
}