首页
社区
课程
招聘
[原创]Riijj crackme 10 anniversary 算法简析
发表于: 2007-3-14 18:54 5061

[原创]Riijj crackme 10 anniversary 算法简析

2007-3-14 18:54
5061

【文章标题】: Riijj crackme 10 anniversary 算法简析
【文章作者】: hawking
【作者邮箱】: rich_hawking@hotmail.com
【软件名称】: riijjcm10f2.exe
【软件大小】: 376k
【下载地址】: http://bbs.pediy.com/showthread.php?s=&threadid=36800
【保护方式】: 启动检测 key file
【编写语言】: VC++6
【使用工具】: OD PEiD
【操作平台】: win2k sp4
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  这是一个纯粹娱乐的 crackme,是riijj兄台作为圣诞礼物奉献给看雪论坛的全体同仁的。当初水平不够,看到浮点指令头大,连爆破都没能做到。想想心有不甘,根据warshon兄弟提供的一些线索,现对其注册过程作简单分析。
  
  一、寻找关键代码
  
  用PEiD检查,程序没有加壳,是Microsoft Visual C++ 6.0程序。用IDA打开分析,然后创建MAP文件,再用OD载入,导入MAP文件。
  根据riijj的介绍,我们知道这个Crackme是通过启动时检测 key file来注册的。下断点 bp CreateFileA 然后F9运行。断下来之后我们看一下堆栈:
  
  0012FD28   00420957  /CALL 到 CreateFileA 来自 riijjcm1.00420951
  0012FD2C   0042A0B0  |FileName = "dinner.bin"
  0012FD30   80000000  |Access = GENERIC_READ
  0012FD34   00000003  |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
  0012FD38   0012FD54  |pSecurity = 0012FD54
  0012FD3C   00000003  |Mode = OPEN_EXISTING
  0012FD40   00000080  |Attributes = NORMAL
  0012FD44   00000000  \hTemplateFile = NULL
  0012FD48   00424552  riijjcm1.00424552
  
  从这里我们知道了key file文件名是dinner.bin。我们新建一个空白文本文件随便输入一些文字并命名为dinner.bin之后复制到Crackme所在的文件夹。
  Ctrl+F2重新运行程序,下断点 bp ReadFile 然后F9运行。断下来之后的堆栈:
  
  0012FD20   0041F9C5  /CALLReadFile 来自 riijjcm1.0041F9BF
  0012FD24   00000080  |hFile = 00000080 (window)
  0012FD28   008D33C0  |Buffer = 008D33C0
  0012FD2C   00001000  |BytesToRead = 1000 (4096.)
  0012FD30   0012FD44  |pBytesRead = 0012FD44
  0012FD34   00000000  \pOverlapped = NULL
  
  从这里可以看出程序从key file中读取数据,并将读取到的数据保存在内存某一块地址当中。不断地Ctrl+F9返回之后我们来到了这里。
  
  004011C1 >|> \56            push    esi
  004011C2  |.  55            push    ebp
  004011C3  |.  8D4C24 40     lea     ecx, dword ptr [esp+40]
  004011C7  |.  E8 E4020000   call    <sub_4014B0>                     ;  我们就是从这里返回的
  004011CC  |.  8D4C24 40     lea     ecx, dword ptr [esp+40]
  004011D0  |.  E8 8B1A0000   call    <sub_402C60>                     ;  closefile
  004011D5  |.  85C0          test    eax, eax
  004011D7  |.  75 13         jnz     short <loc_4011EC>
  004011D9  |.  50            push    eax
  004011DA  |.  8B4424 3C     mov     eax, dword ptr [esp+3C]
  004011DE  |.  6A 02         push    2
  004011E0  |.  8B48 04       mov     ecx, dword ptr [eax+4]
  004011E3  |.  8D4C0C 40     lea     ecx, dword ptr [esp+ecx+40]
  004011E7  |.  E8 54020000   call    <sub_401440>
  004011EC >|>  B9 0A000000   mov     ecx, 0A
  004011F1  |.  8BF5          mov     esi, ebp                         ;  这里的ebp指向了刚刚我们从key file中读取到的数据
  004011F3  |.  8DBC24 C80000>lea     edi, dword ptr [esp+C8]          ;  buffer
  004011FA  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>;  复制40个读取到的字节到buffer
  
  这里往下出现了不少浮点指令,关键代码段就是下面了。这里我们可以看出,程序真正用到的也只是key file中的前40个字节。
  
  二、算法简析
  
  004011EC >|>  B9 0A000000   mov     ecx, 0A
  004011F1  |.  8BF5          mov     esi, ebp                         ;  这里的ebp指向了刚刚我们从key file中读取到的数据
  004011F3  |.  8DBC24 C80000>lea     edi, dword ptr [esp+C8]          ;  buffer
  004011FA  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>;  复制40个读取到的字节到buffer
  004011FC  |.  DD05 D8414200 fld     qword ptr [<dbl_4241D8>]         ;  0.0
  00401202  |.  DD5424 18     fst     qword ptr [esp+18]               ;  n2 = 0
  00401206  |.  DD05 D8414200 fld     qword ptr [<dbl_4241D8>]         ;  0.0
  0040120C  |.  DD5424 10     fst     qword ptr [esp+10]               ;  n1 = 0
  00401210  |.  0FBF9424 C800>movsx   edx, word ptr [esp+C8]           ;  取buffer前2个字节(len)
  00401218  |.  8BCA          mov     ecx, edx
  0040121A  |.  8DB424 CA0000>lea     esi, dword ptr [esp+CA]
  00401221  |.  8BC1          mov     eax, ecx
  00401223  |.  8D7C24 20     lea     edi, dword ptr [esp+20]
  00401227  |.  C1E9 02       shr     ecx, 2
  0040122A  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>
  0040122C  |.  8BC8          mov     ecx, eax
  0040122E  |.  33C0          xor     eax, eax                         ;  i=0
  00401230  |.  83E1 03       and     ecx, 3
  00401233  |.  85D2          test    edx, edx
  00401235  |.  F3:A4         rep     movs byte ptr es:[edi], byte ptr>
  00401237  |.  0F8E 2B010000 jle     <loc_401368>
  
  这一段代码先初始化两个浮点数n1和n2,使其全部为0,然后再取buffer当中的前两位(len),其实buffer中的前两位代表长度,也就是用户名的长度。再将buffer当中从第3位起,长度为len的字节复制到内存当中的另一个地方待用。
  其实可以这么看,buffer前2位代表用户名长度len,从第3位开始长len的数据代表的就是用户名name。
  
  0040123D >|> /0FBE4C04 20   /movsx   ecx, byte ptr [esp+eax+20]      ;  name[i]
  00401242  |. |894C24 0C     |mov     dword ptr [esp+C], ecx
  00401246  |. |40            |inc     eax                             ;  i++
  00401247  |. |DB4424 0C     |fild    dword ptr [esp+C]
  0040124B  |. |3BC2          |cmp     eax, edx
  0040124D  |. |D9C0          |fld     st
  0040124F  |. |DEC3          |faddp   st(3), st
  00401251  |. |D9CA          |fxch    st(2)
  00401253  |. |DC0D D0414200 |fmul    qword ptr [<dbl_4241D0>]        ;  1.2
  00401259  |. |D9CA          |fxch    st(2)
  0040125B  |. |DEC1          |faddp   st(1), st
  0040125D  |. |DC0D C8414200 |fmul    qword ptr [<dbl_4241C8>]        ;  1.3
  00401263  |.^\7C D8         \jl      short <loc_40123D>
  00401265  |.  DD5C24 10     fstp    qword ptr [esp+10]               ;  n1
  00401269  |.  DD5C24 18     fstp    qword ptr [esp+18]               ;  n2
  
  这里对用户名作运算,反复取用户名的每一位,分别乘以1.3和1.2并累加至n1和n2 。
  
  0040126D >|> /55            push    ebp                              
  0040126E  |. |E8 5C810000   call    <sub_4093CF>                     ;  heapfree
  00401273  |. |DD4424 14     fld     qword ptr [esp+14]               ;  n1
  00401277  |. |DC0D C0414200 fmul    qword ptr [<dbl_4241C0>]         ;  5.0
  0040127D  |. |DD4424 1C     fld     qword ptr [esp+1C]               ;  n2
  00401281  |. |DC0D B8414200 fmul    qword ptr [<dbl_4241B8>]         ;  9.0
  00401287  |. |83C4 04       add     esp, 4
  0040128A  |. |DEC1          faddp   st(1), st
  0040128C  |. |DD8424 E80000>fld     qword ptr [esp+E8]               ;  f2 key file 中33至40字节所代表的浮点数
  00401293  |. |DC0D B0414200 fmul    qword ptr [<dbl_4241B0>]         ;  7.0
  00401299  |. |DEC1          faddp   st(1), st
  0040129B  |. |DD8424 E00000>fld     qword ptr [esp+E0]               ;  f1 key file 中25至32字节所代表的浮点数
  004012A2  |. |DCC0          fadd    st, st
  004012A4  |. |DEC1          faddp   st(1), st
  004012A6  |. |DC25 A8414200 fsub    qword ptr [<dbl_4241A8>]         ;  50.0
  004012AC  |. |E8 C7460100   call    <__ftol>                             ;  取整
  
  这里对n1 n2 f1 f2作运算( 5 * n1 + 9 * n2 + 7 * f2 + f1 + f1 - 50.0)并将结果取整(result1)。
  
  004012B1  |.  99            cdq
  004012B2  |.  33C2          xor     eax, edx
  004012B4  |.  5E            pop     esi
  004012B5  |.  2BC2          sub     eax, edx
  004012B7  |.  5D            pop     ebp
  004012B8  |.  894424 04     mov     dword ptr [esp+4], eax           ;  取result1绝对值
  004012BC  |.  DB4424 04     fild    dword ptr [esp+4]
  004012C0  |.  DC1D A0414200 fcomp   qword ptr [<dbl_4241A0>]         ;  0.01
  004012C6  |.  DFE0          fstsw   ax                               ;  将结果与0.01比较
  004012C8  |.  F6C4 01       test    ah, 1                            ;  
  004012CB  |.  0F84 A0000000 je      <loc_401371>                     ;  大于则跳 Game Over
  
  这里要求result1的绝对值要小于0.01,由于result1是整数,所以result1只能为0 ,也就是说( 5*n1 + 9*n2 + 7*f2 + f1 + f1 - 50.0 )只能大于-1且小于1 。
  
  004012D1  |.  DD4424 08     fld     qword ptr [esp+8]                ;  n1
  004012D5  |.  DC8424 E00000>fadd    qword ptr [esp+E0]               ;  f2
  004012DC  |.  DC0D 98414200 fmul    qword ptr [<dbl_424198>]         ;  4.0
  004012E2  |.  DD4424 10     fld     qword ptr [esp+10]               ;  n2
  004012E6  |.  DC0D B0414200 fmul    qword ptr [<dbl_4241B0>]         ;  7.0
  004012EC  |.  DEC1          faddp   st(1), st
  004012EE  |.  DD8424 D80000>fld     qword ptr [esp+D8]               ;  f1
  004012F5  |.  DC0D 90414200 fmul    qword ptr [<dbl_424190>]         ;  3.0
  004012FB  |.  DEC1          faddp   st(1), st
  004012FD  |.  DC25 88414200 fsub    qword ptr [<dbl_424188>]         ;  40.0
  00401303  |.  E8 70460100   call    <__ftol>
  00401308  |.  99            cdq
  00401309  |.  33C2          xor     eax, edx
  0040130B  |.  2BC2          sub     eax, edx
  0040130D  |.  894424 04     mov     dword ptr [esp+4], eax
  00401311  |.  DB4424 04     fild    dword ptr [esp+4]
  00401315  |.  DC1D A0414200 fcomp   qword ptr [<dbl_4241A0>]         ;  0.01
  0040131B  |.  DFE0          fstsw   ax
  0040131D  |.  F6C4 01       test    ah, 1
  00401320  |.  74 4F         je      short <loc_401371>
  
  同上,要求 -1 < ( n1 + f2 ) * 4 + 7 * n2 + 3 * f1 - 40.0 < 1 不等式成立。
  
  00401322  |.  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]          ;  成功的话走这里
  00401329  |.  C78424 080100>mov     dword ptr [esp+108], -1
  00401334  |.  E8 47020000   call    <sub_401580>
  00401339  |.  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]
  00401340  |.  C78424 8C0000>mov     dword ptr [esp+8C], offset <off_>
  0040134B  |.  E8 887E0000   call    <std::ios_base::~ios_base(void)>
  00401350  |.  B0 01         mov     al, 1                            ;  设标志flag = 1
  00401352  |.  5F            pop     edi
  00401353  |.  8B8C24 FC0000>mov     ecx, dword ptr [esp+FC]
  0040135A  |.  64:890D 00000>mov     dword ptr fs:[0], ecx
  00401361  |.  81C4 08010000 add     esp, 108
  00401367  |.  C3            retn
  00401368 >|>  DDD8          fstp    st                              
  0040136A  |.  DDD8          fstp    st
  0040136C  |.^ E9 FCFEFFFF   jmp     <loc_40126D>
  00401371 >|>  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]          ;  失败的话走这里
  00401378  |.  C78424 080100>mov     dword ptr [esp+108], -1
  00401383  |.  E8 F8010000   call    <sub_401580>
  00401388  |.  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]
  0040138F  |.  C78424 8C0000>mov     dword ptr [esp+8C], offset <off_>
  0040139A  |.  E8 397E0000   call    <std::ios_base::~ios_base(void)>
  0040139F  |.  EB 23         jmp     short <loc_4013C4>
  004013A1 >|>  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]        
  004013A8  |.  C78424 080100>mov     dword ptr [esp+108], -1
  004013B3  |.  E8 C8010000   call    <sub_401580>
  004013B8  |.  8D8C24 8C0000>lea     ecx, dword ptr [esp+8C]
  004013BF  |.  E8 6C000000   call    <sub_401430>
  004013C4 >|>  8B8C24 000100>mov     ecx, dword ptr [esp+100]         
  004013CB  |.  32C0          xor     al, al                           ;  设标志flag = 0
  004013CD  |.  5F            pop     edi
  004013CE  |.  64:890D 00000>mov     dword ptr fs:[0], ecx
  004013D5  |.  81C4 08010000 add     esp, 108
  004013DB  \.  C3            retn
  
  最终返回到这里:
  
  00405F2F >|> \8B4C24 08     mov     ecx, dword ptr [esp+8]           ;  loc_405F2F
  00405F33  |.  51            push    ecx                              ; /ShowState
  00405F34  |.  50            push    eax                              ; |hWnd
  00405F35  |.  FF15 64414200 call    dword ptr [<&USER32.ShowWindow>] ; \ShowWindow
  00405F3B  |.  8B15 C4E34200 mov     edx, dword ptr [<hWnd>]
  00405F41  |.  52            push    edx                              ; /hWnd => 001B04AA ('Riijj crackme 10 - anniversary',class='riijj')
  00405F42  |.  FF15 5C414200 call    dword ptr [<&USER32.UpdateWindow>; \UpdateWindow
  00405F48  |.  E8 33B1FFFF   call    <sub_401080>                     ;  关键call
  00405F4D  |.  A2 F8E24200   mov     byte ptr [<byte_42E2F8>], al     ;  [42E2F8]处保存的是标志位,程序后面就是根据这个标志来显示不同的画面
  00405F52  |.  FF15 BC404200 call    dword ptr [<&KERNEL32.GetTickCou>; [GetTickCount
  00405F58  |.  50            push    eax
  00405F59  |.  E8 9D030100   call    <sub_4162FB>
  00405F5E  |.  83C4 04       add     esp, 4
  00405F61  |.  E8 7A000000   call    <sub_405FE0>
  00405F66  |.  B8 01000000   mov     eax, 1
  00405F6B  \.  C3            retn
  
  三、注册机(C#)
  
  算法其实不难,主要是从key file中读取到的用户名生成两个浮点数n1 n2 ,并且将key file文件中特定的两个位置的数据看作两个浮点数f1 f2
  要求下面两个不等式成立,则注册成功。
  
  -1 <  5 * n1 + 9 * n2 + 7 * f2 + f1 + f1 - 50.0 < 1
  -1 < ( n1 + f2 ) * 4 + 7 * n2 + 3 * f1 - 40.0 < 1
  
  最后我们再来看看一个可用的key file中包含信息
  
  07 00 68 61 77 6B 69 6E 67 00 00 00 00 00 00 00  .hawking.......
  00 00 00 00 00 00 00 00
F1 12 28 3F 00 6C A8 C0  ........?(?.l?
  37 BE E8 12 6E F9 A6 C0                          7捐n?
  
  上面的数据结构中,前2位代表用户名长度,后面22位代表用户名(也就是说用户名最大有效长度为22位),再往后就是2个浮点数了。
  
  struct RegisteInfo
  {
          ushort Length ;
          char UserName[22] ;
          double Fnum1 ;
          double Fnum2 ;
  };
  
  注册机见附件。
  
--------------------------------------------------------------------------------
【版权声明】: 感谢看雪论坛、一蓑烟雨, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年03月14日 18:53:17


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 297
活跃值: (21)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
2
好好学习一下,当初我也没弄出来
2007-3-15 08:46
0
雪    币: 220
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
分析那么详细的文章,我支持!
学习了
2007-3-16 10:32
0
游客
登录 | 注册 方可回帖
返回
//