-
-
[原创]南极动物厂 游戏安全2024决决决赛 PC
-
发表于: 2024-4-22 00:00 2893
-
腾讯游戏安全2024决赛-PC-WriteUp
感谢宝子们对我的帮助!
特别感谢!逍哥!遥哥!呜呜呜呜!
ps:就是觉得挺有意思,稍微模仿一下.
简单来说,题目为编写程序实现注册机,以及内存扫描
在1-2-3中提到的为实现注册代码
文中提到样本需要在 "C:\card.txt" 中放置注册码
那么必然会使用读取文件API
那么内核读文件API有那些呢?
Zw/NtCreateFile
Zw/NtOpenFile
IoCreateFile
FltCreateFileEx
包含不仅限与上列 函数.
我们HOOK监控这些函数,得知样本使用ZwCreateFile打开文件,ZwReadFile 函数读取文件
通过windbg 断在ZwReadFile 读取卡密文件时,使用命令 ba r1 读取缓冲区
一路跟踪到:
v3 = sub_FFFFF80789969B40(v9, qword_FFFFF8078996AC90, 0i64); if ( v3 != -1 ) { str_userName_Key_fenge_sub_FFFFF8078996A41C(v9, user_name, 0i64, v3); str_userName_Key_fenge_sub_FFFFF8078996A41C(v9, key_so, v3 + 1, -1i64); v0 = str_c_str_sub_FFFFF8078996820C((__int64)key_so); str_to_int_sub_FFFFF8078996A7D0(v0, (__int64)&str_g_323032, 10i64); if ( !*str_g_323032 ) { user_name_str_len = get_str_len((__int64)user_name); user_name_1 = str_c_str_sub_FFFFF8078996820C((__int64)user_name); calc_user_name_sub_FFFFF80789969CBC(user_name_1, user_name_str_len); } sub_FFFFF80789967028((__int64)key_so); sub_FFFFF80789967028((__int64)user_name); } }
其中这个用与解析key,他是读取key字符串转换成4字节整数,稍后他将与计算用户名函数返回值做对比
str_to_int_sub_FFFFF8078996A7D0
这个函数用于计算用户名与key的对应关系
calc_user_name_sub_FFFFF80789969CBC(user_name_1, user_name_str_len);
这个函数有简单的混淆,我们手动去除,看看真实,到底有多真.
__int64 __fastcall sub_FFFFF8078A45C131(char* _RCX, unsigned __int64 user_name_str_len) { int i; // [rsp+4h] [rbp-24h] unsigned int v4; // [rsp+8h] [rbp-20h] v4 = 0; for (i = 0; i < user_name_str_len; ++i) v4 = 65599 * (v4 + (unsigned __int8)_RCX[i]); return v4; }
就是这么真实,朴实无华的操作.
附上出题人真实姓名图(还是怕难为我胖虎):
接下来就是第5点了.为什么没有第4点?因为我不知道他到底要干什么????
what? 邓总:what are you talking about??
好的,扫描ShellCode,既然提到ShellCode,那么必然会触发释放ShellCode
我们Hook ExAllocatePoolWithTag 函数,找到他申请的内存
分析内存中有什么
API_JMP
PE
这文中我们一直在HOOK来达到监控的目的,其中我没有提一个坑,那就是API_JMP
我们通常HOOK 函数的头部,因为可以很方便的调用原始函数,以及拦截参数.
API_JMP 做了什么呢?
读取API第一指令字节码,填充头 + 跳转字节,如下
ffffb188`1a382183 681570fb4d push 4DFB7015h ffffb188`1a382188 c744240405f8ffff mov dword ptr [rsp+4],0FFFFF805h ffffb188`1a382190 c3 ret
我们观察到 他申请API_JMP 内存为0x1000 字节,调用地址是随机的,(他在强迫我们进行搜索)
哪里有压迫哪里就有反抗,举起手中的大..
每个API_JMP 都是独立的一个页面(0x1000)
我们提取特征:
68 ?? ?? ?? ?? c7 44 24 04 ?? ?? ?? ?? c3
那么接下来就要说到PE了,这个就比较无脑且特征很多,我们可随意发挥
而笔者则是直接把PE头0x1000字节作为特征码.
在内核中扫描内存,特别是x64中是比较困难的.
x64内核空间无比巨大,我们需要效率(需要快),当然这里举例的是暴力扫描
x64内核中是没有像应用层那样使用可以查询内存的API
我们需要获取获取"内核布局"
win10 1909逆向(反向计算windows内核内存布局及代码实现)
直接一手 CV 不解释.
根据拦截到的信息,我们知道他申请ShellCode内存为:NonPagedPool
我们的范围就定在 0x100000000000 大小了.
接下来只需要编写扫描函数就可以了
好的,完!
对了,编写查找函数还是有许多地方要注意的.
建议:自己编写一遍.