003-Cruehead
KeyFile类型
下面进行逆向分析操作,首先拖入PEiD中进行查壳操作,并判断其开发工具。
从图中可以判断出来该软件没有加壳,是MASM32/TASM32的汇编程序。
下面将该软件直接拖入到OllyDbg中进行动态分析。
按理说,这里应该进行搜索字符串的操作,但是我们不这么做。
从上图中的红色注释我们可以发现,程序即将进行CreateFile和ReadFile操作,因此我们关注一下输入的参数,记录一下文件名:
00401028 |. 68 D7204000 push Cruehead.004020D7 ; |CRACKME3.KEY
我们随之在当前软件的目录下创建这个名字的文件:“CRACKME3.KEY”,其中写入26个英文字母作为输入。
继续进行调试,发现到这个位置的时候,字母已经读入内存之中。
0x00401066处将读入的字符数和0x12进行比较,进而选择是否跳转。这部分的含义是判断输入的字符个数是否为0x12也就是18。
保存在0x00402008中的字符串也在注释中可以显示出来,即a-r的18个字母。
那么继续跟踪此流程,F7进入0x00401074的函数之中,如下图:
首先清空ecx和eax寄存器的数值。并且将参数(a-r字符串)赋值给esi,将0x41(‘A’)赋值给bl。
下面进行一个循环,首先将esi地址处保存的一字节数据(‘a’)赋值给al。
再将al(‘a’)与bl(‘A’)异或的值赋给al,结果为0x20(0x20在ASCII码中表示为空格),再将此al值赋给esi地址处保存的一字节数据,此时原参数字符串变为(‘ b-r’)。
之后对esi、bl进行加一操作(字符数组指针后移指向下一个字符、对此当前字符的ASCII码改变),并且将上面异或的结果加在0x004020F9的内存中。
最后将cl作为计数器加一,在跳转处判断bl是否增加到0x4F,如果有则退出,如果没有则继续循环。
ecx作为本次程序的计数器,则最终给0x402149的赋值为0xE。
这便是循环的内容了,总共会循环0x4F-0x41,即14次。得到两个结果:
- 就是将参数字符串的前14个字符分别与0x41-0x4F进行异或,并且将异或的值覆盖在参数字符串的对应位置上,得到 ’ ’ *14+“opqr”(共18位)。
- 每次异或后的结果累加在0x4020F9的位置之中。
跳出这个函数之后,继续进行运算:
先将0x4020F9内存处的值(前14位和0x41-0x4F分别异或之和)与常量0x12345678进行异或。
其中0x0040108B处的函数所作的操作为:将18位字符串的后4位赋值给eax寄存器,再和0x4020F9内存处的值(前14位和0x41-0x4F分别异或之和)进行比较。
如果这部分判断成功,"CrackMe v3.0"字符就会显示在弹出的程序框中。后面的流程不再赘述,我们用C语言程序来模拟这个汇编的判断过程,先得出正确的字符串,再看看结果。
#include<iostream>
#include <fstream>
using namespace std;
int main()
{
unsigned char key[18] = { 0 };
char str[] = "monster290test";
int bl = 0x41;
int sum = 0, tmp = 0;
for (size_t i = 0; i < 14; i++, bl++) {
tmp = str[i] ^ bl;
sum += str[i];
if (tmp == 0)
{
break;
}
key[i] = tmp;
}
cout << sum << endl;
sum ^= 0x12345678;
*(unsigned int*)&key[14] = sum;
ofstream outFile("CRACKME3.key", ios::out | ios::binary);
if (!outFile)
{
cout << "无法打开文件" << endl;
return 1;
}
outFile.write(reinterpret_cast<const char*>(&key), 18);
outFile.close();
return 0;
}
生成的CRACKME.KEY文件放在当前软件目录下,重新打开软件,发现如下的结果。
至此我们破解成功。