好友
阅读权限 10
听众
最后登录 1970-1-1
0度
发表于 2013-10-25 09:03
本帖最后由 0度 于 2013-10-25 09:03 编辑
小弟最近在学习win32汇编,经常会把addr 和 offset搞混了,于是专门去搜集了相关信息,发现了一篇不错的文章,就转来了,希望能给也对此分不清的童鞋一点小小的帮助,大牛勿看,影响心情 第一次发帖 俺就散点分吧 虽然分不多 70%的概率 希望大家支持下哈
一、相同点
1、addr 和 offset 操作符都是获得操作数的偏移地址;
2、addr 和 offset 的处理都是先检查处理的是全局还是局部变量,若是全局变量则把其地址放到目标文件中。
二、不同点
1、addr 伪操作符,只能用在 invoke 伪指令语句中,不能用于赋值操作;
2、offset 伪操作符可以用在任何可能涉及偏移地址的指令(当然包括 invoke 伪指令)并想获取操作数偏移地址的场合中;
3、addr 不能处理向前引用(即 addr 引用的操作数必须在使用 addr 前就得定义或声明),而offset 则能(不管引用的操作数是其前或其后定义或声明);
所谓向前引用是指:标号的定义是在invoke 语句之后,比如在如下的例子:
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK //引用MsgBoxText、MsgBoxCaption 在先
......
MsgBoxCaption db "Iczelion Tutorial No.2",0 //定义或声明 MsgBoxCaption 在 addr 后
MsgBoxText db "Win32 Assembly is Great!",0 //定义或声明 MsgBoxText 在 addr 后
如果你是用addr 而不是offset 的话,那MASM就会报错
4、addr 是运行阶段在堆栈中分配内存空间,offset 是编译阶段由编译器解释。因此,addr 可以处理局部变量而 offset 则不能。
5、addr 如果检查到待处理的变量是局部变量,就在执行 invoke 语句前产生如下指令序列:
lea eax,operand
push eax
因为 lea 指令能够在运行时决定标号的有效地址,所以有了上述指令序列,就可以保证invoke的正确执行了。addr 面对全局变量时直接调用offset.
总结:为了避免出现错误,建议除在局部变量中引用 addr 操作符外,其它场合使用 offset。
说明:某些文章中对 addr 和 offset 所引用的对象仅用了“变量或标号”,我是用“操作数”来阐述的,本人的观点是:
变量或标号感觉上包含的概念过窄,比如结构、函数等等,因此,觉得使用操作数好像感觉准确些。
代码说明:
PTR: 指定要操作的数据尺寸
; Test1.asm
.386
.model flat , stdcall
include windows.inc
include kernel32.inc
include masm32.inc
include debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib
.data
val db 11h , 22h , 33h , 44h , 55h , 66h , 77h , 88h
.code
main proc
xor eax , eax ;清空 EAX, 同 mov eax, 0
mov eax , dword ptr val ;
PrintHex eax ;44332211
xor eax , eax ;
mov eax , dword ptr val+1 ;
PrintHex eax ;55443322
xor eax , eax ;
mov ax , word ptr val ;
PrintHex eax ;00002211
xor eax , eax ;
mov al , byte ptr val ;
PrintHex eax ;00000011
ret
main endp
end main
OFFSET: 获取全局变量或标号的偏移地址 ; Test2.asm
.386
.model flat , stdcall
include windows.inc
include kernel32.inc
include masm32.inc
include debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib
.data
v1 db 'abcdefg' , 0
v2 dd 11223344h
.code
main proc
PrintHex offset v1 ;00403000
PrintHex offset v2 ;00403008
PrintHex offset main ;00401000 - 这里的 main 是个标号
ret
;本例中的 offset 不能用 addr 代替
main endp
end main
ADDR: 类似 offset 也是获取变量的地址... ; Test3.asm
.386
.model flat , stdcall
;include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
v1 dd 00434241h ;ABC
v2 dd 00636261h ;abc
.code
main proc
invoke MessageBox, 0 , offset v1, offset v2, 0 ;现在 v1、v2 是全局变量
invoke MessageBox, 0 , addr v2, addr v1, 0 ;使用 offset 和 addr 均可
invoke ExitProcess, 0
main endp
end main
获取局部变量的地址只能使用 ADDR: ; Test4.asm
.386
.model flat , stdcall
;include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.code
main proc
LOCAL v1,v2
mov v1, 00434241h
mov v2, 00636261h
;invoke MessageBox, 0, offset v1, offset v2, 0 ;offset 不能获取局部变量的地址
invoke MessageBox, 0 , addr v2, addr v1, 0
invoke ExitProcess, 0
main endp
end main
THIS: ; Test5.asm
.386
.model flat , stdcall
include windows.inc
include kernel32.inc
include masm32.inc
include debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib
.data
TextAddr equ this byte ;伪指令 this 可让当前变量和下一个变量同址
szText db 'Asm' , 0
.code
main proc
PrintHex offset szText ;00403000
PrintHex offset TextAddr ;00403000
PrintString szText ;Asm
mov [TextAddr], 'a' ;给 TextAddr 赋值
PrintString szText ;asm
ret
main endp
end main