首页
论坛
课程
招聘
[原创] 腾讯游戏安全竞赛2022初赛
2022-4-26 17:34 6051

[原创] 腾讯游戏安全竞赛2022初赛

2022-4-26 17:34
6051

腾讯游戏安全竞赛初赛

Flag截图:

 

image-20220416191644068

解题思路

题目先创建一个1920x1080的窗口,初始化d3d11创建swapchain

 

image-20220416192002417

 

然后进入绘制主循环

 

image-20220416192027328

 

题目用的是shellcode来进行绘制的,这个shellcode看起来是编译器里提取出来的

 

代码将绘制所需的shellcode修复,并调用ZwAllocateVirtualMemory申请RWX内存写入shellcode

 

shellcode入口点为+0x650,传参swapchain指针

 

image-20220416192349203

 

进入shellcode后,先拿ID3D11Device指针和ID3D11DeviceContext指针

 

image-20220416192517993

 

初始化d3d11参数,编译所需的Shader,设置顶点格式,设置Vertex Shader和Pixel Shader

 

image-20220416192710386

 

image-20220416192735634

 

image-20220416192801616

 

提取出来Vertex Shader

1
2
3
4
5
6
7
8
9
10
11
12
cbuffer ConstantBuffer : register(b0){matrix World;matrix View;matrix Projection;}
struct VS_OUTPUT{float4 Pos : SV_POSITION;float4 Color : COLOR0;};
VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    output.Pos = mul(Pos, World);
    output.Pos = mul(output.Pos, View);
    output.Pos = mul(output.Pos, Projection);
    output.Color = Color;
    return output;
}
float4 PS(VS_OUTPUT input) : SV_Target{return input.Color;}

Pixel Shader

1
2
3
4
5
6
7
8
9
struct VSOut{float4 Col : COLOR;float4 Pos : SV_POSITION;};
VSOut VS(float4 Col : COLOR, float4 Pos : POSITION)
{
    VSOut Output;
    Output.Pos = Pos;
    Output.Col = Col;
    return Output;
}
float4 PS(float4 Col : COLOR) : SV_TARGET{return Col;}

由上面的CreateInputLayout函数调用参数得到顶点数据的结构为

1
2
3
4
5
struct Vertex
{
    float x,y,z;
    float r,g,b;
}

纠正一下,因为顶点结构中没有设置alpha通道,所以透明度忽略,而下面代码设置顶点结构大小为28并不代表顶点有alpha通道
图片描述

 

创建顶点缓冲区

 

image-20220416192923873

 

+420是读取内存中的"ACE"文字顶点数据然后调用+0将正方形画到directx11缓冲区里

 

题目的目标是画出flag,所以不管这些顶点数据,定位到画正方形部分逻辑

 

