一、追踪目标代码
进入游戏对局后,发现全局World地址无法读取到正确的数据。查看进程的内存映射(maps)发现,该地址对应的虚拟内存区域(VMA)属性已经被设置为---p(即PROT_NONE,不可访问)。

既然外部无法访问,那么进程自身是如何正常访问这些数据的呢?通过在内核层挂钩(hook)mprotect和sigaction系统调用,发现游戏为SIGSEGV(信号11)注册了自定义的信号处理器(signal_handler),专门用于处理因访问被保护内存而触发的异常。

二、SIGSEGV处理概览
游戏注册的sigsegv_handler是整套保护机制的核心。其处理逻辑可以概括为:捕获异常、解析指令、模拟执行、修复上下文、恢复流程。ARM指令的编码规则是理解后续分析的基础,可参考官方文档:ARM Architecture Reference Manual。
下面是sigsegv_handler函数的主要逻辑框架代码:

处理函数首先会判断异常地址(si_addr)是否落在预先设定的受保护内存范围内。如果命中,则根据引发异常的指令地址(pc)读取指令,并按照ARM指令集规范对指令进行分类处理,主要是 加载/存储指令 (Loads & Stores) 和 系统指令 (SYS)。
三、异常指令分类处理
1、处理 Loads & Stores Instructions
这类指令直接读写内存。处理器的第一步是解析指令,提取指令中使用的寄存器编号、立即数等操作数信息,为后续的模拟执行准备上下文(context)数据。

接着,从异常上下文(ucontext_t)中读取目标寄存器的“加密值”。这个值实际上是原始数据与保护密钥进行异或等运算后的结果。处理函数会使用对应的密钥(xor_key1, xor_key2)和内存块信息(size)进行解密,计算出原始数据。
随后,代码会调用sub_75A299FDA0函数。该函数负责为当前线程加载并准备好用于模拟执行单条指令的shellcode代码段,并将需要单步执行的指令(可能是修改后的形式)填充到shellcode的特定位置。
sub_75A299FDA0函数内部会通过mmap申请一块具有读、写、执行权限(RWX)的内存,并将预编译好的shellcode模板写入其中。每个线程独立一份,下文会详述。

在调用shellcode之前,处理器会刷新其所在内存区域的指令缓存和数据缓存(通过DC CVAU和IC IVAU指令),确保CPU能取到最新的指令。然后,使用已经修正了寄存器值(即写入了解密后数据)的异常上下文来执行这段shellcode。
shellcode的本质是将传入的context结构体加载到真实的物理寄存器中,然后执行之前被填充进去的那一条指令,最后将执行结果(寄存器状态)再保存回context结构体。处理器从执行后的context中提取出指令的执行结果。

最终,将shellcode执行得到的结果写回SIGSEGV的上下文寄存器中,并修改程序计数器(PC)使其跳过引发异常的指令,然后恢复进程的正常执行。
2、处理 SYS Instructions
系统指令(如MSR, MRS)的处理逻辑与加载/存储指令类似。处理器同样会解析指令,获取指令隐式使用的寄存器值,进行解密操作后写回上下文。
随后,直接利用准备好的shellcode模拟执行这条系统指令。通过模拟执行来修正指令执行后的上下文状态,最后同样通过修改PC来跳过该异常指令。


3、模拟执行shellcode构造
sub_75A299FDA0函数是构造模拟执行环境的关键。它通过mmap分配RWX内存,并写入用于模拟执行的shellcode。这段shellcode实现了标准流程:从指定地址加载context到所有寄存器 -> 执行一条指令 -> 将所有寄存器状态保存回context。
有趣的是,代码预置了四个功能逻辑几乎完全一致的shellcode副本。它们唯一的区别在于:从context结构体中加载数据时,用于存储context基地址的寄存器不同,分别是X0、X1、X2、X3。
这样设计的目的是为了防止“解析context参数的寄存器”与“异常指令本身要使用的寄存器”发生冲突,确保模拟执行的准确性。

构造完成后,shellcode的地址会被存储在一个与线程相关的自定义结构体中(类似于Windows的TEB,但这里是游戏保护方自己实现的)。同时,函数会刷新这片新shellcode区域的缓存。

以下是以X0寄存器传递context地址的shellcode实例,其他版本的shellcode逻辑与此一致,仅基址寄存器不同。


以上技术分析内容仅供游戏安全领域学习交流,切不可用于非法目的!
总结
这套保护机制的核心思路是:通过mprotect将关键内存区域设置为PROT_NONE(不可访问),同时利用sigaction注册自定义信号处理器来接管访问这些内存时触发的SIGSEGV异常。
在sigsegv_handler中,保护系统完成了一系列精细操作:
- 解析与判断:解析异常地址,确认其属于受保护范围;解析引发异常的ARM指令,获取其操作数、寄存器等详细信息。
- 解密数据:从异常上下文(
context)中读取“加密”的寄存器值,使用预存的密钥进行解密,并将解密后的原始数据写回context。
- 模拟执行:动态构造或复用线程专用的
shellcode,将修正后的context传入,在隔离环境中模拟单步执行引发异常的指令。
- 恢复执行:
shellcode将指令执行结果回写到context中,处理器只需修改PC跳过原指令,即可恢复进程执行,此时上下文中的数据已是解密状态。
至此,内存访问和数据解密在异常处理流程中透明完成,对游戏正常逻辑无感。
引用
本文由看雪论坛优秀文章改编,更多深度技术分析,欢迎访问云栈社区交流讨论。