汇编开发(三):程序

1. 堆栈操作
1). 运行时栈
  • PUSH 操作

作用:32位PUSH操作将堆栈指针递减4并将值复制到该位置堆栈指针指向的堆栈

3110861-57da38b1c6bb41e6.png
PUSH操作.png

  • POP 操作

作用:POP操作从栈中移除一个数据, 数据移除之后, 栈指针增加指向更高的堆栈位置

3110861-d27ccde3fbfe12b2.png
POP操作.png
2). PUSH 和 POP 指令
  • PUSH 指令

PUSH指令将ESP地址减小并且源操作数到栈中

PUSH reg/mem16
PUSH reg/mem32
PUSH imm32
  • POP 指令

POP指令复制ESP指针指向的数据到目的操作数中,并增加ESP的数值

POP reg/mem16
POP reg/mem32
  • PUSHFD 和 POPFD 指令

PUSHFD 指令将32位EFLAGS寄存器压入栈中,POPFD将32位EFLAGS移出栈中

pushfd
popfd
  • PUSHAD, PUSHA, POPAD, and POPA 指令

PUSHAD / POPAD 将32位通用寄存器(EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI)压入/移出栈中
PUSHA / POPA 将16位寄存器(AX, CX, DX, BX, SP, BP, SI, DI)压入 / 移出栈中

MySub PROC
pushad ; save general-purpose registers
.
.
mov eax,...
mov edx,...
mov ecx,...
.
.
popad ; restore general-purpose registers
ret
MySub ENDP
3). 查看数组地址

方法:调试 -> 窗口 -> 内存 -> 内存1,在地址栏中输入(&+变量名)即可查看数组

3110861-43bca9d487f87cc6.png
内存.png

4). 字符串翻转
.486        ; 定义32位程序可以接受32位的寄存器和地址
.model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
.stack 4096             ; 设置运行的堆栈大小为4096字节
ExitProcess PROTO, dwExitCode: DWORD    


COMMENT &
    字符串翻转
&
.data
    aName BYTE "Abraham Lincoln", 0
    nameSize = ($ - aName) - 1

.code
main PROC                   ; 定义主函数开始位置
    ; 将那么长度存入寄存器
    mov ecx, nameSize
    mov esi, 0
L1: movzx eax, aName[esi]   ; 获取字符
    push eax                ; 压入栈中
    inc esi                 ; 循环变量自加
    LOOP L1

    ; 翻转字符串
    mov ecx, nameSize
    mov esi, 0
L2: pop eax                 ; 获取字符
    mov aName[esi], al      ; 存放字符
    inc esi                 ; 循环变量自加
    LOOP L2

    INVOKE ExitProcess, 0   ; 退出程序2
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
END main            ; 设置了函数的入口与出口
2. 定义和使用程序
1). PROC 指令
  • 定义一个程序
main PROC
.
.
main ENDP

非main方法定义如下:

sample PROC
.
.
 ret
sample ENDP

其中RET强制CPU返回方法被Call的位置。

  • 程序中的标签
jmp Destination
Destination::
2). CALL 和 RET 指令
  • CALL
    处理器直接调用程序开始执行在一个新的内存位置

  • RET
    程序指针返回被调用的位置

3). 嵌套过程调用

循环嵌套调用应该当被调用的过程在第一个之前调用另一个过程时
程序返回

3110861-ada0a9e95c727d58.png
循环嵌套调用.png

假设main函数调用一段名为Sub1的程序,当Sub1正在执行的时候,它调用名为Sub2的程序,当Sub2正在执行的时候,它调用名为Sub3的程序。

4). 参数传递

注:通过寄存器传递参数

示例:

.486        ; 定义32位程序可以接受32位的寄存器和地址
.model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
.stack 4096             ; 设置运行的堆栈大小为4096字节
ExitProcess PROTO, dwExitCode: DWORD    

COMMENT &
    求和
&
.data
    theSum DWORD ?

.code
;------------------------------------------------------
; SumOf
; 计算并返回是三个数之和
; Receieves: EAX, EBX, ECX, 三个数. 
; Returns: EAX = sum
;------------------------------------------------------
SumOf PROC
    add eax, ebx            ; 计算EAX与EBX之和
    add eax, ecx            ; 计算EAX与ECX之和
    RET                     ; 返回程序调用的位置
SumOf ENDP

main PROC                   ; 定义主函数开始位置
    mov eax, 10000h         ; 参数1
    mov ebx, 20000h         ; 参数2
    mov ecx, 30000h         ; 参数3

    call SumOf              ; 调用SumOf方法,并传递参数

    mov theSum, eax         ; 将计算结果赋值给theSum变量

    INVOKE ExitProcess, 0   ; 退出程序2
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
END main            ; 设置了函数的入口与出口
5). 数组求和之程序调用
.486        ; 定义32位程序可以接受32位的寄存器和地址
.model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
.stack 4096             ; 设置运行的堆栈大小为4096字节
ExitProcess PROTO, dwExitCode: DWORD    

