Skip to content

Commit 3fcd289

Browse files
committed
Fix not working on Unity 4 on MacOS
1 parent 78fd470 commit 3fcd289

File tree

3 files changed

+779
-126
lines changed

3 files changed

+779
-126
lines changed

doorstop.c

Lines changed: 163 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -30,47 +30,48 @@ extern void *_dl_sym(void *, const char *, void *);
3030
#define DYLD_INTERPOSE(_replacment, _replacee)
3131
#define INIT_DLSYM \
3232
{ \
33-
if (real_dlsym == NULL) \
34-
real_dlsym = _dl_sym(RTLD_NEXT, "dlsym", dlsym_proxy); \
35-
if (!strcmp(name, "dlsym")) \
36-
return (void *)dlsym_proxy; \
33+
if (real_dlsym == NULL) \
34+
real_dlsym = _dl_sym(RTLD_NEXT, "dlsym", dlsym_proxy); \
35+
if (!strcmp(name, "dlsym")) \
36+
return (void *)dlsym_proxy; \
3737
}
3838
#define INIT_FCLOSE \
3939
{ \
40-
if (real_fclose == NULL) \
41-
real_fclose = real_dlsym(RTLD_NEXT, "fclose"); \
40+
if (real_fclose == NULL) \
41+
real_fclose = real_dlsym(RTLD_NEXT, "fclose"); \
4242
}
4343

4444
#elif __APPLE__
4545
#include <mach-o/dyld.h>
46+
#include "plthook.h"
4647

4748
#define PATH_MAX 1024 // Maximum on macOS
4849
#define real_dlsym dlsym
4950
#define real_fclose fclose
5051
#define dlsym_proxy dlsym_proxy
5152
#define fclose_proxy fclose_proxy
5253
#define program_path(app_path) \
53-
{ \
54-
uint32_t bufsize = PATH_MAX; \
55-
_NSGetExecutablePath(app_path, &bufsize); \
56-
}
54+
{ \
55+
uint32_t bufsize = PATH_MAX; \
56+
_NSGetExecutablePath(app_path, &bufsize); \
57+
}
5758
#define DYLD_INTERPOSE(_replacment, _replacee) \
58-
__attribute__((used)) static struct \
59-
{ \
60-
const void *replacment; \
61-
const void *replacee; \
62-
} _interpose_##_replacee \
63-
__attribute__((section("__DATA,__interpose"))) = {(const void *)(unsigned long)&_replacment, (const void *)(unsigned long)&_replacee};
59+
__attribute__((used)) static struct \
60+
{ \
61+
const void *replacment; \
62+
const void *replacee; \
63+
} _interpose_##_replacee \
64+
__attribute__((section("__DATA,__interpose"))) = {(const void *)(unsigned long)&_replacment, (const void *)(unsigned long)&_replacee};
6465
#define INIT_DLSYM
6566
#define INIT_FCLOSE
6667
#endif
6768

6869
// Set MonoArray's index to a reference type value (i.e. string)
6970
#define SET_ARRAY_REF(arr, index, refVal) \
70-
{ \
71-
void **p = (void **)r_mono_array_addr_with_size(arr, sizeof(void *), index); \
72-
r_mono_gc_wbarrier_set_arrayref(arr, p, refVal); \
73-
}
71+
{ \
72+
void **p = (void **)r_mono_array_addr_with_size(arr, sizeof(void *), index); \
73+
r_mono_gc_wbarrier_set_arrayref(arr, p, refVal); \
74+
}
7475

