星号密码查看器原理完全揭秘

星号密码一般分为窗口和网页两种,密码一般以“*”字符显示,也有些以其它字符比如圆点或#显示,在编程中可以设置任意字符为密码掩码。密码框一般为编辑框(Edit),也叫文本框(TextBox),也有些是自绘框,这里我们只看标准Edit,密码框具有一个ES_PASSWORD的样式,普通Edit没有。但是,并不是所有显示“*”号的都是密码框,有些看似是密码框,其实是伪密码框,或者说是“真”星号,就是说它根本就是一个普通的Edit,密码原本就是*字符,而不是掩码,这种框用句柄工具(ViewWizard)查看,看到的是*号,而看不到真正的密码,这种情况一般是设计者为了保留密码位数而临时设置的,单击它会直接清空密码,因为它里面存放的是*,如果不清空程序就会把*当成是真正的密码,从而发生错误,所以对于这种情况,是看不到密码的。

 

    如何查看原本的密码呢?我们可以用一个消息:EM_GETPASSWORDCHAR和EM_SETPASSWORDCHAR,一个是获取密码字符,一个是设置密码字符,获取到的是密码掩码的ASCII码,要显示密码,我们先将密码框设置为0字符,将它变成一个普通的文本框,然后真正的密码就显示出来了,然后再用常规的WM_GETTEXT消息,即可复制密码框文本,之后再还原,否则密码就会一直显示。顺便说一下WM_GETTEXT消息,这个消息是用来获取编辑框的内容,但是它也可以用来获取“窗口”内容,实际上编辑框也是窗口,但是在获取和设置文本时稍有不同。普通的窗口(非Edit控件)获取和设置文本一般用GetWindowText和SetWindowText,获取窗口文本长度用GetWindowTextLength,包括Button(按钮)、Static(静态控件)等都可以用这些函数,但Edit比较特殊,因为它的内容比较多,不像窗口标题一样,只有一行,所以Edit文本不能用上面说的函数,而必须借助WM_GETTEXT和WM_SETTEXT消息实现,获取长度用WM_GETTEXTLENGTH,后者可兼容前者,用后者的方法也可对前者进行修改,反过来不行。但是,GetWindowText并非间接调用WM_GETTEXT,设置也一样,你可以在窗口函数中hook掉WM_GETTEXT或WM_SETTEXT,别的程序便不能控制你的窗口内容了,但用Get/Set之类的仍然能改变或获取你的窗口标题。

 

    到现在你可能以为可以自己做一个星号密码查看器了,其实问题没那么简单,还有个搜索问题需要解决。要查看密码,就要先找到密码窗口,如何搜索窗口呢?星号查看器程序一般用WindowFromPoint来获取鼠标所在位置的窗口,获取鼠标位置可以用GetCursorPos,当然有人喜欢用钩子,钩子虽灵活但容易被拦截,这里我们用GetCursorPos+定时器。接下来,你会发现有些窗口无法获取,比如WinRAR的设置密码窗口,这是为什么呢?其实WinRAR并没有拦截我们的代码,而是WinRAR的界面有许多“框架”窗口,这些窗口是透明的,而且比其它控件都要大,所以挡住了其它控件,而WindowFromPoint的搜索顺序是按窗口Z轴进行的,也就是先搜索最上层的窗口或控件,所以虽然后面的控件能够看到,可是却获取不到。如何才能做到精准搜索呢?我们可以自己编一个函数,当发现一个子窗口,就递归搜索它的兄弟窗口,看有没有比它Size小的,有的话就把小的当作搜索目标,问题就解决了。然而,很少有软件解决了这个问题,我知道的能精确搜索的软件有微软的Spy++,还有我自己的ViewWizard,还有个WndSpy等。不过,测试发现Spy++还是有漏洞的,或者说不算太精确,有些窗口它仍然搜索不到,比如某些无效(Disabled)的控件。如此,问题算基本上解决了,但还有一个问题,就是透明窗口问题,有些窗口具有WS_EX_TRANSPARENT样式,鼠标是点击不到的,用WindowFromPoint是搜索不到的,ChildWindowFromPointEx函数说是能够搜索透明窗口,实际测试仍然搜索不到,这个问题虽然不是很重要,但要解决还是很容易的,只需重新遍历一次目标窗口之上的顶层窗口即可。透明窗口目前只有ViewWizard工具能够搜索,尚无发现有其它软件支持透明窗口的搜索。除了搜索之外,还可以用其它方法,例如遍历,这种方法不存在上述的问题,但是遍历也有不同的方法,例如典型的EnumWindows/EnumChildWindows,这两个函数都无法获取隐藏窗口(这里的隐藏指进程在内核中hook了NtUserQueryWindow,而非窗口的Style属性),但GetWindow却能获取,不过这一点并不影响我们获取密码,可以不考虑这个。另外,星号查看器程序只适用于标准Edit控件,如果密码框非以上标准Windows控件,比如是自写控件,非窗口类,那用以上方法就无法获取密码了。

 

    好了,窗口密码查看器已经实现了,接下来看网页密码。能否获取网页密码和浏览器有关,不同的浏览器方法不同,这里以IE为例,谈一下如何查看IE网页中的星号密码。要获取网页对象,在工程中需要引用一个mshtml.tlb的库,然后就能操作一个网页元素了,把页面元素复制过来,判断是否包含密码(password)元素,有则是密码,没有则不代表一定没有密码,因为password类型可以改成别的,不过没关系,只要能看到星号,我们就能查看密码,只要遍历页面所有元素,就能找到密码值。然而,网页元素也要面对一个窗口对象所遇到的问题,前面说了,框架形式的窗口会阻碍我们搜索下层的窗口,同样的,框架页面元素也会屏蔽掉它里面的子元素,框架类(IFRAME)也是一个元素,它本身又包含另一个页面对象,所以搜索到框架,我们就必须递归寻找最内层的元素,这样才能找到最终的元素。实际上,用这种方法,不但能做一个网页密码查看器,还能做一个网页Spy++。

 

    最后说一下如何防范星号查看器程序。对设计者而言,最简单的方法是做一个假的密码框,也就是用一个普通的编辑框代替密码框,里面直接输入*字符,不放密码,只为显示密码长度用。还有种方法是Hook掉WM_GETTEXT,让其它进程无法获取自己的文本。按照上面的方法,我共制作了五个版本的星号查看器程序,还差最后一个没研究出来,就是暴力读内存强制获取密码,但不同的程序需要完全不同的方法,所以只研究了记事本程序(notepad),发现notepad程序的Edit控件文本地址一直位于Edit窗口字节值+0x4F0处(WinXP/SP3下文本偏移始终为0xAABE8),直接读取该地址处的值就是文本框的内容。

  

