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寄存器的值确定程序执行的位置。
完整程序如下:
#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的更多相关文章
- 使用cydia substrate 来进行android native hook
cydia不仅可以hook java代码,同样可以hook native代码,下面举一个例子来进行android native hook 我是在网上找到的supermathhook这个项目,在他基 ...
- android ART hook
0x00 前言 之前一直都是在Dalvik 虚拟机上在折腾,从Android 4.4开始开始引入ART,到5.0已经成为默认选择.而且最近看到阿里开源的 Dexposed 框架,已经提供了对于andr ...
- Android Xpose Hook(一)
实验环境: Droid4x模拟器 (目前Android版本4.2.2) Android Studio 1.下载相关工具 XposedInstaller下载 http://repo.xp ...
- ***CI异常记录到日志:CodeIgniter中设计一个全局exception hook
在CodeIgniter中,当发生异常时,经常要通知系统管理员,因此有必要在全局的高度上 捕捉异常,因此可以写一个hook, 比如在config目录的hook.php中,加入: $hook['pre_ ...
- android inline hook
最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...
- Android Native Hook技术(二)
Hook技术应用 已经介绍了安卓 Native hook 原理,这里介绍 hook 技术的应用,及 Cyida Substrate 框架. 分析某APP,发现其POST请求数据经过加密,我们希望还原其 ...
- Android Native Hook技术(一)
原理分析 ADBI是一个著名的安卓平台hook框架,基于 动态库注入 与 inline hook 技术实现.该框架主要由2个模块构成:1)hijack负责将so注入到目标进程空间,2)libbase是 ...
- android的hook方面知识点
android hook分为另种: native层hook---理解ELF文件 java层---虚拟机特性和Java上的反射的作用 注入代码: 存放在哪? 用mmap函数分配临时内存来完成代码存放,对 ...
- 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 ...
随机推荐
- RSA 签名、验证、加密、解密帮助类
import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyFactor ...
- pycharm 2019.2 安装包失败
简介: 最近使用学生账号注册了pycharm,貌似全家桶都可以免费用了,就升级了pycharm到最新版. 但是在使用包管理,安装包的时候出错了 提示没有匹配的版本,下面还提示一个 --trusted- ...
- Python十大装B语法
https://blog.csdn.net/xufive/article/details/102856921
- aspose.slides for java去除水印
前一段时间使用aspose.slides for java,处理了下才去除的水印,但是时间一久就忘记了,重新整理了下 下载aspose-slides-19.3-jdk16.jar,重命名为aspose ...
- Spring boot使用Aspose.Slides操作ppt转PDF、转图片
最近要将ppt转为PDF和图片,Apache poi ,jacob都试了下 Apache poi 转图片乱码,处理了,还会存在部分乱码 jacob对系统依赖比较大,必须是windows还得安装MS O ...
- Mysql中TO_DAYS函数
原文地址:https://blog.csdn.net/sinat_19569023/article/details/50417273 SQL博大精深 TO_DAYS函数 返回一个天数! 啊哈?什么天数 ...
- python json模块(15)
JSON 的全称是 JavaScript Object Notation,即 JavaScript 对象符号,它是一种轻量级.跨平台.跨语言的数据交换格式,其设计意图是把所有事情都用设计的字符串来表示 ...
- python递归函数(10)
一个函数在函数体内部调用自己,这样的函数称为递归函数,递归的次数在python是有限制的,默认递归次数是997次,超过997次会报错:RecursionError. 一.递归函数案例 案例一:计算数字 ...
- Windows常用网络命令(3)
5.Netstat Netstat命令可以帮助网络管理员了解网络的整体使用情况.它可以显示当前正在活动的网络连接的详细信息,例如显示网络连接.路由表和网络接口信息,可以统计目前总共有哪些网络连接正在运 ...
- Fiddler如何监听PC和手机