11#include < dlfcn.h>
22#include < pthread.h>
3- #include < sched.h>
43#include < sys/mman.h>
5- #include < sys/mount.h>
6- #include < sys/resource.h>
7- #include < sys/stat.h>
84#include < unistd.h>
9- #include < fcntl.h>
10- #include < linux/seccomp.h>
11- #include < linux/filter.h>
5+ #include < sys/resource.h>
126#include < sys/prctl.h>
13- #include < sys/syscall.h>
14- #include < linux/audit.h>
157#include < stdint.h>
168#include < stdlib.h>
179
1810#include < lsplt.hpp>
1911
2012#include " android_util.hpp"
21- #include " elf_utils.hpp"
2213#include " daemon.hpp"
2314#include " misc.hpp"
2415#include " module.hpp"
@@ -116,8 +107,7 @@ DCL_HOOK_FUNC(static char *, strdup, const char *str) {
116107 // Wipe the old map paths populated by hook_plt() before overwriting them.
117108 // The new scan will repopulate the map info with the same paths, but they
118109 // will be wiped again in hook_zygote_jni() after we are done with hooking.
119- g_hook->clear_map_paths ();
120- g_hook->refresh_map_infos ();
110+ g_hook->hook_unloader ();
121111 zygote_hooked = true ;
122112 }
123113 }
@@ -149,40 +139,6 @@ DCL_HOOK_FUNC(static int, unshare, int flags) {
149139 return old_unshare (flags);
150140}
151141
152- DCL_HOOK_FUNC (int , property_get, const char *key, char *value, const char *default_value) {
153-
154- static bool unloader_triggered = false ;
155-
156- if (unlikely (!unloader_triggered)) {
157- unloader_triggered = true ;
158-
159- if (!g_hook->skip_hooking_unloader ) {
160- g_hook->hook_unloader ();
161- g_hook->skip_hooking_unloader = true ;
162-
163- for (size_t i = g_hook->plt_backup .size ; i > 0 ; ) {
164- i--;
165- const auto & bkp = g_hook->plt_backup .data [i];
166-
167- if (bkp.backup_ptr == reinterpret_cast <void *>(old_property_get)) {
168- if (!lsplt::RegisterHook (bkp.dev , bkp.inode , bkp.sym , *bkp.backup_ptr , nullptr ) ||
169- !lsplt::CommitHook (g_hook->cached_map_infos , true )) {
170- PLOGE (" unhook %s" , bkp.sym );
171- } else {
172- if (i < g_hook->plt_backup .size - 1 ) {
173- __builtin_memmove (&g_hook->plt_backup .data [i], &g_hook->plt_backup .data [i + 1 ],
174- (g_hook->plt_backup .size - i - 1 ) * sizeof (PltBackupEntry));
175- }
176- g_hook->plt_backup .size --;
177- }
178- }
179- }
180- g_hook->clear_map_paths ();
181- }
182- }
183- return old_property_get (key, value, default_value);
184- }
185-
186142// We cannot directly call `munmap` to unload ourselves, otherwise when `munmap` returns,
187143// it will return to our code which has been unmapped, causing segmentation fault.
188144// Instead, we hook `pthread_attr_setstacksize` which will be called when VM daemon threads start.
@@ -212,9 +168,13 @@ DCL_HOOK_FUNC(static int, pthread_attr_setstacksize, void *target, size_t size)
212168
213169// -----------------------------------------------------------------
214170static size_t get_fd_max () {
215- rlimit r{32768 , 32768 };
216- getrlimit (RLIMIT_NOFILE, &r);
217- return r.rlim_max ;
171+ static size_t cached_max = 0 ;
172+ if (unlikely (cached_max == 0 )) {
173+ rlimit r{32768 , 32768 };
174+ getrlimit (RLIMIT_NOFILE, &r);
175+ cached_max = r.rlim_max ;
176+ }
177+ return cached_max;
218178}
219179
220180ZygiskContext::ZygiskContext (JNIEnv *env, void *args)
@@ -223,14 +183,12 @@ ZygiskContext::ZygiskContext(JNIEnv *env, void *args)
223183 process (nullptr ),
224184 pid(-1 ),
225185 flags(0 ),
226- info_flags(0 ),
227- hook_info_lock(PTHREAD_MUTEX_INITIALIZER) {
186+ info_flags(0 ) {
228187
229188 size_t fd_max = get_fd_max ();
230189 allowed_fds.capacity = fd_max;
231190 allowed_fds.size = fd_max;
232- allowed_fds.data = (bool *)malloc (fd_max * sizeof (bool ));
233- __builtin_memset (allowed_fds.data , 0 , fd_max * sizeof (bool ));
191+ allowed_fds.data = static_cast <bool *>(calloc (fd_max, sizeof (bool )));
234192 g_ctx = this ;
235193}
236194
@@ -264,90 +222,53 @@ void HookContext::register_hook(dev_t dev, ino_t inode, const char *symbol, void
264222
265223#define PLT_HOOK_REGISTER (DEV, INODE, NAME ) PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)
266224
267- void HookContext::refresh_map_infos () {
268- map_info_cache.size = 0 ;
269- cached_map_infos = lsplt::Scan ();
270-
271- for (size_t i = 0 ; i < cached_map_infos.size ; i++) {
272- const auto & map = cached_map_infos.data [i];
273- if (map.path [0 ] != ' \0 ' ) {
274- const char * filename = __builtin_strrchr (map.path , ' /' );
275- filename = filename ? filename + 1 : map.path ;
276- CachedMapEntry entry;
277- entry.name = filename;
278- entry.name_hash = calc_gnu_hash (filename);
279- entry.info = ↦
280- map_info_cache.push_back (entry);
281- }
282- }
283-
284- if (map_info_cache.size > 0 ) {
285- ::sort (map_info_cache.data, map_info_cache.data + map_info_cache.size,
286- [](const CachedMapEntry& a, const CachedMapEntry& b) {
287- return a.name_hash < b.name_hash ;
288- });
289- }
290- }
291-
292225void HookContext::hook_plt () {
293226 ino_t android_runtime_inode = 0 ;
294227 dev_t android_runtime_dev = 0 ;
295228
296- refresh_map_infos ();
229+ cached_map_infos = lsplt::Scan ();
297230
298- if (const auto * info = find_in_cache (map_info_cache, " libandroid_runtime.so" )) {
299- android_runtime_inode = info->inode ;
300- android_runtime_dev = info->dev ;
301- }
231+ for (size_t i = 0 ; i < cached_map_infos.size ; i++) {
232+ const char * path = cached_map_infos.data [i].path ;
233+ if (path[0 ] == ' \0 ' ) continue ;
234+
235+ const char * filename = __builtin_strrchr (path, ' /' );
236+ filename = filename ? filename + 1 : path;
237+
238+ if (__builtin_strcmp (filename, " libandroid_runtime.so" ) == 0 ) {
239+ android_runtime_inode = cached_map_infos.data [i].inode ;
240+ android_runtime_dev = cached_map_infos.data [i].dev ;
241+ } else if (__builtin_strcmp (filename, " libart.so" ) == 0 ) {
242+ g_art_inode = cached_map_infos.data [i].inode ;
243+ g_art_dev = cached_map_infos.data [i].dev ;
244+ }
302245
303- if (const auto * info = find_in_cache (map_info_cache, " libart.so" )) {
304- g_art_inode = info->inode ;
305- g_art_dev = info->dev ;
246+ if (android_runtime_inode && g_art_inode) break ;
306247 }
307248
308249 PLT_HOOK_REGISTER (android_runtime_dev, android_runtime_inode, fork);
309250 PLT_HOOK_REGISTER (android_runtime_dev, android_runtime_inode, unshare);
310251 PLT_HOOK_REGISTER (android_runtime_dev, android_runtime_inode, strdup);
311- PLT_HOOK_REGISTER (android_runtime_dev, android_runtime_inode, property_get);
312252
313253 if (!lsplt::CommitHook (cached_map_infos)) LOGE (" HookContext::hook_plt failed" );
314-
315- size_t new_size = 0 ;
316- for (size_t i = 0 ; i < plt_backup.size ; i++) {
317- if (*plt_backup.data [i].backup_ptr != nullptr ) {
318- plt_backup.data [new_size++] = plt_backup.data [i];
319- }
320- }
321- plt_backup.size = new_size;
322254}
323255
324256void HookContext::hook_unloader () {
325- clear_map_paths ();
326- refresh_map_infos ();
327- if (g_art_inode == 0 || g_art_dev == 0 ) {
328- if (const auto * info = find_in_cache (map_info_cache, " libart.so" )) {
329- g_art_inode = info->inode ;
330- g_art_dev = info->dev ;
257+ if (g_art_inode != 0 && g_art_dev != 0 ) {
258+ PLT_HOOK_REGISTER (g_art_dev, g_art_inode, pthread_attr_setstacksize);
259+ if (!lsplt::CommitHook (cached_map_infos)) {
260+ LOGE (" HookContext::hook_unloader failed" );
331261 }
262+ } else {
263+ LOGE (" libart.so not found! Unloader hook failed." );
332264 }
333-
334- PLT_HOOK_REGISTER (g_art_dev, g_art_inode, pthread_attr_setstacksize);
335- if (!lsplt::CommitHook (cached_map_infos)) {
336- LOGE (" HookContext::hook_unloader failed" );
337- }
265+ clear_map_paths ();
338266}
339267
340268void HookContext::clear_map_paths () {
341- static atomic_flag clearing = ATOMIC_FLAG_INIT;
342- if (atomic_flag_test_and_set_explicit (&clearing, memory_order_acquire)) return ;
343-
344269 for (size_t i = 0 ; i < cached_map_infos.size ; i++) {
345- auto & map = cached_map_infos.data [i];
346- size_t len = strnlen (map.path , sizeof (map.path ));
347- if (len > 0 ) memzero (map.path , len);
270+ __builtin_memset (cached_map_infos.data [i].path , 0 , sizeof (cached_map_infos.data [i].path ));
348271 }
349-
350- atomic_flag_clear_explicit (&clearing, memory_order_release);
351272}
352273
353274void HookContext::restore_plt_hook () {
@@ -362,10 +283,6 @@ void HookContext::restore_plt_hook() {
362283 LOGE (" failed to restore plt_hook" );
363284 should_unmap = false ;
364285 }
365-
366- // Clear cached map info
367- clear_map_paths ();
368- cached_map_infos.size = 0 ;
369286}
370287
371288// -----------------------------------------------------------------
@@ -378,7 +295,7 @@ void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods meth
378295 return ;
379296 }
380297
381- JNINativeMethod * hooks = new JNINativeMethod[ methods.size ()] ;
298+ auto * hooks = static_cast < JNINativeMethod*>( alloca ( sizeof (JNINativeMethod) * methods.size ())) ;
382299 size_t hooks_count = 0 ;
383300
384301 for (auto &native_method : methods) {
@@ -397,11 +314,6 @@ void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods meth
397314 continue ;
398315 }
399316 auto method = util::jni::ToReflectedMethod (env, clazz, method_id, is_static);
400- auto modifier = util::jni::CallIntMethod (env, method, member_getModifiers);
401- if ((modifier & MODIFIER_NATIVE) == 0 ) {
402- native_method.fnPtr = nullptr ;
403- continue ;
404- }
405317 auto artMethod = util::art::ArtMethod::FromReflectedMethod (env, method);
406318 hooks[hooks_count++] = native_method;
407319 auto original_method = artMethod->GetData ();
@@ -410,50 +322,28 @@ void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods meth
410322 }
411323
412324 if (hooks_count > 0 ) env->RegisterNatives (clazz, hooks, hooks_count);
413- delete[] hooks;
414325}
415326
416327void HookContext::hook_zygote_jni () {
417328 auto get_created_java_vms = reinterpret_cast <jint (*)(JavaVM **, jsize, jsize *)>(
418329 dlsym (RTLD_DEFAULT, " JNI_GetCreatedJavaVMs" ));
419330 if (!get_created_java_vms) {
420- void * sym = resolve_symbol (" libnativehelper.so" , " JNI_GetCreatedJavaVMs" );
421- if (sym) {
422- get_created_java_vms = reinterpret_cast <decltype (get_created_java_vms)>(sym);
423- } else {
424- LOGW (" JNI_GetCreatedJavaVMs not found in memory" );
425- return ;
426- }
331+ LOGE (" JNI_GetCreatedJavaVMs not found in memory!" );
332+ return ;
427333 }
428334 JavaVM *vm = nullptr ;
429335 jsize num = 0 ;
430- jint res = get_created_java_vms (&vm, 1 , &num);
431- if (res != JNI_OK || vm == nullptr ) return ;
336+ if (get_created_java_vms (&vm, 1 , &num) != JNI_OK || vm == nullptr ) return ;
432337 JNIEnv *env = nullptr ;
433- res = vm->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6);
434- if (res != JNI_OK || env == nullptr ) return ;
435-
436- auto classMember = util::jni::FindClass (env, " java/lang/reflect/Member" );
437- if (classMember != nullptr )
438- member_getModifiers = util::jni::GetMethodID (env, classMember, " getModifiers" , " ()I" );
439- auto classModifier = util::jni::FindClass (env, " java/lang/reflect/Modifier" );
440- if (classModifier != nullptr ) {
441- auto fieldId = util::jni::GetStaticFieldID (env, classModifier, " NATIVE" , " I" );
442- if (fieldId != nullptr )
443- MODIFIER_NATIVE = util::jni::GetStaticIntField (env, classModifier, fieldId);
444- }
445- if (member_getModifiers == nullptr || MODIFIER_NATIVE == 0 ) return ;
338+ if (vm->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6) != JNI_OK || env == nullptr ) return ;
339+
446340 if (!util::art::ArtMethod::Init (env)) {
447341 LOGE (" failed to init ArtMethod" );
448342 return ;
449343 }
450344 hook_jni_methods (env, kZygote , JNIMethods (zygote_methods, sizeof (zygote_methods) / sizeof (zygote_methods[0 ])));
451345}
452346
453- void HookContext::restore_zygote_hook (JNIEnv *env) {
454- hook_jni_methods (env, kZygote , JNIMethods (zygote_methods, sizeof (zygote_methods) / sizeof (zygote_methods[0 ])));
455- }
456-
457347// -----------------------------------------------------------------
458348
459349void hook_entry (void *start_addr, size_t block_size) {
@@ -475,6 +365,14 @@ void hook_entry(void *start_addr, size_t block_size) {
475365 g_hook->hook_plt ();
476366}
477367
368+ void HookContext::restore_zygote_hook (JNIEnv *env) {
369+ auto clazz = env->FindClass (kZygote );
370+ if (clazz) {
371+ env->RegisterNatives (clazz, zygote_methods, sizeof (zygote_methods) / sizeof (zygote_methods[0 ]));
372+ }
373+ env->ExceptionClear ();
374+ }
375+
478376void hookJniNativeMethods (JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) {
479377 g_hook->hook_jni_methods (env, clz, {methods, (size_t ) numMethods});
480378}
0 commit comments