@@ -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
7576void * (* r_mono_jit_init_version )(const char * root_domain_name , const char * runtime_version );
7677void * (* 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
123124void * 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
234235void * 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
247248int 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
257260DYLD_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