基于文件内部指针拦截maps读写
- 目前想拦截
maps
中的加载记录最好的方式就是进行io
重定向 但是进行重定向需要处理的函数太多了 还需要防止app通过fd
反查文件路径是否是maps
- 这里提供一个思路 我在
app
内实现确实可行-基于文件内部指针 - 有一个弊端是会影响效率
- 并且不是比较通用
- 提供一个思路 并不是很有效的解决方案
- 首先知道 在
int fd = syscall(__NR_openat, AT_FDCWD, "/proc/self/maps", O_RDONLY);
打开一个文件的情况下 文件内部的指针 是在文件的开始 等待读取 - 这个时候 调用
syscall(__NR_read, fd, &byte, 1)
将会读取一个字节到byte
中 内部指针往后移动一位 下次调用循环往复 直到读取到文件结尾
- 那么我们就可以拦截文件的读取
read
在每次传递进来的fd
中 事先读取一整行 判断是否有我们的模块加载记录信息, 当存在我们的加载记录 则直接返回 因为我们主动调用了read
文件内部指针往后移到了下一行 那么这行记录将不会被app
读取到,当不存在 我们把文件内部指针往回移动读取的字节长度 并且设置一个计数器因为每次调用read
读取的是一个字节 我们事先读取了一整行 接下去的那么多字节长度的次数内 都是我们已经判断过的字符串 所以不需要再进行判断, 在指定次数内 不再进入判断逻辑 - 这样一来 当遇到注入的内存段 文件指针跳转到下一行 当没有注入内存段 文件指针回移 不影响其运行
- 下面提供代码以进行测试
LOGE("开始读取maps"); int fd = syscall(__NR_openat, AT_FDCWD, "/proc/self/maps", O_RDONLY); // 使用系统调用打开文件 if (fd == -1) { LOGE("Failed to open maps file"); } char buf[BUF_SIZE]; char byte; int bytesRead; int i = 0; while ((bytesRead = syscall(__NR_read, fd, &byte, 1)) > 0) { // 逐字节读取文件内容 buf[i++] = byte; if (byte == '\n') { // 如果读取到换行符,输出整行内容 buf[i] = '\0'; // 在行末添加字符串终止符 LOGE("%s", buf); i = 0; // 重置缓冲区索引 } } if (bytesRead == -1) { LOGE("Error while reading file"); close(fd); } close(fd);
- 上面的代码读取maps的每一行 进行输出 下面尝试拦截libart.so的加载记录
#define BUF_SIZE 1024 static int count = 0; int checkFd(int fd) { if (count == 0) { char buf[BUF_SIZE] = {0}; char byte; int i = 0; while (syscall(__NR_read, fd, &byte, 1) > 0) { // 逐字节读取文件内容 buf[i++] = byte; if (byte == '\n') { // 如果读取到换行符,输出整行内容 buf[i] = '\0'; // 在行末添加字符串终止符 if (strstr(buf, "libart.so")) { // 读取到指定内容 字节返回fd return fd; } else { // 回移文件指针 off_t current_pos = lseek(fd, 0, SEEK_CUR); syscall(__NR_lseek, fd, current_pos - i, SEEK_SET); count = i; return fd; } } } } count--; return fd; }
- 我们只需要对
read
的fd
额外套一层函数调用即可 bytesRead = syscall(__NR_read, checkFd(fd), &byte, 1)
- 那么再次运行将不会读取到
libart.so
内存段 - 因为每一行都额外增加了读取操作 会比较影响性能
- 这里提供的思路不仅仅是读取