飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 3079|回复: 0

教菜鸟写注册机——高级篇

[复制链接]
  • TA的每日心情
    开心
    2018-2-26 08:32
  • 签到天数: 19 天

    [LV.4]偶尔看看III

    发表于 2006-1-25 08:01:24 | 显示全部楼层 |阅读模式
    教菜鸟写注册机——高级篇(注意我这里说的高级只是对偶辈菜鸟来说是难一些)

    HEYA,我又来灌喽!还是那个系列的第3位CRACKME。下载:
    http://opencrackmes.crackmes.de/ope...ackmes/k4n3.zip

    用W32DASM来反,(可以先看后面说明)


    代码:--------------------------------------------------------------------------------
    :004011BF 6A45                    push 00000045
    :004011C1 50                      push eax
    :004011C2 A4                      movsb

    * Reference To: USER32.GetDlgItemTextA, Ord:0104h
                                      |
    :004011C3 8B3DA8404000            mov edi, dword ptr [004040A8]  ;注意这里把地址放在EDI

    * Possible Reference to Dialog: DialogID_0065, CONTROL_ID:03E8, ""
                                      |
    :004011C9 68E8030000              push 000003E8
    :004011CE 51                      push ecx
    :004011CF FFD7                    call edi    ;实际是CALL GetDlgItemTextA,得到用户名
    :004011D1 8BF0                    mov esi, eax
    :004011D3 85F6                    test esi, esi
    :004011D5 0F844B010000            je 00401326
    :004011DB 83FE40                  cmp esi, 00000040
    :004011DE 0F8742010000            ja 00401326
    :004011E4 8B4508                  mov eax, dword ptr [ebp+08]
    :004011E7 8D5594                  lea edx, dword ptr [ebp-6C]
    :004011EA 6A13                    push 00000013
    :004011EC 52                      push edx

    * Possible Reference to Dialog: DialogID_0065, CONTROL_ID:03E9, ""
                                      |
    :004011ED 68E9030000              push 000003E9
    :004011F2 50                      push eax
    :004011F3 FFD7                    call edi    ;再次调用GetDlgItemText,得到注册码
    :004011F5 6BC003                  imul eax, 00000003  ;EAX是注册码的长度
    :004011F8 C1E002                  shl eax, 02    ;左移二位
    :004011FB 05CD000000              add eax, 000000CD  ;加上0CD
    :00401200 8945FC                  mov dword ptr [ebp-04], eax
    :00401203 817DFCA5010000          cmp dword ptr [ebp-04], 000001A5;看看计算结果是不是1A5
    :0040120A 0F85BC000000            jne 004012CC    ;不是就死,可以逆算出(1A5-0CD)>>2=12
    :00401210 33C0                    xor eax, eax    ;即注册码不能小于12h位
    :00401212 8A4594                  mov al, byte ptr [ebp-6C]
    :00401215 84C0                    test al, al
    :00401217 7413                    je 0040122C
    :00401219 8D4D94                  lea ecx, dword ptr [ebp-6C]

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:0040122A(C)
    |
    :0040121C 3C30                    cmp al, 30
    :0040121E 0F82C6000000            jb 004012EA    ;注册码每位不能小于30h,即'0'
    :00401224 8A4101                  mov al, byte ptr [ecx+01]
    :00401227 41                      inc ecx
    :00401228 84C0                    test al, al
    :0040122A 75F0                    jne 0040121C

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:00401217(C)
    |
    :0040122C E8CFFDFFFF              call 00401000    ;这是什么呀?好像很重要哟,进去看看!
    :00401231 8D852CFFFFFF            lea eax, dword ptr [ebp+FFFFFF2C]
    :00401237 50                      push eax
    :00401238 E843FEFFFF              call 00401080    ;转换过程一,跟进
    :0040123D 8945FC                  mov dword ptr [ebp-04], eax
    :00401240 E8BBFDFFFF              call 00401000    ;还进去不?呀....别打我!
    :00401245 8D8D2CFFFFFF            lea ecx, dword ptr [ebp+FFFFFF2C]
    :0040124B 56                      push esi
    :0040124C 51                      push ecx
    :0040124D E8BEFDFFFF              call 00401010
    :00401252 83C40C                  add esp, 0000000C
    :00401255 33C9                    xor ecx, ecx

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:00401284(C)
    |
    :00401257 8B45FC                  mov eax, dword ptr [ebp-04] \
    :0040125A 33D2                    xor edx, edx                |
    :0040125C BE1A000000              mov esi, 0000001A           |
    :00401261 F7F6                    div esi                     |
    :00401263 8A941510FFFFFF          mov dl, byte ptr [ebp+edx-000000F0]
    :0040126A 88540DC8                mov byte ptr [ebp+ecx-38], dl|
    :0040126E 8B45FC                  mov eax, dword ptr [ebp-04] |
    :00401271 C1E003                  shl eax, 03                 |---转换过程二
    :00401274 BA45230100              mov edx, 00012345           |
    :00401279 F7E8                    imul eax                     |
    :0040127B 03C2                    add eax, edx                |
    :0040127D 8945FC                  mov dword ptr [ebp-04], eax |
    :00401280 41                      inc ecx                     |
    :00401281 83F912                  cmp ecx, 00000012           |
    :00401284 72D1                    jb 00401257                 /
    :00401286 E875FDFFFF              call 00401000      
    :0040128B 33C0                    xor eax, eax

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:004012A2(C)
    |
    :0040128D 8A4C0594                mov cl, byte ptr [ebp+eax-6C] \
    :00401291 8A5405C8                mov dl, byte ptr [ebp+eax-38] |
    :00401295 80E930                  sub cl, 30                    |
    :00401298 32D1                    xor dl, cl                    |---转换过程三
    :0040129A 885405C8                mov byte ptr [ebp+eax-38], dl |
    :0040129E 40                      inc eax                       |
    :0040129F 83F812                  cmp eax, 00000012             |
    :004012A2 72E9                    jb 0040128D                   /
    :004012A4 E857FDFFFF              call 00401000
    :004012A9 8D55C8                  lea edx, dword ptr [ebp-38]
    :004012AC 52                      push edx
    :004012AD E85EFEFFFF              call 00401110    ;转换过程四
    :004012B2 E849FDFFFF              call 00401000   
    :004012B7 8D45C8                  lea eax, dword ptr [ebp-38]

    * Possible StringData Ref from Data Obj ->"KeyGenNING4NEWBIES"
                                      |
    :004012BA 6814514000              push 00405114    ;固定字串"KEYGENNING4NEWBIES"
    :004012BF 50                      push eax    ;上面转换而来的字串
    :004012C0 E86BFEFFFF              call 00401130    ;进行比较
    :004012C5 83C40C                  add esp, 0000000C
    :004012C8 85C0                    test eax, eax
    :004012CA 753C                    jne 00401308    ;关键跳转
    --------------------------------------------------------------------------------

    找串式参考,然后向上很容易找到关键跳转。可以发现是对EAX进行判断,在一般情况下这EAX就是上面CALL的返回值了。再看CALL上面有两个PUSH,猜猜是什么?应该是对字符串比较吧。在上面就可以看出405114是一个固定的字串"KEYGENNIG4NEWBIES",动态跟一跟看EAX是啥,果然,也是一个同样长度的字符串。这时几乎可以肯定这个EAX就是我们的用户名和注册码进行某种运算产生的结果。那么我们就跟一跟看:

    从前面的方法找到这个函数的开头,开头还是一些初始化的东东,向下看,可以找到一个API:GetDlgItemText这个API从名字猜猜看,就是我们介绍过的GetDlgItem和GetWindowText和“合体” 具体说明请查资料,我不写了。值得一提的是程序把这个CALL的地址先放在EDI里,以后每次调用时只用CALL EDI就行了,这是多次调用同一API时翻译器的优化方法,注意!

    调用这个API后返回值是得到的文本的长度,然后在后面对注册码的长度进行一些计算(注释在上面),我们可以反算出注册码的长度为12h位(多于12h位也可以,因为只取前12h位)。

    然后就是一个CALL 401000,我直觉感到这是一个重要的CALL,结果跟进去一看:!@#$%,只是把几个寄存器清零了,这告诉我们:直觉会有出错的时候,代码才是检验真理的唯一标准。

    下面一个CALL 401080,在它前面压入了一个参数正是我们的用户名,我直觉感到这个CALL有问题。跟进看看,这回对了啦:

    代码:--------------------------------------------------------------------------------
    * Referenced by a CALL at Address:
    |:00401238   
    |
    :00401080 55                      push ebp
    :00401081 8BEC                    mov ebp, esp
    :00401083 51                      push ecx
    :00401084 53                      push ebx
    :00401085 56                      push esi
    :00401086 57                      push edi

    * Possible StringData Ref from Data Obj ->"eheh"
                                      |
    :00401087 6880504000              push 00405080
    :0040108C 6A00                    push 00000000
    :0040108E E8ADFFFFFF              call 00401040    ;这个CALL有问题
    :00401093 83C408                  add esp, 00000008
    :00401096 8BD8                    mov ebx, eax
    :00401098 E863FFFFFF              call 00401000

    * Possible StringData Ref from Data Obj ->" is a whore."
                                      |
    :0040109D BF70504000              mov edi, 00405070
    :004010A2 83C9FF                  or ecx, FFFFFFFF
    :004010A5 33C0                    xor eax, eax
    :004010A7 F2                      repnz
    :004010A8 AE                      scasb
    :004010A9 F7D1                    not ecx
    :004010AB 2BF9                    sub edi, ecx
    :004010AD 8BF7                    mov esi, edi
    :004010AF 8B7D08                  mov edi, dword ptr [ebp+08]
    :004010B2 8BD1                    mov edx, ecx
    :004010B4 83C9FF                  or ecx, FFFFFFFF
    :004010B7 F2                      repnz
    :004010B8 AE                      scasb
    :004010B9 8BCA                    mov ecx, edx
    :004010BB 4F                      dec edi
    :004010BC C1E902                  shr ecx, 02
    :004010BF F3                      repz
    :004010C0 A5                      movsd
    :004010C1 8BCA                    mov ecx, edx
    :004010C3 83E103                  and ecx, 00000003
    :004010C6 F3                      repz
    :004010C7 A4                      movsb
    :004010C8 33FF                    xor edi, edi
    :004010CA 33F6                    xor esi, esi    ;上面这一段是不是有点晕,没关系,只看结果

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:004010F6(C)
    |
    :004010CC 8B4508                  mov eax, dword ptr [ebp+08]
    :004010CF 50                      push eax    ;在这里D eax看看是什么,RoBa is a whore.
    :004010D0 56                      push esi    ;ESI每次加4,从第ESI个字符开始取值
    :004010D1 E86AFFFFFF              call 00401040    ;还是上面的CALL
    :004010D6 8B8E30504000            mov ecx, dword ptr [esi+00405030];这也是一个表,从里面取值
    :004010DC 83C408                  add esp, 00000008
    :004010DF 33CF                    xor ecx, edi
    :004010E1 03C1                    add eax, ecx
    :004010E3 8945FC                  mov dword ptr [ebp-04], eax
    :004010E6 C145FC07                rol dword ptr [ebp-04], 07  ;ROL是一个“滚动”移位
    :004010EA 8B45FC                  mov eax, dword ptr [ebp-04]
    :004010ED 83C604                  add esi, 00000004
    :004010F0 33D8                    xor ebx, eax    ;进行一些运算,EBX最初是"eheh"=68656865
    :004010F2 47                      inc edi
    :004010F3 83FE40                  cmp esi, 00000040  ;ESI每次加4,所以这是计算10H=16次
    :004010F6 7CD4                    jl 004010CC    ;这里是循环计算
    :004010F8 5F                      pop edi
    :004010F9 8BC3                    mov eax, ebx
    :004010FB 5E                      pop esi
    :004010FC 5B                      pop ebx
    :004010FD 8BE5                    mov esp, ebp
    :004010FF 5D                      pop ebp
    :00401100 C3                      ret
    --------------------------------------------------------------------------------

    呵呵,别害怕,这是整个程序最复杂的一处运算,过了这儿就简单了

    一上来压入了一个"eheh",一个0,然后一个CALL 401040,我跟进。。。。别着急,先带过看返回值是什么?嗯,EAX=68656865。作了这么多练习了,你的“先死”练出来了么?这个EAX怎么这么别扭,就是"eheh"倒过来了呀。'e'=65h,'h'=68h。当然如果没有看出来,跟进一下更保险了。

    再向下,一个字串" is a whore."(whore什么意思牙,我英语太菜)接下来就是倒来倒去。记住repnz scasb/ repnz movsd这样的计算经常是字符串的计算。我们不算三七二十一先走过这一段看看结果如何。走到4010CF时下D eax。呵呵,是"RoBa is a whore."(到底是什么意思?不是骂我的吧)。可见上面那一段就是把用户名接上一个" is a whore." 所以,看不懂的计算不妨先跳过去,往往结果却非常明了。

    接下来就是一个循环。又用了CALL 401040。这次是从字串"RoBa is a whore."里取值,而第二个参数ESI每次加4,可以看出它是控制从第几个字符开始取。从循环的最后可以看出要算10h次。

    每次从字串中取出四个字符放在EAX中后,再从[405030]这个表里从一个值,进行一些计算,把结果与EBX进行XOR,(EBX最初是上面得到的68656865)。这个CALL返回的值就是10H次计算后的EBX。(这一段较乱,也不太好说明,亲自跟一下就会明白,有疑问请看我的注册机是如何实现的。或者直接找我啦)

    好了,我们出来喽,往下走,把返回放在[EBP-4],清空一些寄存器,又来到一个循环。(过程二)

    代码:--------------------------------------------------------------------------------
    :00401257 8B45FC?????????mov eax, dword ptr [ebp-04]?;最初这里是上面过程一的结果
    :0040125A 33D2??????????xor edx, edx???
    :0040125C BE1A000000???????mov esi, 0000001A??
    :00401261 F7F6??????????div esi???;除以1Ah=26
    :00401263 8A941510FFFFFF?????mov dl, byte ptr [ebp+edx-000000F0];根据余数从表中取值
    :0040126A 88540DC8????????mov byte ptr [ebp+ecx-38], dl?;把取得的值组成一个字串
    :0040126E 8B45FC?????????mov eax, dword ptr [ebp-04]?
    :00401271 C1E003?????????shl eax, 03???
    :00401274 BA45230100???????mov edx, 00012345??
    :00401279 F7E8??????????imul eax???
    :0040127B 03C2??????????add eax, edx???;进行一些计算,准备下次再取
    :0040127D 8945FC?????????mov dword ptr [ebp-04], eax?
    :00401280 41???????????inc ecx???;ECX是裚环变量
    :00401281 83F912?????????cmp ecx, 00000012??;共计算12h=18次
    :00401284 72D1??????????jb 00401257???
    --------------------------------------------------------------------------------

    先讲下DIV除法运算。它把商放在EAX,余数放在EDX。在这里我们可以看到除以1Ah即26。[ebp-F0]看看是啥,哈哈是一个字符表"ABCDEFGHIJKLMNOPQRSTUVWXYZ"。根据除得的余数从这个表中取出一个字符,(恰好除以26)。然后把取出的字符放在[ebp+ecx-38]这个地方,因为ECX是递增的,这样就形成了一个新串。对后面的计算也有一点要说明。imul eax这句,是两个32位的数相乘,这样结果会是64位,结果的高32位放在EDX,低32放入EAX,一定要注意!

    下面我们来到过程三:

    代码:--------------------------------------------------------------------------------
    :0040128D 8A4C0594????????mov cl, byte ptr [ebp+eax-6C]?;裚环取假注册码
    :00401291 8A5405C8????????mov dl, byte ptr [ebp+eax-38]?;裚环取过程二的结果字串
    :00401295 80E930?????????sub cl, 30???
    :00401298 32D1??????????xor dl, cl???
    :0040129A 885405C8????????mov byte ptr [ebp+eax-38], dl?
    :0040129E 40???????????inc eax???;EAX是裚环变量
    :0040129F 83F812?????????cmp eax, 00000012??;还是18位
    :004012A2 72E9??????????jb 0040128D???
    --------------------------------------------------------------------------------

    又一个裚环,EAX是裚环变量,[EBP-38]记得是什么,就是过程二得到的那个新串。[EBP-6C]呢,在很远的上面,就是我们的假注册码。用EAX控制每次取一个字符,然后(假码的字符-30h) XOR 新串的字符,生成的新的新串还放在[EBP-38].

    好了,先别晕,下面还有:

    lea edx,[EBP-38]。这时EDX是过程三产生的新串了,又压进去,然后CALL 401110。跟进过程四:

    代码:--------------------------------------------------------------------------------
    :00401110 8B4C2404????????mov ecx, dword ptr [esp+04]?;[ESP+4]就是上面PUSH来的字串啦
    :00401114 33C0??????????xor eax, eax

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:
    |:00401122(C)
    |
    :00401116 8A1408?????????mov dl, byte ptr [eax+ecx]?;依次取过程三产生的新串
    :00401119 32D0??????????xor dl, al???;与裚环变量XOR
    :0040111B 881408?????????mov byte ptr [eax+ecx], dl?;放好结果
    :0040111E 40???????????inc eax
    :0040111F 83F812?????????cmp eax, 00000012??;EAX为裚环变量,计算12H=18次
    :00401122 72F2??????????jb 00401116?
    :00401124 C3???????????ret
    --------------------------------------------------------------------------------

    这个比较简单吧。CHEERS!最终结果终于出来了,然后判断这个最终结果是不是一个固定字串。当然我们的胡乱输入得不到字串"KEYGENNING4NEWBIES"的。那么应该怎么办呢?过程一和过程二都是对用户名的计算,这两步是不可逆的。也就是说从用户名N算出一个新串K,我们不能从K算出N来(我以为是这样,即使可逆应该也十分复杂)可以表示成 F12(N)=K。然后新串K和注册码S都参考计算,又得出一个新串K'。这是过程三,这个计算可逆(不明白?我在最后有说明)。F3(K,S)=K'。再从K'得到一个K''(过程四,也是可逆的)。最后判断这个K''是不是等于固定字串,即判断是否F4(K')=K''="KEYGENNING4NEWBIES"。

    我们写注册机时呢,既然过程一和二(F12)不可逆,我们把它照砡下来得到K,然后可以从"KEYGENNING4NEWBIES"反算(过程四的逆运算)F4'("KEYGENNING4NEWBIES")=K''。最后F2可逆,所以可以求出F3'(K,K')=S。这样就根据用户名求出了注册码S。

    (不知各位看懂没有,我自己都说晕了,这种符号表示真麻烦。其实只要想一想就发现很简单的。)

    注册机:(C,写得很烂,各位将就)

    代码:--------------------------------------------------------------------------------
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    void main()
    {
      unsigned int iEAX,iEBX,i;
      char st[]="KEYGENNING4NEWBIES";
      char alpha[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ";?//过程二用到的表
      char name[70]=;
      int st1[]={
    ? 0x12,0x5C,0x34,0x22,0xAB,0x9D,0x54,0x00,
    ? 0xDD,0x84,0xAE,0x66,0x31,0x78,0x73,0xCF?//过程一用到的表?
      };
      char st2[20]=;
      char password[20]=;

      printf("Please input your name:");
      scanf("%s",name);
      strcat(name," is a whore.");
      
      iEBX=0x68656865;
      for (i=0;i<16;i++)
      {
    ? iEAX=(name[i*4])|(name[i*4+1]<<8)|
    ?? (name[i*4+2]<<16)|(name[i*4+3]<<24);
    ? iEAX+=(st1^i);
    ? iEAX=(iEAX<<7)|(iEAX>>25);?//我用这个表示ROL
    ? iEBX^=iEAX;
      }????//过程一
      iEAX=iEBX;
      for (i=0;i<0x12;i++)
      {
    ? int k=iEAX%26;
    ? st2=alpha[k];
    ? __asm
    ? {
    ?? mov eax,iEAX
    ?? shl eax,3
    ?? mov edx,12345h
    ?? imul eax
    ?? add eax,edx
    ?? mov iEAX,eax
    ? }???//用了汇编
      }????//过程二
      for (i=0;i<18;i++)
    ? st=st^i;??//过程四的逆运算
      for (i=0;i<18;i++)
      {
    ? password=st2^st;
    ? password+=0x30;
      }????//过程三的逆运算,得到注册码
      printf("Your Serial number is:\n%s\n",password);
      printf("Keygen by RoBa\nEnjoy Cracking!\n");
    }
    --------------------------------------------------------------------------------

    一个可用的用户名:RoBa
    注册码::98D34M<N9?:AH85F

    这个CRACKME设计有点缺陷,算出的注册码有可能是不可显示字符。开始我以为是我注册机错了,可把字符串粘贴过去以后是正确的。

    一点说明:过程二的那个乘法我前面说了,其实结果是64位的,而高级语言里表示64位真的很麻烦,我只知道C里面有一个__int64,但程序老是出错,不得已加进了一段汇编,简单几句就解决了,可见汇编决不能丢掉

    还有过程三和过程四为什么可逆,我想各位菜鸟同胞搞不清的可能就是异或。我可以明确的告诉大家,XOR运算是可逆的,逆运算仍然是XOR。下面我来证明:因为XOR是按位计算,所以只要一个二进制位的XOR可逆,那么整个数的计算也就可逆。来看

    当A=0,B=0时,C=A XOR B=0,那么A=C XOR B=0,B=C XOR A=0
    当A=0,B=1时,C=A XOR B=1,那么A=C XOR B=0,B=C XOR A=1
    当A=1,B=0时,C=A XOR B=1,那么A=C XOR B=1,B=C XOR A=0
    当A=1,B=1时,C=A XOR B=0,那么A=C XOR B=1,B=C XOR A=1
      
    我说的对吧?

    后记:这个CRACKME单从注册算法来说算是比较复杂的了,能够达到大部分共享软件的水平(某些采用密码学算法的BT除外)。对于新手来说可能会有些吃力(写破文更费劲),但如果能够弄明白的话,相信你已綺能够搞定很大一部分软件的算法了。

    当然,在具体的软件保护中不光是要解开算法,PE文件知识、壳的知识等也非常重要。(我正学脱壳,已綺能脱UPX,ASPack了,呵呵,是不是很白痴。。。)让我们一起努力吧。

    [ 本帖最后由 wzwgp 于 2006-2-14 09:16 编辑 ]
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表