【WINAPI】MessageBox细解(二)

本文是《WinAPI MessageBox细解》系列的第二部分,主要探讨未公开的MessageBoxTimeoutW()和MessageBoxWorker()函数。通过静态分析,揭示了MessageBox系列函数的工作原理,包括线程状态检测、窗口构建过程、按钮处理等核心代码。通过对函数的详细剖析,补充了MSGBOXDATA结构的理解,并预告了后续将深入讲解ServiceMessageBox()和SoftModalMessageBox()。
摘要由CSDN通过智能技术生成

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 运行结果

  1. MessageBoxTimeoutW:MessageBoxTimeoutW
  2. MessageBoxWorker:MessageBoxWorker
    可以看到两个函数都可以正常执行。MessageBoxWorker()有两个更改按钮语言的语句,其中有一个是无效的。无效的原因在本文其实已经能够看到,但请允许我留到最后一篇再去说明。

0x02 函数细解

2.1 前篇回顾

MessageBox细解(一)中我们介绍了三个微软公开的主要创建消息盒子的函数。分别是:

  1. MessageBox()
  2. MessageBoxEx()
  3. 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:
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值