• 标 题: 【原创】菜鸟也算法分析 -- 局域网查看工具(LanSee V1.52
  • 作 者:prince
  • 时 间:2005-01-13 11:03

菜鸟也算法分析 -- 局域网查看工具(LanSee V1.52)注册算法分析
  
  软件说明:

  局域网查看工具(LanSee)是一款主要用于对局域网(Internet上也适用)上的各种信息进行查看的工具。采用多线程技术,搜索速度很快。它将局域网上比较实用的功能完美地融合在一起,比如搜索计算机(包括计算机名,IP地址,MAC地址,所在工作组,用户),搜索共享资源,搜索共享文件,多线程复制文件(支持断点传输),发短消息,高速端口扫描,捕获指定计算机上的数据包,查看本地计算机上活动的端口,远程重启/关闭计算机等,功能十分强大。该软件是一款绿色软件,解压后直接打开运行,无需安装。

  软件信息:

  未加壳,用Delphi编译器编写。

  平台:Windows XP SP1
  工具:W32Dasm 8.93,OLLYDBG1.10

  分析说明:
  
  仅仅分析算法,进行技术交流,不做商业用途。如果你条件允许并喜欢这个软件的话,请支持正版。

  Cracker: prince
  E-mail : Cracker_prince@163.com

  V1.52版下载地址:http://www.yqdown.com/soft/7854.htm

  W32Dasm加载反汇编,根据提示信息很容易找到爆破点。根据提示来到这里(然后用OD下断00492307,F9运行调试,输入用户名prince,注册码:987654321012):
................................................
:00492307 55                      push ebp
:00492308 68FD234900              push 004923FD
:0049230D 64FF30                  push dword ptr fs:[eax]
:00492310 648920                  mov dword ptr fs:[eax], esp
:00492313 8D55FC                  lea edx, dword ptr [ebp-04]
:00492316 8B83FC020000            mov eax, dword ptr [ebx+000002FC]
:0049231C E83B14FBFF              call 0044375C                       <--- 获得用户输入的注册码个数
:00492321 8B45FC                  mov eax, dword ptr [ebp-04]         <--- [ebp-04]中是用户输入的注册码
:00492324 E82FFFFFFF              call 00492258                       <--- 像这种下面比较完就跳的CALL最可疑,跟进
:00492329 84C0                    test al, al
:0049232B 0F84A7000000            je 004923D8                         <--- 这里跳到错误提示(爆破点之一)
:00492331 8D55F8                  lea edx, dword ptr [ebp-08]
:00492334 8B830C030000            mov eax, dword ptr [ebx+0000030C]
:0049233A E81D14FBFF              call 0044375C                       <--- 得到用户名的长度
:0049233F 8B45F8                  mov eax, dword ptr [ebp-08]
:00492342 E8CDFDFFFF              call 00492114                       <--- 关键CALL,跟进
:00492347 84C0                    test al, al 
:00492349 0F8489000000            je 004923D8                         <--- 这里跳到错误提示(爆破点之一)
:0049234F B201                    mov dl, 01
:00492351 A1AC014700              mov eax, dword ptr [004701AC]
...................................................................
根据省力不蛮干的原则,前几遍跟踪都是粗跟,不是十分可疑的CALL都F8带过,但是要注意CALL过之后寄存器的变化,尤其是EAX寄存器的值,必要时把值注释在旁边。这个软件检测注册的地方交互调用很多,如果上来就都F7跟进的话,呵呵,很快你就会晕了。继续分析上面的代码,在0049231C这个CALL时,F8带过,看EAX寄存器,变成了多少?0XC,嘿嘿,0XC不就是十进制的12吗?我们“恰好”也输入了12个字符作为注册码,明白了吧?这个CALL就是用来计算用户输入的注册码个数的。执行完下面那一句,EAX中就是我们输入的假码了。再往下又是一个CALL,而且下面比较完会跳到出错的地方,足够可疑!跟进!来到这里:
...................................................................
:00492258 55                      push ebp
:00492259 8BEC                    mov ebp, esp
:0049225B 83C4F8                  add esp, FFFFFFF8
:0049225E 53                      push ebx
:0049225F 33D2                    xor edx, edx
:00492261 8955F8                  mov dword ptr [ebp-08], edx
:00492264 8945FC                  mov dword ptr [ebp-04], eax
:00492267 8B45FC                  mov eax, dword ptr [ebp-04]
:0049226A E85527F7FF              call 004049C4                    <--- 这个CALL是干什么的?不管它,先F8带过
:0049226F 33C0                    xor eax, eax
:00492271 55                      push ebp
:00492272 68E1224900              push 004922E1
:00492277 64FF30                  push dword ptr fs:[eax]
:0049227A 648920                  mov dword ptr fs:[eax], esp
:0049227D 33DB                    xor ebx, ebx
:0049227F 8D55F8                  lea edx, dword ptr [ebp-08]
:00492282 8B45FC                  mov eax, dword ptr [ebp-04]
:00492285 E84E68F7FF              call 00408AD8                    <--- 未知,先带过
:0049228A 8B45F8                  mov eax, dword ptr [ebp-08]
:0049228D E84A25F7FF              call 004047DC                    <--- 未知,同样带过,但是注意EAX变成了0XC,也就是用户输入的注册码的个数
:00492292 83F80C                  cmp eax, 0000000C                <--- 个数同0XC(12)进行比较,
:00492295 7C2F                    jl 004922C6                      <--- 小于12个就跳走,哈哈,这下知道为什么输入12个字符作为注册码了吧?
:00492297 8B45F8                  mov eax, dword ptr [ebp-08]      <--- [ebp-8]为假码
:0049229A 80780131                cmp byte ptr [eax+01], 31        <--- 将假码的第2位同ASCII码0X31(数字1)进行比较
:0049229E 7526                    jne 004922C6         <--- 不等就失败啦!
:004922A0 8B45F8                  mov eax, dword ptr [ebp-08]
:004922A3 80780439                cmp byte ptr [eax+04], 39        <--- 将假码的第5位同ASCII码0X39(数字9)进行比较
:004922A7 751D                    jne 004922C6         
:004922A9 8B45F8                  mov eax, dword ptr [ebp-08]
:004922AC 80780639                cmp byte ptr [eax+06], 39        <--- 将假码的第7位同ASCII码0X39(数字9)进行比较
:004922B0 7514                    jne 004922C6
:004922B2 8B45F8                  mov eax, dword ptr [ebp-08]
:004922B5 80780737                cmp byte ptr [eax+07], 37        <--- 将假码的第8位同ASCII码0X37(数字7)进行比较
:004922B9 750B                    jne 004922C6
:004922BB 8B45F8                  mov eax, dword ptr [ebp-08]
:004922BE 80780935                cmp byte ptr [eax+09], 35        <--- 将假码的第10位同ASCII码0X35(数字5)进行比较
:004922C2 7502                    jne 004922C6
:004922C4 B301                    mov bl, 01
...................................................................
看到这里你可能感到奇怪:怎么不用根据用户名计算注册码吗?怎么直接就比较用户输入的注册码了?呵呵,其实这个软件的注册码比较部分到这就全部结束了。别急,00492342处还有一个CALL呢,下面也会跳到错误提示的地方啊!让我们继续跟。0049233A这个CALL同样用F8带过,EAX变成了6,有了上一次的经验,你应该知道了吧?这个是得到用户名的长度。接下来来到00492342第2个关键CALL,F7跟进,来到这里:
...................................................................
首先不要被这么长的代码吓到,很多是我们不需要了解的,如果你看到代码很长就放弃的话,你就永远也不会有提高。
|:00492342   
|
:00492114 55                      push ebp                       <--- 保护现场原来的EBP指针
:00492115 8BEC                    mov ebp, esp                   <--- 设置新的EBP指针,指向栈顶ESP  
:00492117 83C4D4                  add esp, FFFFFFD4              <--- 在堆栈中留出空间放局部变量,这三句都是子程序的老套路了,如果不懂,记住也行。
:0049211A 53                      push ebx
:0049211B 33D2                    xor edx, edx                   <--- EDX清零
:0049211D 8955F4                  mov dword ptr [ebp-0C], edx    <--- [EBP-0C]清零
:00492120 8955D4                  mov dword ptr [ebp-2C], edx    <--- [EBP-2C]清零
:00492123 8955F0                  mov dword ptr [ebp-10], edx   <--- [EBP-10]清零
:00492126 8955F8                  mov dword ptr [ebp-08], edx   <--- [EBP-08]清零
:00492129 8945FC                  mov dword ptr [ebp-04], eax   <--- 用户名写入[EBP-04]
:0049212C 8B45FC                  mov eax, dword ptr [ebp-04]    <--- 这句很奇怪,EAX本来就是用户名,干吗写进写出的?
:0049212F E89028F7FF              call 004049C4                  <--- 这个CALL很短,你也可以跟进去看看。它是计算出用户名地址-8的内存数据然后放到EDX中
:00492134 33C0                    xor eax, eax
:00492136 55                      push ebp
:00492137 683A224900              push 0049223A
:0049213C 64FF30                  push dword ptr fs:[eax]
:0049213F 648920                  mov dword ptr fs:[eax], esp
:00492142 33DB                    xor ebx, ebx
:00492144 8D55F8                  lea edx, dword ptr [ebp-08]
:00492147 8B45FC                  mov eax, dword ptr [ebp-04]    <--- 用户名放入EAX中,准备给下面的CALL用
:0049214A E88969F7FF              call 00408AD8                  <--- 可以带过,它是用来检查用户名中是否含有非法字符(ASCII码小于等于20h的它就认为是非法字符)
:0049214F 8B45F8                  mov eax, dword ptr [ebp-08]    <--- 用户名放入EAX中,准备给下面的CALL用
:00492152 E88526F7FF              call 004047DC                  <--- 带过,看寄存器,EAX变化了,变成了6(我的用户名长度),对了,就是用来得到用户名长度的
:00492157 83F804                  cmp eax, 00000004              <--- 这句很明显吧,用户名长度同4比较
:0049215A 0F8CB7000000            jl 00492217                    <--- 小于4就没的玩了
:00492160 8D45EC                  lea eax, dword ptr [ebp-14]    <--- 保存返回地址
:00492163 8B55F8                  mov edx, dword ptr [ebp-08]    <--- 用户名送至EDX
:00492166 8A12                    mov dl, byte ptr [edx]         <--- 将用户名的第1个字符存入DL
:00492168 885001                  mov byte ptr [eax+01], dl      <--- 将DL写入内存[EAX+01]中
:0049216B C60001                  mov byte ptr [eax], 01         <--- 把1写入内存[EAX]中
:0049216E 8D55EC                  lea edx, dword ptr [ebp-14]    
:00492171 8D45E8                  lea eax, dword ptr [ebp-18]
:00492174 E85B0DF7FF              call 00402ED4                  <--- 先带过,这种下面还要经常调用的CALL通常都是“大战之前的准备”,不要在无谓的地方浪费时间,大不了回头再跟。 :)
:00492179 8D45E4                  lea eax, dword ptr [ebp-1C]
:0049217C 8B55F8                  mov edx, dword ptr [ebp-08]    <--- 用户名送至EDX
:0049217F 8A5201                  mov dl, byte ptr [edx+01]      <--- 将用户名的第2个字符送入DL
:00492182 885001                  mov byte ptr [eax+01], dl      <--- DL写入内存[EAX+01]处
:00492185 C60001                  mov byte ptr [eax], 01         <--- 1写入[EAX]所指的内存中
:00492188 8D55E4                  lea edx, dword ptr [ebp-1C]
:0049218B 8D45E8                  lea eax, dword ptr [ebp-18]
:0049218E B102                    mov cl, 02                     <--- 注意这里,执行下面CALL之前CL赋值为2
:00492190 E80F0DF7FF              call 00402EA4                  <--- 先带过,因为下面还要调用,走几遍再猜
:00492195 8D55E8                  lea edx, dword ptr [ebp-18]    <--- 这里执行完,EDX里是什么?哈,“02,"pr" (用户名的前两位)”
:00492198 8D45E0                  lea eax, dword ptr [ebp-20]
:0049219B E8340DF7FF              call 00402ED4                  <--- 这个CALL上面已经调用过了,不妨再带过,因为下面还有嘛,但是注意,EAX有变化,EAX=3
:004921A0 8D45E4                  lea eax, dword ptr [ebp-1C]
:004921A3 8B55F8                  mov edx, dword ptr [ebp-08]    <--- 用户名存至EDX
:004921A6 8A5202                  mov dl, byte ptr [edx+02]       <--- 熟悉吧?将用户名的第3个字符送到DL中
:004921A9 885001                  mov byte ptr [eax+01], dl   <--- 写入内存[EAX+01]中
:004921AC C60001                  mov byte ptr [eax], 01   <--- 内存[EAX]写入1
:004921AF 8D55E4                  lea edx, dword ptr [ebp-1C]
:004921B2 8D45E0                  lea eax, dword ptr [ebp-20]
:004921B5 B103                    mov cl, 03                     <--- 好象见过?对了,上面调用CALL 00402EA4之前CL送入的值是2,这次是3,呵呵,明白了吧?下面这个CALL就是用来取用户名的CL个字符的
:004921B7 E8E80CF7FF              call 00402EA4                  <--- 现在知道了,取用户名的某些字符
:004921BC 8D55E0                  lea edx, dword ptr [ebp-20]    <--- 这里执行完EDX内容为:03,"pri"
:004921BF 8D45D8                  lea eax, dword ptr [ebp-28]
:004921C2 E80D0DF7FF              call 00402ED4       <--- 又是它!带过看EAX,EAX=4,结合上面看看,哦,原来它是用来取它需要的第EAX个字符的!
:004921C7 8D45E4                  lea eax, dword ptr [ebp-1C]
:004921CA 8B55F8                  mov edx, dword ptr [ebp-08]    <--- 用户名送至EDX
:004921CD 8A5205                  mov dl, byte ptr [edx+05]      <--- 注意这里,上面取的是用户名的第3个字符,这里没有继续按顺序往下取,而是取用户名的第6个字符!
:004921D0 885001                  mov byte ptr [eax+01], dl      <--- 第6个字符写入内存[EAX+01]
:004921D3 C60001                  mov byte ptr [eax], 01   <--- 1写入[EAX]
:004921D6 8D55E4                  lea edx, dword ptr [ebp-1C]
:004921D9 8D45D8                  lea eax, dword ptr [ebp-28]    <--- 执行完EAX是:03, "pri"
:004921DC B104                    mov cl, 04                     <--- 看,又来了吧!取它需要的第4个字符啦!
:004921DE E8C10CF7FF              call 00402EA4                  <--- 取它需要的第4个字符,注意是它需要的,不是用户名的第4个字符
:004921E3 8D55D8                  lea edx, dword ptr [ebp-28]   <--- 果然吧,EDX:04, "prie",因为它取了用户名的第6个字符,所以这里是"e"
:004921E6 8D45F0                  lea eax, dword ptr [ebp-10]    
:004921E9 E89225F7FF              call 00404780                  <--- 这个CALL没见过,省力原则先带过,EDX被清零
:004921EE 8B45F0                  mov eax, dword ptr [ebp-10]    <--- 将它需要的四个字符"prie"送入EAX
:004921F1 8D55F4                  lea edx, dword ptr [ebp-0C]
:004921F4 E88F66F7FF              call 00408888                  <--- 这个是干什么的呢?看到下面还要调用,先带过
:004921F9 8B45F4                  mov eax, dword ptr [ebp-0C]    <--- 执行完,EAX= ASCII "PRIE",哈哈,原来是小写转大写用的
:004921FC 50                      push eax       <--- 准备工作做完了,压栈准备“开战”了,呵呵
:004921FD 8D55D4                  lea edx, dword ptr [ebp-2C]

