好友
阅读权限 35
听众
最后登录 1970-1-1
solly
发表于 2019-6-30 03:22
本帖最后由 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 开头的代码:
[Asm] 纯文本查看 复制代码
00401000 c> $ C705 64234000 93144000 mov dword ptr [402364], 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 [ebx*4]
0040101B . D12D 80234000 shr dword ptr [402380], 1 ; [addr]== 0x00401224
00401021 . 66:A1 E7204000 mov ax, word ptr [4020E7] ; AX == 0x01AC
00401027 . A2 63234000 mov byte ptr [402363], al ; [addr]==0xAC,解码密钥
0040102C . E8 4A040000 call 0040147B ; 解码 API 代-理函数的索引表
00401031 . 83C0 05 add eax, 5 ; eax = 0x0201
00401034 . 8D0485 00000000 lea eax, dword ptr [eax*4]
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 [402364] ; 第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 [402364] ; crueme.00401493
0040107D . FF35 54204000 push dword ptr [402054]
00401083 . B0 0A mov al, 0A ; ExitProcess()
00401085 . FF15 64234000 call dword ptr [402364] ; crueme.00401493
0040108B > A3 81214000 mov dword ptr [402181], eax ; 保存文件句柄
可以看到 call dword ptr [402364] 就是这个 API 代-理调用,al 为参数,如 al = 0x1F,表示调用 CreateFileA() ,al=0x1A,表示调用 MessageBoxA(),等等,并且该函数内部通过对 al 参数进行一定的计算,而且参与计算的数据中有一个数据是通过查表取得,结合在一起才能确定 API 的入口跳转位置。这个数据表是加密的,通过上面代码中的 call 0040147B 进行解码,解码后才可以用。同时上面代码中的 call 0040146A 解码了一个字符串: "Win95 File Monitor",后面会用 FindWindow()查找这个标题的窗口,防止文件访问监控。
读 取文件内容后,会与一组常量进行运算,不过后面真正计算密码时也没有用到这些数据。
F8 往下走,来到这里,如下图:
中间插入了生成对话框应用的代码,就是由这段代码来显示主对话框界面的。而附近有很多其它代码,其实都是没用的废代码,包括生成标准的 Windows 窗口代码,消息循环代码,WndProc代码,都是没用的。
具体代码如下:
[Asm] 纯文本查看 复制代码
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 [4023D6] ; hInstance
0040114C . B0 12 mov al, 12 ; DialogBoxParamA()
0040114E . FF15 64234000 call dword ptr [402364] ; crueme.00401493
00401154 . EB 64 jmp short 004011BA
如下图,上面代码前面的 LoadIconA(), LoadCursorA() 等,就没什么用。
通过前面的代码,我们知道 DlgProc 的地址是 0x004011C1,其主体部分如下图所示:
只处理了 WM_COMMAND 和 WM_CLOSE 消息。具体代码如下:
[Asm] 纯文本查看 复制代码
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 [ebp+C], 111 ; WM_COMMAND
004011CF |. 0F84 2F010000 je 00401304
004011D5 |. 837D 0C 10 cmp dword ptr [ebp+C], 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。如下图所示:
具体的代码如下所示:
[Asm] 纯文本查看 复制代码
00401213 |> BA 1D214000 mov edx, 0040211D ; 保存密码的缓冲区地址
00401218 |. 8D1C85 00000000 lea ebx, dword ptr [eax*4] ; (无用代码)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 [ebp+8] ; hInstance, 堆栈 ss:[0019F8D8]=006F0C10
00401231 |. B0 25 mov al, 25 ; SendDlgItemMessageA(WM_GETTEXT)
00401233 |. FF15 64234000 call dword ptr [402364] ; 取得注册密码"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 [edi]
0040125A |. 8A1E |mov bl, byte ptr [esi]
0040125C |. 32C3 |xor al, bl
0040125E |. D0C0 |rol al, 1
00401260 |. 8806 |mov byte ptr [esi], 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 中,如下图所示:
继续:
其算法代码如下:
[Asm] 纯文本查看 复制代码
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 [402172], 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 [ebx] ; 第1~4字符
00401B90 |. 05 87D61200 ||add eax, 12D687
00401B95 |. 0343 08 ||add eax, dword ptr [ebx+8] ; 第9~12字符
00401B98 |. 2D 672B0000 ||sub eax, 2B67
00401B9D |. 33D2 ||xor edx, edx ; 清空积高32位
00401B9F |. F725 72214000 ||mul dword ptr [402172] ; 乘以长度值,取积低32位
00401BA5 |. 8B53 04 ||mov edx, dword ptr [ebx+4] ; 第5~8字符
00401BA8 |. 81C2 EAF48F04 ||add edx, 48FF4EA
00401BAE |. 33C2 ||xor eax, edx
00401BB0 |. 8B53 08 ||mov edx, dword ptr [ebx+8] ; 第9~12字符
00401BB3 |. 81EA 015CBC00 ||sub edx, 0BC5C01
00401BB9 |. 2B13 ||sub edx, dword ptr [ebx] ; 第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 [402162] ; [addr]== 0x15263748,静态常量
00401BD0 |. 8B03 ||mov eax, dword ptr [ebx] ; 第1~4字符
00401BD2 |. 2D 87D61200 ||sub eax, 12D687
00401BD7 |. 2B43 08 ||sub eax, dword ptr [ebx+8] ; 第9~12字符
00401BDA |. 05 CE560000 ||add eax, 56CE
00401BDF |. 33D2 ||xor edx, edx ; 清除被除数高32位
00401BE1 |. F735 72214000 ||div dword ptr [402172] ; 除以长度值
00401BE7 |. 8B53 04 ||mov edx, dword ptr [ebx+4] ; 第5~8字符
00401BEA |. 81EA EAF48F04 ||sub edx, 48FF4EA
00401BF0 |. 33C2 ||xor eax, edx
00401BF2 |. 8B53 08 ||mov edx, dword ptr [ebx+8] ; 第9~12字符
00401BF5 |. 81C2 015CBC00 ||add edx, 0BC5C01
00401BFB |. 0313 ||add edx, dword ptr [ebx] ; 第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 [402166] ; [addr] = 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 [402133] ; 0xFF,静态初始化的变量
00401C2D |. 803D 33214000 00 |cmp byte ptr [402133], 0
00401C34 |.^ 0F85 4FFFFFFF \jnz 00401B89
00401C3A |. C605 33214000 FF mov byte ptr [402133], 0FF ; 恢复
00401C41 |. BB 1D214000 mov ebx, 0040211D ; ebx ===> (ASCII "1234567890")
00401C46 |. B9 0E000000 mov ecx, 0E ; 长度,14
00401C4B |> C603 00 /mov byte ptr [ebx], 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:[edi], 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 [ebp+8] ; hInstance
00401C82 |. B0 15 mov al, 15 ; SetDlgItemTextA()
00401C84 |. FF15 64234000 call dword ptr [402364] ; 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 又对密码进行了一番操作,但是返回后,并没有正确的进行提示。如下代码:
[Asm] 纯文本查看 复制代码
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 [ebp+8]
004012D6 |. B0 15 mov al, 15 ; SetDlgItemTextA()
004012D8 |. FF15 64234000 call dword ptr [402364] ; 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 [ebp+8] ; hInstance
004012EB |. B0 15 mov al, 15 ; SetDlgItemTextA()
004012ED |. FF15 64234000 call dword ptr [402364] ; 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秒):
[C++] 纯文本查看 复制代码
#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[16];
unsigned long key[4];
} passwd;
/// 初始化缓冲区
passwd.key[0] = 0;
passwd.key[1] = 0;
passwd.key[2] = 0;
passwd.key[3] = 0;
long n = strlen(password);
if(n<=0) {
return -1;
}
/// 长度处理,最长12个字符
if (n>12) {
n = 12;
password[12] = '\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[0];
a += 0x0012D687;
a += passwd.key[2];
a -= 0x00002B67;
a *= n;
d = passwd.key[1];
d += 0x048FF4EA;
a ^= d; // xor
d = passwd.key[2];
d -= 0x00BC5C01;
d -= passwd.key[0];
d += 0x00002B67;
a ^= d;
a |= 0x029359E2;
check1 += a;
check1 &= 0x15263748;
a = passwd.key[0];
a -= 0x0012D687;
a -= passwd.key[2];
a += 0x000056CE;
//a &= 0xFFFFFFFF;
a /= n;
d = passwd.key[1];
d -= 0x048FF4EA;
a ^= d;
d = passwd.key[2];
d += 0x00BC5C01;
d += passwd.key[0];
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[16];
for(int i=0; i<16;i++) {
password[i] = '\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[index] = (char)i;
int b = genPassword(index+1, len, testPassword);
if(b==0) {
return 0;
}
}
return 1;
}
计算结果:
[HTML] 纯文本查看 复制代码
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 还是牛。
[Asm] 纯文本查看 复制代码
#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[16];
unsigned long key[4];
} passwd;
/// 初始化缓冲区
passwd.key[0] = 0;
passwd.key[1] = 0;
passwd.key[2] = 0;
passwd.key[3] = 0;
long n = strlen(password);
if(n<=0) {
return -1;
}
/// 长度处理,最长12个字符
if (n>12) {
n = 12;
password[12] = '\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[0] + passwd.key[2] + 0x0012AB20) * n;
D1 = passwd.key[1] + 0x048FF4EA;
A1 ^= D1; // xor
////-----------------------------------------------------------------
D1 = passwd.key[2] - passwd.key[0] - 0x00BC309A;
A1 ^= D1;
A1 |= 0x029359E2;
////=================================================================
A2 = (passwd.key[0] - passwd.key[2] - 0x00127FB9) / n;
D2 = passwd.key[1] - 0x048FF4EA;
A2 ^= D2; // Xor
///------------------------------------------------------------------
D2 = passwd.key[2] + passwd.key[0] + 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[16];
for(int i=0; i<16;i++) {
password[i] = '\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[index] = (char)i;
int b = genPassword(index+1, len, testPassword);
if(b==0) {
return 0;
}
}
return 1;
}
免费评分
查看全部评分