首页
社区
课程
招聘
最近逆向搞得头昏,来捏个入口即化的柿子,呵呵
发表于: 2006-7-29 17:38 6448

最近逆向搞得头昏,来捏个入口即化的柿子,呵呵

2006-7-29 17:38
6448

若说我图省事的,你们自己解的Crackme不也基本上都是菜鸟级的吗!有本事咱一块来逆向!

下载地址:http://www.crackmes.de/users/unicorn/crackme1/
使用工具:OllyDbg

  这是一个VC(可能是吧,我没PEid过)写的Crackme,运行试注册,随便输个号,没有任何
提示便退出。用OllyDbg加载,找到一个关键字符串:

       "Correct Serial Number !!!"

跟随这个字符串来到:

=====================以下是代码===================
004013F3  /.  55            push    ebp
004013F4  |.  8BEC          mov     ebp, esp
004013F6  |.  83EC 0C       sub     esp, 0C
004013F9  |.  56            push    esi
004013FA  |.  8D45 F4       lea     eax, [ebp-C]
004013FD  |.  6A 0B         push    0B                               ; /Arg3 = 0000000B
004013FF  |.  50            push    eax                              ; |Arg2
00401400  |.  8BF1          mov     esi, ecx                         ; |
00401402  |.  68 E8030000   push    3E8                              ; |Arg1 = 000003E8
00401407  |.  E8 69940000   call    0040A875                         ; \Crackme1.0040A875
0040140C  |.  8D45 F4       lea     eax, [ebp-C]
0040140F  |.  50            push    eax
00401410  |.  E8 DB0B0000   call    00401FF0                         ;  求串长度
00401415  |.  83F8 0A       cmp     eax, 0A                          ;  注册码应为10字符长
00401418  |.  59            pop     ecx
00401419  |.  75 2F         jnz     short 0040144A
0040141B  |.  8D45 F4       lea     eax, [ebp-C]
0040141E  |.  50            push    eax
0040141F  |.  E8 A7FCFFFF   call    004010CB                         ;  检测注册码的字符组成(跟进去就知道了)
00401424  |.  85C0          test    eax, eax
00401426  |.  59            pop     ecx
00401427  |.  74 21         je      short 0040144A                   ;  如果不是全由数码组成,则为非法注册码
00401429  |.  8D45 F4       lea     eax, [ebp-C]
0040142C  |.  50            push    eax
0040142D  |.  E8 B7FCFFFF   call    004010E9                         ;  关键算法call
00401432  |.  85C0          test    eax, eax
00401434  |.  59            pop     ecx
00401435  |.  74 13         je      short 0040144A                   ;  关键跳转
00401437  |.  6A 00         push    0
00401439  |.  68 BC404100   push    004140BC                         ;  ASCII "Cool"
0040143E  |.  68 A0404100   push    004140A0                         ;  ASCII "Correct Serial Number !!!"
00401443  |.  8BCE          mov     ecx, esi
00401445  |.  E8 A1830000   call    004097EB
0040144A  |>  8BCE          mov     ecx, esi
0040144C  |.  E8 CD680000   call    00407D1E
00401451  |.  5E            pop     esi
00401452  |.  C9            leave
00401453  \.  C3            retn
=====================以上是代码===================

很自然地会想到断在40142C,但是一般情况下还是断不下来。再往上看,401402这一句里的3E8
是什么?好象是控件ID哦!(3E8的十进数值就是1000,象1000+xx这些典型的数值通常都会用在
对话框资源中作为模板ID或者控件ID。没有经过分析资源的实践是不会有这种经验的)想一下,
哪些API的参数里会用到控件ID?(GetDlgItemText……)呵呵,不管怎么说,想到这就好办
了。那么就断在这个call的下一句,也就是40140C这一句上面吧。

  401410这个call把上面获得的输入码串指针作为参数,出来的时候马上把返回值跟0Ah比
