0%

Android使用硬件断点调试app

对于一些有 crc 检测,而且注册了 sigaction 的 app 来说,通过异常的方式来追踪检测流程属实是太困难了,不过硬件断点解决了这个难题.
代码来源 rwProcMem33.
本文仅说明使用方法.

设置驱动访问权限及 selinux

参考定制 SELinux 规则 允许 app 访问内核模块
首先加载模块,设置驱动权限

1
2
insmod /data/local/tmp/hwBreakpointProc1.ko
chmod 666 /dev/hwBreakpointProc1

接下来可以关闭 selinux 或设置 sepolicy

1
2
#关闭selinux
setenforce 0

或者

1
2
3
#设置sepolicy
magiskpolicy --live "allow untrusted_app device chr_file { read write ioctl open getattr map }"
magiskpolicy --live "attradd untrusted_app mlstrustedsubject"

app 连接驱动,设置断点

连接驱动

1
2
3
4
5
6
int err = 0;
if (!driver.ConnectDriver(err))
{
LOGD("连接驱动失败\n");
return nullptr;
}

获取当前进程内所有线程 id, 因为硬件断点是针对线程的

1
2
3
4
5
6
7
std::vector<int> vTask;
GetProcessTask(getpid(), vTask);
if (vTask.size() == 0)
{
LOGD("获取当前进程task失败\n");
return nullptr;
}

设置硬件断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for (int task : vTask)
{
//打开task
uint64_t hProcess = driver.OpenProcess(task);
// LOGD("调用驱动 OpenProcess(%d) 返回值:%lu", task, hProcess);
if (!hProcess)
{
LOGD("调用驱动 OpenProcess 失败\n");
fflush(stdout);
continue;
}

//设置进程硬件断点
uint64_t hwBpHandle = driver.AddProcessHwBp(hProcess, (uint64_t)address,
HW_BREAKPOINT_LEN_4, HW_BREAKPOINT_R);
// LOGD("调用驱动 AddProcessHwBp(%lx) 返回值:%lu", address, hwBpHandle);

if (hwBpHandle)
{
vHwBpHandle.push_back(hwBpHandle);
}
//关闭task
driver.CloseHandle(hProcess);
// LOGD("调用驱动 CloseHandle:%lu", hProcess);
}

删除硬断,读取硬件断点信息

删除硬件断点

1
2
3
4
5
6
7
8
9
LOGD("请等待2秒");
sleep(2);
// printf("==========================================================================\n");
//删除进程硬件断点
for (uint64_t hwBpHandle : vHwBpHandle)
{
driver.DelProcessHwBp(hwBpHandle);
// printf("调用驱动 DelProcessHwBp(%" PRIu64 ")\n", hwBpHandle);
}

读取硬件断点命中信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
for (uint64_t hwBpHandle : vHwBpHandle)
{
std::vector<USER_HIT_INFO> vHit;
BOOL b = driver.ReadHwBpInfo(hwBpHandle, vHit);
// printf("==========================================================================\n");
// printf("调用驱动 ReadProcessHwBp(%" PRIu64 ") 返回值:%d\n", hwBpHandle, b);
for (USER_HIT_INFO userhInfo : vHit)
{
LOGD("==========================================================================\n");
LOGD("硬件断点命中地址:%p,命中次数:%zu\n", userhInfo.hit_addr, userhInfo.hit_count);
for (int r = 0; r < 30; r += 5)
{
LOGD("\tX%-2d=%-12llx X%-2d=%-12llx X%-2d=%-12llx X%-2d=%-12llx X%-2d=%-12llx\n",
r, userhInfo.regs.regs[r],
r + 1, userhInfo.regs.regs[r + 1],
r + 2, userhInfo.regs.regs[r + 2],
r + 3, userhInfo.regs.regs[r + 3],
r + 4, userhInfo.regs.regs[r + 4]);
}
LOGD("\tLR=%-12llx SP=%-12llx PC=%-12llx\n",
userhInfo.regs.regs[30],
userhInfo.regs.sp,
userhInfo.regs.pc);

LOGD("\tprocess status:%-12llx orig_x0:%-12llx syscallno:%-12llx\n",
userhInfo.regs.pstate,
userhInfo.regs.orig_x0,
userhInfo.regs.syscallno);
LOGD("==========================================================================\n");
}
}

清空硬件断点命中信息

1
2
driver.CleanHwBpInfo();
LOGD("调用驱动 CleanHwBpInfo()\n");

硬件断点的分类

根据 rwProcMem33 大佬的代码来看
硬件断点分为以下几类

1
2
3
4
5
6
7
8
9
enum
{
HW_BREAKPOINT_EMPTY = 0, //不知道有啥用
HW_BREAKPOINT_R = 1, //读断点
HW_BREAKPOINT_W = 2, //写断点
HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W, //读写断点
HW_BREAKPOINT_X = 4, //执行断点
HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X, //读写执行断点
};

硬件断点的长度为

1
2
3
4
5
6
7
enum
{
HW_BREAKPOINT_LEN_1 = 1, //1字节
HW_BREAKPOINT_LEN_2 = 2, //2字节
HW_BREAKPOINT_LEN_4 = 4, //4字节
HW_BREAKPOINT_LEN_8 = 8, //8字节
};

硬件断点的设置

1
driver.AddProcessHwBp(hProcess, (uint64_t)address, HW_BREAKPOINT_LEN_4, HW_BREAKPOINT_R);

其中,hProcess 是从内核中拿到的线程句柄,address 为断点地址,HW_BREAKPOINT_LEN_4 为断点长度,HW_BREAKPOINT_R 为断点分类.

最后

测试 app Github 地址:
https://github.com/PShocker/HwBpApp

Releases:
https://github.com/PShocker/PShocker.github.io/releases/tag/HwBpApp