0%

ATL框架逆向初学

  • 小实例与atl基本概念

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    #include "pch.h"
    #include "framework.h"
    #include "resource.h"
    #include "ATLProject1_i.h"

    #include <windows.h>
    #include <atlbase.h>
    #include <atlwin.h>
    #include <tchar.h>
    #include <sal.h>

    using namespace ATL;

    // 自定义消息(仿你的 0x8007/0x8009/0x37F)
    constexpr UINT WM_APP_8007 = 0x8007;
    constexpr UINT WM_APP_8009 = 0x8009;
    constexpr UINT WM_APP_037F = 0x037F;

    // 按钮 ID
    enum : int {
    IDC_BTN_SEND8007 = 1001,
    IDC_BTN_ASK8009 = 1002,
    IDC_BTN_SEND037F = 1003
    };

    // 用来模拟你看到的 *(*(this+32)+32)=1(handled flag)
    struct DispatchState
    {
    BYTE pad[32]; // 0..31
    int handledFlag; // offset 32
    DispatchState() : handledFlag(0) {}
    };

    class CMainWindow : public CWindowImpl<CMainWindow>
    {
    public:
    // 避免 DECLARE_WND_CLASSW 在某些工程配置下宏解析异常,使用最稳的 GetWndClassInfo
    static ATL::CWndClassInfo& GetWndClassInfo()
    {
    static ATL::CWndClassInfo wc =
    {
    { sizeof(WNDCLASSEXW),
    CS_HREDRAW | CS_VREDRAW,
    StartWindowProc,
    0, 0,
    nullptr,
    nullptr,
    nullptr,
    (HBRUSH)(COLOR_WINDOW + 1),
    nullptr,
    L"ATLProject1_MainWindow",
    nullptr },
    nullptr, nullptr, IDC_ARROW, TRUE, 0, _T("")
    };
    return wc;
    }

    BEGIN_MSG_MAP(CMainWindow)
    MESSAGE_HANDLER(WM_CREATE, OnAnyMessage)
    MESSAGE_HANDLER(WM_DESTROY, OnAnyMessage)
    MESSAGE_HANDLER(WM_CLOSE, OnAnyMessage)

    MESSAGE_HANDLER(WM_COMMAND, OnCommand)

    MESSAGE_HANDLER(WM_APP_8007, OnAnyMessage)
    MESSAGE_HANDLER(WM_APP_8009, OnAnyMessage)
    MESSAGE_HANDLER(WM_APP_037F, OnAnyMessage)

    // 其他消息走默认处理
    END_MSG_MAP()

    CMainWindow()
    {
    m_state = new DispatchState();
    }
    ~CMainWindow()
    {
    delete m_state;
    m_state = nullptr;
    }

    private:
    DispatchState* m_state = nullptr;

    HWND m_forwardHwnd = nullptr; // 对应你反编译里的 *(this+64)
    int m_counter = 0;

    private:
    // 仿 sub_7217A0(this, lParam)
    int sub_7217A0_like(LPARAM /*lParam*/)
    {
    m_forwardHwnd = ::CreateWindowExW(
    0, L"STATIC", L"(Forward target: STATIC)",
    WS_CHILD | WS_VISIBLE,
    20, 20, 320, 24,
    m_hWnd, nullptr, ::GetModuleHandleW(nullptr), nullptr);

    ::CreateWindowExW(0, L"BUTTON", L"Send 0x8007",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    20, 60, 140, 32,
    m_hWnd, (HMENU)(INT_PTR)IDC_BTN_SEND8007, ::GetModuleHandleW(nullptr), nullptr);

    ::CreateWindowExW(0, L"BUTTON", L"Ask 0x8009",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    170, 60, 140, 32,
    m_hWnd, (HMENU)(INT_PTR)IDC_BTN_ASK8009, ::GetModuleHandleW(nullptr), nullptr);

    ::CreateWindowExW(0, L"BUTTON", L"Send 0x037F",
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    320, 60, 140, 32,
    m_hWnd, (HMENU)(INT_PTR)IDC_BTN_SEND037F, ::GetModuleHandleW(nullptr), nullptr);

    return 0;
    }

    // 仿 sub_721920(this)
    void sub_721920_like()
    {
    m_forwardHwnd = nullptr;
    }

    // 仿 sub_721780()
    void sub_721780_like()
    {
    ::MessageBoxW(m_hWnd, L"WM_CLOSE intercepted (sub_721780-like).", L"ATLProject1", MB_OK);
    }

    // 仿 sub_721980(this, lParam, wParam)
    int sub_721980_like(LPARAM lp, WPARAM wp)
    {
    wchar_t buf[128]{};
    ::wsprintfW(buf, L"Handled 0x037F: wp=%llu lp=%llu",
    (unsigned long long)wp, (unsigned long long)lp);
    ::MessageBoxW(m_hWnd, buf, L"0x037F", MB_OK);
    return 123;
    }

    // ====== 核心:仿你那个 maybe3 的 switch(但用 ATL handler 包起来)======
    BOOL maybe3_like(UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& outResult, int a7_like)
    {
    if (a7_like) // 对应 if(a7) return 0;
    return FALSE;

    switch (Msg)
    {
    case WM_CREATE: // 1
    m_state->handledFlag = 1;
    outResult = sub_7217A0_like(lParam);
    return TRUE;

    case WM_DESTROY: // 2
    m_state->handledFlag = 1;
    sub_721920_like();
    outResult = 0;
    return TRUE;

    case WM_CLOSE: // 0x10
    m_state->handledFlag = 1;
    sub_721780_like();
    outResult = 0;
    return TRUE;

    case WM_APP_8007: // 0x8007
    m_state->handledFlag = 1;
    outResult = (m_forwardHwnd)
    ? ::SendMessageW(m_forwardHwnd, WM_APP_8007, wParam, lParam)
    : 0;
    return TRUE;

    case WM_APP_8009: // 0x8009
    m_state->handledFlag = 1;
    outResult = 1;
    return TRUE;

    case WM_APP_037F: // 0x37F
    m_state->handledFlag = 1;
    outResult = sub_721980_like(lParam, wParam);
    return TRUE;
    }

    return FALSE;
    }

    LRESULT OnAnyMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
    LRESULT lr = 0;

    // 这里的 a7_like 我们固定传 0,等价于“允许处理”
    if (maybe3_like(uMsg, wParam, lParam, lr, 0))
    {
    bHandled = TRUE;
    // 对应你反编译的:if(flag) return 1;
    return lr;
    }

    bHandled = FALSE;
    return 0;
    }

    LRESULT OnCommand(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
    {
    if (HIWORD(wParam) != BN_CLICKED)
    {
    bHandled = FALSE;
    return 0;
    }

    switch (LOWORD(wParam))
    {
    case IDC_BTN_SEND8007:
    ::SendMessageW(m_hWnd, WM_APP_8007, 0x11, 0x22);
    bHandled = TRUE;
    return 0;

    case IDC_BTN_ASK8009:
    {
    LRESULT r = ::SendMessageW(m_hWnd, WM_APP_8009, 0, 0);
    wchar_t buf[64]{};
    ::wsprintfW(buf, L"0x8009 returned: %lld", (long long)r);
    ::MessageBoxW(m_hWnd, buf, L"Ask 0x8009", MB_OK);
    bHandled = TRUE;
    return 0;
    }

    case IDC_BTN_SEND037F:
    ::SendMessageW(m_hWnd, WM_APP_037F, 0xAA, 0xBB);
    bHandled = TRUE;
    return 0;
    }

    bHandled = FALSE;
    return 0;
    }
    };

    // ====== ATL EXE Module(你的模板风格)======
    class CATLProject1Module : public ATL::CAtlExeModuleT<CATLProject1Module>
    {
    public:
    DECLARE_LIBID(LIBID_ATLProject1Lib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_ATLPROJECT1, "{2f5aaa8a-ac5d-490a-aabe-66c834990abe}")

    int Run(int nShowCmd) throw()
    {
    CMainWindow wnd;

    HWND hWnd = wnd.Create(nullptr, CWindow::rcDefault,
    L"ATLProject1 - maybe3-like (no ProcessWindowMessage override)",
    WS_OVERLAPPEDWINDOW);
    if (!hWnd)
    return 0;

    ::ShowWindow(hWnd, nShowCmd);
    ::UpdateWindow(hWnd);

    MSG msg{};
    while (::GetMessageW(&msg, nullptr, 0, 0))
    {
    ::TranslateMessage(&msg); // ✅ 正确:没有 TranslateMessageW
    ::DispatchMessageW(&msg);
    }
    return (int)msg.wParam;
    }
    };

    CATLProject1Module _AtlModule;

    // ✅ 用 SAL 注解,解决 /analyze 下 “wWinMain 批注不一致”
    // (_tWinMain 在 UNICODE 下就是 wWinMain,但这里我们按模板签名加注解)
    extern "C" int WINAPI _tWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPTSTR lpCmdLine,
    _In_ int nShowCmd)
    {
    (void)hInstance; (void)hPrevInstance; (void)lpCmdLine;
    return _AtlModule.WinMain(nShowCmd);
    }

    • ATL框架

      C++模板库用于简化COM组件和windows窗口开发

    • 窗口类:CWindowImpl

      1
      class CMainWindow : public CWindowImpl<CMainWindow>

      CWindowImpl<T> 是ATL窗口类的基本模板,派生类 CMainWindow 将自己作为模板参数传入

      这个主要是负责窗口创建和消息循环之类的底层细节,跟逆向关系不大

    • windows消息ID

      • 消息ID划分

        系统保留信息(0x0000 - 0x03FF),用户自定义消息(0x0400 - 0x7FFF),应用程序注册消息(0x8000 - 0xBFFF)

        在代码中就是:

        1
        2
        3
        constexpr UINT WM_APP_8007 = 0x8007;
        constexpr UINT WM_APP_8009 = 0x8009;
        constexpr UINT WM_APP_037F = 0x037F;

        定义了三个应用程序注册信息

    • 消息映射:BEGIN_MSG_MAP / END_MSG_MAP

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      BEGIN_MSG_MAP(CMainWindow)
      MESSAGE_HANDLER(WM_CREATE, OnAnyMessage)
      MESSAGE_HANDLER(WM_DESTROY, OnAnyMessage)
      MESSAGE_HANDLER(WM_CLOSE, OnAnyMessage)

      MESSAGE_HANDLER(WM_COMMAND, OnCommand)

      MESSAGE_HANDLER(WM_APP_8007, OnAnyMessage)
      MESSAGE_HANDLER(WM_APP_8009, OnAnyMessage)
      MESSAGE_HANDLER(WM_APP_037F, OnAnyMessage)

      // 其他消息走默认处理
      END_MSG_MAP()

      这个是ATL最重要的一部分,可以看到其有四个win32标准消息,三个用户自定义的消息

      MESSAGE_HANDLER(消息ID,处理函数) 函数代表其会把对应的消息路由至成员函数 OnAnyMessage 进行处理

      ATL内部会生成并维护一份代码 ProcessWindowMessage 用于查找消息映射表

      大概类似这样:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 
      LRESULT& lResult)
      {
      // uMsg = 0x8007

      // 遍历消息映射表
      if (uMsg == WM_CREATE) { /* ... */ }
      if (uMsg == WM_DESTROY) { /* ... */ }
      if (uMsg == WM_CLOSE) { /* ... */ }
      if (uMsg == WM_COMMAND) { /* ... */ }

      // ✅ 匹配到这里!
      if (uMsg == WM_APP_8007)
      {
      BOOL bHandled = TRUE;
      lResult = OnAnyMessage(uMsg, wParam, lParam, bHandled); // 调用 OnAnyMessage
      if (bHandled)
      return TRUE;
      }
      // ... 其他消息
      }

      在用户点击自定义消息 WM_APP_8007 对应的按钮时:

      1
      2
      3
      case IDC_BTN_SEND8007:
      ::SendMessageW(m_hWnd, WM_APP_8007, 0x11, 0x22); // 发送消息
      // 此时程序会 "跳入" 到消息处理流程

      其会进入 ProcessWindowMessage 进行消息映射查找,并根据里面的代码调用 OnAnyMessage

      OnAnyMessage 中:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
          LRESULT OnAnyMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
      {
      LRESULT lr = 0;
      // 这里的 a7_like 我们固定传 0,等价于“允许处理”
      if (maybe3_like(uMsg, wParam, lParam, lr, 0))
      {
      bHandled = TRUE;
      // 对应你反编译的:if(flag) return 1;
      return lr;
      }
      bHandled = FALSE;
      return 0;
      }

      又会调用 maybe3_like 进行二次分发,在switch语句中走 case WM_APP_8007 的分支进行消息转发

    • 逆向分析

      需要用spy++

      对于刚刚那个小程序的exe进行逆向分析,spy++的日志中全部过滤只留下WM_COMMAND

      当按下第一个按钮时:

      spy++的消息意思是发送了 WM_COMMAND 的消息,且包含了参数 1001

      WM_COMMAND 是win32框架内的,在 WinUser.h 中已经定义好了消息ID为 0x111

      也就是在ida逆向出的 ProcessWindowMessage 函数中,对应的这一段:

      1
      2
      3
      4
      5
      6
      7
      if ( uMsg == 0x111 )
      {
      bHandled[0] = 1;
      *lResult = CMainWindow::OnCommand(this, 0x111u, wParam, lParam, bHandled);
      if ( bHandled[0] )
      return 1;
      }

      这里的 wParam 就是刚刚捕捉到的参数 1001

      那么跳转到 OnCommand 函数实现中:

      可以看到这里 wParam==1001 时,其调用了 SendMessageW 来发送0x8007

      而按钮2、3所对应的参数值就是1002和1003,其经过同样的调用链,进入这里的不同分支中

  • 实操

    目标是某v开头的软件

    点击目标按钮后得到的是 WM_COMMAND wID:8002

    由于没有符号表,不能直接搜索到 ProcessWindowMessage 函数

    用alt-i搜索WM_COMMAND对应的消息ID 0x111

    显示有很多种,但是不太会筛,大致挨个点过去一遍,发现这个的上半部分符合 ProcessWindowMessage 的特征,左边是目标程序,右边是刚刚的实例:

    两者的数据类型差不多,而且都是通过第三个参数 n0x132/uMsg 做选择判断

    所以猜测这个函数上面就是 ProcessWindowMessage

    而下面则是对应的 OnCommand

    所以8002对应的函数就是:

    1
    2
    3
    4
    5
    case 0x8002:
    *(this[6] + 32) = 1;
    sub_608590(this, HIWORD(wParam), 32770, lParam);
    *a6 = 0;
    goto LABEL_5;