吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 16487|回复: 46
收起左侧

[原创] 160个CrackMe练手之024

  [复制链接]
Double_Z 发表于 2015-5-28 20:17
本帖最后由 Double_Z 于 2015-5-28 21:28 编辑

     最近一直在练习160CrackMe,也积累了一些东西,一直没时间发,今天可能刚好有些时间就找一些可以发的东西发一下。

     这里有 44018723 发的相同的一篇帖子:可以参考一下 重复的内容我就不发了:http://www.52pojie.cn/thread-268511-1-1.html

     首先找到关键代码区,查找字符串,或者右键->查找->所有程序间的调用 找到编辑框相关断点下断 运行即可。
[Asm] 纯文本查看 复制代码
00401255   > \3B05 58314000 cmp eax, dword ptr [0x403158]
0040125B   .  74 0C         je short 00401269
0040125D   .  3B05 54314000 cmp eax, dword ptr [0x403154]
00401263   .  0F85 AE000000 jnz 00401317
00401269   >  C705 D9124000>mov dword ptr [0x4012D9], 0x584554
00401273   .  6A 00         push 0x0                                 ; /IsSigned = FALSE
00401275   .  8D45 FC       lea eax, dword ptr [ebp-0x4]             ; |
00401278   .  50            push eax                                 ; |pSuccess
00401279   .  6A 64         push 0x64                                ; |ControlID = 64 (100.)
0040127B   .  FF35 50314000 push dword ptr [0x403150]                ; |hWnd = NULL
00401281   .  E8 BC010000   call <jmp.&USER32.GetDlgItemInt>         ; \GetDlgItemInt
00401286   .  837D FC 00    cmp dword ptr [ebp-0x4], 0x0
0040128A      74 5F         je short 004012EB
0040128C   .  50            push eax                                 ;  在此处下F2断点
0040128D   .  6A 14         push 0x14                                ; /Count = 14 (20.)
0040128F   .  68 6C314000   push 0040316C                            ; |用户名
00401294   .  FF35 54314000 push dword ptr [0x403154]                ; |hWnd = NULL
0040129A   .  E8 AF010000   call <jmp.&USER32.GetWindowTextA>        ; \GetWindowTextA
0040129F   .  85C0          test eax, eax                            ;  eax = length  用户名长度
004012A1   .  74 48         je short 004012EB
004012A3   .  A1 0B304000   mov eax, dword ptr [0x40300B]            ;  1480938563 0x58455443
004012A8   .  BB 6C314000   mov ebx, 0040316C                        ;  用户名
004012AD   >  0303          add eax, dword ptr [ebx]                 ;  +*((int*)Name)
004012AF   .  43            inc ebx
004012B0   .  81FB 7C314000 cmp ebx, 0040317C                        ;  循环 16次
004012B6   .^ 75 F5         jnz short 004012AD
004012B8   .  5B            pop ebx
004012B9   .  03C3          add eax, ebx                             ;  eax = 用户名累加 ebx = serial 注册码
004012BB   .  3105 D9124000 xor dword ptr [0x4012D9], eax            ;  00584554 ^ eax 对下面的代码进行改写
004012C1   .  C1E8 10       shr eax, 0x10                            ;  右移 16位
004012C4   .  66:2905 D9124>sub word ptr [0x4012D9], ax              ;  0x4012d9 - ax
004012CB   .  BE EC114000   mov esi, 004011EC
004012D0   .  B9 3E000000   mov ecx, 0x3E
004012D5   .  33DB          xor ebx, ebx
004012D7   .  EB 04         jmp short 004012DD
004012D9      54            push esp                                 ;  改写位置
004012DA      45            inc ebp
004012DB      58            pop eax                                  ;  改写位置
004012DC      00AD 33D84975 add byte ptr [ebp+0x7549D833], ch        ;  改写位置
004012E2   ?  FA            cli
004012E3   .  81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9   .^ 74 EE         je short 004012D9
004012EB   >  68 59304000   push 00403059                            ; /Your serial is not valid.
004012F0   .  FF35 5C314000 push dword ptr [0x40315C]                ; |hWnd = NULL
004012F6   .  E8 7D010000   call <jmp.&USER32.SetWindowTextA>        ; \SetWindowTextA
004012FB   .  33C0          xor eax, eax
004012FD   .  C9            leave                                    ;  (Initial CPU selection)
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 [0x40315C]                ; |hWnd = NULL
0040130C   .  E8 67010000   call <jmp.&USER32.SetWindowTextA>        ; \SetWindowTextA
00401311   .  33C0          xor eax, eax
00401313   .  C9            leave
00401314   .  C2 1000       retn 0x10



