承接上一篇文章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[];
char file_line[];
if (pid < ) {
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 -;
}
long addr_start = -, addr_end = ;
while (fgets(file_line, sizeof(file_line), fp)) {
if (strstr(file_line, module_name)) {
if ( == 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 - ));
if (writable == true) {
return mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != -;
} else {
return mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC) != -;
}
} 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 ? : ); 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(-, library);
g_code_address = target_address + address;
return write_ill_instruction(g_code_address, &g_code_origin);
}

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. .py .pyc .pyd .pyw文件区别

  2. ADB命令使用大法

    ​前言 Android开发调试工具ADB的使用.ADB(Android Debug Bridge)是Android SDK中的一个工具, 使用ADB可以直接操作管理Android模拟器或者真实的And ...

  3. 【tensorflow基础】TensorFlow查看GPU信息

    re 1. TensorFlow查看GPU信息; end

  4. ObjectARX创建带文字的线型实例代码

    AcDbLinetypeTable* pLinetypeTable=NULL; Acad::ErrorStatus es = acdbHostApplicationServices()->wor ...

  5. Oracle Spatial分区应用研究之二:按县分区与按省分区对比测试报告

    1.实验目的 在上一轮的实验中,oracle 11g r2版本下,在87县市实验数据的基础上,比较了分表与分区的效率,得出了分区+全局索引效率较高的结论(见上一篇博客).不过我们尚未比较过不同的分区粒 ...

  6. Jmeter3.1 使用及新增报告功能

    一.JMeter官网 下载地址http://jmeter.apache.org/download_jmeter.cgi Jmeter wikihttps://wiki.apache.org/jmete ...

  7. Postman 使用方法详细介绍

    1,下载安装: https://www.getpostman.com/apps 2,打开Postman,如图所示: 3,创建一个接口项目 4,新增接口文件 5,下面以登录接口login为例,介绍如何做 ...

  8. 酷!微软发布新终端工具,Win 10 将自带 Linux 内核

    原创:技术最前线(id:TopITNews) 北京时间 5 月 7 日,2019 年微软 Build 开发者大会在雷德蒙德召开.今年大会上亮点很多,本文汇总一些和开发者相关的内容. 1. Window ...

  9. C语言单链表简单实现(简单程序复杂化)

    PS: goto还是很好玩的. #include <stdio.h> #include <stdlib.h> typedef struct _node{ int value; ...

  10. flask框架(一)——初识Flask

    一.初识flask 1.什么是Flask:Flask是一个python编写的web框架,只是一个内核,默认依赖2个外部库:jinja2模板引擎和WSGI工具集--Werkzeug. 2.安装flask ...