tagWnd在win7是公开的 在windbg中输入dt tagWnd是可以看到完整结构体的.
So,这个就不讨论win7了.虽然win10 win32k大改了,但是还是参考的.
先看下win7的结构体
可以看到,大部分的信息都可以直接看到的.
好的,那定一个目标,最后实现的结果是什么样的?
1.支持win10 14393-win10 17763 x64 只弄下x64, x86原理一致
2.获取窗口的句柄,tagWnd,窗口标题,窗口类名信息,下一个窗口,上一个窗口,父窗口,RECT,以及样式
Ojbk!开整!
回想一下,5*16*16+2*16+2=1282+32=1314,522是16进制的1314,黑客情人节啊!
额...不好意思,突然脑抽想到了这个.正经的
第一步:
我们需要得到所有的tagWnd.嗯 year.怎么得到tagWnd呢?
稍微了解一点子系统的小哥哥小姐姐就知道win32k!ValidateHwndEx返回tagWnd. So,We need to see see it!
怎么得到所有的tagWnd呢?啊哈哈哈哈哈哈哈.see later.
第二步:
得到偏移,取值
得到了tagWnd,就是找偏移,取值了,这些偏移怎么搞到?
想一下:5*16*16+2*16+2=1282+32=1314,522是16进制的1314,黑客情人节啊!
.....额...看win32k相关api的实现啊.
就两步呗,那ojbk啊.
First,We need IDA!打开我从某解下载的泄露版ida !O shit,i love ida.
先从简单的搞起,看下7601 win32k!ValidateHwnd:
为什么说[rdi+0x10]可以看出rdi就是tagWnd呢?
看下公开的结构体就知道了,都对的上,标题,RECT,句柄,啥的都是ojbk的.
Ojbk!
So,总结一下:
Win7的tagWnd计算公式:
[hwnd->cx*[gSharedInfo+0x10]] + [gSharedInfo+0x8]]
到这里,我们可以猜想,如何枚举所有窗口的tagWnd呢?
1.公式的唯一变数就是hwnd的低16位,那么0~0xffff就是所有的啦
2.tagWnd中记录了上一个窗口和下一个窗口,并且打开彗星小助手或者spy++,会发现总窗口(最大的那个窗口hwnd是固定的,就是0x10010),通过把0x10010转换为tagWnd,然后通过下一个窗口遍历即可.
Ojbk!
搞win10!
先搞一个14393的!
ida打开win32kbase!ValidateHwndEx
大概看下 公式也没变,只是部分偏移改变了
So,
14393:[hwnd->cx*[gSharedInfo+0x10]] + [gSharedInfo+0x8]]
鉴于篇幅,还有写此文章时的电脑并非调试机,就简单说下
Win10:
14393的计算公式同win7
>= 15063 的计算公式都改变了
上面说过了>=15063的公式是改变的
下面就选择16299的win32kbase.sys来看
同样的,也是看win32kbase!ValidateHwndEx
只是基数不同
So,总结一下:
7601:[hwnd->cx*[gSharedInfo+0x10]] + [gSharedInfo+0x8]]
14393:同7601
15063:[[gpKernelHandleTable] + 0x10 * ((hwnd->cx * [gSharedInfo+0x10]) >> 5)]
16299:[[gpKernelHandleTable] + 0x18 * ((hwnd->cx * [gSharedInfo+0x10]) >> 5)]
17134:[[gpKernelHandleTable] + 0x18 * ((hwnd->cx * [gSharedInfo+0x10]) >> 5)]
17763:[[gpKernelHandleTable] + 0x18 * ((hwnd->cx * [gSharedInfo+0x10]) >> 5)]
Win10中的gpKernelHandleTable和gSharedInfo是导出的.So
到这里,我们已经可以得到所有tagWnd了(1903还没看),那么下面就是去tagWnd获取信息了.
我们需要以下信息:
1.窗口hwnd(好废话)
2.窗口标题
3.窗口类名
4.窗口RECT
5.窗口的样式
6.窗口的进程信息
7.窗口的关系
提一下:应用层与驱动层都有tagWnd结构,在7601-16299 结构体是相同的
但>16299是不同的,也就是说在>16299的win10中有两个tagWnd,一个是用户层的,一个是内核的,虽然名字一样(看符号),但是内容不一样,用户层获取用户层的tagWnd最简单的就是调用一下NtUserCallOneParam(hwnd, xxx),这个xxx不同的build都不同,最后都是调用到内核的MapDesktopObject.
鉴于不可抗拒的原因,这里只列出内核的tagWnd
这个是最简单的,tagWnd的前8个字节就是hwnd.
联想一下:5*16*16+2*16+2=1282+32........
好的,继续,窗口标题,看下win32kfull!NtUserFindWindowEx
鉴于篇幅,这里只看下14393的,其他一样.
NtUserFindWindowEx->_FindWindow
其中0xe0就是偏移(wchar*) 其实d8就是一个UNICODE_STRING
Ojbk,其他的这里总结下:
strName:
7601:0xe0
14393:0xe0
15063:0xf0
16299:0xf0
17134:0xa8
17763:0xa8
窗口类名并没有直接保存在tagWNd,而是通过索引去查Atom表
这里就只说下索引
看win32kfull!NtUserGetClassName(15063)
或者
窗口标题那张图的上一句
在调试过程中并没有走到v12!=0x19里面
其中Index就是索引
总结一下:
14393:WORD[[tagWnd+0x98] + 0xA]
15063:WORD[[tagWnd+0xA8] + 0xA]
16299:WORD[[tagWnd+0xA8] + 0xA]
17134:WORD[[[tagWnd+0x70] + 8] + 2]
17763:WORD[[[tagWnd+0x70] + 8] + 2]
有两个RECT,一个是RcWindow,一个是Rcclient
是一个tagRect结构
RcClient的上8个字节就是RcWindow
看下win32kfull!xxxGetUpdateRect(15063)
0x90指向一个tagRect结构,而0x90-8则指向RcWindow
RcClient:
14393:[tagWnd+0x80] hrgnUpdate=0xA0
15063:[tagWnd+0x90] hrgnUpdate=0xB0
16299:[tagWnd+0x90] hrgnUpdate=0xB0
17134:[[tagWnd+0x28]+0x68] hrgnUpdate=[[tagWnd+0x28]+0x88]
17763:[[tagWnd+0x28]+0x68] hrgnUpdate=[[tagWnd+0x28]+0x88]
RcWindow:
偏移-8
17134:[[tagWnd+0x28]+0x60] hrgnUpdate=[[tagWnd+0x28]+0x88]
17763:[[tagWnd+0x28]+0x60] hrgnUpdate=[[tagWnd+0x28]+0x88]
内核中共有3个样式
1. Style
2. ExStyle
3. ExStyle2
怎么找?
Win32kfull!xxxSetWindowStyle(15063)
ExStyle2往下找UnsetLayeredWindow的上一句
这里的样式并不是用户层spy++或者彗星小助手显示的样式格式,需要转换,这里就不进行转换了.
14393:
Style:0x34 DWORD
ExStyle:0x30 DWORD
ExStyle2:0x120 DWORD
15063:
Style:0x44 DWORD
ExStyle:0x40 DWORD
ExStyle2:0x130 DWORD
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!