较,这想都不用想,肯定是求串长度的一个过程。(话说回来,要达到“不用想”这种境界,也
不是一朝一夕的事情。想当初我开始破解第一个程序的时候,也是见call必入,把精力都浪费在
一些无关紧要的地方。虽然最后程序破了,头却同时也昏到非连睡24小时不可的地步)废话完了
没有啊!这明显是在说:注册码必须是10个字符长。下面(40141F这里)这个call呢?光看外表
就看不太明白了,那就跟进去看看吧。这个过程比较短小,一旦看见30和39这两个典型数值,不
用细看也知道它是在检测输入码的每个字符是否为数码。这些都是对注册码格式的前期合法性检
测。

  接下来到了关键的地方了,40142D这个call跟进去:

=====================以下是代码===================
004010E9  /$  56            push    esi
004010EA  |.  8B7424 08     mov     esi, [esp+8]
004010EE  |.  57            push    edi
004010EF  |.  6A 31         push    31                               ;  字符参数2
004010F1  |.  56            push    esi
004010F2  |.  E8 BDFFFFFF   call    004010B4                         ;  统计目的串中字符参数2的个数
004010F7  |.  59            pop     ecx
004010F8  |.  59            pop     ecx
004010F9  |.  0FBE0E        movsx   ecx, byte ptr [esi]              ;  将注册码的第1位取出
004010FC  |.  6A 30         push    30
004010FE  |.  5F            pop     edi
004010FF  |.  03C7          add     eax, edi                         ;  上面得到的个数加上'0'
00401101  |.  3BC8          cmp     ecx, eax
00401103  |.  0F85 C0000000 jnz     004011C9                         ;  不相等则注册失败
00401109  |.  6A 32         push    32
0040110B  |.  56            push    esi
0040110C  |.  E8 A3FFFFFF   call    004010B4
00401111  |.  59            pop     ecx
00401112  |.  03C7          add     eax, edi
00401114  |.  59            pop     ecx
00401115  |.  0FBE4E 01     movsx   ecx, byte ptr [esi+1]
00401119  |.  3BC8          cmp     ecx, eax
0040111B  |.  0F85 A8000000 jnz     004011C9
00401121  |.  6A 33         push    33
00401123  |.  56            push    esi
00401124  |.  E8 8BFFFFFF   call    004010B4
00401129  |.  59            pop     ecx
0040112A  |.  03C7          add     eax, edi
0040112C  |.  59            pop     ecx
0040112D  |.  0FBE4E 02     movsx   ecx, byte ptr [esi+2]
00401131  |.  3BC8          cmp     ecx, eax
00401133  |.  0F85 90000000 jnz     004011C9
00401139  |.  6A 34         push    34
0040113B  |.  56            push    esi
0040113C  |.  E8 73FFFFFF   call    004010B4
00401141  |.  59            pop     ecx
00401142  |.  03C7          add     eax, edi
00401144  |.  59            pop     ecx
00401145  |.  0FBE4E 03     movsx   ecx, byte ptr [esi+3]
00401149  |.  3BC8          cmp     ecx, eax
0040114B  |.  75 7C         jnz     short 004011C9
0040114D  |.  6A 35         push    35
0040114F  |.  56            push    esi
00401150  |.  E8 5FFFFFFF   call    004010B4
00401155  |.  59            pop     ecx
00401156  |.  03C7          add     eax, edi
00401158  |.  59            pop     ecx
00401159  |.  0FBE4E 04     movsx   ecx, byte ptr [esi+4]
0040115D  |.  3BC8          cmp     ecx, eax
0040115F  |.  75 68         jnz     short 004011C9
00401161  |.  6A 36         push    36
00401163  |.  56            push    esi
00401164  |.  E8 4BFFFFFF   call    004010B4
00401169  |.  59            pop     ecx
0040116A  |.  03C7          add     eax, edi
0040116C  |.  59            pop     ecx
0040116D  |.  0FBE4E 05     movsx   ecx, byte ptr [esi+5]
00401171  |.  3BC8          cmp     ecx, eax
00401173  |.  75 54         jnz     short 004011C9
00401175  |.  6A 37         push    37
00401177  |.  56            push    esi
00401178  |.  E8 37FFFFFF   call    004010B4
0040117D  |.  59            pop     ecx
0040117E  |.  03C7          add     eax, edi
00401180  |.  59            pop     ecx
00401181  |.  0FBE4E 06     movsx   ecx, byte ptr [esi+6]
00401185  |.  3BC8          cmp     ecx, eax
00401187  |.  75 40         jnz     short 004011C9
00401189  |.  6A 38         push    38
0040118B  |.  56            push    esi
0040118C  |.  E8 23FFFFFF   call    004010B4
00401191  |.  59            pop     ecx
00401192  |.  03C7          add     eax, edi
00401194  |.  59            pop     ecx
00401195  |.  0FBE4E 07     movsx   ecx, byte ptr [esi+7]
00401199  |.  3BC8          cmp     ecx, eax
0040119B  |.  75 2C         jnz     short 004011C9
0040119D  |.  6A 39         push    39
0040119F  |.  56            push    esi
004011A0  |.  E8 0FFFFFFF   call    004010B4
004011A5  |.  59            pop     ecx
004011A6  |.  03C7          add     eax, edi
004011A8  |.  59            pop     ecx
004011A9  |.  0FBE4E 08     movsx   ecx, byte ptr [esi+8]
004011AD  |.  3BC8          cmp     ecx, eax
004011AF  |.  75 18         jnz     short 004011C9
004011B1  |.  57            push    edi
004011B2  |.  56            push    esi
004011B3  |.  E8 FCFEFFFF   call    004010B4
004011B8  |.  59            pop     ecx
004011B9  |.  03C7          add     eax, edi
004011BB  |.  59            pop     ecx
004011BC  |.  0FBE4E 09     movsx   ecx, byte ptr [esi+9]
004011C0  |.  3BC8          cmp     ecx, eax
004011C2  |.  75 05         jnz     short 004011C9
004011C4  |.  6A 01         push    1
004011C6  |.  58            pop     eax
004011C7  |.  EB 02         jmp     short 004011CB
004011C9  |>  33C0          xor     eax, eax
004011CB  |>  5F            pop     edi
004011CC  |.  5E            pop     esi
004011CD  \.  C3            retn
=====================以上是代码===================