经过反编译后的+0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
__int64 __fastcall drawbox(int a1, int a2, int a3, int a4, __int64 a5, ID3D11DeviceContext *ctx, __int64 buffer, __int64 layout, __int64 vs, __int64 ps)
{
  __int64 v10; // rdi
  ID3D11DeviceContext *ctx_1; // r14
  unsigned int rgba; // ebx
  int v17; // ecx
  float y_top_off; // xmm9_4
  float y_bottom_off; // xmm11_4
  float x_left_off; // xmm7_4
  float x_right_off; // xmm10_4
  Vertex *vtx_buffer; // rcx
  float f255; // xmm0_4
  float mid; // xmm8_4
  float x_left; // xmm6_4
  float y_top; // xmm3_4
  float x_right; // xmm1_4
  float y_bottom; // xmm8_4
  float v29; // xmm7_4
  float v30; // xmm5_4
  float v31; // xmm4_4
  float v32; // xmm2_4
  ID3D11DeviceContextVtbl *v33; // rax
  D3D11_MAPPED_SUBRESOURCE v35; // [rsp+20h] [rbp-C8h] BYREF
  char v36[8]; // [rsp+30h] [rbp-B8h] BYREF
  float v37; // [rsp+38h] [rbp-B0h]
  float v38; // [rsp+3Ch] [rbp-ACh]
  __int64 v39; // [rsp+C8h] [rbp-20h]
  int v40; // [rsp+F8h] [rbp+10h] BYREF
  int v41; // [rsp+100h] [rbp+18h] BYREF
  __int64 v42; // [rsp+108h] [rbp+20h] BYREF
 
  v39 = v10;
  ctx_1 = ctx;
  v40 = 1;
  rgba = a5 + (a3 ^ (a1 + a2)) % 256 - a4 % 256;
  ((void (__fastcall *)(ID3D11DeviceContext *, int *, char *))ctx->lpVtbl->RSGetViewports)(ctx, &v40, v36);
  v17 = (a3 ^ (a2 * a1)) % 256 - (a4 >> 8) % 256;
  y_top_off = (float)(v38 - (float)(2 * (v17 + a2) - 1)) / v38;
  y_bottom_off = (float)(v38 - (float)(2 * a2 + 99)) / v38;
  x_left_off = (float)((float)(2 * (v17 + a1) - 1) - v37) / v37;
  x_right_off = (float)((float)(2 * a1 + 99) - v37) / v37;
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64, _QWORD, __int64, _DWORD, D3D11_MAPPED_SUBRESOURCE *))ctx_1->lpVtbl->Map)(
    ctx_1,
    buffer,
    0i64,
    4i64,
    0,
    &v35);
  vtx_buffer = (Vertex *)v35.pData;
  LODWORD(a5) = 0x437F0000;                     // 255.0
  *((float *)v35.pData + 2) = (float)0;
  f255 = *(float *)&a5;
  mid = (float)((a3 ^ (a2 + a1 * (a2 + 1))) % 256 - (a4 >> 16) % 256);
  x_left = mid + x_left_off;
  y_top = mid + y_top_off;
  vtx_buffer->x = mid + x_left_off;
  x_right = mid + x_right_off;
  vtx_buffer->y = mid + y_top_off;
  y_bottom = mid + y_bottom_off;
  v29 = (float)BYTE2(rgba) / f255;
  v30 = (float)(unsigned __int8)(rgba >> 12) / f255;
  v31 = (float)(unsigned __int8)rgba / f255;
  vtx_buffer->r = v29;
  vtx_buffer->g = v30;
  v32 = (float)HIBYTE(rgba) / f255;
  vtx_buffer->a = v32;
  vtx_buffer[1].z = (float)0;
  vtx_buffer[1].a = v32;
  vtx_buffer[2].z = (float)0;
  vtx_buffer[2].a = v32;
  vtx_buffer[3].a = v32;
  vtx_buffer->b = v31;
  vtx_buffer[1].x = x_right;
  vtx_buffer[1].y = y_top;
  vtx_buffer[1].r = v29;
  vtx_buffer[1].g = v30;
  vtx_buffer[1].b = v31;
  vtx_buffer[2].x = x_left;
  vtx_buffer[2].y = y_bottom;
  vtx_buffer[2].r = v29;
  vtx_buffer[2].g = v30;
  vtx_buffer[2].b = v31;
  vtx_buffer[3].x = x_right;
  vtx_buffer[3].y = y_bottom;
  vtx_buffer[3].r = v29;
  vtx_buffer[3].g = v30;
  vtx_buffer[3].b = v31;
  vtx_buffer[3].z = (float)0;
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64, _QWORD))ctx_1->lpVtbl->Unmap)(ctx_1, buffer, 0i64);
  v33 = ctx_1->lpVtbl;
  LODWORD(v42) = 28;
  v41 = 0;
  ((void (__fastcall *)(ID3D11DeviceContext *, _QWORD, __int64, __int64 *, __int64 *, int *))v33->IASetVertexBuffers)(
    ctx_1,
    0i64,
    1i64,
    &buffer,
    &v42,
    &v41);
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64))ctx_1->lpVtbl->IASetPrimitiveTopology)(ctx_1, 5i64);// D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64))ctx_1->lpVtbl->IASetInputLayout)(ctx_1, layout);
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64, _QWORD, _QWORD))ctx_1->lpVtbl->VSSetShader)(
    ctx_1,
    vs,
    0i64,
    0i64);
  ((void (__fastcall *)(ID3D11DeviceContext *, __int64, _QWORD, _QWORD))ctx_1->lpVtbl->PSSetShader)(
    ctx_1,
    ps,
    0i64,
    0i64);
  ((void (__fastcall *)(ID3D11DeviceContext *, _QWORD, _QWORD, _QWORD))ctx_1->lpVtbl->GSSetShader)(
    ctx_1,
    0i64,
    0i64,
    0i64);
  return ((__int64 (__fastcall *)(ID3D11DeviceContext *, __int64))ctx_1->lpVtbl->Draw)(ctx_1, 4i64);
}

逻辑一目了然,接下来就是hook+420来实现绘制flag图形

 

用CreateProcess的CREATE_SUSPENDED标志在exe入口点加载之前注入dll

 

修改shellcode内容,先jmp过去

1
2
3
4
5
6
g_myrender_draw = (ULONG64)myrender_draw;
p_myrender = (ULONG64)myrender_saver;
 
UCHAR jmpcode[] = "\x48\xB8\x00\x00\x00\x00\x10\x00\x00\x00\x50\xC3";
*(ULONG64*)(jmpcode + 2) = (ULONG64)myproc;
memcpy(MainModule + 0x5460, jmpcode, 12);

因为绘制是单线程的,所以可以任意的使用全局变量

 

先把临时将返回地址保存在全局变量g_lr里,然后将栈中返回地址修改为后半段,跳到myrender_saver函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mov rax,qword ptr [rsp]
mov g_lr,rax
call labl_getrip
labl_getrip:
pop rax
add rax,offset myproc_resume - offset labl_getrip
mov qword ptr [rsp],rax
push p_myrender
ret
 
myproc_resume:
call callgate
push g_lr
db 48h,89h,5Ch,24h,18h
db 89h,54h,24h,10h
db 89h,4Ch,24h,08h
jmp g_resume_ip
 
ret

把本次调用的参数保存到全局变量里,然后赋值g_resume_ip为exe调用ZwAllocateVirtualMemory返回的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void myrender_saver(__int64 a1, __int64 dev, ID3D11DeviceContext* ctx, ID3D11Buffer* buffer_1, ID3D11InputLayout* layout, ID3D11VertexShader* vs, ID3D11PixelShader* ps) {
    static bool first = true;
    if (first) {
        first = false;
        ULONG64 pshellcode = *(ULONG64*)(MainModule + 0x8318);
        pshellcode -= 0x650;
        pshellcode += 0x42D;
        g_resume_ip = (ULONG64)pshellcode;
    }
    g_ctx = ctx;
    g_buffer_1 = buffer_1;
    g_layout = layout;
    g_vs = vs;
    g_ps = ps;
}

返回之后需要保存全部寄存器环境调用自己的绘制函数,调用自己的绘制函数之前还需要强制平衡堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
callgate:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    push rbp
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15
    pushf
 
    call WinAPI_EnterStack
    call g_myrender_draw
    call WinAPI_ExitStack
 
    popf
    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rbp
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret
WinAPI_EnterStack:
    lea r11,[rsp+8]
    and rsp,0fffffffffffffff0h
    push r11
    push r11
    sub rsp,30h
    push qword ptr[r11-8]
    ret
WinAPI_ExitStack:
    pop r11
    add rsp,38h
    pop rsp
    push r11
    ret

最后调用myrender_draw函数,正式开始绘制flag流程

 

因为题目没初始化shader的 WVP matrix,所以绘制的坐标系是d3d11的左手坐标系,(0,0,0)就是屏幕的中心,摄像机坐标在(0,0,0),所以整个1920x1080的屏幕最左边x坐标是-1,最右边x坐标是1,最顶端y坐标是1,最底端y坐标是-1

 

