Android Exception Hook

Posted mmmmar的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Exception Hook相关的知识,希望对你有一定的参考价值。

承接上一篇文章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寄存器的值确定程序执行的位置。

完整程序如下:

  1 #include <assert.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <stdio.h>
  5 #include <signal.h>
  6 #include <ucontext.h>
  7 #include <sys/mman.h>
  8 
  9 int g_code_type;
 10 long g_code_address;
 11 uint32_t g_code_origin;
 12 uint32_t g_code_next;
 13 
 14 long get_module_addr(pid_t pid, const char *module_name)
 15 {
 16     FILE *fp;
 17     char file_path[256];
 18     char file_line[512];
 19     if (pid < 0) {
 20         snprintf(file_path, sizeof(file_path), "/proc/self/maps");
 21     } else {
 22         snprintf(file_path, sizeof(file_path), "/proc/%d/maps", pid);
 23     }
 24     fp = fopen(file_path, "r");
 25     if (fp == NULL) {
 26         return -1;
 27     }
 28     long addr_start = -1, addr_end = 0;
 29     while (fgets(file_line, sizeof(file_line), fp)) {
 30         if (strstr(file_line, module_name)) {
 31             if (2 == sscanf(file_line, "%8lx-%8lx", &addr_start, &addr_end)) {
 32                 break;
 33             }
 34         }
 35     }
 36     fclose(fp);
 37     return addr_start;
 38 }
 39 
 40 bool change_addr_attr(long address, bool writable) {
 41     //根据内存页大小对齐
 42     long page_size = sysconf(_SC_PAGESIZE);
 43     long page_start = address & (~(page_size - 1));
 44     if (writable == true) {
 45         return mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != -1;
 46     } else {
 47         return mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC) != -1;
 48     }
 49 }
 50 
 51 bool write_code(long address, uint32_t data)
 52 {
 53     if (change_addr_attr(address, true) == false) {
 54         return false;
 55     }
 56     if (g_code_type == IS_THUMB) {
 57         *((uint16_t*)address) = (uint16_t)data;
 58     } else {
 59         *((uint32_t*)address) = data;
 60     }
 61     return change_addr_attr(address, false);
 62 }
 63 
 64 bool write_ill_instruction(long address, uint32_t *save)
 65 {
 66     if (g_code_type == IS_THUMB) {
 67         *save = *((uint16_t*)address);
 68     } else if (g_code_type == IS_ARM) {
 69         *save = *((uint32_t*)address);
 70     }
 71     return write_code(address, 0xFFFFFFFF);
 72 }
 73 
 74 static void signal_handler(int signum, siginfo_t *Ssiginfo, void *context)
 75 {
 76     ucontext_t *uc = context;
 77     struct sigcontext *sigc = &uc->uc_mcontext;
 78   
 79     long next_address = g_code_address + (IS_ARM ? 4 : 2);
 80 
 81     if (sigc->arm_pc == g_code_address) {
 82         //恢复hook点指令
 83         write_code(g_code_address, g_code_origin);
 84         //将hook点下一条指令改为异常指令
 85         write_ill_instruction(next_address, &g_code_next);
 86     } else if (sigc->arm_pc == next_address){
 87         //恢复hook点下一条指令
 88         write_code(next_address, g_code_next);
 89         //将hook点指令改为异常指令
 90         write_ill_instruction(g_code_address, &g_code_origin);
 91     } else {
 92         exit(EXIT_FAILURE);
 93     }
 94 }
 95 
 96 bool hook_exception_make(const char *library, long address, enum code_type type)
 97 {
 98     g_code_type = type;
 99 
100     struct sigaction sig;
101     sigemptyset(&sig.sa_mask);
102     sig.sa_flags = SA_SIGINFO;
103     sig.sa_sigaction = signal_handler;
104     sigaction(SIGILL, &sig, NULL);
105     
106     long target_address = get_module_addr(-1, library);
107     g_code_address = target_address + address;
108     return write_ill_instruction(g_code_address, &g_code_origin);
109 }

 

以上是关于Android Exception Hook的主要内容,如果未能解决你的问题,请参考以下文章

springboot报错说 Failed to parse multipart servlet request; nested exception is java.io.IOException(代码片

mysql 错 Could not open JDBC Connection for transaction; nested exception is java.sql.SQLExceptio(代码片

android inline hook

Hook实现Android 微信,陌陌 ,探探位置模拟

Android Adapter代码片

查找和定位Android应用的按钮点击事件的代码位置基于Xposed Hook实现