鱼C论坛

 找回密码
 立即注册
查看: 2807|回复: 1

[技术交流] win32汇编中数据结构的引用

[复制链接]
发表于 2016-1-2 13:21:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
这阵在看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,[esi].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,[403004h],因为lpfnWndProc是stWndClass中的第二个字段,第一个字段是dword,已经占用了4字节的空间。

在实际使用中,常常有使用指针存取数据结构的情况,如果使用esi寄存器做指针寻址,可以使用下列语句完成同样的功能:

mov esi,offset stWndClass

mov eax,[esi + WNDCLASS.lpfnWndProc]

注意:第二句是[esi + WNDCLASS.lpfnWndProc]而不是[esi + stWndClass.lpfnWndProc],因为前者会被编译成mov eax,[esi+4],而后者会被编译成mov eax,[esi+403004h],后者的结果显然是错误的!如果要对一个数据结构中的大量字段进行操作,这种写法显然比较烦琐,MASM还有一个用法,可以用assume伪指令把寄存器预先定义为结构指针,再进行操作:

mov esi,offset stWndClass

assume  esi:ptr WNDCLASS

mov eax,[esi].lpfnWndProc



assume  esi: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,[esi].oldWndClass.lpfnWndProc

罗书原文

人家这段话就是介绍assume的用法 你的问题也是问这个的 为啥不复制这段话呢

而且你的问题相对于0分来说有点多了 呵呵

;**********************************************************************************************************************************************************
追问:
;***********************************************************************************************************************************************************
你老大也不能看分答题啊,心态不好

;*********************************************************************************************************************************************************
追答:
;**********************************************************************************************************************************************************
你工作能不看工资么 不给工资你工作么

这里其实你就问了一个问题 就是assume怎么用

你先知道 assume是masm支持的一个高级的伪指令 一个高级宏 什么是伪指令 就是编译期起作用的东东 自然也不是指令 所以你说的什么覆盖 无稽之谈

区别 什么是指令 什么是伪指令!

其次 这个是干啥的 是为了解决实际问题的 也就是罗书举的那个例子

mov esi,offset stWndClass

mov eax,[esi + WNDCLASS.lpfnWndProc]

第二句是[esi + WNDCLASS.lpfnWndProc]而不是[esi + stWndClass.lpfnWndProc],因为前者会被编译成mov eax,[esi+4],而后者会被编译成mov eax,[esi+403004h],后者的结果显然是错误的!

后面跟着解释 你别说看不懂 看不懂再开帖问

哦 好了 这样的写法复杂 能不能有一种类似C的使用结构体的方法呢 注意啊 这里还得在汇编的框架里 于是 有了assume

这个是伪指令啊 再说一遍 是告诉编译器 我这个寄存器里放的是一个什么类型的东西

assume  esi:ptr WNDCLASS

告诉编译器 esi里放的是 ptr WNDCLASS 这个类型的东西 这个是什么呢 其实ptr在这里起到了C指针中的*的作用(虽然指针这个概念是C里的)

esi是C里的指针变量 ptr WNDCLASS是指针的类型 assume在这里建立了两者的联系 就这么简单

诶 你可能会问 为啥ptr是*呢

ptr在这里做了这样一件事:让mov eax,[esi].oldWndClass.lpfnWndProc被理解为 mov eax,(WNDCLASS ptr [esi]).oldWndClass.lpfnWndProc

至于后面的写法能不能编译通过就不知道了 反正我不用这么诡异的写法 但是道理上是这样的 esi 也就是(WNDCLASS *)喽 [esi] 也就是(WNDCLASS) 你说 ptr是什么?

我不是说了么 上面的写法诡异 而且通用性不强 罗书举的第一种方法写起来麻烦 而且还要知道变量的类型 使用assume呼之欲出啊。。。

最后送你一句Deng的话共勉 不讲多劳多得,不重视物质利益,对少数先进分子可以,对广大群众不行,一段时间可以,长期不行!
追问:
你说对了一半,我是对assume不懂,不懂的地方在于,你说assume  esi: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里 . 不是叫什么作用域符号还是什么 那 前面是啥 作用域就是啥呀 另外 还可以解释 如果不这样 有些问题解决不了 总之这样好。。。 不知道你还有啥要问的。。。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-2-9 08:00:05 | 显示全部楼层
学习,多多指教
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-26 12:17

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表