win32汇编中的sizeof
win32汇编中的sizeof不同于其它语言的sizeof ,这个是真正的sizeof,以字节为单位的。
看下例
szhello db 'hello,world!',0
mov eax,sizeof szhello
eax=?
答案是
eax=13
因为hello,world!为13个字节,然后0占一个,所以是13
看下例
szhello db 'hello,world!',0
mov eax,sizeof szhello
eax=?
答案是
eax=13
因为hello,world!为13个字节,然后0占一个,所以是13
变量命名风格
前缀 含义
b byte
w word
dw dword
h 句柄
lp 指针
sz 以0结尾的字符串
lpsz 指向以0结尾的字符串的指针
f 表示浮点数
st 表示一个数据结构
全局变量下面的前缀为_ 局部变量的前缀为@
b byte
w word
dw dword
h 句柄
lp 指针
sz 以0结尾的字符串
lpsz 指向以0结尾的字符串的指针
f 表示浮点数
st 表示一个数据结构
全局变量下面的前缀为_ 局部变量的前缀为@
enter与leave
ENTER是建立当前函数的栈框架,即相当于以下两条指令:
pushl %ebp
movl %esp,%ebp
8)LEAVE是释放当前函数或者过程的栈框架,即相当于以下两条指令:
movl ebpesp
popl ebp
如果反汇编一个函数,很多时候会在函数进入和返回处,发现有类似如下形式的汇编语句:
pushl %ebp ; ebp寄存器内容压栈,即保存main函数的上级调用函数的栈基地址
movl %esp,%ebp ; esp值赋给ebp, 设置 main函数的栈基址
........... ; 以上两条指令相当于 enter0,0
...........
leave ; 将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
ret ; main函数返回,回到上级调用
这些语句就是用来创建和释放一个函数或者过程的栈框架的。
原来编译器会自动在函数入口和出口处插入创建和释放栈框架的语句。
函数被调用时:
1) EIP/EBP成为新函数栈的边界
函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界
2) EBP成为栈框架指针SFP,用来指示新函数栈的边界
栈框架建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现backtrace功能的
3) ESP总是作为栈指针指向栈顶,用来分配栈空间
栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数值,例如,分配一个整型数据就是 ESP-4
4) 函数的参数传递和局部变量访问可以通过SFP即EBP来实现
由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
+8+xx(%ebp) ; 函数入口参数的的访问
-xx(%ebp) ; 函数局部变量访问
pushl %ebp
movl %esp,%ebp
8)LEAVE是释放当前函数或者过程的栈框架,即相当于以下两条指令:
movl ebpesp
popl ebp
如果反汇编一个函数,很多时候会在函数进入和返回处,发现有类似如下形式的汇编语句:
pushl %ebp ; ebp寄存器内容压栈,即保存main函数的上级调用函数的栈基地址
movl %esp,%ebp ; esp值赋给ebp, 设置 main函数的栈基址
........... ; 以上两条指令相当于 enter0,0
...........
leave ; 将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
ret ; main函数返回,回到上级调用
这些语句就是用来创建和释放一个函数或者过程的栈框架的。
原来编译器会自动在函数入口和出口处插入创建和释放栈框架的语句。
函数被调用时:
1) EIP/EBP成为新函数栈的边界
函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界
2) EBP成为栈框架指针SFP,用来指示新函数栈的边界
栈框架建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现backtrace功能的
3) ESP总是作为栈指针指向栈顶,用来分配栈空间
栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数值,例如,分配一个整型数据就是 ESP-4
4) 函数的参数传递和局部变量访问可以通过SFP即EBP来实现
由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
+8+xx(%ebp) ; 函数入口参数的的访问
-xx(%ebp) ; 函数局部变量访问
80x86堆栈的增长及PUSH和POP
1、堆栈向地址减小的方向增长
2、PUSH后,先压入堆栈,再减少ESP值;POP与之相反。
3、ESP指向堆栈顶的那个值,而不是堆栈的下一个空白处
通用寄存器的目的
1、EAX和AX:累加器,所有的I/O指令用它来与外部设备传送信息
2、EBX和BX:在计算存储单元地址时常用作基地址寄存器
3、ECX和CX:保存计数值
4、EDX和DX:做四字或二字运算时,可以把EDX(DX)和EAX(AX)组合在一起存放一个四字或二字长的数据,在对某些I/O操作时,DX可以放I/O的端口地址
5、ESP和SP:堆栈栈顶指针。
6、EBP和BP:基址寄存器
7、ESI和SI:源变址
8、EDI和DI:目的变址
invoke语句
invoke既可以调用WINDOWS API,也可以调用汇编子程序。
格式为invoke 程序名,参数1,参数2,。。。。
参数2先入堆栈,参数1再入堆栈,以此类推
如invoke mysubpro,eax,ecx
编译器会编译成下面这个模样:
push ecx
push eax
call mysubpro
格式为invoke 程序名,参数1,参数2,。。。。
参数2先入堆栈,参数1再入堆栈,以此类推
如invoke mysubpro,eax,ecx
编译器会编译成下面这个模样:
push ecx
push eax
call mysubpro
类似于高级语言的条件选择语句,
.if 条件1
语句1
.else 条件2
语句2
....
.else
语句3
.endif
但实际编译后
1、对if ebx
2、对if eax 会翻译成or eax,eax
je 0040100c如果eax为0,则该条件不满足,跳转到下一条语句或下个条件判断,0040100c是举例,即下个语句的地址。
.if 条件1
语句1
.else 条件2
语句2
....
.else
语句3
.endif
但实际编译后
1、对if ebx
2、对if eax 会翻译成or eax,eax
je 0040100c如果eax为0,则该条件不满足,跳转到下一条语句或下个条件判断,0040100c是举例,即下个语句的地址。
循环语句
.while 条件
..........
..........
[.break[.if 退出条件]]
[.contine]
.endw
.repeat
...........
..........
[.break[.if 退出条件]]
[.contine]
.until 条件(或.untilcxz [条件])
标号与变量
1、标号
@@:标号
@F:前面一个标号
@B:后面一个标号
2、全局变量定义在.data和.data?内
3、局部变量用local指令定义
local 变量名1:类型,变量名2:类型
数据结构
1、声明
wndclass struct
....
.....
......
wndclass ends
2、定义
mystruct wndclass<1,1,...,1>
mystruct wndclass <>
3、使用
mov eax,mystruct.lpfnwndproc
mov esi,offset mystruct
assume esi: ptr WNDCLASS
mov eax,[esi].lpfnwndproc
.......
assume esi:nothing
变量使用
1、
mov eax,dword prt 变量名
2、
sizeof:变量、数据类型或数据结构以字节为单位的长度。
lengthof:取得变量中数据的项数。
3、
offset:取变量地址的伪操作符,在编译时完成
addr:在运行时完成取地址
子程序
1、定义
子程序名 proc [距离][语言类型][可视区域][USERS 寄存器列表][,参数:类型]...[VARARG]local 局部变量列表
..............
...............
...............
子程序名 endp
2、如果在未定义前使用,要声明、
函数名 proto [距离][语言][参数1]:数据类型,[参数2]:数据类型,...............
保护模式下的段的寻址
一、虚拟地址为:
XXXX:YYYYYYYYYYY
二、16位的段寄存器只有高13位表示索引值,剩下的3个数据位中,第0、1位表示程序的当前优先级,第2位TI位用来表示在段描述符的位置;TI=0表示在GDT中,TI=1表示在LDT中。
三、
1、首先要看XXXX的TI位是否为0,如果是的话,则先从GDTR寄存器中获取GDT的地址,然后,在GDT中根据段寄存器的索引值来等到段描述符,从而得到段的起始地址。
2、如果XXXX的TI位为1,表示段寄存器存放的是LDT中的段,首先从GDTR寄存器中获取GDT的地址,然后从LDTR中获得LDT在GDT中的索引值,以这个位置索引在GDT中得到LDT段的位置,最后使用表示索引值的高13位来在LDT中寻找