本题是一个windows 32位opengl游戏程序,打开发现是一个3d游戏,视角移动受限,未提供坐标移动功能,无法看到屏幕中央箭头指向的区域。
使用ida搜索字符串可以发现使用的glfw版本为3.3,并且使用了opengl es。
由于glfw库采用静态编译,程序中大量调用其api函数而缺少符号信息,为了更方便的分析,从官网现在了glfw 3.3的lib库(https://github.com/glfw/glfw/releases/download/3.3.3/glfw-3.3.3.zip),使用ida的flair功能制作函数sig库,具体命令如下:
pcf glfw3_mt.lib
sigmake glfw3_mt.pat glfw3_mt.sig
将sig放入ida识别,即可成功识别部分库函数。
而对于题目中中大量虚表函数引用,其地址为动态导入,有函数名字符串其实更方便我们识别,例如下面的导入代码:
void sub_404710() { if ( dword_466D24 ) { sub_418CB0("glColorMaski"); sub_418CB0("glGetBooleani_v"); sub_418CB0("glGetIntegeri_v"); sub_418CB0("glEnablei"); sub_418CB0("glDisablei"); sub_418CB0("glIsEnabledi"); sub_418CB0("glBeginTransformFeedback"); sub_418CB0("glEndTransformFeedback"); sub_418CB0("glBindBufferRange"); sub_418CB0("glBindBufferBase"); sub_418CB0("glTransformFeedbackVaryings"); sub_418CB0("glGetTransformFeedbackVarying"); sub_418CB0("glClampColor"); sub_418CB0("glBeginConditionalRender"); sub_418CB0("glEndConditionalRender"); sub_418CB0("glVertexAttribIPointer"); sub_418CB0("glGetVertexAttribIiv"); sub_418CB0("glGetVertexAttribIuiv"); sub_418CB0("glVertexAttribI1i"); sub_418CB0("glVertexAttribI2i"); sub_418CB0("glVertexAttribI3i"); sub_418CB0("glVertexAttribI4i"); sub_418CB0("glVertexAttribI1ui"); sub_418CB0("glVertexAttribI2ui"); sub_418CB0("glVertexAttribI3ui"); sub_418CB0("glVertexAttribI4ui"); sub_418CB0("glVertexAttribI1iv"); sub_418CB0("glVertexAttribI2iv"); sub_418CB0("glVertexAttribI3iv"); sub_418CB0("glVertexAttribI4iv"); sub_418CB0("glVertexAttribI1uiv"); sub_418CB0("glVertexAttribI2uiv"); sub_418CB0("glVertexAttribI3uiv"); sub_418CB0("glVertexAttribI4uiv"); sub_418CB0("glVertexAttribI4bv"); sub_418CB0("glVertexAttribI4sv"); sub_418CB0("glVertexAttribI4ubv"); sub_418CB0("glVertexAttribI4usv"); sub_418CB0("glGetUniformuiv"); sub_418CB0("glBindFragDataLocation"); sub_418CB0("glGetFragDataLocation"); sub_418CB0("glUniform1ui"); sub_418CB0("glUniform2ui"); sub_418CB0("glUniform3ui"); sub_418CB0("glUniform4ui"); sub_418CB0("glUniform1uiv"); sub_418CB0("glUniform2uiv"); sub_418CB0("glUniform3uiv"); sub_418CB0("glUniform4uiv"); sub_418CB0("glTexParameterIiv"); sub_418CB0("glTexParameterIuiv"); sub_418CB0("glGetTexParameterIiv"); sub_418CB0("glGetTexParameterIuiv"); sub_418CB0("glClearBufferiv"); sub_418CB0("glClearBufferuiv"); sub_418CB0("glClearBufferfv"); sub_418CB0("glClearBufferfi"); glGetStringi = sub_418CB0("glGetStringi"); sub_418CB0("glIsRenderbuffer"); sub_418CB0("glBindRenderbuffer"); sub_418CB0("glDeleteRenderbuffers"); sub_418CB0("glGenRenderbuffers"); sub_418CB0("glRenderbufferStorage"); sub_418CB0("glGetRenderbufferParameteriv"); sub_418CB0("glIsFramebuffer"); sub_418CB0("glBindFramebuffer"); sub_418CB0("glDeleteFramebuffers"); sub_418CB0("glGenFramebuffers"); sub_418CB0("glCheckFramebufferStatus"); sub_418CB0("glFramebufferTexture1D"); sub_418CB0("glFramebufferTexture2D"); sub_418CB0("glFramebufferTexture3D"); sub_418CB0("glFramebufferRenderbuffer"); sub_418CB0("glGetFramebufferAttachmentParameteriv"); glGenerateMipmap = (int (__stdcall *)(_DWORD))sub_418CB0("glGenerateMipmap"); sub_418CB0("glBlitFramebuffer"); sub_418CB0("glRenderbufferStorageMultisample"); sub_418CB0("glFramebufferTextureLayer"); sub_418CB0("glMapBufferRange"); sub_418CB0("glFlushMappedBufferRange"); glBindVertexArray = (int (__stdcall *)(_DWORD))sub_418CB0("glBindVertexArray"); glDeleteVertexArrays = (int (__stdcall *)(_DWORD, _DWORD))sub_418CB0("glDeleteVertexArrays"); glGenVertexArrays = (int (__stdcall *)(_DWORD, _DWORD))sub_418CB0("glGenVertexArrays"); sub_418CB0("glIsVertexArray"); } }
这样大大提高了程序可读性。
从winmain函数开始分析,首先程序读取了其文件数据中的两个纹理图案数据:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { char *v4; // esi DWORD v5; // ebx HANDLE v6; // eax void *v7; // edi void *v8; // ebx DWORD v9; // edi HANDLE v10; // eax void *v11; // esi LONG lDistanceToMove; // [esp+Ch] [ebp-110h] void *lpBuffer; // [esp+10h] [ebp-10Ch] LPVOID lpBuffera; // [esp+10h] [ebp-10Ch] DWORD NumberOfBytesRead; // [esp+14h] [ebp-108h] CHAR Filename; // [esp+18h] [ebp-104h] v4 = (char *)hInstance + *(_DWORD *)((char *)hInstance + *((_DWORD *)hInstance + 15) + 0x54); memset(&Filename, 0, 0x100u); GetModuleFileNameA(hInstance, &Filename, 0x100u); lDistanceToMove = *((_DWORD *)v4 - 2); sub_407380(&dword_465FE4, *((_DWORD *)v4 - 3)); lpBuffer = (void *)dword_465FE4; v5 = dword_465FE8 - dword_465FE4; v6 = CreateFileA(&Filename, 0x80000000, 1u, 0, 3u, 0x80u, 0); v7 = v6; if ( v6 != (HANDLE)-1 ) { SetFilePointer(v6, lDistanceToMove, 0, 0); NumberOfBytesRead = 0; ReadFile(v7, lpBuffer, v5, &NumberOfBytesRead, 0); CloseHandle(v7); } lpBuffera = (LPVOID)*((_DWORD *)v4 - 4); sub_407380((int *)&::lpBuffer, *((_DWORD *)v4 - 5)); v8 = ::lpBuffer; v9 = dword_465FF8 - (_DWORD)::lpBuffer; v10 = CreateFileA(&Filename, 0x80000000, 1u, 0, 3u, 0x80u, 0); v11 = v10; if ( v10 != (HANDLE)-1 ) { SetFilePointer(v10, (LONG)lpBuffera, 0, 0); NumberOfBytesRead = 0; ReadFile(v11, v8, v9, &NumberOfBytesRead, 0); CloseHandle(v11); } return sub_4064D0(); }
接着进入关键的sub_4064D0,该函数就是一个较为典型的opengl 3d程序,使用标准的MVP模型构建,使用shader着色器渲染游戏。
signed int sub_4064D0() { glfwInit(); glfwWindowHint(0x22002, 3); glfwWindowHint(0x22003, 3); glfwWindowHint(0x22008, 0x32001); v0 = glfwCreateWindow(800, 600, (int)"XDDDDDDDDD", 0, 0); window = v0; if ( !v0 ) { glfwTerminate(); return -1; } sub_418D00(v0); sub_419330((int)window, (int)sub_407100); glfwSetWindowSizeCallback((int)window, (int)sub_407120); sub_419A30(window, 208897, 212995); if ( !sub_4051B0() ) return -1; glEnable(2929); sub_4060D0(&v41, v30, v3); // shader init? *(_OWORD *)v64 = xmmword_455340; v65 = xmmword_456AA0; v66 = xmmword_455470; v67 = xmmword_4554D0; v68 = xmmword_455560; v69 = xmmword_455320; v70 = xmmword_456AE0; v71 = xmmword_456A90; v72 = xmmword_455450; v73 = xmmword_455300; v74 = xmmword_455540; v75 = xmmword_4554A0; v76 = xmmword_455500; v77 = xmmword_456A70; v78 = xmmword_4552A0; v79 = xmmword_455550; v80 = xmmword_456A60; v81 = xmmword_456AC0; v82 = xmmword_456A80; v83 = xmmword_455530; v84 = xmmword_4552D0; v85 = xmmword_4554B0; v86 = xmmword_455470; v87 = xmmword_455460; v88 = xmmword_455560; v89 = xmmword_455330; v90 = xmmword_456AB0; v91 = xmmword_456A40; v92 = xmmword_455450; v93 = xmmword_4552F0; v94 = xmmword_455340; v95 = xmmword_456AB0; v96 = xmmword_456A50; v97 = xmmword_455460; v98 = xmmword_455300; v99 = xmmword_4552D0; v100 = xmmword_456AD0; v101 = xmmword_4554F0; v102 = xmmword_4554C0; v103 = xmmword_455560; v104 = xmmword_455540; v105 = xmmword_455480; v106 = xmmword_4554E0; v107 = xmmword_456A30; v108 = xmmword_455520; glGenVertexArrays(1, &v63); glGenBuffers(1, &v59); glBindVertexArray(v63); glBindBuffer(34962, v59); glBufferData(34962, 720, v64, 35044); glVertexAttribPointer(0, 3, 5126, 0, 20, 0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, 5126, 0, 20, 12); glEnableVertexAttribArray(1); glGenTextures(1, &v61); glBindTexture(3553, v61); glTexParameteri(3553, 10242, 10497); glTexParameteri(3553, 10243, 10497); glTexParameteri(3553, 10241, 9729); glTexParameteri(3553, 10240, 9729); dword_465FC4 = 1; v4 = sub_409D00(dword_465FE8 - dword_465FE4, dword_465FE4, &v43, &v42, (int *)&v62); v5 = v4; if ( v4 ) { glTexImage2D(3553, 0, 6407, v43, v42, 0, 6407, 5121, v4); glGenerateMipmap(3553); } j___free_base(v5); glGenTextures(1, &v60); glBindTexture(3553, v60); glTexParameteri(3553, 10242, 10497); glTexParameteri(3553, 10243, 10497); glTexParameteri(3553, 10241, 9729); glTexParameteri(3553, 10240, 9729); v6 = sub_409D00(dword_465FF8 - (_DWORD)lpBuffer, (int)lpBuffer, &v43, &v42, (int *)&v62); v7 = v6; if ( v6 ) { glTexImage2D(3553, 0, 6407, v43, v42, 0, 6408, 5121, v6); glGenerateMipmap(3553); } j___free_base(v7); v8 = v41; glUseProgram(v41); v46 = 15; v45 = 0; LOBYTE(lpMem) = 0; gl_init_str(&lpMem, "texture1", 8u); v109 = 0; v9 = &lpMem; if ( v46 >= 0x10 ) v9 = lpMem; v10 = glGetUniformLocation(v8, v9, 0); glUniform1i(v10); v109 = -1; sub_407420(&lpMem); v46 = 15; v45 = 0; LOBYTE(lpMem) = 0; gl_init_str(&lpMem, "texture2", 8u); v109 = 1; v11 = &lpMem; if ( v46 >= 0x10 ) v11 = lpMem; v12 = glGetUniformLocation(v8, v11, 1); glUniform1i(v12); v109 = -1; sub_407420(&lpMem); if ( !sub_419650((int)window) ) { _libm_sse2_tan_precise(); v41 = 0; *(float *)&v43 = 1.0 / (float)0.3926990926265717; v62 = 1.0 / (float)((float)0.3926990926265717 * 1.3333334); do { flt_465FBC = glfwGetTime(); if ( sub_419920((int)window, 256) == 1 ) sub_419370((int)window, 1); glClearColor(1045220557, 1050253722, 1050253722, 1065353216); glClear(16640); glActiveTexture(33984); glBindTexture(3553, v61); glActiveTexture(33985); glBindTexture(3553, v60); glUseProgram(v8); sub_409640(&v41); v47 = v62; v48 = v43; v50 = 0xBF800000; v49 = 0xBF80419A; v51 = 0xBE4D0148; v46 = 15; v45 = 0; LOBYTE(lpMem) = 0; gl_init_str(&lpMem, "projection", 0xAu); v109 = 2; v13 = &lpMem; if ( v46 >= 0x10 ) v13 = lpMem; v14 = glGetUniformLocation(v8, v13, 1); glUniformMatrix4fv(v14); v109 = -1; if ( v46 >= 0x10 ) { v15 = (char *)lpMem; if ( v46 + 1 >= 0x1000 ) { if ( (unsigned __int8)lpMem & 0x1F || (v16 = *((_DWORD *)lpMem - 1), v16 >= (unsigned int)lpMem) || (v15 = (char *)lpMem - v16, (char *)lpMem - v16 < (char *)4) || (unsigned int)v15 > 0x23 ) { LABEL_53: _invalid_parameter_noinfo_noreturn(v15); } v15 = (char *)*((_DWORD *)lpMem - 1); } j_j___free_base(v15); } v35 = *(float *)&dword_464CC4 + *(float *)&qword_464CAC; v36 = *(float *)&dword_464CC8 + *((float *)&qword_464CAC + 1); v37 = *(float *)&dword_464CCC + *(float *)&dword_464CB4; sub_4092A0(&v35); v46 = 15; v45 = 0; LOBYTE(lpMem) = 0; gl_init_str(&lpMem, "view", 4u); v109 = 3; v17 = &lpMem; if ( v46 >= 0x10 ) v17 = lpMem; v18 = glGetUniformLocation(v8, v17, 1); glUniformMatrix4fv(v18); v109 = -1; if ( v46 >= 0x10 ) { v15 = (char *)lpMem; if ( v46 + 1 >= 0x1000 ) { if ( (unsigned __int8)lpMem & 0x1F ) goto LABEL_53; v19 = *((_DWORD *)lpMem - 1); if ( v19 >= (unsigned int)lpMem ) goto LABEL_53; v15 = (char *)lpMem - v19; if ( (char *)lpMem - v19 < (char *)4 || (unsigned int)v15 > 0x23 ) goto LABEL_53; v15 = (char *)*((_DWORD *)lpMem - 1); } j_j___free_base(v15); } v46 = 15; v45 = 0; LOBYTE(lpMem) = 0; glBindVertexArray(v63); v20 = 0; v21 = dword_46601C; if ( (dword_466020 - (signed int)dword_46601C) >> 2 ) { v32 = 0x3F800000; v33 = 0x3E99999A; v34 = 0x3F000000; do { v22 = v21[v20 + 1]; v23 = v21[v20 + 2]; v56 = v21[v20]; v57 = v22; v58 = v23; sub_407F30(&v52); v25 = (__int128 *)sub_4086F0(v24, (__m128 *)&v31, (float *)&v56); v52 = *v25; v53 = v25[1]; v54 = v25[2]; v55 = v25[3]; v26 = (__int128 *)sub_408850(&v32); v40 = 15; v39 = 0; LOBYTE(v38) = 0; v52 = *v26; v53 = v26[1]; v54 = v26[2]; v55 = v26[3]; gl_init_str(&v38, "model", 5u); v109 = 4; v27 = &v38; if ( v40 >= 0x10 ) v27 = v38; v28 = glGetUniformLocation(v8, v27, 1); glUniformMatrix4fv(v28); v109 = -1; if ( v40 >= 0x10 ) { v15 = (char *)v38; if ( v40 + 1 >= 0x1000 ) { if ( (unsigned __int8)v38 & 0x1F ) goto LABEL_53; v29 = *((_DWORD *)v38 - 1); if ( v29 >= (unsigned int)v38 ) goto LABEL_53; v15 = (char *)v38 - v29; if ( (char *)v38 - v29 < (char *)4 || (unsigned int)v15 > 0x23 ) goto LABEL_53; v15 = (char *)*((_DWORD *)v38 - 1); } j_j___free_base(v15); } v40 = 15; v39 = 0; LOBYTE(v38) = 0; glDrawArrays(4, 0, 36); v20 += 3; v21 = dword_46601C; } while ( v20 < (dword_466020 - (signed int)dword_46601C) >> 2 ); } sub_418D80((int)window); sub_419310(); } while ( !sub_419650((int)window) ); } glDeleteVertexArrays(1, &v63); glDeleteBuffers(1, &v59); glfwTerminate(); return 0; }
其中,游戏使用sub_407120监听鼠标移动事件:
float __usercall sub_407120@<eax>(int a1, double a2, double a3) { int v3; // xmm3_4 int v4; // xmm4_4 float v5; // xmm0_4 float v6; // xmm2_4 float v7; // xmm1_4 float v8; // xmm2_4 float v9; // xmm2_4 float v10; // xmm1_4 int v11; // xmm0_4 int v12; // xmm0_4 float v13; // xmm1_4 signed int v14; // xmm0_4 float v15; // ST0C_4 float v16; // ST10_4 float v17; // ST08_4 double v18; // xmm0_8 __m128 v19; // xmm1 __m128 v20; // xmm2 __m128 v21; // xmm0 float result; // eax float v23; // [esp+0h] [ebp-1Ch] if ( byte_464C90 ) { *(float *)&v3 = a2; byte_464C90 = 0; *(float *)&v4 = a3; v5 = *(float *)&v3; dword_464C98 = v3; v6 = *(float *)&v4; dword_464C9C = v4; dword_466018 = v3; dword_46602C = v4; } else { v3 = dword_464C98; v4 = dword_464C9C; v5 = *(float *)&dword_466018; v6 = *(float *)&dword_46602C; } v7 = (float)((float)(a2 - v5) * 0.1) + *(float *)&dword_465FF0; v8 = v6 - a3; v9 = (float)(v8 * 0.1) + *(float *)&dword_466028; if ( (v7 >= 70.0 || v7 <= -70.0) && v7 <= 110.0 && v7 >= -110.0 && v9 <= 30.0 && v9 >= -30.0 ) { v10 = *(float *)&v4 - a3; *(float *)&v11 = a2; dword_464C98 = v11; *(float *)&v12 = a3; v13 = (float)(v10 * 0.1) + *(float *)&dword_465FB8; dword_464C9C = v12; v14 = 1118961664; v23 = (float)((float)(a2 - *(float *)&v3) * 0.1) + *(float *)&dword_464C94; *(float *)&dword_464C94 = (float)((float)(a2 - *(float *)&v3) * 0.1) + *(float *)&dword_464C94; dword_465FB8 = LODWORD(v13); if ( v13 > 89.0 || (v14 = -1028521984, v13 < -89.0) ) { v13 = *(float *)&v14; dword_465FB8 = v14; } sub_405290(); sub_405290(); v15 = (float)(v23 * 0.017453292) * (float)(v13 * 0.017453292); sub_4052B0(); v16 = v13 * 0.017453292; sub_4052B0(); v17 = (float)(v23 * 0.017453292) * (float)(v13 * 0.017453292); v18 = (float)((float)((float)((float)(v13 * 0.017453292) * (float)(v13 * 0.017453292)) + (float)(v15 * v15)) + (float)(v17 * v17)); _libm_sse2_sqrt_precise(); v19 = (__m128)0x3F800000u; *(float *)&v18 = v18; v19.m128_f32[0] = 1.0 / *(float *)&v18; v20 = v19; v21 = v19; v20.m128_f32[0] = v19.m128_f32[0] * v15; v21.m128_f32[0] = v19.m128_f32[0] * v16; qword_464CAC = (unsigned __int128)_mm_unpacklo_ps(v20, v21); result = v19.m128_f32[0] * v17; *(float *)&dword_464CB4 = v19.m128_f32[0] * v17; } return result; }
可以看出,其本质是通过修改view矩阵中的视角信息实现的,同时也发现了视角受限的逻辑:
if ( (v7 >= 70.0 || v7 <= -70.0) && v7 <= 110.0 && v7 >= -110.0 && v9 <= 30.0 && v9 >= -30.0 )
此外,通过分析代码,不难发现dword_464CC4、dword_464CC8、qword_464CAC这三个全局变量就代表了view矩阵中的视角所在的坐标位置。
通过ida的keypatch插件,可以很轻松的将该处逻辑限制去掉,使其无论如何都jmp到视角移动代码处即可
我们已经知道可以通过修改上述的三个全局变量进行坐标移动,为了看清箭头所指区域,我们需要移动到一个合适的位置和视角,但我们并不知道哪里才是合适的位置,所以我制作了一个游戏作弊程序,方便我们人工的找到这个位置,这个任务使用Cheat Engine来完成最合适不过了,这样节省了很多编写作弊程序代码的时间,附件给出了我制作的ct作弊脚本。我注册了w、a、s、d、q、e六个热键分别控制x、y、z方向上的坐标移动。
最终我找到了如下坐标,能够较为清楚的看清箭头所指区域,即flag:dogod