【第五节】Win32汇编程序设计

news2025/2/1 16:01:27

目录

一、汇编的第一个“helloworld”

二、汇编中的标号

三、@@的使用

四、数据定义

五、全局变量

六、局部变量

七、结构体

八、结构体的访问

九、获取变量地址

十、函数

十一、分支与循环

十二、内联汇编

十三、裸函数的使用


一、汇编的第一个“helloworld”

.386  ; 指定本工程应用的指令集为 80386
.model flat, stdcall  ; 模式定义为平坦内存模式,调用模式为 stdcall
option casemap:none  ; 选项设定为对大小写敏感

include windows.inc  ; 包含 Windows 头文件
include user32.inc   ; 包含 user32 头文件
include kernel32.inc  ; 包含 kernel32 头文件

includelib user32.lib  ; 包含 user32 库文件
includelib kernel32.lib  ; 包含 kernel32 库文件

.data  ; 数据段
szCaption db 'Win32汇编', 0  ; 定义消息框标题字符串,以 0 结尾
szText db 'Hello World!', 0  ; 定义消息框内容字符串,以 0 结尾

.code  ; 代码段
start:
    invoke MessageBox, NULL, offset szText, offset szCaption, MB_OK  ; 调用 MessageBox 函数显示消息框
    invoke ExitProcess, NULL  ; 调用 ExitProcess 函数退出进程
end start

功能说明(代码注释用;)

  • invoke MessageBox, NULL, offset szText, offset szCaption, MB_OK

    • 调用 MessageBox 函数显示一个消息框。

    • NULL 表示消息框没有所有者窗口。

    • offset szTextoffset szCaption 分别是消息框的内容和标题。

    • MB_OK 表示消息框只有一个 "OK" 按钮。

  • invoke ExitProcess, NULL

    • 调用 ExitProcess 函数退出当前进程。

    • NULL 表示没有返回值。

这段代码的主要功能是显示一个带有 "Hello World!" 内容和 "Win32汇编" 标题的消息框,并在用户点击 "OK" 按钮后退出程序。

 

二、汇编中的标号

在汇编语言中,标号(Label)是一个非常重要的概念。标号本质上是一个符号,用于表示代码或数据的位置。通过使用标号,程序员可以更方便地引用和跳转到特定的代码段或数据段。

汇编语言中的标号规则:
1)允许使用字母、数字、下划线以及特殊符号@、$和?,但首字符不得为数字。
2)其长度限制在240个字符以内。
3)禁止使用指令名或其他保留关键字。
4)在同一作用域内,标号必须是唯一的。

### 标号的类型

1. **代码标号**:用于标记代码的位置,通常用于跳转指令(如 `JMP`、`CALL` 等)。
2. **数据标号**:用于标记数据的位置,通常用于数据定义指令(如 `DB`、`DW`、`DD` 等)。

### 代码标号

代码标号通常位于指令的前面,用于标记该指令的位置。例如:

start:
    mov ax, 42
    jmp start

在这个例子中,`start` 是一个代码标号,标记了 `mov ax, 42` 这条指令的位置。`jmp start` 指令会无条件跳转到 `start` 标号所在的位置。

### 数据标号

数据标号通常位于数据定义指令的前面,用于标记数据的位置。例如:

message db 'Hello, World!', 0

在这个例子中,`message` 是一个数据标号,标记了字符串 `'Hello, World!'` 的起始位置。程序可以通过 `message` 标号来访问这个字符串。

### 标号的用途

1. **跳转和调用**:通过标号,程序可以实现条件和无条件跳转,以及函数调用。
2. **数据访问**:通过标号,程序可以方便地访问数据段中的数据。
3. **模块化编程**:标号可以帮助组织代码,使其更具模块化和可读性。

### 示例

以下是一个简单的汇编程序,展示了代码标号和数据标号的使用:

.data
    message db 'Hello, World!', 0

.code
start:
    mov ax, @data
    mov ds, ax

    mov ah, 09h
    lea dx, message
    int 21h

    mov ax, 4C00h
    int 21h
