-
-
[原创]2022游戏安全技术竞赛初赛writeup
-
发表于: 2022-4-28 00:14 5132
-
感想
在朋友的推荐下,第一次参加这个竞赛,也许也是第一次接触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 , ®ion_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, ®ion_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的函数,动态调试后发现这两个函数分别是ZwAllocateVirtualMemory
和 ZwFreeVirtualMemory
申请好大小为 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
赞赏
- [原创]2022游戏安全技术竞赛初赛writeup 5133
- unicorn-pe内的blackbone编译不过咋整? 8219
- 将js代码注入到第三方CEF应用程序的一点浅见 22523
- 删帖 14387
- [求助] 关于内核APC注入32位进程的shellcode崩溃问题 6079