COMMENT &
    计算数组之和
&
.data
    array DWORD 10000h, 20000h, 30000h, 40000h, 50000h
    theSum DWORD ?

.code
main PROC                   ; 定义主函数开始位置
    mov esi, OFFSET array   ; ESI 指向数组
    mov ecx, LENGTHOF array ; ECX 存放数组长度
    call ArraySum           ; 调用数组求和程序
    mov theSum, eax         ; 赋值

    INVOKE ExitProcess, 0   ; 退出程序2
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 

;------------------------------------------------------
; ArraySum
; 计算数组元素之和
; Receieves: ESI = 数组偏移地址
;            ECS = 数组长度
; Returns: EAX = 各数组元素之和
;------------------------------------------------------
ArraySum PROC
    push esi            ; 保存ESI
    push ecx            ; 保存ECX
    mov eax, 0          ; 设置数组之和为0
L1:                     ; 循环头
    add eax, [esi]      ; 将当前数组指针指向的数组元素值加入EAX寄存器
    add esi, TYPE DWORD ; 移动数组指针到下一个元素
    LOOP L1             ; 设置循环

    pop ecx             ; 恢复ecx值
    pop esi             ; 恢复esi值
    ret                 ; 返回程序调用位置
ArraySum ENDP

END main            ; 设置了函数的入口与出口
6). 保存和恢复寄存器

问题:每一段中都需要不PUSH和POP寄存值,这样在书写程序时会出现代码冗余的现象,并且如果忘记,则程序数据会出现异常。丢失数据。

  • USES 操作数
    伴随着PROC指令出现,后面跟着程序要修改的所有寄存器的名字列表。
;------------------------------------------------------
; ArraySum
; 计算数组元素之和
; Receieves: ESI = 数组偏移地址
;            ECS = 数组长度
; Returns: EAX = 各数组元素之和
;------------------------------------------------------
ArraySum PROC USES esi ecx
    mov eax,0           ; 设置和为0
L1:
    add eax,[esi]       ; 添加每一项到EAX
    add esi,TYPE DWORD  ; 移动指针
    loop L1             ; 循环加
    ret                 ; 返回到程序调用的位置
ArraySum ENDP
3. 链接外部库
1). 背景资料
  • 使用
WriteString proto

call WriteString
  • 链接命令行参数
    link hello.obj irvine32.lib kernel32.lib
3110861-b01659c7113a108d.png
链接32位库.png
4. Irvine32 库
1). 配置Irvine32库
2). Irvine32 库内容

专业程序员通常更喜欢建立自己的库,这样做是一种很好的教育体验。 在Windows下运行的32位模式下,输入输出库必须直接调用操作系统。

ProcedureDescription
CloseFileCloses a disk file that was previously opened.
ClrscrClears the console window and locates the cursor at the upper left corner.
CreateOutputFileCreates a new disk file for writing in output mode.
CrlfWrites an end-of-line sequence to the console window.
DelayPauses the program execution for a specified n-millisecond interval.
DumpMemWrites a block of memory to the console window in hexadecimal.
DumpRegsDisplays the EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EFLAGS, and EIP registers in hexadecimal. Also displays the most common CPU status flags.
GetCommandTailCopies the program’s command-line arguments (called the command tail) into an array of bytes.
GetDateTimeGets the current date and time from the system.
GetMaxXYGets the number of columns and rows in the console window’s buffer.
GetMsecondsReturns the number of milliseconds elapsed since midnight.
GetTextColorReturns the active foreground and background text colors in the console window.
GotoxyLocates the cursor at a specific row and column in the console window.
IsDigitSets the Zero flag if the AL register contains the ASCII code for a decimal digit (0–9).
MsgBoxDisplays a popup message box.
MsgBoxAskDisplay a yes/no question in a popup message box.
OpenInputFileOpens an existing disk file for input.
ParseDecimal32Converts an unsigned decimal integer string to 32-bit binary.
ParseInteger32Converts a signed decimal integer string to 32-bit binary.
Random32Generates a 32-bit pseudorandom integer in the range 0 to FFFFFFFFh.
RandomizeSeeds the random number generator with a unique value.
RandomRangeGenerates a pseudorandom integer within a specified range.
ReadCharWaits for a single character to be typed at the keyboard and returns the character.
ReadDecReads an unsigned 32-bit decimal integer from the keyboard, terminated by the Enter key.
ReadFromFileReads an input disk file into a buffer.
ReadHexReads a 32-bit hexadecimal integer from the keyboard, terminated by the Enter key.
ReadIntReads a 32-bit signed decimal integer from the keyboard, terminated by the Enter key.
ReadKeyReads a character from the keyboard’s input buffer without waiting for input.
ReadStringReads a string from the keyboard, terminated by the Enter key.
SetTextColorSets the foreground and background colors of all subsequent text output to the console.
Str_compareCompares two strings.
Str_copyCopies a source string to a destination string.
Str_lengthReturns the length of a string in EAX.
Str_trimRemoves unwanted characters from a string.
Str_ucaseConverts a string to uppercase letters.
WaitMsgDisplays a message and waits for a key to be pressed.
WriteBinWrites an unsigned 32-bit integer to the console window in ASCII binary format.
WriteBinBWrites a binary integer to the console window in byte, word, or doubleword format.
WriteCharWrites a single character to the console window.
WriteDecWrites an unsigned 32-bit integer to the console window in decimal format.
WriteHexWrites a 32-bit integer to the console window in hexadecimal format.
WriteHexBWrites a byte, word, or doubleword integer to the console window in hexadecimal format.
WriteIntWrites a signed 32-bit integer to the console window in decimal format.
WriteStackFrameWrites the current procedure’s stack frame to the console.
WriteStackFrameNameWrites the current procedure’s name and stack frame to the console.
WriteStringWrites a null-terminated string to the console window.
WriteToFileWrites a buffer to an output file.
WriteWindowsMsgDisplays a string containing the most recent error generated by MS-Windows.
3). 概览
  • Console Window
    控制台窗口(或命令窗口)是在显示命令提示符时由MS-Windows创建的纯文本窗口。
