0%

Android内存读写断点--mprotect

这几天听朋友说 GG 可以在不加载驱动的情况下设置内存读写断点,类似 PC 上 ce 的 "查看谁访问了该内存" 功能。网上也查了下资料,发现其实不难.

简单来说,就是先注册一个异常处理函数,然后把内存属性设置为无法访问 (PROT_NONE), 然后当程序读取或改写这段内存后会触发异常,在异常处理函数里获取到 pc 寄存器的值后恢复内存属性即可.

1. 注册异常处理函数

1
2
3
4
5
struct sigaction act ;
memset(&act ,0 ,sizeof(act));
act.sa_sigaction = &handler ; //异常处理函数
act.sa_flags =SA_SIGINFO |SA_RESETHAND|SA_NODEFER;
sigaction(SIGSEGV,&act ,NULL); //注册异常处理函数,SIGSEGV表示读取或写入无效内存的错误类型
1
2
3
4
5
6
7
8
9
10
11
12
13
void handler (int sig ,siginfo_t *siginfo , void * context){
//匹配我们要监控的地址
if (siginfo->si_addr==&i){
ucontext_t ctx=(*(ucontext_t*)context);
LOGD("pc = 0x%llx \n",ctx.uc_mcontext.pc); //查看是哪个代码访问了这段内存
}
//还原原来内存的属性
int pageSize = getpagesize();
unsigned long addr= reinterpret_cast<unsigned long>(&start);
unsigned long mem = addr & (~(pageSize - 1));
LOGD("mprotect:%d",mprotect(reinterpret_cast<void *>(mem), pageSize, PROT_READ|PROT_WRITE));
return;
}

2. 设置要读取的内存的属性,注意这个内存必须按页对齐。这里以读取 i 为例

1
2
3
4
5
6
int pageSize = getpagesize();
unsigned long addr= reinterpret_cast<unsigned long>(&i);
start = addr & (~(pageSize - 1)); //按页对齐,start表示对齐后的内存地址
LOGD("start:%p ",start);
LOGD("pageSize:%p ",pageSize);
LOGD("mprotect:%d",mprotect(reinterpret_cast<void *>(start), pageSize, PROT_NONE)); //0表示成功,-1表示失败

效果如下: