首页
社区
课程
招聘
[原创]2022游戏安全技术竞赛初赛writeup
发表于: 2022-4-28 00:14 5113

[原创]2022游戏安全技术竞赛初赛writeup

2022-4-28 00:14
5113

感想

在朋友的推荐下,第一次参加这个竞赛,也许也是第一次接触ctf相关的竞赛,在这次比赛中收获到很多实用的技巧,同时也看到了自己的欠缺之处,明白了自己的逆向之路前途漫漫,还有很长的路需要走。慢慢来吧,让那个从小就植根自己心中的种子慢慢发芽,用心呵护它,在一次次比赛的经验中获得成长,我很幸运结实了很多既是良师也是益友的前辈(@はつゆき @はつゆき),在我对所做之事迷茫时给予我鼓励与引导,在未来的逆向之路上,也请多多指教了。

 

以下为初赛参赛原提交wp

2022游戏安全技术竞赛初赛PC端题解

参赛人

xxx

初步浏览

先看一眼题目,发现是一个dx11绘图程序,需要把丢失的11个黄色小方块找回来

 

要求不能自行手动绘制,颜色必须一致

 

打开程序后,等了不到5s程序的蓝色小方块就消失了,开始以为是系统出问题了,尝试多次是同样的结果,确定是程序故意让他消失的

IDA初步分析

用IDA打开程序,定位到WinMain

 

发现程序有禁止重复打开,这给同时IDA调试和外部测试造成了一些不便

1
2
3
hObject = CreateMutexA(0i64, 0, "avoid repeat open");
if ( GetLastError() == 183 )                  // 重复打开则关闭
  return 0;

然后就是一些标准的建立窗口操作,通过AdjustWindowRect让窗口高宽自适应当前系统的高宽

 

窗口创建完毕后开始挂接dx11,此时发现一个大小40的结构体,存储了一些全局的变量,包括dx11的设备,主程序的实例句柄等等

1
size40Arr = (AStruct *)operator new(0x40ui64);

逆向出来的结构如下:

1
2
3
4
5
6
7
8
9
10
11
struct AStruct
{
    unsigned __int64 dx11vt;
    unsigned __int64 hInstance;
    unsigned __int64 hwnd;
    unsigned __int64 DRIVER_TYPE;
    ID3D11Device* g_pd3dDevice;
    ID3D11DeviceContext* g_pd3dDeviceContext;
    IDXGISwapChain* g_pSwapChain;
    ID3D11RenderTargetView* g_mainRenderTargetView;
};

开始初始化d3d程序,是非常平常的初始化,全局有关d3d的指针都存在这个结构体里

 

接着开始窗口循环,循环结束后释放d3d资源,因此关注中心来到了循环中反复调用的一个函数void __fastcall AllocateAndFreeMemory(AStruct *a1)

Shellcode在内存的写入与释放

对申请和释放内存的函数,逆向如下:

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
void __fastcall AllocateAndFreeMemory(AStruct *a1)
{
  ID3D11DeviceContext *pd3dDeviceContext; // rcx
  ID3D11RenderTargetView *mainRenderTargetView; // rdx
  HMODULE v4; // rax
  FARPROC ZwAllocateVirtualMemory; // rax
  char *v6; // rbx
  HMODULE v7; // rax
  FARPROC ZwFreeVirtualMemory; // rax
  void *out_allocate_baseaddress; // [rsp+30h] [rbp-50h] BYREF
  __int64 region_size; // [rsp+38h] [rbp-48h] BYREF
  float color[4]; // [rsp+40h] [rbp-40h] BYREF
  CHAR ModuleName[16]; // [rsp+50h] [rbp-30h] BYREF
  CHAR ProcName[16]; // [rsp+60h] [rbp-20h] BYREF
  char v14[8]; // [rsp+70h] [rbp-10h] BYREF
 
  pd3dDeviceContext = a1->g_pd3dDeviceContext;
  if ( pd3dDeviceContext )
  {
    mainRenderTargetView = a1->g_mainRenderTargetView;
    *(_OWORD *)color = background_color;
    ((void (__fastcall *)(ID3D11DeviceContext *, ID3D11RenderTargetView *, float *))pd3dDeviceContext->lpVtbl->ClearRenderTargetView)(
      pd3dDeviceContext,
      mainRenderTargetView,
      color);
    if ( !is_inited )
    {
      *(__m128i *)ProcName = _mm_load_si128((const __m128i *)&xmmword_7FF60B813490);
      out_allocate_baseaddress = 0i64;
      region_size = 11257i64;
      strcpy(ModuleName, "ntdll.dll");
      strcpy(v14, "lMemory");
      v4 = GetModuleHandleA(ModuleName);
      ZwAllocateVirtualMemory = GetProcAddress(v4, ProcName);
      if ( ZwAllocateVirtualMemory )
        ((void (__fastcall *)(__int64, void **, _QWORD, __int64 *, int, int))ZwAllocateVirtualMemory)(
          -1i64,
          &out_allocate_baseaddress,
          0i64,
          &region_size,
          4096,
          64);
      v6 = (char *)out_allocate_baseaddress;
      g_allocate_baseaddress = (__int64)out_allocate_baseaddress;
      if ( out_allocate_baseaddress )
      {
        memcpy(out_allocate_baseaddress, &shellcode_1, 0x1301ui64);
      }
      else
      {
        *errno() = 22;
        invalid_parameter_noinfo();
      }
      *(_DWORD *)v6 = 1405389640;
      *((_DWORD *)v6 + 1) = 1096242773;
      *((_WORD *)v6 + 4) = 16726;
      if ( v6 == (char *)-4865i64 )
      {
        *errno() = 22;
        invalid_parameter_noinfo();
      }
      else
      {
        memcpy(v6 + 4865, &shellcode_2, 0x18F0ui64);
      }
      *(_QWORD *)(v6 + 11249) = D3DCompile;
      another_function = (__int64 (__fastcall *)(_QWORD))(v6 + 1616);
      *((_DWORD *)v6 + 411) = 9601;
      *(_DWORD *)(v6 + 1097) = 3764;
      *((_DWORD *)v6 + 384) = 4865;
      is_inited = 1;
      start_tick_count = GetTickCount();
    }
    if ( another_function )
    {
      another_function(a1->g_pSwapChain);
      if ( GetTickCount() - start_tick_count > 0xFA0 )// 超过4秒就会消失
      {
        out_allocate_baseaddress = (void *)g_allocate_baseaddress;
        *(__m128i *)ProcName = _mm_load_si128((const __m128i *)&xmmword_7FF60B8134A0);
        region_size = 11257i64;
        strcpy(ModuleName, "ntdll.dll");
        strcpy(v14, "ory");
        v7 = GetModuleHandleA(ModuleName);
        ZwFreeVirtualMemory = GetProcAddress(v7, ProcName);
        if ( ZwFreeVirtualMemory )
          ((void (__fastcall *)(__int64, void **, __int64 *, __int64))ZwFreeVirtualMemory)(
            -1i64,
            &out_allocate_baseaddress,
            &region_size,
            0x4000i64);
        memset(&shellcode_1, 0, 0x1301ui64);
        memset(&shellcode_2, 0, 0x18F0ui64);
        another_function = 0i64;
      }
    }
    ((void (__fastcall *)(IDXGISwapChain *, _QWORD, _QWORD))a1->g_pSwapChain->lpVtbl->Present)(
      a1->g_pSwapChain,
      0i64,
      0i64);
  }
}

来到AllocateAndFreeMemory这个函数,发现他导入两个ntdll的函数,动态调试后发现这两个函数分别是ZwAllocateVirtualMemoryZwFreeVirtualMemory

 

申请好大小为 11257 的内存空间后,程序向里面写入了一堆数据,并做了一些简单的解密工作,同时把D3DCompile的函数地址写入了这块内存空间,然后设置一个变量为1表明初始化过程已经完成,可以开始绘制。因此我们已经可以初步猜测,程序写入的数据就是绘制的shellcode,并且自行完成了D3DCompile的重定位

 

继续观察代码发现程序调用GetTickCount()求差值,获取程序已经启动时间,并且在启动时间大于4000ms后会调用ZwFreeVirtualMemory释放shellcode的内存,因此我们之前打开程序后4秒钟蓝色框消失就解释的通了。

 

由于程序中没有其他地方调用绘图函数,因此我们接下来重心将放在shellcode分析上

Shellcode 的初步分析

为了防止我们调试分析过程中,超过4秒后shellcode被释放,我们从GetTickCount()入手,hook此函数并总是返回一个固定值,这样程序就不会主动去释放内存了

 

同时,为了弄清楚程序到底申请的shellcode地址在何处,我们从ZwAllocateVirtualMemory入手,hook此函数并记录下申请大小为11257的调用所获得的内存地址,此地址即为shellcode所在地址

 

void __fastcall AllocateAndFreeMemory(AStruct *a1)调用shellcode的情况来看,该shellcode应该包括不止一个函数,CE查看对应内存地址也发现,该区域有多个函数,我们直接使用IDA反编译第一个Shellcode函数,也即从AllocateAndFreeMemory调用的another_function这个函数

 

从调用情况可以推断,这个函数的参数仅有一个,即IDXGISwapChain* swapchain,地址为shellcode + 0x650,我们这里暂且称其为__int64 __fastcall DistributeSub(IDXGISwapChain* swapchain)

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
__int64 __fastcall DistributeSub(IDXGISwapChain* swapchain)
{
    __int64 v2; // rsi
    __int64 len1; // rdx
    __int64 len2; // rdx
    ID3D11DeviceVtbl* v5; // rdi
    __int64 v6; // rbx
    __int64 v7; // rax
    ID3D11DeviceVtbl* v8; // rdi
    __int64 v9; // rbx
    __int64 v10; // rax
    ID3D11DeviceVtbl* v11; // rdi
    __int64 v12; // rbx
    __int64 v13; // rax
    __int64 v14; // rdx
    ID3D11DeviceVtbl* v15; // rdi
    __int64 v16; // rbx
    __int64 v17; // rax
    ID3D11DeviceVtbl* v18; // rdi
    __int64 v19; // rbx
    __int64 v20; // rax
    ID3D11DeviceVtbl* v21; // rdi
    __int64 v22; // rbx
    __int64 v23; // rax
    __int64 v24; // rdx
    __int64 v25; // rcx
    ID3D11Device* pDevice; // [rsp+60h] [rbp-A0h] BYREF
    char v28[8]; // [rsp+68h] [rbp-98h] BYREF
    char v29[8]; // [rsp+70h] [rbp-90h] BYREF
    char v30[16]; // [rsp+78h] [rbp-88h] BYREF
    int IId[4]; // [rsp+88h] [rbp-78h] BYREF
    int __uuidof_ID3D11Texture2D[4]; // [rsp+98h] [rbp-68h] BYREF
    ID3DBlob* v33; // [rsp+A8h] [rbp-58h] BYREF
    ID3D11RenderTargetView* RenderTargetView; // [rsp+B0h] [rbp-50h] BYREF
    ID3D11Texture2D* Texture2D; // [rsp+B8h] [rbp-48h] BYREF
    __int64 v36; // [rsp+C0h] [rbp-40h] BYREF
    void(__fastcall * pD3DCompile)(char*, __int64, _QWORD, __int128*, _QWORD, char*, char*, _DWORD, _DWORD, ID3DBlob**, __int64*); // [rsp+C8h] [rbp-38h]
    ID3DBlob* v38; // [rsp+D0h] [rbp-30h] BYREF
    ID3D11DeviceContext* context; // [rsp+D8h] [rbp-28h] BYREF
    ID3D11InputLayout* D3D11InputLayout; // [rsp+E0h] [rbp-20h] BYREF
    ID3D11PixelShader* D3D11PixelShader2; // [rsp+E8h] [rbp-18h] BYREF
    ID3D11PixelShader* D3D11PixelShader3; // [rsp+F0h] [rbp-10h] BYREF
    ID3D11Buffer* D3D11Buffer; // [rsp+F8h] [rbp-8h] BYREF
    __int64 v44; // [rsp+100h] [rbp+0h] BYREF
    __int64 v45; // [rsp+108h] [rbp+8h] BYREF
    __int64 v46; // [rsp+110h] [rbp+10h] BYREF
    float v47; // [rsp+118h] [rbp+18h]
    float v48; // [rsp+11Ch] [rbp+1Ch]
    int v49; // [rsp+120h] [rbp+20h]
    float v50; // [rsp+124h] [rbp+24h]
    int v51[4]; // [rsp+128h] [rbp+28h] BYREF
    __int64 v52; // [rsp+138h] [rbp+38h]
    _DWORD v53[60]; // [rsp+140h] [rbp+40h] BYREF
    char v54[440]; // [rsp+230h] [rbp+130h] BYREF
    int v55; // [rsp+3E8h] [rbp+2E8h]
    ID3D11InputLayout* D3D11InputLayout2; // [rsp+3F0h] [rbp+2F0h] BYREF
    ID3D11VertexShader* D3D11VertexShader1; // [rsp+3F8h] [rbp+2F8h] BYREF
    ID3D11PixelShader D3D11PixelShade1; // [rsp+400h] [rbp+300h] BYREF
    char* v59; // [rsp+410h] [rbp+310h] BYREF
    int v60; // [rsp+418h] [rbp+318h]
    __int64 v61; // [rsp+41Ch] [rbp+31Ch]
    __int64 v62; // [rsp+424h] [rbp+324h]
    int v63; // [rsp+42Ch] [rbp+32Ch]
    char* v64; // [rsp+430h] [rbp+330h]
    int v65; // [rsp+438h] [rbp+338h]
    __int64 v66; // [rsp+43Ch] [rbp+33Ch]
    __int64 v67; // [rsp+444h] [rbp+344h]
    int v68; // [rsp+44Ch] [rbp+34Ch]
    __int128 v69; // [rsp+450h] [rbp+350h] BYREF
    D3D11_TEXTURE2D_DESC* TEXTURE2D_DESC[2]; // [rsp+460h] [rbp+360h] BYREF
    __int128 v71; // [rsp+470h] [rbp+370h]
    __int64 v72; // [rsp+480h] [rbp+380h]
    int v73; // [rsp+488h] [rbp+388h]
    char v74[8]; // [rsp+4C0h] [rbp+3C0h] BYREF
    char v75[8]; // [rsp+4C8h] [rbp+3C8h] BYREF
    int v76; // [rsp+4D0h] [rbp+3D0h]
    char v77[8]; // [rsp+4D8h] [rbp+3D8h] BYREF
 
    pD3DCompile = *(void(__fastcall**)(char*, __int64, _QWORD, __int128*, _QWORD, char*, char*, _DWORD, _DWORD, ID3DBlob**, __int64*))D3DCompile_;
    IId[0] = -613454373;
    IId[1] = 1317579895;
    IId[2] = -1652468862;
    IId[3] = 1089584121;
    ((void(__fastcall*)(IDXGISwapChain*, int*, ID3D11Device**))swapchain->lpVtbl->GetDevice)(
        swapchain,
        IId,
        &pDevice);
    ((void(__fastcall*)(IDXGISwapChain*))swapchain->lpVtbl->AddRef)(swapchain);
    ((void(__fastcall*)(ID3D11Device*, ID3D11DeviceContext**))pDevice->lpVtbl->GetImmediateContext)(pDevice, &context);
    Texture2D = 0i64;
    RenderTargetView = 0i64;
    __uuidof_ID3D11Texture2D[0] = 1863690994;
    __uuidof_ID3D11Texture2D[1] = 1317655048;
    __uuidof_ID3D11Texture2D[2] = -1790397286;
    __uuidof_ID3D11Texture2D[3] = -1672490187;
    ((void(__fastcall*)(IDXGISwapChain*, _QWORD, int*, ID3D11Texture2D**))swapchain->lpVtbl->GetBuffer)(
        swapchain,
        0i64,
        __uuidof_ID3D11Texture2D,
        &Texture2D);
    ((void(__fastcall*)(ID3D11Device*, ID3D11Texture2D*, _QWORD, ID3D11RenderTargetView**))pDevice->lpVtbl->CreateRenderTargetView)(
        pDevice,
        Texture2D,
        0i64,
        &RenderTargetView);
    *(_OWORD*)TEXTURE2D_DESC = 0i64;
    v71 = 0i64;
    v72 = 0i64;
    v73 = 0;
    ((void(__fastcall*)(ID3D11Texture2D*, D3D11_TEXTURE2D_DESC**))Texture2D->lpVtbl->GetDesc)(
        Texture2D,
        TEXTURE2D_DESC);
    ((void(__fastcall*)(ID3D11DeviceContext*, __int64, ID3D11RenderTargetView**, _QWORD))context->lpVtbl->OMSetRenderTargets)(
        context,
        1i64,
        &RenderTargetView,
        0i64);
    v76 = 1;
    v46 = 0i64;
    v47 = (float)SLODWORD(TEXTURE2D_DESC[0]);
    v48 = (float)SHIDWORD(TEXTURE2D_DESC[0]);
    v49 = 0;
    v50 = (float)1;
    ((void(__fastcall*)(ID3D11DeviceContext*, __int64, __int64*))context->lpVtbl->RSSetViewports)(context, 1i64, &v46);
    v69 = 0i64;
    v33 = 0i64;
    v38 = 0i64;
    v44 = 0i64;
    v55 = 0;
    strcpy(
        v54,
        "cbuffer ConstantBuffer : register(b0){matrix World;matrix View;matrix Projection;}struct VS_OUTPUT{float4 Pos : SV_P"
        "OSITION;float4 Color : COLOR0;};VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR){VS_OUTPUT output = (VS_OUT"
        "PUT)0;output.Pos = mul(Pos, World);output.Pos = mul(output.Pos, View);output.Pos = mul(output.Pos, Projection);outpu"
        "t.Color = Color;return output;}float4 PS(VS_OUTPUT input) : SV_Target{return input.Color;}");
    strcpy(v74, "VS");
    strcpy(v28, "vs_4_0");
    strcpy(v75, "PS");
    strcpy(v29, "ps_4_0");
    v2 = -1i64;
    len1 = -1i64;
    do
        ++len1;
    while (v54[len1]);
    pD3DCompile(v54, len1, 0i64, &v69, 0i64, v74, v28, 0, 0, &v33, &v44);
    len2 = -1i64;
    do
        ++len2;
    while (v54[len2]);
    pD3DCompile(v54, len2, 0i64, &v69, 0i64, v75, v29, 0, 0, &v38, &v44);
    v5 = pDevice->lpVtbl;
    v6 = ((__int64(__fastcall*)(ID3DBlob*))v33->lpVtbl->GetBufferSize)(v33);
    v7 = ((__int64(__fastcall*)(ID3DBlob*))v33->lpVtbl->GetBufferPointer)(v33);
    ((void(__fastcall*)(ID3D11Device*, __int64, __int64, _QWORD, ID3D11VertexShader**))v5->CreateVertexShader)(
        pDevice,
        v7,
        v6,
        0i64,
        &D3D11VertexShader1);
    v8 = pDevice->lpVtbl;
    v9 = ((__int64(__fastcall*)(ID3DBlob*))v38->lpVtbl->GetBufferSize)(v38);
    v10 = ((__int64(__fastcall*)(ID3DBlob*))v38->lpVtbl->GetBufferPointer)(v38);
    ((void(__fastcall*)(ID3D11Device*, __int64, __int64, _QWORD, ID3D11PixelShader*))v8->CreatePixelShader)(
        pDevice,
        v10,
        v9,
        0i64,
        &D3D11PixelShade1);
    strcpy(v30, "POSITION");
    strcpy(v77, "COLOR");
    v59 = v30;
    v60 = 0;
    v61 = 6i64;
    v62 = 0i64;
    v63 = 0;
    v64 = v77;
    v65 = 0;
    v66 = 6i64;
    v67 = 12i64;
    v68 = 0;
    v11 = pDevice->lpVtbl;
    v12 = ((__int64(__fastcall*)(ID3DBlob*))v33->lpVtbl->GetBufferSize)(v33);
    v13 = ((__int64(__fastcall*)(ID3DBlob*))v33->lpVtbl->GetBufferPointer)(v33);
    ((void(__fastcall*)(ID3D11Device*, char**, __int64, __int64, __int64, ID3D11InputLayout**))v11->CreateInputLayout)(
        pDevice,
        &v59,
        2i64,
        v13,
        v12,
        &D3D11InputLayout2);
    v53[57] = 0;
    strcpy(
        (char*)v53,
        "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;}");
    D3D11InputLayout = 0i64;
    D3D11Buffer = 0i64;
    D3D11PixelShader2 = 0i64;
    D3D11PixelShader3 = 0i64;
    v14 = -1i64;
    do
        ++v14;
    while (*((_BYTE*)v53 + v14));
    pD3DCompile((char*)v53, v14, 0i64, 0i64, 0i64, v74, v28, 0, 0, (ID3DBlob**)&v36, 0i64);
    v15 = pDevice->lpVtbl;
    v16 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v36 + 32i64))(v36);
    v17 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v36 + 24i64))(v36);
    ((void(__fastcall*)(ID3D11Device*, __int64, __int64, _QWORD, ID3D11PixelShader**))v15->CreateVertexShader)(
        pDevice,
        v17,
        v16,
        0i64,
        &D3D11PixelShader2);
    v18 = pDevice->lpVtbl;
    v19 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v36 + 32i64))(v36);
    v20 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v36 + 24i64))(v36);
    ((void(__fastcall*)(ID3D11Device*, char**, __int64, __int64, __int64, ID3D11InputLayout**))v18->CreateInputLayout)(
        pDevice,
        &v59,
        2i64,
        v20,
        v19,
        &D3D11InputLayout);
    do
        ++v2;
    while (*((_BYTE*)v53 + v2));
    pD3DCompile((char*)v53, v2, 0i64, 0i64, 0i64, v75, v29, 0, 0, (ID3DBlob**)&v45, 0i64);
    v21 = pDevice->lpVtbl;
    v22 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v45 + 32i64))(v45);
    v23 = (*(__int64(__fastcall**)(__int64))(*(_QWORD*)v45 + 24i64))(v45);
    ((void(__fastcall*)(ID3D11Device*, __int64, __int64, _QWORD, ID3D11PixelShader**))v21->CreatePixelShader)(
        pDevice,
        v23,
        v22,
        0i64,
        &D3D11PixelShader3);
    v52 = 0i64;
    v51[1] = 2;
    v51[0] = 112;
    v51[2] = 1;
    v51[3] = 0x10000;
    ((void(__fastcall*)(ID3D11Device*, int*, _QWORD, ID3D11Buffer**))pDevice->lpVtbl->CreateBuffer)(
        pDevice,
        v51,
        0i64,
        &D3D11Buffer);
    sub_242D8DB0420(
        v25,
        v24,
        (int)context,
        (int)D3D11Buffer,
        (__int64)D3D11InputLayout,
        (__int64)D3D11PixelShader2,
        (__int64)D3D11PixelShader3);
    ((void(__fastcall*)(ID3D11InputLayout*))D3D11InputLayout2->lpVtbl->Release)(D3D11InputLayout2);
    ((void(__fastcall*)(ID3D11InputLayout*))D3D11InputLayout->lpVtbl->Release)(D3D11InputLayout);
    ((void(__fastcall*)(ID3D11VertexShader*))D3D11VertexShader1->lpVtbl->Release)(D3D11VertexShader1);
    (*((void(__fastcall**)(ID3D11PixelShaderVtbl*))D3D11PixelShade1.lpVtbl->QueryInterface + 2))(D3D11PixelShade1.lpVtbl);
    ((void(__fastcall*)(ID3D11PixelShader*))D3D11PixelShader2->lpVtbl->Release)(D3D11PixelShader2);
    ((void(__fastcall*)(ID3D11PixelShader*))D3D11PixelShader3->lpVtbl->Release)(D3D11PixelShader3);
    ((void(__fastcall*)(ID3D11Buffer*))D3D11Buffer->lpVtbl->Release)(D3D11Buffer);
    ((void(__fastcall*)(ID3DBlob*))v33->lpVtbl->Release)(v33);
    ((void(__fastcall*)(ID3DBlob*))v38->lpVtbl->Release)(v38);
    ((void(__fastcall*)(ID3D11RenderTargetView*))RenderTargetView->lpVtbl->Release)(RenderTargetView);
    ((void(__fastcall*)(ID3D11Texture2D*))Texture2D->lpVtbl->Release)(Texture2D);
    if (RenderTargetView)
    {
        ((void(__fastcall*)(ID3D11RenderTargetView*))RenderTargetView->lpVtbl->Release)(RenderTargetView);
        RenderTargetView = 0i64;
    }
    if (Texture2D)
        ((void(__fastcall*)(ID3D11Texture2D*))Texture2D->lpVtbl->Release)(Texture2D);
    return 0i64;
}

使用IDA查看改函数,发现其通过swapchain,获取了一d3ddevice,context,还创建了材质和targetview,同时从字符串编译了shader(之前重定位的D3DCompile函数就在这里使用到了)。这些与我们最终的目的没有太大关系,因此我们快速略过

 

值得注意的是最后几行,该函数调用了shellcode中的另一处函数,函数地址是shellcode+0x420,参数可以直接推断出,因此我们继续关注这个函数,我们暂且称其为__int64 __fastcall CtlFlowSub(__int64 a1, __int64 a2, ID3D11DeviceContext* a3, ID3D11Buffer* a4, ID3D11InputLayout* a8, ID3D11PixelShader* a9, ID3D11PixelShader* a10)

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
__int64 __fastcall CtlFlowSub(__int64 a1, __int64 a2, ID3D11DeviceContext* a3, ID3D11Buffer* a4, ID3D11InputLayout* a5, ID3D11PixelShader* a6, ID3D11PixelShader* a7)
{
    __int64 v7; // rsi
    __int64 result; // rax
    __int64 v11; // rcx
    int v12; // edx
    int v13; // er9
    int v14; // eax
    __int64 v15; // [rsp+58h] [rbp-28h]
    __int128 v16; // [rsp+60h] [rbp-20h]
    __int64 v17; // [rsp+70h] [rbp-10h]
    int v18; // [rsp+78h] [rbp-8h]
    int v19; // [rsp+7Ch] [rbp-4h]
 
    v7 = 0i64;
    v15 = 0i64;
    v16 = 0i64;
    v17 = 0i64;
    v18 = 50;
    v19 = 50;
    while (2)
    {
        result = (__int64)controller;
        switch (controller[v7])
        {
        case 0:
            result = HIDWORD(v15);
            LODWORD(v15) = HIDWORD(v15) + v15;
            goto LABEL_10;
        case 1:
            result = HIDWORD(v15);
            LODWORD(v15) = v15 - HIDWORD(v15);
            goto LABEL_10;
        case 2:
            v11 = controller[v7 + 1];
            v7 += 2i64;
            result = *((unsigned int*)&v15 + v11);
            *((_DWORD*)&v15 + controller[v7]) = result;
            goto LABEL_10;
        case 3:
            v12 = controller[v7 + 1];
            v7 += 2i64;
            result = (__int64)controller;
            *((_DWORD*)&v15 + controller[v7]) = v12;
            goto LABEL_10;
        case 4:
            ++v7;
            v13 = v15;
            v14 = v15 * (HIDWORD(v15) + 1);
            LODWORD(v15) = controller[v7] ^ 0x414345;
            result = (unsigned int)((int)(v15 ^ (HIDWORD(v15) + v13)) % 256
                + (((int)(v15 ^ (v13 * HIDWORD(v15))) % 256
                    + (((int)(v15 ^ (HIDWORD(v15) + v14)) % 256) << 8)) << 8));
            HIDWORD(v15) = result;
            goto LABEL_10;
        case 5:
            // 调用函数
            result = sub_242D8DB0000(SDWORD2(v16), SHIDWORD(v16), v17, SHIDWORD(v17), -256, a3, a4, a5, a6, a7);
            goto LABEL_10;
        case 6:
            // 调用函数
            result = sub_242D8DB0000(SDWORD2(v16), SHIDWORD(v16), v17, SHIDWORD(v17), -13771801, a3, a4, a5, a6, a7);
            goto LABEL_10;
        case 7:
            return result;
        default:
        LABEL_10:
            if ((unsigned __int64)++v7 < 0x1301)
                continue;
            return result;
        }
    }
}

因为进入该函数后发现一个明显的虚拟机,从一个全局的指令表中读入指令,并进行相应的操作,特别地注意到在case5 和 6的地方调用了另一个shellcode中的函数,并传入大量绘图相关的指针,我们有理由怀疑这个函数就是最终执行绘图的函数,我们暂且称这个函数为__int64 __fastcall CoreDrawSub(int a1, int a2, int a3, int a4, int a5, ID3D11DeviceContext* a6, ID3D11Buffer* a7, ID3D11InputLayout* a8, ID3D11PixelShader* a9, ID3D11PixelShader* a10)。这个函数地址就是shellcode的首地址,函数前5个参数为整数,后面的参数全部是d3d相关指针,因此我们应该重点关注前五个参数的意义。

 

第五个整数参数的含义是非常好猜测的,因为该程序应该绘制两种颜色的方块,而在虚拟机调用处,我们看到两个常量-256 -13771801 ,我们有理由推测这就是颜色代码,至于验证,只需要hook一下shellcode首地址函数,并将第五个参数固定为某一个常量,运行程序就可以看出结果。

 

经过测试,-256对应黄色方块,-13771801对应蓝色方块。同时我们可以在hook时输出前4个参数来观察一下规律

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
-950 50 -14703700 1248208 -256
50 -390 1822057 -1524539 -256
-950 170 -7425437 14227863 -256
50 230 1897472 15215384 -256
-950 -210 3743658 7267794 -256
50 350 966258 14122466 -256
-890 -270 463184 7666472 -256
170 -270 -2474473 12856971 -256
-770 230 3460907 -13492529 -256
110 -390 -5989351 14280177 -256
-830 170 -3514649 12856715 -256
650 50 15384343 11002795 -13771801
590 110 12856715 13307703 -13771801
530 170 14096303 2054931 -13771801
470 230 12869865 15314261 -13771801
410 290 13085065 12188981 -13771801
470 290 11235584 6581496 -13771801
530 290 12847529 3263901 -13771801
710 50 14122635 3090291 -13771801
770 50 14265601 10052917 -13771801
830 50 3793238 14305830 -13771801
710 110 1253500 3434568 -13771801
770 170 13177867 745383 -13771801
830 230 5441311 13085499 -13771801
890 290 14094598 14037658 -13771801
950 290 15425697 10849657 -13771801
1010 290 15427533 16116185 -13771801
1070 290 14094689 884017 -13771801
950 50 6780433 6659577 -13771801
1010 50 4200253 5601561 -13771801
1070 50 14120545 4037889 -13771801
1130 50 15728538 13250054 -13771801
1010 110 12846315 11999115 -13771801
1070 170 14141167 9134903 -13771801
1130 170 14118943 6781707 -13771801
1190 170 14094705 16600353 -13771801
1250 170 3737259 769831 -13771801
1130 230 616035 15687475 -13771801
1190 290 7545175 8608671 -13771801
1250 290 3487536 3683380 -13771801
1310 290 6444969 9786857 -13771801
1370 290 14120879 14638035 -13771801
-950 50 -14703700 1248208 -256
50 -390 1822057 -1524539 -256
-950 170 -7425437 14227863 -256
50 230 1897472 15215384 -256
-950 -210 3743658 7267794 -256
50 350 966258 14122466 -256
-890 -270 463184 7666472 -256
170 -270 -2474473 12856971 -256
-770 230 3460907 -13492529 -256
110 -390 -5989351 14280177 -256
-830 170 -3514649 12856715 -256
650 50 15384343 11002795 -13771801
590 110 12856715 13307703 -13771801
530 170 14096303 2054931 -13771801
470 230 12869865 15314261 -13771801

节选了部分,可以看到前两个参数规律非常明显,纵向观察可以发现两行之间相差60或60的倍数,这让我们直接联想到了前两个参数就是方块的坐标

 

至于第3和4个参数,此时还不是很清晰,鉴于其如此庞大,猜测应该是key

 

至于第五个参数,我们可以观察到一个有趣的现象,-256持续11次后,-13771801持续31次,我们刚才已经知道了-256是黄色方块,同时从题目描述中知道黄色方块刚好有11块。因此如此推知,黄色方块并不是没有绘制,而是坐标错了,因此我们明确了当下的目标是要修正黄色方块的位置。

 

因此当前的焦点聚焦在第3、4个参数是如何生成上,而要弄清楚如何生成的,我们得回到虚拟机函数上来

虚拟机函数分析

为了更加明了虚拟机运行时的具体流程,我们开始分析虚拟机函数,因为IDA识别问题等原因,我们需要修正一下一些变量的类型,比如v15也应该被修正为 int v15[10]的形式

 

此时的v15实际上充当了临时寄存器的功能,而指令列表实际上是从一个全局表中读入的,我们可以通过shellcode基地址定位到这个表的位置。所以我们可以自己写一个控制流函数模拟这个过程,替换原控制流函数,并在中途输出我们想要的过程信息。

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
__int64 __fastcall MyCtlFlowSub(__int64 a1, __int64 a2, ID3D11DeviceContext* a3, ID3D11Buffer* a4, ID3D11InputLayout* a8, ID3D11PixelShader* a9, ID3D11PixelShader* a10)
{
    __int64 v7; // rsi
    __int64 result; // rax
    __int64 v11; // rcx
    int v12; // edx
    int v13; // er9
    int v14; // eax
    int v15[100]; // [rsp+58h] [rbp-28h]
 
    v7 = 0i64;
    for (int i = 0; i < 100; i++) v15[i] = 0;
    v15[8] = 50;
    v15[9] = 50;
    int* dword_18683811301 = (int*)(CtlTableAdd);
    ptrCoreDrawSub sub_18683810000 = (ptrCoreDrawSub)CoreDrawSub;
    int fcol;
    const bool printFlow = true;
    if(printFlow) Print("{In Control Flow!}");
    while (2)
    {
        result = (__int64)dword_18683811301;
        int xx11,yy11;
        switch (dword_18683811301[v7])
        {
        case 0:
            result = (unsigned int)v15[1];
            if(printFlow) Print("\tR0 += %d (R0 -> %d)", v15[1], v15[0] + v15[1]);
            v15[0] += v15[1];
            goto LABEL_10;
        case 1:
            result = (unsigned int)v15[1];
            if (printFlow) Print("\tR0 -= %d (R0 -> %d)", v15[1], v15[0] - v15[1]);
            v15[0] -= v15[1];
            goto LABEL_10;
        case 2:
            v11 = dword_18683811301[v7 + 1];
            v7 += 2i64;
            result = (unsigned int)v15[v11];
            if (printFlow) Print("\tR%d = R%d (R%d = %d)", dword_18683811301[v7], v11, v11, (unsigned int)v15[v11]);
            v15[dword_18683811301[v7]] = result;
            goto LABEL_10;
        case 3:
            v12 = dword_18683811301[v7 + 1];
            v7 += 2i64;
            result = (__int64)dword_18683811301;
            if (printFlow) Print("\tR%d = %d", dword_18683811301[v7], v12);
            v15[dword_18683811301[v7]] = v12;
            goto LABEL_10;
        case 4:
            ++v7;
            v13 = v15[0];
            v14 = v15[0] * (v15[1] + 1);
            if (printFlow) Print("\tori: R0 = %d R1 = %d", v15[0], v15[1]);
            xx11 = v15[0];
            yy11 = v15[1];
            if (printFlow) Print("\tR0 = %d(origin=%d)", dword_18683811301[v7] ^ 0x414345, dword_18683811301[v7]);
            v15[0] = dword_18683811301[v7] ^ 0x414345;
            result = (unsigned int)((v15[0] ^ (v15[1] + v13)) % 256
                + (((v15[0] ^ (v13 * v15[1])) % 256 + (((v15[0] ^ (v15[1] + v14)) % 256) << 8)) << 8));
            if (printFlow) Print("\tR1 = %d(xor)", result);
            v15[1] = result;
            goto LABEL_10;
        case 5:
            if (printFlow) Print("\tCall %d %d %d %d %d", v15[4], v15[5], v15[6], v15[7], -256);
            sub_18683810000(v15[4], v15[5], v15[6], v15[7], -256, a3, a4, a8, a9, a10);
            goto LABEL_10;
        case 6:
            if (printFlow) Print("\tCall %d %d %d %d %d", v15[4], v15[5], v15[6], v15[7], -13771801);
            sub_18683810000(v15[4], v15[5], v15[6], v15[7], -13771801, a3, a4, a8, a9, a10);
            goto LABEL_10;
        case 7:
            if (printFlow) Print("\tRet");
            return result;
        default:
        LABEL_10:
            if ((unsigned __int64)++v7 < 0x1301)
                continue;
            return result;
        }
    }
}

