0x00 简介
0.1 关于
MessageBox细解(一)发布到现在已经过去两天有余了,想着也该更新第二部分了。第一部分讲述了基础的三个微软公开的消息盒子的函数。这一部分就好好研究一下未被公开的两个函数MessageBoxTimeoutW()和MessageBoxWorker()。相信各能够更加理解消息盒子的实现原理。预告:《MessageBox细解》系列还有第三部分和第四部分。
0.2 工具
操作系统:Windows10。
调试器:Ollydbg以及IDA。
编译器:VS2019
0x01 程序对象
0.1 程序源码
同第一部分,0x02的程序。
#include <Windows.h>
#include "resource.h"
#define MSGBOXSP_TIME 0//设置预编译选项
typedef struct _MSGBOXDATA {
MSGBOXPARAMSW mbparams;
BYTE unknown[0X68 - sizeof(MSGBOXPARAMSW)];
}MSGBOXDATA, *LPMSGBOXDATA;
typedef int (WINAPI *MSGTIMEPROC)(HWND, LPCWSTR, LPCWSTR, UINT, WORD, DWORD);
typedef int (_fastcall *MSGWORKERPROC)(LPMSGBOXDATA);
VOID CALLBACK MsgBoxCallback(LPHELPINFO lpHelpInfo);//消息盒子帮助回调函数
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) {
HMODULE hDllUser32;
#ifdef MSGBOXSP_TIME
MSGTIMEPROC MessageBoxTimeout;
#elif defined(MSGBOXSP_WORKER)
MSGBOXDATA mbdata;
MSGBOXPARAMSW mbparams;
MSGWORKERPROC MessageBoxWorker;
#endif
#ifdef MSGBOXSP_TIME
if((hDllUser32 = LoadLibrary(TEXT("user32.dll"))) != NULL) {
MessageBoxTimeout = (MSGTIMEPROC)GetProcAddress(hDllUser32, "MessageBoxTimeoutW");
MessageBoxTimeout(NULL, TEXT("Hello World!"), TEXT("Test"), MB_OKCANCEL,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 3000);
}
#elif defined(MSGBOXSP_WORKER)
if((hDllUser32 = LoadLibrary(TEXT("user32.dll"))) != NULL) {
//这个是10.0.19041.610的user32偏移,昨天系统自动更新了,凑合着吧。
MessageBoxWorker = (MSGWORKERPROC)((DWORD)hDllUser32 + (DWORD)0x0007E5BD);
memset(&mbparams, 0, sizeof(MSGBOXPARAMSW));
mbparams.cbSize = sizeof(MSGBOXPARAMSW);
mbparams.dwStyle = MB_OKCANCEL | MB_USERICON | MB_HELP;
mbparams.lpszText = TEXT("Hello World!");
mbparams.lpszCaption = TEXT("Test");
//设置自定义图标
mbparams.hInstance = hInstance;
mbparams.lpszIcon = TEXT("USERICON");
//设置帮助回文ID和回调函数
mbparams.dwContextHelpId = 0x1;
mbparams.lpfnMsgBoxCallback = MsgBoxCallback;
//设置显示语言
mbparams.dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
memset(&mbdata, 0, sizeof(MSGBOXDATA));
memcpy(&mbdata, &mbparams, sizeof(MSGBOXPARAMSW));
MessageBoxWorker(&mbdata);
}
#endif
return 0;
}
//帮助回调函数
VOID CALLBACK MsgBoxCallback(LPHELPINFO lpHelpInfo) {
switch(lpHelpInfo->dwContextId) {
case 0x1:
MessageBox(NULL, TEXT("这是0x1的帮助"), TEXT("帮助"), MB_OK);
break;
case 0x100:
MessageBox(NULL, TEXT("这是0x100的帮助"), TEXT("帮助"), MB_OK);
break;
default:
MessageBox(NULL, TEXT("这是默认帮助"), TEXT("帮助"), MB_OK);
break;
}
}
本例的user32.dll版本为10.0.19041.610。如果需要兼容性的场合请根据特征码获取函数偏移。
1.2 运行结果
- MessageBoxTimeoutW:
- MessageBoxWorker:
可以看到两个函数都可以正常执行。MessageBoxWorker()有两个更改按钮语言的语句,其中有一个是无效的。无效的原因在本文其实已经能够看到,但请允许我留到最后一篇再去说明。
0x02 函数细解
2.1 前篇回顾
MessageBox细解(一)中我们介绍了三个微软公开的主要创建消息盒子的函数。分别是:
- MessageBox()
- MessageBoxEx()
- MessageBoxIndirect()
并详细分析了三个函数的具体实现原理,并找到了底层调用的MessageBoxTimeoutW()和MessageBoxWorker(),还发现了MessageBoxIndirect()无法更改显示语言的问题。
2.2 静态分析
2.2.1 程序源码
#include <Windows.h>
#include "resource.h"
#define MSGBOXSP_SOFT 0//设置预编译选项
enum close_button_flag {
button_gray,
button_enable_ID1,
button_enable_ID2
};
typedef struct _MSGBOXDATA {
MSGBOXPARAMSW mbparams;
BYTE unknown[8];
WORD wLanguageId;
BYTE unknown1[2];
PDWORD pdwButtonID;
LPCWSTR *pszButtonTextTable;
DWORD dwButtonSum;
DWORD dwButtonDef;
close_button_flag enCloseButtonFlag;
DWORD dwMilliseconds;
BYTE unknown2[28];
}MSGBOXDATA, *LPMSGBOXDATA;
typedef int(_stdcall *MSGSOFTPROC)(LPMSGBOXDATA);
typedef int(_fastcall *MSGSERVICEPROC)(LPCWSTR,LPCWSTR,UINT,DWORD);
VOID CALLBACK MsgBoxCallback(LPHELPINFO lpHelpInfo);//消息盒子帮助回调函数
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) {
HMODULE hDllUser32;
#ifdef MSGBOXSP_SOFT
MSGBOXDATA mbdata;
MSGBOXPARAMSW mbparams;
MSGSOFTPROC SoftModalMessageBox;
DWORD dwButtonIdTable[5] = {
1,2,3,4,9};
LPCWSTR szButtonTextTable[5] ={
TEXT("利雅"),TEXT("出品"),TEXT("必属"),TEXT("精品"),TEXT("帮助")};
#elif defined(MSGBOXSP_SERVICE)
MSGSERVICEPROC ServiceMessageBox;
#endif
#ifdef MSGBOXSP_SOFT
if((hDllUser32 = LoadLibrary(TEXT("user32.dll"))) != NULL) {
//这个是10.0.19041.610的user32偏移,昨天系统自动更新了,凑合着吧。
SoftModalMessageBox = (MSGSOFTPROC)((DWORD)hDllUser32 + (DWORD)0x0007F410);
memset(&mbparams, 0, sizeof(MSGBOXPARAMSW));
mbparams.cbSize = sizeof(MSGBOXPARAMSW);
mbparams.dwStyle = MB_USERICON;
mbparams.lpszText = TEXT("Hello World!");
mbparams.lpszCaption = TEXT("Test");
//设置自定义图标
mbparams.hInstance = hInstance;
mbparams.lpszIcon = TEXT("USERICON");
//设置帮助回文ID和回调函数
mbparams.dwContextHelpId = 0x1;
mbparams.lpfnMsgBoxCallback = MsgBoxCallback;
//设置显示语言
//mbparams.dwLanguageId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
memset(&mbdata, 0, sizeof(MSGBOXDATA));
memcpy(&mbdata, &mbparams, sizeof(MSGBOXPARAMSW));
//设置显示语言(真)
mbdata.wLanguageId = MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN);
//设置自动关闭时间
mbdata.dwMilliseconds = 10000;
//设置按钮
mbdata.dwButtonSum = 5;
mbdata.pdwButtonID = dwButtonIdTable;
mbdata.pszButtonTextTable = szButtonTextTable;
mbdata.dwButtonDef = 0;
mbdata.enCloseButtonFlag = button_enable_ID2;
SoftModalMessageBox(&mbdata);
}
#elif defined(MSGBOXSP_SERVICE)
if((hDllUser32 = LoadLibrary(TEXT("user32.dll"))) != NULL) {
//这个是10.0.19041.610的user32偏移,昨天系统自动更新了,凑合着吧。
ServiceMessageBox = (MSGSERVICEPROC)((DWORD)hDllUser32 + (DWORD)0x0007E986);
ServiceMessageBox(TEXT("Hello World"),TEXT("Test"), MB_OKCANCEL | MB_HELP | MB_ICONWARNING,5000);
OpenThreadToken(0, 0, 0, 0);
}
#endif
return 0;
}
//帮助回调函数
VOID CALLBACK MsgBoxCallback(LPHELPINFO lpHelpInfo) {
switch(lpHelpInfo->dwContextId) {
case 0x1:
MessageBox(NULL, TEXT("这是0x1的帮助"), TEXT("帮助"), MB_OK);
break;
case 0x100:
MessageBox(NULL, TEXT("这是0x100的帮助"), TEXT("帮助"), MB_OK);
break;
default:
MessageBox(NULL, TEXT("这是默认帮助"), TEXT("帮助"), MB_OK);
break;
}
}
示例代码分别调用了ServiceMessageBox()和SoftModalMessageBox(),它们都是属于MessageBoxWorker()下更底层的调用。
2.2.2 MessageBoxTimeoutW()
.text:69E7EDE0 ; __stdcall MessageBoxTimeoutW(x, x, x, x, x, x)
.text:69E7EDE0 public _MessageBoxTimeoutW@24
.text:69E7EDE0 _MessageBoxTimeoutW@24 proc near ; CODE XREF: DisplayExitWindowsWarnings(x)+20F↑p
.text:69E7EDE0 ; MessageBoxExW(x,x,x,x,x)+16↑p ...
.text:69E7EDE0
.text:69E7EDE0 mbdata = _MSGBOXDATA ptr -70h
.text:69E7EDE0 var_4 = dword ptr -4
.text:69E7EDE0 hwnd = dword ptr 8
.text:69E7EDE0 lptext = dword ptr 0Ch
.text:69E7EDE0 lpCaption = dword ptr 10h
.text:69E7EDE0 uType = dword ptr 14h
.text:69E7EDE0 wLanguageId = word ptr 18h
.text:69E7EDE0 dwMilliseconds = dword ptr 1Ch
.text:69E7EDE0
.text:69E7EDE0 mov edi, edi
.text:69E7EDE2 push ebp
.text:69E7EDE3 mov ebp, esp
.text:69E7EDE5 and esp, 0FFFFFFF8h
.text:69E7EDE8 sub esp, 74h
.text:69E7EDEB mov eax, ___security_cookie
.text:69E7EDF0 xor eax, esp
.text:69E7EDF2 mov [esp+74h+var_4], eax
.text:69E7EDF6 push ebx
.text:69E7EDF7 mov ebx, [ebp+lpCaption]
.text:69E7EDFA lea eax, [esp+78h+mbdata]
.text:69E7EDFE push esi
.text:69E7EDFF mov esi, [ebp+hwnd]
.text: