承接上一篇文章 Android Inline Hook ,接下来我们看一下 android 系统中基于异常的 hook 方式,这种方式与 inline hook 相比实现较为简单,但执行效率是它的短板。
exception hook 的执行流程大致如下:
如图所示,在 hook 过程中需要多次对 hook 点指令和 hook 点的下一条指令进行修改,由此造成在执行效率上的损耗。
首先我们需要将 hook 点指令替换为一条不合法的异常指令,当程序执行到该位置时进程会接收到信号 SIGILL(illegal instruction),然后进入到我们注册的 SIgnal Handler 中,在信号处理函里我们需要做两件事,一是执行我们的 hook 逻辑(如修改寄存器的值),二是恢复 hook 点指令并将 hook 点指令的下一条指令替换为异常指令,再恢复程序的运行。当程序运行到 hook 点的下一条指令时会再次触发异常进入信号处理函数,这一次我们需要在信号处理函数中将 hook 点指令再次替换为异常指令,然后恢复 hook 点的下一条指令,最后恢复程序运行。
由此可见,我们在信号处理函数中需要对异常的发生位置进行判断,对 hook 点跟 hook 点的下一条指令进行区分。当然最重要的是理解 Linux 系统中的信号机制。
- void signal_handler(int signum, siginfo_t *Ssiginfo, void *context)
- {
- //信号处理函数
- ucontext_t *uc = context;
- struct sigcontext *sigc = &uc->uc_mcontext;
- }
- struct sigaction sig;
- //initialize the signal set
- sigemptyset(&sig.sa_mask);
- //make sigaction.sa_sigaction specifies the
- //signal-handling function for signum
- sig.sa_flags = SA_SIGINFO;
- //attach our handler
- sig.sa_sigaction = signal_handler;
- sigaction(SIGILL, &sig, NULL);
通过 sigaction 函数对信号进行注册后我们便可以在信号处理函数中对其进行处理。Linux 用户手册中对该函数进行了详细的说明,可参阅: Linux Programmer's Manual SIGACTION(2)
在信号处理函数中我们可以通过结构体 sigcontext 获取异常发生时各个寄存器的信息,其定义如下:
- struct sigcontext {
- unsigned long trap_no; unsigned long error_code; unsigned long oldmask;
- unsigned long arm_r0; unsigned long arm_r1; unsigned long arm_r2;
- unsigned long arm_r3; unsigned long arm_r4; unsigned long arm_r5;
- unsigned long arm_r6; unsigned long arm_r7; unsigned long arm_r8;
- unsigned long arm_r9; unsigned long arm_r10; unsigned long arm_fp;
- unsigned long arm_ip; unsigned long arm_sp; unsigned long arm_lr;
- unsigned long arm_pc; unsigned long arm_cpsr;unsigned long fault_address;
- };
例如我们可以通过 pc 寄存器的值确定程序执行的位置。
完整程序如下:
- #include <assert.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <signal.h>
- #include <ucontext.h>
- #include <sys/mman.h>
- int g_code_type;
- long g_code_address;
- uint32_t g_code_origin;
- uint32_t g_code_next;
- long get_module_addr(pid_t pid, const char *module_name)
- {
- FILE *fp;
- char file_path[256];
- char file_line[512];
- if (pid < 0) {
- snprintf(file_path, sizeof(file_path), "/proc/self/maps");
- } else {
- snprintf(file_path, sizeof(file_path), "/proc/%d/maps", pid);
- }
- fp = fopen(file_path, "r");
- if (fp == NULL) {
- return -1;
- }
- long addr_start = -1, addr_end = 0;
- while (fgets(file_line, sizeof(file_line), fp)) {
- if (strstr(file_line, module_name)) {
- if (2 == sscanf(file_line, "%8lx-%8lx", &addr_start, &addr_end)) {
- break;
- }
- }
- }
- fclose(fp);
- return addr_start;
- }
- bool change_addr_attr(long address, bool writable) {
- //根据内存页大小对齐
- long page_size = sysconf(_SC_PAGESIZE);
- long page_start = address & (~(page_size - 1));
- if (writable == true) {
- return mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != -1;
- } else {
- return mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC) != -1;
- }
- }
- bool write_code(long address, uint32_t data)
- {
- if (change_addr_attr(address, true) == false) {
- return false;
- }
- if (g_code_type == IS_THUMB) {
- *((uint16_t*)address) = (uint16_t)data;
- } else {
- *((uint32_t*)address) = data;
- }
- return change_addr_attr(address, false);
- }
- bool write_ill_instruction(long address, uint32_t *save)
- {
- if (g_code_type == IS_THUMB) {
- *save = *((uint16_t*)address);
- } else if (g_code_type == IS_ARM) {
- *save = *((uint32_t*)address);
- }
- return write_code(address, 0xFFFFFFFF);
- }
- static void signal_handler(int signum, siginfo_t *Ssiginfo, void *context)
- {
- ucontext_t *uc = context;
- struct sigcontext *sigc = &uc->uc_mcontext;
- long next_address = g_code_address + (IS_ARM ? 4 : 2);
- if (sigc->arm_pc == g_code_address) {
- //恢复hook点指令
- write_code(g_code_address, g_code_origin);
- //将hook点下一条指令改为异常指令
- write_ill_instruction(next_address, &g_code_next);
- } else if (sigc->arm_pc == next_address){
- //恢复hook点下一条指令
- write_code(next_address, g_code_next);
- //将hook点指令改为异常指令
- write_ill_instruction(g_code_address, &g_code_origin);
- } else {
- exit(EXIT_FAILURE);
- }
- }
- bool hook_exception_make(const char *library, long address, enum code_type type)
- {
- g_code_type = type;
- struct sigaction sig;
- sigemptyset(&sig.sa_mask);
- sig.sa_flags = SA_SIGINFO;
- sig.sa_sigaction = signal_handler;
- sigaction(SIGILL, &sig, NULL);
- long target_address = get_module_addr(-1, library);
- g_code_address = target_address + address;
- return write_ill_instruction(g_code_address, &g_code_origin);
- }
来源: http://www.bubuko.com/infodetail-2453086.html