Frida 注入目标进程后会使用Interceptor.attach对退出进程的方法进行inline hook。此处可以定位三个关键函数:exit、_exit、abort
GitHub源码链接:
365K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6X3M7X3W2V1j5g2)9J5c8X3k6J5K9h3c8S2i4K6u0V1j5$3!0J5k6g2)9J5c8X3u0D9L8$3u0Q4x3V1j5I4x3o6p5^5j5h3y4S2x3U0S2V1y4o6W2S2j5$3f1J5x3h3y4T1j5U0g2V1y4e0c8V1x3U0x3J5z5r3x3H3x3U0R3K6k6o6x3J5y4$3k6W2i4K6u0r3L8r3W2T1i4K6u0r3M7r3q4&6L8r3!0S2k6q4)9J5c8X3g2^5K9i4c8Q4x3X3c8E0L8$3&6A6N6r3!0J5i4K6u0W2N6X3q4D9j5g2)9J5x3@1H3K6y4l9`.`.
frida 调用了gum_interceptor_replace函数对libc库的signal和sigaction函数进行了inline hook。
GitHub链接:886K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6X3M7X3W2V1j5g2)9J5c8X3k6J5K9h3c8S2i4K6u0V1k6%4g2E0i4K6u0r3j5X3I4G2j5W2)9J5c8U0l9$3y4K6q4U0x3U0N6V1z5e0b7I4k6o6M7%4y4o6V1H3k6X3k6X3y4r3y4S2y4X3c8T1j5U0W2U0j5e0p5H3j5e0m8X3z5o6M7J5j5X3u0Q4x3V1k6Y4N6h3#2Q4x3V1k6T1j5h3y4C8k6h3&6V1i4K6u0V1M7r3!0K6K9i4S2Q4x3V1k6Y4N6h3#2W2P5r3y4W2M7s2c8G2M7W2)9J5k6s2m8G2M7$3W2^5i4K6u0W2j5#2)9J5x3@1H3J5x3U0R3`.
前提:已知frida注入目标进程会hook libc库以下目标函数:
{"sigaction", "signal", "exit", "abort", "_exit"}
实现代码如下:
分析源码中frida注入逻辑进行检测,这种方法类似于开卷考试,不过优点明显:针对性很强,检测逻辑复杂度很低,性能开销小。
不知道后续frida会不会修改相关逻辑。
interceptor.attach ((void *) Gum.Process.find_module_by_name ("kernel32.dll").find_export_by_name ("ExitProcess"),
listener);
var libc = Gum.Process.get_libc_module ();
const string[] apis = {
"exit",
"_exit",
"abort",
};
foreach (var symbol in apis) {
interceptor.attach ((void *) libc.find_export_by_name (symbol), listener);
}
interceptor.attach ((void *) Gum.Process.find_module_by_name ("kernel32.dll").find_export_by_name ("ExitProcess"),
listener);
var libc = Gum.Process.get_libc_module ();
const string[] apis = {
"exit",
"_exit",
"abort",
};
foreach (var symbol in apis) {
interceptor.attach ((void *) libc.find_export_by_name (symbol), listener);
}
static void
gum_exceptor_backend_attach (GumExceptorBackend * self)
{
GumInterceptor * interceptor = self->interceptor;
const gint handled_signals[] = {
SIGABRT,
SIGSEGV,
SIGBUS,
SIGILL,
SIGFPE,
SIGTRAP,
SIGSYS,
};
gint highest, i;
struct sigaction action;
highest = 0;
for (i = 0; i != G_N_ELEMENTS (handled_signals); i++)
highest = MAX (handled_signals[i], highest);
g_assert (highest > 0);
self->num_old_handlers = highest + 1;
self->old_handlers = g_new0 (struct sigaction *, self->num_old_handlers);
action.sa_sigaction = gum_exceptor_backend_on_signal;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_SIGINFO | SA_NODEFER;
action.sa_flags |= SA_ONSTACK;
for (i = 0; i != G_N_ELEMENTS (handled_signals); i++)
{
gint sig = handled_signals[i];
struct sigaction * old_handler;
old_handler = g_slice_new0 (struct sigaction);
self->old_handlers[sig] = old_handler;
gum_original_sigaction (sig, &action, old_handler);
}
gum_interceptor_begin_transaction (interceptor);
gum_interceptor_replace (interceptor, gum_original_signal,
gum_exceptor_backend_replacement_signal, self, NULL);
gum_interceptor_replace (interceptor, gum_original_sigaction,
gum_exceptor_backend_replacement_sigaction, self, NULL);
gum_interceptor_end_transaction (interceptor);
}
static void
gum_exceptor_backend_attach (GumExceptorBackend * self)
{
GumInterceptor * interceptor = self->interceptor;
const gint handled_signals[] = {
SIGABRT,
SIGSEGV,
SIGBUS,
SIGILL,
SIGFPE,
SIGTRAP,
SIGSYS,
};
gint highest, i;
struct sigaction action;
highest = 0;
for (i = 0; i != G_N_ELEMENTS (handled_signals); i++)
highest = MAX (handled_signals[i], highest);
g_assert (highest > 0);
self->num_old_handlers = highest + 1;
self->old_handlers = g_new0 (struct sigaction *, self->num_old_handlers);
action.sa_sigaction = gum_exceptor_backend_on_signal;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_SIGINFO | SA_NODEFER;
action.sa_flags |= SA_ONSTACK;
for (i = 0; i != G_N_ELEMENTS (handled_signals); i++)
{
gint sig = handled_signals[i];
struct sigaction * old_handler;
old_handler = g_slice_new0 (struct sigaction);
self->old_handlers[sig] = old_handler;
gum_original_sigaction (sig, &action, old_handler);
}
gum_interceptor_begin_transaction (interceptor);
gum_interceptor_replace (interceptor, gum_original_signal,
gum_exceptor_backend_replacement_signal, self, NULL);
gum_interceptor_replace (interceptor, gum_original_sigaction,
gum_exceptor_backend_replacement_sigaction, self, NULL);
gum_interceptor_end_transaction (interceptor);
}
// 获取函数地址
void* get_function_address(void* handle, const char* func_name) {
void* func_addr = dlsym(handle, func_name);
if (!func_addr) {
LOGD("[-] Function %s not found in global symbol table", func_name);
} else {
LOGD("[+] Function: %s, addr: 0x%lx", func_name, (uintptr_t)func_addr);
}
return func_addr;
}
// 获取函数偏移
uintptr_t get_function_offset(void* func_addr) {
Dl_info info;
if (dladdr(func_addr, &info) == 0) {
LOGD("[-] Unable to get function info");
return 0;
}
return (uintptr_t)func_addr - (uintptr_t)info.dli_fbase;
}
// 读取库文件中的字节
bool read_bytes_from_libso(const char* libpath, uintptr_t offset, uint8_t* buffer, size_t size) {
int fd = open(libpath, O_RDONLY);
if (fd == -1) {
LOGD("[-] Failed to open %s: %s", libpath, strerror(errno));
return false;
}
if (lseek(fd, offset, SEEK_SET) == -1) {
LOGD("[-] Seek failed at %lx in %s: %s", (unsigned long)offset, libpath, strerror(errno));
close(fd);
return false;
}
ssize_t bytes_read = read(fd, buffer, size);
close(fd);
if (bytes_read != (ssize_t)size) {
LOGD("[-] Read %zd bytes, expected %zu from %s at offset %lx", bytes_read, size, libpath, (unsigned long)offset);
return false;
}
return true;
}
// 字节数组转十六进制字符串
void bytes_to_hex_string(const uint8_t* bytes, size_t size, char* hex_string) {
for (size_t i = 0; i < size; i++) {
sprintf(hex_string + i * 2, "%02x", bytes[i]);
}
hex_string[size * 2] = '\0';
}
// 比较内存中的字节和库文件中的字节
bool compare_function_bytes(void* func_addr, uint8_t* file_bytes, size_t size) {
uint8_t mem_bytes[BYTE_BUFFER_SIZE];
memcpy(mem_bytes, func_addr, size);
char mem_hex_string[BYTE_BUFFER_SIZE * 2 + 1];
char file_hex_string[BYTE_BUFFER_SIZE * 2 + 1];
bytes_to_hex_string(mem_bytes, size, mem_hex_string);
bytes_to_hex_string(file_bytes, size, file_hex_string);
LOGD("[*] Memory: %s | File: %s", mem_hex_string, file_hex_string);
return memcmp(mem_bytes, file_bytes, size) == 0;
}
// Hook 检测函数
bool detect_hook(void* handle, const char* lib_path, const char* func_name) {
void* func_addr = get_function_address(handle, func_name);
if (!func_addr) return false;
uintptr_t offset = get_function_offset(func_addr);
if (offset == 0) {
LOGD("[-] Failed to get offset for %s", func_name);
return false;
}
uint8_t file_bytes[BYTE_BUFFER_SIZE];
if (!read_bytes_from_libso(lib_path, offset, file_bytes, sizeof(file_bytes))) {
LOGD("[-] Failed to read bytes for %s", func_name);
return false;
}
bool is_hooked = !compare_function_bytes(func_addr, file_bytes, sizeof(file_bytes));
LOGD("[+] %s in %s is %s", func_name, lib_path, is_hooked ? "HOOKED" : "NOT HOOKED");
return is_hooked;
}
// 执行 Hook 检测
int do_hook_check() {
const char* libpath = "/system/lib64/libc.so";
void* handle = dlopen(libpath, RTLD_LAZY);
if (!handle) {
LOGD("[-] Failed to load %s", libpath);
return -1;
}
const char* funcs[] = {"sigaction", "signal", "exit", "abort", "_exit"};
bool hooked = false;
for (size_t i = 0; i < sizeof(funcs) / sizeof(funcs[0]); i++) {
if (detect_hook(handle, libpath, funcs[i])) {
hooked = true;
}
}
dlclose(handle);
return hooked;
}
// 获取函数地址
void* get_function_address(void* handle, const char* func_name) {
void* func_addr = dlsym(handle, func_name);
if (!func_addr) {
LOGD("[-] Function %s not found in global symbol table", func_name);
} else {
LOGD("[+] Function: %s, addr: 0x%lx", func_name, (uintptr_t)func_addr);
}
return func_addr;
}
// 获取函数偏移
uintptr_t get_function_offset(void* func_addr) {
Dl_info info;
if (dladdr(func_addr, &info) == 0) {
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!