在安卓和 iOS 系统中,当程序访问一个没有被加载到内存的内存地址时,就会发生缺页(page fault)。此时,系统会触发一个异常,并执行缺页处理程序来解决这个问题。通常一些游戏会将此作为判断是否内存被读取的检测方案。

image-20221230142700822

缺页简介

通常情况下,缺页处理程序会将缺页的内存地址所在的页面从磁盘中加载到内存中,然后重新执行访问内存地址的指令。但是,如果内存地址所在的页面无法从磁盘加载到内存中,缺页处理程序就会报告一个内存地址无效的错误。

为了判断内存地址是否缺页,可以在安卓和 iOS 系统中使用一些工具来监控缺页的情况。例如,可以使用 Android Studio 或 Xcode 等开发工具中的调试器来查看缺页事件,或者使用命令行工具如 top、vmstat 和 dmesg 等来查看系统的缺页信息。

需要注意的是,在安卓和 iOS 系统中,缺页是一种正常的运行情况,并不一定意味着程序出错或者系统有问题。通常情况下,缺页处理程序能够很好地解决缺页问题,使得程序能够正常运行。

安卓

在 arm64 安卓系统上不访问内存地址的情况下判断内存地址是否缺页的 c/c++ 函数的一种可能的实现方式如下:

[hidecontent type=“reply”]

#include <sys/mman.h>

bool isPageFault(void* address) {
  // 尝试将内存地址标记为无效
  int result = mprotect(address, 1, PROT_NONE);
  if (result == 0) {
    // 如果标记成功,说明内存地址有效
    return false;
  } else {
    // 如果标记失败,说明内存地址无效
    return true;
  }
}

[/hidecontent]

该函数使用了 mprotect 函数来尝试将内存地址标记为无效。 如果内存地址有效,则标记操作会失败,函数返回 false;如果内存地址无效,则标记操作会成功,函数返回 true

IOS

在 arm64 iOS 系统上不访问内存地址的情况下判断内存地址是否缺页的 c/c++ 函数的一种可能的实现方式如下:

[hidecontent type=“reply”]

#include <mach/vm_map.h>
#include <mach/mach_init.h>

bool isPageFault(void* address) {
  // 获取当前进程的任务句柄
  mach_port_t task = mach_task_self();

  // 获取内存地址的保护属性
  vm_region_basic_info_data_64_t info;
  mach_vm_size_t size;
  mach_port_t object_name;
  unsigned int depth;
  kern_return_t result = mach_vm_region(
    task,
    (mach_vm_address_t*) &address,
    &size,
    VM_REGION_BASIC_INFO_64,
    (vm_region_info_t) &info,
    &size,
    &object_name
  );

  // 如果内存地址无效,则返回 true
  if (result != KERN_SUCCESS) {
    return true;
  } else {
    return false;
  }
}

[/hidecontent]

该函数使用了 mach_vm_region 函数来获取内存地址的保护属性。 如果内存地址有效,则函数返回 false;如果内存地址无效,则函数返回 true

这种方法的优点在于不会直接访问内存地址,因此不会存在安全风险。 但是,这种方法仅适用于 iOS 系统,对于其他操作系统可能不适用。