Win32子窗口创建,子窗口回调函数,消息堆栈,逆向定位子窗口消息处理过程

news2024/11/25 22:30:03

本专栏上一篇文章中我们讲解了Win32程序入口识别,定位回调函数,具体事件处理的定位,这一章节中我们来讲解一下子窗口的创建,子窗口的回调函数,并且逆向分析子窗口消息处理过程。

文章目录

  • 一.子窗口按钮的创建
    • - 创建子窗口:
    • 获取系统注册的窗口类的信息
  • 二.按钮事件处理
  • 三.消息堆栈
  • 四.逆向定位子窗口消息处理过程

一.子窗口按钮的创建

我在学到本课程中Win32的时候,实在是听不懂,所以我专门去学习了Win32应用程序设计,当然,我也在CSDN中分享出来了我的学习成果,大家如果不是很理解这里的子窗口的创建的话,可以到我的达内Windows/Win32编程中大致看一下:达内Windows/Win32编程专栏。
这里我们讲解子窗口:按钮的创建过程。
在windows内核中,不仅有我们注册进去的窗口类,还有一些系统已经帮我们注册好的窗口类,这里的按钮就是系统帮我们注册好的窗口类。我们在创建按钮的时候,直接调用就可以了。
我们来看看创建按钮的实操:

- 创建子窗口:

使用CreateWindow()函数:
MSDN官方文档解释该函数

	HWND hButton = CreateWindow(
		TEXT("Button"), 
		TEXT("anniu"),
		WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_DEFPUSHBUTTON,
		10, 10,
		80, 20,
		hwnd,
		(HMENU)1001,     //这是一个编号,我们后面对子串口消息做处理的时候会用到
		hIns,
		NULL);

注意这里的创建子窗口一定要在主窗口创建之后,因为我们在创建子窗口的时候需要用到父窗口句柄。
实际上这里创建了子窗口之后,正常编写显示窗口等等,按钮就被创建出来了:

// P74子窗口(按钮).cpp : 定义应用程序的入口点。
//
#define _CRT_SECURE_NO_WARNINGS 1
#include "framework.h"
#include "P74子窗口(按钮).h"
#include <stdio.h>
#define MAX_LOADSTRING 100