以下是5个版本的核心代码:

 

'http://zzmzzff.blog.163.com

 

'版本1 跟踪焦点获取密码

 

Public Function m_ReadPwd() As String

        Dim hWndSrc     As Long

        Dim hEdit       As Long

        Dim ThreadId    As Long

        Dim nLength     As Long

        Dim ProcessId   As Long

        Dim CurTID      As Long

       

        hWndSrc = GetForegroundWindow()

        If hWndSrc = 0 Then Exit Function

       

        CurTID = GetCurrentThreadId()

        ThreadId = GetWindowThreadProcessId(hWndSrc, ByVal 0)

        If CurTID = ThreadId Then m_ReadPwd = Chr(0): Exit Function

       

        AttachThreadInput CurTID, ThreadId, True

        hEdit = GetFocus()

        If hEdit <> 0 Then

                Dim nChar       As Long

                Dim szText      As String

                If GetWindowLong(hEdit, GWL_STYLE) And ES_PASSWORD Then '是密码框

                        nChar = SendMessage(hEdit, EM_GETPASSWORDCHAR, 0, ByVal 0&)

                        If nChar <> 0 Then

                                PostMessage hEdit, EM_SETPASSWORDCHAR, 0, 0

                                Sleep 10

                                nLength = SendMessage(hEdit, WM_GETTEXTLENGTH, 0, ByVal 0&)

                                If nLength > 0 Then

                                        szText = String(nLength + 1, Chr(0))

                                        Call SendMessage(hEdit, WM_GETTEXT, nLength + 1, ByVal szText)

                                        szText = Left(szText, InStr(szText, Chr(0)) - 1)

                                End If

                                PostMessage hEdit, EM_SETPASSWORDCHAR, nChar, 0

                        End If

                End If

        End If

        AttachThreadInput CurTID, ThreadId, False

        m_ReadPwd = szText