4). 个别程序说明
  • CloseFile: 关闭之前创建/打开的文件呢, 通过接收一个32位整型句柄。如果文件成功关闭,则EAX将返回非0值。
mov eax,fileHandle
call CloseFile
  • Clrscr: 清除控制台窗口,在程序的开始或结尾调用。其他时间如果要调用,需要调用WaitMsg暂停程序,然后再调用此程序。
call WaitMsg ;   "Press any key to continue..."
call Clrscr
  • CreateOutputFile: 在磁盘上创建一个新的文件,并以写的方式打开。当我们调用这个程序的时候,为EDX寄存器设置一个文件名。当程序返回时,如果创建成功,EAX包含一个有效的文件句柄;否则EAX等于INVALID_HANDLE_VALUE
.data
filename BYTE "newfile.txt",0
.code
mov edx,OFFSET filename
call CreateOutputFile
  • Crlf: 控制台换行,它输出一个包含ASCII码'0Dh'和'0Ah'的字符串
call Crlf
  • Delay: 暂停程序。当调用的时候,设置期望值到EAX中,当为毫秒(ms).
mov eax,1000 ; 1 second
call Delay
  • DumpMem: 以十六进制的形式将一系列内存写入控制台窗口。并将通过ESI赋值首地址,ECX赋值数组长度,EBX赋值数组元素所占字节数
.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          ; 数组元素所占字节
    call DumpMem
  • DumpRegs: 以16进制形式显示EAX, EBX, ECX, EDX, ESI, EDI, EBP,ESP, EIP, and EFL (EFLAGS) 寄存器数值。它也可以显示CF,SF,ZF,OF,AF,PF标志位的数值。
call DumpRegs
  • GetCommandTail: 将程序的命令行复制到以null结尾的字符串。如果命令行是空的,则设置CF标志位;否则,CF标志位清空。这段程序用在命令行参数获取上。当我们调用该程序时,EDX必须包含至少129个字节的偏移。
.data
    cmdTail BYTE 129 DUP(0)     ; empty buffer
.code
    mov edx,OFFSET cmdTail
    call GetCommandTail         ; fills the buffer

设置命名行参数方法:工程名右键 -> 属性 -> 调试 -> 命名参数

3110861-aabc9e5d66347bfe.png
命名行参数设置.png
  • GetMaxXY: 获取控制台缓冲字节。如果控制台窗口缓冲字节大于可视化窗口大小,滚动条自动显示。该程序没有输入参数,返回时,DX寄存器包含缓冲列长度,AX寄存器包含缓冲行长度,它们的值不会大于255。
.data
    rows BYTE ?
    cols BYTE ?
.code
    call GetMaxXY
    mov rows,al
    mov cols,dl
  • GetMseconds: 从主机电脑获取当前的系统时间,并将时间返回给EAX寄存器。这段程序用于计算事件之间的执行时间。没有输入参数。
.data
    startTime DWORD ?
.code
    call GetMseconds    
    mov startTime,eax
L1:
    ; (loop body)
    loop L1
    call GetMseconds
    sub eax,startTime ; EAX = loop time, in milliseconds
  • GetTextColor: 获取当前控制台的前景与背景色。没有输入参数,返回当前背景色并赋值给AL的高4位,前景色赋值给AL的低4位。
.data
    color byte ?
.code
    call GetTextColor
    mov color,AL
  • Gotoxy: 将控制台中的光标移动到指定位置(行与列)。默认的,控制台的x可选值为0—79,y可选值为0—24.当我们调用Gotoxy时,将Y(row)的值赋给DH,X(column)的值赋给DL。
mov dh,10 ; row 10
mov dl,20 ; column 20
call Gotoxy ; locate cursor