这段代码就是将要分析的代码 ,很短,算法也很简单。
首先是 这段代码:

[Asm] 纯文本查看 复制代码
0040128C   .  50            push eax                                 ;  在此处下F2断点
0040128D   .  6A 14         push 0x14                                ; /Count = 14 (20.)
0040128F   .  68 6C314000   push 0040316C                            ; |用户名
00401294   .  FF35 54314000 push dword ptr [0x403154]                ; |hWnd = NULL
0040129A   .  E8 AF010000   call <jmp.&USER32.GetWindowTextA>        ; \GetWindowTextA
0040129F   .  85C0          test eax, eax                            ;  eax = length  用户名长度
004012A1   .  74 48         je short 004012EB
004012A3   .  A1 0B304000   mov eax, dword ptr [0x40300B]            ;  1480938563 0x58455443
004012A8   .  BB 6C314000   mov ebx, 0040316C                        ;  用户名
004012AD   >  0303          add eax, dword ptr [ebx]                 ;  +*((int*)Name)
004012AF   .  43            inc ebx
004012B0   .  81FB 7C314000 cmp ebx, 0040317C                        ;  循环 16次
004012B6   .^ 75 F5         jnz short 004012AD
004012B8   .  5B            pop ebx
004012B9   .  03C3          add eax, ebx                             ;  eax = 用户名累加 ebx = serial 注册码

分析一下,大体意思就是这样滴:
[C] 纯文本查看 复制代码
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

main()
{
char strUsername[20]="doubleZ"; // 用户名
int serial= 12345678;        // 注册码
int *pSerial=&serial;
char *pName = strUsername;
int EAX = 0x58455443; //dword ptr [0x40300B]
int i=0;
int nLen=strlen(strUsername);

for( i=nLen;i<20;i++) //字符串后续地址清零 
{
*(pName+i)=0;
}
for( i=0;i<16;i++)
{

EAX += *((int*)pName); // add eax, dword ptr [ebx]
pName++;
}

EAX +=serial; //add eax, ebx
printf("EAX:%X\n",EAX);

system("pause");

}


不好意思,我用 DEV C++ 写的,代码比较粗糙。可以用OD调试下程序,看看结果是否正确:运行程序,然后输入用户名,注册码(这个要和C代码中的一致)
然后,找到刚才的代码区,下断点,点击注册框即可断下。
[Asm] 纯文本查看 复制代码
0040128A     /74 5F         je short 004012EB
0040128C   . |50            push eax                                 ;  在此处下F2断点

F8 单步运行,也可直接F4 到此处:
[Asm] 纯文本查看 复制代码
004012B8   .  5B            pop ebx
004012B9   .  03C3          add eax, ebx                             ;  eax = 用户名累加 ebx = serial 注册码


查看EAX的值是否与自己写的代码得出的结果是否相同。

然后分析一下下面的代码

[Asm] 纯文本查看 复制代码
004012BB   .  3105 D9124000 xor dword ptr [0x4012D9], eax            ;  00584554 ^ eax 对下面的代码进行改写
004012C1   .  C1E8 10       shr eax, 0x10                            ;  右移 16位
004012C4   .  66:2905 D9124>sub word ptr [0x4012D9], ax              ;  0x4012d9 - ax
004012CB   .  BE EC114000   mov esi, 004011EC
004012D0   .  B9 3E000000   mov ecx, 0x3E
004012D5   .  33DB          xor ebx, ebx
004012D7   .  EB 04         jmp short 004012DD