End Function

 

'版本2 自动获取

 

'搜索密码

Public Function m_SearchPwd(Optional ByVal bForceSearch As Boolean = True) As String

        Dim hWndSrc     As Long

        Dim ThreadId    As Long

        Dim nLength     As Long

        Dim CurTID      As Long

        Dim pt          As POINTAPI

        

        GetCursorPos pt

        hWndSrc = WindowFromPoint(pt.x, pt.y) '获取鼠标所在窗口

       

        '检查窗口,以获取深层/透明/无效窗口,有些控件Z序和尺寸不谐调,默认只能获取上层控件,例如WinRAR的密码窗口,此法可获取任意窗口

        If bForceSearch Then m_CheckWnd hWndSrc, pt.x, pt.y

        If hWndSrc = 0 Then Exit Function

       

        CurTID = GetCurrentThreadId()

        ThreadId = GetWindowThreadProcessId(hWndSrc, ByVal 0)

        If CurTID = ThreadId Then m_SearchPwd = Chr(0): Exit Function '如果是自己退出

       

        nLength = SendMessage(hWndSrc, WM_GETTEXTLENGTH, 0, ByVal 0&) '长度

        If nLength <= 0 Then Exit Function

       

        Dim nChar       As Long

        Dim szText      As String

        szText = String(nLength + 1, Chr(0))

        If GetWindowLong(hWndSrc, GWL_STYLE) And ES_PASSWORD Then

                nChar = SendMessage(hWndSrc, EM_GETPASSWORDCHAR, 0, ByVal 0&) '是否密码框

                If nChar <> 0 Then

                        PostMessage hWndSrc, EM_SETPASSWORDCHAR, 0, 0

                        Sleep 10

                End If

        End If

        '这次取所有窗口文本

        Call SendMessage(hWndSrc, WM_GETTEXT, nLength + 1, ByVal szText)

        If nChar <> 0 Then PostMessage hWndSrc, EM_SETPASSWORDCHAR, nChar, 0

        szText = Left(szText, InStr(szText, Chr(0)) - 1)

        m_SearchPwd = szText

End Function

 

'检查窗口