HANDLE g_hOUTPUT = 0;   //接收标准输出句柄
HINSTANCE hIns = 0;
LRESULT CALLBACK WindowProc(
	IN  HWND hwnd,
	IN  UINT uMsg,
	IN  WPARAM wParam,
	IN  LPARAM lParam
);        //普通窗口回调函数

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	hIns = hInstance;
	//窗口类名
	TCHAR className[] = TEXT("My First Windows.");
	//创建窗口对象
	WNDCLASS wndclass = { 0 };
	wndclass.hbrBackground = (HBRUSH)0;            //窗口背景颜色
	wndclass.lpfnWndProc = WindowProc;                    //窗口的过程函数
	wndclass.lpszClassName = className;                     //窗口的类名字
	wndclass.hInstance = hInstance;                         //定义窗口类的应用程序的实例句柄
	wndclass.lpszMenuName = NULL;
	//注册窗口
	RegisterClass(&wndclass);
	//创建窗口
	HWND hwnd = CreateWindow(
		className,                         //lpname类名
		TEXT("我的第一个窗口。"),          //窗口标题
		WS_OVERLAPPEDWINDOW,               //dwStyle
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL);
	HWND hButton = CreateWindow(
		TEXT("Button"), 
		TEXT("按钮"),
		WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_DEFPUSHBUTTON,
		10, 10,
		80, 20,
		hwnd,
		(HMENU)1001,
		hIns,
		NULL);

	//显示窗口
	ShowWindow(hwnd, SW_SHOW);

	// 主消息循环:
	MSG msg;
	while (1) {
		if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
			if (GetMessage(&msg, NULL, 0, 0)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else {
				return 0;
			}
		}
		else {
			//在空闲的时候,我们想让它做什么就可以填在这里
			//i++;
			//TCHAR std[256] = { 0 };
			//sprintf((char*)std,TEXT("进程无消息....%d\n"), i++);
			//WriteConsole(g_hOUTPUT, std,strlen((const char*)std), 0, 0);
		}
	}
	return (int)msg.wParam;
};
LRESULT CALLBACK WindowProc(
	IN  HWND hwnd,
	IN  UINT uMsg,
	IN  WPARAM wParam,
	IN  LPARAM lParam
)
{
	char output[256] = { 0 };
	switch (uMsg)
	{
		//常见消息
	case WM_DESTROY: {
		PostQuitMessage(0);
		return 0;
	}
	case WM_CREATE: {
		sprintf(output, "检测到WM_CREATE消息,将创建窗口。\n");
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_SIZE: {
		sprintf(output, "lParam:窗口宽变化为:%d,窗口高变化为:%d \n", HIWORD(lParam), LOWORD(lParam));
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	//键盘消息
	case WM_KEYDOWN: {
		sprintf(output, "检测到WM_KEYDOWN消息,键码值:%d.\n", wParam);
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_KEYUP: {
		sprintf(output, "检测到WM_KEYUP消息,键码值:%d.该按键被放开\n", wParam);
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	//鼠标消息
	case WM_LBUTTONDOWN: {
		sprintf(output, "检测到WM_LBUTTONDOWN消息,鼠标左键被按下。\n");
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_LBUTTONUP: {
		sprintf(output, "检测到WM_LBUTTONUP消息,鼠标左键被放开。\n");
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_RBUTTONDOWN: {
		sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被按下。\n");
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_RBUTTONUP: {
		sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被放开。\n");
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_MOUSEMOVE: {
		sprintf(output, "检测到WM_MOUSEMOVE消息,鼠标移动中,鼠标位置(%d,%d).\n", LOWORD(lParam), HIWORD(lParam));
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0; break;
	}
	case WM_MOUSEWHEEL: {
		sprintf(output, "鼠标滚轮滚动中,偏移量:%d,鼠标当前位置(%d,%d)\n", HIWORD(wParam), LOWORD(lParam), HIWORD(lParam));
		WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
		return 0;
	}
	case WM_CONTEXTMENU: {
		TrackPopupMenu(
			CreatePopupMenu(),
			TPM_LEFTALIGN,
			0, 0, 0, hwnd, 0);
		return 0;
	}
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

这里我是用的很多代码都是之前在编写Windows32程序调试的代码,大家主要看一下创建按钮的过程函数就可以了,我们来看看创建效果:

按钮

获取系统注册的窗口类的信息

我们在使用按钮等系统帮我们注册的窗口类的时候,我们并不能知道类的内部成员的参数,那么既然我们是学底层的,我们就应该了解一下它内部的参数:

  • 获取类名:
    使用GetClassName()函数,MSDN官方文档解释
int GetClassName(
  [in]  HWND   hWnd,
  [out] LPTSTR lpClassName,
  [in]  int    nMaxCount
);

参数解释:
hWnd:我们已经创建了的窗口的句柄
lpClassName:这是一个OUT类型的参数,获取到的名称字符串将被存储进去
nMaxCount:最大长度

这个函数能获取已经创建了的窗口类名

  • 获取窗口类的完整参数:
    使用GetClassInfo()函数,MSDN官方文档解释
BOOL GetClassInfoA(
  [in, optional] HINSTANCE   hInstance,
  [in]           LPCSTR      lpClassName,
  [out]          LPWNDCLASSA lpWndClass
);

参数解释:
hInstance:当前应用程序实例句柄,也就是那个ImageBase,我们需要使用全局变量来取得
lpClassName:窗口类名
lpWndClass:这是一个OUT类型的参数,我们需要先定义一个WndClass类型的结构,用于存储获取到的参数,然后将这个结构的地址给这个参数,获取到的参数将会被存储到这个结构体中。

二.按钮事件处理

我们来看看按钮消息的处理过程:

按钮被点击,产生消息->操作系统WinProc->转化为WM_COMMAND类型的消息->父窗口的WinProc

这里其实子窗口的消息处理函数最后都会被传到主窗口的消息处理函数中,所以我们需要处理按钮消息的时候,只需要在主窗口的消息处理函数中处理即可:
这里给出一段伪代码,供大家理解:

主窗口WinProc(...){
	switch(uMsg){
		case WM_COMMAND:{
			switch(LOWORD(wParam){
				case(消息编号){
				.......//具体处理
				}
			}
		}
	}
}

看到这里,相信大家能看出来,这里的wParam的低两字节,就存储的是消息编号,我们根据这个,就可以对按钮消息做具体处理了。

三.消息堆栈

还记得我们在上一篇文章中对具体的事件做的条件断点的条件吗?
[esp+8] == WM_KEYDOWN
可能有很多人还是不理解这里为啥这里是esp+8,这里我们来看看消息堆栈,相信大家就会恍然大悟:
消息堆栈

WinProc函数需要四个参数,在调用函数之前,就已经压入栈中了,在进入函数的时候,就会在栈中压入返回地址,所以这里的esp+8就是uMsg了。

四.逆向定位子窗口消息处理过程

我们在逆向过程中,必须要学会定位子窗口的消息处理。
我们来看看在逆向过程中定位子窗口消息处理的过程:

  • 定位入口函数
  • 定位主窗口回调函数
    在定位主窗口回调函数之后,结合我们之前讲解的,我们需要在回调函数中做消息断点即可,比如我们要定位的按钮编号为0x000003E9,这里我们将条件设置为[esp+8]==WM_COMMAND && [EXP+0XC]==0X00003E9即可得到断点。
  • 获取按钮编号:我们在OllyDbg中可以直接看到按钮的编号:
    按钮编号
  • 条件断点设置:
    条件断点设置
    这样,当我们点击按钮的时候,程序就会断在这里了,我们就可以逆向分析出当点击按钮的时候,程序到底做了哪些处理:

程序断点
今天的文章就分享到这里,希望对大家有所帮助,另外,如果大家发现了文章中的错误,还请大家指出来,我会非常虚心地学习。希望我们共同进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

charge pump的分析与应用

春节前最后一更&#xff0c;提前祝大家新春快乐&#xff0c;阖家安康&#xff0c;工作顺利&#xff01; 定义&#xff1a; 电荷泵是利用电容的充放电来实现电压的转换的&#xff0c;输入回路和输出回路轮流导通。通过调节占空比来调节输出电压。 它们能使输入电压升高或降低&…

基于PyQt5连接本地SQLite实现简单人力资源管理系统

人力资源管理系统 使用环境&#xff1a;Python3.86 PyQt5.15.4 sqlite3 记录一下最近学校举办的一个程序设计比赛&#xff0c;题目是实现一个简单的人力资源管理系统&#xff0c;文末有效果展示 我认为程序是面向人类而不是面向机器的&#xff0c;所以我使用了PyQt5封装了一…

SpringCloud源码分析 (Eureka-Server-处理客户端续约请求) (七)

文章目录 1.处理客户端续约请求1.1 InstanceResource.renewLease()1.2 InstanceRegistry.renew()1.3 PeerAwareInstanceRegistryImpl.renew()1.4 AbstractInstanceRegistry.renew()1.6 PeerAwareInstanceRegistryImpl.replicateToPeers()1.7 PeerEurekaNode.headbeat() 1.处理客…

大数据Doris(二十二):Rollup物化索引创建与操作

文章目录 Rollup物化索引创建与操作 一、创建测试表 二、创建Rollup物化索引表

岗位少,竞争激烈,这是今年软件测试就业的真实写照,也是所有岗位的真实写照。

前两天跟一个HR朋友聊天&#xff0c;她表示刚在boss上发布了一个普通测试岗位&#xff0c;不到一小时竟然收到了几百份简历。而且简历质量极高&#xff0c;这是往年不敢想象的。岗位少&#xff0c;竞争激烈&#xff0c;这是今年软件测试就业的真实写照&#xff0c;也是所有岗位…

若依框架快速开发项目(避坑超详细)

若依框架快速开发项目&#xff08;避坑超详细&#xff09; 初衷&#xff1a; 若依框架使用及其普遍&#xff0c;是一个非常优秀的开源框架&#xff0c;框架本身的权限系统&#xff0c;字典设置以及相关封装&#xff0c;安全拦截相当完善&#xff0c;本人受益匪浅&#xff0c;学…

Python进阶实际应用开发实战(一)

目录 原型设计和环境环境设置创建新项目 原型设计和环境 原书第一章内容 环境设置 对于一个项目我们需要安装库并管理依赖项&#xff0c;这意味着需要有一个虚拟环境。我们使用pipenv来指定依赖项。 python -m pip install --user pipenv在命令行中启动Python脚本的时候&am…

分布式锁概念

什么是分布式锁 方案一&#xff1a;SETNX EXPIRE 方案二&#xff1a;SETNX value值是&#xff08;系统时间过期时间&#xff09; 方案三&#xff1a;使用Lua脚本(包含SETNX EXPIRE两条指令) 方案四&#xff1a;SET的扩展命令&#xff08;SET EX PX NX&#xff09; 方案五…

chatgpt赋能Python-aidlearning安装pycharm

Aid Learning: 如何安装PyCharm PyCharm是一款由JetBrains开发的用于Python编程的集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的编辑器和调试工具&#xff0c;可以帮助开发者更高效地编写Python代码。本文将介绍如何安装PyCharm。 安装前准备工作 在安装PyCharm…

vue diff算法与虚拟dom知识整理(8) 手写patch实现简易版的节点第一次上dom树

上一文 我们整理了一下 patch 函数的整体过程 我不知道大家有没有保留我们之前学手写H函数时的那个案例 我们要将index.jsi还原成这样 参考代码如下 import h from "./snabbdom/h";const dom1 h("div",{props: {class: "dom"} },"文本测试…

Qt QJson 使用

文章目录 1. 简介2. 简单实例3. 结果 1. 简介 QJson 是一个用于 Qt 应用程序的 JSON 解析和生成库。JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;具有良好的可读性和可扩展性&#xff0c;常用于 Web 应用程序中。QJson 将 …

Python通过natcap.invest库调用InVEST模型批处理数据(Carbon Storage and Sequestration模块)

InVEST&#xff08;Integrated Valuation of Ecosystem Servicesand Tradeoffs&#xff09;生态系统服务和权衡的综合评估模型&#xff0c;旨在通过模拟不同土地覆被情景下生态系统物质量和价值量的变化。它提供了多种生态系统服务功能评估&#xff0c;包括了淡水生态系统评估、…

Liunx压缩命令 - gz

gz命令详解及使用 Linux中gz命令的全称是gzip&#xff0c;它是一种常用的压缩程序&#xff0c;可将文件或目录压缩为.gz格式&#xff0c;以节省存储空间&#xff0c;同时也可以通过解压缩操作重新获取原始文件掌握 gzip 命令&#xff0c;可以帮助我们更加高效地进行文件压缩与…

视频编码测试平台CodecWar

​在视频编码的研究和开发中&#xff0c;我们经常需要对编码器的性能进行比较&#xff0c;一般的做法是构建一个数据集&#xff0c;然后将两个编码器在同一个配置上&#xff08;LD,RA,AI等&#xff09;使用不同参数&#xff08;CQP下一般为4个QP值&#xff09;编码&#xff0c;…

Py之pymc:pymc的简介、安装、使用方法之详细攻略

Py之pymc&#xff1a;pymc的简介、安装、使用方法之详细攻略 目录 pymc的简介 pymc的安装 pymc的使用方法 1、时序性任务 (1)、使用 Euler-Maruyama 方案推断 SDE 的参数 pymc的简介 PyMC&#xff08;以前称为PyMC3&#xff09;是一个专注于高级马尔科夫链蒙特卡洛&#x…

⑤电子产品拆解分析-人体感应灯

⑤电子产品拆解分析-人体感应灯 一、功能介绍二、电路分析以及器件作用三、原理图复现与学习 一、功能介绍 ①感应人体活动亮灯20S&#xff1b;②Micro-USB进行锂电池充电&#xff1b; 二、电路分析以及器件作用 三、原理图复现与学习 R1为下拉电阻&#xff0c;防止上电因芯…

功率放大器的选型原则和方法是什么

功率放大器是一种能够将低电平信号放大到足够高的电平以驱动负载的电子器件。在各种电子设备中&#xff0c;功率放大器被广泛应用&#xff0c;如音响系统、电视广播、汽车音响、射频通信等。因此&#xff0c;正确选型功率放大器非常重要&#xff0c;可以提高设备的性能和可靠性…

Web3的应用及发展

Web3兼具去中心化和交互性&#xff0c;打造了一个全新的互联网模式。在其中&#xff0c;用户可以绕过中介直接交互。dApp用户无需许可即可访问金融工具&#xff0c;以点对点的方式交易加密资产&#xff0c;获得参数型保险理赔&#xff0c;通过NFT交易可验证所有权的数字艺术品&…

[网鼎杯 2020 青龙组]bang 复现

一.前言 在NSSCTF练习安卓逆向,第一次遇到安卓脱壳题 大佬的题解只有一句话"frida-dexdump一把嗦" 听起来容易做起来难,还遇到了安卓虚拟机的玄学bug,折磨了我很久,好在最终使用真机成功dump并得到flag 题目来源:[网鼎杯 2020 青龙组]bang 如果直接用jadx打开会发现…