-
Notifications
You must be signed in to change notification settings - Fork 214
Clean up injection traces of ReZygisk that are detected by detectors #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Relying on dlclose to unload libzygisk.so will block us to clean its trace in the solist. This commit allows us to unmap libzygisk.so without using dlclose. To call munmap, we use the function pthread_attr_setstacksize instead of pthread_attr_destroy, so that tail-call can still be applied here since it has the same signature as munmap.
1. fix typo vsdo -> vdso and allow this symbol to be not found, which is the case for the 32bit linkers on some devices 2. use soinfo_free to fully remove the soinfo record of libzygisk.so 3. set `soinfo.size = 0` to avoid the library being unmapped while removing its soinfo record 4. add more debug logs for troubleshooting
5b3bb46
to
b9ea635
Compare
Nice work RZ teams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, however there are some syntax things that would be better changed
@@ -98,28 +110,51 @@ namespace SoList { | |||
return addr == NULL ? NULL : *addr; | |||
} | |||
|
|||
static void DropSoPath(const char* target_path) { | |||
static bool DropSoPath(const char* target_path) { | |||
bool path_found = false; | |||
if (solist == NULL && !Initialize()) { | |||
LOGE("Failed to initialize solist"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a line to separate the logging and the return.
if (iter == *sonext) { | ||
*sonext = prev; | ||
LOGI("dropping solist record for %s loaded at %s with size %zu", iter->get_name(), iter->get_path(), iter->get_size()); | ||
if (iter->get_size() > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A line between logging and if
} | ||
prev = iter; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A line between the return and }
|
||
static void ResetCounters(size_t load, size_t unload) { | ||
if (solist == NULL && !Initialize()) { | ||
LOGE("Failed to initialize solist"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
return; | ||
} | ||
if (g_module_load_counter == NULL || g_module_unload_counter == NULL) { | ||
LOGI("g_module counters not defined, skip reseting them"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
auto unloaded_modules = *g_module_unload_counter; | ||
if (loaded_modules >= load) { | ||
*g_module_load_counter = loaded_modules - load; | ||
LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto (up to you)
bool path_found = SoList::DropSoPath(path); | ||
if (!path_found || !spoof_maps) return; | ||
|
||
LOGD("spoofing virtual maps for %s", path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO we should plainly remove that as it is still easy to detect either way.
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
|
|||
#include <cstring> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Language server suggestion, because in
void clearApi() { memset(&api, 0, sizeof(api)); }
memset
is defined in that header
@@ -850,15 +819,21 @@ static void hook_unloader() { | |||
ino_t art_inode = 0; | |||
dev_t art_dev = 0; | |||
|
|||
for (auto &map : lsplt::MapInfo::Scan()) { | |||
for (auto &map : cached_map_infos) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above the parent function, please add a comment explaining why we do cache, especially here.
void *start_addr = nullptr; | ||
size_t block_size = 0; | ||
for (auto &info : map) { | ||
if (strstr(info.path.c_str(), "libzygisk.so")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (strstr(info.path.c_str(), "libzygisk.so")) { | |
if (!strstr(info.path.c_str(), "libzygisk.so")) continue; |
Note that it is impossible to hide injecting trace of virtual memory maps from the hooked target process. ReZygisk will only focus on removing its trace for approcess that are not hooked by modules.
Reading the file `/proc/self/maps` is detectable by the target process. Hence, we should cache scanned virtual maps after `libart.so` is loaded for later plt hooks in the target process.
Two counters for module loading and unloading are introduced in the commit https://cs.android.com/android/_/android/platform/bionic/+/a2e83ab34845759f0999d0ec88f4cdf558c0a9f5. To remove linker traces of libzygisk.so and Zygisk modules, we should reset them properly.
Changes
dlclose
oflibzygisk.so
by manually deleting its soinfo record and unmap its memory blocks./proc/self/maps
for PLT hooks oflibart.so
to avoid reading this file in the target process.Detailed explanations are given as commit messages.
Why
Injection detector applications, such as Native Test, Holmes, Native Detection and JingMatrix/Demo, have found traces of ReZygisk for a long time. Current pull-request will eliminate the traces brought by solist and virtual map scanning.
Checkmarks
Additional information
Please review this pull-request carefully to avoid my unnoticeable mistakes.