我们把v15数组看成R0-R9的寄存器,输出过程信息:

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
{In Control Flow!}
    R0 = R8 (R8 = 50)
    R4 = R0 (R0 = 50)
    R0 = R4 (R4 = 50)
    R1 = 1000
    R0 -= 1000 (R0 -> -950)
    R4 = R0 (R0 = -950)
    R0 = R9 (R9 = 50)
    R5 = R0 (R0 = 50)
    R0 = R4 (R4 = -950)
    R1 = R5 (R5 = 50)
    ori: R0 = -950 R1 = 50
    R0 = 1248208(origin=5392533)
    R1 = -14703700(xor)
    R3 = R0 (R0 = 1248208)
    R0 = R1 (R1 = -14703700)
    R1 = R3 (R3 = 1248208)
    R6 = R0 (R0 = -14703700)
    R7 = R1 (R1 = 1248208)
    Call -950 50 -14703700 1248208 -256
    R0 = R8 (R8 = 50)
    R4 = R0 (R0 = 50)
    R0 = R9 (R9 = 50)
    R1 = 60
    R0 += 60 (R0 -> 110)
    R5 = R0 (R0 = 110)
    R0 = R5 (R5 = 110)
    R1 = 500
    R0 -= 500 (R0 -> -390)
    R5 = R0 (R0 = -390)
    R0 = R4 (R4 = 50)
    R1 = R5 (R5 = -390)
    ori: R0 = 50 R1 = -390
    R0 = 1822057(origin=5934636)
    R1 = -1524539(xor)
    R6 = R0 (R0 = 1822057)
    R7 = R1 (R1 = -1524539)
    Call 50 -390 1822057 -1524539 -256
    R0 = R8 (R8 = 50)
    R4 = R0 (R0 = 50)
    R0 = R4 (R4 = 50)
    R1 = 1000
    R0 -= 1000 (R0 -> -950)
    R4 = R0 (R0 = -950)
    R0 = R9 (R9 = 50)
    R1 = 120
    R0 += 120 (R0 -> 170)
    R5 = R0 (R0 = 170)
    R0 = R4 (R4 = -950)
    R1 = R5 (R5 = 170)
    ori: R0 = -950 R1 = 170
    R0 = 14227863(origin=9984722)
    R1 = -7425437(xor)
    R3 = R0 (R0 = 14227863)
    R0 = R1 (R1 = -7425437)
    R1 = R3 (R3 = 14227863)
    R6 = R0 (R0 = -7425437)
    R7 = R1 (R1 = 14227863)
    Call -950 170 -7425437 14227863 -256
    ...

注意看调用call之前的几次操作

1
2
3
4
5
6
7
8
9
ori: R0 = -950 R1 = 50
R0 = 1248208(origin=5392533)
R1 = -14703700(xor)
R3 = R0 (R0 = 1248208)
R0 = R1 (R1 = -14703700)
R1 = R3 (R3 = 1248208)
R6 = R0 (R0 = -14703700)
R7 = R1 (R1 = 1248208)
Call -950 50 -14703700 1248208 -256

可以看到第三个第四个参数实际上是由CtlFlowSub的case4计算出来的,而在case4的情况下,R0和R1实际上存的就是想要绘制的坐标,因此还原flag的第一个思路便有了;

解法1: hook

我们只需要在CtlFlowSub的case4处添加我们的特判,如果是错误的坐标,就修正为正确的坐标,同时立即调用绘制函数,即可完成flag的找回

 

代码在附件的Solution1文件夹

 

采用劫持d3d11.dll注入

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
__int64 __fastcall MyCtlFlowSub(__int64 a1, __int64 a2, ID3D11DeviceContext* a3, ID3D11Buffer* a4, ID3D11InputLayout* a8, ID3D11PixelShader* a9, ID3D11PixelShader* a10)
{
    __int64 v7; // rsi
    __int64 result; // rax
    __int64 v11; // rcx
    int v12; // edx
    int v13; // er9
    int v14; // eax
    int v15[100]; // [rsp+58h] [rbp-28h]
 
    v7 = 0i64;
    for (int i = 0; i < 100; i++) v15[i] = 0;
    v15[8] = 50;
    v15[9] = 50;
    int* dword_18683811301 = (int*)(CtlTableAdd);
    ptrCoreDrawSub sub_18683810000 = (ptrCoreDrawSub)CoreDrawSub;
    int fcol;
    const bool printFlow = false;
    if(printFlow) Print("{In Control Flow!}");
    while (2)
    {
        result = (__int64)dword_18683811301;
        int xx11,yy11;
        switch (dword_18683811301[v7])
        {
        case 0:
            result = (unsigned int)v15[1];
            if(printFlow) Print("\tR0 += %d (R0 -> %d)", v15[1], v15[0] + v15[1]);
            v15[0] += v15[1];
            goto LABEL_10;
        case 1:
            result = (unsigned int)v15[1];
            if (printFlow) Print("\tR0 -= %d (R0 -> %d)", v15[1], v15[0] - v15[1]);
            v15[0] -= v15[1];
            goto LABEL_10;
        case 2:
            v11 = dword_18683811301[v7 + 1];
            v7 += 2i64;
            result = (unsigned int)v15[v11];
            if (printFlow) Print("\tR%d = R%d (R%d = %d)", dword_18683811301[v7], v11, v11, (unsigned int)v15[v11]);
            v15[dword_18683811301[v7]] = result;
            goto LABEL_10;
        case 3:
            v12 = dword_18683811301[v7 + 1];
            v7 += 2i64;
            result = (__int64)dword_18683811301;
            if (printFlow) Print("\tR%d = %d", dword_18683811301[v7], v12);
            v15[dword_18683811301[v7]] = v12;
            goto LABEL_10;
        case 4:
            ++v7;
 
            fcol = -13771801;
            if (v15[0] == -950 && v15[1] == 50) {
                v15[0] = 50;
                v15[1] = 50;
                fcol = -256;
            }
            else if (v15[0] == 50 && v15[1] == -390) {
                v15[0] = 50;
                v15[1] = 110;
                fcol = -256;
            }
            else if (v15[0] == -950 && v15[1] == 170) {
                v15[0] = 50;
                v15[1] = 170;
                fcol = -256;
            }
            else if (v15[0] == 50 && v15[1] == 230) {
                v15[0] = 50;
                v15[1] = 230;
                fcol = -256;
            }
            else if (v15[0] == -950 && v15[1] == -210) {
                v15[0] = 50;
                v15[1] = 290;
                fcol = -256;
            }
            else if (v15[0] == 50 && v15[1] == 350) {
                v15[0] = 50;
                v15[1] = 350;
                fcol = -256;
            }
            else if (v15[0] == -890 && v15[1] == -270) {
                v15[0] = 110;
                v15[1] = 110;
                fcol = -256;
            }
            else if (v15[0] == 170 && v15[1] == -270) {
                v15[0] = 110;
                v15[1] = 230;
                fcol = -256;
            }
            else if (v15[0] == -770 && v15[1] == 230) {
                v15[0] = 170;
                v15[1] = 170;
                fcol = -256;
            }
            else if (v15[0] == 110 && v15[1] == -390) {
                v15[0] = 170;
                v15[1] = 230;
                fcol = -256;
            }
            else if (v15[0] == -830 && v15[1] == 170) {
                v15[0] = 230;
                v15[1] = 230;
                fcol = -256;
            }
 
            v13 = v15[0];
            v14 = v15[0] * (v15[1] + 1);
            if (printFlow) Print("\tori: R0 = %d R1 = %d", v15[0], v15[1]);
            xx11 = v15[0];
            yy11 = v15[1];
            if (printFlow) Print("\tR0 = %d(origin=%d)", dword_18683811301[v7] ^ 0x414345, dword_18683811301[v7]);
            v15[0] = dword_18683811301[v7] ^ 0x414345;
            result = (unsigned int)((v15[0] ^ (v15[1] + v13)) % 256
                + (((v15[0] ^ (v13 * v15[1])) % 256 + (((v15[0] ^ (v15[1] + v14)) % 256) << 8)) << 8));
            if (printFlow) Print("\tR1 = %d(xor)", result);
            v15[1] = result;
            sub_18683810000(xx11, yy11, v15[0], v15[1], fcol, a3, a4, a8, a9, a10);
            goto LABEL_10;
        case 5:
            if (printFlow) Print("\tCall %d %d %d %d %d", v15[4], v15[5], v15[6], v15[7], -256);
            goto LABEL_10;
        case 6:
            if (printFlow) Print("\tCall %d %d %d %d %d", v15[4], v15[5], v15[6], v15[7], -13771801);
            goto LABEL_10;
        case 7:
            if (printFlow) Print("\tRet");
            return result;
        default:
        LABEL_10:
            if ((unsigned __int64)++v7 < 0x1301)
                continue;
            return result;
        }
    }
}

