好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 OrpheusQ 于 2021-4-14 21:55 编辑
[ 写在前面 ]
<帖子发晚了,前几天都在练科三,今天才考完,得空整理一下分析报告>
快要毕业了,却还没参加过一次CTF之类的比赛,为了不留遗憾,就报名了此次的比赛,作为自己不怎么秀的首秀(决赛肯定是无望了hhhh)。
以下仅仅是个人的分析思路,分享给大家,适合像我一样的逆向初学者阅读 ,也勉强算是为吾爱做出一点微薄的贡献了~
(参考答案or更精彩的解法还得参考同版块下其他/她师傅的帖子)
[主要分析结论]
1. 外挂通过读取 hack.dat 文件中的 flag, 通过自定义算法 1 解码+拼接后生成游戏进程名:
ShooterClient.exe。
2. 外挂通过自定义算法 2 解析出 hack.dll 文件的字节码,并通过写内存、创建远程线程的方
式使得黑模块得以执行。
3. 在黑模块中,通过 GetAsyncKeyState 截获鼠标右键的按下情况作为外挂的开关,以遍
历对象、基址+偏移的方式获取人物坐标,并计算、改写俯仰角(pitch)、偏航角(yaw),以
实现自瞄功能。
[分析过程]
<hack.exe 部分>
1. 先将 hack.exe 拖入 IDA 中静态分析,依次调用 CreateFileA->GetFileSize->ReadFile,
不难猜出,此处读取的文件内容即为 flag。由于文件名是计算出来的,动态调试得:
hack.dat。
2. 之后进入到一个函数 hack.exe@1400014D0,该函数由 2 个 if 语句组成,分别都会进行
解密操作,但是只有第 2 个 if 语句才能解出 flag(第一个 if 是用于解析出黑模块 hack.dll
的,不同的情况下只会进入 1 个 if 语句中):
(注意 :此处对flag的分析不够周到,虽然我猜出来的flag可以开启外挂,但后面看了其他/她师傅的帖子才发现有且仅有一个官方的flag, 原以为只要创建一个能把外挂成功运行起来的hack.dat就算是通过第一关了......不够细*1)
解析 flag 的操作:将 flag 的每个字节与 0x3F 异或,之后减去 0x13。
3. flag 解密完成后,会将 flag 从第 2 字节开始的偶数位拼接到局部变量 ProcName 中,结
合下文分析推测,应该是要打开游戏进程“ShooterClient.exe”,所以可得 flag 解密后
的特征应该是:除 0 外的偶数位保证为特定字节(游戏进程名), 其余位置可为任意值(这里
采用 0x00)。
分析出 flag 的解密过程和 flag 解密后的样子之后,便可反过来计算 flag,计算脚本如下:
使用 16 进制编辑器,写入到 hack.dat 中:
4. 拼接完进程名后,以拍摄快照的方式遍历进程,匹配字符串成功,打开进程:
之后,同样以解密文件名的方式,解析出 ntdll.dll,并获取了该模块中的:LdrLoadDll、
RtlInitAnsiString、RtlFreeUnicodeString、LdrGetProcedureAddress、
NtAllocateVirtualMemory、RtlAnsiStringToUnicodeString 函数地址。
5. 最后,调用 WriteProcessMemory,写入外挂代码,调用 CreateRemoteThreadEx 创建
远程线程执行黑模块代码。
其中写入的部分,最重要的是调用 hack.exe@1400014D0 解密得到的黑模块文件,以下 2图分别为解密前后的内存:
解密 hack.dll 的算法(已在注释中详细说明):
至此,外挂 hack.exe 可以被打开,并成功使用,外挂功能为自瞄。
<hack.dll 部分>
1. 将外挂黑模块 dump 下来分析,dllmain 中创建工作线程,进入线程回调,开头获取了游
戏进程基址,并由基址+偏移的方式获取了多个全局变量,其中就包括较为重要的,游戏
引擎 this 指针。
2. 由于外挂的自瞄功能是需要在鼠标右键按下的情况下启动,所以根据这个特点,查找
GetAsyncKeyState 等相关函数的引用情况可以快速定位到外挂的核心代码:
3. 在该函数中,外挂事先根据游戏引擎的 this 指针(this_engine = [module_base +
0x2F71060]),获取到角色的 this 指针(this_player =
[[[[[module_base+0x2F71060]+0x160]+0x38]+0]+0x30]),并获取角色的 3 维坐标
(float)。
x = [[[this_player+0x360]+0x158]+0x164]
y = [[[this_player+0x360]+0x158]+0x168]
z = [[[this_player+0x360]+0x158]+0x16C]
添加到 ce 中验证一下:
再通过遍历人物对象的方式(调用 hack.dll@180004510),获取到敌人的指针,选出离自
己较近的敌人后(xy 坐标系的距离<24.0),以相同的方式获取其 3 维坐标。
(注意 :此处分析有误,看完其他/她师傅的帖子才发现这里计算的应该是屏幕中心和敌人之间的距离,不够细*2)
之后调用 sqrtf 求出角色和敌人在 xy 坐标系上的相对距离,调用 atan2f 求出俯仰角;再
调用一次 atan2f 求出偏航角,并将所求结果写入到对应的游戏内存中,以实现自瞄:
[this_player + 0x398] = pitch
[this_player + 0x39C] = yaw
[自瞄实现]
大致思路:
·根据基址+偏移的方式获取对象数组,过滤筛选出敌人对象(观察发现敌人对象在对
象数组中的存放顺序在角色之后(推测是先创建了角色 再生成的敌人))。
·计算每个敌人对象与角色对象的距离,选出距离最近的敌人对象,并获得其 3 维坐标。
·通过获取的摄像机矩阵(如图),将 3 维的世界坐标转换成 2 维的屏幕坐标。最后通过
模拟鼠标消息,移动到敌人在屏幕的位置,实现自瞄。
·已将上述思路转成代码的表达形式(附件),遗憾的是最终未能完全实现预期的效
果( 一个没有成功的代码就不在吾爱放出来干扰大家了 ) 。不过在这些天的比赛中,对于一个初次分析 FPS 游戏的自己而言已经是收获不少,学
习到了很多新知识,路漫漫其修远兮,吾将上下而求索!
免费评分
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。