第一句 004012BB . xor dword ptr [0x4012D9], eax ; 其中eax 为前面计算的那个数,0x4012d9 dword 是什么呢,多调试几次你就突然发现下面的代码会莫名的变红。对
其实就是下面的代码区(jmp 后的四个字节)。那为什么  到对这几个字节更改呢?看下面
第二 三句
[Asm] 纯文本查看 复制代码
004012C1   .  C1E8 10       shr eax, 0x10                            ;  右移 16位
004012C4   .  66:2905 D9124>sub word ptr [0x4012D9], ax              ;  0x4012d9 - ax


同样是对0x4012d9 位置的地址进行更改,且是根据用户名和注册码计算出的码进行更改,那猜一下,如果是你,你会改成什么?现在还不知道,待会再猜。

在后面的几句是为JMP后面的循环清零;

看看JMP后面是什么?
[Asm] 纯文本查看 复制代码
004012DD      AD            lods dword ptr [esi]                     ;  JMP跳到此处
004012DE      33D8          xor ebx, eax
004012E0   .  49            dec ecx
004012E1   .^ 75 FA         jnz short 004012DD
004012E3   .  81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9   .^ 74 EE         je short 004012D9


这段程序的意思就是从地址004011EC 到 004011EC+0x3E(注意:这个区间包括0x4012d9这个位置,这点很重要) 取值,然后依次异或,最后的计算结果和0xAFFCCFFB比较,相等则跳转。如果不跳转,下面就是显示注册失败的提示,那么这里就是所谓的关键跳了。那么如果相等的话,那会跳转到哪里呢?按照正常的思路,应该是是提示注册成功的那个位置。
也就是这里:
[Asm] 纯文本查看 复制代码
00401301   .  68 73 30 40 0>ascii "hs0@",0                           ;  YES! You found your serial!!
00401306   .  FF35 5C314000 push dword ptr [0x40315C]                ; |hWnd = NULL
0040130C   .  E8 67010000   call <jmp.&USER32.SetWindowTextA>        ; \SetWindowTextA
00401311   .  33C0          xor eax, eax
00401313   .  C9            leave
00401314   .  C2 1000       retn 0x10

但是,他没有跳到这里(爆破的话可以改跳到这里),而是跳到 0x4012d9这个位置,这个位置的代码是根据咱输入的用户名和注册码计算出来的,所以这里的代码也和咱的
注册码和用户名一样是错的。如何强制改下跳转,你会发现程序会死掉滴。

那么,假若,我们输入的是正确的用户名和注册码,那么0x4012d9 位置的代码是什么? 废话,当然是跳到提示注册成功的哪里喽!
我们将 0x4012d9 出的代码改一下,改成这样。若是这样的话,那么若通过注册码检验,则跳到这里,然后在跳到提示成功!
[Asm] 纯文本查看 复制代码
004012D9     /EB 26         jmp short 00401301                       ;  改写位置
004012DB     |90            nop                                      ;  改写位置
004012DC     |90            nop                                      ;  改写位置


这里有两个关键字节,即EB 26 (jmp short 00401301)。那如何验证我们的猜想呢?往下看。

我们先总结一下作者的思路:首先对用户名和注册码进行一系列的计算,得出Number1,
然后通Number1对0x4012d9 进行异或,加减一系列的操作,总之就是要改变dword [0x4012d9]
这个数。改变这个数的目的,是为了改变0x4012d9处的运行代码(我们猜测含有EB 26 这两个字节)。
最后,对地址004011EC 到 004011EC+0x3E的Dword读取的数进行异或运算,判断是否符合要求。
我们发现地址004011EC 到 004011EC+0x3E 只有两个数是会改变的,即0x4012d8 和 0x4012dc 中的 前者的后三个字节,后者的第一个字节,
这正是被改写的0x4012d9 的 四个字节。 所以最后的验证是验证0x4012d9 是否被改写正确。

如果我们得出0x4012d9的值,那么我们就所有的问题就解决了。那如何得出0x4012d9 处的代码后者说是值呢?

我们在来看一下最后的验证的代码:
[Asm] 纯文本查看 复制代码
004012DD      AD            lods dword ptr [esi]                     ;  JMP跳到此处
004012DE      33D8          xor ebx, eax
004012E0   .  49            dec ecx
004012E1   .^ 75 FA         jnz short 004012DD
004012E3   .  81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9   .^ 74 EE         je short 004012D9


