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 ...
随机推荐
- cesharp 完美支持flash
直接上代码: cefSettings.CefCommandLineArgs.Add("enable-npapi", "1"); //cefSettings.Ce ...
- android RelativeLayout实现左中右布局
RelativeLayout实现左中右布局 <RelativeLayout android:layout_width="match_parent" android:lay ...
- 解决端口被占用问题(端口timewait)
当jmeter做千级并发时,有报错的接口,查看是不是本地端口被占用完了 netstat -an 查看是否有端口在 timewait timewait是知道用那个端口,但是端口被别人占用着 见tcp ...
- rConfig v3.9.2 授权认证与未授权RCE (CVE-2019-16663) 、(CVE-2019-16662)
rConfig v3.9.2 authenticated and unauthenticated RCE (CVE-2019-16663) and (CVE-2019-16662) 原文:https: ...
- 【VS开发】Visual C++内存泄露检测—VLD工具使用说明
Visual C++内存泄露检测-VLD工具使用说明 一. VLD工具概述 Visual Leak Detector(VLD)是一款用于Visual C++的免费的内存泄露检测工具.他的 ...
- [神经网络与深度学习][计算机视觉]SSD编译时遇到了json_parser_read.hpp:257:264: error: ‘type name’ declared as function ret
运行make之后出现如下错误: /usr/include/boost/property_tree/detail/json_parser_read.hpp:257:264: error: 'type n ...
- MiniProfiler 来分析 ASP.NET Core
使用 MiniProfiler 来分析 ASP.NET Core 应用 使用 MiniProfiler 来分析 ASP.NET Core 应用 MiniProfiler(https://minip ...
- 15 SpringMVC的入门案例
1.入门程序的需求 2.搭建开发环境 <1>Create New Project <2>创建一个webapps <3>设置maven路径和解决maven项目创建过慢 ...
- python with方法
在实际的编码过程中,有时有一些任务,需要事先做一些设置,事后做一些清理,这时就需要python with出场了,with能够对这样的需求进行一个比较优雅的处理,最常用的例子就是对访问文件的处理. 一般 ...
- Modelsim——do脚本、bat命令
一.do脚本实现自动化仿真 Modelsim是支持命令的,我们可以用 .do 文件将这些命令先写好然后在Modelsim上调用.因为我的编辑器不支持.do的语法,所以这里改用 .tcl文件,它和 .d ...