个人笔记本配置win8系统,intel core i5 cpu。
一、masm32的安装
官网DownLoad masm32一路下来,安装到D盘根目录下即可。
二、配置环境变量(用户变量)
include ==>> D:\masm32\include
lib ==>> D:\masm32\lib
path ==>> D:\masm32\bin
三、写一个汇编代码保存为D:\masm32\hello.asm
.386
.model flat,stdcall
option casemap:none
include kernel32.inc
includelib kernel32.lib
include masm32.inc
includelib masm32.lib
.data
hello db "hello world" ;如果用str代替hello会报错
.code
start:
push offset hello
call StdOut ;调用显示函数 大小写不能错
;int 21h ;此方法运行程序时报错
;mov ah,01 ;此方法运行程序时报错
;int 21h ;此方法运行程序时报错
exit:
push 0
call ExitProcess
end start
四、编译并执行
控制台编译程序
五、程序执行效果
点击工具栏命令行按钮快速打开cmd界面运行hello.exe 显示输出hello world
根据问题描述,该问题的主要难点在于如何优化顺序结构的设计,具体地说,如何在设计顺序结构时提高代码的执行效率和减小代码的体积。针对该问题,可以从以下两个方面进行优化:
在设计顺序结构时,应该优先考虑如何提高代码的执行效率。具体来说,可以采用以下几种方法:
(1)尽可能使用寄存器
寄存器是CPU中的一种高速存储器件,可以大幅提高代码的执行速度。因此,在编写汇编代码时,应该尽可能地使用寄存器,减少对内存的访问次数。例如,在处理大型数组时,可以将数组的地址存到寄存器中,从而减少内存访问次数。
(2)使用循环和跳转语句
循环和跳转语句可以大幅降低代码的执行次数,从而提高程序的执行效率。在设计顺序结构时,可以使用循环和跳转语句来减少重复代码的出现,从而提高代码的效率。
(3)使用宏定义和函数封装
在编写较长的汇编程序时,可以采用宏定义、函数封装等方式来减少代码的重复,并提高代码的重用性。例如,可以将一些常用的操作封装为函数,然后通过调用函数的方式来实现功能,从而减少重复代码的出现。
除了代码执行效率,代码体积的大小也是设计顺序结构时需要考虑的重要问题。具体来说,可以采用以下几种方法:
(1)使用MOVZX和MOVZB等指令
MOVZX和MOVZB等指令可以将数据从一个寄存器或内存地址传递到另一个寄存器中,并在传递的同时把高位置为0。这种指令可以有效地减少代码的体积,提高程序的执行效率。
(2)使用变量的首次引用
在引用一个变量时,需要指定变量的存储地址。如果在后续的代码中重复引用同一变量,可以省略原来的指令,直接使用变量的首次引用进行引用。例如,可以使用LEA指令将数组的首地址存储到一个寄存器中,并在后续的代码中直接使用寄存器来引用数组。
(3)使用指令的别名
汇编指令有许多别名,可以在语句中使用这些别名来减少代码的长度。例如,MOV AX,0可以使用XOR AX,AX等代码替代。
参考代码:
下面是汇编程序的优化版本,其中采用了寄存器存储和循环跳转等方式来提高代码的执行效率,同时采用了MOVZX和MOVZB等指令以及指令的别名等方式来减小代码的体积:
.386 .model flat, stdcall option casemap:none include D:\masm32\include\windows.inc include D:\masm32\include\kernel32.inc include D:\masm32\include\masm32.inc includelib D:\masm32\lib\kernel32.lib includelib D:\masm32\lib\masm32.lib include D:\masm32\include\user32.inc includelib D:\masm32\lib\user32.lib ExitProcess PROTO, dwEXITCODE: DWORD
.data ;待处理文件绝对路径 path BYTE "C:\Users\Em1ya\Desktop\Re\NOTE.exe",0
;存放待处理文件的句柄 filehandle DWORD ?
;以下数据全为显示字符,没有实际含义 str0 BYTE " ",0 str1 BYTE "IMAGE_DOS_HEADER:",0 str2 BYTE "IMAGE_NT_HEADER:",0 str3 BYTE "IMAGE_FILE_HEADE:",0 str4 BYTE "IMAGE_OPTIONAL_HEADER:",0 str5 BYTE "e_magic:",0 str6 BYTE "e_lfanew:",0 str7 BYTE "Signature:",0 str8 BYTE "NumberOfSections:",0 str9 BYTE "TimeDateStamp:",0 stra BYTE "Characteristic:",0 strb BYTE "AddressOfEntryPoint:",0 strc BYTE "ImageBase:",0 strd BYTE "SectionAligment:",0 stre BYTE "FileAligment:",0 strn BYTE 0ah,00h
filebuf BYTE 4000 DUP(0) filebase DWORD ? var DWORD ?
;下面声明的是把十六进制值转为字符串输出的函数 Print PROTO stdcall:DWORD,:DWORD
.code Print PROC res:DWORD,hexdw:DWORD ;参数res待写缓冲区,hexdw待转换十六进制值 ;edi指向待转换的值,ecx为循环变量,eax指向待写的缓冲区字符串 MOV ecx,8h MOV eax,res ;循环将最高位保留做与运算,拿到最低位作为索引在table里面拿值 L2: MOV ebx,hexdw AND ebx,0f0000000h SHR ebx,28 MOV DL,BYTE PTR [table+EBX] MOV BYTE PTR[eax],dl INC eax SHL hexdw,4 LOOP L2
;输出
INVOKE StdOut, res
ret
Print ENDP
main PROC ;通过路径得到句柄,打开程序和文件的联系->通过句柄得到指向文件的指针->通过句柄读取文件内容->关闭文件和程序的连接 INVOKE CreateFile,addr path,\ GENERIC_READ,\ FILE_SHARE_READ,\ 0,\ OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,\ 0 MOV filehandle,eax INVOKE SetFilePointer, filehandle,\ 0,\ 0,\ FILE_BEGIN INVOKE ReadFile, filehandle,\ addr filebuf,\ 3900,\ 0,\ 0 MOV esi,OFFSET filebuf MOV ecx,10d ;以下操作将文件的内容写到了filebuf,之后读取filebuf就可以拿到PE文件的内容 ;以下所有,esi为目标量在内存中的地址,eax为该地址对应值,print函数把eax传入,输出对应8-t个字符串 L1:
TEST ecx,ecx JZ L999 MOV EAX,DWORD PTR [esi] TEST ecx,1 JZ L2 MOVZX EAX,WORD PTR [esi] L2: INVOKE Print,addr Res,\ eax INVOKE StdOut,addr strn ADD esi,4 DEC ecx JMP L1
L999: INVOKE CloseHandle,filehandle INVOKE ExitProcess,0 main ENDP END main