win32汇编中数据结构的引用
{:9_241:} 这阵在看win32汇编的在程序中检测版本信息5.6.2章节中invoke VerQueryValue,addr @dbVerInfo,addr szRoot,addr @lpBuffer,addr @dwLen
mov esi,@lpBuffer
assume esi:ptr VS_FIXEDFILEINFO
所有的不明白正如以下的百度知道问答,值得收藏分享...
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;提问
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
esi,offset swWndClass
assume esi:ptr WNDCLASS
mov eax,.lpfnWndProc
...
assume esi:nothing
1问:上边这段什么意思
2问:mov esi,offset swWndClass这句已经把swWndClass的地址放到了esi中,还要assume esi:ptr WNDCLASS这句再把esi指向WNDCLASS结构干嘛,不等于覆盖了esi的值了吗
3问:为什么WNDCLASS.lpfnWndProc的值是lpfnWndProc字段在WNDCLASS结构中的偏移,而stWndClass.lpfnWndProc的值就会是lpfnWndProc字段在内存中的实际地址
书我当然看了,正是这点不懂我才来这问的,你老大结果又给我复制了一遍,哎。。。
;***************************************************************************************************************************************************
;回答
;****************************************************************************************************************************************************
数据结构实际上是由多个字段组成的数据“样板”,相当于一种自定义的数据类型,数据结构中间的每一个字段可以是字节、字、双字、字符串或所有可能的数据类型。比如在API函数RegisterClass中要使用到一个叫做WNDCLASS的数据结构,Microsoft的手册上是如下定义的:
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
Int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
注意,这是C语言格式的,这个数据结构包含了10个字段,字段的名称是style,lpfnWndProc和cbClsExtra等,前面的UINT和WNDPROC等是这些字段的类型,在汇编中,数据结构的写法如下:
结构名 struct
字段1 类型 ?
字段2 类型 ?
……
结构名 ends
上面的WNDCLASS结构定义用汇编的格式来表示就是:
WNDCLASS struct
Style DWORD ?
LpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
WNDCLASS ends
和大部分的常量一样,几乎所有API所涉及的数据结构在Windows.inc文件中都已经有定义了。要注意的是,定义了数据结构实际上只是定义了一个“样板”,上面的定义语句并不会在哪个段中产生数据,和Word中使用各种“信纸”与“文书”等模板类似,定义了数据结构以后就可以多次在源程序中用这个“样板”当做数据类型来定义数据,使用数据结构在数据段中定义数据的方法如下:
.data?
stWndClass WNDCLASS <>
…
或者:
.data
stWndClass WNDCLASS <1,1,1,1,1,1,1,1,1,1>
……
这个例子定义了一个以WNDCLASS为结构的变量stWndClass,第一段的定义方法是未初始化的定义方法,第二段是在定义的同时指定结构中各字段的初始值,各字段的初始值用逗号隔开,在这个例子中10个字段的初始值都指定为1。
在汇编中,数据结构的引用方法有好几种,以上面的定义为例,如果要使用stWndClass中的lpfnWndProc字段,最直接的办法是:
mov eax,stWndClass.lpfnWndProc
它表示把lpfnWndProc字段的值放入eax中去,假设stWndClass在内存中的地址是403000h,这句指令会被编译成mov eax,,因为lpfnWndProc是stWndClass中的第二个字段,第一个字段是dword,已经占用了4字节的空间。
在实际使用中,常常有使用指针存取数据结构的情况,如果使用esi寄存器做指针寻址,可以使用下列语句完成同样的功能:
mov esi,offset stWndClass
mov eax,
注意:第二句是而不是,因为前者会被编译成mov eax,,而后者会被编译成mov eax,,后者的结果显然是错误的!如果要对一个数据结构中的大量字段进行操作,这种写法显然比较烦琐,MASM还有一个用法,可以用assume伪指令把寄存器预先定义为结构指针,再进行操作:
mov esi,offset stWndClass
assumeesi:ptr WNDCLASS
mov eax,.lpfnWndProc
…
assumeesi:nothing
这样,使用寄存器也可以用逗点引用字段名,程序的可读性比较好。这样的写法在最后编译成可执行程序的时候产生同样的代码。注意:在不再使用esi寄存器做指针的时候要用assume esi:nothing取消定义。
结构的定义也可以嵌套,如果要定义一个新的NEW_WNDCLASS结构,里面包含一个老的WNDCLASS结构和一个新的dwOption字段,那么可以如下定义:
NEW_WNDCLASS struct
DwOption dword ?
OldWndClass WNDCLASS<>
NEW_WNDCLASS ends
假设现在esi是指向一个NEW_WNDCLASS的指针,那么引用里面嵌套的oldWndClass中的lpfnWndProc字段时,就可以用下面的语句:
mov eax,.oldWndClass.lpfnWndProc
罗书原文
人家这段话就是介绍assume的用法 你的问题也是问这个的 为啥不复制这段话呢
而且你的问题相对于0分来说有点多了 呵呵
;**********************************************************************************************************************************************************
追问:
;***********************************************************************************************************************************************************
你老大也不能看分答题啊,心态不好
;*********************************************************************************************************************************************************
追答:
;**********************************************************************************************************************************************************
你工作能不看工资么 不给工资你工作么
这里其实你就问了一个问题 就是assume怎么用
你先知道 assume是masm支持的一个高级的伪指令 一个高级宏 什么是伪指令 就是编译期起作用的东东 自然也不是指令 所以你说的什么覆盖 无稽之谈
区别 什么是指令 什么是伪指令!
其次 这个是干啥的 是为了解决实际问题的 也就是罗书举的那个例子
mov esi,offset stWndClass
mov eax,
第二句是而不是,因为前者会被编译成mov eax,,而后者会被编译成mov eax,,后者的结果显然是错误的!
后面跟着解释 你别说看不懂 看不懂再开帖问
哦 好了 这样的写法复杂 能不能有一种类似C的使用结构体的方法呢 注意啊 这里还得在汇编的框架里 于是 有了assume
这个是伪指令啊 再说一遍 是告诉编译器 我这个寄存器里放的是一个什么类型的东西
assumeesi:ptr WNDCLASS
告诉编译器 esi里放的是 ptr WNDCLASS 这个类型的东西 这个是什么呢 其实ptr在这里起到了C指针中的*的作用(虽然指针这个概念是C里的)
esi是C里的指针变量 ptr WNDCLASS是指针的类型 assume在这里建立了两者的联系 就这么简单
诶 你可能会问 为啥ptr是*呢
ptr在这里做了这样一件事:让mov eax,.oldWndClass.lpfnWndProc被理解为 mov eax,(WNDCLASS ptr ).oldWndClass.lpfnWndProc
至于后面的写法能不能编译通过就不知道了 反正我不用这么诡异的写法 但是道理上是这样的 esi 也就是(WNDCLASS *)喽 也就是(WNDCLASS) 你说 ptr是什么?
我不是说了么 上面的写法诡异 而且通用性不强 罗书举的第一种方法写起来麻烦 而且还要知道变量的类型 使用assume呼之欲出啊。。。
最后送你一句Deng的话共勉 不讲多劳多得,不重视物质利益,对少数先进分子可以,对广大群众不行,一段时间可以,长期不行!
追问:
你说对了一半,我是对assume不懂,不懂的地方在于,你说assumeesi:ptr WNDCLASS之后,esi就成了一个指针,一个指向WNDCLASS结构的指针,指针指向一个变量,存放变量的地址这我能理解,但让指针指向一个结构体,那它存放什么呢?
另外,第3个问题你好像一直避而不答哎
最后,难得碰见一个像你这么有耐心的人,所以我也就更执著了点。。。
追答:
1 我知道多少说多少 说不明白也就没法了 你不能奢望我给你完全讲明白 是罢 插一句 有些人还说 有的人就是理解不了指针
2 你敢执著我不怕 接1呀 我解释的不好也没什么 大不了你关闭问题
3 接上面的解释接着说
assume esi:ptr WNDCLASS之后,esi就成了一个指针——确切的说 是被编译器理解为一个指定类型的指针 这里变的是它的类型 而esi成为指针 或者说里面放的是地址 是由mov指令导致的 我反复强调的就是 这个assume是伪指令 改的是编译器对代码的理解 无法改变esi的值 自然无法影响它是不是指针 或者说是里面存的是不是地址 直白一点就是这个是指针已经是事实了 如果不用assume 也可以用它操作相应变量 就是麻烦点
指针指向一个变量,存放变量的地址这我能理解,但让指针指向一个结构体,那它存放什么呢?——很有意思 你前面理解了指针的概念 诶 下面又问存放的是什么 这什么意思呢
那它存放什么呢? 存放的是结构体的第一个字节的地址 和变量是一样的 指向变量的指针存的不是变量第一个字节的地址?
这里穿插C里的概念 或许说这个比较好懂 指针 或者具体是指针变量 【本身是一个变量】 它的内容是【某个变量】的地址 它的功能是对前面说的某个变量及其相关变量进行操作 C的指针是这么回事么 那对于结构体 指针变量指向的是第一个内存单元(就是字节)的地址呗 和指向变量不是一样么 至于编译器具体怎么理解 怎么编译 也就是 pa是指针 (*pa)是操作了一个int还是一个double(也就是pa这个指针变量中存放地址后面多少个字节的内容) 在C里可以用强制转换或者是根据定义来决定 同样了 汇编里可以用上面的方法来改 只不过这里assume只用于结构体而已 因为其他的用汇编的伪指令可以实现 assume是创新 仅此而已
3问:为什么WNDCLASS.lpfnWndProc的值是lpfnWndProc字段在WNDCLASS结构中的偏移,而stWndClass.lpfnWndProc的值就会是lpfnWndProc字段在内存中的实际地址 ——顾左右而言他的原因有两个 一个是 不知道 一个是 无需知道原因 如果非要解释就是 这是汇编语言的规定 如果再要解释就是 第一个是 WNDCLASS. 第二个是 stWndClass. C里 . 不是叫什么作用域符号还是什么 那 前面是啥 作用域就是啥呀 另外 还可以解释 如果不这样 有些问题解决不了 总之这样好。。。 不知道你还有啥要问的。。。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
学习,多多指教
页:
[1]