zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    使用postman做接口测试(三)
    使用postman做接口测试(二)
    使用postman做接口测试(一)
    RobotFramework安装扩展库包autoitlibrary(四)
    RobotFramework安装扩展库包Selenium2Library(三)
    记录.gitattributes 设置合并时使用本地文件无效的解决方案
    golang 踩坑日记
    linux常用命令记录
    vim配置文件
    mysql case when记录
  • 原文地址:https://www.cnblogs.com/mmmmar/p/8227915.html
Copyright © 2011-2022 走看看