1. win32汇编程序的结构
例子
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Sample code for < Win32ASM Programming 3rd Edition>; by 罗云彬;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Hello.asm; 使用 Win32ASM 写的 Hello, world 程序;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 使用 nmake 或下列命令进行编译和链接:; ml /c /coff Hello.asm; Link /subsystem:windows Hello.obj;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .386 ;指定使用的指令集 .model flat,stdcall ;指定内存模式和语言模式(即程序的调用模模式),影响最后生成的可执行文件 ;stdcall WINDOWS api使用stdcall模式 规定了参数传递的次序 option casemap:none ;定义了程序中的变量和子程序名大小写敏感;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Include 文件定义;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>include windows.incinclude user32.incincludelib user32.libinclude kernel32.incincludelib kernel32.lib;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .dataszCaption db 'A MessageBox !',0szText db 'Hello, World !',0;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 代码段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .codestart: invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK invoke ExitProcess,NULL;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> end start
程序的结构
.386.model flat,stdcalloption casemap:none <一些include语句> .stack [堆栈段 的大小] ;不必定义,系统会自动分配 可读可写可执行 可用于缓冲区溢出.data <一些初始化过的变量定义> ;可读可写的数据,存放在pe文件的_DATA节中.data? <一些没有初始化过的变量定义> ;未定义变量,临时变量在PE文件的_BSS节.const <一些常量定义> ;可读不可写.code ;在PE文件的_TEXT节 不可写的,可通过修改PE头来可写 <代码> <开始标号> <其它语句> end 开始标号 ;指定程序的开始位置,由开始标号指定,被其它程序调用时可不指定入口;;表示 注释 \用来换行 \后面可加注释 其它语句> 开始标号> 代码> 一些常量定义> 一些没有初始化过的变量定义> 一些初始化过的变量定义> 一些include语句>
2. 调用API
在DOS汇编程序中,使用软中断来调用系统api
如打印字符mov ah,9 ;中断的编号 9表示屏幕显示 需要查询中断表mov dx, offset szHelloint 21h
windows api的dll
kernel32.dll 系统服务功能 如内存管理、任务管理和动态链接等gdi32.dll 图形设备功能user32.dll 用户接口功能 建立窗口和传送消息Wsock32.dll TCP/IP协议通信
win32 api使用堆栈来传递参数
可参考 下载地址. 需要winhlp32.exe才能打开,这个工具的win10版本的下载地址调用api
windows api的数据类型可参考下面这篇文章
MessageBox的定义
int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box );
使用汇编调用
伪指令 invoke
invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK
等价于
push uTypepush lpCaptionpush lpTextpush hWndcall MessageBox
api的返回值
返回值 的类型对于汇编程序来说只有dword类型,存放于eax中
若返回值 不能存储在eax中,会将返回值缓冲区的地址存入eax函数 的声明
函数名 proto [距离] [语言] [参数1]:数据类型,[参数2]:数据类型...
MessageBox的声明
MessageBox Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword参数名可省略MessageBox Proto :dword,:dword,:dword,:dword
include语句
语法
include 文件名或 include <文件名>文件名>
include user32.incinclude kernel32.inc
includelib
语法
includelib 文件名或 includelib <文件名>文件名>
include user32.libinclude kernel32.lib
函数的代码放在dll文件中
函数的定位信息和参数数目等信息存放在Lib文件中 链接器和链接时会到指定的库文件中去找api函数的位置信息API参数中的等值定义
在windows api定义了许多常量,包含在windows.inc文件中
需要在汇编程序中包含这个文件include windows.inc
3. 标号 变量和数据结构
MASM变量的命名规范
可以用字母 数字 下划线和@ $ ?第一个符号不能是数字长度小于240不能使用指令名等关键字在作用域中必须唯一
3.1 标号
语法
标号名: 目的指令 ;在当前 的子程序中跳转或标号名:: 目的指令 ;在当前程序中跳转
当用@@作标号时,可以用@F 和@B来引用 @F表示本条指令后的第一个@@标号,@B表示本条指令前的第一个@@标号,程序中可以有多个@@标号
3.2 全局变量
数据类型
只有在定义全局变量时可使用缩写
示例.datawHour dw ? ;word类型未初始化wMinute dw 10 ;word类型变量wMinute 值为10_hWnd dd ? ;双字类型 未初始化word_Buffer dw 100 dup (1,2) ;定义了一个word数组 1,2重复100遍szBuffer byte 1024 dup (?) ;定义了一个1024字节的缓冲区szText db 'Hello World!' ;定义了一个12字节的字符串szText db 'hello world!',0dh,0ah,'jlfajflafj',0dh,0ah,0;byte类型变量定义时,引号定义的字符串和数值定义的方法可混用
未初始化的变量为0
3.3 局部变量
使用栈来管理局部变量
语法local 变量名1[[重复数量][:类型]]
不能使用类型缩写
定义dword类型的变量时,可省略类型 不能使用dup指令 不能与全局变量重名 例子local loc1[1024]:bytelocal loc2local loc3:WNDCLASS
使用
3.4 数据结构
汇编中结构体的定义
引用
mov eax,stWndClass.lpfnWndProc
也可以这样
嵌套定义结构体
movzx指令
sizeof lengthof
sizeof 变量名 类型名或数据结构 名lengthof 变量名 类型名或数据结构名sizeof伪指令可以取变量 数据类型或数据结构以字节为单位的长度lengthof可以取变量中数据的项数
lstrlen用于记录字符串的长度
offset 对于全局变量
用于获取变量的地址 编译时已经确定 对于局部变量是使用ebp来取其地址的,所以不可以用offset来获取它的地址addr
addr 局部变量名和全局变量名 当addr后接全局变量时,编译器会自动按照offset的用法来 当addr后接局部变量时,编译器会自动用lea指令先把地址取到eax中,然后用eax来代替变量地址使用4 使用子程序
4.1 子程序的定义
4.2 参数传递和堆栈平衡
C类型和stdcall类型是先把右边的参数先压入堆栈。
PASCAL类型先把左边的参数压入堆栈 C类型的调用者使用call指令完成后,自行用add esp,8指令将参数空间清除 PASCAL和stdcall则不管这个事,子程序会使用ret 8来实现堆栈平衡 win32 api的调用方式是stdcall5 高级语法
5.1 条件测试
条件测试伪指令会被翻译成cmp test之类的汇编指令
限制 表达式的左边只能是变量或寄存器,不能为常数
表达式的两边不能同时为变量,但可以同时为寄存器 标志位5.2 分支语句
注意.if 和if的区别
5.3 循环语句
伪指令
汇编指令
6 代码风格
6.1 变量和函数的
匈牙利表示法
约定