承接上一篇文章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系统中的信号机制。

  1. void signal_handler(int signum, siginfo_t *Ssiginfo, void *context)
  2. {
  3. //信号处理函数
    ucontext_t *uc = context;
       struct sigcontext *sigc = &uc->uc_mcontext;
  4. }
  5. struct sigaction sig;
  6. //initialize the signal set
  7. sigemptyset(&sig.sa_mask);
  8. //make sigaction.sa_sigaction specifies the
  9. //signal-handling function for signum
  10. sig.sa_flags = SA_SIGINFO;
  11. //attach our handler
  12. sig.sa_sigaction = signal_handler;
  13. sigaction(SIGILL, &sig, NULL);

通过sigaction函数对信号进行注册后我们便可以在信号处理函数中对其进行处理。Linux用户手册中对该函数进行了详细的说明,可参阅:Linux Programmer's Manual SIGACTION(2)

在信号处理函数中我们可以通过结构体sigcontext获取异常发生时各个寄存器的信息,其定义如下:

  1. struct sigcontext {
  2. unsigned long trap_no; unsigned long error_code; unsigned long oldmask;
  3. unsigned long arm_r0; unsigned long arm_r1; unsigned long arm_r2;
  4. unsigned long arm_r3; unsigned long arm_r4; unsigned long arm_r5;
  5. unsigned long arm_r6; unsigned long arm_r7; unsigned long arm_r8;
  6. unsigned long arm_r9; unsigned long arm_r10; unsigned long arm_fp;
  7. unsigned long arm_ip; unsigned long arm_sp; unsigned long arm_lr;
  8. unsigned long arm_pc; unsigned long arm_cpsr;unsigned long fault_address;
  9. };

例如我们可以通过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[];
  18. char file_line[];
  19. if (pid < ) {
  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 -;
  27. }
  28. long addr_start = -, addr_end = ;
  29. while (fgets(file_line, sizeof(file_line), fp)) {
  30. if (strstr(file_line, module_name)) {
  31. if ( == 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 - ));
  44. if (writable == true) {
  45. return mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != -;
  46. } else {
  47. return mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC) != -;
  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 ? : );
  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(-, library);
  107. g_code_address = target_address + address;
  108. return write_ill_instruction(g_code_address, &g_code_origin);
  109. }

Android Exception Hook的更多相关文章

  1. 使用cydia substrate 来进行android native hook

      cydia不仅可以hook java代码,同样可以hook native代码,下面举一个例子来进行android native hook 我是在网上找到的supermathhook这个项目,在他基 ...

  2. android ART hook

    0x00 前言 之前一直都是在Dalvik 虚拟机上在折腾,从Android 4.4开始开始引入ART,到5.0已经成为默认选择.而且最近看到阿里开源的 Dexposed 框架,已经提供了对于andr ...

  3. Android Xpose Hook(一)

    实验环境:     Droid4x模拟器 (目前Android版本4.2.2)     Android Studio 1.下载相关工具 XposedInstaller下载 http://repo.xp ...

  4. ***CI异常记录到日志:CodeIgniter中设计一个全局exception hook

    在CodeIgniter中,当发生异常时,经常要通知系统管理员,因此有必要在全局的高度上 捕捉异常,因此可以写一个hook, 比如在config目录的hook.php中,加入: $hook['pre_ ...

  5. android inline hook

    最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...

  6. Android Native Hook技术(二)

    Hook技术应用 已经介绍了安卓 Native hook 原理,这里介绍 hook 技术的应用,及 Cyida Substrate 框架. 分析某APP,发现其POST请求数据经过加密,我们希望还原其 ...

  7. Android Native Hook技术(一)

    原理分析 ADBI是一个著名的安卓平台hook框架,基于 动态库注入 与 inline hook 技术实现.该框架主要由2个模块构成:1)hijack负责将so注入到目标进程空间,2)libbase是 ...

  8. android的hook方面知识点

    android hook分为另种: native层hook---理解ELF文件 java层---虚拟机特性和Java上的反射的作用 注入代码: 存放在哪? 用mmap函数分配临时内存来完成代码存放,对 ...

  9. Android Exception 13(Can't create handler inside thread that has not called Looper.prepare())

    10-12 17:02:55.500: E/AndroidRuntime(28343): FATAL EXCEPTION: Timer-2 10-12 17:02:55.500: E/AndroidR ...

随机推荐

  1. 将AD域漫游用户配置文件放在samba服务器中

    书接上回https://www.cnblogs.com/jackadam/p/11448497.html 我们已经将linux服务器设置为域成员,启动samba服务后,已经实现了使用域账号验证,自动创 ...

  2. linux:使用python脚本监控某个进程是否存在(不使用crontab)

    背景: 需要每天定时去检测crontab进程是否启动,所以不能用crontab来启动检测脚本了,直接使用while 循环和sleep方式实现定时检测 # coding:utf-8 import os ...

  3. 【VS开发】MFC学习之 解决StretchBlt()图片缩放绘图失真

    vc中位图伸缩函数StretchBlt在对图片进行缩放时会造成严重的图片失真.在了解解决方法前先巩固下StretchBlt的用法: StretchBlt 函数功能:函数从源矩形中复制一个位图到目标矩形 ...

  4. C# 与 .NET Framework 对应关系

    C#各版本新增加功能(系列文章)   本系列文章主要整理并介绍 C# 各版本的新增功能. C# 8.0 C#8.0 于 2019年4月 随 .NET Framework 4.8 与 Visual St ...

  5. 2017ACM/ICPC广西邀请赛 1007 Duizi and Shunzi

    Duizi and Shunzi Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  6. 基于thinkphp5框架做一个可以区别开发、测试、生产三种环境的配置加载

    在日常的开发测试中我们经常会遇到本地开发和测试或者线上配置参数不同的场景,必要你要是使用一个三方的支付,它的本地测试和线上的key值或者账号存在不同.最基本的做法是本地用测试参数,提交到测试的时候再改 ...

  7. 问题三:Appium 的 UIAutomator2 模式下使用 sendKeys 出现错误

    在Appium默认的模式下,可以对TextFiled控件进行sendKeys操作: 设置capabilities.setCapability("automationName",&q ...

  8. python实践项目九:操作文件-修改文件名

    描述:多个文件,文件名名包含美国风格的日期( MM-DD-YYYY),需要将它们改名为欧洲风格的日期( DD-MM-YYYY) 代码1:先创建100个文件名为美国风格日期的文件(文件路径为项目当前路径 ...

  9. QT 5.x 网络资源集锦

    github上的好书:太好了: http://qmlbook.github.io/en/ch01/index.html 论坛: 基于QT的音乐创作软件:(是不是可以跟谷歌的深度学习艺术项目结合) ht ...

  10. Oracle VM VirtualBox安装配置虚拟机Redhat7.6

    首先,准备好材料,需要下载Oracle VM VirtualBox.Oracle19C的安装包.Redhat7.6镜像 下面列出地址: Oracle VM VirtualBox安装包:链接:https ...