锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务,锦州广厦维修电脑,公司IT外包服务
topFlag1 设为首页
topFlag3 收藏本站
 
maojin003 首 页 公司介绍 服务项目 服务报价 维修流程 IT外包服务 服务器维护 技术文章 常见故障
锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务技术文章
投鼠忌器(更新注册机)——160个crackme之24:Chafe.2

作者: buddama  日期:2017-04-27 11:34:52   来源: 本站整理

 界面很简单,输入name和serial的输入框和一个结果提示的标签。错误提示如下:
    
        没有按钮触发检验过程,也没有对话框弹出。所以不能用这些相关函数下断点了。好在搜索文本的话,可以看到如以上二图Status那一栏的提示语:“Your serial is not valid.”或“YES! You found your serial!!!”。好了,双击Your serial is not valid来到函数调用的地方。

[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
004012D7   . /EB 04         jmp short 160-24-C.004012DD
004012D9     |54            push esp
004012DA     |45            inc ebp
004012DB     |58            pop eax                                 ;  kernel32.766DEF8C
004012DC     |00AD 33D84975 add byte ptr ss:[ebp+0x7549D833],ch
004012E2   ?  FA            cli
004012E3   .  81FB FBCFFCAF cmp ebx,0xAFFCCFFB
004012E9    ^ 74 EE         je short 160-24-C.004012D9              ;  je short 004012D9->JNE
004012EB      68 59304000   push 160-24-C.00403059                  ; /Your serial is not valid.
004012F0   .  FF35 5C314000 push dword ptr ds:[0x40315C]            ; |hWnd = NULL
004012F6   .  E8 7D010000   call <jmp.&USER32.SetWindowTextA>       ; \SetWindowTextA
004012FB   .  33C0          xor eax,eax                             ;  kernel32.BaseThreadInitThunk
004012FD   .  C9            leave
004012FE   .  C2 1000       retn 0x10
00401301   .  68 73 30 40 0>ascii "hs0@",0                          ;  YES! You found your serial!!
00401306   .  FF35 5C314000 push dword ptr ds:[0x40315C]            ; |hWnd = NULL
0040130C   .  E8 67010000   call <jmp.&USER32.SetWindowTextA>       ; \SetWindowTextA
00401311   .  33C0          xor eax,eax                             ;  kernel32.BaseThreadInitThunk
00401313   .  C9            leave
00401314   .  C2 1000       retn 0x10


    看这段代码的话,错误提示排在正确提示前面,那么推测他们前面必然有一个关键跳,serial正确的话会跳到00401301;错的话会走向004012EB。因此我们开始往上翻。不对啊,翻到段头也没有看到一个满足这样的跳转逻辑的关键跳。怎么回事?
    没办法,只好翻到段首下断点。

[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
00401255   > \3B05 58314000 cmp eax,dword ptr ds:[0x403158]
0040125B   .  74 0C         je short 160-24-C.00401269
0040125D   .  3B05 54314000 cmp eax,dword ptr ds:[0x403154]
00401263   .  0F85 AE000000 jnz 160-24-C.00401317
00401269      C705 D9124000>mov dword ptr ds:[0x4012D9],0x584554    ;  vengers
00401273   .  6A 00         push 0x0                                ; /IsSigned = FALSE
00401275   .  8D45 FC       lea eax,dword ptr ss:[ebp-0x4]          ; |0
00401278   .  50            push eax                                ; |pSuccess = kernel32.BaseThreadInitThunk
00401279   .  6A 64         push 0x64                               ; |ControlID = 64 (100.)
0040127B   .  FF35 50314000 push dword ptr ds:[0x403150]            ; |hWnd = NULL
00401281   .  E8 BC010000   call <jmp.&USER32.GetDlgItemInt>        ; \GetDlgItemInt
00401286   .  837D FC 00    cmp dword ptr ss:[ebp-0x4],0x0
0040128A   .  74 5F         je short 160-24-C.004012EB
0040128C   .  50            push eax                                ;  kernel32.BaseThreadInitThunk
0040128D   .  6A 14         push 0x14                               ; /用户名最长不得超过(20.)
0040128F   .  68 6C314000   push 160-24-C.0040316C                  ; |用户名
00401294   .  FF35 54314000 push dword ptr ds:[0x403154]            ; |hWnd = NULL
0040129A   .  E8 AF010000   call <jmp.&USER32.GetWindowTextA>       ; \GetWindowTextA
0040129F   .  85C0          test eax,eax                            ;  判断name不为空
004012A1   .  74 48         je short 160-24-C.004012EB
004012A3   .  A1 0B304000   mov eax,dword ptr ds:[0x40300B]         ;  CTEX
004012A8   .  BB 6C314000   mov ebx,160-24-C.0040316C               ;  输入的name
004012AD   >  0303          add eax,dword ptr ds:[ebx]
004012AF   .  43            inc ebx
004012B0   .  81FB 7C314000 cmp ebx,160-24-C.0040317C
004012B6   .^ 75 F5         jnz short 160-24-C.004012AD             ;  这是一轮累加,CTEX开始,依次取用户名四位加和,会溢出。eax中为后八位。


        运行程序,输入name:abcdefgh,serial打算用123456。刚输入一个1,运行就被中断到段首。需要注意的是,程序的窗口上,数字1还没有显示出来呢。可见程序的处理流程是接收到用户输入的每一个serial的字符都要先判断一次,确定正确与否之后才能显示在serial的文本框里。Name就不存在这种情况。简单的说序列号是随输随检的。
        好了,我们用F8步进,并同时观察数据窗。刚开始就是GetDlgItemInt函数,这是在读取用户输入,还要求是整数形式。好的,我们输入的就是数字。
        继续,在0040128F出现了name 的GetWindowTextA函数,这时我们发现输入的name,最长字符长度等参数的入栈过程。这里0x14就是最长长度,20个字符。
        在判断name不为空之后,程序对name进行了一种运算。它从字符串CTEX出发,将name截取前四个字符,与CTEX相加。然后截去name的首位,再截取前四个字符,继续与之前的累加和相加。一直进行,截到后面不足4位的就用0补齐。这样就得到一个累加和。 
    这里有一点需要留意的是,这个累加和会溢出,而eax只能保存后八位。
    继续往下走。

[Asm] 纯文本查看 复制代码
1
2
3
4
5
004012B8   .  5B            pop ebx                                 ;  取序列号来检测,随输随检,此时界面都还没显示;EBX=1
004012B9   .  03C3          add eax,ebx                             ;  160-24-C.0040317C
004012BB      3105 D9124000 xor dword ptr ds:[0x4012D9],eax         ;
004012C1   .  C1E8 10       shr eax,0x10
004012C4      66:2905 D9124>sub word ptr ds:[0x4012D9],ax           ;


       以上几句看起来是很简单的运算,之前的累加和与序列号接收到的数字(此处为1)相加,其和与004012D9处的dword字符串做异或运算,结果保存在004012D9;其和取后四位,再与004012D9处的word做减法,结果保存在004012D9。毫不起眼的几句指令啊。
      楼主随即用perl复现了以上流程。

[Perl] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/perl
$name="abcdefgh";
#程序对serial随输随检,这里只用serial的第一位;
$serial="1";
$len=length($name);
@rev =$name=~/\w{1}/g;
$rev= join "",reverse(@rev);
$sum=hex(58455443);#"CTEX";
for($i=1;$i<5;$i++){
         $out=substr($rev,0,$i);
         $out=hex(str2hex($out));
         $sum += $out;
         }
for($i=1;$i<$len-3;$i++){
         $out=substr($rev,$i,4);
         $out=hex(str2hex($out));
         $sum += $out;
         }
print hex($sum),"\n";#报错,这个数会溢出,因此只取后八位。
#取后八位;
$mod=&de_overflow_dec($sum);
$mod += hex($serial);
sub de_overflow_dec(){
                   $dec = shift;
                   # print "$dec will be de_overflowed...\n";
                   $dec %= 4294967296;#0x100000000;
                   return $dec;
         }
         
sub str2hex{
         my $s = shift;
         my $str;
         for (0..length($s)-1) {
                   $str .= sprintf "%0x", ord substr($s, $_, 1);
         }
         return $str;
}
sub dec2hex(){
                   $dec=shift;
                   $hex=sprintf "%0x",$dec;
                   return $hex;
                   }



        一算,完全没问题,跟程序算出来的一模一样。感觉离结果更近了有没有?
       继续。

[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
004012CB   .  BE EC114000     mov esi,160-24-C.004011EC               ;  字串的位置,共62个dword
004012D0   .  B9 3E000000     mov ecx,0x3E
004012D5   .  33DB            xor ebx,ebx
004012D7   .  EB 04           jmp short 160-24-C.004012DD
004012D9      14 07           adc al,0x7
004012DB      18F0            sbb al,dh
004012DD      AD              lods dword ptr ds:[esi]
004012DE      33D8            xor ebx,eax
004012E0      49              dec ecx
004012E1    ^ 75 FA           jnz short 160-24-C.004012DD
004012E3   .  81FB FBCFFCAF   cmp ebx,0xAFFCCFFB
004012E9    ^ 74 EE           je short 160-24-C.004012D9              ;  疑似的关键跳转。


       继续F8。我们发现程序指定了一个内存位置004011EC做起始,和一个计数器3E(即62),然后跳向004012DD循环读取Dword。以EBX=0起始,每读取依次均与EBX异或,直至循环完成。简单的说,就是0与62个Dword依次异或。最后判断这个结果是否等于0xAFFCCFFB.如果不等的话回到004012D9。这次对al操作先加7再减D,随后又进入上面那样的异或的循环。
        你是不是隐约感到抓住关键判断和关键跳转了?Too young too simple, sometimes naïve。 我们不能让004012E9跳转,因为跳转了就会进入一个无穷无尽的循环;可是不跳转马上就报错了,怎么办啊?
      于是想到那我修改004012E3处的校验码0xAFFCCFFB不就可以了?嘿嘿,难不到我啦!
        楼主还是用perl算啦。先从004011EC到004012E3复制出来一点HEX,然后用它们做异或,只要我得到这个结果,替换原先的校验码0xAFFCCFFB一定可以的,哈哈。
        说干就干,还是perl;

[Perl] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
#!/usr/bin/perl
#下面这个字符串就是用来校验的;
$cons="558BEC83C4FC8B450C83F810750D6A00E86B02000033C0C9C2100083F80F750E8B
4508E81801000033C0C9C2100083F801750633C0C9C210003D110100000F85E70000008B4
5143B0560314000751A6A00689630400068A7304000FF7508E81702000033C0C9C210003B0
558314000740C3B05543140000F85AE000000C705D9124000544558006A008D45FC506A64F
F3550314000E8BC010000837DFC00745F506A14686C314000FF3554314000E8AF01000085C
07448A10B304000BB6C31400003034381FB7C31400075F55B03C33105D9124000C1E81066
2905D9124000BEEC114000B93E00000033DBEB049306F100AD33D84975FA81";
$seed="0x00000000";
for($i=1;$i<63;$i++){
         $curstr=substr($cons,($i-1)*8,8);
         @splitstr=split(//,$curstr);
         $curstr=join('',$splitstr[6],$splitstr[7],$splitstr[4],$splitstr[5],$splitstr[2],$splitstr[3],$splitstr[0],$splitstr[1]);
         $seed=hex($seed) ^ hex($curstr);
         $seed=sprintf "%0x",$seed;
}
print $seed,"\n";



好了,根据以上脚本的计算结果,楼主兴冲冲的把得到的结果0adcb7fb替换了0xAFFCCFFB。保存,重新运行,我靠了,居然还是“Your serial is not valid.”




      楼主只好重新一步步对比异或循环的结果,一个数值一个数值的对。一直到快接近字串末尾的049306F1运算时,居然跟eax的值不相同了。下一个数值00AD33D8出现了同样的情况。这样异或运算到最后的时候,已经不是0adcb7fb了。
    真TM的见鬼了,一个字符串还能不相同,我明明是拷贝过去的啊!
    楼主在此盘旋了好久好久,就是想不明白。于是把004011EC到004012E3内存位置下了一个写入断点,看看到底发生了什么诡异事件。




    结果断在了这里:

[Asm] 纯文本查看 复制代码
1
00401269      C705 D9124000 5>mov dword ptr ds:[0x4012D9],0x584554    ;  vengers


         什么,那个校验用的字符串被修改了,校验值还能对吗?可是这程序把自己修改了,TM的居然还能正常运行。
        继续运行,又断在了以下两处:

[Asm] 纯文本查看 复制代码
1
2
004012BB      3105 D9124000   xor dword ptr ds:[0x4012D9],eax         ;
004012C4      66:2905 D912400>sub word ptr ds:[0x4012D9],ax           ;


       我一下恍然大悟了,这不是之前觉得毫不起眼的那几句指令啊!程序把自己修改了,而且修改的一定是关键的跳转,否则我们无法解释找不到合理的关键跳的理由。那怎么找回原来的代码呢?程序之前给出的校验码就能起到这个作用啊。利用校验码倒推!
        楼主看了下,这几处修改涉及了第60和61个字符串,根据00401269、004012BB和004012C4的运算可以推出正确的代码应该是04XXXXXXXX和XXAD33D8的样式。我们现在把这些X算出来。
        因为其他60个字串时候不变的,可以算出他们的累计的异或值,再与校验码0xAFFCCFFB异或,即为04XXXXXXXX和XXAD33D8异或的值。楼主这里就不贴perl脚本了。这样就算出他们分别是:04EB2654和58AD33D8。
        验证一下。

 

        好了,楼主又兴冲冲的在反汇编窗口把那些被修改的指令修改乘了推算的指令,奇迹出现了:

[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
004012D7   . /EB 04           jmp short 160-24-C.004012DD
004012D9     |EB 26           jmp short 160-24-C.00401301                                ;被复原的指令
004012DB     |54              push esp
004012DC     |58              pop eax
004012DD     \AD              lods dword ptr ds:[esi]
004012DE      33D8            xor ebx,eax
004012E0      49              dec ecx
004012E1    ^ 75 FA           jnz short 160-24-C.004012DD
004012E3   .  81FB FBCFFCAF   cmp ebx,0xAFFCCFFB
004012E9    ^ 74 EE           je short 160-24-C.004012D9              ;
……
00401301   .  68 73 30 40 00  ascii "hs0@",0                          ;  YES! You found your serial!!
00401306   .  FF35 5C314000   push dword ptr ds:[0x40315C]            ; |hWnd = 0037095E ('Your serial is not valid.',class='Edit',parent=00430852)
0040130C   .  E8 67010000     call <jmp.&USER32.SetWindowTextA>       ; \SetWindowTextA


        原来被修改的是一个关键的跳转!而且,这个跳转直接指向序列号正确的提示便签那里!
        问题好像解决了。保存,运行,我去,还是提示“Your serial is not valid.”!




      什么?指令都按照校验的要求修改过了,怎么还不对?忽然想起曾经被我忽略的那三条语句,它们每次校验前都会修-改-一-次指令,而且修改是根据输入的serial计算的。如果你计算出这个修改,并替换校验码的话,下一次运行它又变了!
      那我就不让你修改内存指令了行不行?楼主二话不说,把上面3个指令全部nop掉。而且这次程序虽然不修改指令了,可是你nop掉不是又改了校验字串了吗?对。楼主又把校验那个004012E9的跳转指令从je改为了jne。
     运行,serial随便输入一些数字,终于正确了:

 



         最后提醒一下,将修改后的字符串用我前面提供的脚本计算循环异或值的话,结果是0x1aeea0dd。所以,之前修改je后面的校验值改为这个数。我想起了这个软件的作者恶作剧的脸……
 

        爆破部分就写到这里了。



        注册机。要使被修改了的代码正确的复原,必须是name和serial满足一定的计算关系。楼主用perl来实现serial的计算。

[Perl] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/perl
#name和serial需满足的条件:
#(1)以二者为参数进行运算,对内存代码进行修改,使0x004012D8-0x004012DF段成为正确的代码(04EB265458AD33D8);
#(2)为满足1,要求window-sum(name)应小于推算出来的(window-sum(name)+serial);或者说serial必须为正整数;因此name不是随意定的。
 
print "-" x 50,"\n";
$name=$ARGV[0];
$len=length($name);
@rev =$name=~/\w{1}/g;
$rev= join "",reverse(@rev);
$sum=hex(58455443);#"CTEX";
 
print "Window-sum(name) starts......\n";
for($i=1;$i<5;$i++){
        $out=substr($rev,0,$i);
        $out=hex(str2hex($out));
        $sum += $out;
        }
for($i=1;$i<$len-3;$i++){
        $out=substr($rev,$i,4);
        $out=hex(str2hex($out));
        $sum += $out;
        }
$sum_str=&de_overflow_hex($sum);
print "Window-sum(name) is: $sum_str\n";
print "Window-sum(name) done.\n";
print "-" x 50,"\n";
print "Serial computing starts......\n";
$m_low_digits_str=join("",(split(//,$sum_str))[6,7,4,5]);
$m_high_digits_str=join("",(split(//,$sum_str))[2,3,0,1]);
$low_digits_str=join("",(split(//,$sum_str))[4,5,6,7]);
$high_digits_str=join("",(split(//,$sum_str))[0,1,2,3]);
print "Low_digits,high_digits are in memory: $m_low_digits_str $m_high_digits_str.\n";
print "Actually low and high values are $low_digits_str,$high_digits_str.\n";
$tail=hex("5854")^hex("0058");
print "The high digits are 5854 xor 0058 =  ",dec2hex($tail),"\n";
$head=((hex("26eb")+$tail))^hex("4554");
print "The low digits are (26eb+",dec2hex($tail),") xor 4554 = ",dec2hex($head),"\n";
print "Window-sum(name)+serial should be: ",join("",dec2hex($tail),dec2hex($head)),"\n";
$serial=hex(join("",dec2hex($tail),dec2hex($head)))-hex($sum_str);
$serial_hex_str=dec2hex($serial);
print "The serial should be: ",$serial," or in hex: ",$serial_hex_str,"\n";
print "Serial computing done......\n";
print "-" x 50,"\n";
 
 
if($serial < 0){
        print "Illegal name! Please try a new name with short length (<8 letters)and low ASCII value, i. e. 7654321;abcde;\n";
        }
else{
        print "Your serial is: $serial.\n";
        }
print "-" x 50,"\n";
 
sub de_overflow_hex(){
                $dec = shift;
                # print "$dec will be de_overflowed...\n";
                $dec %= 4294967296;#0x100000000;
                $d2h = sprintf("%0x",$dec);
                return $d2h;
        }
sub de_overflow_dec(){
                $dec = shift;
                # print "$dec will be de_overflowed...\n";
                $dec %= 4294967296;#0x100000000;
                return $dec;
        }
         
sub str2hex{
        my $s = shift;
        my $str;
        for (0..length($s)-1) {
                $str .= sprintf "%0x", ord substr($s, $_, 1);
        }
        return $str;
}
sub dec2hex(){
                $dec=shift;
                $hex=sprintf "%0x",$dec;
                return $hex;
                }




        需要特别留心的是,name不能是随即定出来的,需要满足一定的条件才可以。所以可能要多试试几个Name。

 

 



        PS: 这是楼主的第二篇帖子,这一篇耗费的精力更大。之所以起这样一个题目,是为了提现楼主破解这个程序时左右彷徨的心境。楼主完全依照思维推导的流程走,所以里面有些是走了弯路,但完全是合理的。这样写帖子的好处是可以完整细致的与感兴趣的爱好者交换想法。



热门文章
  • 机械革命S1 PRO-02 开机不显示 黑...
  • 联想ThinkPad NM-C641上电掉电点不...
  • 三星一体激光打印机SCX-4521F维修...
  • 通过串口命令查看EMMC擦写次数和判...
  • IIS 8 开启 GZIP压缩来减少网络请求...
  • 索尼kd-49x7500e背光一半暗且闪烁 ...
  • 楼宇对讲门禁读卡异常维修,读卡芯...
  • 新款海信电视机始终停留在开机界面...
  • 常见打印机清零步骤
  • 安装驱动时提示不包含数字签名的解...
  • 共享打印机需要密码的解决方法
  • 图解Windows 7系统快速共享打印机的...
  • 锦州广厦电脑上门维修

    报修电话:13840665804  QQ:174984393 (联系人:毛先生)   
    E-Mail:174984393@qq.com
    维修中心地址:锦州广厦电脑城
    ICP备案/许可证号:辽ICP备2023002984号-1
    上门服务区域: 辽宁锦州市区
    主要业务: 修电脑,电脑修理,电脑维护,上门维修电脑,黑屏蓝屏死机故障排除,无线上网设置,IT服务外包,局域网组建,ADSL共享上网,路由器设置,数据恢复,密码破解,光盘刻录制作等服务

    技术支持:微软等