lods dword ptr[esi] 可以这样理解:mov eax,dword ptr[esi]; add esi,4;
即不断取值,然后异或,最后和0xAFFCCFFB 比较相同即可;
即 可以转换为
A xor B xor C ocr D ...... xor E ?= 0xAFFCCFFB ,其中有有两个 F 和 G (F G的一部分)是未知的。那如何求解F,G;
这里有关异或的资料可以参考:http://www.cnblogs.com/suoloveyou/archive/2012/04/25/2470292.html
看完资料我们会发现 A xor B  = C ; 则  B = A xor C ;

好了,不废话了,说一下我的思路吧:  
我们先计算 0x004012d8 处的值 因为这个值有三个字节是未知的,
把 0x004012d8 之前的 A xor D xor C xor D .... 计算出的值看成一个数 M 后面的看成 N

则 M xor dword [0x004012d8] xor N = 0xAFFCCFFB ;
   dword [0x004012d8] = 0xAFFCCFFB xor M xor N ;

那我们如何计算 M 和 N 呢?

嗯,又是一个问题,怎么办呢?我们用现成的工具 ——OD;
再次运行,断下,单步到此处(或F4);
[Asm] 纯文本查看 复制代码
004012DD      AD            lods dword ptr [esi]                     ;  JMP跳到此处
004012DE      33D8          xor ebx, eax
004012E0   .  49            dec ecx
004012E1   .^ 75 FA         jnz short 004012DD

然后调试->设置条件->选择条件为真 填写 esi == 004012d8(注意 ==) 然后跟踪步入,你会发现esi为004012d8,
然后F8一步,此时把ebx的值记下 M =0xA213FC72.
然后F8 单步走到达 xor ebx,ebx 处 此时 eax 为 0x004012d8的值,此时把eax和ebx中的值改为零,当然你也可以只改eax中的值。这里将0x4012dc 中最后一个字节改为00 即D833AD00,
然后在F8,注意jne是否向上跳,不跳的时候把 ebx 记下 N = 0x59C9D849;

则 dword 0x4012d8 =  0xAFFCCFFB xor M xor N ;
计算看下面:
[C] 纯文本查看 复制代码
#include "stdio.h"
#include "stdlib.h"
main()
{ 
int b = 0xAFFCCFFB ^ 0x59C9D849 ^ 0xA213FC72;//b=0x5426EBC0
printf("%X",b);
system("pause");
} 

0x5426EBC0,嗯,果然,有 eb 26 这两个字节,但是因为 0x4012d8 第一个字节是知道的为04
故0x4012d8 = 0x5426EB04;
然后用同样的方法计算出0x4012dc的值,跟踪 到 esi == 0x4012d8,然后F8 等到 0x004012d8 的数值载入到eax 时将eax 改为0x5426EB04这个值
然后 f8,我们用另一个方法,再次f8 到载入 0x4012dc 的值到eax 后,将eax 改为 0,然后f8直到循环结束,记下ebx 的值 S =0x77CF623F
0x4012dc  =  0xAFFCCFFB xor S  =0xD833ADC4; 明显和原来的数只差一个字节;

0x4012d9 = C45426EB;;//得到这个数的方法很多,同学们可以总结下。

然后我们向上分析:
[Asm] 纯文本查看 复制代码
004012BB   .  3105 D9124000 xor dword ptr [0x4012D9], eax            ;  00584554 ^ eax 对下面的代码进行改写
004012C1   .  C1E8 10       shr eax, 0x10                            ;  右移 16位
004012C4   .  66:2905 D9124>sub word ptr [0x4012D9], ax              ;  0x4012d9 - ax


很简单:     0x00584554 xor C - C >> 0x10  =0xC41926eb ; 问题又来了,我们如何计算C?
我们转换一下:
                0x00584554 xor C = C41926eb + C>>0x10;
方法很笨,但很有效:

[C] 纯文本查看 复制代码
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

