著名程序员刘涛涛WinMount程序BUG

/**************************************
/* 作者:半斤八兩
/* 博客:http://cnblogs.com/bjblcracked
/* 日期:2013-08-12  22:44
/**************************************



只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!



同事说win也有mount工具,好奇心驱使就搜了一下.
发现国内的著名程序员刘涛涛写了一款免费的win mount工具.
于是下载了一份就开始玩起来了.安装后发现winmount程序的界面非常漂亮友好,而且广告也很少.
甚是喜欢.图1

0x1 bug
只可惜我在使用的过程中,程序会占用100%CPU  :(

0x2 BUG重现(如何产生).
首先弄个压缩软件,我当前用的是 7z 加密压缩任意一个文件.(不加密文件名).
然后用mount工具双击打开.这个时候就可以看到mount创建了一个新窗口.(输入密码框)
此时我无意中点到父窗口,发现程父窗口也是活动窗口. (原来是非模态窗口,有点意思)图2

知道是非模态窗口后,我就下意识的关闭了父窗口,有意思的事情发生了,CPU就跟打了鸡血一样,一个劲猛彪~图3

一开始我不相信自己的眼睛,我在心里想,肯定是系统或者其他什么软件出了问题了.
不可能是mount自身的问题.为了搞清楚到底是什么原因导致的CPU占用100,我决定分析它.

OD载入后,自己能想到的所有断点,都试了一遍,都没有断下来.
在一阵狂试都没有断下后,终于放弃下断点的方法了.

找了几首经典歌曲,边听边想思路.

mount占CPU的时候,操作系统虽然有点卡,但并没有卡死.
说明至少不是 

for(true){;} 
while(true){;}

这一类的死循环导致的卡死.

所有自己已知的如等待类函数也都下断点测试过了.
最后想就从线程下手吧.
打开线程窗口,可以看到有四个线程在拼命工作,(为了方便查看,我们可以点一下user time)图4

现在终于知道了,是这四个线程导致的,那么从entry处分析就行了.
最终分析得知导致卡死的是  kernel32.GetQueuedCompletionStatus 这个函数.

我们来看一下函数原型和介绍.

 1 GetQueuedCompletionStatus
 2 The GetQueuedCompletionStatus function attempts to dequeue an I/O completion packet from a specified I/O completion port. If there is no completion packet queued, the function waits for a pending I/O operation associated with the completion port to complete. 
 3 
 4 BOOL GetQueuedCompletionStatus(
 5   HANDLE CompletionPort,       // handle to completion port
 6   LPDWORD lpNumberOfBytes,     // bytes transferred
 7   PULONG_PTR lpCompletionKey,  // file completion key
 8   LPOVERLAPPED *lpOverlapped,  // buffer
 9   DWORD dwMilliseconds         // optional timeout value
10 );
 1 6B1192E0          $  55            push ebp
 2 6B1192E1          .  8BEC          mov ebp, esp
 3 6B1192E3          .  83EC 0C       sub esp, 0C
 4 6B1192E6          .  56            push esi
 5 6B1192E7          .  8B35 4870126B mov esi, dword ptr ds:[<&KERNEL32.GetQue>;  kernel32.GetQueuedCompletionStatus
 6 6B1192ED          .  8D49 00       lea ecx, dword ptr ds:[ecx]
 7 6B1192F0          >  6A FF         push -1
 8 6B1192F2          .  8D45 FC       lea eax, dword ptr ss:[ebp-4]
 9 6B1192F5          .  50            push eax
10 6B1192F6          .  8B47 08       mov eax, dword ptr ds:[edi+8]
11 6B1192F9          .  8D4D F4       lea ecx, dword ptr ss:[ebp-C]
12 6B1192FC          .  51            push ecx
13 6B1192FD          .  8D55 F8       lea edx, dword ptr ss:[ebp-8]
14 6B119300          .  52            push edx
15 6B119301          .  50            push eax
16 6B119302          .  FFD6          call near esi
17 6B119304          .  8B45 FC       mov eax, dword ptr ss:[ebp-4]
18 6B119307          .  85C0          test eax, eax
19 6B119309          .^ 74 E5         je short WMCore.6B1192F0                 ;  // 这里导致死循环了~
20 6B11930B          .  8B55 F8       mov edx, dword ptr ss:[ebp-8]
21 6B11930E          .  8B40 14       mov eax, dword ptr ds:[eax+14]
22 6B119311          .  E8 EAF7FFFF   call WMCore.6B118B00
23 6B119316          .^ EB D8         jmp short WMCore.6B1192F0

通过帮助文档介绍我们知道 kernel32.GetQueuedCompletionStatus 这个函数
的意思是从指定的IOCP获取CP,当CP队列为空时.对此函数的调用将被阻塞.
阻塞?没错,阻塞就不会占用CPU100%,但是他的IO句柄是父窗口创建的 父窗口关闭了,也把句柄析构了.
他的逻辑写的是"死循环",所以句柄不存在了,就会不停的重复调用.也就会CPU占用100%了.



0x3 修复方案
1) 那个窗口改成模态窗口
2) 父窗口关闭时,应该先提前关闭非模态窗口.



这个应该也不能算是BUG吧,只能算是我误操作.  
而且一般人应该也不会像我那样操作.直接关闭父窗口的  

--==mount英文官方==--
--==mount中文官方==--
--==mount官方招聘==--

才发现,英文版的是PAGE是2011更新的.
中文版的PAGE是2010更新的  

原文地址:https://www.cnblogs.com/BjblCracked/p/3269132.html