end start

在这个例子中:
- `message` 是一个数据标号,标记了字符串 `'Hello, World!'` 的起始位置。
- `start` 是一个代码标号,标记了程序的入口点。

通过这些标号,程序可以方便地访问数据和控制程序的执行流程。

三、@@的使用

在MASM中,@@提供了便捷的本地标号功能。
例如:

xor eax, eax
test eax, eax
je @F  ; 跳转到此条指令后的第一个@@的地址
mov ebx, 99h
@@     ; 此条指令前的第一个@@的地址
loop @B  ; 建议:在同一@@作用域下,@@、@F与@B之间不要间隔太远

通过使用@@、@F和@B,程序员可以更灵活地控制代码的跳转,尤其是在循环和条件判断中。

四、数据定义

常量定义:

const
IDD_DIALOG1 equ 101  ; 定义一个值为101的常量

数据定义:

data db "%02x", 0  ; 定义一个字符串FormatStr

未初始化数据定义:

hInstance dd ?  ; 定义一个未初始化的变量

        在汇编语言中,数据定义用于在数据段中分配内存并初始化数据。常量定义用于定义不可更改的值,数据定义用于定义已初始化的数据,而未初始化数据定义用于定义未初始化的变量。这些定义有助于组织和管理程序中的数据。

五、全局变量

格式:

变量名 类型 初始值1, 初始值2, ...
变量名 类型 重复数量 dup(初始值1, ...)

示例:

.data
wHour dw ?  ; 2字节,未初始化
wMinute dw 10  ; 2字节,初始化为10
hWnd dd ?  ; 4字节,未初始化
Buffer dw 100 dup(1, 2)  ; 2 * 100字节,初始化为1和2的重复序列
szBuffer byte 1024 dup(?)  ; 1024字节,未初始化
szText db 'Hello, world!', 0  ; 12字节,字符串
szHello db 'Hello,', 0dh, 0ah, 'world!', 0  ; 包含换行符的字符串

        在汇编语言中,全局变量定义用于在数据段中分配内存并初始化数据。变量名后面跟着类型和初始值或重复数量及初始值。这些定义有助于组织和管理程序中的数据。

数据类型及表示方式:

6342c19519fc4db19c8824c731563be9.jpeg

六、局部变量

        声明局部变量的关键字为 `LOCAL`,它会在栈中开辟出相应大小的空间用以保存这个局部变量。


例如:

LOCAL hDlgEdt : HWND  ; 4字节空间
LOCAL dwNameSize : DWORD  ; 4字节空间
LOCAL szName[128] : BYTE  ; 128字节空间

        在汇编语言中,局部变量通过 `LOCAL` 关键字声明,并在栈中分配内存。每个局部变量后面跟着类型和大小。这些定义有助于在函数或代码块中管理临时数据。

 

七、结构体

定义结构体:

WNDCLASS struct
    Style       DWORD ?
    LpfnWndProc DWORD ?
    cbClsExtra  DWORD ?
WNDCLASS ends

声明结构体:

data ?
stWndClass WNDCLASS <>  ; 声明结构体
data
stWndClass WNDCLASS <1, 1, 1>  ; 声明结构体并赋值

        在汇编语言中,结构体用于定义复杂的数据结构。通过 `struct` 和 `ends` 关键字定义结构体,并在数据段中声明结构体变量。结构体变量可以不初始化,也可以在声明时进行初始化。这些定义有助于组织和管理复杂的数据结构。

八、结构体的访问

在MASM中访问结构体有三种方法:


1. **直接访问**:

   mov eax, stWndClass.lpfnWndProc

2. **利用寄存器访问**:

   mov esi, offset stWndClass
   mov eax, [esi + WNDCLASS.lpfnWndProc]

 注意:第二句是 `[esi + WNDCLASS.lpfnWndProc]` 而不是 `[esi + stWndClass.lpfnWndProc]`。


