solly 发表于 2019-6-30 03:22

160 个 CrackMe 之 091 - Cruehead ,避过陷阱及防护,暴力算出密码

本帖最后由 solly 于 2019-7-1 12:40 编辑

160 个 CrackMe 之 091 - Cruehead 在本坛中已有人研究过,不过其陷入 CrackMe 设置的陷阱,没能发现其真正的密码。
这个 CrackMe 也包含了多种反调试,包括 API 代-理,防 bpx 中断,数据加密,防静态分析,代码动态破坏/恢复,设置陷阱(真假两个密码验证,文件内容验证也没有用)。


同时, CrackMe 还要求在其目录中有一个"crueme.dat"的文件,并且其长度、内容无限制,但在假的密码验证内有进行计算等操作。
启动后,如果没有这个“crueme.dat"文件,会报个提示,并会退出,如下图:

我们用文本编辑工具生成一个这样的文件并保存到CrackMe的目录内即可。


再次启动,就可以正常启动了,界面如下:



我们重新用 OD 载入,来到OEP,如下图:

可以看到,其第一条指令就是修改内存的值,其实这个值 0x00401493,一看就是一个地址,实际上,这个地址就是一个 API 代-理函数的入口地址,这个 代-理函数封装了42个API函数,通过 al = ”索引号“ 的方式来调用这些API函数,因此,静态分析就基本没有可能了。


下面就是 CrackMe 开头的代码:
00401000 c> $C705 64234000 93144000   mov   dword ptr , 00401493    ;修正 api 代-理 call的函数入口
0040100A    .68 D9204000            push    004020D9                        ; /pLocaltime = crueme.004020D9
0040100F    .E8 F20C0000            call    <jmp.&KERNEL32.GetLocalTime>    ; \GetLocalTime
00401014    .8D1C9D 00000000          lea   ebx, dword ptr
0040101B    .D12D 80234000            shr   dword ptr , 1         ;== 0x00401224
00401021    .66:A1 E7204000         mov   ax, word ptr          ;AX == 0x01AC
00401027    .A2 63234000            mov   byte ptr , al         ;==0xAC,解码密钥
0040102C    .E8 4A040000            call    0040147B                        ;解码 API 代-理函数的索引表
00401031    .83C0 05                  add   eax, 5                        ;eax = 0x0201
00401034    .8D0485 00000000          lea   eax, dword ptr
0040103B    .83C0 05                  add   eax, 5                        ;eax = 0x0809
0040103E    .E8 27040000            call    0040146A                        ;解码字符串:"Win95 File Monitor"
00401043    .6A 00                  push    0
00401045    .68 80000000            push    80
0040104A    .6A 03                  push    3
0040104C    .6A 00                  push    0
0040104E    .6A 00                  push    0
00401050    .68 000000C0            push    C0000000
00401055    .68 76214000            push    00402176                        ;文件名:CRUEME.DAT
0040105A    .B0 1F                  mov   al, 1F                        ;函数 CreateFileA() 的索引
0040105C    .FF15 64234000            call    dword ptr             ;第1条指令修改了这里的地址,改为 0x00401493,这个调用是一个API入口代-理,AL为索引参数
00401062    .83F8 FF                  cmp   eax, -1                         ;eax != -1,表示读取文件成功
00401065    .75 24                  jnz   short 0040108B
00401067    .6A 30                  push    30
00401069    .68 A0214000            push    004021A0                        ;ASCII "This is the end - My only friend the end!"
0040106E    .68 CA214000            push    004021CA                        ;ASCII "AAARGH! Where is my CRUEME.DAT file???",CR,"    I cant go on without my beloved file!"
00401073    .6A 00                  push    0
00401075    .B0 1A                  mov   al, 1A                        ;MessageBoxA()
00401077    .FF15 64234000            call    dword ptr             ;crueme.00401493
0040107D    .FF35 54204000            push    dword ptr
00401083    .B0 0A                  mov   al, 0A                        ;ExitProcess()
00401085    .FF15 64234000            call    dword ptr             ;crueme.00401493
0040108B    >A3 81214000            mov   dword ptr , eax         ;保存文件句柄
可以看到    call    dword ptr       就是这个 API 代-理调用,al 为参数,如 al = 0x1F,表示调用 CreateFileA() ,al=0x1A,表示调用 MessageBoxA(),等等,并且该函数内部通过对 al 参数进行一定的计算,而且参与计算的数据中有一个数据是通过查表取得,结合在一起才能确定 API 的入口跳转位置。这个数据表是加密的,通过上面代码中的 call    0040147B 进行解码,解码后才可以用。同时上面代码中的 call 0040146A 解码了一个字符串: "Win95 File Monitor",后面会用 FindWindow()查找这个标题的窗口,防止文件访问监控。


