论坛: 编程破解 标题: [转帖]Riijjcrackme10anniversary算法简析 复制本贴地址    
作者: yongmin [yongmin]    论坛用户   登录
作者:hawking
转贴自:一蓑烟雨

【文章标题】: 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  /CALL 到 ReadFile 来自 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 ;
  };
 
--------------------------------------------------------------------------------
【版权声明】: 感谢看雪论坛、一蓑烟雨, 转载请注明作者并保持文章的完整, 谢谢!

地主 发表时间: 07-03-17 10:45

论坛: 编程破解

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号