3. **使用 `assume` 伪指令预先定义寄存器访问**:

   mov esi, offset stWndClass
   assume esi: ptr WNDCLASS
   mov eax, [esi].lpfnWndProc
   assume esi: nothing

        在汇编语言中,访问结构体成员可以通过直接访问、利用寄存器访问或使用 `assume` 伪指令预先定义寄存器访问。这些方法提供了灵活性和便利性,使得程序员可以根据具体需求选择最合适的方式来访问结构体成员。

九、获取变量地址

        对于全局变量,它的地址在编译时已经由编译器确定,其用法如下:

mov 寄存器, offset 变量名

        如果要在 `invoke` 伪指令的参数中用到一个局部变量的地址,该怎么办呢?参数中是不可能写入 `lea` 指令的,用 `offset` 也是不对的。MASM 对此有一个专用的伪操作符 `addr`,其格式为:

addr 局部变量名或全局变量名

注意:

mov eax, addr 局部变量名  ; 错误用法

假设在一个子程序中有如下 `invoke` 指令:

invoke Test, eax, addr szHello

        其中 `Test` 是一个需要两个参数的子程序,`szHello` 是一个局部变量,会发生什么结果呢?编译器会把 `invoke` 伪指令和 `addr` 翻译成下面这个模样:

lea eax, [ebp-4]
push eax  ; 参数2: addr szHello
push eax  ; 参数1: eax
call Test

        我们可以发现,在 `push` 第一个参数 `eax` 之前,`eax` 的值已经被 `lea eax, [ebp-4]` 指令覆盖了!
        所以,当在 `invoke` 中使用 `addr` 伪操作符时,注意在它的前面不能用 `eax`,否则 `eax` 的值会被覆盖掉。当然,`eax` 在 `addr` 的后面的参数中用是可以的。幸亏 MASM 编译器对这种情况有如下错误提示:

error A2133: register value overwritten by INVOKE

        这个错误提示提醒我们在使用 `addr` 伪操作符时要注意寄存器的使用顺序,避免不必要的问题。

十、函数

函数声明:

DlgProc proto :HWND, :UINT, :WPARAM, :LPARAM

函数定义:

FunName proc hDlg:HWND, dwVar:DWORD
    ret
FunName endp

        在汇编语言中,函数通过 `proto` 伪指令进行声明,并通过 `proc` 和 `endp` 关键字进行定义。函数声明指定了函数的名称和参数类型,而函数定义则包含了函数的具体实现。这些定义有助于组织和管理程序中的函数调用。

 

十一、分支与循环

        在汇编语言中,分支和循环结构可以通过 `.if`、`.while`、`.break` 和 `.continue` 等伪指令来实现。

1. **条件分支**:

   .if 表达式1
       表达式1为“真”要执行的指令
   .endif

        当 `表达式1` 为真时,执行指定的指令。

2. **循环**:

   .while 条件测试表达式
       指令
       [.break [.if 退出条件]]
       [.continue]
   .endw

        当 `条件测试表达式` 为真时,执行循环体内的指令。可以使用 `.break` 和 `.if` 组合来提前退出循环,或者使用 `.continue` 跳过当前循环的剩余部分,直接进行下一次循环。

        这些伪指令提供了类似于高级语言中的控制结构,使得汇编代码更具可读性和可维护性。

十二、内联汇编

        在C/C++中,可以使用内联汇编来嵌入汇编代码。内联汇编有两种形式:块内联汇编和行内联汇编,二者可以交叉使用。

示例:

#define EXAMPLE_CODE asm
/* 内联汇编宏定义示例 */
EXAMPLE_CODE {
    pushad
    popad
}

        在定义一个具有多行汇编指令的宏时,一定要采取二者交叉使用的模式,否则会引起编译错误。块内联汇编使用 `asm { ... }` 的形式,而行内联汇编使用 `asm ...` 的形式。通过合理使用这两种形式,可以更灵活地在C/C++代码中嵌入汇编指令,从而实现更高效的代码执行。

十三、裸函数的使用

        裸函数是一个没有任何可执行代码的空函数,它在内存中仅仅是一条地址信息。裸函数使用关键字 `__declspec(naked)` 定义,一个可运行的最简单的裸函数如下所示:

void __declspec(naked) TestFun()
{
    __asm ret
}

        裸函数的特点是编译器不会为函数生成任何 prologue 或 epilogue 代码,这意味着函数体内必须手动编写所有的汇编代码,包括函数返回指令。裸函数通常用于需要精细控制函数执行流程的场合,例如编写中断处理程序或性能敏感的代码。

 

 

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

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

相关文章

从桌面到云端,2024年智能录屏解决方案全攻略

从教学演示到游戏直播&#xff0c;从软件教程到会议记录&#xff0c;录屏软件已经逐渐成为不可或缺的工具。那么面对这众多录屏软件我们要怎么选择呢&#xff1f;有没有和win10录屏快捷键一样可以快捷操控的工具呢&#xff1f;这次我们一起来探讨吧。 1.福昕录屏大师 链接&am…

Windows下线程的创建与使用(win32-API)

一、前言 线程是比进程更轻量级的执行单元&#xff0c;允许在一个进程中并发执行多个控制流。每一个线程都有自己的程序计数器、寄存器集和栈空间&#xff0c;但它们共享所属进程的全局数据和资源。这种共享内存模型使线程间的通信比进程间通信更为高效&#xff0c;同时也带来…

2-71 基于matlab的小波分析在心电信号去噪中的应用

基于matlab的小波分析在心电信号去噪中的应用&#xff0c;主要针对心电信号中的肌电干扰/基线漂移/工频干扰进行的算法研究&#xff0c;输出了三类去噪结果。程序已调通&#xff0c;可直接运行。 2-71 基线漂移去噪 工频干扰去噪 - 小红书 (xiaohongshu.com)

android apk 加固后的地图加载异常及重新签名

1.首先根据需求将打包生成后的APK进行加固&#xff0c;可以使用360、阿里、腾讯加固等。 2.加固后的APK无法直接安装&#xff0c;需要重新进行签名。 3.首先找到sdk的位置&#xff0c;进入build-tools目录。 4.根据gradle文件选择版本目录。 5.将加固后的APK放至该目录下。在…

QT-小游戏翻金币

QT-小游戏翻金币 一、演示效果二、使用步骤三、下载链接 一、演示效果 二、使用步骤 #include "chooselevelscene.h" #include <QMenuBar> #include <QPainter> #include "mypushbutton.h" #include <QDebug> #include <QTimer> …

Ajax-3

一.图片上传 1.获取图片文件对象 2.使用FormData携带图片文件 const fd new FormData() fd.append(参数名, 值) 3.提交表单数据到服务器&#xff0c;使用图片url网址 二.AJAX原理—XMLHttpRequest 定义&#xff1a;XMLHttpReques&#xff08;XHR&#xff09;对象用于与服务器…

P38-数据存储1

百度2015年系统工程师笔试题 编程题 编程题 编程题 编程题

20240820飞凌的OK3588-C的核心板在Linux R4下使用poweroff关机

20240820飞凌的OK3588-C的核心板在Linux R4下使用poweroff关机 2024/8/20 14:03 经过测试&#xff0c;poweroff有效&#xff0c;关机之后&#xff0c;12V/0.024A12*0.0240.288W shutdown无效。 reboot -p无效。 rootok3588:/# rootok3588:/# shutdown -h now sh: shutdown: c…

Maven-06.依赖管理-依赖传递

一.依赖传递 什么是依赖传递&#xff1a;projectA依赖于JAR包和projectB&#xff0c;而JAR包又依赖于黄色的JAR包。而projectB依赖于projectC和其他JAR包。因此projectA依赖于projectB,projectC和图中的所有JAR包。这就是依赖的传递性。其中蓝绿色部分成为直接依赖。在当前项目…

ant design pro 的环境变量的使用

如上图所示&#xff0c;定义好环境变量后&#xff0c;整个应用的名字就发生的变化。 规则 环境变量的名称要以 UMI_APP 开头 如何使用这个环境变量 直接用 process.env.UMI_APP_APP_NAME 来调它。 ant design pro 如何去保存颜色ant design pro v6 如何做好角色管理ant desi…