7576
void *(*r_mono_jit_init_version)(const char *root_domain_name, const char *runtime_version);
7677
void *(*r_mono_domain_assembly_open)(void *domain, const char *name);
@@ -99,149 +100,149 @@ void doorstop_init_mono_functions(void *handle)
99100
{
100101
#define LOAD_METHOD(m) r_##m = real_dlsym(handle, #m)
101102

102-
LOAD_METHOD(mono_jit_init_version);
103-
LOAD_METHOD(mono_domain_assembly_open);
104-
LOAD_METHOD(mono_assembly_get_image);
105-
LOAD_METHOD(mono_runtime_invoke);
106-
LOAD_METHOD(mono_method_desc_new);
107-
LOAD_METHOD(mono_method_desc_search_in_image);
108-
LOAD_METHOD(mono_method_signature);
109-
LOAD_METHOD(mono_signature_get_param_count);
110-
LOAD_METHOD(mono_array_new);
111-
LOAD_METHOD(mono_gc_wbarrier_set_arrayref);
112-
LOAD_METHOD(mono_array_addr_with_size);
113-
LOAD_METHOD(mono_get_string_class);
114-
LOAD_METHOD(mono_string_new);
115-
LOAD_METHOD(mono_thread_current);
116-
LOAD_METHOD(mono_thread_set_main);
117-
LOAD_METHOD(mono_domain_set_config);
118-
LOAD_METHOD(mono_assembly_getrootdir);
103+
LOAD_METHOD(mono_jit_init_version);
104+
LOAD_METHOD(mono_domain_assembly_open);
105+
LOAD_METHOD(mono_assembly_get_image);
106+
LOAD_METHOD(mono_runtime_invoke);
107+
LOAD_METHOD(mono_method_desc_new);
108+
LOAD_METHOD(mono_method_desc_search_in_image);
109+
LOAD_METHOD(mono_method_signature);
110+
LOAD_METHOD(mono_signature_get_param_count);
111+
LOAD_METHOD(mono_array_new);
112+
LOAD_METHOD(mono_gc_wbarrier_set_arrayref);
113+
LOAD_METHOD(mono_array_addr_with_size);
114+
LOAD_METHOD(mono_get_string_class);
115+
LOAD_METHOD(mono_string_new);
116+
LOAD_METHOD(mono_thread_current);
117+
LOAD_METHOD(mono_thread_set_main);
118+
LOAD_METHOD(mono_domain_set_config);
119+
LOAD_METHOD(mono_assembly_getrootdir);
119120

120121
#undef LOAD_METHOD
121122
}
122123

