欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

密码监听器注册算法分析

程序员文章站 2022-07-03 11:34:38
 本篇文章源自《黑客防线》2007年10月刊转载请注明版权 文/图 落叶树===================================    ...

 
本篇文章源自《黑客防线》2007年10月刊
转载请注明版权

文/图 落叶树
===================================
        Pswmonitor 3.0是一款用于监听网页密码的软件,只需在一台电脑上运行,就可以监听局域网内任意一台电脑登录的账号和密码,并将密码显示、保存,或发送到用户指定的邮箱。开发者很厚道,未注册版本除了监听到的最后一位密码显示为“?”外没做什么限制,虽然如此,但是每次都要穷举几十次最后一位也是很麻烦的,穷人自有穷办法,动手吧。
        用PEiD侦测显示软件为ASPACK加壳,用PEiD自带的通用脱壳插件脱掉即可,如图1所示。通用脱壳插件使用很简单,先侦测OEP,再点unpack就可以了,通常大多数壳都能脱掉。这里我们需要重建一下导入表,再用PEiD看看脱壳后的文件,显示用Microsoft Visual C++ 6.0编写,运行,正常。OK,脱壳的工作就完成了。
 
                                图1
        分析前我们需要获取一些相关信息。运行软件,马上出来的就是注册界面,随便输入一些东西点“确定”,关键的东西出来了:“注册失败!”。除了这个重要信息外,我们还可以发现用户名输入的字母只会显示大写,而注册码只能输入数字,这在我们的破解过程中应该有用,先记下来。
        用OllyDBG载入软件,点击“右键搜索搜索所有参考文本字符串”,粗略看了一下没什么发现,继续用超级字符串查找插件(Ultra String Reference)搜索ASCII字符,这次找到了,如图2所示。这里有三处“注册失败!”字符串,有一处“您成功注册!”字符串,我们该怎么选择呢?一般来说都是选失败的,为什么呢?稍微想想就知道了,我们看到的是“注册失败”啊,这也正是我们要中断的,如果随便输入都能出来个“您成功注册!”,那我建议您去买彩票,^_^。
  
                        图2
问题是我们要怎么下断点呢?很简单,有时笨方法未必不是好办法,我们全部下断,然后慢慢试。结果软件显示“注册失败”时断在了004104D1处,往上一瞧,发现偏移地址004104C4是从0041032D跳过来的,我们前往那里看看吧。呵呵,0041032A处是一个比较,看来是这里了,那么我们怎么找这段判断注册码的代码开始处呢?用RETN规律一路往上,直到发现第一个RETN,它下面004100F0处有对esp的操作,我们就在这里下断吧。所谓RETN规律就是汇编代码中每段子程序的结束处都要用ret指令返回上层调用,而每段子程序的开始一般都是push ebp、mov ebp,esp、sub ebp,n或sub esp,n或add ebp,-n这类堆栈操作,所以我们判断这是注册算法代码的开始处。下断后按Ctrl+F2重新运行,输入用户名注册码,确定后果然中断在这里,万事开头难,我们已经开好头了,接下来就是慢慢分析咯。一路F8,下面的分析省去了机器码。

00410147   call <jmp.&MFC42.#6282>
;取用户名到eax
0041014C   lea ecx,dword ptr ss:[ebp-10]
0041014F   call <jmp.&MFC42.#6283>
;取注册码到eax
00410154   mov eax,dword ptr ss:[ebp-14]
00410157   cmp dword ptr ds:[eax-8],edi
0041015A   je pswmonit.004104DD
00410160   mov eax,dword ptr ss:[ebp-10]
00410163   cmp dword ptr ds:[eax-8],edi
00410166   je pswmonit.004104DD
;这两个比较用户名和密码是否为空,为空显示注册失败
0041016C   lea eax,dword ptr ss:[ebp-2C]
0041016F   push 2
00410171   push eax
00410172   lea ecx,dword ptr ss:[ebp-10]
00410175   call <jmp.&MFC42.#4129>
;取注册码前两位
0041017A   mov eax,dword ptr ds:[eax]
0041017C   mov esi,dword ptr ds:[<&msvcrt._mbscmp>]
00410182   push pswmonit.00419A0C
00410187   push eax
00410188   call esi
0041018A   pop ecx
0041018B   pop ecx
0041018C   test eax,eax
0041018E   lea ecx,dword ptr ss:[ebp-2C]
00410191   setne bl
00410194   call <jmp.&MFC42.#800>
00410199   test bl,bl
0041019B   je short pswmonit.004101A8
;如果注册码前两位不等于30则失败