注:用户可能重设控制台窗口的大小,所以应当应用调用GetMaxXY获取rows和columns的取值。

  • IsDigit: 判断AL中的ASCII码是否为一个有效的十进制数字。当调用这段程序时,将ASCII码赋值给AL,如果AL中的内容是一个有效的十进制数据,那么设置零标记位;否则,清空零标记位。
mov AL,somechar
call IsDigit
  • MsgBox: 显示一个带着文本的图形弹出消息窗口(控制台空口模式下)。为EDX设置字符串,用于显示在窗体内部。可选的,在EBX中设置窗体的标题。如果没有标题,则设置EBX为0
.data
    caption BYTE "Dialog Title", 0
    HelloMsg BYTE "This is a pop-up message box.", 0dh,0ah BYTE "Click OK to continue...", 0
.code
    mov ebx,OFFSET caption
    mov edx,OFFSET HelloMsg
    call MsgBox
  • MsgBoxAsk: 显示一个带着Yes/No的图形弹出消息框(控制台模式下)。消息框内容,为EDX设置字符串。可选,EBX设置消息框标题,如果没有标题则设置EBX为0.该程序在EAX中返回一个整型,以告诉你用户的选择。IDYES为6,IDNO为7.
.data
    caption BYTE "Survey Completed",0
    question BYTE "Thank you for completing the survey." 
             BYTE 0dh,0ah
             BYTE "Would you like to receive the results?",0
.code
    mov ebx,OFFSET caption
    mov edx,OFFSET question
    call MsgBoxAsk
    ;(check return value in EAX)
  • OpenInputFile: 为输入打开一个已经存在的文件。其中EDX设置文件名,返回时,如果文件打开成功,EAX包含一个有效的文件句柄;否则,EAX等于INVALID_HANDLE_VALUE
.data
    filename BYTE "myfile.txt",0
.code
    mov edx,OFFSET filename
    call OpenInputFile
  • ParseDecimal32: 转换无符号十进制整数字符串到32位二进制。其中EDX设置字符串的首地址,ECX设置字符串的长度,返回时将二进制的值设置给EAX。
.data
    buffer BYTE "8193"
    bufSize = ($ - buffer)
.code
    mov edx,OFFSET buffer
    mov ecx,bufSize
    call ParseDecimal32 ; returns EAX

-- 如果这个整型是空白的, 则EAX = 0 and CF = 1
-- 如果这个整型仅仅包含空格, 则EAX = 0 and CF = 1
-- 如果这个整型是大于2^32�1, 则EAX = 0 and CF = 1
-- 否则, EAX包含转换后的整型并且CF = 0

  • ParseInteger32: 转换一个有符号的十进制整型字符串为32位二进制数据。所有有效的数字从字符串第一个非数字字符开始转换。字符串之前的空格忽略。EDX设置字符串的首地址,ECX设置字符串的长度,返回的数值存放在EAX中。
.data
    buffer byte "-8193"
    bufSize = ($ - buffer)
.code
    mov edx,OFFSET buffer
    mov ecx,bufSize
    call ParseInteger32 ; returns EAX
  • Random32: 在EAX寄存器中生成一个32位随机整型。重复调用时,Random32生成模拟随机序列。使用seed方法创建一个数字,这段程序使用种子生成一个随机数。
.data
    randVal DWORD ? 
.code
    call Random32
    mov randVal,eax 
  • Randomize: 初始化Random32的起始种子值和RandomRange程序。种子等于一天中的时间,精确到1/100秒。 每次运行一个调用Random32和RandomRange的程序的时间,生成的序列随机数将是唯一的。
    call Randomize
    mov ecx,10
L1: call Random32
    ; use or display random value in EAX here...
    loop L1
  • RandomRange: 从0—(n-1)生成一个随机数,其中n赋值给EAX寄存器。随机数返回给EAX寄存器。
.data
    randVal DWORD ?
.code
    mov eax,5000
    call RandomRange
    mov randVal,eax
  • ReadChar: 从键盘读取一个字符,并返回这个字符到AL寄存器中。
.data
    char BYTE ?
.code
    ReadChar
    mov char, al

如果用户按下一个扩展键,例如功能键Ins,Del,程序将设置AL为0,AH设置对应的键值。

  • ReadDec: 从键盘读取一个32位无符号整型,并将值设置到EAX中,忽略开头的空格。返回值是从第一个有效的数字开始直到一个非数字。
.data
    intVal DWORD ?
.code
    call ReadDec
    mov intVal, eax

-- 如果这个整型是空白的, 则EAX = 0 and CF = 1
-- 如果这个整型仅仅包含空格, 则EAX = 0 and CF = 1
-- 如果这个整型是大于2^32�1, 则EAX = 0 and CF = 1
-- 否则, EAX包含转换后的整型并且CF = 0

  • ReadFromFile: 读取文件中的内容到内存中。当你调用ReadFromFile时,EAX中存储已打开文件句柄,EDX中存储文件起始地址,ECX中存储文件内容最大字节数。ReadFromFile返回值,检查CF进位标志,如果CF是空的,EAX包含从文件中读取到的字节数,但如果CF被设置,EAX中包含一个系统的错误码。你可以调用WriteWindowsMsg程序获取异常文本。
