160-CrackMe之-Cruehead-CrackMe-3
160-CrackMe之-Cruehead-CrackMe-3
先打开程序观察程序的布局
载入PEiD,没有查到壳
载入od
可以发现一个敏感的文件名“CRACKME3.KEY
”,以及使用了CreateFileA函数
下方还有个跳转,通过动调可以知道:这一部分就是调用Windows API读取该程序所在目录的“CRACKME3.KEY”文件,如果不存在,则会将CrackMe v3.0
字符串后添加- Uncracked
。
我就自行创建了一个“CRACKME3.KEY”文件,内容写了“1234567890abcdefghijklmnopqrst”
在od观察可以看到ReadFile函数,配合动态调试,知道这是读取“CRACKME3.KEY”文件的前18个字符
再看红线处,将读取的字符串压入栈后,进入一个call,说明这个call里面应该包含了密码匹配的算法。
还要注意橙色横线处指向的地址,很有可能就是在call Cruehead.00401311
内部有结果存入该地址
下图是初始时,地址内容:
跟入Cruehead.00401311
需要详细分析这段代码:
- 首先是做了初始化,将ecx、eax置为0,esi指向从文件中读取的字符串
1234567890abcdefgh
,bl置为0x41 - 看红线部分以及
jnz short 0040131B
,可以知道是一个循环,使bl从 0x41 -> 0x4F的循环,对应ASCII码表知:这就是要bl从A
遍历到O
,也是要循环改变字符串的前14个字符 - 再看蓝框部分,这是将al赋值为esi指向的开头字符,使其与bl进行异或运算,后将结果返回至原本内存处
- 对应
inc xx
等部分,就是使对应寄存器数值加一,esi + 1
是要使其开头字符变为下一位,bl + 1
既是对应数据更新,也是循环条件数的更新,cl + 1
是为了记录进行了几次循环 - 注意
add dword ptr ds:[0x4020F9],eax
,这指向的地址就是上文橙线地址,可以知道:该地址数据就是累加al异或bl的结果 - 还有
cmp al,0x0
与je short 00401335
,此处是判断异或的结果是否为零,若为零则跳出循环
转换成C++代码,如下:
unsigned int Gnum = 0;
int countfor = 0;
unsigned char s[] = "1234567890abcdefgh";
void f(char* s){
int c = 0;
for(char i = 'A';i < 'O';i++){
s[i - 'A'] ^= i;
unsigned int a = s[i - 'A'];
Gnum += a;
if(s[i - 'A'] = 0){
break;
}
c++;
}
countfor = c;
return;
}
返回到主函数里:
可以看到内存0x4020F9处的数据与0x12345678进行异或,而后进入call 0040133C
:
此函数就是将字符串的后四位字符赋值给eax,注意此四个字符在前面函数中并为修改。
然后cmp eax,dword ptr ds:[0x4020F9]
,就是使字符串后四个字符与前14个字符异或结果累加值进行比较,sete al
是使ZF标志位赋值给al,如果一样的话,al为一,后续就不会回跳至未破解的函数处理处;如果不一致,则会回跳:
所以后续逻辑,使用C++代码:
unsigned char Gnum_byte[4]; //先变成字节类型,方便比较
Gnum ^= 0x12345678;
for (int i = 3, j = 24; i >= 0; i--, j -= 8) {
Gnum_byte[i] = Gnum >> j;
}
for(int i = 0;i < 4;i++){
if(s[i + 14] != Gnum_byte[i])
return -1;
}
return 1;
下列是通过C++创建“CRACKME3.KEY”文件代码:
#include<iostream>
#include<fstream>
using namespace std;
int main() {
unsigned char s[] = "1234567890abcdefgh";
unsigned char s2[] = "1234567890abcdefgh";
unsigned int Gnum = 0;
for (char i = 'A'; i < 'O'; i++) {
s[i - 'A'] ^= i;
unsigned int a = s[i - 'A'];
Gnum += a;
if (s[i - 'A'] == 0) {
break;
}
}
unsigned char Gnum_byte[4]; //先变成字节类型,方便比较
Gnum ^= 0x12345678;
for (int i = 3, j = 24; i >= 0; i--, j -= 8) {
Gnum_byte[i] = Gnum >> j;
}
for (int i = 0; i < 4; i++) {
s2[i + 14] = Gnum_byte[i];
}
ofstream ofs;
ofs.open("CRACKME3.KEY", ios::out);
ofs << s2 << endl;
ofs.close();
return 0;
}