Sub m_CheckWnd(ByRef hWinCheck As Long, x As Long, y As Long, Optional bSearchHidden As Boolean, Optional bSearchThrough As Boolean)

        Dim hWin                As Long

        Dim hWinParent          As Long

        Dim hWinChild           As Long

        Dim lpRectChild         As RECT

        Dim lpRectWin           As RECT

       

        '是否搜索透明窗口,以获取最上层窗口 //这个可以省略,一般用不上,要达到完美的话添上

        If bSearchThrough Then

                CheckTransparentWin hWinCheck, x, y

        End If

       

        '检查子窗口,以便获取灰色窗口

        hWin = hWinCheck

        CheckSubWin hWin, x, y

       

        '获取最深兄弟窗口,如果是控件再进行更深层次搜索

        If IsHaveCaption(hWin) = False Then

                hWinParent = GetAncestor(hWin, GA_PARENT)

                'Debug.Print "hWinParent=", hWinParent

                If hWinParent = GetDesktopWindow() Then

                        hWinParent = hWin

                End If

                GetWindowRect hWin, lpRectWin

                hWinChild = GetWindow(hWinParent, GW_CHILD)

                Do While hWinChild <> 0

                        If hWinChild <> hWin Then

                                '是否搜索隐藏窗口 //注意,此处不能用IsWindowVisible,父窗隐藏后将无法知道子窗的显示状态

                                If Not bSearchHidden Imp (GetWindowLong(hWinChild, GWL_STYLE) And WS_VISIBLE) <> 0 Then

                                        GetWindowRect hWinChild, lpRectChild

                                        If PtInRect(lpRectChild, x, y) Then

                                                If PtInRect(lpRectWin, lpRectChild.Left, lpRectChild.Top) <> 0 And _

                                                        PtInRect(lpRectWin, lpRectChild.Right, lpRectChild.Bottom) <> 0 Then

                                                        hWin = hWinChild

                                                        'Debug.Print "目标窗口=", hWin

                                                        Exit Do

                                                End If

                                        End If

                                End If

                        End If

                        hWinChild = GetWindow(hWinChild, GW_HWNDNEXT)

                Loop

        End If

       

        If hWin <> hWinCheck Then

                hWinCheck = hWin

                m_CheckWnd hWinCheck, x, y '递归

        End If

End Sub

 

'检查子窗口

Private Sub CheckSubWin(hWin As Long, x As Long, y As Long, Optional bSearchHidden As Boolean)

        Dim hChild      As Long

        Dim lpRect      As RECT

        hChild = GetWindow(hWin, GW_CHILD)

        Do While hChild <> 0

                If Not bSearchHidden Imp (GetWindowLong(hChild, GWL_STYLE) And WS_VISIBLE) <> 0 Then

                        GetWindowRect hChild, lpRect

                        If PtInRect(lpRect, x, y) Then

                                hWin = hChild

                                Exit Do

                        End If

                End If

                hChild = GetWindow(hChild, GW_HWNDNEXT)

        Loop

End Sub

 

'检查穿透窗口

Private Sub CheckTransparentWin(hWin As Long, x As Long, y As Long)

        Dim hTop        As Long

        Dim lpRect      As RECT

        hTop = GetAncestor(hWin, GA_ROOT)

        hTop = GetWindow(hTop, GW_HWNDPREV)

        Do While hTop <> 0

                If IsWindowVisible(hTop) <> 0 Then

                        GetWindowRect hTop, lpRect

                        If PtInRect(lpRect, x, y) Then

                                hWin = hTop

                                CheckTransparentWin hWin, x, y

                        End If

                End If

                hTop = GetWindow(hTop, GW_HWNDPREV)

        Loop

End Sub

 

'有标题栏

Private Function IsHaveCaption(ByVal hWin As Long) As Boolean

        IsHaveCaption = ((GetWindowLong(hWin, GWL_STYLE) And WS_CAPTION) = WS_CAPTION)

End Function

 

'版本3  //一次性获取

 

'获取所有密码

Public Function m_EnumPwd(Optional ByVal bShowPwd As Boolean) As String

        Dim hWndSrc     As Long

        Dim nLength     As Long

        Dim szText      As String

        Dim szTilte     As String

        Dim szPwd       As String

       

        hWndSrc = FindWindow(vbNullString, vbNullString)

        Do While hWndSrc <> 0

                szPwd = ""

                m_GetText hWndSrc, szPwd, bShowPwd

                If Not bShowPwd Then

                        If szPwd <> "" Then

                                nLength = GetWindowTextLength(hWndSrc)

                                If nLength > 0 Then

                                        If nLength > 255 Then nLength = 255 '取窗口标题,只取前255个

                                        szTilte = String(nLength + 1, Chr(0))

                                        Call GetWindowText(hWndSrc, szTilte, nLength + 1)

                                        szTilte = Left(szTilte, InStr(szTilte, Chr(0)) - 1)

                                End If

                                szText = szText & "窗口 " & szTilte & " 的密码:" & vbCrLf & szPwd & vbCrLf

                        End If

                End If

                hWndSrc = GetWindow(hWndSrc, GW_HWNDNEXT)

        Loop

        m_EnumPwd = szText

