个人感觉这个CM是我玩过的最另类的一个了,如果有什么不对的地方还请各位大佬更正。
0x0 寻找按钮事件
OD载入,发现程序打开crueme.dat文件,如果打开失败则提示错误然后结束程序。
00401043 6A 00 push 0x0
00401045 68 80000000 push 0x80
0040104A 6A 03 push 0x3
0040104C 6A 00 push 0x0
0040104E 6A 00 push 0x0
00401050 68 000000C0 push 0xC0000000
00401055 68 76214000 push 00402176 ; CRUEME.DAT
0040105A B0 1F mov al, 0x1F
0040105C FF15 64234000 call dword ptr [0x402364] ; CreateFileA
00401062 83F8 FF cmp eax, -0x1
00401065 75 24 jnz short 0040108B
00401067 6A 30 push 0x30
00401069 68 A0214000 push 004021A0 ; This is the end - My only friend the end!
0040106E 68 CA214000 push 004021CA ; AAARGH! Where is my CRUEME.DAT file???\r I cant go >
00401073 6A 00 push 0x0
00401075 B0 1A mov al, 0x1A
00401077 FF15 64234000 call dword ptr [0x402364] ; MessageBoxA
0040107D FF35 54204000 push dword ptr [0x402054]
00401083 B0 0A mov al, 0xA
00401085 FF15 64234000 call dword ptr [0x402364] ; ExitProcess
在程序目录下新建一个crueme.dat文件,内容为12345678,然后重新载入。
载入以后程序弹出了一个界面,如下图所示:
给GetWindowTextA下断点,然后随便输入一串密码,比如87654321,最后点击[Check it!]。
断下后,回溯到程序领空,找到按钮事件0x00401213处,下断,然后重新载入程序即可。
0x1 分析算法1
程序首先改写了0x00401224到0x00401247处的代码。
0040123F BF 01000000 mov edi, 0x1
00401244 E8 FD080000 call 00401B46 ; 代码改写
然后将crueme.dat中的内容和一串数据逐位异或然后循环左移一位。
00401249 B9 08000000 mov ecx, 0x8 ; 循环八次
0040124E BF 85214000 mov edi, 00402185 ; crueme.dat中的内容
00401253 BE 34214000 mov esi, 00402134 ; 一个数据
00401258 8A07 mov al, byte ptr [edi]
0040125A 8A1E mov bl, byte ptr [esi]
0040125C 32C3 xor al, bl ; 依次异或
0040125E D0C0 rol al, 1 ; 循环左移一位
00401260 8806 mov byte ptr [esi], al ; 保存
00401262 47 inc edi
00401263 46 inc esi
00401264 ^ E2 F2 loopd short 00401258
其中,0x00402134处的数据如下:
FF 77 88 66 99 55 AA 44
然后检测user32!GetDlgItemTextA函数的断点
0040126F BF 141E4000 mov edi, <jmp.&USER32.GetDlgItemText>
00401274 6A 0A push 0xA
00401276 68 1D214000 push 0040211D
0040127B 68 EA030000 push 0x3EA
00401280 FF75 08 push dword ptr [ebp+0x8]
00401283 1E push ds
00401284 0E push cs
00401285 1f pop ds
00401286 8B7F 02 mov edi, dword ptr [edi+0x2]
00401289 8B3F mov edi, dword ptr [edi]
0040128B 8B07 mov eax, dword ptr [edi]
0040128D 1f pop ds
0040128E 25 FF000000 and eax, 0xFF
00401293 3D CC000000 cmp eax, 0xCC
00401298 75 24 jnz short 004012BE
0040129A 6A 30 push 0x30
0040129C 68 8D224000 push 0040228D ; Breakpoints - just say no!
004012A1 68 A8224000 push 004022A8 ; So - You're trying to put a breakpoint on GetDlgItemTe>
004012A6 6A 00 push 0x0
004012A8 B0 1A mov al, 0x1A
004012AA FF15 64234000 call dword ptr [0x402364] ; crueme.00401493
004012B0 FF35 54204000 push dword ptr [0x402054]
004012B6 B0 0A mov al, 0xA
004012B8 FF15 64234000 call dword ptr [0x402364] ; crueme.00401493
然后调用user32!GetDlgItemTextA函数获取用户输入的Password,最后调用一个函数,如果这个函数返回0则失败。
004012BE FFD7 call edi ; GetDlgItemTextA
004012C0 E8 EC000000 call 004013B1 ; 算法
004012C5 85C0 test eax, eax
004012C7 74 15 je short 004012DE
004012C9 68 36224000 push 00402236 ; Correct password - Good work!
004012CE 68 F3030000 push 0x3F3
004012D3 FF75 08 push dword ptr [ebp+0x8]
004012D6 B0 15 mov al, 0x15
004012D8 FF15 64234000 call dword ptr [0x402364] ; crueme.00401493
004012DE 68 54224000 push 00402254 ; False password - Try again
004012E3 68 F2030000 push 0x3F2
004012E8 FF75 08 push dword ptr [ebp+0x8]
004012EB B0 15 mov al, 0x15
004012ED FF15 64234000 call dword ptr [0x402364] ; crueme.00401493
0x2 分析算法2
来到0x004013B1处,发现程序检测了Password的长度,如果长度不为8则失败。
004013B1 3C 08 cmp al, 0x8
004013B3 0F85 9E000000 jnz 00401457
......
00401457 33C0 xor eax, eax
00401459 BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
0040145E B9 08000000 mov ecx, 0x8
00401463 C606 00 mov byte ptr [esi], 0x0
00401466 46 inc esi
00401467 ^ E2 FA loopd short 00401463
00401469 C3 retn
程序首先将用户输入的Password的每个字节和0x50异或。
004013B9 B9 08000000 mov ecx, 0x8
004013BE BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
004013C3 8036 50 xor byte ptr [esi], 0x50
004013C6 46 inc esi
004013C7 ^ E2 FA loopd short 004013C3
然后加上剩余的循环次数(第一个字节加8,第二个字节加7,以此类推,一直加到Password的最后一个字节),最后的结果记为s
004013C9 B9 08000000 mov ecx, 0x8
004013CE BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
004013D3 000E add byte ptr [esi], cl
004013D5 46 inc esi
004013D6 ^ E2 FB loopd short 004013D3
然后两个字符两个字符异或,保存到0x0040212B处。
004013D8 33FF xor edi, edi
004013DA BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
004013DF B9 04000000 mov ecx, 0x4
004013E4 8A06 mov al, byte ptr [esi]
004013E6 8A5E 01 mov bl, byte ptr [esi+0x1]
004013E9 32C3 xor al, bl
004013EB 8887 2B214000 mov byte ptr [edi+0x40212B], al
004013F1 83C6 02 add esi, 0x2
004013F4 47 inc edi
004013F5 ^ E2 ED loopd short 004013E4
然后将0x0040212B的每个字节异或(记为a1),如果a1是0就会失败。
004013F7 BE 2B214000 mov esi, 0040212B
004013FC 8A06 mov al, byte ptr [esi]
004013FE 8A5E 01 mov bl, byte ptr [esi+0x1]
00401401 32C3 xor al, bl
00401403 8A5E 02 mov bl, byte ptr [esi+0x2]
00401406 8A4E 03 mov cl, byte ptr [esi+0x3]
00401409 32D9 xor bl, cl
0040140B 32C3 xor al, bl
0040140D 84C0 test al, al
0040140F 74 46 je short 00401457 ; 失败
然后s的每个字节和a1异或。
00401411 BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
00401416 B9 08000000 mov ecx, 0x8
0040141B 3006 xor byte ptr [esi], al
0040141D 46 inc esi
0040141E ^ E2 FB loopd short 0040141B
再和0x0040213B的每个字节异或。
00401420 B9 10000000 mov ecx, 0x10
00401425 D1E9 shr ecx, 1
00401427 BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
0040142C BF 3B214000 mov edi, 0040213B
00401431 8A07 mov al, byte ptr [edi]
00401433 3006 xor byte ptr [esi], al
00401435 47 inc edi
00401436 46 inc esi
00401437 ^ E2 F8 loopd short 00401431
最后和0x00402134的每个字节比较,如果相等则成功,否则失败。
00401439 1E push ds
0040143A 07 pop es
0040143B B9 08000000 mov ecx, 0x8
00401440 BF 34214000 mov edi, 00402134
00401445 BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
0040144A F3:A6 repe cmps byte ptr es:[edi], byte ptr [esi]
0040144C 85C9 test ecx, ecx
0040144E 75 07 jnz short 00401457
00401450 B8 01000000 mov eax, 0x1
00401455 EB 02 jmp short 00401459
00401457 33C0 xor eax, eax
00401459 BE 1D214000 mov esi, 0040211D ; ASCII "87654321"
0040145E B9 08000000 mov ecx, 0x8
00401463 C606 00 mov byte ptr [esi], 0x0
00401466 46 inc esi
00401467 ^ E2 FA loopd short 00401463
00401469 C3 retn
这里有一点要注意:0x00402134 + 8 - 1 == 0x0040213B
因此这是属于一段数据中的。
其中0x0040213B中的数据如下(第一个字节随着crueme.dat文件的内容变化而变化):
44 BB 33 CC 22 DD 11 EE
0x3 注册机的编写
因为有一个字节是未知的,所以可以自己指定,分析出算法以后,编写出的注册机如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char rol1B(char a)
{
char result;
_asm
{
mov al,a
rol al,1
mov result,al
}
return result;
}
char ror1B(char a)
{
char result;
_asm
{
mov al,a
ror al,1
mov result,al
}
return result;
}
int main(void)
{
char KeyFile[512];
char RegisterInfo[512];
int a1;
unsigned char *Serial;
unsigned char Data[4];
unsigned char Data1[] = {
0xFF,0x77,0x88,0x66,0x99,
0x55,0xAA,0x44,0xBB,0x33,
0xCC,0x22,0xDD,0x11,0xEE};
/*
Input:char + Password
Output:Password + crueme.dat
*/
scanf("%s",KeyFile);
strcpy(RegisterInfo,KeyFile);
if(strlen(KeyFile) != 9)
return 0;
KeyFile[0] ^= Data1[7];
KeyFile[0] = rol1B(KeyFile[0]);
Serial = (unsigned char*)&KeyFile[1];
for(int i = 0;i < 8;i++)
{
Serial[i] ^= 0x50;
Serial[i] += (8 - i);
}
for(int i = 0;i < 4;i++)
Data[i] = Serial[i * 2] ^ Serial[i * 2 + 1];
a1 = Data[0] ^ Data[1] ^ Data[2] ^ Data[3];
if(a1 == 0)
return 0;
for(int i = 0;i < 8;i++)
Serial[i] ^= a1;
Serial[0] ^= KeyFile[0];
for(int i = 1;i < 8;i++)
Serial[i] ^= Data1[7 + i];
puts("-----------------------");
printf("%s\n",&RegisterInfo[1]);
for(int i = 0;i < 7;i++)
{
Serial[i] = ror1B(Serial[i]);
Serial[i] ^= Data1[i];
printf("%0.2X ",Serial[i]);
}
printf("%0.2X\n",RegisterInfo[0]);
system("pause");
return 0;
}
运行程序,输入指定的第一个字节和指定的Password(我指定的第一个字节是'~',Password是"[_KaQqi]"),得到结果如下:
用二进制编辑器将4F A2 82 18 09 AA BC 7E写入到crueme.dat中,然后打开程序,输入"[_KaQqi]",在OD中查看已经通过验证。
至此,注册机编写完毕。
0x4 修改程序
仔细观察代码,发现这个程序的有一点奇怪。
004012C5 85C0 test eax, eax
004012C7 74 15 je short 004012DE
004012C9 68 36224000 push 00402236 ; Correct password - Good work!
004012CE 68 F3030000 push 0x3F3
004012D3 FF75 08 push dword ptr [ebp+0x8]
004012D6 B0 15 mov al, 0x15
004012D8 FF15 64234000 call dword ptr [0x402364]
004012DE 68 54224000 push 00402254 ; False password - Try again
004012E3 68 F2030000 push 0x3F2
004012E8 FF75 08 push dword ptr [ebp+0x8]
004012EB B0 15 mov al, 0x15
004012ED FF15 64234000 call dword ptr [0x402364]
其中,程序中并没有0x3F3的ControlID,然后程序修改完以后没有返回,直接把标题改成失败了,这里我怀疑可能有BUG。为了达到注册成功的目的,这里我修改了一下程序。
首先,这个程序中有设置只读属性,导致我们无法直接修改这个程序,因此我们需要去掉这个只读属性。
去掉以后,使用FFI新建一个区段。
然后修改区段属性如下图所示:
添加完以后,发现程序有重定位表,去掉。
至此,就可以使用OD来修改程序了。
OD载入,在新区段中添加如下代码:
保存程序,文件名为crueme.exe
保存以后,重新载入程序,将0x004012C9处的代码修改成如下图所示的代码。
然后保存即可。
最后打开保存的文件,输入注册码,得到结果如下:
全文完。