.data
    BUFFER_SIZE = 5000
    buffer BYTE BUFFER_SIZE DUP(?)
    bytesRead DWORD ?
.code
    mov edx,OFFSET buffer       ; points to buffer
    mov ecx,BUFFER_SIZE         ; max bytes to read
    call ReadFromFile           ; read the file
    ; 如果CF标志位是空的,则可以执行这行代码
    ; mov bytesRead, eax        ; count of bytes actually read
    ; 如果CF标志位被设置,则调用WriteWindowsMsg
    ; call WriteWindowsMsg
  • ReadHex: 从键盘读取一个32位的十六进制数据,并将获取到的数据设置到EAX中。
.data
    hexVal DWORD ?
.code
    call ReadHex
    mov hexVal,eax
  • ReadInt: 从键盘读取一个32位的有符号整型,并将值设置到EAX中。开头位置用户可以使用+/-, 其余的数字仅包含数字。如果用户输入的值不是一个正确的32位有符号整数,那么该程序设置OF标志位显示一个异常信息。该程序返回所有有效的数字直到非数字字符为止。
.data
    intVal SDWORD ? 
.code
    call ReadInt
    mov intVal,eax
  • ReadKey: 执行无等待键盘检查。换句话说,检测用户是否按下了某个键。如果没有键盘数据,ZF标志位被设置;如果有键被按下,则ZF标志位被清空并设置AL寄存器为ASCII码值。如果AL等于0,则用户可能按下了一下特殊键,AH寄存器将包含虚拟的扫描码,DX包含一个虚拟键值,EBX包含键盘标识位。
if no_keyboard_data then
    ZF = 1
else
    ZF = 0
    if AL = 0 then
        extended key was pressed, and AH = scan code, DX = virtual key code, and EBX = keyboard flag bits
    else
        AL = the key's ASCII code
    endif
endif
  • ReadString: 从键盘读取一个字符串,知道用户输入回车键为止。EDX设置字符串首地址,ECX设置用户最大输入字符数。返回时EAX中存放字符个数。
.data
    buffer BYTE 21 DUP(0)   ; input buffer
    byteCount DWORD ?       ; holds counter
.code
    mov edx,OFFSET buffer   ; point to the buffer
    mov ecx,SIZEOF buffer   ; specify max characters
    call ReadString         ; input the string
    mov byteCount,eax       ; number of characters
  • SetTextColor: 设置文本输出区域前景色和背景色。当调用SetTextColor时,分配一个颜色给EAX寄存器。下面是与定义的颜色常量,可以使用在前景色与背景色的设置。
ColorColorColorColor
black = 0red = 4gray = 8lightRed = 12
blue = 1magenta = 5lightBlue = 9lightMagenta = 13
green = 2brown = 6lightGreen = 10yellow = 14
cyan = 3lightGray = 7lightCyan = 11white = 15

设置颜色时,EAX的高16位为背景色,低16位为前景色

mov eax,white  (blue * 16)   ; white on blue
call SetTextColor
  • Str_length: 返回一个控制终止的字符串长度。其中EDX设置字符串偏移量。返回时,EAX设置为字符串长度。
.data
    buffer BYTE "abcde",0
    bufLength DWORD ?
.code
    mov edx,OFFSET buffer   ; point to string
    call Str_length         ; EAX = 5
    mov bufLength,eax       ; save length
  • WaitMsg: 显示一个字符串"Press any key to continue..."并等待用户按下一个键。
call WaitMsg
  • WriteBin: 以ASCII二进制格式将整数写入控制台窗口。其中EAX存放待输出的字符串。
    mov eax,12346AF9h
    call WriteBin
  • WriteBinB: 以ASCII二进制格式将32位整数写入控制台窗口,EAX寄存器中存放待输出的数值,EBX寄存器中存放要显示类型的字节大小。
    mov eax,00001234h
    mov ebx,TYPE WORD   ; 2 bytes
    call WriteBinB      ; displays 0001 0010 0011 0100
  • WriteChar: 在控制台窗口中输出一个字符,AL寄存器中存放该字符或ASCII码。
    mov al,'A'
    call WriteChar  ; displays: "A"
  • WriteDec: 在控制台窗口中输出一个开头没有0的32位无符号整数,其中EAX寄存器存放该数字。
    mov eax,295
    call WriteDec   ; displays: "295"
  • WriteHex: 在控制台窗口中输出一个8位十六进制的32位无符号整数,如果需要开始位置补0,EAX寄存器存放待输出的整数。
    mov eax,7FFFh
    call WriteHex   ; displays: "00007FFF"
  • WriteHexB: 在控制台窗口中输出一个指定十六进制格式的32位无符号整数,如果必须,开始位置补0。EAX寄存器存储待输出整数,EBX寄存器中存放待显示的位数。
    mov eax,7FFFh
    mov ebx,TYPE WORD   ; 2 bytes
    call WriteHexB      ; displays: "7FFF"
  • WriteInt: 在控制台窗口中输出一个带符号且不带0的32位十进制整数,EAX寄存器中存放待输出的整数。
    mov eax,216543
    call WriteInt   ; displays: "+216543"
  • WriteString: 在控制台窗口输出一个以空值终止的字符串,EDX寄存器中存放字符串首地址。
