Win32简单图形界面程序逆向
阅读原文时间:2023年07月10日阅读:1
  • 前言

    • 为了了解与学习底层知识,从 汇编开始 -> C语言 -> C++ -> PE文件 ,直至今天的Win32 API,着实学的令我头皮发麻(笑哭)。
    • 就在昨天的课程中,老师布置了一个作业:逆向一个简单的Win32程序。
    • 本着总结与分享知识的心态,便有了这篇文章,希望能够帮到大家。
  • 本文章所逆向的Win32程序:https://files.cnblogs.com/files/02SWD/ReverseTraining_1.rar?t=1663236309

1.1 简单介绍

  1. 该程序是经 release版本 进行编译的。

  2. 打开该程序之后,使用 鼠标左键 点击窗口,会弹出标题和内容均为 1 的窗口;使用 鼠标右键 点击窗口,会弹出标题和内容均为 2 的窗口。

  3. 打开该程序之后,选中窗口,当我们按下正确的键盘按键时,会弹出该按键的键盘码,否则便会弹出Error窗口。正确的键盘码有3个。下图以其中一个按键“A”为例,请找出剩余的2个(当然你要是一个个试那就没意思了)

1.2 逆向目标任务

  1. 通过逆向,找到该程序的窗口回调函数对鼠标左/右键进行处理的代码。
  2. 通过逆向,找到另外2个正确的键盘按键。

2.1 逆向思路剖析

  • 要想完成上面的2个目标,我们要做的第一件事就是:定位“窗口消息处理程序(窗口回调函数)”的位置,而若想定位“窗口回调函数”,我们就需要知道该函数的函数地址,那么我们要如何得知该函数的函数地址呢
  • 我们知道:“窗口回调函数”的函数地址保存在一个名为 WNDCLASS结构体lpfnWndProc属性 中,找到了该结构体就等于找到了回调函数,那么我们如何定位WNDCLASS结构体呢
  • 我们可以从RegisterClass()函数入手,WNDCLASS结构体的首地址是RegisterClass()函数的参数。那这就意味着:如果我们找到了RegisterClass()函数,就相当于找到了WNDCLASS结构体的首地址,那么我们又如何定位RegisterClass()函数的位置呢
  • 由于在这个程序中,RegisterClass()函数是由WinMain()函数所调用的,所以我们只要找到WinMain()函数,就自然而然的可以定位RegisterClass()函数了。
  • WinMain()函数就是win32应用程序的入口函数。

2.2 逆向思维概括

  • 定位WinMain()函数 -> 定位RegisterClass()函数 -> 定位WNDCLASS结构体 -> 得知窗口回调函数的函数地址,以致定位到回调函数的代码

3.1 步骤1:定位WinMain()函数

  • 先将待逆向程序拖入OD,此时要注意:

    • 现在的代码所停的位置并不是WinMain()函数的入口点,而是WinMain()函数的 父函数的 入口点,那我们该如何定位WinMain()函数呢?
  • 首先我们要清楚 WinMain()函数的必要特点

    • 第一个参数为 hInstance(代表的是该应用程序的imageBase)
    • 该函数的调用约定为 __stdcall(即:参数的压栈顺序为:从右至左,平栈方式为内平栈)
  • 这就意味着,hInstance参数是最后一个被压栈的,即 在call WinMain指令位置的上方一定会有一个instance参数的压栈

  • 并且 由于WinMain()函数的参数有4个,那么在 内平栈 时,应该使用指令:retn 0x10

  • 定位过程如下图:

3.2 步骤2:定位RegisterClass()函数

  • 该函数比较好定位:当我们进入WinMain函数后,向下寻找,根据OD给我们提供的注释,可以轻松的找到:WinMain函数 在0x401055位置处执行了call指令,调用了RegisterClass()函数

3.3 步骤3:定位WNDCLASS结构体

  • 在步骤3我们成功定位了WinMain函数调用RegisterClass函数的位置,由此我们便可以轻松的找到 RegisterClass函数的参数

  • 0x401050位置处的指令 便为RegisterClass()函数的 传参指令,而 eax寄存器 中存储的便是 WNDCLASS结构体的地址

3.4 步骤4:获得“窗口回调函数”的函数地址,以致定位其代码

  • 由步骤3我们已经知道 WNDCLASS结构体的数据。根据该结构体的定义,我们可以得知 WNDCLASS结构体的第二个属性 存储的便是 “窗口回调函数”的函数地址,跟进该地址,我们便可以找到回调函数的代码。(WNDCLASS结构体的定义可以通过MSDN查询得知)

4.1 目标1:找到该程序对鼠标左/右键进行处理的代码

  • 定位程序对于鼠标左键的处理代码,过程如下图:

  • 定位程序对于鼠标右键的处理代码,过程如下图:

4.2 目标2:找到另外2个正确的键盘按键

  • 分析过程如下图:

4.3 在这里,对上述分析过程做一个简要说明

  • 对于回调函数WindowProc的参数做一个简单说明,详情可在MSDN中查询:

    LRESULT CALLBACK WindowProc(
      IN  HWND hwnd,
      IN  UINT uMsg,
      IN  WPARAM wParam,
      IN  LPARAM lParam
    ){...}
    • 当在窗口中按下 鼠标左键 时,该函数的 uMsg参数 会被赋值为 0x0201
    • 当在窗口中按下 鼠标右键 时,该函数的 uMsg参数 会被赋值为 0x0204
    • 当在窗口中按下 键盘按键 时,该函数的 uMsg参数 会被赋值为 0x0100wParam参数 会被赋值为 按键对应的键盘码

  • 对于 0x4010F0位置处 的汇编指令:mov eax,dword ptr ss:[esp+0x8],ss:[esp+0x8]中存储的是什么?或者说它指的是什么?

  • 对于 0x401120位置处 的汇编指令:mov eax,dword ptr ss:[esp+0x30],ss:[esp+0x30]中存储的是什么?或者说它指的是什么?

    • 答:首先我们要知道,本次分析的程序是 release版 的,也就是说此时 对于参数和局部变量 所采用的 寻址方式 不再是 ebp寻址,而是 __esp寻址__了。相比于ebp寻址,esp寻址分析起来会比较的麻烦,因为 esp的值是会不断发生变化的

    • 0x4010F0位置处的汇编指令:mov eax,dword ptr ss:[esp+0x8] 汇编指令分析

    • 0x401120位置处的汇编指令:mov eax,dword ptr ss:[esp+0x30]汇编指令分析