新手破解练习Crackme160之119 - TryThisOne

依旧是98程序~

  1. 脱壳
    这个程序有壳, 我们先脱壳~ 由于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按钮, 选择我们刚才脱壳后的程序, 即可完成修复, 生成了一个新修复后的程序, 运行测试正常~ 到此脱壳完成~
     

  2. 正常破解
    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;
	}

 
 
 
这是160个软件part1
这是160个软件part2

1~160每个破解过程,在吾爱破解论坛都有高手破解过了,也有整理好现成的, 我这边主要就是自己动手操作的过程,与他们的不太一样
附上高手们的连接: 点击前往查看
使用的工具连接(工具有点多有点大,可以先下OD,其它的后面慢慢下) 点击前往下载

新人入门教程"玩玩破解,写给新人看" 点击前往查看
我就是从这里开始的,对我这样的小白感觉超级友好~

下面是我的OD的界面布局,我觉得这4个是最常用的界面,其它的我基本上没用到~
OD界面布局

posted @ 2024-07-23 17:33  hankerstudio  阅读(14)  评论(0)    收藏  举报