.data
    prompt BYTE "Enter your name: ",0
.code
    mov edx,OFFSET prompt
    call WriteString
  • WriteToFile: 将缓冲的内容输出到文件中,其中EAX寄存器中存放一个有效的文件句柄,EDX寄存器中存放缓冲内容的偏移地址,ECX寄存器中存放待写入的字节长度。当程序返回时,如果EAX寄存器大于0,则它的值为已经写入的字节长度;否则,为异常信息。
BUFFER_SIZE = 5000
.data
    fileHandle DWORD ?
    buffer BYTE BUFFER_SIZE DUP(?)
.code
    mov eax,fileHandle
    mov edx,OFFSET buffer
    mov ecx,BUFFER_SIZE
    call WriteToFile
  • WriteWindowsMsg: 当执行系统函数时,你的应用在控制台窗口中输出一个最近生成的异常信息。
    call WriteWindowsMsg
5. Irvine32 库测试程序
1). 教程:Library Test #1
  • Step 1: 在程序开始位置添加标准头
; Library Test #1: Integer I/O
; 测试 Clrsrc, Crlf, DumpMem, ReadInt, SetTextColor
; WaitMsg, WriteBin, WriteHex, WriteString
Include Irvine32.inc
  • Step 2: 定义常量COUNT用于决定程序循环次数,常量BlueTextOnGrayDefaultColor用于改变控制台背景与前景颜色。
.data
    COUNT = 4               ; 设置循环次数
    BlueTextOnGray = blue + (lightGray * 16)            ; 控制台的前景色和背景色
    DefaultColor = lightGray + (black * 16)             ; 默认的控制台前景色和背景色
  • Step 3: 定义一个有符号的32位整型,使用十进制表示常量。定义一个字符串用于提示用于输入一个整数
.data
    arrayD SDWORD 12345678h, 1A4B2000h, 3434h, 7AB9h    ; 数组
    prompt BYTE "Enter a 32-bit signed integer: ", 0    ; 提示信息
  • Step 4: 代码区域定义主程序,设置EAX值为BlueTextOnGray, 调用SetTextColor改变背景和前景色。为了使设置的颜色生效,必须调用清屏程序清屏。
.code
main PROC                   ; 定义主函数开始位置
    mov eax, BlueTextOnGray ; 设置控制台窗口颜色
    call SetTextColor       ; 设置颜色
    call Clrscr             ; 为了使设置的颜色生效,调用清屏方法

    INVOKE ExitProcess, 0   ; 退出程序
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
END main            ; 设置了函数的入口与出口
  • Step 5: 将ESI寄存器设置为arrayD数组的首地址
    mov esi, OFFSET arrayD  ; 设置数组首地址
  • Step 6: 将EBX设置为数组每个元素所占用的字节数。
    mov ebx, TYPE arrayD    ; 设置数组元素所占字节数
  • Step 7: 将ECX设置为数组长度,然后调用DumpMem显示。
    mov esi, OFFSET arrayD  ; 设置数组首地址
    mov ebx, TYPE arrayD    ; 设置数组元素所占字节数
    mov ecx, LENGTHOF arrayD; 数组元素个数
    call DumpMem            ; 显示信息

效果如下:


3110861-5eec8cbb6898f946.png
DumpMem效果.png
  • Step 8: 调用Crlf输出一个空行,然后初始化ECX的值为COUNT用于循环。
    call Crlf               ; 输出空行
    mov ecx, COUNT          ; 设置循环次数
  • Step 9: 显示一个字符串信息提示用户输入数字,字符串的首地址赋值给EDX,调用WriteString程序输出,然后调用ReadInt程序接受用户的输入,将这个值赋值给EAX。
