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

news2024/9/20 0:57:31

之前在学习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.coloradmin.cn/o/544322.html

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

相关文章

inquirer 用户与命令行交互工具

学习脚手架的时候接触到inquirer &#xff0c;用来创建用户与命令行交互工具&#xff0c;使用方式如下&#xff1a; 1、安装 npm i -S inquirer 2、所有type使用范例 var inquirer require(inquirer);const questions [{type: confirm,name: order,message: 您好&#xf…

如何用 Midjourney 绘制你自己的拟人头像?

在元宇宙时代&#xff0c;许多人都使用各种AI应用程序来生成他们自己的头像&#xff0c;其中Midjourney是一个非常流行的选择。然而&#xff0c;成功的先行者可能不愿意透露具体的prompt&#xff08;提示语&#xff09;&#xff0c;因为他们可能担心自己的创意被其他人抄袭。此…

Talk | 北航助理教授孙庆赟 :图学习里的拓扑不均衡问题初探

本期为TechBeat人工智能社区第497期线上Talk&#xff01; 北京时间5月17日(周三)20:00&#xff0c;北京航空航天大学计算机学院 助理教授—孙庆赟的Talk将准时在TechBeat人工智能社区开播&#xff01; 她与大家分享的主题是: “图学习里的拓扑不均衡问题初探 ”&#xff0c;届…

Stream流 - 两个list集合对象属性的合并、对象属性值运算

两个list集合对象属性的合并 合并两个 list&#xff1c;map&#xff1e;, 并将 userId 相同的所有属性合并到一个 map 中 list1中对象的属性&#xff1a;userId、userName list2中对象的属性&#xff1a;userId、gender、age 最总集合中对象的属性&#xff1a;userId、user…

C# | [二进制字符串] 与 [字节数组] 互相转换,一行代码就搞定! - CodePlus系列

C#二进制字符串与字节数组互相转换 文章目录 C#二进制字符串与字节数组互相转换前言示例代码实现思路扩展方法说明引用CodePlus库结束语 前言 开发中有时需要将二进制数据转换为字符串或相反。虽然.NET提供了一些用于二进制数据操作的类库&#xff0c;但是它们的使用有时候会比…

Elasticsearch 安装 X-pack

X-Pack是Elastic Stack扩展功能&#xff0c;提供安全性&#xff0c;警报&#xff0c;监视&#xff0c;报告&#xff0c;机器学习和许多其他功能。 ES7.0之后&#xff0c;默认情况下&#xff0c;当安装Elasticsearch时&#xff0c;会安装X-Pack&#xff0c;无需单独再安装。 1.…

编程测试被候选人吐槽了?原因可能是这些

一位前Facebook Tech Lead曾经说过&#xff1a;面试就好像是在第一次约会的时候&#xff0c;就决定是不是要跟对方结婚。 这虽然是个无奈的笑话&#xff0c;但也真实地反映了技术面试中的一个难题&#xff1a;面试官需要在相当有限的时间里&#xff0c;准确地判断候选人的技术…

什么是自然语言处理的机器翻译?

机器翻译&#xff08;Machine Translation&#xff0c;MT&#xff09;是一种自然语言处理技术&#xff0c;旨在将一种语言的文本自动翻译成另一种语言。机器翻译是自然语言处理领域的重要应用之一&#xff0c;它可以帮助人们在跨语言交流、文档翻译和信息检索等方面更加便捷和高…

Maven聚合和继承,使用IDEA构建聚合与继承工程

文章目录 1 聚合步骤1:创建一个空的maven项目步骤2:将项目的打包方式改为pom步骤3:pom.xml添加所要管理的项目步骤4:使用聚合统一管理项目 2 继承步骤1:创建一个空的Maven项目并将其打包方式设置为pom步骤2:在子项目中设置其父工程步骤3:优化子项目共有依赖导入问题步骤4:优化子…

企业数据治理内训的好处这么多,赶紧安排!

数据治理是确保数据的质量和完整性的一种方法&#xff0c;这对企业非常重要&#xff0c;因为它们需要准确的数据来做出正确的决策。 学习有效地管理和维护数据 通过内部培训&#xff0c;员工可以学习如何有效地管理和维护数据&#xff0c;从而提高数据质量和可靠性。 帮助企业…

SpringAop的实践应用

使用AOP来对前端传来的对象参数进行 BaseDto中属性的填充 这样就不用每次都去UserThreadLocal中拿了再又往BaseDto中放了 Aspect Component public class UserAspect {Pointcut("annotation(org.springframework.web.bind.annotation.RequestMapping) || annotation(o…

测试将被开发、运维替代?我后悔了!?

记得在求职的时候&#xff0c;面试官经常问我&#xff1a;“为什么要选择软件测试工作?”而我也会经常说一堆自己有的没的优势去应付。 工作这么久了&#xff0c;也不再浮躁&#xff0c;静下心来回忆当初选择软件测试工作的历程&#xff0c;也是对自己职业生涯的一次回顾。 …

每日一题162——重塑矩阵

在 MATLAB 中&#xff0c;有一个非常有用的函数 reshape &#xff0c;它可以将一个 m x n 矩阵重塑为另一个大小不同&#xff08;r x c&#xff09;的新矩阵&#xff0c;但保留其原始数据。 给你一个由二维数组 mat 表示的 m x n 矩阵&#xff0c;以及两个正整数 r 和 c &…

Conmi的正确答案——Cordova安装并编译Android应用

系统&#xff1a;debian 11 Cordova版本&#xff1a;11.1.0 Cordova的Android平台&#xff1a;10.1.2 当前安卓最新稳定API&#xff1a;33&#xff08;Android版本列表&#xff09; 1、安装npm&#xff08;cordova是基于nodejs开发的&#xff09; apt install npm -y2、使用n…

OpenCV中的图像处理3.10(八)直方图-寻找、绘制、分析(掩膜)与均衡化

目录 3.10 OpenCV中的直方图3.10.1 直方图--1&#xff1a;寻找、绘制、分析目标理论寻找直方图绘制直方图掩膜的应用其他资源 3.10.2 直方图--2&#xff1a;直方图均衡化目标理论OpenCV中的直方图均衡化CLAHE&#xff08;对比度有限的自适应直方图均衡&#xff09;其他资源 翻译…

【笔试强训选择题】Day16.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…

Linux Qt6 安装教程及错误解决

在Linux环境&#xff0c;通常为Ubuntu&#xff0c;安装Qt开发环境&#xff0c;与Windows安装相比&#xff0c;还是稍显繁琐&#xff0c;需要多做几个步骤。 这里的Ubuntu版本采用的是ubuntu-22.04.2-desktop-amd64&#xff0c;所以&#xff0c;比旧版本会少很多坑&#xff0c;…

今天面了个字节跳动拿35K出来的测试,真是砂纸擦屁股,给我露了一手啊

今年的春招已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山之石…

现在的00后,实在是太卷了,我们这些老油条都想辞职了......

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快要超过我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家…

MathType7公式编辑器新版详细介绍下载安装

由于CSDN这边不能发相关的教程等&#xff0c;若仅用于学习体验&#xff0c;请移步&#xff0c;有能力请支持正版。 wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;mathtype 免费获取MathType-win-zh.exe安装包 它是一款用于数学公式编辑和排版的软件。MathType可以在Mi…