[Writeups] 安天逆向课程 exercise-welcome WriteUp

{eda2cda7-cf9c-4cd5-9d92-dd0ee6e8189a}

常规获取字串

由于穷举法太简单,因此在此处不再涉及。容易知道卡片的点击顺序为

img

经过这样的点击就可以轻易的获取“有趣的数字”

image-20220512203746957

通过逆向手段获取字串

拖入die查看文件的加壳等属性

image-20220512203846007

签到题确实不加壳。die上显示位I386架构,因此导入IDA32执行分析

image-20220512203931653

按下Shift+F12打开字符串界面,考虑到测试运行时产生的错误对话框是中文的,因此这里可能使用LPWSTR之类宽字符类型,这样的类型对于IDA识别来说有一点困难。定位到.data段,手动查看是否存在可疑变量

image-20220512204141629

这个%s的一家明显引起了注意,双击跟踪之后发现残余的中文字符串

image-20220512204231765

右键选择对应的中文译码结果

image-20220512204300126

对该字符串的变量按下x键查找其交叉引用,然后跟踪到目标函数

image-20220512204407353

对函数的关键部分进行分析,不难发现这个签到题还是很签到的

Source = 0;
      memset(v12, 0, sizeof(v12));
      v13 = 0;
      v14 = 0;
      wsprintfA(
        &Source,
        "恭喜过关,送你一串神奇数字\n{%s-%s-%s-%s-%s}",
        aEda2cda7,
        aCf9c,
        a4cd5,
        a9d92,
        aDd0ee6e8189a);
      strncpy(Destination, ::Source, 0x104u);
      strncpy(byte_4079A4, &Source, 0x104u);
      DialogBoxParamA(hInst, (LPCSTR)0x8C, hWndParent, sub_401FA0, 0);
      return 0;

彩蛋

题目中说明了存在一可以速通的彩蛋。这个彩蛋不需要更改程序,换句话说就是纯粹的逆向分析流程。

对于具有限时的逆向分析比赛,我可能会尝试各种提交试图撞库来在一知半解的情况下完成题目。今天这个题没有时间限制,因此在这里可以详细说明此题目的解题思路。

触发彩蛋

动画

