新手破解练习Crackme160之116 - crueme
依旧是win98程序
98下OD载入程序搜索失败关键词定位到按钮事件入口004010E6, 下好断点, 输入伪码name: abc, code: 123单步跟踪分析算法:
整个代码都是系列号计算, 跟用户名好像是没关系, 算法后段有个常量串bbaa(mov eax,401320)有点像我们输入的abc, 我们换一个输入看一下是不是有关系, 输入aaa变成了aaaa, 输入cba,变成了bbcc, 看起来是1, 2位的叠数, 所以可以确定这个值与用户名有关, 我们把汇编区的滑块拉到最上面, 从最开始搜索(右键->查找->常量) 00401320, 成功搜索到004010C0处mov dword ptr ds:[401320],eax 所以我们在这个方法入口处下个断点~ 按下check后果然能在这里停下~ 说明作者把用户名和系列号分开两个方法来做验证了, 下面是根据这两段算法分析出来的注册机代码: keygen.c
#include <stdio.h>
#include <string.h>
#include <time.h>
int ror(unsigned int num, unsigned char i);
int rol(unsigned int num, unsigned char i);
unsigned int getNameValue(char *name); // Name的算法
unsigned int getCodeValue(char *code); // Serial的算法
int chkCode(int index, int len, char* code, unsigned int nval);
int getNameCode(int index, int len, char* name, char* code);
int main() {
//unsigned int a = getNameValue("aaoo");
//unsigned int b = getCodeValue("vj41");
char name[21] = {0}; //用户名
char code[21] = {0};
clock_t s = clock(), e;
double t; int rlt;
/*for(int i=2; i<5; i++){ //尝试2~5位
printf("%d位:\n", i);
rlt = getNameCode(0, i, name, code);
e = clock();
t = ((double)(e - s)) / CLOCKS_PER_SEC;
if(rlt == 1) printf("时间: %.3f秒\n用户名: %s\n序列号: %s\n", t, name, code);
}*/
rlt = getNameCode(0, 4, name, code); //2,3位都没找到
e = clock();
t = ((double)(e - s)) / CLOCKS_PER_SEC;
if(rlt == 0) printf("时间: %.3f秒, 没有找到~\n", t);
else printf("时间: %.3f秒\n用户名: %s\n序列号: %s\n", t, name, code);
getchar();getchar();
return 0;
}
int ror(unsigned int num, unsigned char i){ //这个简单方便, 右移几位, 把顶掉的几位补到前面
return (num >> i) | (num << (sizeof(int) * 8 - i));
}
int rol(unsigned int num, unsigned char i){
return (num << i) | (num >> (sizeof(int) * 8 - i));
}
unsigned int getNameValue(char *name){
int lenn = strlen(name);
unsigned int ecx = 0, eax = 0;
for(int i=0; i<lenn; i++){
eax &= 0xFFFFFF00;
eax += name[i]; //i
eax = rol(eax, 8);
eax &= 0xFFFFFF00;
eax += name[i]; //i
eax = rol(eax, 8);
eax &= 0xFFFFFF00;
if(++i>=lenn) break;
eax += name[i]; //i+1
eax = rol(eax, 8);
eax &= 0xFFFFFF00;
eax += name[i]; //i+1
ecx += eax;
}
return ecx;
}
unsigned int getCodeValue(char *code){
unsigned int eax=0,ecx=0,edx=0;
int len = strlen(code);
for(int i=0; i<len; i++){
eax &= 0xFFFFFF00;
eax += code[i];
ecx &= 0xFFFFFF00;
ecx += code[i+1];
eax -= ecx;
ecx += eax;
edx += ecx;
edx++;
eax = rol(eax, 8);
ecx = ror(ecx, 8);
edx = rol(edx, 4);
}
eax = edx;
if(ecx != 1){
eax = ror(eax, 8);
if(eax && 0xFF != 0){
eax &= 0xFFFFFF00;
eax = ror(eax, 8);
}
}
eax = rol(eax, 8) + (edx & 0xFF);
return eax;
}
int chkCode(int index, int len, char* code, unsigned int nval){
int rlt = 0;
unsigned int codeValue = 0;
if(index == len){ //得到code
codeValue = getCodeValue(code);
rlt = (codeValue == nval) ? 1 : 0;
return rlt;
}
//for(int i=0x20; i<0x7F; i++){ //可见字符区域
// code[index] = (char) i;
char *str = "abcdefghijklmnopqrstuvwxyz0123456789";
for(int i=0; i<strlen(str); i++){
code[index] = str[i];
rlt = chkCode(index+1, len, code, nval);
if(rlt == 1) return 1;
}
return 0;
}
int getNameCode(int index, int len, char* name, char* code){
int rlt = 0;
unsigned int nameValue = 0;
if(index == len){ //得到name
nameValue = getNameValue(name);
rlt = chkCode(0, len, code, nameValue);
if(rlt == 1){
printf("用户名: %s\n序列号: %s\n", name, code);
return 0;
}
return rlt;
}
//for(int i=0x20; i<0x7F; i++){ //可见字符区域
// name[index] = (char) i;
char *str = "abcdefghijklmnopqrstuvwxyz0123456789";
for(int i=0; i<strlen(str); i++){
name[index] = str[i];
rlt = getNameCode(index+1, len, name, code);
if(rlt == 1) return 1;
}
return 0;
}
哪位大佬对算法比较厉害的, 可以直接逆向求解? 我只能暴破了, 尝试了纯数字, 纯字母, 没有找到结果, 2位3位的也没找到, 所以直接上4位, 5位的时间太久了, 跑了几十分钟没出结果放弃了, 上面代码运行结果示例: (发现序列号都是同一个, 不知道继续运行下去会不会出现不一样的)
用户名: aaoo
序列号: vj4l
用户名: abon
序列号: vj4l
用户名: acom
序列号: vj4l
用户名: adol
序列号: vj4l
用户名: aeok
序列号: vj4l
用户名: afoj
序列号: vj4l
1~160每个破解过程,在吾爱破解论坛都有高手破解过了,也有整理好现成的, 我这边主要就是自己动手操作的过程,与他们的不太一样
附上高手们的连接: 点击前往查看
使用的工具连接(工具有点多有点大,可以先下OD,其它的后面慢慢下) 点击前往下载
新人入门教程"玩玩破解,写给新人看" 点击前往查看
我就是从这里开始的,对我这样的小白感觉超级友好~
下面是我的OD的界面布局,我觉得这4个是最常用的界面,其它的我基本上没用到~


浙公网安备 33010602011771号