读取文件内容后,会与一组常量进行运算,不过后面真正计算密码时也没有用到这些数据。

F8 往下走,来到这里,如下图:

中间插入了生成对话框应用的代码,就是由这段代码来显示主对话框界面的。而附近有很多其它代码,其实都是没用的废代码,包括生成标准的 Windows 窗口代码,消息循环代码,WndProc代码,都是没用的。
具体代码如下:
00401138    .6A 00                  push    0
0040113A    .68 C1114000            push    004011C1                        ;DlgProc
0040113F    .6A 00                  push    0
00401141    .68 E9204000            push    004020E9                        ;字符串 (ASCII "DLG_START")
00401146    .FF35 D6234000            push    dword ptr             ;hInstance
0040114C    .B0 12                  mov   al, 12                        ;DialogBoxParamA()
0040114E    .FF15 64234000            call    dword ptr             ;crueme.00401493
00401154    .EB 64                  jmp   short 004011BA

如下图,上面代码前面的 LoadIconA(), LoadCursorA() 等,就没什么用。



通过前面的代码,我们知道 DlgProc 的地址是 0x004011C1,其主体部分如下图所示:

只处理了 WM_COMMAND 和 WM_CLOSE 消息。具体代码如下:
004011C1   /.C8 000000                enter   0, 0                            ;DlgProc
004011C5   |.53                     push    ebx
004011C6   |.56                     push    esi
004011C7   |.57                     push    edi
004011C8   |.817D 0C 11010000         cmp   dword ptr , 111          ;WM_COMMAND
004011CF   |.0F84 2F010000            je      00401304
004011D5   |.837D 0C 10               cmp   dword ptr , 10         ;WM_CLOSE
004011D9   |.0F84 48010000            je      00401327
004011DF   |.B8 00000000            mov   eax, 0
004011E4   |>5F                     pop   edi
004011E5   |.5E                     pop   esi
004011E6   |.5B                     pop   ebx
004011E7   |.C9                     leave
004011E8   |.C2 1000                  retn    10
通过跟随    je      00401304 可以找到处理 WM_COMMAND 消息的 Handler。如下图所示:

然后跟随 je 00401213 可以找到按钮”Check It!“的Click事件的 Handler。如下图所示:

具体的代码如下所示:
00401213   |>BA 1D214000            mov   edx, 0040211D                   ;保存密码的缓冲区地址
00401218   |.8D1C85 00000000          lea   ebx, dword ptr           ;(无用代码)Win9x下eax=ecx=0, WinNT下eax=ecx=DlgProc
0040121F   |.83C3 05                  add   ebx, 5                        ;(无用代码)改成 mov ebx, 5
00401222   |.03DA                     add   ebx, edx                        ;(无用代码)
00401224   |.52                     push    edx                           ;lParam, 缓冲区地址
00401225   |.6A 0E                  push    0E                              ;wParam, 可取得的最大字符数,14
00401227   |.6A 0D                  push    0D                              ;uMsg = WM_GETTEXT
00401229   |.68 EA030000            push    3EA                           ;ControlID. 密码文本框
0040122E   |.FF75 08                  push    dword ptr                ;hInstance, 堆栈 ss:=006F0C10
00401231   |.B0 25                  mov   al, 25                        ;SendDlgItemMessageA(WM_GETTEXT)
00401233   |.FF15 64234000            call    dword ptr             ;取得注册密码"1234567890", eax为长度
00401239   |.E8 35090000            call    00401B73                        ;密码验证,正确返回0,错误返回1
0040123E   |.50                     push    eax                           ;保存验证结果
0040123F   |.BF 01000000            mov   edi, 1
00401244   |.E8 FD080000            call    00401B46                        ;edi = 1,将0x00401224处的代码替成错误代码
00401249   |.B9 08000000            mov   ecx, 8
0040124E   |.BF 85214000            mov   edi, 00402185                   ;文件内容 (ASCII "787878787878")
00401253   |.BE 34214000            mov   esi, 00402134                   ;esi ==> 常量 FF 77 88 66 99 55 AA 44 BB 33 CC 22 DD 11 EE C0
00401258   |>8A07                     /mov   al, byte ptr
0040125A   |.8A1E                     |mov   bl, byte ptr
0040125C   |.32C3                     |xor   al, bl
0040125E   |.D0C0                     |rol   al, 1
00401260   |.8806                     |mov   byte ptr , al
00401262   |.47                     |inc   edi
00401263   |.46                     |inc   esi
00401264   |.^ E2 F2                  \loopd   short 00401258
00401266   |.58                     pop   eax                           ;密码验证结果
00401267   |.85C0                     test    eax, eax
00401269   |.0F84 84000000            je      004012F3                        ;密码正确则跳转
这一段代码就是真正的密码验证过程,其中算法在 call    00401B73 中,如下图所示:

继续:


其算法代码如下:
00401B73   /$33FF                     xor   edi, edi
00401B75   |.33F6                     xor   esi, esi
00401B77   |.85C0                     test    eax, eax                        ;长度
00401B79   |.0F84 0F010000            je      00401C8E
00401B7F   |.A3 72214000            mov   dword ptr , eax         ;注册密码的长度 len
00401B84   |.BB 1D214000            mov   ebx, 0040211D                   ;ebx ===> (ASCII "1234567890")
00401B89   |>B9 FF000000            /mov   ecx, 0FF                     ;循环次数, count=255
00401B8E   |>8B03                     |/mov   eax, dword ptr           ;第1~4字符
00401B90   |.05 87D61200            ||add   eax, 12D687
00401B95   |.0343 08                  ||add   eax, dword ptr       ;第9~12字符
00401B98   |.2D 672B0000            ||sub   eax, 2B67
00401B9D   |.33D2                     ||xor   edx, edx                      ;清空积高32位
00401B9F   |.F725 72214000            ||mul   dword ptr             ;乘以长度值,取积低32位
00401BA5   |.8B53 04                  ||mov   edx, dword ptr       ;第5~8字符
00401BA8   |.81C2 EAF48F04            ||add   edx, 48FF4EA
00401BAE   |.33C2                     ||xor   eax, edx
00401BB0   |.8B53 08                  ||mov   edx, dword ptr       ;第9~12字符
00401BB3   |.81EA 015CBC00            ||sub   edx, 0BC5C01
00401BB9   |.2B13                     ||sub   edx, dword ptr           ;第1~4字符
00401BBB   |.81C2 672B0000            ||add   edx, 2B67
00401BC1   |.33C2                     ||xor   eax, edx
00401BC3   |.0D E2599302            ||or      eax, 29359E2
00401BC8   |.03F8                     ||add   edi, eax
00401BCA   |.233D 62214000            ||and   edi, dword ptr        ;== 0x15263748,静态常量
00401BD0   |.8B03                     ||mov   eax, dword ptr           ;第1~4字符
00401BD2   |.2D 87D61200            ||sub   eax, 12D687
00401BD7   |.2B43 08                  ||sub   eax, dword ptr       ;第9~12字符
00401BDA   |.05 CE560000            ||add   eax, 56CE
00401BDF   |.33D2                     ||xor   edx, edx                      ;清除被除数高32位
00401BE1   |.F735 72214000            ||div   dword ptr             ;除以长度值
00401BE7   |.8B53 04                  ||mov   edx, dword ptr       ;第5~8字符
00401BEA   |.81EA EAF48F04            ||sub   edx, 48FF4EA
00401BF0   |.33C2                     ||xor   eax, edx
00401BF2   |.8B53 08                  ||mov   edx, dword ptr       ;第9~12字符
00401BF5   |.81C2 015CBC00            ||add   edx, 0BC5C01
00401BFB   |.0313                     ||add   edx, dword ptr           ;第1~4字符
00401BFD   |.81EA CE560000            ||sub   edx, 56CE
00401C03   |.33C2                     ||xor   eax, edx
00401C05   |.25 E2599302            ||and   eax, 29359E2
00401C0A   |.03F0                     ||add   esi, eax
00401C0C   |.0B35 66214000            ||or      esi, dword ptr        ; = 0x596A7B8C,静态常量
00401C12   |.E2 02                  ||loopd   short 00401C16
00401C14   |.EB 05                  ||jmp   short 00401C1B
00401C16   |>^ E9 73FFFFFF            |\jmp   00401B8E
00401C1B   |>81C7 11090000            |add   edi, 911
00401C21   |.81EE 11090000            |sub   esi, 911
00401C27   |.FE0D 33214000            |dec   byte ptr             ;0xFF,静态初始化的变量
00401C2D   |.803D 33214000 00         |cmp   byte ptr , 0
00401C34   |.^ 0F85 4FFFFFFF            \jnz   00401B89
00401C3A   |.C605 33214000 FF         mov   byte ptr , 0FF          ;恢复
00401C41   |.BB 1D214000            mov   ebx, 0040211D                   ;ebx ===> (ASCII "1234567890")
00401C46   |.B9 0E000000            mov   ecx, 0E                         ;长度,14
00401C4B   |>C603 00                  /mov   byte ptr , 0            ;循环清除密码数据
00401C4E   |.43                     |inc   ebx
00401C4F   |.^ E2 FA                  \loopd   short 00401C4B
00401C51   |.81C7 AFE3FDEF            add   edi, EFFDE3AF
00401C57   |.75 35                  jnz   short 00401C8E                  ;等于0
00401C59   |.81C6 238D94A4            add   esi, A4948D23
00401C5F   |.75 2D                  jnz   short 00401C8E                  ;等于0
00401C61   |.BE 2C224000            mov   esi, 0040222C                   ;ASCII "for input"
00401C66   |.83C6 0A                  add   esi, 0A                         ;esi ===> "Correct password - Good work!"
00401C69   |.BF 6F224000            mov   edi, 0040226F                   ;ASCII "Correct password - Good work!"
00401C6E   |.B9 1D000000            mov   ecx, 1D
00401C73   |.F3:A4                  rep   movs byte ptr es:, byte pt>;复制 "Correct password - Good work!"
00401C75   |.68 6F224000            push    0040226F                        ;addr ===> "Correct password - Good work!"
00401C7A   |.68 F2030000            push    3F2
00401C7F   |.FF75 08                  push    dword ptr                ;hInstance
00401C82   |.B0 15                  mov   al, 15                        ;SetDlgItemTextA()
00401C84   |.FF15 64234000            call    dword ptr             ;crueme.00401493
00401C8A   |.33C0                     xor   eax, eax
00401C8C   |.EB 05                  jmp   short 00401C93
00401C8E   |>B8 01000000            mov   eax, 1
00401C93   \>C3                     retn
可见,在上面函数包括了验证算法,并且验证通过,则会修改界面静态文本标签(控件ID为 0x03F2)的内容为"Correct password - Good work!",不会象另一个假的验证过程,会在一个无效的控件ID(0x03F3)上显示,正确标签控件上还是会显示错误提示。
这个算法并不复杂,但对数据计算是破坏性的,并且循环计算256x256次,好象无法逆推,只好暴破。暴破条件就是最后 edi 和 esi 都等于0时,则输入的密码有效。如下图所示位置,就是所需的条件:

先回到界面,输入假码:

进行验证处理,可了解其算法,并写出暴力计算密码代码。通过35分钟计算,得到一组4字符的密码:
”*A*
其中引号是半角的。这里说明一下,虽然算法中用到了 12 个字符密码,但并没有要求一定要 12 个字符,只要长度大于 0 即可,其它位置可以置 '\0'代替,所以暴力计算密码时可以从1 ~ 12 逐个增加密码长度,控制一定的计算量,不然,一开始就测试 12 位的密码,那只有请 "神威·太湖之光" 出马了。
输入这个密码,如下图:

再次按“Check it!”,可得到正确的提示“Correct password - Good work!”。


下面看看其陷阱,call 00401B73 中如果验证错误,则没有显示提示直接返回,来到下面代码处,如下图:

先对API 函数 GetDlgItemTextA 进行检查,看看有没有 BPX GetDlgItemTextA 进行中断跟踪。有则提示并退出。没有则来到下面的代码:

可以看到 call 004013B1 又对密码进行了一番操作,但是返回后,并没有正确的进行提示。如下代码:
004012BE   |>FFD7                     call    edi                           ;call GetDlgItemTextA(),读取密码
004012C0   |.E8 EC000000            call    004013B1                        ;对密码进行操作
004012C5   |.85C0                     test    eax, eax
004012C7   |.74 15                  je      short 004012DE
004012C9   |.68 36224000            push    00402236                        ;addr ===> (ASCII "Correct password - Good work!")
004012CE   |.68 F3030000            push    3F3                           ;ControlID,这个控件ID是错误的
004012D3   |.FF75 08                  push    dword ptr
004012D6   |.B0 15                  mov   al, 15                        ;SetDlgItemTextA()
004012D8   |.FF15 64234000            call    dword ptr             ;crueme.00401493,提示完成但并没有跳转,而是又直接显示失败的提示了
004012DE   |>68 54224000            push    00402254                        ;addr ===>(ASCII "False password - Try again")
004012E3   |.68 F2030000            push    3F2                           ;提示框 ControlID
004012E8   |.FF75 08                  push    dword ptr                ;hInstance
004012EB   |.B0 15                  mov   al, 15                        ;SetDlgItemTextA()
004012ED   |.FF15 64234000            call    dword ptr             ;crueme.00401493
004012F3   |>33FF                     xor   edi, edi                        ;edi = 0 恢复代码
004012F5   |.E8 4C080000            call    00401B46                        ;恢复代码
004012FA   |.B8 01000000            mov   eax, 1
004012FF   |.^ E9 E0FEFFFF            jmp   004011E4
不管call    004013B1 返回什么值,最终我们能看到的提示都是“False password - Try again”。所以这里就是个陷阱,并不是真正的密码验证的地方。

另一个需要说明一下的是,在真正的算法函数的前面,CrackMe 是通过 SendDlgItemMessageA(WM_GETTEXT)来取得的密码,并不是常用的 GetDlgItemTextA()函数。并且通过API代-理封装,不注意就很难断下其真正取密码和验证的位置。因为后面假验函数是用的 GetDlgItemTextA()取的密码,并且没有进行API代-理封装,是明文调用的,更具迷惑性,很容易上当掉入其陷阱。

下面交出暴破代码,使用 Dev-C++调试通过(没有逆推,如果谁有能力,看看是否可以逆推或优化,我这个在 i5 4670K 上计算了2047秒):
#include <iostream>
#include <string.h>

int checkPassword(char * password);

int getPassword(int len);
int genPassword(int index, int len, char * testPassword);

int main(int argc, char** argv) {
      
      //char password[] = "1234567890";
      //char password[] = "\"*A*";
      //checkPassword(password);
      
      /// 暴破密码
      int len = 4;
      int b = getPassword(len);
      
      return 0;
}