123124
void *jit_init_hook(const char *root_domain_name, const char *runtime_version)
124125
{
125-
// Call the original r_mono_jit_init_version to initialize the Unity Root Domain
126-
void *domain = r_mono_jit_init_version(root_domain_name, runtime_version);
126+
// Call the original r_mono_jit_init_version to initialize the Unity Root Domain
127+
void *domain = r_mono_jit_init_version(root_domain_name, runtime_version);
127128

128-
if (strcmp(getenv("DOORSTOP_ENABLE"), "TRUE"))
129-
{
130-
printf("[Doorstop] DOORSTOP_ENABLE is not TRUE! Disabling Doorstop...\n");
131-
return domain;
132-
}
133-
if (getenv("DOORSTOP_INITIALIZED"))
134-
{
135-
printf("DOORSTOP_INITIALIZED is set! Skipping!\n");
136-
return domain;
137-
}
138-
setenv("DOORSTOP_INITIALIZED", "TRUE", TRUE);
129+
if (strcmp(getenv("DOORSTOP_ENABLE"), "TRUE"))
130+
{
131+
printf("[Doorstop] DOORSTOP_ENABLE is not TRUE! Disabling Doorstop...\n");
132+
return domain;
133+
}
134+
if (getenv("DOORSTOP_INITIALIZED"))
135+
{
136+
printf("DOORSTOP_INITIALIZED is set! Skipping!\n");
137+
return domain;
138+
}
139+
setenv("DOORSTOP_INITIALIZED", "TRUE", TRUE);
139140

140-
if (r_mono_domain_set_config)
141-
{
142-
char app_path[PATH_MAX] = "\0";
143-
char config_path[PATH_MAX] = "\0";
144-
program_path(app_path);
145-
strcpy(config_path, app_path);
141+
if (r_mono_domain_set_config)
142+
{
143+
char app_path[PATH_MAX] = "\0";
144+
char config_path[PATH_MAX] = "\0";
145+
program_path(app_path);
146+
strcpy(config_path, app_path);
146147

147-
strcat(config_path, ".config");
148-
char *folder_path = dirname(app_path);
148+
strcat(config_path, ".config");
149+
char *folder_path = dirname(app_path);
149150

150-
printf("Setting config paths; basedir: %s; config: %s\n", folder_path, config_path);
151-
r_mono_domain_set_config(domain, folder_path, config_path);
152-
}
151+
printf("Setting config paths; basedir: %s; config: %s\n", folder_path, config_path);
152+
r_mono_domain_set_config(domain, folder_path, config_path);
153+
}
153154

154-
char *dll_path = getenv("DOORSTOP_INVOKE_DLL_PATH");
155+
char *dll_path = getenv("DOORSTOP_INVOKE_DLL_PATH");
155156

156-
char *assembly_dir = r_mono_assembly_getrootdir();
157-
printf("Managed dir: %s\n", assembly_dir);
158-
setenv("DOORSTOP_MANAGED_FOLDER_DIR", assembly_dir, TRUE);
159-
free(assembly_dir);
157+
char *assembly_dir = r_mono_assembly_getrootdir();
158+
printf("Managed dir: %s\n", assembly_dir);
159+
setenv("DOORSTOP_MANAGED_FOLDER_DIR", assembly_dir, TRUE);
160+
free(assembly_dir);
160161

161-
// Load our custom assembly into the domain
162-
void *assembly = r_mono_domain_assembly_open(domain, dll_path);
162+
// Load our custom assembly into the domain
163+
void *assembly = r_mono_domain_assembly_open(domain, dll_path);
163164

164-
if (assembly == NULL)
165-
{
166-
printf("Failed to load assembly\n");
167-
return domain;
168-
}
165+
if (assembly == NULL)
166+
{
167+
printf("Failed to load assembly\n");
168+
return domain;
169+
}
169170

170-
// Get assembly's image that contains CIL code
171-
void *image = r_mono_assembly_get_image(assembly);
171+
// Get assembly's image that contains CIL code
172+
void *image = r_mono_assembly_get_image(assembly);
172173

173-
printf("Got image: %p \n", image);
174+
printf("Got image: %p \n", image);
174175

175-
if (image == NULL)
176-
{
177-
printf("Failed to locate the image!\n");
178-
return domain;
179-
}
176+
if (image == NULL)
177+
{
178+
printf("Failed to locate the image!\n");
179+
return domain;
180+
}
180181

181-
// Note: we use the runtime_invoke route since jit_exec will not work on DLLs
182+
// Note: we use the runtime_invoke route since jit_exec will not work on DLLs
182183

183-
// Create a descriptor for a random Main method
184-
void *desc = r_mono_method_desc_new("*:Main", FALSE);
184+
// Create a descriptor for a random Main method
185+
void *desc = r_mono_method_desc_new("*:Main", FALSE);
185186

186-
// Find the first possible Main method in the assembly
187-
void *method = r_mono_method_desc_search_in_image(desc, image);
187+
// Find the first possible Main method in the assembly
188+
void *method = r_mono_method_desc_search_in_image(desc, image);
188189

189-
if (method == NULL)
190-
{
191-
printf("Failed to locate any entrypoints!\n");
192-
return domain;
193-
}
190+
if (method == NULL)
191+
{
192+
printf("Failed to locate any entrypoints!\n");
193+
return domain;
194+
}
194195

195-
void *signature = r_mono_method_signature(method);
196+
void *signature = r_mono_method_signature(method);
196197

197-
// Get the number of parameters in the signature
198-
uint32_t params = r_mono_signature_get_param_count(signature);
198+
// Get the number of parameters in the signature
199+
uint32_t params = r_mono_signature_get_param_count(signature);
199200

200-
void **args = NULL;
201-
char app_path[PATH_MAX] = "\0";
202-
if (params == 1)
203-
{
204-
// If there is a parameter, it's most likely a string[].
205-
// Populate it as follows
206-
// 0 => path to the game's executable
207-
// 1 => --doorstop-invoke
201+
void **args = NULL;
202+
char app_path[PATH_MAX] = "\0";
203+
if (params == 1)
204+
{
205+
// If there is a parameter, it's most likely a string[].
206+
// Populate it as follows
207+
// 0 => path to the game's executable
208+
// 1 => --doorstop-invoke
208209

209-
program_path(app_path);
210+
program_path(app_path);
210211

211-
void *exe_path = r_mono_string_new(domain, app_path);
212-
void *doorstop_handle = r_mono_string_new(domain, "--doorstop-invoke");
212+
void *exe_path = r_mono_string_new(domain, app_path);
213+
void *doorstop_handle = r_mono_string_new(domain, "--doorstop-invoke");
213214

214-
void *args_array = r_mono_array_new(domain, r_mono_get_string_class(), 2);
215+
void *args_array = r_mono_array_new(domain, r_mono_get_string_class(), 2);
215216

216-
SET_ARRAY_REF(args_array, 0, exe_path);
217-
SET_ARRAY_REF(args_array, 1, doorstop_handle);
217+
SET_ARRAY_REF(args_array, 0, exe_path);
218+
SET_ARRAY_REF(args_array, 1, doorstop_handle);
218219

219-
args = malloc(sizeof(void *) * 1);
220-
args[0] = args_array;
221-
}
220+
args = malloc(sizeof(void *) * 1);
221+
args[0] = args_array;
222+
}
222223

223-
r_mono_runtime_invoke(method, NULL, args, NULL);
224+
r_mono_runtime_invoke(method, NULL, args, NULL);
224225

225-
if (args != NULL)
226-
{
227-
free(args);
228-
args = NULL;
229-
}
226+
if (args != NULL)
227+
{
228+
free(args);
229+
args = NULL;
230+
}
230231

231-
return domain;
232+
return domain;
232233
}
233234