通过调试起来的数据得知一个正方形在dx坐标系里表示高度为0.09259259,宽度为0.05208334,横向间隔为0.01041667,纵向间隔为0.01851857

 

该正方形左上坐标为(-0.57347451,0.46388888)

 

dd111

 

利用该正方形的坐标来推出flag的各个位置来绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
void DrawBox(float x, float y, float w, float h) {
    D3D11_MAPPED_SUBRESOURCE d3d11res = { 0 };
    g_ctx->Map(g_buffer_1, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11res);
    Vertex* vtx = (Vertex*)d3d11res.pData;
    //左上
    vtx[0].x = x;
    vtx[0].y = y;
    vtx[0].z = 0;
    vtx[0].r = 1.0; vtx[0].g = 1.0; vtx[0].b = 0.0; vtx[0].a = 1.0;
 
    //右上
    vtx[1].x = x + w;
    vtx[1].y = y;
    vtx[1].z = 0;
    vtx[1].r = 1.0; vtx[1].g = 1.0; vtx[1].b = 0.0; vtx[1].a = 1.0;
 
    //左下
    vtx[2].x = x;
    vtx[2].y = y - h;
    vtx[2].z = 0;
    vtx[2].r = 1.0; vtx[2].g = 1.0; vtx[2].b = 0.0; vtx[2].a = 1.0;
 
    //右下
    vtx[3].x = x + w;
    vtx[3].y = y - h;
    vtx[3].z = 0;
    vtx[3].r = 1.0; vtx[3].g = 1.0; vtx[3].b = 0.0; vtx[3].a = 1.0;
 
    g_ctx->Unmap(g_buffer_1, 0);
 
    UINT strides = 28;
    UINT off = 0;
    g_ctx->IASetVertexBuffers(0, 1, &g_buffer_1, &strides, &off);
 
    g_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    g_ctx->IASetInputLayout(g_layout);
    g_ctx->VSSetShader(g_vs, 0, 0);
    g_ctx->PSSetShader(g_ps, 0, 0);
    g_ctx->GSSetShader(0, 0, 0);
 
    g_ctx->Draw(4, 0);
 
}
void draw_off(int ox, int oy) {
    const float x_off = 0.01041667f;
    const float y_off = 0.01851857f;
    const float wid = 0.05208334f;
    const float hei = 0.09259259f;
 
    DrawBox(-0.57347451f - ox * wid - ox * x_off, 0.46388888f - oy * hei - oy * y_off, wid, hei);
 
}
void myrender_draw() {
    int o = 0;
 
    draw_off(o + 3, -1);
    draw_off(o + 4, -1);
    draw_off(o + 5, -1);
    draw_off(o + 6, -1);
 
    draw_off(o + 6, 0);
    draw_off(o + 6, 1);
    draw_off(o + 6, -2);
    draw_off(o + 6, -3);
    draw_off(o + 6, -4);
 
    draw_off(o + 5, -3);
    draw_off(o + 4, -2);
 
}

solved


【看雪培训】《Adroid高级研修班》2022年夏季班招生中!

最后于 2022-4-26 21:56 被cslime编辑 ,原因: 加入绘制代码,纠正顶点结构
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (5)
雪    币: 6197
活跃值: 活跃值 (3615)
能力值: ( LV13,RANK:239 )
在线值:
发帖
回帖
粉丝
sunfishi 活跃值 3 2022-4-26 17:38
2
0
mark
雪    币: 9579
活跃值: 活跃值 (966)
能力值: ( LV8,RANK:153 )
在线值:
发帖
回帖
粉丝
CrazymanArmy 活跃值 2 2022-4-26 18:56
3
0
mark
雪    币: 9491
活跃值: 活跃值 (9075)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2 2022-4-26 23:47
4
0
感谢分享
雪    币: 6652
活跃值: 活跃值 (2741)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
v0id_ 活跃值 2022-4-27 12:33
5
0
mark
雪    币: 505
活跃值: 活跃值 (364)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
夏男人 活跃值 2022-5-8 21:42
6
0
mark
游客
登录 | 注册 方可回帖
返回