首页
社区
课程
招聘
[原创]辣鸡版2022腾讯游戏安全竞赛PC赛道决赛writeup
发表于: 2022-5-1 00:30 17318

[原创]辣鸡版2022腾讯游戏安全竞赛PC赛道决赛writeup

2022-5-1 00:30
17318

打开三环的exe,发现是一个MFC的程序。可以看到打开之后创建了一个线程

image-20220424203349918

线程中调用了 NtQuerySystemInformation ,并将 D3DCompile 作为了参数中的一员,初步分析是用于通信的代码。接下来到驱动中找对应的通信函数。

image-20220424203435242

sub_140001438 中,可以看到调用了 ZwSetSystemInformation ,且providersinature和三环传入的参数相同。可以判定这里是在注册通信。 sub_1400013E0 是通信的处理函数。image-20220424203720943

跟进去可以发现 sub_1400014A0 是真正的通信处理函数。至此可以判断三环并没有进行什么操作,只是与零环进行了通信,主要逻辑都在零环实现。

这个函数中,首先获取了 dwm.exeeprocesspid ,然后进行了获取模块地址和获取函数地址的初始化操作。

image-20220424204133182

接下来根据系统版本的不同,用不同的方法寻找特征码并定位到需要hook的函数

image-20220424204245579

接下来申请了两块内存,然后将驱动中保存的 shellcode 进行相应的解密并填充到申请的内存地址中去

image-20220424204418098

之后进行了shellcode中一些参数的填充

image-20220424204503593

在完成参数的填充后,使用mdl+锁页写内存方式,对dwm.exe中dxgi.dll的相关内容进行修改,达到hook的目的。

image-20220424204616926

5秒之后重新使用同样的方式,将hook恢复。因此图像只能被绘制5秒。

image-20220424204734161

再过1.5秒对shellcode的内存进行释放操作

image-20220424210613062

为了方便之后shellcode的定位,这里将恢复hook和释放内存的代码进行patch,使得shellcode和hook不会被清除。分别将+18cb和+19ab处的 jz 改为 jmp

image-20220424210347920

image-20220424210525796

使用CE在 dxgi.dll 中对相应特征码进行查找,可以找到在我本机(21h2)中,hook的位置为 dxgi.dll+6EFC0 。hook后该地址中的内容如下

image-20220424205114913

由于该驱动向dwm.exe中注入了shellcode,因此在shellcode被解密之后使用windbg将shellcode dump下来进行分析。shellcode中的内容如下。

image-20220424205606996

可以看到和初赛的题目类似,也是有三个函数,点进去看也是使用opcode的虚拟机代码。但是发现sub_0并没有发生任何改变。因此这里直接套用我初赛时的代码对其进行分析。分析发现与初赛不同,本次shellcode的绘制并不是先画11个格子的flag再画31个格子的ACE,而是不按顺序地画。因此需要将初赛时的hookhandler函数稍加修改。每次发现坐标在 -1~1 之外的格子时进行旗帜的绘制。修改后的代码如下

为了让钩子正确挂到shellcode+30e的位置,需要进行的流程如下

以下为关键部分的代码。分别在 2022GameSafeRace+16572022GameSafeRace+1791 处挂上inlinehook。hookhandler函数中进行的操作已经在上面给出描述,这里不贴具体代码。

在注入dll并按步骤加载驱动后,可以看到flag被正确绘制了出来。

使用github上的一个开源项目https://github.com/lainswork/dwm-screen-shot ,可以截图。

截图的效果如下

image-20220424213719309

为了给屏幕上的内容进行截图,想到可以对directX中处理swapchain的函数进行hook。直接从swapchain中取出渲染的图像。这样就能绕过通过将图像绘制在不同dc上而达到的反截图效果。一些游戏辅助的绘制代码也是使用了这个思路,对dwm.exe中swapchain相关的一些函数进行挂钩,然后直接在这里面进行绘制,这样就能绕过一般的截图方法。