main()
{
unsigned int i=0x0;
unsigned int j=0x0;
unsigned int k=0x0;
for( i=0x1;i<=0xFFFFFFFE;i++)
{ 

j = 0x00584554^i;
k = 0xC45426EB + (i>>0x10);
if(j==k) printf("%X\t%X\t%X\n",i,j,k);

}
//0xC40CAFA3 Number1
//0xC45426EB 
system("pause");
} 


最终得到 C = 0xC40CAFA3;就是前面我们所说的用用户名和注册码得出的Number1;

最后用户名到注册码的算法前面分析过了,很简单,我把代码放下:

[C] 纯文本查看 复制代码
#include "stdio.h"
#include "string.h"
#include "stdlib.h"

main()
{
  char strUsername[20]="doubleZ";
  int  serial= 0;
  int *pSerial=&serial;
  char *pName = strUsername;
  int i=0;
  int nLen=strlen(strUsername);
  for( i=nLen;i<20;i++)
  {
       *(pName+i)=0;
  }
  serial =0x58455443;
  for( i=0;i<16;i++)
  {
       serial += *((int*)pName);
       pName++;
  }
  
  serial = 0xC40CAFA3-serial;  
  printf("Name:%s\n",strUsername);
  printf("Serial:%u\n",serial);
  
  //Name:doubleZ
  //Serial:3703760779
 system("pause");
     
} 


最后得出一组用户名和注册码Name:doubleZ ,Serial:3703760779。但是这个注册码只能在调试时通过,正常情况下不能显示成功,这个好像程序设计时没处理好,
嗯,就这样吧,反正我们的最终的目的达到了——了解CrackMe作者的思路。

这是我第一次发帖,水平有限,莫怪。好吧,我把文件放下赚点外快。

CrackMe_024.rar (31.35 KB, 下载次数: 34)

























免费评分

参与人数 12热心值 +12 收起 理由
我要学 + 1 谢谢@Thanks!
moonking8008 + 1 我很赞同!
Martin + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
hitpiao + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
lonely_coder + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
Tortoise + 1 赞!!支持楼主!
Pnmker + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
yzh2004 + 1 谢谢@Thanks!
hy0210 + 1 谢谢@Thanks!
吾爱T阿杰 + 1 支持楼主 继续下去
wnfight + 1 谢谢@Thanks!
bigharvest + 1 已答复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Pnmker 发表于 2015-6-19 11:34
Double_Z 发表于 2015-6-19 09:59
嗯,我知道了,这点我没想到。明天有空我把帖子在改一下,省的误导大家。我现在在上班,没有工具。用硬件 ...

不用调试,直接把11EC开始地址开始的 0x3E*4个字节的数据dump出来,分成四部分进行异或就可以了:
xxxxxxxx ^ xxxxxx04 ^ D833ADxx ^ 81FA7549 = AFFCCFFB
最前面的xxxxxxxx可以根据dump出来的数据异或出来, 中间的两个用你的方法就可以计算出来
 楼主| Double_Z 发表于 2015-6-19 09:59
Pnmker 发表于 2015-6-19 00:40
程序没有问题,非调试状态下以下两组用户名验证通过
pnmker 3155544019
doubleZ 1891791755

嗯,我知道了,这点我没想到。明天有空我把帖子在改一下,省的误导大家。我现在在上班,没有工具。用硬件断点调试可以吗?
微笑最美 发表于 2015-5-28 21:12
Hmily 发表于 2015-6-3 12:01
Double_Z分析的很详细了,加精鼓励,期待更多分享,@44018723 带大家一起走入crackme系列,应该搞成一个教程合集,收录到板块推荐贴里。
boyving 发表于 2015-6-3 13:56
好东西,谢谢了哦。
24K灬纯帅 发表于 2015-6-3 14:11 来自手机
这个很详细   我有空也去看看吧
qq734928657 发表于 2015-6-3 15:48
支持学习         
stdcall 发表于 2015-6-3 16:20
收下 有时间多学学
吾爱T阿杰 发表于 2015-6-3 20:57
我很纳闷,原创的精品评分这么少,什么音乐电影啥的评分反而那么多,,,郁闷
Ctrui 发表于 2015-6-4 03:08
收藏了,感谢分享!
wuaixuexi 发表于 2015-6-4 08:00
好文章 学习了 谢谢楼主 思路很好 继续关注你
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 04:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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