* Possible StringData Ref from Code Obj ->"lane"
                                  |
:00492200 B850224900              mov eax, 00492250              <--- 将字符串常量"lane"送至EAX
:00492205 E87E66F7FF              call 00408888       <--- 上面用过的,小写转大写
:0049220A 8B55D4                  mov edx, dword ptr [ebp-2C]    <--- 转换结果送至EDX
:0049220D 58                      pop eax       <--- EAX出栈,哼哼,危险信号,EAX就是已经取完的用户名啊!
:0049220E E80D27F7FF              call 00404920       <--- 上面这么敏感,下面又有跳转!还不够关键吗?跟进!
:00492213 7502                    jne 00492217
:00492215 B301                    mov bl, 01

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0049215A(C), :00492213(C)
|
:00492217 33C0                    xor eax, eax
:00492219 5A                      pop edx
:0049221A 59                      pop ecx
:0049221B 59                      pop ecx
:0049221C 648910                  mov dword ptr fs:[eax], edx
:0049221F 6841224900              push 00492241
...................................................................
只是粗跟就已经得到这么多有用的信息了,对于不是很复杂的算法来说有时就足够了!我们接着看,0049220E处的关键CALL跟进来到这里:
...................................................................
大概看一下,恩,没有调用了,更可疑!说明是比较判断的关键部分了!
:00404920 53                      push ebx
:00404921 56                      push esi
:00404922 57                      push edi
:00404923 89C6                    mov esi, eax                   <--- EAX是什么来着?"PRIE"
:00404925 89D7                    mov edi, edx       <--- EDX: "LANE"
:00404927 39D0                    cmp eax, edx       <--- 比较是否相等
:00404929 0F848F000000            je 004049BE       <--- 相等就跳了!不过跳了是好事还是坏事呢?试试看才知道嘛。双击Z flag改变跳转标志,F9运行,看到什么了?嘿嘿,感谢注册!好,先高兴会儿,等下回头总结。
:0040492F 85F6                    test esi, esi
:00404931 7468                    je 0040499B
:00404933 85FF                    test edi, edi
:00404935 746B                    je 004049A2
:00404937 8B46FC                  mov eax, dword ptr [esi-04]
:0040493A 8B57FC                  mov edx, dword ptr [edi-04]
:0040493D 29D0                    sub eax, edx
:0040493F 7702                    ja 00404943
:00404941 01C2                    add edx, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040493F(C)
|
:00404943 52                      push edx
:00404944 C1EA02                  shr edx, 02
:00404947 7426                    je 0040496F
...................................................................

  刚拿到这个软件的时候看到没有加壳,爆破点又清晰明了,就想他肯定是明码比较注册码,就搜内存,堆栈,结果不行。于是我以为是非明码比较的注册算法,所以拿出OD...可是没想到,“算法”竟然是这样的。

总结:

  判断条件1:
  用户名不得少于4个字符(程序里这么写的),但我想应该是不能少于6个字符吧?其中前三个字符必须为:lan, 随后两个字符随便填,第6个字符必须为e,然后就随便填了。
  判断条件2:
  注册码不得少于12个字符,其中第2,5,7,8,10个字符必须分别为:1,9,9,7,5,其他字符就随便填了。

  只要以上两个条件都满足,注册成功!简单清晰明了,注册机就不用写了吧?

注:最新版V1.59注册算法已经更改,如果你有兴趣可以自己分析。

  V1.59版下载地址: http://www1.skycn.com/soft/14357.html  

  菜鸟写菜文,欢迎高手指正。
                                    prince 2005.01.13