新手破解练习Crackme160之119 - TryThisOne
依旧是98程序~
-
脱壳
这个程序有壳, 我们先脱壳~ 由于98下没什么工具可以用, 所以直接用OD手工脱壳, 载入程序后, 当前处于kernel32模块中BFF76921 retn, 这行, 只需要F8一次即可返回到程序段1F021300, F8单步跟踪可知, 前面几句是解密程序, 所以我们直接在1F02131B处下断点, 直接F9跳过循环, F8继续运行直到call eax(1F0212CA), 中间还有循环的也可以直接跳过~ 来到call eax后 F7进入方法, 里面(1F021000)即是去壳后的真正程序了~ 这时, 右键"用OllyDump脱壳调试进程", 记住此时的OEP被修正为1000(后面还有用, 需要记住), 选择方式2, 点脱壳~ 这时OD会异常退出, 我们重新开启OD, 重新载入程序~ F9运行~ 然后就可以晾一边了, 我们还需要运行另一个软件"Import REC"来修复脱壳后的iat, 最上面的下拉框选择OD加载的程序trythisone.exe, 右侧或下侧(不同版本位置不同, 功能一样)找到OEP这个值, 修改为1000, 再点Get Imports, 可以看到左侧出来了两个dll, kernel32.dll 和 user32.dll, 说明分析正确了, 点Fix Dump按钮, 选择我们刚才脱壳后的程序, 即可完成修复, 生成了一个新修复后的程序, 运行测试正常~ 到此脱壳完成~
-
正常破解
OD载入脱壳后的程序, 搜索定位成功关键词1F0211C7, 往上找到方法入口1F021040, 下好断点, 输入123456确定~ F8单步分析算法~
1). 1F021064 不得少于3位, 后面1F0210C1又要求4位以上
2). 将2D CC 77 66 33 22 DD CC存到[1F021F00]
3). 1F0210DD ~ 1F0210FA 将注册码与内存固定串(200多位)通过相关运算得到新串
4). 从计算结果中取出"eDoC"对应值 位置的数 作为密串1
5). 取注册码前4位与固定串0x20~0x7E(所有可见字符)相关运算后也得到4个值组成密串2
6). 对比两个密串~
7). 1F021243 注册码后5位运算后的200多位中对应的5位相等~(所以注册码9位以内即可后4位也可以是\0)
写成程序是这样的:
#include <stdio.h>
#include <string.h>
unsigned int turn(unsigned int val);
int main() {
char *code = "12345"; //输入注册码
char * str = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
int lens = strlen(str);
int lenc = strlen(code);
unsigned int ecx = 0x436F4465, eax = 0, tmp;
unsigned int rlt1[5] = {0};
unsigned int rlt2[5] = {0};
unsigned char CBB[5] = {0x65,0x44,0x6F,0x43,0}; //eDoC
unsigned char A00[0xF0] = {
0xCC,0x77,0x66,0x33,0x98,0xEF,0xCC,0x66,0x64,0x67,0x33,0x9A,0x30,0xDF,0x99,0xCD,
0xFC,0x56,0x00,0x01,0xC8,0xCE,0x66,0x34,0x94,0x46,0xCD,0x67,0x60,0xBE,0x33,0x9B,
0x2C,0x36,0x9A,0xCE,0xF8,0xAD,0x00,0x02,0xC4,0x25,0x67,0x35,0x90,0x9D,0xCD,0x68,
0x5C,0x15,0x34,0x9C,0x28,0x8D,0x9A,0xCF,0xF4,0x04,0x01,0x03,0xC0,0x7C,0x67,0x36,
0x8C,0xF4,0xCD,0x69,0x58,0x6C,0x34,0x9D,0x24,0xE4,0x9A,0xD0,0xF0,0x5B,0x01,0x04,
0xBC,0xD3,0x67,0x37,0x88,0x4B,0xCE,0x6A,0x54,0xC3,0x34,0x9E,0x20,0x3B,0x9B,0xD1,
0xEC,0xB2,0x01,0x05,0xB8,0x2A,0x68,0x38,0x84,0xA2,0xCE,0x6B,0x50,0x1A,0x35,0x9F,
0x1C,0x92,0x9B,0xD2,0xE8,0x09,0x02,0x06,0xB4,0x81,0x68,0x39,0x80,0xF9,0xCE,0x6C,
0x4C,0x71,0x35,0xA0,0x18,0xE9,0x9B,0xD3,0xE4,0x60,0x02,0x07,0xB0,0xD8,0x68,0x3A,
0x7C,0x50,0xCF,0x6D,0x48,0xC8,0x35,0xA1,0x14,0x40,0x9C,0xD4,0xE0,0xB7,0x02,0x08,
0xAC,0x2F,0x69,0x3B,0x78,0xA7,0xCF,0x6E,0x44,0x1F,0x36,0xA2,0x10,0x97,0x9C,0xD5,
0xDC,0x0E,0x03,0x09,0xA8,0x86,0x69,0x3C,0x74,0xFE,0xCF,0x6F,0x40,0x76,0x36,0xA3,
0x0C,0xEE,0x9C,0xD6,0xD8,0x65,0x03,0x0A,0xA4,0xDD,0x69,0x3D,0x70,0x55,0xD0,0x70,
0x3C,0xCD,0x36,0xA4,0x08,0x45,0x9D,0xD7,0xD4,0xBC,0x03,0x0B,0xA0,0x34,0x6A,0x3E,
0x6C,0xAC,0xD0,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
unsigned int * A00i = (unsigned int *) A00;
int lenA = 0;
while(*(A00i + lenA) != 0){
tmp = *(A00i + lenA);
eax = code[lenA % lenc] * ecx;
tmp ^= eax;
eax = turn(eax); //反序
tmp += eax;
*(A00i + lenA) = tmp;
lenA += 1;
}
for(int i=0; i<4; i++){ //得到4个密数
rlt1[i] = *((unsigned int*)(A00 + A00[0xE4+i]));
}
eax = 0; ecx = 0;
for(int i=0; i<4; i++){ //取注册码前4位
eax = (eax & 0xFF) + code[i];
for(int j=0; j<lens; j++){
ecx = (ecx & 0xFF) + str[j];
ecx *= ecx;
tmp = eax; eax = ecx; ecx = tmp; // eax <=> ecx
eax += ecx;
eax *= eax;
ecx = 0;
}
rlt2[i] = eax; //这里也得到4个密数
eax = 0;
}
if((unsigned char *)rlt1 != (unsigned char *)rlt2) {
printf("注册码错误\n");
} else {
//继续验证后5位
printf("注册码正确\n");
}
getchar();getchar();
return 0;
}
//将数字反序输出,例: 0x10203040 => 0x40302010
unsigned int turn(unsigned int val){
unsigned int rlt = 0;
while(val > 0){
rlt = rlt << 8;
rlt += val & 0xFF;
val = val >> 8;
}
return rlt;
}
这么多运算, 应该没办法逆向求出注册码吧? 反正我是没什么办法, 只能写个爆破程序了~ (结果跑了几个小时没得到结果)
#include <stdio.h>
#include <string.h>
unsigned int turn(unsigned int val);
int getCode(int index, int len, char *code);
int chkCode(char *code);
int main() {
int rlt = 0;
char code[10] = {0};
for(int i=5; i<10; i++){
printf("注册码%d位:\n", i);
rlt = getCode(0, i, code);
if(rlt == 1){
printf("注册码: %s\n", code);
break;
}
}
printf("结束~");
getchar();getchar();
return 0;
}
int getCode(int index, int len, char *code){ //递归获取len位字符
int rlt = 0;
if(index == len){
printf("%s\n",code);
return chkCode(code);
}
for(int i=0x20; i<0x7F; i++){ //所有可见字符,等同下面的str
code[index] = (char)i;
rlt = getCode(index+1, len, code);
if(rlt == 1) return 1;
}
return 0;
}
int chkCode(char *code) { //输入注册码
//code = "123456789";
char * str = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
int lens = strlen(str);
int lenc = strlen(code);
unsigned int ecx = 0x436F4465, eax = 0, tmp;
unsigned int rlt1[5] = {0};
unsigned int rlt2[5] = {0};
unsigned char A00[0xF0] = {
0xCC,0x77,0x66,0x33,0x98,0xEF,0xCC,0x66,0x64,0x67,0x33,0x9A,0x30,0xDF,0x99,0xCD,
0xFC,0x56,0x00,0x01,0xC8,0xCE,0x66,0x34,0x94,0x46,0xCD,0x67,0x60,0xBE,0x33,0x9B,
0x2C,0x36,0x9A,0xCE,0xF8,0xAD,0x00,0x02,0xC4,0x25,0x67,0x35,0x90,0x9D,0xCD,0x68,
0x5C,0x15,0x34,0x9C,0x28,0x8D,0x9A,0xCF,0xF4,0x04,0x01,0x03,0xC0,0x7C,0x67,0x36,
0x8C,0xF4,0xCD,0x69,0x58,0x6C,0x34,0x9D,0x24,0xE4,0x9A,0xD0,0xF0,0x5B,0x01,0x04,
0xBC,0xD3,0x67,0x37,0x88,0x4B,0xCE,0x6A,0x54,0xC3,0x34,0x9E,0x20,0x3B,0x9B,0xD1,
0xEC,0xB2,0x01,0x05,0xB8,0x2A,0x68,0x38,0x84,0xA2,0xCE,0x6B,0x50,0x1A,0x35,0x9F,
0x1C,0x92,0x9B,0xD2,0xE8,0x09,0x02,0x06,0xB4,0x81,0x68,0x39,0x80,0xF9,0xCE,0x6C,
0x4C,0x71,0x35,0xA0,0x18,0xE9,0x9B,0xD3,0xE4,0x60,0x02,0x07,0xB0,0xD8,0x68,0x3A,
0x7C,0x50,0xCF,0x6D,0x48,0xC8,0x35,0xA1,0x14,0x40,0x9C,0xD4,0xE0,0xB7,0x02,0x08,
0xAC,0x2F,0x69,0x3B,0x78,0xA7,0xCF,0x6E,0x44,0x1F,0x36,0xA2,0x10,0x97,0x9C,0xD5,
0xDC,0x0E,0x03,0x09,0xA8,0x86,0x69,0x3C,0x74,0xFE,0xCF,0x6F,0x40,0x76,0x36,0xA3,
0x0C,0xEE,0x9C,0xD6,0xD8,0x65,0x03,0x0A,0xA4,0xDD,0x69,0x3D,0x70,0x55,0xD0,0x70,
0x3C,0xCD,0x36,0xA4,0x08,0x45,0x9D,0xD7,0xD4,0xBC,0x03,0x0B,0xA0,0x34,0x6A,0x3E,
0x6C,0xAC,0xD0,0x71,0x65,0x44,0x6F,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}; //后4位是eDoC
unsigned int * A00i = (unsigned int *) A00;
int lenA = 0;
while(*(A00i + lenA) != 0){
tmp = *(A00i + lenA);
eax = code[lenA % lenc] * ecx;
tmp ^= eax;
eax = turn(eax); //反序
tmp += eax;
*(A00i + lenA) = tmp;
lenA += 1;
if(lenA == 3){ //先校验注册码后5位
int sign = 1;
for(int i=0; i<5; i++){
if(A00[2*i] != code[4+i]){
sign = 0;
break;
}
}
return sign;
}
}
for(int i=0; i<4; i++){ //得到4个密数
rlt1[i] = *((unsigned int*)(A00 + A00[0xE4+i]));
}
/*
for(int i=0; i<0xe5; i++){
printf("%02X,", A00[i]);
if((i+1) % 16 == 0) printf("\n");
}
*/
eax = 0; ecx = 0;
for(int i=0; i<4; i++){ //取注册码前4位
eax = (eax & 0xFF) + code[i];
for(int j=0; j<lens; j++){
ecx = (ecx & 0xFF) + str[j];
ecx *= ecx;
tmp = eax; eax = ecx; ecx = tmp; // eax <=> ecx
eax += ecx;
eax *= eax;
eax += ecx;
ecx = 0;
}
rlt2[i] = eax; //这里也得到4个密数
eax = 0;
}
/*
for(int i=0; i<4; i++){
printf("\n%08X, %08X", rlt1[i], rlt2[i]);
}
*/
if((unsigned char *)rlt1 == (unsigned char *)rlt2) {
return 1;
}
return 0;
}
//将数字反序输出,例: 0x10203040 => 0x40302010
unsigned int turn(unsigned int val){
unsigned int rlt = 0;
while(val > 0){
rlt = rlt << 8;
rlt += val & 0xFF;
val = val >> 8;
}
return rlt;
}
1~160每个破解过程,在吾爱破解论坛都有高手破解过了,也有整理好现成的, 我这边主要就是自己动手操作的过程,与他们的不太一样
附上高手们的连接: 点击前往查看
使用的工具连接(工具有点多有点大,可以先下OD,其它的后面慢慢下) 点击前往下载
新人入门教程"玩玩破解,写给新人看" 点击前往查看
我就是从这里开始的,对我这样的小白感觉超级友好~
下面是我的OD的界面布局,我觉得这4个是最常用的界面,其它的我基本上没用到~


浙公网安备 33010602011771号