【Win32】资源文件(对话框),逆向对话框回调函数,消息断点(附带恶意软件源码)

news/2024/11/15 1:39:27/

之前在学习windows编程的时候已经写过对话框的创建了,其中包括了对话框的分类,原理等等,大家可以去看一下:【windows编程之对话框】对话框原理,对话框的创建。原理今天就讲的不是很多了,直接给大家给出步骤+代码,如果大家不懂原理的话,可以去那一片博客中看一下。

我们今天主要讲解资源文件(对话框)的创建步骤,以及在逆向过程中很有用的一种断点方式:消息断点。

一.创建对话框

    1. 可视化界面绘制对话框
      绘制对话框
    1. 我们在可视化界面绘制对话框之后,实际上就可以使对话框显示了:
      我们使用DialogBox函数即可让会话框显示:
DialogBox(hInst, (LPCWSTR)IDD_DIALOG1, NULL, DiaLogProc);

对话框

实际上这里是模式对话框,是根据返回值退出的,具体可以看着一篇博客:【windows编程之对话框】对话框原理,对话框的创建。

二.获取文本框内容

我们的应用程序,很多时候都需要获取文本框中的内容做判断,比如判断密码是否正确等等,那么我们怎么获取文本框的内容呢?

  • 1.获取文本框句柄
    使用GetDlgItem()函数,可以获取指定文本框的句柄,MSDN官方文档解释该函数
HWND GetDlgItem([in, optional] HWND hDlg,[in]           int  nIDDlgItem
);

参数解释:

  • hDlg:对话框句柄
  • nIDDlgItem:要检索的控件标识符
    返回值:HWND,指定对话框的窗口句柄

在获取到文本框句柄后,就可以获取文本框内容了:

    1. 获取文本内容:
      使用GetWindowText()函数,MSDN官方文档解释该函数
int GetWindowTextA([in]  HWND  hWnd,[out] LPSTR lpString,[in]  int   nMaxCount
);

参数解释:

  • hWnd:对话框句柄,我们上一个函数的返回值
    lpString:注意这是一个OUT类型的参数,接收文本的缓冲区,在使用函数之前,我们需要申请一块内存,用于接收文本内容
    nMaxCount:复制到缓冲区的最大字符数,包括NULL字符。

三.消息断点(附对话框回调函数的寻找)

我们之前一直是找到回调函数之后,根据消息堆栈对应用程序进行条件断点,但是有很多情况下,应用程序都很复杂,我们很难找到回调函数,那今天我们就来带领大家学习一种新的断点方式:消息断点,我们不需要找到回调函数当应用程序接收到消息后,会自动断点,以便我们的逆向工作。
这里先给大家一个我自己编写的应用程序,我们后续做消息断点就使用这个应用程序,大家注意生成应用程序的时候发布为Release版本,因为大多数Debug版本的程序,寻址方式与Release版本不同(Debug大多使用ebp寻址,而Release版大多使用esp寻址,而我们平时做逆向肯定做的是Release版):

很好用的一个软件(肯定不是恶意软件)