L1: 
    mov edx, OFFSET prompt  ; 设置字符串首地址
    call WriteString        ; 输出提示信息
    call ReadInt            ; 将输入的数字读入EAX
    call Crlf               ; 输出空行
  • Step 10: 调用WriteInt显示一个格式化的十进制有符号数字, 然后调用Crlf输出空行
    call WriteInt           ; 显示一个有符号的十进制数
    call Crlf               ; 输出空行
  • Step 11: 调用WriteHexWriteBin显示十六进制数和二进制数。
    call WriteHex           ; 显示十六进制数
    call Crlf               ; 输出空行
    call WriteBin           ; 显示二进制数
    call Crlf               ; 输出空行
    call Crlf               ; 输出空行
  • Step 12: 设置循环
    LOOP L1                 ; 设置循环,此时ECX递减
  • Step 13: 循环结束后,显示一个结束信息并暂停程序。
    call WaitMsg            ; 显示请按任意键继续信息
  • Step 14: 程序的结尾,将颜色设置回默认的
    mov eax, DefaultColor   ; 控制器默认的前景色与背景色
    call SetTextColor       ; 设置颜色
    call Clrscr             ; 清屏

效果:

3110861-922351b21eb97535.png
InputLoop程序.png
  • Step 15: 完整程序
; Library Test #1: Integer I/O
; 测试 Clrsrc, Crlf, DumpMem, ReadInt, SetTextColor
; WaitMsg, WriteBin, WriteHex, WriteString
Include Irvine32.inc

.data
    COUNT = 4               ; 设置循环次数
    BlueTextOnGray = blue + (lightGray * 16)            ; 控制台的前景色和背景色
    DefaultColor = lightGray + (black * 16)             ; 默认的控制台前景色和背景色
    arrayD SDWORD 12345678h, 1A4B2000h, 3434h, 7AB9h    ; 数组
    prompt BYTE "Enter a 32-bit signed integer: ", 0    ; 提示信息

.code
main PROC                   ; 定义主函数开始位置
    ; 设置蓝色文本和亮灰色背景
    mov eax, BlueTextOnGray ; 设置控制台窗口颜色
    call SetTextColor       ; 设置颜色
    call Clrscr             ; 为了使设置的颜色生效,调用清屏方法

    ; 使用DumpMem显示数组
    mov esi, OFFSET arrayD  ; 设置数组首地址
    mov ebx, TYPE arrayD    ; 设置数组元素所占字节数
    mov ecx, LENGTHOF arrayD; 数组元素个数
    call DumpMem            ; 显示信息
    ; 让用户输入有符号整型
    call Crlf               ; 输出空行
    mov ecx, COUNT          ; 设置循环次数

L1: 
    mov edx, OFFSET prompt  ; 设置字符串首地址
    call WriteString        ; 输出提示信息
    call ReadInt            ; 将输入的数字读入EAX
    call Crlf               ; 输出空行

    ; 以十进制,十六进制,二进制形式输出一个整数
    call WriteInt           ; 显示一个有符号的十进制数
    call Crlf               ; 输出空行

    call WriteHex           ; 显示十六进制数
    call Crlf               ; 输出空行
    call WriteBin           ; 显示二进制数
    call Crlf               ; 输出空行
    call Crlf               ; 输出空行

    LOOP L1                 ; 设置循环,此时ECX递减

    call WaitMsg            ; 显示请按任意键继续信息

    ; 设置控制台颜色为默认的
    mov eax, DefaultColor   ; 控制器默认的前景色与背景色
    call SetTextColor       ; 设置颜色
    call Clrscr             ; 清屏

    exit
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
END main            ; 设置了函数的入口与出口
2). Library Test #2: 随机数
; Library Test #2
; Rendom Integers
Include Irvine32.inc

TAB = 9             ; Tab的ASCII码
    
.code
main PROC                   ; 定义主函数开始位置
    call Randomize          ; 初始化随机数生成器
    call Rand1              ; 调用Rand1程序
    call Rand2              ; 调用Rand2程序

    call WaitMsg            ; 输出等待按键信息
    exit
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 

Rand1 PROC
    ; 生成10个伪随机数
    mov ecx, 10             ; 循环10次
L1: 
    call Random32           ; 随机数生成初始化
    call WriteDec           ; 输出一个无符号十进制数
    mov al, TAB             ; 设置TAB键的ASCII码
    call WriteChar          ; 输出一个字符
    LOOP L1                 ; 设置循环

    call Crlf               ; 输处空行
    RET                     ; 回到调用该程序的位置
Rand1 ENDP

Rand2 PROC
    ; 从-50到49生成是个伪随机数
    mov ecx, 10             ; 设置循环次数
L1:
    mov eax, 100            ; 设置值的范围为0-99
    call RandomRange        ; 生成随机数
    sub eax, 50             ; 减去50,使生成的值的范围为-50——49
    call WriteInt           ; 输出有符号的十进制
    mov al, TAB             ; 设置TAB的ASCII码
    call WriteChar          ; 输出TAB
    LOOP L1                 ; 设置循环

    call Crlf               ; 输出空行
    RET
Rand2 ENDP
END main            ; 设置了函数的入口与出口
3110861-61f519f65e85854a.png
Random 效果.png
3). Library Test #3 : 性能时间
; Library Test #3: Performance Timing
; 计算嵌套循环的执行时间
Include Irvine32.inc
    
