0x00 前言
这个CM是《加密与解密》里的例题 书上也有讲解 我也是刚开始学逆向 第一次分析没看讲解想尝试自己把它分析出来 分析过程有什么不对也请大牛指正。
0x01 分析过程
先用od载入运行程序,点击Check发现没有任何提示。这里我对check下消息断点然后点击Check断到系统领空,然后对模块.text(00401000)处下访问断点并运行,来到程序领空后继续单步又会进入系统领空 ,接着再对.text下内存访问断点再按f9继续单步来到下图的按钮事件代码。
看到这里有个CreateFile函数 打开程序文件名是KWAZYWEB.BIT 此时程序并没有创建这个文件 需要我们自己创建一个,这里我用winhex对KWAZYWEB.BIT进行编辑写了一串简单的数字
然后用OD重新载入运行来到我们刚刚分析的地方往下走发现有三个ReadFile函数
一个一个单步可以从堆栈窗口发现第一个ReadFile是读取文件的第一个字符的编码,
第二个ReadFile根据第一个ReadFile读取的编码数作为读取个数往下读取,它的下面又有一个call我们跟进看一下
这里它将后面的字符编码累加并取累加的低位字节,循环次数为第一个字符“1”的编码
也就是31次(但我这里并没有输入这么多的字符,也不要紧我们继续分析)
出来后来到第三个ReadFile函数call这里发现它要接着第二个ReadFile读取位置往后再读
取18个字符,到这里我想应该把KWAZYWEB.BIT的内容改一下
把第一个字符的编码改为01 从第二个编码开始增加18个字符。再重新载入OD。来到最后一个ReadFile后面的call处跟进看下。
这个call将之前累加的编码低位字节与第三个ReadFile读取的18个字符依次异或存在004034EA中。返回后继续往下看。
这里分析一下可以看出是两个循环,内部小循环循环4次,外部大循环循环18次(刚好对应第三次ReadFile读取的个数)。
而小循环是每次将送入的单个字节(8位)分成4份(al)依次送入中间的call里,接下来就是分析这个call了。
这里是个case语句,判断al的值,并将[00403184]所指地址004031CC的值(也就是数据窗口C的位置)进行修改
al为00B(0)则地址减0x10、为01B(1)则地址加0x1、为10B(2)则地址加0x10、为11B(3)则地址减0x1
继续往下分析
下面有两个判断,到这里我们应该就能知道要跳到成功解密的地方需要要从地址004031CC位置(C的位置)
走到下面的X处期间不能碰到” * “也就是只能顺着” · “走。这里每走4步对应一个字节该字节为之前异或得到的。
一共要走4×18次(之前的大小循环)。这里根据走的方向和判断条件可以推算出异或得到的正确Key:
0xA9,0xAB,0xA5,0x10,
0x54,0x3F,0x30,0x55,
0x65,0x16,0x56,0xBE,
0xF3,0xEA,0xE9,0x50,
0x55,0xAF
0x02 写逆算法
#include<iostream>
#include<iomanip>
using namespace std;
int flag[18] = { 0xA9,0xAB,0xA5,0x10,
0x54,0x3F,0x30,0x55,
0x65,0x16,0x56,0xBE,
0xF3,0xEA,0xE9,0x50,
0x55,0xAF };
int main() {
int i, len, sum = 0;
char name[100];
cin.getline(name, 100);
len = strlen(name);
cout << setw(2)<<setfill('0')<<hex << len<<" ";
for (i = 0; name[i] != 0; i++) {
cout << hex << int(name[i])<<" " ;
sum += name[i];
}
sum = sum & 0xFF;//取sum第一字节;
for (i = 0; flag[i] != 0; i++) {
flag[i] = flag[i] ^ sum;
cout << hex << flag[i] << " ";
}
system("pause");
}
0x03 附上文件