Loading... # 前言 为实现通过主线程调用游戏Frame_excute(),来执行自定义脚本,来对新版本增加的 retaddr 检测进行逆向分析。 # 背景知识 函数调用过程中,除了参数赋值,执行Call跳转外,还有个很重要的事,就是将Call 指令下一条指令地址(通常来说就是函数执行完成返回后,执行的第一条指令所在地址,也就是返回地址)push到堆栈中。 这样一来,可以通过rbp/rsp指针获取返回地址,并对返回地址进行合法性校验。 该游戏采用非常规的进程载入方式:createfile、createfilemapping、mapviewoffile来加载到内存空间。 可执行模块属性为read&excute。 # 具体检测分析 ##### 0xE8检测 通过rbp/rsp指针获取返回地址,存放到比如r8寄存器,然后通过`*(unsigned char*)(r8-5)`与`0xE8`进行比较,其伪代码如下所示: ```伪代码 mov r8,qword ptr ss:[rsp+98] //r8为retaddr movzx eax,byte ptr ds:[r8-5] //获取 retaddr-5处硬编码 cmp al,E8 //判断该硬编码是否等于0xE8 je 合法代码 非法代码: xxx 合法代码: xxx ``` ##### 返回地址范围检测 ```伪代码 mov rdx,qword ptr ds:[xxx] //rdx 为模块可执行代码区起始地址 mov eax,dword ptr ds:[xxx] //rax为模块可执行代码区块大小 add rax,rdx cmp r8,rdx jb 非法代码 cmp r8,rax jae 非法代码 合法代码: 当 rdx ≤ r8 < rax 时执行合法代码 xxx 非法代码: xxx ``` ##### 返回地址(retaddr)合法性校验 ```伪代码 经过分析,其核心逻辑如下: 1) ret = retaddr >> 0xE (逻辑位移shr) 2) value = *(yyy+4*ret) value != 0 3) addr = yyy+value 4) value_1 = *addr 5) 判断(value_1 - retaddr) ==0 ``` # 处理思路 该游戏与常规做法:判断retaddr 是否处于模块可执行代码空间范围并不完全一致,增加了特定值检测,给处理带来巨大难度。 ##### 人为构造合法retaddr 通过 pop 合法返回地址,jmp call 的方式,发现:关键call上层依旧存在retaddr检测。 ##### 人工实现关键call功能 通过动态、静态分析,以及与以前无检测版本对比分析,实现关键call人工实现,测试发现:下层3-4层call有进行retaddr检测。 ##### 重新map内存块 根据看雪论坛某个大神回复(www.kanxue.com)`1.暂停掉进程;2.MAP内存块复制一份;3.unmap掉;4.在原位置申请同等大小的内存;5.复制原本的内存数据回去;6.恢复进程运行`进行测试,情况如下: * 通过遍历线程暂停进程,存在无视suspend信号的线程,无法在稳定情况umap特定内存块 * 通过第三方工具暂停进程,umap内存块,游戏将在10秒左右崩溃 ###### 待续 最后修改:2021 年 09 月 22 日 11 : 38 AM © 允许规范转载