承接上一篇文章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. python限定方法参数类型、返回值类型、变量类型等

    typing模块的作用 自python3.5开始,PEP484为python引入了类型注解(type hints) 类型检查,防止运行时出现参数和返回值类型.变量类型不符合. 作为开发文档附加说明,方 ...

  2. 深入理解Flink ---- 系统内部消息传递的exactly once语义

    At Most once,At Least once和Exactly once 在分布式系统中,组成系统的各个计算机是独立的.这些计算机有可能fail. 一个sender发送一条message到rec ...

  3. matlab学习笔记10_7数值计算类型和常用计算公式

    一起来学matlab-matlab学习笔记11 数值数据类型以及特殊函数 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等著 ...

  4. vue-cli3 每次打包都改变css img js文件名,还有自带版本号

    let Version = new Date().getTime(); css: { // 是否使用css分离插件 ExtractTextPlugin extract: { //一种方式,打包后的cs ...

  5. (CSDN迁移)js中的判空

    <c:if test="${something.something == NaN}">style="display: none;"</c:if ...

  6. 测试报告ExtentReport改进

    具体步骤Step-1:在pom.xml文件中添加 Maven 依赖包 <?xml version="1.0" encoding="UTF-8"?> ...

  7. php_mvc实现步骤十

    shop34-19-商品添加 功能:添加商品表单 Index.php?p=back&c=Goods&a=add Controller-Action: 新建商品控制器类 Applicat ...

  8. java并发-ReentrantLock的lock和lockInterruptibly的区别

    ReentrantLock的加锁方法Lock()提供了无条件地轮询获取锁的方式,lockInterruptibly()提供了可中断的锁获取方式.这两个方法的区别在哪里呢?通过分析源码可以知道lock方 ...

  9. Java开发笔记(一百二十一)AWT输入框

    前面介绍了文本标签Label,该控件展示的文字是不可编辑的,若要用户在界面上输入文本,就得使用专门的编辑框控件.在AWT的控件家族当中,用作编辑框的有两种控件,分别是单行输入框TextField和多行 ...

  10. 29 匿名内部类、函数型接口、lamda表达式的引入

    匿名内部类 参考:https://www.runoob.com/w3cnote/java-inner-class-intro.html 进入后搜索匿名内部类. 函数型接口 函数式接口(Function ...