.data
    OUTER_LOOP_COUNT = 3    ; 循环次数
    startTIme DWORD ?       ; 循环开始时间
    msg1 BYTE "Please wait...", 0Dh, 0Ah, 0 ;等待信息
    msg2 BYTE "Elapsed milliseconds: ", 0   ; 经过时间

.code
main PROC                   ; 定义主函数开始位置
    mov edx, OFFSET msg1    ; 设置msg1的偏移地址
    call WriteString        ; 输出msg1
    
    ; 保存开始时间
    call GetMSeconds        ; 获取当前时间
    mov startTime, eax      ; 保存开始时间

    ; 设置外层循环次数
    mov ecx, OUTER_LOOP_COUNT

L1: 
    call innerLoop          ; 调用内层循环
    LOOP L1

    ; 计算执行时间
    call GetMSeconds        ; 获取当前时间
    sub eax, startTime      ; 计算出执行时间

    ; 显示执行时间
    mov edx, OFFSET msg2    ; 设置msg2的偏移地址
    call WriteString        ; 输出msg2
    call WriteDec           ; 输出循环执行时间
    call Crlf               ; 输出空行

    call WaitMsg            ; 输出等待按键信息
    exit
main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 

innerLoop PROC USES ecx
    mov ecx, 0FFFFFFFh      ; 设置循环次数
L1: 
    mul eax                 ; 乘法
    mul eax             
    mul eax
    LOOP L1                 ; 重复内层循环

    RET
innerLoop ENDP

END main            ; 设置了函数的入口与出口
3110861-54b2c977edee345c.png
Performance Timing.png
6. 64位汇编程序
1). Irvine64 库
ProcedureDescription
CrlfWrites an end-of-line sequence to the console.
Random64Generates a 64-bit pseudorandom integer in the range 0 to 2^64�1. The random value is returned in the RAX register.
RandomizeSeeds the random number generator with a unique value.
ReadInt64Reads a 64-bit signed integer from the keyboard, terminated by the Enter key. It returns the integer value in the RAX register.
ReadStringReads a string from the keyboard, terminated by the Enter key. Pass it the offset of the input buffer in RDX, and set RCX to the maximum number of characters the user can enter, plus 1 (for the null terminator byte). It returns a count (in RAX) of the number of characters typed by the user.
Str_compareCompares two strings. Pass it a pointer to the source string in RSI, and a pointer to the target string in RDI. Sets the Zero and Carry flags in the same way as the CMP (Compare) instruction.
Str_copyCopies a source string to the location indicated by a target pointer. Pass the source offset in RSI, and the target offset in RDI.
Str_lengthReturns the length of a null-terminated string in the RAX register. Pass it the string’s offset in RCX.
WriteInt64Displays the contents of the RAX register as a 64-bit signed decimal integer, with a leading plus or minus sign. It has no return value.
WriteHex64Displays the contents of the RAX register as a 64-bit hexadecimal integer. It has no return value.
WriteHexBDisplays the contents of the RAX register as a hexadecimal integer in either a 1-byte, 2-byte, 4-byte, or 8-byte format. Pass it the display size (1, 2, 4, or 8) in the RBX register. It has no return value.
WriteStringDisplays a null-terminated ASCII string. Pass it the string’s 64-bit offset in RDX. It has no return value.
2). 调用64位子程序
ExitProcess PROTO   ; Windows API 中
WriteInt64 PROTO    ; Irvine64 库中

    call ExitProcess    ; 调用
3). 调用过程示例
; Calling a subroutine in 64-bit mode           (CallProc_64.asm)
; 添加现有项Irvine64.obj

ExitProcess PROTO   ; Windows API 中
WriteInt64 PROTO    ; Irvine64 库
Crlf PROTO          ; Irvine64 库

.code
main PROC
    sub rsp, 8          ; 对齐栈指针
    sub rsp, 20h        ; 保留32个字节

    mov rcx, 1          ; 设置参数
    mov rdx, 2
    mov r8, 3
    mov r9, 4

    call AddFour        ; 返回值赋给RAX
    call WriteInt64     ; 显示数字
    call Crlf           ; 输出空行

    mov ecx, 0  
    call ExitProcess
main ENDP

AddFour PROC
    mov rax, rcx
    add rax, rdx
    add rax, r8
    add rax, r9         ; 四个数之和放在RAX中
    RET
AddFour ENDP

END

提示:该程序需要链接Irvine64.obj文件,在解决方案资源管理器窗口右键工程项目名 -> 添加 -> 现有项... -> 选择Irvine64.obj文件,如果出现Irvine64.obj : error LNK2017: 没有 /LARGEADDRESSAWARE:NO,“ADDR32”到“bufferLHB”的重定位无效问题,项目名右键 -> 属性 -> 配置属性 -> 链接器 -> 系统 -> 启用大地址 -> 选择否(/LARGEADDRESSAWARE:NO), 原文地址

3110861-c9785cedc9f4ef0b.png
/LARGEADDRESSAWARE:NO.png

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值