具体代码删掉了,找别人要的一份绘制的代码,闭源的,不好往出发(,简单来说就是找了一个hook点,可以拿到swapchain。

这里用了DirectXTK库里面的 SaveWICTextureToFile 来将图像保存到硬盘上,代码如下。

最后将得到的dll注入到dwm.exe中,可以成功得到截图。

image-20220501001028272

EXTERN_C VOID HookHandler(PGuestContext context)
{
    static bool flag = false; if (!flag) { flag = true; A("inline hook成功!"); }
 
    float  xgap = 0.064935, ygap = 0.118343; // 同一格子内部距离
    float xbet = 0.012987, ybet = 0.023669; // 两个相邻格子之间的距离
 
    float trans = getScreenZoom() / 1.25;
    xgap *= trans, ygap *= trans, xbet *= trans, ybet *= trans; // 进行缩放
 
    float origin_a1_x = -0.9305;
    float origin_a1_y = 0.906692;
#define eps 1e-2
    if (fabs(getScreenZoom() - 1.00) < eps) origin_a1_y = 0.906692; // 缩放100%
    else if (fabs(getScreenZoom() - 1.25) < eps) origin_a1_y = 0.882840; // 缩放125%
    else if (fabs(getScreenZoom() - 1.50) < eps) origin_a1_y = 0.858773; // 缩放150%
    else if (fabs(getScreenZoom() - 1.75) < eps) origin_a1_y = 0.834448; // 缩放175%
 
    if (fabs(getScreenZoom() - 1.00) < eps) origin_a1_x = -0.9472; // 缩放100%
    else if (fabs(getScreenZoom() - 1.25) < eps) origin_a1_x = -0.9305// 缩放125%
    else if (fabs(getScreenZoom() - 1.50) < eps) origin_a1_x = -0.9236// 缩放150%
    else if (fabs(getScreenZoom() - 1.75) < eps) origin_a1_x = -0.9139// 缩放175%
 
    PInfo info = (PInfo)(context->mRcx);
 
    static int ct = 0;
    ct++;
 
    static int wrong_ct = 0;
    bool wrong_flag = false;
 
    if (info->a1.x < -1.0 || info->a1.x > 1.0 ||
        info->a2.x < -1.0 || info->a2.x > 1.0 ||
        info->a3.x < -1.0 || info->a3.x > 1.0 ||
        info->a4.x < -1.0 || info->a4.x > 1.0 ||
 
        info->a1.y < -1.0 || info->a1.y > 1.0 ||
        info->a2.y < -1.0 || info->a2.y > 1.0 ||
        info->a3.y < -1.0 || info->a3.y > 1.0 ||
        info->a4.y < -1.0 || info->a4.y > 1.0
        )
    {
        wrong_flag = true;
        wrong_ct++;
    }
 
    if (wrong_flag)
    {
        if (wrong_ct <= 6)
        {
            float a1x = origin_a1_x;
            float a1y = origin_a1_y - (wrong_ct - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct <= 9)
        {
            float a1x = origin_a1_x + (wrong_ct - 6) * (xgap + xbet);
            float a1y = origin_a1_y - (4 - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct == 10)
        {
            float a1x = origin_a1_x + (7 - 6) * (xgap + xbet);
            float a1y = origin_a1_y - (2 - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct == 11)
        {
            float a1x = origin_a1_x + (8 - 6) * (xgap + xbet);
            float a1y = origin_a1_y - (3 - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
            wrong_ct = 0;
        }
    }
 
    if (ct == 42) ct = 0;
}
EXTERN_C VOID HookHandler(PGuestContext context)
{
    static bool flag = false; if (!flag) { flag = true; A("inline hook成功!"); }
 
    float  xgap = 0.064935, ygap = 0.118343; // 同一格子内部距离
    float xbet = 0.012987, ybet = 0.023669; // 两个相邻格子之间的距离
 
    float trans = getScreenZoom() / 1.25;
    xgap *= trans, ygap *= trans, xbet *= trans, ybet *= trans; // 进行缩放
 
    float origin_a1_x = -0.9305;
    float origin_a1_y = 0.906692;
#define eps 1e-2
    if (fabs(getScreenZoom() - 1.00) < eps) origin_a1_y = 0.906692; // 缩放100%
    else if (fabs(getScreenZoom() - 1.25) < eps) origin_a1_y = 0.882840; // 缩放125%
    else if (fabs(getScreenZoom() - 1.50) < eps) origin_a1_y = 0.858773; // 缩放150%
    else if (fabs(getScreenZoom() - 1.75) < eps) origin_a1_y = 0.834448; // 缩放175%
 
    if (fabs(getScreenZoom() - 1.00) < eps) origin_a1_x = -0.9472; // 缩放100%
    else if (fabs(getScreenZoom() - 1.25) < eps) origin_a1_x = -0.9305// 缩放125%
    else if (fabs(getScreenZoom() - 1.50) < eps) origin_a1_x = -0.9236// 缩放150%
    else if (fabs(getScreenZoom() - 1.75) < eps) origin_a1_x = -0.9139// 缩放175%
 
    PInfo info = (PInfo)(context->mRcx);
 
    static int ct = 0;
    ct++;
 
    static int wrong_ct = 0;
    bool wrong_flag = false;
 
    if (info->a1.x < -1.0 || info->a1.x > 1.0 ||
        info->a2.x < -1.0 || info->a2.x > 1.0 ||
        info->a3.x < -1.0 || info->a3.x > 1.0 ||
        info->a4.x < -1.0 || info->a4.x > 1.0 ||
 
        info->a1.y < -1.0 || info->a1.y > 1.0 ||
        info->a2.y < -1.0 || info->a2.y > 1.0 ||
        info->a3.y < -1.0 || info->a3.y > 1.0 ||
        info->a4.y < -1.0 || info->a4.y > 1.0
        )
    {
        wrong_flag = true;
        wrong_ct++;
    }
 
    if (wrong_flag)
    {
        if (wrong_ct <= 6)
        {
            float a1x = origin_a1_x;
            float a1y = origin_a1_y - (wrong_ct - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct <= 9)
        {
            float a1x = origin_a1_x + (wrong_ct - 6) * (xgap + xbet);
            float a1y = origin_a1_y - (4 - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct == 10)
        {
            float a1x = origin_a1_x + (7 - 6) * (xgap + xbet);
            float a1y = origin_a1_y - (2 - 1) * (ygap + ybet);
            info->a1.x = a1x, info->a1.y = a1y;
            info->a2.x = a1x + xgap, info->a2.y = a1y;
            info->a3.x = a1x, info->a3.y = a1y - ygap;
            info->a4.x = a1x + xgap, info->a4.y = a1y - ygap;
        }
        else if (wrong_ct == 11)

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 2677
活跃值: (5340)
能力值: ( LV10,RANK:177 )
在线值:
发帖
回帖
粉丝
2
钟师傅tql
2022-5-1 15:06
0
雪    币: 2927
活跃值: (5243)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
3
YenKoc [em_13]钟师傅tql
YenKoc师傅才真的强
2022-5-1 18:00
0
雪    币: 3144
活跃值: (1624)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
牛逼牛逼,赤裸裸教过检测!
2022-5-4 01:35
0
雪    币: 37
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
shun其自然 牛逼牛逼,赤裸裸教过检测!
是反过检测吧/xk
2022-8-30 00:37
0
游客
登录 | 注册 方可回帖
返回
//