zoukankan      html  css  js  c++  java
  • Win32汇编过程与宏调用

    汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言.在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地址符号(Symbol)或标号(Label)代替指令或操作数的地址.在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令,普遍地说,特定的汇编语言和特定的机器语言指令集是相互对应的,不同平台之间不可直接移植.

    堆栈操作指令

    在计算机领域,堆栈是一个不容忽视的概念,堆栈是一种后进先出(LIFO,Last-In,First-Out)的数据结构,这是因为最后压入堆栈的值总是最先被取出,而新数值在执行PUSH压栈时总是被加到堆栈的最顶端,数据也总是从堆栈的最顶端被取出,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场.

    当程序运行时,栈是由CPU直接管理线性内存数组,它使用两个寄存器(SS和ESP)来保存堆栈的状态.在保护模式下,SS寄存器存放段选择符(Segment Selector)运行在保护模式下的程序不能对其进行修改,而ESP寄存器的值通常是指向特定位置的一个32位偏移值,我们很少需要直接操作ESP寄存器,相反的ESP寄存器总是由CALL,RET,PUSH,POP等这类指令间接性的修改.

    接着来简单介绍下关于堆栈操作的两个寄存器,CPU系统提供了两个特殊的寄存器用于标识位于系统栈顶端的栈帧.
    ESP 栈指针寄存器: 栈指针寄存器,其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶.
    EBP 基址指针寄存器: 基址指针寄存器,其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部.

    ◆堆栈参数传递◆

    在通常情况下ESP是可变的,随着栈的生产而逐渐变小,而EBP寄存器是固定的,只有当函数的调用后,发生入栈操作而改变.

    1.在32位系统中,执行PUSH压栈时,堆栈指针自动减4,再将压栈的值复制到堆栈指针所指向的内存地址.
    2.在32位系统中,执行POP出栈时,从栈顶移走一个值并将其复制给内存或寄存器,然后再将堆栈指针自动加4.
    3.在32位系统中,执行CALL调用时,CPU会用堆栈保存当前被调用过程的返回地址,直到遇到RET指令再将其弹出.

    PUSH/POP指令: 在32位环境下,分别将数组中的元素100h-300h压入堆栈,并且通过POP将元素反弹出来.

    .data
    	Array DWORD 100h,200h,300h,400h
    .code
    	main PROC
    		xor eax,eax
    		push eax                      ; push 0
    		push DWORD PTR [Array]        ; push 100
    		push DWORD PTR [Array+4]      ; push 200
    		push DWORD PTR [Array+8]      ; push 300
    		pop eax                       ; pop 300
    		pop eax                       ; pop 200
    		pop eax                       ; pop 100
    		pop eax                       ; pop 0
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    PUSHFD/POPFD指令: PUSHFD在堆栈上压入EFLAGS寄存器的值,POPFD将堆栈的值弹出并送至EFLAGS寄存器.

    .data
    	SaveFlage DWORD ?
    .code
    	main PROC
    		pushfd            ; 标识入栈
    		pop SaveFlage     ; 弹出并保存到内存
    
    		push SaveFlage    ; 从内存取出,并入栈
    		popfd             ; 恢复到EFLAGS寄存器中
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    PUSHAD/POPAD指令: 将通用寄存器按照EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI的顺序压栈保存.

    .code
    	main PROC
    		pushad
    		mov eax,1000
    		mov ebx,2000
    		mov ecx,3000
    		mov edx,4000
    		popad
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    ◆声明局部变量◆

    高级语言程序中,在单个过程中创建使用和销毁的变量我们称它为局部变量(local variable),局部变量是在程序运行时,由系统动态的在栈上开辟的,在内存中通常在基址指针(EBP)之下,尽管在汇编时不能给定默认值,但可以在运行时初始化,如下一段伪代码:

    void MySub()
    {
    	int var1 = 10;
    	int var2 = 20;
    }
    

    上面的一段代码经过C编译器转换后,会变成如下的样子,其中EBP-4必须是4的倍数,因为默认就是4字节存储.

    MySub PROC
    	push ebp                  ; 将EBP存储在栈中
    	mov ebp,esp               ; 堆栈框架的基址
    	sub esp,8                 ; 创建局部变量空间
    
    	mov DWORD PTR [ebp-4],10  ; var1 = 10
    	mov DWORD PTR [ebp-8],20  ; var2 = 20
    
    	mov esp,ebp               ; 从堆栈上删除局部变量
    	pop ebp                   ; 恢复EBP指针
    	ret 8                     ; 返回,清理堆栈
    MySub ENDP
    

    如果去掉了上面的mov esp,ebp,那么当执行pop ebp时将会得到EBP等于10,执行RET指令会导致控制转移到内存地址10处执行,从而程序会崩溃.

    为了使代码更加的容易阅读,可以在上面的代码的基础上给每个变量的引用地址都定义一个符号并在代码中使用这些符号来完成编写.

    var1_local EQU DWORD PTR [ebp-4]
    var2_local EQU DWORD PTR [ebp-8]
    
    MySub PROC
    	push ebp
    	mov ebp,esp
    	sub esp,8
    	mov var1_local,10
    	mov var2_local,20
    	mov esp,ebp
    	pop ebp
    	ret 8
    MySub ENDP
    

    ◆ENTER/LEAVE 伪指令◆

    ENTRE指令自动为被调用过程创建堆栈框架,它为局部变量保留堆栈空间并在堆栈上保存EBP,该指令执行后会执行以下动作.

    1.在堆栈上压入EBP(push ebp)
    2.把EBP设为堆栈框架的基指针(mov ebp,esp)
    3.为局部变量保留适当的空间(sub esp,numbytes)

    ENTER指令有两个参数,第一个操作数是一个常量,用于指定要为局部变量保留多少堆栈空间(numbytes),第二个参数指定过程的嵌套层数,这两个操作数都是立即数,numbytes总是向上取整为4的倍数,以使ESP按照双字边界地址对其.

    比如以下代码,使用ENTER为局部变量保存8字节的堆栈空间:

    MySub PROC
    	enter 8,0
    MySub ENDP
    

    经过编译器转换后,会首先转换为以下的样子:

    MySub PROC
    	push ebp
    	mov ebp,esp
    	sub esp,8
    MySub ENDP
    

    上面的代码只有开头没有结尾,如果要使用ENTER指令分配空间的话,则必须在结尾加上LEAVE指令,这样程序才完整.

    MySub PROC
    	enter 8,0
    ....
    	leave
    	ret
    MySub ENDP
    

    下面代码和上面代码作用是相同的,它首先为局部变量保留8字节的堆栈空间然后丢弃.

    MySub PROC
    	push ebp
    	mov ebp,esp
    	sub esp,8
    ....
    	mov esp,ebp
    	pop ebp
    	ret
    MySub ENDP
    

    ◆USES/LOCAL 伪指令◆

    USES操作符: 该操作符用于指定需要压栈的寄存器,其会自动生成压栈出栈代码无需手动添加.

    .code
    	main PROC
    		mov eax,1
    		mov ebx,2
    		mov ecx,3
    		call mycall
    		push 0
    		call ExitProcess
    	main ENDP
    
    	mycall PROC USES eax ebx ecx     ; 生成压栈代码,自动压eax,ebx,ecx
    		xor eax,eax              ; 压栈的寄存器可以随意修改
    		xor ebx,ebx              ; 过程结束后会自动恢复这些寄存器
    		ret
    	mycall ENDP
    END main
    

    LOCAL操作符: 在过程内声明一个或多个命名局部变量,并赋予相应的尺寸属性,该语句必须紧跟PROC指令后面.

    .code
    	main PROC
    		LOCAL var1:WORD
    		LOCAL var2:DWORD,var3:BYTE
    
    		mov DWORD PTR [var1],1024
    		mov eax,DWORD PTR [var1]
    		mov [var2],1024            ; DWORD
    		mov eax,[var2]
    		mov [var3],10              ; BYTE
    		mov al,[var3]
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    局部变量:

    .code
    	lyshark PROC var1:WORD,var2:DWORD
    		LOCAL @loca1:BYTE,@loca2:DWORD
    		LOCAL @local_byte[100]:BYTE
    		
    		mov ax,var1
    		mov ebx,@loca2
    		
    		lea ecx,@local_byte
    		mov @local_byte[0],0
    		mov @local_byte[1],1
    		mov @local_byte[2],2
    		mov @local_byte[3],3
    	lyshark ENDP
    
    	main PROC
    		invoke lyshark,100,10000
    		ret
    	main ENDP
    END main
    

    LOCAL(申请数组):

    .code
    	main PROC
    		LOCAL var[3]:DWORD
    		
    		mov var[0],100
    		mov var[1],200
    		
    		mov eax,var[0]
    		mov ebx,var[1]
    	main ENDP
    END main
    
    .code
    	main PROC
    		LOCAL ArrayDW[10]:DWORD
    		LOCAL ArrayB[10]:BYTE
    
    		lea eax,[ArrayDW]
    		mov [ArrayDW],10
    		mov [ArrayDW + 4],20
    		mov [ArrayDW + 8],30
    	main ENDP
    END main
    

    ## 过程调用指令

    CALL指令指示处理器在新的内存地址执行指令,当用户调用CALL指令时,该指令会首先将CALL指令的下一条指令的内存地址压入堆栈保存,然后将EIP寄存器修改为CALL指令的调用处,等调用结束后返回从堆栈弹出CALL的下一条指令地址.

    1.当遇到CALL指令时,程序会经过计算得到CALL指令的下一条指令的地址,并将其压入堆栈.
    2.接着会将EIP寄存器的地址指向被调用过程的地址,被调用过程被执行.
    3.最后过程内部通过RET指令返回,将从堆栈中弹出EIP的地址,程序继续向下执行.
    4.CALL相当于push+jmp,RET相当于pop+jmp.

    普通参数传递:

    .code
    	sum PROC var1:DWORD,var2:DWORD,var3:DWORD
    		mov eax,var1
    		mov ebx,var2
    		mov ecx,var3
    		ret
    	sum ENDP
    
    	main PROC
    		invoke sum,10,20,30      ; 调用并传递参数
    		ret
    	main ENDP
    END main
    

    寄存器传递参数:

    .code
    	sum PROC
    		add eax,ebx
    		add eax,ecx
    		ret
    	sum ENDP
    
    	main PROC
    		mov eax,10
    		mov ebx,20
    		mov ecx,30
    		call sum
    		ret
    	main ENDP
    END main
    

    使用PROTO声明: 如果调用的函数在之后实现, 须用 PROTO 提前声明,否则会报错

    sum PROTO :DWORD,:DWORD,:DWORD ; 函数声明的主要是参数类型,省略参数名
    
    .code
    	main PROC
    		invoke sum,10,20,30    ; 现在调用的是之后的函数
    		ret
    	main ENDP
    
    	sum PROC var1,var2,var3
    		mov eax,var1
    		add eax,var2
    		add eax,var3
    		ret
    	sum ENDP
    END main
    

    CALL/RET指令: 编写一个过程,实现对整数数组的求和,并将结果保存到EAX寄存器中.

    .data
    	array DWORD 1000h,2000h,3000h,4000h,5000h
    	theSum DWORD ?
    .code
    	main PROC
    		mov esi,offset array          ; ESI指向array
    		mov ecx,lengthof array        ; ECX=array元素个数
    		call ArraySum                 ; 调用求和指令
    		mov theSum,eax                ; 将结果保存到内存
    		push 0
    		call ExitProcess
    	main ENDP
    
    	ArraySum PROC
    		push esi           ; 保存ESI,ECX
    		push ecx
    		mov eax,0          ; 初始化累加寄存器
    	L1:
    		add eax,[esi]      ; 每个整数都和EAX中的和相加
    		add esi,TYPE DWORD ; 递增指针,继续遍历
    		loop L1
    		pop ecx            ; 恢复寄存器
    		pop esi
    		ret
    	ArraySum ENDP
    END main
    

    通过该语句块配合可以生成自定义过程,下面我们创建一个名为Sum的过程,实现EBX+ECX并将结果保存在EAX寄存器中.

    .data
    	TheSum DWORD ?
    .code
    	main PROC
    		mov ebx,100     ; 传递ebx
    		mov ecx,100     ; 传递ecx
    		call Sum        ; 调用过程
    		mov TheSum,eax  ; 保存结果到TheSum
    
    		push 0
    		call ExitProcess
    	main ENDP
    	
    	Sum PROC
    		xor eax,eax
    		add eax,ebx
    		add eax,ecx
    		ret
    	Sum ENDP
    END main
    

    INVOKE调用系统API: 默认情况下,会将返回结果保存在eax寄存器中.

    .data
    	szCaption db "MsgBox",0
    	szText db "这是一个提示框,请点击确定完成交互!",0
    .code
    	main PROC
    		.WHILE (1)
    			invoke MessageBox,NULL,offset szText,offset szCaption,MB_YESNO
    			.break .if(eax == IDYES)
    		.ENDW
    		ret
    	main ENDP
    END main
    

    模块化调用: 首先创建一个sum.asm然后在main.asm中引用sum这个文件中的函数.

    ; sum.asm 首先编译这个文件,并将其放入指定目录下
    	.386
    	.model flat, stdcall
    .code
    	sum PROC v1, v2, v3
    	    mov eax, v1
    	    add eax, v2
    	    add eax, v3
    	    ret
    	sum ENDP
    end
    
    ; main.asm 直接引用编译后的lib文件即可
    ;这里的引入路径可以是全路径, 这里是相对路径
    includelib /masm32/lib/sum.lib
    
    ;子程序声明
    sum proto :dword, :dword, :dword
    .code
    	main PROC
    		invoke sum,10,20,30    ;调用过程
    		ret
    	main ENDP
    END main
    

    ## 结构与联合

    结构(struct)时逻辑上互相关联的一组变量的模板或模式,结构中的单个变量称为域(field),程序的语句可以把结构作为一个实体进行访问,也可以对结构的单个域进行访问,结构通常包括不同类型的域,而联合(union)同样也是把多个标识符组合在一起,不过与结构不同的是,联合体共用用一块内存区域,内存的大小取决于联合体中最大的元素.

    引用结构变量: 通过使用<>,{}均可声明结构体,同时可以初始化,对结构体赋初值.

    ;定义结构
    MyPoint struct
    	pos_x DWORD ?
    	pos_y DWORD ?
    MyPoint ends
    
    .data
    	;声明结构, 使用 <>、{} 均可
    	ptr1 MyPoint <10,20>
    	ptr2 MyPoint {30,40}
    
    .code
    	main PROC
    		lea edx, ptr1
    		mov eax, (MyPoint ptr [edx]).pos_x   ; 此时eax=10
    		mov ebx, (MyPoint ptr [edx]).pos_y   ; 此时ebx=20
    		mov (MyPoint PTR [edx]).pos_x,100    ; 将100写入MyPoint.pos_x结构中存储
    		ret
    	main ENDP
    END main
    

    结构初始化: 以下定义了MyStruct结构,并将user2初始化,FName=lyshark,FAge=25.

    MyStruct struct
    	FName db 20 dup(0)
    	FAge db 100
    MyStruct ends
    
    .data
    	user1 MyStruct <>
    	user2 MyStruct <'lyshark',25>
    
    .code
    	main PROC
    		;lea edx, user1
    		;mov eax,DWORD PTR (MyStruct ptr[edx]).FName
    		;mov ebx,DWORD PTR (MyStruct ptr[edx]).FAge
    
    		mov eax,DWORD PTR [user2.FName]   ; eax=lyshark
    		mov ebx,DWORD PTR [user2.FAge]    ; ebx=25
    		ret
    	main ENDP
    END main
    

    使用系统结构: 通过调用GetLocalTime获取系统时间,并存储到SYSTEMTIM结构体中.

    .data
    	sysTime SYSTEMTIME <>           ; 声明结构体
    
    .code
    	main PROC
    		invoke GetLocalTime,addr sysTime    ; 获取系统时间并放入sysTime
    		mov eax,DWORD PTR sysTime.wYear     ; 获取年份
    		mov ebx,DWORD PTR sysTime.wMonth    ; 获取月份
    		mov ecx,DWORD PTR sysTime.wDay      ; 获取天数
    		ret
    	main ENDP
    END main
    

    结构体的嵌套定义:

    MyPT struct
    	pt_x DWORD ?
    	pt_y DWORD ?
    MyPT ends
    Rect struct
    	Left MyPT <>
    	Right MyPT <>
    Rect ends
    
    .data
    	LyShark1 Rect <>
    	LyShark2 Rect {<10,20>,<100,200>}
    .code
    	main PROC
    		mov [LyShark1.Left.pt_x],100
    		mov [LyShark1.Left.pt_y],200
    		
    		mov [LyShark1.Right.pt_x],1000
    		mov [LyShark1.Right.pt_y],2000
    		mov eax,[LyShark1.Left.pt_x]
    		ret
    	main ENDP
    END main
    

    联合体的声明:

    ; 定义联合体
    MyUnion union
    	My_Dword DWORD ?
    	My_Word WORD ?
    	My_Byte BYTE ?
    MyUnion ends
    
    .data
    	test1 MyUnion {1122h}; ;只能存放初始值
    .code
    	main PROC
    		mov eax, [test1.My_Dword]
    		mov ax, [test1.My_Word]
    		mov al, [test1.My_Byte]
    		ret
    	main ENDP
    END main
    

    ## 关于宏汇编

    宏过程(Macro Procedure)是一个命名的语汇编语句块,一旦定义后,宏过程就可以在程序中被调用任意多次,调用宏过程的时候,宏内的语句块将替换到调用的位置,宏的本质是替换,但像极了子过程,宏可定义在源程序的任意位置,但一般放在.data前面.

    一个简单的宏:

    MyCode macro
    	xor eax,eax
    	xor ebx,ebx
    	xor ecx,ecx
    	xor edx,edx
    endm
    
    .code
    	main PROC
    		MyCode   ; 将被替换为上面两行代码
    		ret    
    	main ENDP
    END main
    

    一个代替求和函数的宏

    MySum macro  var1, var2, var3
    	mov eax,var1
    	add eax,var2
    	add eax,var3
    endm
    
    .code
    	main PROC
    		MySum 10,20,30
    		MySum 10,20,30,40   ; 多余的参数40会被忽略
    		ret    
    	main ENDP
    END main
    

    宏参数的默认值: 通过定义默认值,可以不给默认的变量传递参数.

    ; 参数 var1、var2 通过 REQ 标识说明是必备参数
    MySum macro  var1:req, var2:req, var3:=<30>    ; var3默认值是30
    	mov eax,var1
    	add eax,var2
    	add eax,var3
    endm
    
    .code
    	main PROC
    		MySum 10,20
    		ret    
    	main ENDP
    END main
    

    使用EXITM终止宏执行: 可使用关键字exitm 终止宏代码的后面内容.

    MySum macro
    	xor eax,eax
    	xor ebx,ebx
    	xor ecx,ecx
    	exitm        ; 只会清空前三个寄存器,后面的跳过了
    	xor edx,edx
    	xor esi,esi
    endm
    
    .code
    	main PROC
    		MySum
    		ret    
    	main ENDP
    END main
    

    使用PURGE取消指定宏的展开:

    MySum macro
    	xor eax,eax
    	xor ebx,ebx
    endm
    
    .code
    	main PROC
    		MySum           ; 这个会被展开
    		purge MySum     ; 这个不会展开
    		MySum           ; 这个宏也不会展开了
    		ret
    	main ENDP
    END main
    

    在宏内使用局部标号:

    MyMax macro var1,var2
    	LOCAL jump
    	
    	mov eax,var1
    	cmp eax,var2
    	jge jump
    	xor eax,eax
    jump:	ret
    endm
    
    .code
    	main PROC
    		MyMax 20,10
    	main ENDP
    END main
    

    特殊操作符: &、<>、%、!

    &  ;替换操作符
    <> ;字符串传递操作符
    %  ;表达式操作符, 也用于得到一个变量或常量的值
    !  ;转义操作符
    ;自定义的宏
    mPrint macro Text
        PrintText '* &Text& *'
    endm
    
    .code
    main proc
        ;该宏会把参数直接替换过去
        mPrint 1234    ;* 1234 *
        
        ;要保证参数的完整应该使用 <>
        mPrint 12,34   ;* 12 *
        mPrint <12,34> ;* 12,34 *
        
        ;需要计算结果应该使用 %()
        mPrint 34+12   ;* 34+12 *
        mPrint %(34+12)   ;* 46 *
        
        ;用到 &、<、>、%、! 应该使用 ! 转义
        mPrint 10 !% 2 = %(10/2)!! ;* 10 % 2 = 5! *
        ret
    main endp
    end main
    

    ## 过程小例子

    整数求和: 通过使用汇编语言实现一个整数求和的小例子.

    .data
    	String WORD 100h,200h,300h,400h,500h
    .code
    	main PROC
    		;lea edi,String           ; 取String数组的基址
    		mov edi,offset String     ; 同上,两种方式均可
    		mov ecx,lengthof String   ; 取数组中的数据个数
    		mov ax,0                  ; 累加器清零
    	L1:
    		add ax,[edi]              ; 加上一个整数
    		add edi,TYPE String       ; 指向下一个数组元素,type(2byte)
    		loop L1
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    正向复制字符串: 使用汇编语言实现字符串的复制,将数据从source复制到target内存中.

    .data
    	source BYTE "hello lyshark welcome",0h
    	target BYTE SIZEOF source DUP(0),0h       ; 取源地址数据大小
    .code
    	main PROC
    		mov esi,0                  ; 使用变址寄存器
    		mov ecx,sizeof source      ; 循环计数器
    	L1:
    		mov al,source[esi]         ; 从源地址中取一个字符
    		mov target[esi],al         ; 将该字符存储在目标地址中
    		inc esi                    ; 递增,将指针移动到下一个字符
    		loop L1
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    反向复制字符串: 使用汇编语言实现字符串的复制,将数据从source复制到target内存中且反向存储数据.

    .data
    	source BYTE "hello lyshark welcome",0h
    	target BYTE SIZEOF source DUP(0),0h
    .code
    	main PROC
    		mov esi,sizeof source
    		mov ecx,sizeof source
    		mov ebx,0
    	L1:
    		mov al,source[esi]
    		mov target[ebx],al
    		dec esi
    		inc ebx
    		loop L1
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    查看内存与寄存器: 通过调用DumpMem/DumpRegs显示内存与寄存器的快照.

    .data
    	array DWORD 1,2,3,4,5,6,7,8,9,0ah,0bh
    .code
    	main PROC
    		mov esi,offset array       ; 设置内存起始地址
    		mov ecx,lengthof array     ; 设置元素数据,偏移
    		mov ebx,type array         ; 设置元素尺寸(1=byte,2=word,4=dword)
    		call DumpMem               ; 调用内存查询子过程
    		call DumpRegs              ; 调用查询寄存器子过程
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    汇编实现性能度量: 通过调用库函数,实现对指定代码执行的性能度量.

    .data
    	StartTime DWORD ?
    .code
    	main PROC
    
    		call GetMseconds       ; 调用区本地时间过程
    		mov StartTime,eax      ; 将返回值赋值给StartTime
    
    		mov ecx,10             ; 通过调用延时过程,模拟程序的执行
    	L1:
    		mov eax,1000           ; 指定延时1s=1000ms
    		call Delay             ; 调用延时过程
    		loop L1
    
    		call GetMseconds       ; 再次调用本地时间过程
    		sub eax,StartTime      ; 结束时间减去开始时间
    		call WriteDec          ; 以十进制形式输出eax寄存器的值
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    字符输出: WriteString(字符串),WriteInt(整数),WriteHex(16进制),WriteChar(字符),WriteDec(10进制).

    .data
    	Message BYTE "Input String:",0h
    	String DWORD ?
    
    .code
    	main PROC
    		; 设置控制台背景颜色
    		mov eax,yellow +(blue*16)     ; 设置为蓝底黄字
    		call SetTextColor             ; 调用设置过程
    		call Clrscr                   ; 清除屏幕,clear
    
    		; 提示用户一段话
    		mov edx,offset Message        ; 指定输出的文字
    		call WriteString              ; 调用回写过程
    		call Crlf                     ; 调用回车
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    字符输入: ReadString(字符串),ReadInt(整数),ReadHex(16进制),ReadChar(字符),ReadDec(10进制).

    .data
    	Buffer BYTE 21 DUP(0)          ; 输入缓冲区
    	ByteCount DWORD ?              ; 存放计数器      
    .code
    	main PROC
    		mov edx,offset Buffer      ; 指向缓冲区指针
    		mov ecx,sizeof Buffer      ; 指定最多读取的字符数
    		call ReadString            ; 读取输入字符串
    		mov ByteCount,eax          ; 保存读取的字符数
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    生成伪随机数:

    .code
    	main PROC
    		mov ecx,5           ; 循环生成5个随机数
    	L1:
    		call Random32       ; 生成随机数
    		call WriteDec       ; 以十进制显示
    		mov al,TAB          ; 水平制表符
    		call WriteChar      ; 显示水平制表符
    		loop L1
    		call Crlf           ; 回车
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    生成自定义随机数:

    .code
    	main PROC
    		mov ecx,5           ; 循环生成5个随机数
    	L1:
    		mov eax,100         ; 0-99之间
    		call RandomRange    ; 生成随机数
    		sub eax,50          ; 范围在-50-49
    		call WriteInt       ; 十进制输出
    		mov al,TAB
    		call WriteChar      ; 输出制表符
    		loop L1
    		call Crlf           ; 回车
    
    		push 0
    		call ExitProcess
    	main ENDP
    END main
    

    参考文献:《Intel 汇编语言程序设计》,《琢石成器-Win32汇编语言程序设计》,《汇编语言-王爽》

  • 相关阅读:
    SharePoint Permission Extension
    SharePoint暂时禁用事件触发
    视图xsl定制之嵌入服务器控件
    自定义View字段表头
    自定义母版页之列表过滤菜单位置issue fix
    ListDefinition Tips
    ThinkPHP6安装
    sqlalchemy 执行原生sql语句(转)
    python操作mysql(ORM)(转)
    Python Selenium 测试
  • 原文地址:https://www.cnblogs.com/LyShark/p/11136326.html
Copyright © 2011-2022 走看看