int checkPassword(char * password) {
      union {
                char pwd;
                unsigned long key;
      } passwd;
      
      /// 初始化缓冲区
      passwd.key = 0;
      passwd.key = 0;
      passwd.key = 0;
      passwd.key = 0;
      
      long n = strlen(password);
      if(n<=0) {
                return -1;
      }
      
      /// 长度处理,最长12个字符
      if (n>12) {
                n = 12;
                password = '\0'; /// 截断
      }
      
      //// 存入计算缓冲区
      strcpy(passwd.pwd, password);
      
      long a, d;
      
      long check1 = 0;/// edi
      long check2 = 0;/// esi
      
      for(long i=255; i>0; i--) {
                for(long j=255; j>0; j--) {
                        a = passwd.key;
                        a += 0x0012D687;
                        a += passwd.key;
                        a -= 0x00002B67;
                        a *= n;
                        
                        d = passwd.key;
                        d += 0x048FF4EA;
                        a ^= d;// xor
                        
                        d = passwd.key;
                        d -= 0x00BC5C01;
                        d -= passwd.key;
                        d += 0x00002B67;
                        a ^= d;
                        a |= 0x029359E2;
                        
                        check1 += a;
                        check1 &= 0x15263748;
                        
                        a = passwd.key;
                        a -= 0x0012D687;
                        a -= passwd.key;
                        a += 0x000056CE;
                        //a &= 0xFFFFFFFF;
                        a /= n;
                        
                        d = passwd.key;
                        d -= 0x048FF4EA;
                        a ^= d;
                        
                        d = passwd.key;
                        d += 0x00BC5C01;
                        d += passwd.key;
                        d -= 0x000056CE;
                        a ^= d;
                        a &= 0x029359E2;
                        
                        check2 += a;
                        check2 |= 0x596A7B8C;
                }
                check1 += 0x00000911;
                check2 -= 0x00000911;
      }
      //printf("check: 0x%08X - 0x%08X\n", check1, check2);

      check1 += 0xEFFDE3AF;
      check2 += 0xA4948D23;

      //// check1 = 0x05201300,check2 = 0x000EFFEE
      //printf("check: 0x%08X - 0x%08X\n", check1, check2);
      
      if(check1 | check2) {
                return 1; //// ERROR
      }
      
      return 0; ///OK
}

int getPassword(int len) {
      char password;
      for(int i=0; i<16;i++) {
                password = '\0';
      }
      
      int b = genPassword(0, len, password);
      if(b) {
                printf("Password not found.\n");
      } else {
                printf("Found Password: %s\n", password);
      }

      
      return b;
}

int genPassword(int index, int len, char * testPassword) {
      if(index == len) {
                return checkPassword(testPassword);
      }
      
      for(int i=0x20; i<0x7F; i++) { // printable characters,有
      //for(int i=0x30; i<=0x39; i++) { // '0'~'9',无
      //for(int i=0x41; i<=0x5A; i++) { // 'A'~'Z',无
      //for(int i=0x61; i<=0x7A; i++) {// 'a'~'z',无
                testPassword = (char)i;
                int b = genPassword(index+1, len, testPassword);
                if(b==0) {
                        return 0;
                }
      }
      
      return 1;
}


计算结果:
Found Password: "*A*

--------------------------------
Process exited after 2047 seconds with return value 0




分析完毕!!!

更新一下,算码程序用 VC6 编译,只要120秒左右即可算出4位密码。

2019-07-01 整理调整了一下算法,Dev-C++下 465 秒,VC6下70秒,都是 Release 编译,VC 还是牛。
#include <iostream>
#include <string.h>

int checkPassword(char * password);

int getPassword(int len);
int genPassword(int index, int len, char * testPassword);

int main(int argc, char** argv) {
         
      //char password[] = "1234567890";
      //char password[] = "\"*A*";
      //int a = checkPassword(password);
      //if(a) {
      //      printf("test failure!\n");
      //} else {
      //      printf("test seccuss!\n");
      //}
         
      /// 暴破密码
      int len = 4;
      int b = getPassword(len);
         
      return 0;
}