// 破解一切.cpp : 定义应用程序的入口点。
//#include "framework.h"
#include "破解一切.h"
#include "Resource.h"
int x = 0;
int y = 0;
#define MAX_LOADSTRING 100
int style;
// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING]=TEXT("好用的破解软件");                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]=TEXT("main");            // 主窗口类名// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK DiaLogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR    lpCmdLine,_In_ int       nCmdShow)
{style = nCmdShow;// TODO: 在此处放置代码。// 初始化全局字符串MyRegisterClass(hInstance);// 执行应用程序初始化:if (!InitInstance (hInstance, nCmdShow)){return FALSE;}HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY));MSG msg;// 主消息循环:while (GetMessage(&msg, nullptr, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return (int) msg.wParam;
}//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{WNDCLASSEXW wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style          = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc    = WndProc;wcex.cbClsExtra     = 0;wcex.cbWndExtra     = 0;wcex.hInstance      = hInstance;wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MY);wcex.lpszClassName  = szWindowClass;wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));WNDCLASSEXW sc = { 0 };sc.style = CS_HREDRAW | CS_VREDRAW;sc.lpfnWndProc = WndProc;sc.cbClsExtra = 0;sc.cbWndExtra = 0;sc.hInstance = hInstance;sc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));sc.hCursor = LoadCursor(nullptr, IDC_ARROW);sc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);sc.lpszMenuName = MAKEINTRESOURCEW(IDC_MY);sc.lpszClassName = szWindowClass;sc.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));return RegisterClassExW(&wcex);
}//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{hInst = hInstance; // 将实例句柄存储在全局变量中HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,x,y,CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);if (!hWnd){return FALSE;}DialogBox(hInst, (LPCWSTR)IDD_DIALOG1, NULL, DiaLogProc);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{switch (message){case WM_COMMAND:{int wmId = LOWORD(wParam);switch (wmId){case IDM_ABOUT:DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_EXIT:DestroyWindow(hWnd);break;case ID_32774: {int i, j;for (i = 0; i < 100; x += 200) {for (j = 0; j < 30; y += 36) {HWND MyhWnd = CreateWindowW(szWindowClass, (LPCWSTR)TEXT("谁叫你乱点!!!"), WS_OVERLAPPEDWINDOW, x, y,300, 40, nullptr, nullptr, hInst, nullptr);ShowWindow(MyhWnd, style);// Sleep(100);j++;}y = 0;i++;}break;}default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;case WM_PAINT:{PAINTSTRUCT ps;HDC hdc = BeginPaint(hWnd, &ps);// TODO: 在此处添加使用 hdc 的任何绘图代码...EndPaint(hWnd, &ps);}break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{UNREFERENCED_PARAMETER(lParam);switch (message){case WM_INITDIALOG:return (INT_PTR)TRUE;case WM_COMMAND:if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL){EndDialog(hDlg, LOWORD(wParam));return (INT_PTR)TRUE;}break;}return (INT_PTR)FALSE;
}//对话框处理函数
INT_PTR CALLBACK DiaLogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {switch (uMsg) {case WM_COMMAND: {if (wParam == IDR_ACCELERATOR1) {EndDialog(hwndDlg, 100);break;}if (wParam == IDI_SMALL) {int i, j;MessageBox(hwndDlg, TEXT("你确定要退出码?"), NULL, MB_OK);for (i = 0; i < 100; x+=200) {for (j = 0; j < 30; y += 36) {HWND MyhWnd = CreateWindowW(szWindowClass, (LPCWSTR)TEXT("妈的让你退出!!!"), WS_OVERLAPPEDWINDOW, x, y,300,40, nullptr, nullptr, hInst, nullptr);ShowWindow(MyhWnd, style);// Sleep(100);j++;}y = 0;i++;}PostMessage(NULL, WM_QUIT, 0, 0);EndDialog(hwndDlg, 100);break;}}case WM_SYSCOMMAND: {PostQuitMessage(0);}}return FALSE;
}

相信稍微有点基础的都能看出来我编写的这个应用程序不是做什么好事的。
由于我不是做开发的,所以代码写的很烂,但是我们做底层的,了解了基本原理就可以了,这个软件我会在后续做很多改进,大家可以关注我的文章,待程序改进好之后,大家可以去“玩儿玩儿”。

那我们就是用这个应用程序来给大家讲解消息断点:

  • 首先,我们来看看这个应用程序:
    当打开之后,会弹出这样一个对话框,提示输入账号和密码:
    1
  • 当然,你肯定不知道账号和密码,当然我也不知道,因为我还没写账号密码的判定,那肯定会去点“直接破解进去”这个按钮了,当点击这个按钮之后,应用程序主窗口就弹出来了:
    2
    里面有一个释放病毒,实际上不是什么病毒,我们今天不研究这个,这里就不细细讲解了,我们来看一下另一个按钮:取消按钮,这里就是个坑了:
  • 当我们点击取消按钮的时候,会弹出一个提示框:
    3
    这里就是一个坑了,不管你点击哪里,都会让这个恶意代码运行,我们来看看运行之后是什么样的:
    4
    不管点击哪里,都会让这段代码运行,会弹出满屏的窗口,大家也不用担心,应用程序会自动退出。

对话框回调函数

上一篇文章带领大家找过主窗口的回调函数了,相信大家对回调函数一定不陌生了,对话框的回调函数实际上和主窗口的回调函数一样。
我们来看看上文中讲的显示对话框的函数:

DialogBox(hInst, (LPCWSTR)IDD_DIALOG1, NULL, DiaLogProc);

我们看到,第四个参数就是对话框的回调函数。
在调用这个函数的时候,肯定会将参数压栈,那我们直接到堆栈窗口找函数地址,就能很容易地找到回调函数了:
这里实际上OD已经帮我们解析出来了很多东西:
DialogBox
我们能很清晰地看到DialogBox()函数的几个参数,我们直接根据函数地址跟到回调函数就找到了,我们在对话框回调函数中也可以做条件断点

那么我们需要逆向出当点击了取消按钮后底层做了什么工作

  • 我们首先来看一下到底是哪个消息触发了这个消息,我们前面将结果消息类型了,我们也知道是当鼠标左键点击之后触发了消息,那么到底是鼠标左键按下(WM_LBUTTONDOWN),还是鼠标左键弹起(WM_LBUTTONUP)时呢?
  • 这里大家可以去试一下,当鼠标左键按下的时候,代码很明显没有运行,当我们鼠标左键放开的时候,能很直观地看到代码运行了。
  • 经过上面的操作,我们已经知道了我们要做断点的消息是什么了

我们知道了要做断点的条件,根据上一篇文章讲解的,直接做条件断点就可以了:

  • 堆栈窗口:
    堆栈窗口
    我们能看到回调函数地址,跟到反汇编:
    回调函数
    大家可以看到,这个就是对话框回调函数了,我们根据上篇文章的讲解做断点即可。

消息断点讲解

我们在逆向比较简单的应用程序的时候,可以直接找窗口的回调函数,那么当我们要逆向别人的程序的时候,别人发布的应用程序肯定不会像我这个应用程序这样简单,他们的有应用程序很复杂,那么我们该逆向呢?

我们需要逆向出当点击了取消按钮后底层做了什么工作:

  • 我们首先来看一下到底是哪个消息触发了这个消息,我们前面将结果消息类型了,我们也知道是当鼠标左键点击之后触发了消息,那么到底是鼠标左键按下(WM_LBUTTONDOWN),还是鼠标左键弹起(WM_LBUTTONUP)时呢?
  • 这里大家可以去试一下,当鼠标左键按下的时候,代码很明显没有运行,当我们鼠标左键放开的时候,能很直观地看到代码运行了。
  • 经过上面的操作,我们已经知道了我们要做断点的消息是什么了(WM_LBUTTONDOWN),那么我们就来到OllyDbg来做一下消息断点:

1. 找到按钮:

我们可以看到在OD中有这样一行按钮:
OD按钮
我们先让应用程序运行,对话框运行之后,我们点击这里的W按钮,就可以看到很多窗口句柄等信息:
窗口信息
这里有当前应用程序显示出来的所有窗口,我们发现ClassProc已经帮我们找到了定义窗口类的时候写进去的回调函数,我们可以鼠标右键单击我们想要找的窗口,点击跟随ClassProc,就可以找到系统定义的回调函数了。注意这是系统定义的默认回调函数!!!
跟随ClassProc
我们在这里就可以设置消息断点了:
消息断点
当我们设置了消息断点之后,我们发现有两处都做了断点:
两处消息断点
这是因为不管是哪个按钮,都会调用系统定义的同一个回调函数。
我们来点击按钮测试一下:
应用程序断点
这时候我们发现应用程序断在了系统提供的默认的回调函数这里,但是我们想让它停在我们自己写的回调函数,该怎样操作?
你也可以一直单步跟下去,但是这样真的太浪费时间了,而且没有人会这样做。
我们发现,系统提供的回调函数,地址都是7…,说明在应用程序领空,应该是在DLL领空。
这里给大家教一种技巧:大家想想,系统提供的默认回调函数,是不是迟早会调用我们写的那个回调函数?那么我们来到Memory map窗口:
Mmeory map窗口
大家都是学过PE结构的人,相信大家都能理解上述的data,rdata,rsrc几段吧?那么我们在代码断下一个访问断点:
访问断点
这样呢,当访问应用程序的代码段的时候,也就是系统提供的默认回调函数调用我们自己编写的回调函数的时候,就会在我们自己写的那个回调函数上做断点了。
今天的文章就分享到这里,如果大家发现文章中有错误之处或者是个人理解不到位的地方,还请大家指出来,我会非常虚心地学习,希望大家共同进步!!!


http://www.ppmy.cn/news/71045.html

相关文章

Android12之音频类型分组(五十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

Vue-Router(4) 学习之动态路由 addRoute

Vue-Router(4) 学习之动态路由 addRoute 前端动态路由 在前端开发中&#xff0c;动态路由指的是根据用户的操作或应用的状态变化&#xff0c;动态地加载和渲染不同的页面内容&#xff0c;而无需刷新整个页面。这种方式可以提供更流畅和交互性强的用户体验。 Vue Router Vue…

汽车软件需求与用户功能

1、软件需求只包含必要描述性、定义性的信息&#xff0c;而不能包含解释性的内容。 2、每条需求都必须是组成某个功能的最基本单元&#xff0c;不能够再继续分解。 3、每条软件需求都必须具备可测试性&#xff0c;意味着有明确的输入输出。 一份清晰而又准确的需求文档是一个…

关于选择法拍房的五点建议

一边是不断上升的成交量和不断刷新的成交额&#xff0c;另一边是买了房无法入住和面临的一大笔税费&#xff0c;关于“法拍房”&#xff0c;其热度和问题并存交织。然而这一切对于一些有着结婚、孩子入学、养老、改善等购房需求的刚需者来说&#xff0c;丝毫没有影响......因为…

C#串口通信从入门到精通(14)——多个串口接收数据

文章目录 前言1、多串口数据的接收2、源码前言 我们在开发串口通信程序时,有时候会需要连接不止一个串口,这时候该怎么写程序呢?本文就来介绍多个串口数据的接收 1、多串口数据的接收 我们在之前的专栏中介绍了串口数据的发送,当时有提到过,我们是通过创建一个SerialPo…

C++: 通过CMake配置AddressSanitizer并执行内存泄漏和越界检查

文章目录 1. 目的2. 区分编译和链接选项3. 在CMake中全局开启ASan1. 目的 在 C/C++ 工程中, 得益于 Google 工程师开发的 Address Sanitizer 这一神器, 可以快速、准确的发现不规范的内存使用, 包括而不限于: 内存泄漏检查, 例如忘记释放, 或原本持有内存的的指针被赋予…

MQTT 5协议中的基础更改(二)

上期文章中给大家介绍了MQTT规范版本5中基础更改的信息和CONNACK返回码&#xff0c;本篇文章我们继续介绍MQTT5协议中的基础更改中其他新功能的细节描述。 01 干净启动 MQTT 3.1.1的其中一个主流功能是MQTT客户端使用清除会话&#xff08;cleanSession&#xff09;&#xff0…

提前熟知领英被限制被封因素,避免踩坑

领英在什么情况下容易被封 01.同一个人注册使用多个领英帐号。 02.多个人共同使用同一个领英帐号。 03.虚假资料注册领英账号&#xff0c;常见于注册领英账号的时候初始姓名随便填写或胡编乱造&#xff0c;注册时使用了网络虚拟的手机号码或邮箱等。 04.领英帐号的个人档案资料…