首页
社区
课程
招聘
[原创]我的第二个Crack ME
发表于: 2008-3-28 17:20 4685

[原创]我的第二个Crack ME

2008-3-28 17:20
4685
【文章标题】: 加密解密习题破文
【文章作者】: kanghtta
【作者邮箱】: kanghtta@hotmail.com
【作者主页】: b93K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3E0S2L8X3N6Z5N6s2c8S2i4K6u0W2j5%4g2T1L8r3!0Y4i4K6u0W2j5$3^5`.
【作者QQ号】: 18381291
【软件名称】: echap512
【软件大小】: 4kb
【下载地址】: 加密解密第二版光盘
【加壳方式】: 无壳
【编写语言】: masm32
【使用工具】: peid  OllyICE
【操作平台】: windows
【软件介绍】: 难度--稍难(不是我说的,出题人给的)
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
     大家好,有一就有二嘛!昨天做了个我的第一个练习,本想自己练练,但是觉得把自己破解的思路记下来,以后也好温习温习,
  还是写一写好,本来昨晚就写得差不多了,可由于在宿舍住,阿姨关电是不是从来不看时间啊,唉。。我写了2小时的文章
  就那样灰飞烟灭了,也不想抱怨什么了,这个社会上抱怨是没用的。 (我废话真多,嘿嘿。。jmp short 正文)。
  
  由于题目给的是稍难,但是不用管这个,难不难都试一试吧! 
   
  首先,还是老格调,拿到一个程序呢,先运行一下看看,找找提示信息,熟悉熟悉软件环境。好了,看到一个编辑框,
  直接 OK它, 熟悉的Messagebox出来了,不过不用高兴,它带来的不是什么好消息: Wrong S/N #
  
  现在,用peid载入看看,主要是看有没有壳,(乌龟盖),还有软件的一些信息。
  (其它的不用说了吧。其实也不知道怎么说,你用一下就知道了)
  
  刚才说过了,messagebox跳出来说了句:Wrong S/N #,一般的思路都是用字串参考,不过还是用OD载入看看。
  一开始就是GetModuleHandle ,获取程序模块句柄,好了,先不用字串参考了,上下拖拖看。(指滚动条)
  
  窗口产生过程的流程都差不多,获取模块句柄,注册窗口类,创建窗口,更新客户区,消息循环。和昨天不同的是,
  昨天的用对话框,今天用CreateWidnowEx 用了三次,分别产生:窗口,编辑框,按钮,(后两个窗口用系统预定义类)
  
  在往下拉拉看,找到关键,嘿嘿,,,
  0040118F  |. /75 5E         jnz     short 004011EF
  00401191  |. |6A 28         push    28                               ; /Count = 28 (40.)
  00401193  |. |68 A6204000   push    004020A6                         ; |Buffer = echap512.004020A6
  00401198  |. |FF35 4C204000 push    dword ptr [40204C]               ; |hWnd = NULL
  0040119E  |. |E8 00010000   call    <jmp.&USER32.GetWindowTextA>     ; \GetWindowTextA
  004011A3  |. |FF35 44204000 push    dword ptr [402044]               ; /Arg1 = 00000000
  004011A9  |. |E8 5D000000   call    0040120B                         ; \echap512.0040120B
  004011AE  |. |83F8 01       cmp     eax, 1
  004011B1  |. |75 1E         jnz     short 004011D1
  004011B3  |. |6A 30         push    30                               ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
  004011B5  |. |68 81204000   push    00402081                         ; |Title = "Good For U!"
  004011BA  |. |68 8D204000   push    0040208D                         ; |Text = "U Did It!!"
  004011BF  |. |FF35 48204000 push    dword ptr [402048]               ; |hOwner = NULL
  004011C5  |. |E8 EB000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
  004011CA  |. |5E            pop     esi
  004011CB  |. |5F            pop     edi
  004011CC  |. |5B            pop     ebx
  004011CD  |. |C9            leave
  004011CE  |. |C2 1000       retn    10
  004011D1  |> |6A 30         push    30                               ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
  004011D3  |. |68 98204000   push    00402098                         ; |Title = "=)"
  004011D8  |. |68 9B204000   push    0040209B                         ; |Text = "Wrong S/N#"
  004011DD  |. |FF35 48204000 push    dword ptr [402048]               ; |hOwner = NULL
  004011E3  |. |E8 CD000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
  004011E8  |. |5E            pop     esi
  004011E9  |. |5F            pop     edi
  004011EA  |. |5B            pop     ebx
  004011EB  |. |C9            leave
  004011EC  |. |C2 1000       retn    10
  
  建议大家如果程序比较大的时候,还是用字串参考,这里先看两个地址, 如果可能,请记下它们.
  004011E3  |.  E8 CD000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
  
  004011C5  |.  E8 EB000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
  
  分别是不成功和成功注册给的提示信息。接下来,我们上下拉拉看,找找有没有Call Jxx 阿什么的跳到这两个地址。
  结果不容乐观。但不用担心,如果字串找到了,你一般留心下函数。
  看到这个没有
  0040119E  |.  E8 00010000   call    <jmp.&USER32.GetWindowTextA>     ; \GetWindowTextA
  
  GetWindowText函数复制一个指定的窗口标题文本(如果它有的话)到一缓冲区中。如果指定的窗口是控件,那么控件文本
  被复制。然而,GetWindowText不能获得另一应用程序的控件文本。
  原型如下:
  int GetWindowText(
    HWND hWnd,        // handle to window or control with text  有文本的窗口或是控件的句柄。
    LPTSTR lpString,  // address of buffer for text   文本缓冲区地址
    int nMaxCount     // maximum number of characters to copy能复制的最大字符数。
  );
  
  0040119E  |.  E8 00010000   call    <jmp.&USER32.GetWindowTextA>     ; \GetWindowTextA
  004011A3  |.  FF35 44204000 push    dword ptr [402044]               ; /Arg1 = 00000000
  004011A9  |.  E8 5D000000   call    0040120B                         ; \echap512.0040120B
  004011AE  |.  83F8 01       cmp     eax, 1
  004011B1  |.  75 1E         jnz     short 004011D1
  004011B3  |.  6A 30         push    30                               ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
  
  004011A9  |.  E8 5D000000Call 0040120B  初步断定为关键,获取完文本,接下来一般都是对文本做写处理或者是变换。
  
  废话少说 在0040119E处 F2,然后Ctrl +F2 重新载入后,按F9 运行:
  窗口出来了,我输入的是 : kanghtta (当然,你可以输入你喜欢的任何字符,要是直接输入注册码,你就神了,嘿嘿。。)
  
  现在我们F8来到004011A9 处F7跟进,哈哈,看下面
  0040120B  /$  C8 000000     enter   0, 0
  0040120F  |.  53            push    ebx
  00401210  |.  52            push    edx
  00401211  |.  33C0          xor     eax, eax
  00401213  |.  B8 A6204000   mov     eax, 004020A6                    ;  ASCII "kanghtta"( 熟悉吧,我是很熟悉的)
  00401218  |.  8038 00       cmp     byte ptr [eax], 0
  0040121B  |.  74 60         je      short 0040127D 是否输入的是空字符,如果是,跳走,估计程序也完蛋了,这么说的原因是,我不知道它跳到什么地方,干了什么事
  
  0040121D  |.  33DB          xor     ebx, ebx                                                                    
  0040121F  |.  33D2          xor     edx, edx                       ; 接下来两句是:寄存器清0
  00401221  |>  8A18          /mov     bl, byte ptr [eax]           
  00401223  |.  C1C3 08       |rol     ebx, 8
  00401226  |.  03D3          |add     edx, ebx
  00401228  |.  40            |inc     eax
  00401229  |.  8038 00       |cmp     byte ptr [eax], 0
  0040122C  |.^ 75 F3         \jnz     short 00401221                  这一段的功能是循环计算输入字符,算法一会下面给出
  0040122E  |.  52            push    edx                              ; /<%lX> = 7FF45409
  0040122F  |.  68 54204000   push    00402054                         ; |Format = "%lX"
  00401234  |.  68 BF204000   push    004020BF                         ; |s = echap512.004020BF
  00401239  |.  E8 8F000000   call    <jmp.&USER32.wsprintfA>          ; \wsprintfA
  0040123E  |.  BB BF204000   mov     ebx, 004020BF               ;ebx 为74746168
  00401243  |.  803B 38       cmp     byte ptr [ebx], 38          第一字节和38比较
  00401246  |.  75 35         jnz     short 0040127D             结果不为0则跳转,F8跟进,结果程序完蛋.Wrong....
  00401248  |.  807B 01 44    cmp     byte ptr [ebx+1], 44
  0040124C  |.  75 2F         jnz     short 0040127D
  0040124E  |.  807B 02 43    cmp     byte ptr [ebx+2], 43
  00401252  |.  75 29         jnz     short 0040127D
  00401254  |.  807B 03 41    cmp     byte ptr [ebx+3], 41
  00401258  |.  75 23         jnz     short 0040127D
  0040125A  |.  807B 04 46    cmp     byte ptr [ebx+4], 46
  0040125E  |.  75 1D         jnz     short 0040127D
  00401260  |.  807B 05 33    cmp     byte ptr [ebx+5], 33
  00401264  |.  75 17         jnz     short 0040127D
  00401266  |.  807B 06 36    cmp     byte ptr [ebx+6], 36
  0040126A  |.  75 11         jnz     short 0040127D
  0040126C  |.  807B 07 38    cmp     byte ptr [ebx+7], 38
  00401270  |.  75 0B         jnz     short 0040127D
  00401272  |.  B8 01000000   mov     eax, 1
  00401277  |.  5A            pop     edx
  00401278  |.  5B            pop     ebx
  00401279  |.  C9            leave
  0040127A  |.  C2 0400       retn    4
  将运算的结果和 38 44 43 41 46 33 36 38 依次比较,任何一个不等,就跳到0040127D现在我们看看此处代码:
  
  0040127D  |>  33C0          xor     eax, eax   eax 清0
  0040127F  |.  5A            pop     edx  恢复现场
  00401280  |.  5B            pop     ebx
  00401281  |.  C9            leave         恢复现场,和堆栈维护
  00401282  \.  C2 0400       retn    4  
  返回到
  
  004011AE  |.  83F8 01       cmp     eax, 1    eax 和1比 不等则完蛋.
                                               ;而eax在040127D处被清0 这就是说,只要跳转到0040127D处,程序就完蛋
  004011B1  |.  75 1E         jnz     short 004011D1
  004011B3  |.  6A 30         push    30                               ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
  
  下面我们来分析注册的算法:
  这里先留意一个API函数: wsprintf
  The wsprintf function formats and stores a series of characters and values in a buffer.
  Wsprintf在缓冲区中格式化并储存一系列的字符和值,任何自变量都能依照在格式串中指定的格式被修改和复制到输出缓冲区中.
  这个函数在输入的字符串后面附加一个NUll终结符,但是返回值中并不包含这个终结符的计数.
  它可以将数值按指定的格式翻译成字符串:
  int wsprintf(
    LPTSTR lpOut,    // pointer to buffer for output  指向输出缓冲区的指针
    LPCTSTR lpFmt,   // pointer to format-control string  指向格式控制字符串的指针
    ...              // optional arguments   自变量选项,这个参数的类型和大小依赖以在lpFmt参数中指定的格式控制规格.
  );
  lx, lX   A long unsigned hexadecimal integer in lowercase or uppercase
  
  程序算法:
  1.将由GetWindowText获得的文本从左到右输入一个字符到bl中.
  2,bl循环右移8位,
  3,将右移后得到的值和edx相加,edx初值为0,结果保存在edx中
  4,取下一字符
  5,比较,看看取得的字符是否为NULL ,如果是,说明字符串结束,如果不是,回到第1步
  6,将计算的到的edx值格式化后输出到缓冲区中
  7,缓冲区的字符串依次和38 44 43 41 46 33 36 38比较.,字符串翻译为: 8DCAF368
  
  ebx=00006B00
  ebx=006B6100
  ebx=6B616E00
  ebx=616E676B
  ebx=6E676861
  ebx=6768746E
  ebx=68747467
  ebx=74746168
  --------------------------
  edx=7FF45409
  
  0040123E  |.  BB BF204000   mov     ebx, 004020BF       取缓冲区地址             ;  ASCII "7FF45409"
  00401243  |.  803B 38       cmp     byte ptr [ebx], 38  缓冲区数据比较
  00401246  |.  75 35         jnz     short 0040127D
  
  004020BF=004020BF (ASCII "7FF45409")
  ebx=74746168
  
  
  我们知道最后比较的字符串为:8D CA F3 68,现在考虑如何逆向它:
  
  ebx=00006B00   00k0
  ebx=006B6100   0ka0
  ebx=6B616E00   kan0
  ebx=616E676B   angk
  ebx=6E676861   ngha
  
  ebx=6768746E   ghtn
  ebx=68747467   httg
  ebx=74746168   ttah
  --------------------------
  edx=7FF45409
  也就是 最后的edx的结果要是8DCAF368
  
   n个字符经循环移位变换后得n/2个字符,
     
  我们找一些ASIIC小的字符作为被减数,
  
  我们取4字符: ! " # %  作为被减数
   
    由于开始取四字符,从后向前减,往往后面不够减,故从前向后推.
  取 k = !  减去 00002100
  我们的字符依次减,! - # " ! 可到第6步步够减,
  因此缩短注册码的数目减为6位
  
  ebx=00006B00   0 0 k 0
  ebx=006B6100   0 k a 0
  ebx=6B616E00   k a n 0
  ebx=616E676B   a n g k
  ebx=6E676861   n g h a
  
  ebx=6768746E   g h t n
  给出算法:
  假如我们的注册码是: kanght ( 6位)
                                 所以输入超过6位你就不好算了
       k+a+n+g  ==      8D
       k+a+n+g+h  ==    CA
       k+a+n+g+h+t  ==  F3
       k+a+n   ==       68
  
   按顺序来,求这个多项式的值,满足就可以.注册码应该有很多.( 有点像高中的解多项式,哈哈,不过变量是多元的)
   大家观察一下,应该从变量少的加到变量多的,就是先求3个变量的,在求四个的.为什么呢? 好计算阿,所以说学数学很重要。
  
   我最后求的注册码是:  " $ " %  = )  注意输入的时候切换成英文,要不出错那就不怪我了。
                        22 24 22 25 3D 29  
  下面我们来写一个计算注册号的程序;
  伪算法: 
   其中涉及到进制转换,那就是你编程的水平了。
       char k,a,n,g,h,t;
       cout<<"请输入注册号的前两位:(注意,输入两数之和不大于0x68 " <<endl;
       cin>>k>>a;
       n=0x68-k-a;
       g=0x8D-k-a-n;
       h=0xCA-k-a-n-g;
       t=0xF3-k-a-n-g-h;
  
      
   
  
  
  
   
  
  
  
  
  
--------------------------------------------------------------------------------
【经验总结】
    分析的关键部分没花多少时间,可是算法花了我4小时,由于刚开始学习,不过还是自己动手练出来了。嘿嘿。。
   以后得花时间学数学了, 现在明白它真的很重要。。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2008年03月28日 下午 05:15:34

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 8209
活跃值: (4559)
能力值: ( LV15,RANK:2473 )
在线值:
发帖
回帖
粉丝
2
标题不要这样起啊,容易误解
2008-3-28 17:44
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
以及被我误解了。。。。
2008-3-28 19:23
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
我是第三個误解了的人

加上破文两字比较好
2008-3-28 20:14
0
雪    币: 420
活跃值: (77)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝
5
恩,接受!  
下次改,刚开始练,也不知道怎么写,就想从一二三...按顺序写下去, 嘿嘿..
   
    谢谢大家指正 !!
2008-3-28 20:54
0
游客
登录 | 注册 方可回帖
返回