int checkPassword(char * password) {
      union {
                char pwd;
                unsigned long key;
      } passwd;
         
      /// 初始化缓冲区
      passwd.key = 0;
      passwd.key = 0;
      passwd.key = 0;
      passwd.key = 0;
         
      long n = strlen(password);
      if(n<=0) {
                return -1;
      }
         
      /// 长度处理,最长12个字符
      if (n>12) {
                n = 12;
                password = '\0'; /// 截断
      }
         
      //// 存入计算缓冲区
      strcpy(passwd.pwd, password);
         
      //long a, d;
         
      long check1 = 0;/// edi
      long check2 = 0;/// esi

      long A1, D1;      
      long A2, D2;
////=================================================================
      A1 = (passwd.key + passwd.key + 0x0012AB20) * n;
      D1 = passwd.key + 0x048FF4EA;
      A1 ^= D1;// xor      
////-----------------------------------------------------------------
      D1 = passwd.key - passwd.key - 0x00BC309A;
      A1 ^= D1;
      A1 |= 0x029359E2;
      
////=================================================================
      A2 = (passwd.key - passwd.key - 0x00127FB9) / n;
      D2 = passwd.key - 0x048FF4EA;
      A2 ^= D2;// Xor
///------------------------------------------------------------------
      D2 = passwd.key + passwd.key + 0x00BC0533;
      A2 ^= D2;
      A2 &= 0x029359E2;

///==================================================================
      for(long i=255; i>0; i--) {
                for(long j=255; j>0; j--) {
                        
                        check1 += A1; //a;
                        check1 &= 0x15263748;
                        
                        check2 += A2; //a;
                        check2 |= 0x596A7B8C;
                }
                check1 += 0x00000911;
                check2 -= 0x00000911;
      }
      
      //printf("check: 0x%08X - 0x%08X\n", check1, check2);

      check1 += 0xEFFDE3AF;
      check2 += 0xA4948D23;

      //// check1 = 0x05201300,check2 = 0x000EFFEE
      //printf("check: 0x%08X - 0x%08X\n", check1, check2);
         
      if(check1 | check2) {
                return 1; //// ERROR
      }
         
      return 0; //// OK
}

int getPassword(int len) {
      char password;
      for(int i=0; i<16;i++) {
                password = '\0';
      }
         
      int b = genPassword(0, len, password);
      if(b) {
                printf("Password not found.\n");
      } else {
                printf("Found Password: %s\n", password);
      }

      return b;
}

int genPassword(int index, int len, char * testPassword) {
      if(index == len) {
                return checkPassword(testPassword);
      }
         
      for(int i=0x20; i<0x7F; i++) { // printable characters,有
      //for(int i=0x30; i<=0x39; i++) { // '0'~'9',无
      //for(int i=0x41; i<=0x5A; i++) { // 'A'~'Z',无
      //for(int i=0x61; i<=0x7A; i++) {// 'a'~'z',无
                testPassword = (char)i;
                int b = genPassword(index+1, len, testPassword);
                if(b==0) {
                        return 0;
                }
      }
         
      return 1;
}


bohansang 发表于 2019-7-26 20:28

膜拜大神呀

solly 发表于 2019-7-30 16:30

本帖最后由 solly 于 2019-7-30 16:32 编辑

darksmile 发表于 2019-7-30 12:19
楼主好厉害!如果是8位密码,暴力破解需要算几天?!
8位密码,用可见字符尝试,次数最多为94的9次方减1,等于 572994802228616704 - 1 次。每一次尝试,光循环的计算有65000多次,加上前后的计算则更多。几天只怕解决不了滴。。。

Elisaa 发表于 2019-6-30 04:44

给力!膜拜大牛

whg118 发表于 2019-6-30 12:10

好复杂,看的都晕。

wmwamy 发表于 2019-6-30 18:32

技术贴,完全看不懂,有空再对照看看

灬小霸道灬 发表于 2019-6-30 22:15

这个教程确实很溜

学士天下 发表于 2019-7-1 08:50

坚持学习,谢谢!!!

alstar 发表于 2019-7-1 11:30

给大佬,磕头,牛
一个软件被植入了,注册码
需要付费,给作者钱,所以求大神看看
https://www.lanzouj.com/i4sgw1g

む人生似梦 发表于 2019-7-27 07:38

吾爱破解论坛最帅的人是谁?

Yuliangmin1991 发表于 2019-7-27 15:53

大佬们   小弟在此膜拜你们了;。、
页: [1] 2 3
查看完整版本: 160 个 CrackMe 之 091 - Cruehead ,避过陷阱及防护,暴力算出密码