End Function

 

Sub m_GetText(ByVal hWndSrc As Long, szPwd As String, Optional bShowPwd As Boolean)

        Dim nLength     As Long

        Dim nChar       As Long

        Dim szText      As String

       

        hWndSrc = GetWindow(hWndSrc, GW_CHILD)

        Do While hWndSrc <> 0

                If GetWindowLong(hWndSrc, GWL_STYLE) And ES_PASSWORD Then

                        If SendMessageTimeout(hWndSrc, WM_NULL, 0, 0, SMTO_ABORTIFHUNG Or SMTO_BLOCK, 500, ByVal 0&) <> 0 Then

                                nChar = SendMessage(hWndSrc, EM_GETPASSWORDCHAR, 0, ByVal 0&)

                                If nChar <> 0 Then '是密码框

                                        If Not bShowPwd Then

                                                nLength = SendMessage(hWndSrc, WM_GETTEXTLENGTH, 0, ByVal 0&) '长度

                                                szText = ""

                                                If nLength > 0 Then

                                                        szText = String(nLength + 1, Chr(0))

                                                        PostMessage hWndSrc, EM_SETPASSWORDCHAR, 0, 0

                                                        Sleep 10

                                                        Call SendMessage(hWndSrc, WM_GETTEXT, nLength + 1, ByVal szText)

                                                        PostMessage hWndSrc, EM_SETPASSWORDCHAR, nChar, 0

                                                        szText = Left(szText, InStr(szText, Chr(0)) - 1)

                                                End If

                                                szPwd = szPwd & "密码: " & szText & vbCrLf

                                        Else

                                                PostMessage hWndSrc, EM_SETPASSWORDCHAR, 0, 0

                                                PostMessage hWndSrc, WM_SIZE, 0, 0

                                        End If

                                End If

                        End If

                End If

                m_GetText hWndSrc, szPwd, bShowPwd '递归

                hWndSrc = GetWindow(hWndSrc, GW_HWNDNEXT)

        Loop

End Sub

 

'版本4 //获取网页密码

 

'获取所有密码 mod_Main

Public Function m_EnumWebPwd() As String

        Dim hWndSrc     As Long

        Dim hWndIESer   As Long

        Dim nLength     As Long

        Dim szText      As String

        Dim szTilte     As String

        Dim ProcessId   As Long

       

        hWndSrc = FindWindow(vbNullString, vbNullString)

        Do While hWndSrc <> 0

                szPwd = ""

                EnumSubWnd hWndSrc

                If szPwd <> "" Then

                        GetWindowThreadProcessId hWndSrc, ProcessId

                        nLength = GetWindowTextLength(hWndSrc)

                        If nLength > 0 Then

                                If nLength > 255 Then nLength = 255 '取窗口标题,只取前255个

                                szTilte = String(nLength + 1, Chr(0))

                                Call GetWindowText(hWndSrc, szTilte, nLength + 1)

                                szTilte = Left(szTilte, InStr(szTilte, Chr(0)) - 1)

                        End If

                        szText = szText & "句柄: " & hWndSrc & " 进程: " & ProcessId & " 网页标题: " & szTilte & vbCrLf & "密码: " & szPwd & vbCrLf

                End If

                hWndSrc = GetWindow(hWndSrc, GW_HWNDNEXT)

        Loop

        m_EnumWebPwd = szText

End Function

 

Private Function EnumSubWnd(ByVal hWin As Long) As Long

        EnumChildWindows hWin, AddressOf EnumWinCallback, ByVal 0

End Function

 

