能力值:
( LV6,RANK:90 )
2 楼
名称:win32子系统之二:EnterSharedCrit函数分析
作者:mengwuji
时间:2013.5.28 12:35
链接:http://mengwuji.net/forum.php?mod=viewthread&tid=75&extra=page%3D1
上一篇我们说过用户层相关的一些东西,这一篇起我们转入内核分析。下面进入正题:
ULONG NtUserQueryWindow(HWND hWnd,ULONG Flag);
win32k!NtUserQueryWindow:
92dbe20a mov edi,edi
92dbe20c push ebp
92dbe20d mov ebp,esp
92dbe20f push esi
92dbe210 push edi
//win32子系统的系统服务在执行时都会在首部调用EnterSharedCrit和在尾部调用UserSessionSwitchLeaveCrit,因为频繁出现,所以我们有必要研究下它们到底是做什么的(在开始就遇到艰巨的任务了!)。函数见下面分析。
92dbe211 call win32k!EnterSharedCrit (92dd3141)
.................. win32k!EnterSharedCrit:
92dd3141 push dword ptr [win32k!gpresUser (92f226ac)] //未知的一个全局变量
//调用92ef4270偏移量为零的值作为函数地址,这个地址是81e9d3f4
92dd3147 call dword ptr [win32k!_imp__ExEnterPriorityRegionAndAcquireResourceShared (92ef4270)]
92dd314d ret
nt!ExEnterPriorityRegionAndAcquireResourceShared:
81e9d3f4 mov edi,edi
81e9d3f6 push ebp
81e9d3f7 mov ebp,esp
81e9d3f9 push esi
81e9d3fa mov esi,dword ptr fs:[124h] //fs在内核中是_kpcr结构,+0x124偏移是当前线程的ETHREAD指针
81e9d401 inc byte ptr [esi+28Bh] //+0x28B是PriorityRegionActive,表示线程的活动状态
81e9d407 dec word ptr [esi+84h] //+0x84是CombinedApcDisable,关于一个apc相关的计数值
81e9d40e push 1 //应该是一个标记
81e9d410 push dword ptr [ebp+8] //[gpresUser]
//下面函数接收两个参数,执行完成后返回值设置为当前线程的Win32Thread成员,为了一探究竟,我们继续深入的看看这个函数会做些什么;当然,如果不关心它就认为EnterSharedCrit执行结束后,返回值就表示当前线程的的Win32Thread成员就可以了
81e9d413 call nt!ExAcquireResourceSharedLite (81e4ad3c)
81e9d418 mov eax,dword ptr [esi+18Ch]
81e9d41e pop esi
81e9d41f pop ebp
81e9d420 ret 4 (声明一下,首先分析完这个函数时才发现这个函数在WRK中有了(岂不是白白让我浪费时间了!)~~~~(>_<)~~~~ 不过wrk中的没这个这么复杂)
nt!ExAcquireResourceSharedLite:
81e4ad3c mov edi,edi
81e4ad3e push ebp
81e4ad3f mov ebp,esp
81e4ad41 and esp,0FFFFFFF8h
81e4ad44 sub esp,24h
81e4ad47 push ebx
81e4ad48 push esi
//第一个参数是[gpresUser]
81e4ad49 mov esi,dword ptr [ebp+8]
81e4ad4c push edi
//fs:[20h]是_KPRCB结构的指针,这个结构是与当前处理器控制块关联的,里面的信息量庞大到让人头痛的地步
81e4ad4d mov edi,dword ptr fs:[20h]
//_KPRCB结构的35ACh的偏移位置是ExAcqResSharedAttempts,这个值维和了当前处理器尝试请求资源共享的计数
81e4ad54 inc dword ptr [edi+35ACh]
//PerfGlobalGroupMask是全局组掩码,用来提供WMI组件相关的支持
81e4ad5a mov ebx,dword ptr [nt!PerfGlobalGroupMask+0x4 (81f49684)]
//eax=当前线程的ethread
81e4ad60 mov eax,dword ptr fs:[00000124h]
//对得到的一个掩码值进行操作
81e4ad66 shr ebx,11h
//得到第17位的值
81e4ad69 and bl,1
//因为是提供给KeAcquireInStackQueuedSpinLock函数调用的,所以esi+0x34h是个KSPIN_LOCK类型成员,从这里得到一个重要信息,就是esi对应的这个结构我们知道+34h是个KSPIN_LOCK类型,那么通过解析win32.sys的符号链接,一个_ERESOURCE结构体让我重视起来
81e4ad6c lea ecx,[esi+34h]
//edx是表示输出参数,是KLOCK_QUEUE_HANDLE类型
81e4ad6f lea edx,[esp+18h]
//以下两句是保存一些信息
81e4ad73 mov byte ptr [esp+0Eh],bl
//保存线程对象到临时变量
81e4ad77 mov dword ptr [esp+10h],eax
//VOID KeAcquireInStackQueuedSpinLock(_Inout_ PKSPIN_LOCK SpinLock,_Out_ PKLOCK_QUEUE_HANDLE LockHandle);这是函数的原型,其中ecx是第一个参数,edx是第二个参数,这函数是获取一个自旋锁
81e4ad7b call dword ptr [nt!_imp_KeAcquireInStackQueuedSpinLock (81e14144)]
//直接跳到下面
81e4ad81 jmp nt!ExAcquireResourceSharedLite+0xd2 (81e4ae0e)
//+e是Flag,比较这个标记
81e4ad86 test byte ptr [esi+0Eh],80h
根据这个标记&80得到的为真值,就不会跳,否则跳
81e4ad8a je nt!ExAcquireResourceSharedLite+0x75 (81e4adb1)
//eax得到当前线程对象
81e4ad8c mov eax,dword ptr [esp+10h]
//比较当前线程对象和_RESOURCE的OwnerEntry,这个OwnerEntry第一个成员实际也是个线程对象
81e4ad90 cmp dword ptr [esi+18h],eax
//相等就跳转
81e4ad93 je nt!ExAcquireResourceSharedLite+0x116 (81e4ae52)
//调用KeAcquireInStackQueuedSpinLock的输出参数
81e4ad99 lea eax,[esp+18h]
//压入堆栈
81e4ad9d push eax
//eax保存_RESOUCE(寄存器传参)
81e4ad9e mov eax,esi
//调用ExpFindEmptyEntry寻找一个空闲的进入点
81e4ada0 call nt!ExpFindEmptyEntry (81e48f96)
81e4ada5 mov ebx,eax
81e4ada7 test ebx,ebx
//如果没有寻找到就跳转
81e4ada9 je nt!ExAcquireResourceSharedLite+0xce (81e4ae0a)
//寻找到的话,eax保存当前线程对象后跳转
81e4adab mov eax,dword ptr [esp+10h]
81e4adaf jmp nt!ExAcquireResourceSharedLite+0xaf (81e4adeb)
81e4adb1 xor eax,eax
//+2c是NumberOfExclusiveWaiters,是个计数,但目前的信息不能推测出来具体是什么
81e4adb3 cmp dword ptr [esi+2Ch],eax
//根据+2c是否为零来决定al的值
81e4adb6 setne al
//作为ExpFindCurrentThread的一个参数,侥幸的去wrk中尝试获取这个函数的信息;惊奇的发现wrk包括了对于"resource"的很多信息,那么我们肯定好充分的利用好这个便利了!因为我用的win7系统,所以这个函数不是wrk中的三个参数了。在win7下面它是四个参数
81e4adb9 push eax
//esp+18h来保存这个值(如果是ebp来定位的话就方便多了!)
81e4adba mov dword ptr [esp+18h],eax
//这里eax=esp+0x1Ch,经过转换后,实际也就是上面调用KeAcquireInStackQueuedSpinLock后输出的第二个参数
81e4adbe lea eax,[esp+1Ch]
//传入这个值
81e4adc2 push eax
//这里的esp+18通过计算是上面保存线程对象的临时变量
81e4adc3 push dword ptr [esp+18h]
//esi是_RESOURCE结构,我们也要通过寄存器的方式传入这个参数
81e4adc7 mov eax,esi
//好吧,这里ExpFindCurrentThread参数只比wrk中的多出最后一个,最后一个是个bool值,这里我们忽略这个函数的分析(避免增加复杂度,我们的初衷不能在这点小问题上耗费时间,对于它,我们只要知道返回什么东西就可以了)。
81e4adc9 call nt!ExpFindCurrentThread (81eb42ba)
//参考wrk,知道这个函数返回一个POWNER_ENTRY指针,然后给ebx进行保存
81e4adce mov ebx,eax
81e4add0 test ebx,ebx
//如果返回为零(寻找失败?),这里会跳到下面,下面会重新跳到上面来重复此过程
81e4add2 je nt!ExAcquireResourceSharedLite+0xce (81e4ae0a)
//到这里说明寻找成功了,那么进行一些验证;esp+0x10是我们保存过的当前线程对象。而_OWNER_ENTRY的第一个成员是OwnerThread
81e4add4 mov eax,dword ptr [esp+10h]
//比较返回的结构是否是本线程相关的
81e4add8 cmp dword ptr [ebx],eax
//如果是和本线程相关的那么就跳下去执行其他操作
81e4adda je nt!ExAcquireResourceSharedLite+0x15c (81e4ae98)
//这里的esp+0x14在上面保存过,是根据_RESOURCE的NumberOfExclusiveWaiters真假来决定的一个bool值
81e4ade0 cmp dword ptr [esp+14h],0
//这里直接为零直接跳到下面
81e4ade5 je nt!ExAcquireResourceSharedLite+0x1a5 (81e4aee1)
//第二个参数的值来决定是否跳转,因为传入的是1,所以这里不会跳转
81e4adeb cmp byte ptr [ebp+0Ch],0
81e4adef je nt!ExAcquireResourceSharedLite+0x1d4 (81e4af10)
//_ERESOURCE+10h是SharedWaiters,这是个_KSEMAPHORE结构指针类型。判断它是否为零,不为零跳
81e4adf5 cmp dword ptr [esi+10h],0
81e4adf9 jne nt!ExAcquireResourceSharedLite+0x1eb (81e4af27)
//eax是上面调用KeAcquireInStackQueuedSpinLock后输出的第二个参数
81e4adff lea eax,[esp+18h]
//把它压入堆栈
81e4ae03 push eax
//把_RESOURCE压入堆栈
81e4ae04 push esi
//调用ExpAllocateSharedWaiterSemaphore函数,这个函数可以简单理解为分配一个资源
81e4ae05 call nt!ExpAllocateSharedWaiterSemaphore (81e41397)
//esp+E是个全局掩码得到的值
81e4ae0a mov bl,byte ptr [esp+0Eh]
//现在假设esi对应的是_ERESOURCE结构,这个结构的+20h是ActiveEntries,可能表示当前获取到资源的一个关联值,如果这个值不为空应该是我们还不能立即获取它,要等到它为空我们才有获取的权利
81e4ae0e cmp dword ptr [esi+20h],0
//这里是往回跳
81e4ae12 jne nt!ExAcquireResourceSharedLite+0x4a (81e4ad86)
//eax=当前线程对象
81e4ae18 mov eax,dword ptr [esp+10h]
//_RESOURE的OwnerEntry的OwnerThread赋值为当前线程
81e4ae1c mov dword ptr [esi+18h],eax
//esi+0x1c多值组合。
81e4ae1f mov eax,dword ptr [esi+1Ch]
//这里得到低两位
81e4ae22 and eax,3
//第三位值一
81e4ae25 or eax,4
//eax重新赋值给esi+0x1c
81e4ae28 mov dword ptr [esi+1Ch],eax
81e4ae2b xor eax,eax
81e4ae2d inc eax
//ecx得到调用KeAcquireInStackQueuedSpinLock的返回值
81e4ae2e lea ecx,[esp+18h]
//ActiveEntries赋值为1
81e4ae32 mov dword ptr [esi+20h],eax
//ActiveCount赋值为1,实际上面这几句都是把新的_ERESOURCE初始化
81e4ae35 mov word ptr [esi+0Ch],ax
//释放自旋锁
81e4ae39 call dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
//edi是_kprcb,35b4是ExAcqResSharedAcquiresShared
81e4ae3f inc dword ptr [edi+35B4h]
//3584是ExecutiveResourceAcquiresCount
81e4ae45 inc dword ptr [edi+3584h]
81e4ae4b test bl,bl
//跳,这里是等于退出了
81e4ae4d jmp nt!ExAcquireResourceSharedLite+0x2b2 (81e4afee)
81e4ae52 mov eax,dword ptr [esi+1Ch]
81e4ae55 mov ecx,eax
//实际就是得到OwnerEntry的OwnerCount
81e4ae57 and ecx,0FFFFFFFCh
81e4ae5a add ecx,4
81e4ae5d and eax,3
81e4ae60 xor ecx,eax
//经过上述运算后重新给会TableSize
81e4ae62 mov dword ptr [esi+1Ch],ecx
81e4ae65 lea ecx,[esp+18h]
//释放自旋锁
81e4ae69 call dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
//上面解释过了
81e4ae6f inc dword ptr [edi+35B0h]
81e4ae75 inc dword ptr [edi+3584h]
81e4ae7b test bl,bl
//根据全局掩码进行相应跳转
81e4ae7d je nt!ExAcquireResourceSharedLite+0x2c4 (81e4b000)
//ContentionCount
81e4ae83 push dword ptr [esi+24h]
//eax=TableSize
81e4ae86 mov eax,dword ptr [esi+1Ch]
81e4ae89 shr eax,2
81e4ae8c push eax
81e4ae8d push esi
81e4ae8e push 10031h
81e4ae93 jmp nt!ExAcquireResourceSharedLite+0x2bf (81e4affb)
//ebx是OwnerEntry,+4是TableSize
81e4ae98 mov eax,dword ptr [ebx+4]
81e4ae9b mov ecx,eax
81e4ae9d and ecx,0FFFFFFFCh
81e4aea0 add ecx,4
81e4aea3 and eax,3
81e4aea6 xor ecx,eax
//上面是对OwnerCount进行一些运算,然后把运算后的值重新写入回去
81e4aea8 mov dword ptr [ebx+4],ecx
//KeAcquireInStackQueuedSpinLock的输出参数
81e4aeab lea ecx,[esp+18h]
//释放自旋锁
81e4aeaf call dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
//上面讲解过了
81e4aeb5 inc dword ptr [edi+35B8h]
81e4aebb inc dword ptr [edi+3584h]
//比较全局掩码相关
81e4aec1 cmp byte ptr [esp+0Eh],0
//为零就退出
81e4aec6 je nt!ExAcquireResourceSharedLite+0x2c4 (81e4b000)
//ContentionCount
81e4aecc push dword ptr [esi+24h]
81e4aecf mov eax,dword ptr [ebx+4]
81e4aed2 shr eax,2
81e4aed5 push eax
81e4aed6 push esi
81e4aed7 push 10051h
81e4aedc jmp nt!ExAcquireResourceSharedLite+0x2bf (81e4affb)
//eax是当前线程对象,写入到OwnerEntry的OwnerThread成员里
81e4aee1 mov dword ptr [ebx],eax
81e4aee3 mov eax,dword ptr [ebx+4]
81e4aee6 and eax,3
81e4aee9 or eax,4
//得到OwnerEntry的IoPriorityBoosted和OwnerReferenced以及第三位值1,然后把值写入回去
81e4aeec mov dword ptr [ebx+4],eax
//eax = ActiveEntries
81e4aeef mov eax,dword ptr [esi+20h]
81e4aef2 test eax,eax
//ActiveEntries不为零跳转
81e4aef4 jne nt!ExAcquireResourceSharedLite+0x1c1 (81e4aefd)
81e4aef6 inc eax
//ActiveEntries+1后写入到ActiveCount
81e4aef7 mov word ptr [esi+0Ch],ax
81e4aefb jmp nt!ExAcquireResourceSharedLite+0x1c2 (81e4aefe)
81e4aefd inc eax
81e4aefe lea ecx,[esp+18h]
//加1写入回ActiveEntries,并且释放自旋锁
81e4af02 mov dword ptr [esi+20h],eax
81e4af05 call dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
81e4af0b jmp nt!ExAcquireResourceSharedLite+0x2a1 (81e4afdd)
81e4af10 lea ecx,[esp+18h]
//释放自旋锁
81e4af14 call dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
//ExAcqResSharedNotAcquires++
81e4af1a inc dword ptr [edi+35C0h]
81e4af20 xor al,al
81e4af22 jmp nt!ExAcquireResourceSharedLite+0x2c6 (81e4b002)
//当前线程对象写入OwnerThread
81e4af27 mov dword ptr [ebx],eax
81e4af29 mov eax,dword ptr [ebx+4]
81e4af2c and eax,3
81e4af2f or eax,4
81e4af32 mov dword ptr [ebx+4],eax
//上面在这个函数中见的很多,以后不解析;下面把ebx赋值为KeReleaseInStackQueuedSpinLock,好在后面调用
81e4af35 mov ebx,dword ptr [nt!_imp_KeReleaseInStackQueuedSpinLock (81e14140)]
//NumberOfSharedWaiters++
81e4af3b inc dword ptr [esi+28h]
81e4af3e lea ecx,[esp+18h]
//释放自旋锁
81e4af42 call ebx
81e4af44 inc dword ptr [edi+35BCh]
81e4af4a cmp byte ptr [esp+0Eh],0
81e4af4f je nt!ExAcquireResourceSharedLite+0x222 (81e4af5e)
81e4af51 push 0
81e4af53 push esi
81e4af54 push 10044h
//这个是指向等待资源被释放
81e4af59 call nt!PerfLogExecutiveResourceWait (81f25518)
//SharedWaiters入栈,SharedWaiters是个分发器对象,指定此对象是可指向同步处理的
81e4af5e push dword ptr [esi+10h]
81e4af61 mov eax,esi
//在SharedWaiters这个对象上等待被有信号
81e4af63 call nt!ExpWaitForResource (81e4802f)
//Flag&0x4
81e4af68 test byte ptr [esi+0Eh],4
81e4af6c mov byte ptr [esp+0Fh],0
//flag的第三位为零,就跳
81e4af71 je nt!ExAcquireResourceSharedLite+0x2a1 (81e4afdd)
//eax是当前线程对象
81e4af73 mov eax,dword ptr [esp+10h]
//eax+280h是线程对象的CrossThreadFlags成员
81e4af77 mov eax,dword ptr [eax+280h]
//得到线程对象的ThreadIoPriority成员
81e4af7d and eax,1C00h
//ThreadIoPriority800h实际就是高于2
81e4af82 cmp eax,800h
//跳转
81e4af87 jae nt!ExAcquireResourceSharedLite+0x2a1 (81e4afdd)
//下面调用KeAcquireInStackQueuedSpinLock在上面有讲过
81e4af89 lea edx,[esp+24h]
81e4af8d lea ecx,[esi+34h]
81e4af90 call dword ptr [nt!_imp_KeAcquireInStackQueuedSpinLock (81e14144)]
81e4af96 push 1
81e4af98 push 0
81e4af9a push dword ptr [esp+18h]
81e4af9e mov eax,esi
//ExpFindCurrentThread寻找一个资源数组中空闲的节点,返回
81e4afa0 call nt!ExpFindCurrentThread (81eb42ba)
81e4afa5 test byte ptr [eax+4],1
81e4afa9 jne nt!ExAcquireResourceSharedLite+0x289 (81e4afc5)
//ecx是本线程对象
81e4afab mov ecx,dword ptr [esp+10h]
81e4afaf xor edx,edx
//线程对象的IoBoostCount
81e4afb1 add ecx,2A4h
81e4afb7 inc edx
//IoBoostCount++;
81e4afb8 lock xadd dword ptr [ecx],edx
//OwnerEntry的IoPriorityBoosted置1
81e4afbc or dword ptr [eax+4],1
81e4afc0 mov byte ptr [esp+0Fh],1
81e4afc5 lea ecx,[esp+24h]
// call KeReleaseInStackQueuedSpinLock
81e4afc9 call ebx
81e4afcb cmp byte ptr [esp+0Fh],1
//根据返回的结果决定是否调用IoBoostThreadIoPriority函数
81e4afd0 jne nt!ExAcquireResourceSharedLite+0x2a1 (81e4afdd)
81e4afd2 push 2
81e4afd4 push dword ptr [esp+14h]
81e4afd8 call nt!IoBoostThreadIoPriority (81e3593e)
//上面说过
81e4afdd inc dword ptr [edi+35B4h]
81e4afe3 inc dword ptr [edi+3584h]
81e4afe9 cmp byte ptr [esp+0Eh],0
//更加一个全局掩码确认是否调用PerfLogExecutiveResourceAcquire
81e4afee je nt!ExAcquireResourceSharedLite+0x2c4 (81e4b000)
81e4aff0 push dword ptr [esi+24h]
81e4aff3 push 1
81e4aff5 push esi
81e4aff6 push 10041h
81e4affb call nt!PerfLogExecutiveResourceAcquire (81f2541f)
//返回结果
81e4b000 mov al,1
81e4b002 pop edi
81e4b003 pop esi
81e4b004 pop ebx
81e4b005 mov esp,ebp
81e4b007 pop ebp
81e4b008 ret 8
ExAcquireResourceSharedLite汇编代码有点多,但是基本都是些逻辑处理,重要的内容没有多少,下面我简单总结下这个函数的执行流程:
首先这个函数的第一个参数是和一个系统全局资源相关的ERESOURCE数据结构,而在函数的开始处,便对传入的资源请求自旋锁。
请求到自旋锁,就会马上进入一个循环里,在这个循环里面不断尝试请求对这个资源的控制权,直到请求被处理为止才退出循环。
循环的内部,首先根据传入的ERESOURCE结构的ActiveEntries判断当前活动的条目是否存在;如果不存在,那么我们更新这个结构内容,把传入的资源关联到本线程,然后释放请求到的锁,并且根据一个全局掩码来决定是够写入系统日志(关于这个是不是真的是执行更新日志相关内容的操作我不是很确定,所以可能这里表达错误)。
如果当前存在活动的条目,便根据传入资源的Flag成员进行判断,根据Flag的值来决定在接下来的行为;Flag&0x80为真值的话,那么就在这些活动的条目中寻找一个节点返回(调用ExpFindCurrentThread函数完成此任务),如果找到的话,再次判断找到的节点是够关联的是本线程(说明之前本线程可能已经请求过该资源了),是的话,就更新传入的ERESOURCE结构,然后释放对这个结构的锁,中间的流程会根据一个全局掩码值来判断是否把相应操作记入系统日志;不是关联本线程的话,那就说明当前活动条目中不存在当前线程,ExpFindCurrentThread函数执行的时候只是给重新分配了一个可用节点而已,并根据传入的第二个参数决定是否等待此资源可用,然后判断资源的SharedWaiters是否为零,不为零的话,释放资源的锁并更新资源,根据掩码值决定是否计入日志;SharedWaiters为零的话,那就重新执行上述循环,直到找到一个符合条件的节点为止。
Flag&0x80不为真的话,那就直接寻找一个空的条目节点(调用ExpFindEmptyEntry完成此过程),如果没有寻找到,那就重复上面的循环;如果寻找到则根据第二个参数来判断是够等待该资源可用,不等待该资源是否可用的话,那就判断此资源的SharedWaiters成员是否为空,不为空就更新该资源信息,然后释放对该资源的锁,返回就行了;SharedWaiters为空的话,那就重复上述循环,直到找到符号条件的节点为止。
上面就是这个函数的大致流程,实际细节方面没有必要了解多少,主要明白这个函数做什么就行了。
这个函数唯一的任务,是获取当前线程对传入资源的控制权。请求得到许可后,那么当前线程以后对各种资源的访问也就有效进行了。这是操作系统管理公共资源的一种手段,因为资源是比较宝贵的东西,所以制定相关策略使资源被妥善和可控制的利用是很有必要的。所以在gui的各种相关系统服务函数入口都要调用此函数完成上述所说功能。结尾完成的功能我没有分析,不过肯定是释放对系统资源的控制权。
那么,ExAcquireResourceSharedLite函数就分析到这里,没什么用处,只要明白这个函数做的任务就行了,细节可以直接忽视。下一篇中,我们重新转到NtQueryWindow函数里面看看它的执行流程。
新建了一个论坛,希望大家多多光顾。(www.mengwuji.net)
能力值:
( LV6,RANK:90 )
3 楼
名称:win32子系统之三:ValidateHwnd函数分析
作者:mengwuji
时间:2013.5.28 18:32
链接:http://mengwuji.net/forum.php?mod=viewthread&tid=99&extra=page%3D1
win32k!NtUserQueryWindow:
92dbe20a mov edi,edi
92dbe20c push ebp
92dbe20d mov ebp,esp
92dbe20f push esi
92dbe210 push edi
//分析过EnterSharedCrit函数的用处,主要是为了让当前线程获取对系统资源的控制权
92dbe211 call win32k!EnterSharedCrit (92dd3141)
//ecx = hwnd
92dbe216 mov ecx,dword ptr [ebp+8]
//这个函数是验证传入窗口句柄的有效性,那么我们可以看看系统如何判断一个窗口句柄的有效性的(好吧,看来想明白NtUserQueryWindow原理还要等一段时间了。中间调用的函数都够我分析老半天了)
92dbe219 call win32k!ValidateHwnd (92dd90c0)
............ win32k!ValidateHwnd:
92dd90c0 mov edi,edi
92dd90c2 push ebp
92dd90c3 mov ebp,esp
92dd90c5 sub esp,0Ch
//edx = [gpsi] gpsi以前分析过,是个成员为tagSERVERINFO结构的数组,这个结构信息量很多,下面遇到的时候再说
92dd90c8 mov edx,dword ptr [win32k!gpsi (92f2255c)]
92dd90ce push ebx
//和用户层一样的,取句柄低2字节,实际是个下标
92dd90cf movzx eax,cx
92dd90d2 push esi
92dd90d3 push edi
//先保存下句柄,后面还会用到
92dd90d4 mov dword ptr [ebp-8],ecx
//tagSERVERINFO偏移4的位置是cHandleEntries,这个应该是窗口句柄的最大个数
92dd90d7 cmp eax,dword ptr [edx+4]
//大于cHandleEntries就出错了,然后跳到错误处理
92dd90da jae win32k!ValidateHwnd+0x15a (92dd921a)
//gSharedInfo是个tagSHAREDINFO结构(以前分析定时器的时候研究过),偏移+8的位置是HeEntrySize,这个应该是描述窗口句柄关联的一个对象尺寸
92dd90e0 mov esi,dword ptr [win32k!gSharedInfo+0x8 (92f22448)]
//定位到当前句柄所在句柄关联的对象数组里面的偏移
92dd90e6 imul esi,eax
//gSharedInfo+4是aheList,说明aheList是系统中窗口句柄关联的一个对象数组基址。通过它可以定位到具体句柄关联的对象,句柄关联的对象是HANDLEENTRY结构
92dd90e9 add esi,dword ptr [win32k!gSharedInfo+0x4 (92f22444)]
//下面开始取窗口句柄的高2字节
92dd90ef mov eax,ecx
92dd90f1 shr eax,10h
//现在的esi经过上面的计算已经知道是个HANDLEENTRY结构,+a位置是wUniq。比较窗口句柄的高2字节和这个值
92dd90f4 cmp ax,word ptr [esi+0Ah]
//相等跳转
92dd90f8 je win32k!ValidateHwnd+0x4d (92dd910d)
//不相等比较句柄高两字节是否为零
92dd90fa test ax,ax
//为零跳转
92dd90fd je win32k!ValidateHwnd+0x4d (92dd910d)
//比较句柄高两字节是够为ffff
92dd90ff mov ecx,0FFFFh
92dd9104 cmp ax,cx
//不等于ffff,到这里说明出错了。那么就执行错误处理
92dd9107 jne win32k!ValidateHwnd+0x15a (92dd921a)
//HANDLEENTRY的bType是句柄的类型,这个类型和1比较
92dd910d cmp byte ptr [esi+8],1
//不等于1说明出错了,跳到错误处理
92dd9111 jne win32k!ValidateHwnd+0x15a (92dd921a)
//接下来调用PsGetCurrentThreadWin32Thread函数,这个函数很简单,三句代码。我直接放到下面来分析
------------------------------------------------>
//PsGetCurrentThreadWin32Thread
81e9d498 mov eax,dword ptr fs:[00000124h] //eax得到当前线程对象
81e9d49e mov eax,dword ptr [eax+18Ch] //+18c位置是Win32Thread
81e9d4a4 ret
<------------------------------------------------
//上面的只是得到当前线程对象的Win32Thread成员,这个成员是个_W32THREAD结构,但是它不够全面描述一个线程gui信息。实际还有一个tagTHREADINFO结构,也是描述Win32Thread这个成员的,tagTHREADINFO结构的信息才是全面的,大家可以用windbg看看(这些结构是之前分析定时器的时候发现的,这里不重复分析具体分析过程)
92dd9117 call dword ptr [win32k!_imp__PsGetCurrentThreadWin32Thread (92ef4034)]
//ebx = phead,是个句柄关联对象的头
92dd911d mov ebx,dword ptr [esi]
//保存当前线程的tagTHREADINFO结构
92dd911f mov edi,eax
//保存句柄管理的对象头
92dd9121 mov dword ptr [ebp-4],ebx
92dd9124 test ebx,ebx
//对象头等于0跳转,说明发生错误,跳到错误处理位置
92dd9126 je win32k!ValidateHwnd+0x15a (92dd921a)
//比较句柄关联对象的bFlags成员第0位是否为真
92dd912c test byte ptr [esi+9],1
//所关联对象头是个_HEAD结构,但是这个结构貌似也表示的不全面,所以+8位置的值现在还不能确定是什么
92dd9130 mov eax,dword ptr [ebx+8]
//保存+8位置的值
92dd9133 mov dword ptr [ebp-0Ch],eax
//bFlags&0x1为真的话就跳转,跳到错误处理
92dd9136 jne win32k!ValidateHwnd+0x15a (92dd921a)
//上面不知道对象头+8位置是什么东西,下面的比较就暴露了是什么了。是这个对象关联的gui线程的tagTHREADINFO结构,这里进行比较是判断我们传入的句柄,是否是我们这条线程本身创建的窗口
92dd913c cmp eax,edi
//是的话就跳转
92dd913e je win32k!ValidateHwnd+0xac (92dd916c)
//对象头+c,我们看下一句就能明白这个地方是什么了
92dd9140 mov ecx,dword ptr [ebx+0Ch]
//tagTHREADINFO+0xC8位置是rpdesk成员,它是一个tagDESKTOP对象,是桌面相关的数据结构,那么上面的对象头+c位置就是这个句柄所在的桌面了,下面这个比较就是比较这个句柄是否在当前桌面(因为windows支持多个桌面的!)
92dd9143 cmp ecx,dword ptr [edi+0C8h]
//是当前桌面的句柄,就跳转
92dd9149 je win32k!ValidateHwnd+0xac (92dd916c)
//如果不是当前桌面的句柄,就比较看看tagTHREADINFO的TIF_flags第三位是否为真
92dd914b test byte ptr [edi+0D8h],4
92dd9152 jne win32k!ValidateHwnd+0xac (92dd916c)
//tagTHREADINFO的ppi成员,这个成员是一个tagPROCESSINFO对象,这个对象是当前线程所在进程关联的tagPROCESSINFO对象
92dd9154 mov edx,dword ptr [edi+0B8h]
//push rpdesk
92dd915a push ecx
//push ppi
92dd915b push edx
//call GetDesktopView,我们在下面看看GetDesktopView的流程,很简单所以直接放到这里看!
-------------------------------------------------------------->
//GetDesktopView
92dcf9aa mov edi,edi
92dcf9ac push ebp
92dcf9ad mov ebp,esp
92dcf9af mov eax,dword ptr [ebp+8] //ppi
//pdvList,是个tagDESKTOPVIEW结构,这个结构是系统所有桌面组成的一个单链表
92dcf9b2 mov eax,dword ptr [eax+154h]
92dcf9b8 jmp win32k!GetDesktopView+0x1a (92dcf9c4) //这里进行一个简单的循环
92dcf9ba mov ecx,dword ptr [eax+4]
92dcf9bd cmp ecx,dword ptr [ebp+0Ch]
92dcf9c0 je win32k!GetDesktopView+0x1e (92dcf9c8)
92dcf9c2 mov eax,dword ptr [eax]
92dcf9c4 test eax,eax
92dcf9c6 jne win32k!GetDesktopView+0x10 (92dcf9ba)
92dcf9c8 pop ebp
92dcf9c9 ret 8
<--------------------------------------------------------------
//GetDesktopView函数进行一下简单的遍历工作,找到传入句柄所关联的一个tagDESKTOPVIEW对象,实际调用此函数的目的只是为了证明这个句柄是在某个桌面中的,如果一个桌面都没有这个窗口,那么肯定就出错了
92dd915c call win32k!GetDesktopView (92dcf9aa)
92dd9161 test eax,eax
//比较查找的是够为零,是的话,就出错了,然后退出
92dd9163 je win32k!ValidateHwnd+0x15a (92dd921a)
//epb-c保存的是当前句柄关联线程的tagTHREADINFO结构
92dd9169 mov eax,dword ptr [ebp-0Ch]
//一个全局标记,目前不知道做什么
92dd916c cmp byte ptr [win32k!gbValidateHandleForIL (92f21f38)],0
根据这个值执行不同两个流程
92dd9173 je win32k!ValidateHwnd+0x10f (92dd91cf)
//句柄关联的tagTHREADINFO为零跳转
92dd9175 test eax,eax
92dd9177 je win32k!ValidateHwnd+0x10f (92dd91cf)
//ebx = 传入句柄的ppi
92dd9179 mov ebx,dword ptr [eax+0B8h]
//eax = 当前线程的ppi
92dd917f mov eax,dword ptr [edi+0B8h]
//ecx = 传入句柄的pvwplWndGCList
92dd9185 mov ecx,dword ptr [ebx+1ACh]
//edx = 当前线程的pvwplWndGCList
92dd918b mov edx,dword ptr [eax+1ACh]
92dd9191 push ecx
92dd9192 push edx
//这个函数比较简单,我们在下面直接分析
--------------------------------------------------------->
win32k!CheckAccessForIntegrityLevel:
92dd9232 mov edi,edi
92dd9234 push ebp
92dd9235 mov ebp,esp
92dd9237 cmp dword ptr [win32k!gbEnforceUIPI (92f21f3c)],0 //未知的全局变量
92dd923e jne win32k!CheckAccessForIntegrityLevel+0x13 (92dd9245)
//到这里的话就返回1
92dd9240 xor eax,eax
92dd9242 inc eax
92dd9243 jmp win32k!CheckAccessForIntegrityLevel+0x1c (92dd924e)
92dd9245 mov eax,dword ptr [ebp+8] //当前线程的pvwplWndGCList
92dd9248 cmp eax,dword ptr [ebp+0Ch] //传入句柄的pvwplWndGCList,相等cf为1,不相等cf为0
92dd924b sbb eax,eax //如果相等,后面的操作执行后返回零,否则返回1
92dd924d inc eax
92dd924e pop ebp
92dd924f ret 8
<---------------------------------------------------------
//这个函数比较传入的两个参数是否相等,不相等的话返回零,相等的话返回一
92dd9193 call win32k!CheckAccessForIntegrityLevel (92dd9232)
92dd9198 test eax,eax
92dd919a jne win32k!ValidateHwnd+0x10c (92dd91cc)
//eax = 传入句柄的进程对象
92dd919c mov eax,dword ptr [ebx]
//比较这个进程对象是不是csrss.exe进程的
92dd919e cmp eax,dword ptr [win32k!gpepCSRSS (92f225ec)]
//是的话跳转
92dd91a4 je win32k!ValidateHwnd+0x10c (92dd91cc)
//下面直到92dd91cb都是错误处理,我也懒得看了
92dd91a6 movzx ecx,byte ptr [esi+8]
92dd91aa mov edx,dword ptr [ebp-8]
92dd91ad mov eax,dword ptr [edi+0B8h]
92dd91b3 push ecx
92dd91b4 push edx
92dd91b5 push ebx
92dd91b6 push eax
92dd91b7 call win32k!EtwTraceUIPIHandleValidationError (92e588d8)
92dd91bc push 5
92dd91be call win32k!UserSetLastError (92d9769d)
92dd91c3 pop edi
92dd91c4 pop esi
92dd91c5 xor eax,eax
92dd91c7 pop ebx
92dd91c8 mov esp,ebp
92dd91ca pop ebp
92dd91cb ret
//传入句柄关联的那个对象头给ebx
92dd91cc mov ebx,dword ptr [ebp-4]
//+D8是当前线程的TIF_flags
92dd91cf test dword ptr [edi+0D8h],20000000h
//根据比较后选择跳转
92dd91d9 je win32k!ValidateHwnd+0x151 (92dd9211)
//ecx = 当前线程的ppi
92dd91db mov ecx,dword ptr [edi+0B8h]
//edx = 当前进程的pW32Job
92dd91e1 mov edx,dword ptr [ecx+170h]
//eax = 当前进程pW32JOB的restrictions
92dd91e7 mov eax,dword ptr [edx+0Ch]
//看第零位是否存在
92dd91ea and eax,1
//存在就返回
92dd91ed je win32k!ValidateHwnd+0x151 (92dd9211)
//eax是传入的句柄
92dd91ef mov eax,dword ptr [ebp-8]
//esi是句柄关联的对象
92dd91f2 push esi
92dd91f3 push eax
//调用IsHandleEntrySecure函数,这个函数是检测句柄的安全性,具体我也不分析了,有兴趣的人可以看看。
92dd91f4 call win32k!IsHandleEntrySecure (92e38dc2)
92dd91f9 test eax,eax
92dd91fb jne win32k!ValidateHwnd+0x151 (92dd9211)
92dd91fd push 578h
92dd9202 call win32k!UserSetLastError (92d9769d)
92dd9207 mov dword ptr [ebp-4],0
92dd920e mov ebx,dword ptr [ebp-4]
92dd9211 pop edi
92dd9212 pop esi
92dd9213 mov eax,ebx
92dd9215 pop ebx
92dd9216 mov esp,ebp
92dd9218 pop ebp
92dd9219 ret
92dd921a push 578h
92dd921f call win32k!UserSetLastError (92d9769d)
92dd9224 pop edi
92dd9225 pop esi
92dd9226 xor eax,eax
92dd9228 pop ebx
92dd9229 mov esp,ebp
92dd922b pop ebp
92dd922c ret
//ValidateHwnd这个函数无非检测句柄的合法性,通过各种判断然后确定传入的句柄是否有效。至于怎么判断的,汇编代码里面有详细的注释了。值得注意的是tagTHREADINFO是关于gui线程的一个数据结构,tagPROCESSINFO是关于gui进程的一个数据结构。然后遍历窗口句柄实际可以通过gSharedInfo全局结构来遍历,还能得到关于这个句柄对应的线程进程信息,和句柄所对应的句柄对象都可以。总之,这里面涉及的一些数据结构能掌握或者了解就相当不错了。
希望大家耐心看看,我不想在这里反汇编成c代码是因为提升效率。因为这个函数分析完成只是开始,后面还有更加多的内容在等待着我们。
我只起一个引导作用,接下来就看大家各自发挥吧......
ps:www.mengwuji.net
能力值:
( LV6,RANK:90 )
4 楼
名称:win32子系统之四:NtUserQueryWindow函数分析
作者:mengwuji
时间:2013.5.29 14:49
链接:http://mengwuji.net/forum.php?mod=viewthread&tid=103&extra=page%3D1
接着上一篇,我们看看这一篇内容
win32k!NtUserQueryWindow:
92dbe20a mov edi,edi
92dbe20c push ebp
92dbe20d mov ebp,esp
92dbe20f push esi
92dbe210 push edi
//分析过EnterSharedCrit函数的用处,主要是为了让当前线程获取对系统资源的控制权
92dbe211 call win32k!EnterSharedCrit (92dd3141)
//ecx = hwnd
92dbe216 mov ecx,dword ptr [ebp+8]
//这个函数是验证传入窗口句柄的有效性,已经分析过了, 只要它返回有值,那就证明此句柄是“有效”的,至于什么才是判断一个句柄“有效”的凭据呢,认真看过我对ValidateHwnd分析的人应该已经知道。
92dbe219 call win32k!ValidateHwnd (92dd90c0)
92dbe21e mov edi,eax
92dbe220 test edi,edi
//句柄无效则直接跳到退出
92dbe222 je win32k!NtUserQueryWindow+0x102 (92dbe30c)
//这个是查询号
92dbe228 mov ecx,dword ptr [ebp+0Ch]
//edi是ValidateHwnd的返回结果,ValidateHwnd函数的返回结果是句柄所关联的_HANDLEENTRY对象的phead成员,也就是这个对象头部,数据结构是_HEAD,由于_HEAD所表述的信息本身就不完整,我在别的地方分析的时候发现一个_THRDESKHEAD结构和这个十分相似,至少目前我可以确定_HANDLEENTRY不过是体现了_THRDESKHEAD结构的一部分内容罢了,但是通过对ValidateHwnd的分析可以知道一些它不完整的信息;+8位置是一个句柄关联的tagTHREADINFO结构,所以下面的eax = 本句柄的tagTHREADINFO结构
92dbe22b mov eax,dword ptr [edi+8]
//比较查询号
92dbe22e cmp ecx,9
//看来查询号不能超过9,说明有10种类型
92dbe231 ja win32k!NtUserQueryWindow+0x102 (92dbe30c)
//这个是switch语句了,通过查询号的具体指判断应该跳到哪儿去
92dbe237 jmp dword ptr win32k!NtUserQueryWindow+0x114 (92dbe31b)[ecx*4]
//下面为不同的查询号划分了一下所对应的数据块,让本身有点麻烦的函数顿时变得简单了,那么希望接下来的分析能一帆风顺的就万事大吉了!
//查询号为1到来的case块
//这个就再好分析不过了
//既然知道了edi是_THRDESKHEAD结构,那我们当然希望用完整的结构了。第一个成员便是KTHREAD这个结构体
92dbe23e push dword ptr [eax]
//PsGetThreadProcessId这个函数很简单,我们就在这里分析它
------------------------------------------------------->
nt!PsGetThreadProcessId:
81ed4461 mov edi,edi
81ed4463 push ebp
81ed4464 mov ebp,esp
81ed4466 mov eax,dword ptr [ebp+8]
81ed4469 mov eax,dword ptr [eax+22Ch] //KTHREAD的22c偏移处就是进程的id
81ed446f pop ebp
81ed4470 ret 4
<-------------------------------------------------------
//说明调用这个函数的返回值是句柄关联的线程id
92dbe240 call dword ptr [win32k!_imp__PsGetThreadProcessId (92ef40a8)]
92dbe246 mov esi,eax
//这里跳到退出。说明功能号为1表示查询创建传入句柄的那个进程id(根据前面的用户层分析,这明显是2号功能的作用嘛)
92dbe248 jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//查询号为0到来的case块
//好了,到这里就是头痛的开始了。为什么这么说呢,看edi是什么?edi前面说过是一个_THRDESKHEAD结构,但是不巧的是这个结构根本就没有+AC偏移位置的成员,或者说这个结构没这么大!这可怎么是好呢?那么,有两种结论可以参考;第一种是这个结构就像_HANDLEENTRY或者_W32THREAD结构一样是不完整的。第二种是这个结构包含在某个结构中的,就像我们知道KTHREAD结构,如果不知道RTHREAD结构那么超出KTHREAD的范围我们根本也就不知道了。那么第一种我感觉不太靠谱,因为微软除非有毛病,或者win32子系统真的复杂到要分很多很多层次结构来设计,不然我真想不通一个结构为何要分成几个不同的数据结构表示!那么第二种呢,我感觉这个希望很大,尤其是我想到KTHREAD和ETHREAD的关系时更加认定了这一种(毕竟符合微软的设计风格嘛)。那接下来,在参看了一些其他函数后,我的结论被证实了,我又找到了一个结构,这个结构式tagWND!看到它我开始兴奋了,因为之前研究窗口句柄所对应的内核对象的数据结构时,发现这个内核对象就是tagWND!那么我们看看tagWND这个结构的+AC处是什么。+AC处是一些标志,ExStyle可以认为是窗口的类型。ExStyle&400h可以得到bConsoleWindow,为真说明是个控制台窗口
92dbe24d test dword ptr [edi+0ACh],400h
//不是控制台就转到1号功能去执行.....
92dbe257 je win32k!NtUserQueryWindow+0x34 (92dbe23e)
//到这里来我的思绪开始更加混乱了。因为嘛,tagWND就没有+B0成员!难道我分析错了?那这个错误可真能让人受的......可是在分析后发现貌似我没分析错呀,或者我的错误我还没发现!那么在我没发现我分析错误之前,我就姑且认为它是正确的吧,那+B0不就无法解释了吗?是的,我暂时解释不了,只能推测这个tagWND或者分为图形界面的tagWND和控制台窗口的tagWND,当是控制台的时候tagWND貌似后面还有什么其他附加信息,这些附加信息是什么呢?只能通过分析控制台窗口的初始化时大概能搞清楚。那么至少+B0位置通过用户层调用这个功能号返回的结果看,B0位置是句柄所在进程的id了。这里微软不公开控制台的tagWND完整结构,那么我也就不好怎么试验我的推测了,在以后的编程实践中再来验证我说的对错吧。
92dbe259 mov esi,dword ptr [edi+0B0h]
//查询到了就退出了
92dbe25f jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//查询号为2到来的case块
//这个在用户层分析中是来返回创建句柄的线程id的,这里又是判断是否控制台窗口
92dbe264 test dword ptr [edi+0ACh],400h
//不是的话跳转
92dbe26e je win32k!NtUserQueryWindow+0x71 (92dbe27b)
//是的话那么B4位置记录了控制台窗口的线程id........
92dbe270 mov esi,dword ptr [edi+0B4h]
//控制台窗口返回线程id,退出
92dbe276 jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//得到KTHREAD
92dbe27b push dword ptr [eax]
//这个函数更加简单,我们看看
----------------------------------------------------------->
nt!PsGetThreadId:
81ed4876 mov edi,edi
81ed4878 push ebp
81ed4879 mov ebp,esp
81ed487b mov eax,dword ptr [ebp+8]
81ed487e mov eax,dword ptr [eax+230h] //传入KTHREAD关联的线程id
81ed4884 pop ebp
81ed4885 ret 4
<-----------------------------------------------------------
//得到线程id,退出返回
92dbe27d call dword ptr [win32k!_imp__PsGetThreadId (92ef40a4)]
92dbe283 jmp win32k!NtUserQueryWindow+0x3c (92dbe246)
//查询号为3到来的case块
//好了,我们知道eax是tagTHREADINFO结构,+BC是pq成员,这个成员结构是tagQ
92dbe285 mov eax,dword ptr [eax+0BCh]
//tagQ这个结构+28是spwndActive,是个tagWND。看名字知道大概是前一个获取窗口对象
92dbe28b mov eax,dword ptr [eax+28h]
//上一个获取窗口对象否为零
92dbe28e test eax,eax
//为零就直接退出
92dbe290 je win32k!NtUserQueryWindow+0x102 (92dbe30c)
//第一个成员是_THRDESKHEAD结构的成员,_THRDESKHEAD结构的第一个是h,是个PVOID类型,这里可以理解为hwnd?
92dbe292 mov esi,dword ptr [eax]
//返回,3号功能应该是查询上个活动的窗口句柄
92dbe294 jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//查询号为4到来的case块
//这个我懒的分析了,看到+bc,+24让我看到了三号功能号的影子,具体做什么的我还没猜出来,不过对我们理解完全不造成任何障碍
92dbe296 mov eax,dword ptr [eax+0BCh]
92dbe29c mov eax,dword ptr [eax+24h]
92dbe29f jmp win32k!NtUserQueryWindow+0x84 (92dbe28e)
//查询号为5到来的case块
//好吧,这里的edi即是_HEAD类型,也是_THRDESKHEAD类型,或者是tagWND类型,具体哪种看下面调用的函数怎么使用这个结构了
92dbe2a1 push edi
//IsGhostWindow函数很简单,我们看看下面分析
---------------------------------------------------->
win32k!IsGhostWindow:
92da3f54 mov edi,edi
92da3f56 push ebp
92da3f57 mov ebp,esp
92da3f59 mov eax,dword ptr [ebp+8]
92da3f5c movzx eax,word ptr [eax+2Ah] //这里明显传入的参数是tagWND类型,+2A是fnid,干什么的我也不知道
92da3f60 and eax,0FFFF3FFFh
92da3f65 sub eax,2AAh
92da3f6a neg eax
92da3f6c sbb eax,eax
92da3f6e inc eax //返回真或假......
92da3f6f pop ebp
92da3f70 ret 4
<----------------------------------------------------
//判断是不是Ghost窗口,什么是Ghost的窗口我小白....不懂,难道是安装系统时候显示的那个Ghost?
92dbe2a2 call win32k!IsGhostWindow (92da3f54)
92dbe2a7 test eax,eax
//是的话,跳转
92dbe2a9 je win32k!NtUserQueryWindow+0xa6 (92dbe2b0)
92dbe2ab xor esi,esi
92dbe2ad inc esi
//不是跳转退出
92dbe2ae jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//push tagWND
92dbe2b0 push edi
//又是调用一个函数判断啥啥啥的,哎,大家可以去分析下,这东西我都分析腻了.....
92dbe2b1 call win32k!IsHungWindow (92dcc2a4)
92dbe2b6 mov esi,eax
92dbe2b8 test esi,esi
//好吧,这个艰难的功能号也是这个函数里面判断最多的功能号有兴趣的可以分析下,我完全没兴趣!(都是体力活,没难点,难点都被我分析的123了)
92dbe2ba je win32k!NtUserQueryWindow+0x104 (92dbe30e)
92dbe2bc push edi
92dbe2bd call win32k!ShouldProcessHungWindow (92e0a38b)
92dbe2c2 test eax,eax
92dbe2c4 je win32k!NtUserQueryWindow+0x104 (92dbe30e)
92dbe2c6 mov edi,dword ptr [eax]
92dbe2c8 call win32k!LeaveSharedEnterCrit (92e02695)
92dbe2cd mov dl,1
92dbe2cf mov ecx,edi
92dbe2d1 call win32k!HMValidateHandleNoSecure (92dc87c3)
92dbe2d6 test eax,eax
92dbe2d8 je win32k!NtUserQueryWindow+0xd6 (92dbe2e0)
92dbe2da push eax
92dbe2db call win32k!ProcessHungWindow (92e0c43f)
92dbe2e0 call win32k!LeaveCritEnterShared (92e026b8)
92dbe2e5 jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//查询号为7到来的case块
//不知所谓的功能,但是不影响理解意思.....
92dbe2e7 mov eax,dword ptr [eax+0BCh]
92dbe2ed xor ecx,ecx
92dbe2ef cmp eax,dword ptr [win32k!gpqForeground (92f24e60)]
92dbe2f5 sete cl
92dbe2f8 mov esi,ecx
92dbe2fa jmp win32k!NtUserQueryWindow+0x104 (92dbe30e)
//查询号为8到来的case块
//+164是个spwndDefaultIme,IME让我想起了输入法?好吧,我不清楚。。。。。
92dbe2fc mov eax,dword ptr [eax+164h]
92dbe302 jmp win32k!NtUserQueryWindow+0x84 (92dbe28e)
//查询号为9到来的case块
//spDefaultImc更加不知所云。。。谁有兴趣谁看去! 这里不理解阅读
92dbe304 mov eax,dword ptr [eax+168h]
92dbe30a jmp win32k!NtUserQueryWindow+0x84 (92dbe28e)
//查询号为6到来的case块
92dbe30c xor esi,esi
//好吧,亲爱的你终于退出了.....
92dbe30e call win32k!UserSessionSwitchLeaveCrit (92dc356f)
92dbe313 pop edi
92dbe314 mov eax,esi
92dbe316 pop esi
92dbe317 pop ebp
92dbe318 ret 8 这个函数内容不多,但是涉及win32子系统的数据结构很多,也非常复杂,希望大家跟着我的分析自己在windows7下面去看看。
相信你通过我的这个分析结果,自己都能写出来一个这个函数了(至少我现在写出来完全没有障碍....)!
那么,为什么说了这么多,最终想干什么呢?实际就是想告诉大家win32子系统内部的一些数据结构而已,这些数据结构对于管理和维和win32子系统相当重要,所以有必要好好理解。你可以用这些数据结构去做好多好多事情,比如枚举进程(这个我想比你暴力枚举还要实际的多,因为没有哪个进程没窗口的。就算你摘掉了tagWND链表中的自己,那么你也休想逃过gSharedInfo+4位置保存的所有关于句柄的数组!就算你逃过了,那你要考虑的问题将是特别特别多,那这个算不算枚举进程最好的办法呢?在我目前的认知里我觉得是.....)。
好了,本篇结束,win32子系统入门的差不多了,后面我有时间的话就讲讲入门后能做些什么吧。
ps:截止现在,win32子系统系列是我花了两天时间分析出来的,感觉还是不太好分析的。因为win32子系统参考资料很少,所以什么东西你都要亲力亲为。后面我关于它的研究会放慢很对,因为我还要建设好论坛和出驱动方面的视频教程.....最后,希望大家多多支持(www.mengwuji.net)!