0x4012DD处开始向后的三条指令是关键
004012DD lods dword ptr ds:[esi]; 从[0x4011ec处开始,每次向后取4个字节(高端存取法),进行异或运算],直到执行到向后偏移4 * 0x3E个字节
- 004012DD > AD lods dword ptr ds:[esi] ; 从[0x4011ec处开始,每次向后取4个字节(高端存取法),进行异或运算],直到执行到向后偏移4 * 0x3E个字节
- 004012DE . |33D8 xor ebx,eax
- 004012E0 . |49 dec ecx
注意在0x4011ec到向后偏移4 * 0x3E处不能有任何断点,不然某些地方数据会变成0xCC, 0xCC对应的汇编指令为int3,是专门用来调试的中断指令。当CPU执行到int3指令时,会触发异常代码为EXCEPTION_BREAKPOINT的异常,这样OD就能够接收到这个异常,然后进行相应的处理,这也是CC断点也叫int3断点的原因。
因此为了获取数据,我们直接从exe文件中读取
- const int count = 0x3E;
- int size = count * 4;
- FILE *f = fopen("Chafe.2.exe","rb");
- fseek(f,0x5ec,0);//0x4011ec在文件中的绝对偏移为0x5EC
- unsigned long *buffer = new unsigned long[0x3E];
- fread(buffer,4,count,f);
- fclose(f);
接下来,我们模拟这个异或运算
- unsigned long x = 0;
- for (int i=0;i<count;i++) {
- //对于倒数第2组和第三组的数据,是我们需要逆推的数据,这里先跳过
- //由于高位存取法
- //所以倒数第二组数据为0xD833ADXX
- //所以倒数第三组数据为0xXXXXXX04
- if (i == count - 3 || i == count - 2) continue;
- x ^= buffer[i];
- }
异或运算可以分字节运算,所以,为了求解XX,我们先把已知的字节做运算
- //x ^= 0xNNNNNN04
- x ^= 0x00000004;
- //x ^= 0xD833ADNN
- x ^= 0xD833AD00;
然后由于最终结果要等于0xAFFCCFFB,才能注册成功
因此我们再与0xAFFCCFFB做异或运算
- x ^= 0xAFFCCFFB;
现在,x的结果为0x5426eb58
对应起来,倒数第二组的末尾字节为0x58,
倒数第三组的前三个字节为0x54eb26
但是,由于4个数据一组,
- 004012BB xor dword ptr ds:[0x4012D9],eax ; [0x4012d9] ^= ans
- 004012C1 shr eax,0x10 ; ans = ans >> 0x10
- 004012C4 sub word ptr ds:[0x4012D9],ax ; [0x4012d9] -= ans 以上相当于修改了[0x4012d9]处的指令
程序是修改0x4012d9处的数据,4字节数据,即0x4012d9 ,0x4012da,0x4012db,0x4012dc处的数据,而根据逆推,它们分别存储着数据0xEB 0x26 0x54 0x58,由于高位存取法,该处数据实际为0x585426EB
即我们需要把运算结果的最后一字节放到最开头,我们使用位移实现
- //做位调换
- unsigned long t = x;
- x = (t & 0xFF) << 24;
- x = x + (t >> 0x8);
现在,我们得出,要想注册成功,0x4012D9的数据必须为0x585426EB
更为关键的是0x585426EB对应的指令正好有一条jmp 0x00401301,因此这样就能跳转到注册成功的地方了,这种动态改变指令的算法很巧妙,值得我们学习
我们来分析,程序是如何计算这里的数据的
- 004012A3 . A1 0B304000 mov eax,dword ptr ds:[0x40300B] ; unsigned long ans = 0x58455443
- 004012A8 . BB 6C314000 mov ebx,Chafe_2.0040316C ; for (int i=0; i<16; i++) {
- 004012AD > 0303 add eax,dword ptr ds:[ebx] ; ans += *((unsigned long *)p++);
- 004012AF . 43 inc ebx
- 004012B0 . 81FB 7C314000 cmp ebx,Chafe_2.0040317C
- 004012B6 .^ 75 F5 jnz XChafe_2.004012AD ; }
首先是对用户名进行计算,循环16次,每一次用户名字符串指针向后移到一个字节,并把当前指针指向的地址处的数据向后取出4个字节,转成数字,即向后取4个字符,获取ascii码,由高到低组合成4字节数据
接下来是关键的计算
- 004012B8 . 5B pop ebx ; 获取输入的serial,存入ebx
- 004012B9 . 03C3 add eax,ebx ; ans += serial
- 004012BB . 3105 D9124000 xor dword ptr ds:[0x4012D9],eax ; [0x4012d9] ^= ans
- 004012C1 . C1E8 10 shr eax,0x10 ; ans = ans >> 0x10
- 004012C4 . 66:2905 D9124>sub word ptr ds:[0x4012D9],ax ; [0x4012d9] -= ans 以上相当于修改了[0x4012d9]处的指令
这里就是修改0x4012D9处的数据了
首先写出运算过程
ans += serial;
unsigned long x = 0x584554; //0x4012D9的初始内容
x ^= ans;
ans = ans >> 0x10;
x -= ans & 0xFF;
x == 0x585426EB
由于异或运算可以分字节单独运算,于是我们写出方程
0x0058 ^ ans_h = 0x5854
ans_l ^ 0x4554 – ans_h = 0x26EB
解得ans_h = 0x580C
ans_l = 3BA3
所以 ans = 0x580C3BA3
- unsigned long fl = 0x00584554;
- unsigned long key = fl ^ x;
- t = (key >> 0x10) + (x & 0xFFFF);
- t ^= (fl & 0xFFFF);
- key = (key & 0xFFFF0000) + t;
即name的运算结果+serial必须为0x580C3BA3才能注册成功
所以serial = key – (name的运算结果)
综上,我们写出注册机
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- using namespace std;
- int main() {
- const int count = 0x3E;
- int size = count * 4;
- FILE *f = fopen("Chafe.2.exe","rb");
- fseek(f,0x5ec,0);//0x4011ec在文件中的绝对偏移为0x5EC
- unsigned long *buffer = new unsigned long[0x3E];
- fread(buffer,4,count,f);
- fclose(f);
- unsigned long x = 0;
- for (int i=0;i<count;i++) {
- //对于倒数第2组和第三组的数据,是我们需要逆推的数据,这里先跳过
- //由于高位存取法
- //所以倒数第二组数据为0xD833ADXX
- //所以倒数第三组数据为0xXXXXXX04
- if (i == count - 3 || i == count - 2) continue;
- x ^= buffer[i];
- }
- //cout << "异或" << x << endl;
- x ^= 0xAFFCCFFB;
- //x ^= 0xNNNNNN04
- x ^= 0x00000004;
- //x ^= 0xD833ADNN
- x ^= 0xD833AD00;
- //做位调换
- unsigned long t = x;
- x = (t & 0xFF) << 24;
- x = x + (t >> 0x8);
- //0x585426EB
- cout << hex << "0x" << x << endl;
- unsigned long fl = 0x00584554;
- unsigned long key = fl ^ x;
- t = (key >> 0x10) + (x & 0xFFFF);
- t ^= (fl & 0xFFFF);
- key = (key & 0xFFFF0000) + t;
- char name[50];
- memset(name,0,50);
- cout << "请输入用户名:";
- cin >> name;
- int len = strlen(name);
- char *p = name;
- unsigned long ans = 0x58455443;
- for (int i=0; i<16; i++) {
- ans += *((unsigned long *)p++);
- }
- cout << dec << key - ans << endl;
- return 0;
- }