Public Function EnumWinCallback(ByVal MyhWnd As Long, lParam As Long) As Long

        If IsIEServerHwnd(MyhWnd) Then

                m_ReadWebPwd MyhWnd, szPwd

        End If

        EnumWinCallback = 1

End Function

 

'IEServer窗口

Private Function IsIEServerHwnd(ByVal hWin As Long) As Boolean

        IsIEServerHwnd = GetWinClass(hWin) = "Internet Explorer_Server"

End Function

 

'mod_Html

'获取密码

Public Function m_ReadWebPwd(ByVal hWin As Long, szText As String)

        'On Error Resume Next

        Dim Doc         As IHTMLDocument

       

        Set Doc = IEDOMFromhWnd(hWin)

       

        Call GetPwdByDocument(Doc, szText)

End Function

 

Sub GetPwdByDocument(ByVal Doc As IHTMLDocument, szText As String)

        On Error Resume Next

        If Doc Is Nothing Then Exit Sub

        Dim SubDoc      As IHTMLDocument

        Dim Ele         As IHTMLElement

        Dim EleFra      As IHTMLIFrameElement3

        Dim szPwd       As String

        For Each Ele In Doc.All

                If Ele.tagName = "IFRAME" Then '如果是框架,继续递归获取下一级页面

                        Set EleFra = Ele

                        Set SubDoc = EleFra.contentDocument

                        GetPwdByDocument SubDoc, szText

                Else

                        If Ele.getAttribute("type") = "password" Then

                                szPwd = CStr(Ele.getAttribute("value"))

                                If szPwd = "" Then szPwd = Ele.innerText

                                szText = szText & szPwd & vbCrLf

                        End If

                End If

        Next

End Sub

 

'版本5 //自动获取网页密码

 

'搜索密码

Public Function m_SearchPwd() As String

        Dim hWndSrc     As Long

        Dim pt          As POINTAPI

        GetCursorPos pt

        hWndSrc = WindowFromPoint(pt.x, pt.y) '获取鼠标所在窗口

        If IsIEServerHwnd(hWndSrc) Then

                ScreenToClient hWndSrc, pt

                m_SearchPwd = m_GetWebPwd(hWndSrc, pt.x, pt.y)

        End If

End Function

 

'获取密码

Public Function m_GetWebPwd(ByVal hWin As Long, x As Long, y As Long) As String

        'On Error Resume Next

        Dim Doc         As IHTMLDocument

        Dim szText      As String

       

        Set Doc = IEDOMFromhWnd(hWin)

       

        Call GetPwdByPoint(Doc, x, y, szText)

        m_GetWebPwd = szText

End Function

 

Private Sub GetPwdByPoint(ByVal Doc As IHTMLDocument2, ByVal x As Long, ByVal y As Long, szText As String)

        On Error Resume Next

        If Doc Is Nothing Then Exit Sub

        Dim SubDoc      As IHTMLDocument

        Dim Ele         As IHTMLElement

        Dim EleIFrame   As IHTMLIFrameElement3

        Dim rt          As Object

        

        Set Ele = Doc.elementFromPoint(x, y)

        If Not (Ele Is Nothing) Then

                Debug.Print Ele.offsetLeft, Ele.offsetTop

                If Ele.tagName = "IFRAME" Then '如果是框架,继续递归获取下一级页面

                        Set EleIFrame = Ele

                        Set SubDoc = EleIFrame.contentDocument

                        Set rt = Ele.getBoundingClientRect

                        x = x - rt.Left

                        y = y - rt.Top

                        GetPwdByPoint SubDoc, x, y, szText

                Else

                        'Debug.Print x, y, Ele.offsetLeft, Ele.offsetTop

                        If Not IsNull(Ele.getAttribute("value")) Then

                                szText = Ele.getAttribute("value")

                        Else

                                szText = Ele.innerText

                        End If

                End If

        End If

End Sub

'***************** 核心代码结束 ************************

 

完整代码:http://pan.baidu.com/s/1hsFOAfa

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页