从这里可以得知注册码的前两位,重新来一遍吧。输入新的30xxxx注册码后接着上面的过程,我们来到下面的代码处。

004101D2   call <jmp.&MFC42.#4202>
;用户名转成小写
004101D7   xor ebx,ebx
004101D9   cmp dword ptr ds:[41CC10],edi
004101DF   jle short pswmonit.00410220
004101EB   call pswmonit.00407196
004101F0   lea ecx,dword ptr ss:[ebp-24]
004101F3   mov byte ptr ss:[ebp-4],3
004101F7   call <jmp.&MFC42.#4202>
004101FC   push dword ptr ss:[ebp-14]
004101FF   lea ecx,dword ptr ss:[ebp-24]
00410202   call <jmp.&MFC42.#2764>
00410207   test eax,eax
00410209   jge short pswmonit.0041026F
0041020B   lea ecx,dword ptr ss:[ebp-24]
0041020E   mov byte ptr ss:[ebp-4],1
00410212   call <jmp.&MFC42.#800>
00410217   inc ebx
00410218   cmp ebx,dword ptr ds:[41CC10]
; 取黑名单用户名进行比较
0041021E   jl short pswmonit.004101E1

这段比较好玩,如果你输入的用户名和黑名单里的相同就注册失败,十多个名字中有很多大家都熟悉的。通过了黑名单的考验后,继续F8往下走。

004102AE   mov ebx,pswmonit.00419A00
;ASCII "whm_w"
004102B3   lea ecx,dword ptr ss:[ebp-14]
004102B6   push ebx
004102B7   call <jmp.&MFC42.#941>
;用户名连接固定字符串whm_w
004102BC   mov eax,dword ptr ss:[ebp-14]

这里出现了一串固定字符,连接到我们输入的用户名,有什么作用呢?还是往下看。

004102CB   movsx esi,byte ptr ds:[ecx+eax]
004102CF   add dword ptr ss:[ebp-24],esi
;用户名每个字符的ASCII值相加。
004102D2   inc ecx
004102D3   cmp ecx,edx
004102D5   jl short pswmonit.004102CB

原来是将用户名ASCII码求一个值放到[esp-24]里,这个值我们假设为SUM,接下来可以说是算法核心了。
004102D7   xor edi,edi  ;清空EDI
004102D9   mov eax,dword ptr ss:[ebp-10]
;取除开头30的剩余注册码
004102DC   lea ecx,dword ptr ss:[ebp-10]
004102DF   mov eax,dword ptr ds:[eax-8]
;剩余注册码长度
004102E2   add eax,-2  ;长度减掉2
004102E5   push eax
004102E6   lea eax,dword ptr ss:[ebp-2C]
004102E9   push edi
004102EA   push eax
004102EB   call <jmp.&MFC42.#4278>
;得到除去最后两位后的注册码
004102F0   push dword ptr ds:[eax]
004102F2   mov esi,dword ptr ds:[<&msvcrt.atol>]
004102F8   call esi
;atol函数把字符转成数值
004102FA   pop ecx
004102FB   mov edi,eax  ;数值放到EDI
004102FD   lea ecx,dword ptr ss:[ebp-2C]
00410300   call <jmp.&MFC42.#800>
00410305   lea eax,dword ptr ss:[ebp-2C]
00410308   push 2
0041030A   push eax
0041030B   lea ecx,dword ptr ss:[ebp-10]
0041030E   call <jmp.&MFC42.#5710>
;取最后两位注册码
00410313   push dword ptr ds:[eax]
00410315   call dword ptr ds:[<&msvcrt.atoi>]
;atoi函数转换
0041031B   pop ecx
0041031C   mov dword ptr ss:[ebp-28],eax
0041031F   lea ecx,dword ptr ss:[ebp-2C]
00410322   call <jmp.&MFC42.#800>
00410327   xor edi,dword ptr ss:[ebp-28]
;EDI与注册码最后两位进行异或
0041032A   cmp dword ptr ss:[ebp-24],edi
;SUM与EDI比较
0041032D   jnz pswmonit.004104C4
;不相等则跳向失败。

关键