逆向基础-win32窗口与堆栈平衡

1 什么是窗口

​ Windows 是多任务的操作系统,可以同时运行多个程序,各个程序在屏幕上的显示不会互相干扰,在后台的程序也可能随时向屏幕输出内容,这中间的调度是由 Windows 完成的。 Windows 采用的方法是给程序一块矩形的屏幕空间, 这就是窗口。

一个程序可以建立多个顶层窗口, 如Windows 的桌面和任务栏都是顶层窗口;Windows 的窗口采用层次结构, 一个窗口中可以建立多个子窗口,如窗口中的状态栏、工具栏等。运行的程序并非一定就是窗口, 比如悄悄在后台运行的木马程序就不会显示一个窗口向用户报告它在干非法勾当。窗口程序是事件驱动的, 用户可能随时发出各种消息, 这意味着程序要随时可以处理请求。

2 Win32 窗口程序框架

2.1 窗口程序运行流程

在屏幕上显示一个窗口的过程一般有以下步骤, 这就是主程序的结构流程:

  1. 得到应用程序的句柄 (GetModuleHandle)
  2. 注册窗口类 (RegisterClassEx)。在注册之前, 要先填写 RegisterClassEx 的参数WNDCLASSEX 结构
  3. 建立窗口 (CreateWindow Ex)
  4. 显示窗口 (SbowWindow)
  5. 刷新窗口客户区 (UpdateWindow)
  6. 进入无限的消息获取和处理的循环
  7. 首先获取消息 (GetMessage)
  8. 如果有消息到达, 则将消息分派到回调函数处理(DispatchMessage), 如果消息是WM_QUIT, 则退
    出循环。
  9. DispatchMessage 在自己的内部回过来调用窗口过程(_Proc WinMain)。

为什么要由Windows来调用窗口过程?

  1. 如果程序自己处理消息就必须自己维护本程序所属窗口的消息队列,当程序建立多个窗口时,消息队列管理比较复杂
  2. 其它程序也可能用SengMessage通过Windows直接调用你的窗口过程
  3. Windows并不是把所有的消息都放进消息队列,有的消息是直接调用窗口过程处理的,如WM_SETCURSOR等实时性很强的消息

如图

2.2 模块和句柄

​ 一个模块代表的是一个运行中EXE文件或DLL文件, 用来代表这个文件中所有的代码和资源, 磁盘上的文件不是模块, 装入内存后运行时就叫做模块。一个应用程序调用其他DLL中的API时, 这些DLL文件被装入内存, 就产生了不同的模块, 为了区分地址空间中的不同模块, 每个模块都有一个唯一的模块句柄来标识。句柄只是一个数值而已,它的值对程序来说是没有意义的,它只是Windows 用来表示各种资源的编号而已。

3 堆栈平衡

​ 在调用子程序时,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序的参数压入堆栈,子程序在堆栈中取出相应的值再使用, 比如, 如果要调用:

SubRouting(Varl,Var2,Var3)
//经过编译后的最终代码可能是:
push Var3
push Var2
push Var1
call Subrouting
add wsp, 12

​ 调用者首先把参数压入堆栈, 然后调用子程序, 在完成后, 由于堆栈中先前压入的数不再有用, 调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态,这就叫堆栈的平衡。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定(称为调用约定), 不然就会产生错误的结果, 这就是在上述文字中使用“ 可能” 这两个字的原因。

不同语言的调用约定如下图