1. 堆栈操作
1). 运行时栈
- PUSH 操作
作用:32位PUSH操作将堆栈指针递减4并将值复制到该位置堆栈指针指向的堆栈
- POP 操作
作用:POP操作从栈中移除一个数据, 数据移除之后, 栈指针增加指向更高的堆栈位置
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,在地址栏中输入(&
+变量名)即可查看数组
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). 嵌套过程调用
循环嵌套调用应该当被调用的过程在第一个之前调用另一个过程时
程序返回
假设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
4. Irvine32 库
1). 配置Irvine32库
2). Irvine32 库内容
专业程序员通常更喜欢建立自己的库,这样做是一种很好的教育体验。 在Windows下运行的32位模式下,输入输出库必须直接调用操作系统。
Procedure | Description |
---|---|
CloseFile | Closes a disk file that was previously opened. |
Clrscr | Clears the console window and locates the cursor at the upper left corner. |
CreateOutputFile | Creates a new disk file for writing in output mode. |
Crlf | Writes an end-of-line sequence to the console window. |
Delay | Pauses the program execution for a specified n-millisecond interval. |
DumpMem | Writes a block of memory to the console window in hexadecimal. |
DumpRegs | Displays the EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EFLAGS, and EIP registers in hexadecimal. Also displays the most common CPU status flags. |
GetCommandTail | Copies the program’s command-line arguments (called the command tail) into an array of bytes. |
GetDateTime | Gets the current date and time from the system. |
GetMaxXY | Gets the number of columns and rows in the console window’s buffer. |
GetMseconds | Returns the number of milliseconds elapsed since midnight. |
GetTextColor | Returns the active foreground and background text colors in the console window. |
Gotoxy | Locates the cursor at a specific row and column in the console window. |
IsDigit | Sets the Zero flag if the AL register contains the ASCII code for a decimal digit (0–9). |
MsgBox | Displays a popup message box. |
MsgBoxAsk | Display a yes/no question in a popup message box. |
OpenInputFile | Opens an existing disk file for input. |
ParseDecimal32 | Converts an unsigned decimal integer string to 32-bit binary. |
ParseInteger32 | Converts a signed decimal integer string to 32-bit binary. |
Random32 | Generates a 32-bit pseudorandom integer in the range 0 to FFFFFFFFh. |
Randomize | Seeds the random number generator with a unique value. |
RandomRange | Generates a pseudorandom integer within a specified range. |
ReadChar | Waits for a single character to be typed at the keyboard and returns the character. |
ReadDec | Reads an unsigned 32-bit decimal integer from the keyboard, terminated by the Enter key. |
ReadFromFile | Reads an input disk file into a buffer. |
ReadHex | Reads a 32-bit hexadecimal integer from the keyboard, terminated by the Enter key. |
ReadInt | Reads a 32-bit signed decimal integer from the keyboard, terminated by the Enter key. |
ReadKey | Reads a character from the keyboard’s input buffer without waiting for input. |
ReadString | Reads a string from the keyboard, terminated by the Enter key. |
SetTextColor | Sets the foreground and background colors of all subsequent text output to the console. |
Str_compare | Compares two strings. |
Str_copy | Copies a source string to a destination string. |
Str_length | Returns the length of a string in EAX. |
Str_trim | Removes unwanted characters from a string. |
Str_ucase | Converts a string to uppercase letters. |
WaitMsg | Displays a message and waits for a key to be pressed. |
WriteBin | Writes an unsigned 32-bit integer to the console window in ASCII binary format. |
WriteBinB | Writes a binary integer to the console window in byte, word, or doubleword format. |
WriteChar | Writes a single character to the console window. |
WriteDec | Writes an unsigned 32-bit integer to the console window in decimal format. |
WriteHex | Writes a 32-bit integer to the console window in hexadecimal format. |
WriteHexB | Writes a byte, word, or doubleword integer to the console window in hexadecimal format. |
WriteInt | Writes a signed 32-bit integer to the console window in decimal format. |
WriteStackFrame | Writes the current procedure’s stack frame to the console. |
WriteStackFrameName | Writes the current procedure’s name and stack frame to the console. |
WriteString | Writes a null-terminated string to the console window. |
WriteToFile | Writes a buffer to an output file. |
WriteWindowsMsg | Displays 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
设置命名行参数方法:工程名右键 -> 属性 -> 调试 -> 命名参数
- 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寄存器。下面是与定义的颜色常量,可以使用在前景色与背景色的设置。
Color | Color | Color | Color |
---|---|---|---|
black = 0 | red = 4 | gray = 8 | lightRed = 12 |
blue = 1 | magenta = 5 | lightBlue = 9 | lightMagenta = 13 |
green = 2 | brown = 6 | lightGreen = 10 | yellow = 14 |
cyan = 3 | lightGray = 7 | lightCyan = 11 | white = 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
用于决定程序循环次数,常量BlueTextOnGray
和DefaultColor
用于改变控制台背景与前景颜色。
.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 ; 显示信息
效果如下:
- 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: 调用
WriteHex
和WriteBin
显示十六进制数和二进制数。
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 ; 清屏
效果:
- 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 ; 设置了函数的入口与出口
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 ; 设置了函数的入口与出口
6. 64位汇编程序
1). Irvine64 库
Procedure | Description |
---|---|
Crlf | Writes an end-of-line sequence to the console. |
Random64 | Generates a 64-bit pseudorandom integer in the range 0 to 2^64�1. The random value is returned in the RAX register. |
Randomize | Seeds the random number generator with a unique value. |
ReadInt64 | Reads a 64-bit signed integer from the keyboard, terminated by the Enter key. It returns the integer value in the RAX register. |
ReadString | Reads 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_compare | Compares 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_copy | Copies a source string to the location indicated by a target pointer. Pass the source offset in RSI, and the target offset in RDI. |
Str_length | Returns the length of a null-terminated string in the RAX register. Pass it the string’s offset in RCX. |
WriteInt64 | Displays 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. |
WriteHex64 | Displays the contents of the RAX register as a 64-bit hexadecimal integer. It has no return value. |
WriteHexB | Displays 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. |
WriteString | Displays 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)
, 原文地址