2021腾讯游戏安全技术初赛PC客户端解题报告
2021-04-11 20:50:43 Author: xz.aliyun.com(查看原文) 阅读量:164 收藏

概述

本题是一个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

最终我找到了如下坐标,能够较为清楚的看清箭头所指区域,即flag:dogod


文章来源: http://xz.aliyun.com/t/9402
如有侵权请联系:admin#unsafe.sh