逆向学习crackme160题-003-Cruehead-CrackMe-3的 write up

003-Cruehead-CrackMe-3的 write up

抄一百篇不如自己实践操作写一篇,尊重知识产权,写笔记辛苦,禁止转载!!!
原文地址:乙太的小屋
我是同一作者!
如果你觉得有用就请留下一个赞吧,谢谢啦~~

1. 执行程序测试

image-20220925171632463

如上图,程序执行后什么也没有,就是一些字符串,猜测有一些内容是需要破解之后才能够显示出来。

2. 程序查壳

image-20220925172050748

解释:程序无壳

3. 静态调试

  1. 首先将程序拖入32位IDA后先点击View(查看)——>Open subviews——>Strings,结果如下图:

    image-20220927102046415

    解释:可以看到有一些字符串是程序运行的时候没有显示出来的,这应该是需要破解之后才能显示的,猜测CRACKME3.KEY可能是一个文件名。

  2. 查看输入表,点击View——>Open subviews——>Imports,结果如下:

image-20220927102838298

解释:这里只截屏来部分重要的API,并不是全部,但是可以猜测到这是和文件操作有关系。

详情:https://blog.csdn.net/jeanphorn/article/details/44982273
GreateFileA 创建或打开文件或I / O设备,返回值句柄(写的时候记得头文件: Fileapi.h)详情见https://blog.csdn.net/weixin_42810844/article/details/103246109
ReadFile   从文件指针指向的位置开始将数据读出到一个文件中
WriteFile     将数据写入一个文件
OpenFile    以不同方式打开文件的操作
  1. 可以用跟踪关键的字符串,或者是API函数,通过IDA的交叉引用,找到关键的主函数start。如果是流程图的形式,可以采用右键——>Text vie,以整篇形式观察加注释。如下图:

    image-20220927165343104

    解释:可以知道程序的大致思路是需要打开一个包含注册码的文件,00401035文件存在则跳转,文件不存在则顺序执行,0040103C执行call调用的函数是拼接字符串,进入函数如下图:

    image-20220927165830332

    解释:可见拼接的字符串是在CrackMe v3.0后面回车然后拼接“-Uncracked”,这是由于程序是由大端序模式存储的,所以数据高位在低位。

    如果文件存在则跳转到00401043,开始读取文件内容并读取18个字符,保存到缓冲区402008,然后再来判断读取到的文件内字符串是不是18个字符,00401074的call调用00401311的函数进行解密操作,如下图:

    image-20220927172704933

    解释:解密时将把18个字符中的前14个字符每一个依次与“A~N"字符进行异或,所以是14次,然后将每次异或的结果储存到004020F9进行累加。最后回到第一张图,将累加出来的结果进行四个字节的异或,结果继续保存到了4020F9。

  2. 接下来的操作如下图:

    image-20220927181455043

注意重要知识点:SETZ AL //取标志寄存器中ZF的值, 放到AL中. SETNZ取得ZF值后, 取反, 再放到AL中.

解释:把校验结果保存在al里入栈了,接下来到后面还会进行一次检验。

  1. 然后经过一段窗口创建的操作之后,在进入消息循环之前,做了这样一个校验,校验文件内容是否正确,正确就弹框提示,正是通过刚刚push的al进行校验的

image-20220927181729694

4. 动态调试

动态调试结合会让很多的数据看起来比较清晰。

  1. main函数,关节处加入了断电如下

    image-20220927182941224

    5. 程序复现

    注册码的检验是:

    1. 18个字符串中取出前14个字符依次分别与”A~N"进行异或运算

    2. 将每次异或出来的结果累加保存

    3. 最后累加的结果和0x123456再次进行异或

    4. 最后异或的结果和最后面四个字符比较。

      注册机的生成思路是:

      1. 随便输入14个字符串,然后分别依次和“A~N”的结果进行异或
      2. 每次异或的结果累加保存起来,最后和0x123456异或
      3. 然后将最后异或出的结果拼接到14个字符后面,即可完成生成。
#include <stdio.h>
#include <windows.h>
#include <string.h>

int main()
{
    
    
	void generatekey(char key);  //声明函数
	char key[19] = {
    
     0 };//储存用户输入的字符,做实际参数
	printf("enter you username: ");
	scanf("%s", key);
	generatekey(key);    //调用函数
	system("pause");
	return 0;
}
void generatekey(char key[19])
{
    
    
	char KEY[19] = {
    
     0 };           //存储序列号
	int i = 0;					//充当字符串的下标
	int k = 0;					//充当循环计数
	char bl = 'A';				//用来解密的字符串
	unsigned int sum = 0;		//每次异或的累加值储存到这里
	unsigned char* tmp = {
    
     0 };		//无符号的字符串指针初始化0
	strcpy(KEY, key);      //将用户输入的字符串复制到KEY
	do                    //循环解密
	{
    
    
		KEY[i] ^= bl;
		sum += KEY[i];
		i++;
		bl++;
		if (!KEY[i]) break;
	} while (bl!='O');
	sum ^= 0x12345678;		//累加值异或
	*(unsigned int*)&KEY[0xE] = sum;	//将前面异或出来的结果拼接到KEY的后4个字符
	for ( k = 0; k < 18; k++)			//循环遍历以两位十六进制数输出KEY
	{
    
    
		tmp = (unsigned char*)&KEY;
		printf("%02x ", tmp[k]);
	}
	printf("\nstrlen(tmp)=%d\n", strlen(tmp));//求输出字符串的实际长度(不包含/0)
	//由于前14个字符储存的是异或的结果,所以下面要把前14个字符逆向异或回原来的字符
	int j = 0xD;  //充当下标
	char al = 'N';//充当解密字符
	do
	{
    
    
		tmp[j] ^= al;
		j--; 
		al--;
		if (!tmp[j])break;
	} while (al!='@');
	for (k = 0; k < 18; k++)//循环遍历以两位十六进制数输出KEY
	{
    
    
		tmp = (unsigned char*)&KEY;
		printf("%02x ", tmp[k]);
	}
	printf("\nstrlen(tmp)=%d\n", strlen(tmp));//求输出字符串的实际长度(不包含/0)
	printf("KEY: %s", KEY);//以字符串的形式输出KEY
	HANDLE hFile = CreateFileA	//函数的返回值是一个句柄
	(	"CRACKME3.KEY", //要打开的文件名字
		GENERIC_ALL, //允许设备进行所有操作
		0, //不共享
		NULL, //指针
		CREATE_ALWAYS,        //总是创建文件
		//OPEN_ALWAYS, //如果文件不存在则创建它
		FILE_ATTRIBUTE_NORMAL, //默认属性
		NULL		//指定文件句柄
	);
	DWORD retNum = 0;
	WriteFile
	(	hFile, //文件句柄
		KEY, //要写入的数据
		sizeof(KEY), //要写入的字节数,结果应该是给了后面的取地址
		&retNum, //实际写入的字符串
		NULL	//OVERLAPPED 结构,一般设定为 NULL
	);
	CloseHandle(hFile);	//关闭句柄
}

猜你喜欢

转载自blog.csdn.net/m0_64696290/article/details/131258861