【2025校招】4399 NLP算法工程师笔试题

目录 1. 第一题2. 第二题3. 第三题 ⏰ 时间&#xff1a;2024/08/19 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;2h 本试卷分为单选&#xff0c;自我评价题&#xff0c;编程题 单选和自我评价这里不再介绍&#xff0c;4399的编程题一如既往地抽象&#xff…

JavaScript(25)——BOM、延迟函数、JS执行机制

BOM BOM是浏览器对象模型 window对象是一个全局对象&#xff0c;也就是JavaScript中的顶级对象所有通过var定义的全局作用域中的变量&#xff0c;函数都会变成window对象的属性和方法window对象下的属性和方法调用的时候可以省略window 延时函数 let a setTimeout(回调函数…

Python(Falsk) + React Golang(Gin) + Vue 全栈开发的最佳实践

前面分别讲了 Python(Falsk) 、 React 、 Golang(Gin) 、 Vue(Element)&#xff0c;现在整体的给大家汇报一下&#xff0c;这个是简单搭建的demo&#xff0c;后面的添砖加瓦需要自己动手咯&#xff0c;有不明白的可以参考一下小编前面的文章&#xff0c;也许会给大家有答疑解惑…

QTday04

1.思维导图 2. . #include "widget.h" #include "ui_widget.h" #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), speecher(new QTextToSpeech(this)) //给语音播报者实例化空间{ui->setupUi(th…

ansible搭建+ansible常用模块

ansible搭建 管理机安装ansible,被管理节点必须打开ssh服务 1.管理机安装ansible yum -y install ansible 2.查看版本 ansible --version ansible 2.9.27 3.查找配置文件 find /etc/ -name "*ansible*" /etc/ansible /etc/ansible/ansible.cfg 4.三台被管理机…

轻松捕捉屏幕精彩,2024年录屏神器大盘点

随着技术的不断进步和用户需求的日益多样化&#xff0c;市场上的录屏软件更是层出不穷&#xff0c;各有千秋。今天&#xff0c;就让我们一同盘点那些和win10录屏快捷键一样可以快捷控制的专业录屏软件&#xff0c;探索它们如何助力我们更加高效地捕捉屏幕上的每一个精彩瞬间。 …

数据结构-链表-第二天

结合leetcode学习c 链表比数组更易增加和删除数据&#xff0c;但访问速度更慢 定义 链表&#xff08;linked list&#xff09;是一种线性数据结构&#xff0c;其中的每个元素都是一个节点对象&#xff0c;各个节点通过“引用”相连接。 引用记录了下一个节点的内存地址&#…

「字符串」前缀函数|KMP匹配:规范化next数组 / LeetCode 28(C++)

概述 为什么大家总觉得KMP难&#xff1f;难的根本就不是这个算法本身。 在互联网上你可以见到八十种KMP算法的next数组定义和模式串回滚策略&#xff0c;把一切都懂得特别混乱。很多时候初学者的难点根本不在于这个算法本身&#xff0c;而是它令人痛苦的百花齐放的定义。 有…

[C++] map、set的 红黑树 封装(一)

标题&#xff1a;[C] map、set的 红黑树 封装 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、红黑树与AVL树的比较&#xff08;为什么容器选择红黑树&#xff09; 二、map、set的封装 1.模板参数 2.红黑树迭代器设计 正文开始&#xff1a; 一、红黑树与AV…

RK3588J正式发布Ubuntu桌面系统,丝滑又便捷!

本文主要介绍瑞芯微RK3588J的Ubuntu系统桌面演示&#xff0c;开发环境如下&#xff1a; U-Boot&#xff1a;U-Boot-2017.09 Kernel&#xff1a;Linux-5.10.160 Ubuntu&#xff1a;Ubuntu20.04.6 LinuxSDK&#xff1a; rk3588-linux5.10-sdk-[版本号] &#xff08;基于rk3…