这段代码看起来长,实际上都是差不多的一个片段在重复,就以第一小节为例,跟进004010B4这
个call可以知道它是在统计输入码中给定字符的个数,现在这个字符是31也就是'1',取得这个
个数以后加上30,然后与输入码的第1个字符比较,不相等就注册失败。如果撇开具体的数值转
ASCII码的手续,这就是说:注册码的第一个数字应该等于数码'1'在注册码串中的出现次数。

  举一反三,下面的部分就不用仔细看了。验证算法可以总结如下:

   注册码必须是10字符,且全由十进制数码构成。
   注册码的第一个数字等于数码'1'在注册码串中的出现次数。
   注册码的第二个数字等于数码'2'在注册码串中的出现次数。
   注册码的第三个数字等于数码'3'在注册码串中的出现次数。
   注册码的第四个数字等于数码'4'在注册码串中的出现次数。
   注册码的第五个数字等于数码'5'在注册码串中的出现次数。
   注册码的第六个数字等于数码'6'在注册码串中的出现次数。
   注册码的第七个数字等于数码'7'在注册码串中的出现次数。
   注册码的第八个数字等于数码'8'在注册码串中的出现次数。
   注册码的第九个数字等于数码'9'在注册码串中的出现次数。
   注册码的第十个数字等于数码'0'在注册码串中的出现次数。

这些规则看起来十分古怪,并且一时也不容易构造出一个正确的注册码。但是可以用逻辑推理把
合法注册码限制到一个很小的范围。

  首先考虑注册码串中所有数码的和。由上述规则容易知道,这个和应该等于注册码的长度即
10。

  这意味着,注册码的第5、6、7、8、9位都不会超过1。以第5位为例,如果这个位的值大于
或等于2,就意味着串中至少有两个5。如果第5位不是5,那么两个5加上第5位的值已经超过10,
矛盾。如果第5位是5,又意味着有5个5,加起来至少是25,同样得到矛盾。

  由此导致的限制还有:第四位不超过2,第三位不超过3,第二位不超过4。

  现在来估计得更精细些:首先考虑第九位能否为1。如果第九位为1,则意味着某一位是9,
这一位显然只可能是首位或末位。如果是首位,就意味着有9个1。9个1加一个9已经是18 > 10,
矛盾。如果是末位,就意味着有9个0。但是串中除了最后两位以外充其量只能还有8个0,矛盾,
因此第九位只能是0。

  看第八位:如果第八位是1,如上所述,只可能末位是8,其他位全部为0。但这样整个串的
数码和等于9而不是10,矛盾,所以第八位也必须为0。

  第七位:如果第七位是1,则必然末位是7,串中有7个0,还剩一个非零数码,为了凑够整个
串的数码和10,这个数码只能是2。这个2不可能出现在第2位或者以后的地方,不然串中至少还
有两个大于或等于2的数码,数码和就已经不小于7 + 1 + 2 * 2 = 12 > 10了,因此这个2只能
在第一位。但这意味着至少还有另一位是1,与刚才断言除这三个数字以外全是0矛盾,所以第七
位也是0。

  第六位:如果这一位是1,则必然末位是6,串中有6个0,还剩两个非零数码。这两个非零数
码中至少有一个是出现在第2位或者往后的地方,假设出现在第n位,这里n >= 2。于是串中至少
有一个n。另一方面,由于第六位出现了1,第一位就不会是0,当然也不可能是1,假设是m,这
里m >= 2。如果m != n,则整个串的数码和至少是6 + 1 + n + m >= 11 > 10,矛盾。现在考虑
m = n,如果m > 2,则除了m和6以外至少还有三个1,这样就容不下6个0,矛盾;如果m = 2,则
另一个非零数码只能是1,并且由于出现了2,这个1只能被放在第二位,这样得到一个合法的注
册码:

    2100010006

  下面继续探讨第六位为0的情况:

  第五位:前面已说过第五位不能大于1。另一方面,如果第五位是0,由于第六、七、八、九
位都是0,整个串中已至少有五个0,最末位的值势必大于或等于5,这意味着第五到第九位又不
可能全是0,矛盾,所以第五位恰好是1。于是串中有一个5,当然这个5只能在首位或末位。如果
在首位,就意味着除了首位的5以外还有5个1,由于数码和的限制,剩下的四位自然全是0。但是
这样导致末位的值为0或1,与0的数目为4矛盾,所以5应该在末位。于是正确注册码的格式部分
如下:

         XXXX100005

并且前面的四个X中恰有一个是0。

  如果第四位为1,必然还有一个4,4 + 1 + 1 + 5 = 11 > 10矛盾,于是第四位为0。
  这样,剩下的三位都不可能是0了。第一位显然不会是1,至少是2以上。假设是3,则5 + 1
+ 3再加上剩下的两个非零数字已经超过10,所以第一位只能是2,第二位只能是1。但这样不管
第三位数字是什么都会导致矛盾。这就表明,第六位为0是不可能的。

  综上所述,合法的注册码只有一个:2100010006

上传一份,若重复了告诉我把它删掉


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
2
分析的不错
学习了
2006-7-29 19:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
楼主分析得太好了,下功夫了,学习了~
2006-7-29 20:46
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
分析的太好了,适合新手,谢谢!
2006-10-27 15:59
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
5
注册机呢
嘿嘿
2006-10-27 18:44
0
雪    币: 214
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
怎么感觉到楼主的这篇文章好像是数学(字)分析的文章
2006-10-28 01:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
function sum:integer;
var
  i:Integer;
begin
result:=0;
   for i:=1 to length(user) do
     if ord(user[i])=d then result:=result+1;
end;

function sum2:integer;
var
i:Integer;
begin
  result:=0;
  d:=48; //0x30
   for i:=1 to length(user) do
     if ord(user[i])=d then result:=result+1;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
     user:='2100010006';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
i,ok,z,j:Integer;
begin
   ok:=0;
   d:=49;
   for i:=1 to length(user)-1 do
   begin
   if  (sum+48)=ord(user[i]) then
             ok:=ok+1; //验证2100010006这个序列号
             d:=d+1;//0x30++
   end;
   z:=sum2;
   if (z+48)=ord(user[length(user)]) then ok:=ok+1;
   //最后一次验证
   if ok=10 then
   ShowMessage('这个号对了');
end;
2006-11-1 11:40
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
虽然不大看得懂,还是收藏了学习。

多谢lz的辛勤劳动!

2006-11-8 14:00
0
雪    币: 191
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
不错,,,收藏
2006-11-10 12:58
0
游客
登录 | 注册 方可回帖
返回
//