分析

  1. 分析消息流程
    回到消息流程,首先将各个消息转为内置的常量,方便理解:

    case 0x136u按下右键,

    image-20220512205047311

    选择Enum(枚举),也可以按下M键打开枚举窗口。由于我们在IDA工程里没有设置预设的枚举变量,因此将弹出IDA警告,点击Yes即可

    image-20220512205157117

    image-20220512205223969

    这里列举了大量值为0x136u的常量枚举值。我们很清楚这个就是消息循环的消息,因此拉到底部找到WM_开头的消息常量

    image-20220512205502512

    点击OK保存枚举。对于同一个switch里的case,IDA会一同使用同源的枚举值优化这些变量

    image-20220512205608110

    各个消息的含义不赘述。我们专注于WM_COMMAND消息。该消息一般触发于例如按钮按下产生的事件中。因此我们重点查看sub_4018F0的代码

    int __cdecl sub_4018F0(HWND hDlg, int a2, int a3)
    {
      int *v4; // eax
    
      if ( a2 == 2 )
      {
        EndDialog(hDlg, (unsigned __int16)a3);
        return 0;
      }
      if ( a2 != 1005 )
        return 0;
      if ( ++dword_407888 == 10 )
      {
        dword_407888 = 0;
        v4 = dword_40703C;
        while ( v4[1] || *v4 != dword_407898 + 1 )
        {
          v4 += 10;
          if ( (int)v4 >= (int)a0166ec7050b742 )
            return 1;
        }
        SetCursorPos(v4[2], v4[3]);
        mouse_event(4u, 0, 0, 0, 0);
      }
      return 1;
    }
    

    对于上述代码,首先映入眼帘的就是硕大的mose_event事件。此函数主要模拟鼠标点击,其函数原型如下

    void mouse_event(
      [in] DWORD     dwFlags,
      [in] DWORD     dx,
      [in] DWORD     dy,
      [in] DWORD     dwData,
      [in] ULONG_PTR dwExtraInfo
    );
    

    因此很容易的我们想到这个4u也同样是枚举值。其对应MOUSEEVENTF_LEFTUP,代表左鼠标按键抬起。

    那么在整个程序的使用过程中,我们并没有见到模拟鼠标按键的操作,因此可以大胆怀疑这就是所谓“彩蛋”的速通代码的位置。笔者此处轻描淡写,事实上笔者经过大量的实验和注释、理解才最终锁定了这个函数。

    为了确定这是否是所谓的彩蛋,我们使用patch的方法生成一个被修改过的程序来替代源程序,目标是执行这段代码。

    定位到这个函数的字节码。在任意位置按下右键选择同步到IDA View-A

    image-20220512210246572

    可能会由于同步而跳转,只需要找到刚才的函数位置,将行标定位在EndDialog上。我们希望通过一些手段执行这段代码。

    观察EndDialog,这是关闭对话框的函数,这个a2==2的真实身份其实是消息循环中的参数wParamWM_DESTORY,因此可以使用这个函数退出当前对话框。而这个退出的逻辑并不是由我们点击右上角的关闭构成的(在此不赘述,有余力的读者可以自行验证函数sub_4012C0),换句话说,按下ESC键会触发这段代码。

    指导这些之后,打开010Editior,记录我们要跳过的代码头部的硬编码

    8B 44 24 0C
    

    image-20220512211036219

    以及尾部

    A3 88 78 40 00 75 54
    

    定位到要跳过的代码

    image-20220512211225618

    在修改之前,注意其中的

    A1 88 78 40 00 56 40
    

    应当改为(为了保证栈平衡)

    90 90 90 90 90 56 90
    

    修改完成的硬编码如图所示

    image-20220512212001033

    保存之后打开修改好的程序,即可看到在试图退出程序时,不论是按下ESC键还是点击右上角的关闭键,鼠标都会自动移动到正确的卡牌上执行点击。这意味着我们找到的彩蛋是正确的。

    然而如何触发这个彩蛋呢?

  2. 分析对话框布局

    打开ResourceHacker,将程序拖入其中,等待加载。加载结束之后选中“对话框”并打开资源132

    image-20220512212254697

    这就是我们的程序的布局资源文件。从这里面我们可以获得很多信息。读者可以自行探索这里面的另一个彩蛋(未使用的两张贴图)我们观察这个对话框的资源文件,看到

    CONTROL "欢迎来到逆向工程的神秘世界", 1002, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 16, 15, 105, 8 
    

    和控件

    CONTROL "", 1005, STATIC, SS_ETCHEDFRAME | SS_NOTIFY | WS_CHILD | WS_VISIBLE, 13, 13, 109, 12 
    

    几乎重叠在了一起。更为重要的,我们注意到1005号静态字符串控件具有属性SS_NOTIFY,这意味着该控件可以接收用户的鼠标点击指令,并递交给父窗体处理。

    image-20220512212604431

    但是这和我们的彩蛋有什么关系呢?进一步查询值STN_CLICKED可以发现

    image-20220512212645045

    这个消息传递的目标是WM_COMMAND事件,其参数wParam将被指定为静态控件的标识符。

    1. 梳理

    这意味着什么呢?回头查看之前分析的彩蛋代码。笔者已经在上面做了注释

    int __cdecl sub_4018F0(HWND hDlg, int a2, int a3)   //彩蛋代码
    {
        int* v4; // eax
    
        if (a2 == 2)    //a2就是wParam,这里的2是WM_DESTORY的值。
        {
            EndDialog(hDlg, (unsigned __int16)a3);  //如果接到WM_DESTORY参数指示的消息,则关闭窗体。返回这设定为a3(lParam)
            return 0;
        }
        if (a2 != 1005) //如果a2不为1005则返回该函数。
                        //要继续执行,需要满足a2 == 1005
            return 0;
        if (++dword_407888 == 10)   //变量dword_40788先自增,作为计数器
                                    //如果计数器满足10
        {
            dword_407888 = 0;       //则清除计数器
            v4 = dword_40703C;
            while (v4[1] || *v4 != dword_407898 + 1)
            {
                v4 += 10;
                if ((int)v4 >= (int)a0166ec7050b742)
                    return 1;
            }   //获取当前正确的卡牌位置,存储到v4中
            SetCursorPos(v4[2], v4[3]); //将鼠标移动到v4
            mouse_event(4u, 0, 0, 0, 0);//产生点击事件。由前述的消息循环处理
        }
        return 1;
    }
    

    从而,一切都顺利成章的解开了。彩蛋只需要点击这个静态控件10次即可。静态控件在接收到点击之后,通过参数属性SS_NOTIFY的设置,将自身的资源值作为参数传递给WM_COMMAND消息,从而触发彩蛋的执行代码。

posted @ 2022-05-12 21:38  二氢茉莉酮酸甲酯  阅读(137)  评论(0编辑  收藏  举报