解法2:修改指令表

既然CtlFlowSub是通过读取指令表执行指令,我们只需要按照他的指令规则构造一套我们自己的虚拟代码即可

 

根据分析得到一下结论:

1
2
3
4
5
6
7
8
9
opcode        操作
0             R0 = R0 + R1
1             R0 = R0 - R1
2 x y         Ry = Rx
3 val x       Rx = val
4 x           GenKey(R0,R1,x)   //R0,R1是xy坐标;x是存储在原表中的key;结果存储在R0和R1中
5             call yellow
6             call blue
7             ret

我们已经知道每个块的坐标是多少,同时也可以知道原表中的每一个key,即可自行构造出一组虚拟指令完成绘制操作

 

下面编写指令生成代码:

 

理论演示代码在Solution2项目中,这只是理论演示代码,实际生成虚拟指令的代码运行在Code项目RBH.cpp中

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
void GetRightPos(int nowx, int nowy, int *rightx, int *righty) {
    int v15[2];
    v15[0] = nowx;
    v15[1] = nowy;
    if (v15[0] == -950 && v15[1] == 50) {
        v15[0] = 50;
        v15[1] = 50;
    }
    else if (v15[0] == 50 && v15[1] == -390) {
        v15[0] = 50;
        v15[1] = 110;
    }
    else if (v15[0] == -950 && v15[1] == 170) {
        v15[0] = 50;
        v15[1] = 170;
    }
    else if (v15[0] == 50 && v15[1] == 230) {
        v15[0] = 50;
        v15[1] = 230;
    }
    else if (v15[0] == -950 && v15[1] == -210) {
        v15[0] = 50;
        v15[1] = 290;
    }
    else if (v15[0] == 50 && v15[1] == 350) {
        v15[0] = 50;
        v15[1] = 350;
    }
    else if (v15[0] == -890 && v15[1] == -270) {
        v15[0] = 110;
        v15[1] = 110;
    }
    else if (v15[0] == 170 && v15[1] == -270) {
        v15[0] = 110;
        v15[1] = 230;
    }
    else if (v15[0] == -770 && v15[1] == 230) {
        v15[0] = 170;
        v15[1] = 170;
    }
    else if (v15[0] == 110 && v15[1] == -390) {
        v15[0] = 170;
        v15[1] = 230;
    }
    else if (v15[0] == -830 && v15[1] == 170) {
        v15[0] = 230;
        v15[1] = 230;
    }
    *rightx = v15[0];
    *righty = v15[1];
}
int tmpIns[10000], tmpip = 0;
 
void RxV(int x, int val) {
    // rx = val
    tmpIns[++tmpip] = 3;
    tmpIns[++tmpip] = val;
    tmpIns[++tmpip] = x;
}
void Mov(int target, int source) {
    // target = source
    tmpIns[++tmpip] = 2;
    tmpIns[++tmpip] = source;
    tmpIns[++tmpip] = target;
}
void CallYellow() {
    tmpIns[++tmpip] = 5;
}
void CallBlue() {
    tmpIns[++tmpip] = 6;
}
void Ret() {
    tmpIns[++tmpip] = 7;
}
void GenKey(int key) {
    tmpIns[++tmpip] = 4;
    tmpIns[++tmpip] = key;
}
 
void MyDraw(bool isYellow, int x, int y,int key) {
    RxV(0, x);
    RxV(1, y);
    GenKey(key);
    Mov(6, 0);
    Mov(7, 1);
    RxV(4, x);
    RxV(5, y);
    if (isYellow) CallYellow();
    else CallBlue();
}
void BackTrace(int x, int y, int ip) {
    static int cnt = 0;
    cnt++;
 
    int rightx = x, righty = y;
    int isYellow = false;
 
    if (cnt <= 11) {
        isYellow = true;
        GetRightPos(x, y, &rightx, &righty);
    }
    int* dword_18683811301 = (int*)(CtlTableAdd);
    int tableKey = dword_18683811301[ip + 1];
    MyDraw(isYellow, rightx, righty, tableKey);
    if (cnt == 44) {
        Ret();
        for (int i = 1; i <= tmpip; i++) {
            Print("ins[%d] = %d;", i - 1, tmpIns[i]);
        }
    }
}

最后通过运行代码,我们将得到我们自己的instruction code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ins[0] = 3;
ins[1] = 50;
ins[2] = 0;
ins[3] = 3;
ins[4] = 50;
ins[5] = 1;
ins[6] = 4;
ins[7] = 5392533;
ins[8] = 2;
......  //参见Code项目RBH.cpp@390
ins[921] = -390;
ins[922] = 5;
ins[923] = 6;
ins[924] = 7;

由于最后一条指令是ret,我们只需要在shellcode被写入后,修改源程序的指令表内容即可实现无hook找回flag

 

实际的实现代码,请参见Solution2_dll项目

答案说明

/solution1是解法1的正确解法源码

 

/solution2是解法2的理论源码

 

/solution2_dll是解法2的正确解法源码

 

/Code是研究过程中编写的代码,包括解法2的inscode生成代码

 

ida分析文件在2022游戏安全技术竞赛初赛.exe.i64

 

d3d11.dll是solution2编译的劫持dll


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

上传的附件:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//