234235
void *dlsym_proxy(void *handle, const char *name)
235236
{
236-
INIT_DLSYM;
237+
INIT_DLSYM;
237238

238-
if (!strcmp(name, "mono_jit_init_version"))
239-
{
240-
doorstop_init_mono_functions(handle);
241-
return (void *)jit_init_hook;
242-
}
239+
if (!strcmp(name, "mono_jit_init_version"))
240+
{
241+
doorstop_init_mono_functions(handle);
242+
return (void *)jit_init_hook;
243+
}
243244

244-
return real_dlsym(handle, name);
245+
return real_dlsym(handle, name);
245246
}
246247

247248
int fclose_proxy(FILE *stream)
@@ -254,5 +255,41 @@ int fclose_proxy(FILE *stream)
254255
return real_fclose(stream);
255256
}
256257

258+
#if __APPLE__
259+
// Normal case: hook dlsym and get both handle to mono and hook to jit_init_version
257260
DYLD_INTERPOSE(dlsym_proxy, real_dlsym);
258-
DYLD_INTERPOSE(fclose_proxy, real_fclose);
261+
DYLD_INTERPOSE(fclose_proxy, real_fclose);
262+
263+
/*
264+
On older Unity versions, Mono methods are resolved by the OS's loader directly.
265+
Because of this, there is no dlsym, in which case we need to apply a PLT hook.
266+
*/
267+
268+
void *find_mono_image()
269+
{
270+
uint32_t cnt = _dyld_image_count();
271+
for (uint32_t idx = 0; idx < cnt; idx++)
272+
{
273+
const char *image_name = idx ? _dyld_get_image_name(idx) : NULL;
274+
if (image_name && strstr(image_name, "libmono"))
275+
return dlopen(image_name, RTLD_LAZY | RTLD_NOLOAD);
276+
}
277+
return NULL;
278+
}
279+
280+
__attribute__((constructor)) void doorstop_main()
281+
{
282+
plthook_t *hook;
283+
284+
if(plthook_open(&hook, NULL) != 0)
285+
printf("Failed to open current process PLT! err: %s\n", plthook_error());
286+
287+
if(plthook_replace(hook, "mono_jit_init_version", &jit_init_hook, NULL) == 0)
288+
{
289+
void *mono_handle = find_mono_image();
290+
doorstop_init_mono_functions(mono_handle);
291+
}
292+
293+
plthook_close(hook);
294+
}
295+
#endif

0 commit comments

Comments
 (0)