一、
PEID查看,是用汇编语言写的,所以直接看DialogBoxParamA的参数,找到消息循环。在消息循环里面找到0x111(WM_COMMAND),断下慢慢分析:
0040136A |> \3D 11010000 cmp eax,0x111
0040136F |. 75 4D jnz X004013BE
00401371 |. 8B45 10 mov eax,[arg.3] ; Case 111 (WM_COMMAND) of switch 0040134C
00401374 |. 83F8 6A cmp eax,0x6A
00401377 |. 75 28 jnz X004013A1
00401379 |. 8D3D 7A324000 lea edi,dword ptr ds:[<K1>]
0040137F |. 33C0 xor eax,eax
00401381 |. B9 10000000 mov ecx,0x10
00401386 |. F3:AA rep stos byte ptr es:[edi] ; 清0
00401388 |. 8D3D 1E304000 lea edi,dword ptr ds:[<K2>]
0040138E |. B9 10000000 mov ecx,0x10
00401393 |. F3:AA rep stos byte ptr es:[edi] ; 清0
00401395 |. E8 A9FCFFFF call 00401043
0040139A |. E8 97FDFFFF call 00401136
0040139F |. /EB 37 jmp X004013D8
二、分析:
call 00401043是取得用户名后,由它运算出一个长度最大为15的BYTE数组(K2),实际长度以用户名长度为准。
call 00401136比较复杂,先看前面:
00401136 /$ 6A 10 push 0x10 ; /Count = 10 (16.)
00401138 |. 68 5F314000 push offset <sn> ; |Buffer = offset <CM_One.sn>
0040113D |. 6A 67 push 0x67 ; |ControlID = 67 (103.)
0040113F |. FF35 5E324000 push dword ptr ds:[0x40325E] ; |hWnd = NULL
00401145 |. E8 E0020000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
0040114A |. 83F8 00 cmp eax,0x0
0040114D |. 0F84 85010000 je <EXIT>
00401153 |. 8BC8 mov ecx,eax ; 循环次数
00401155 |. 8D3D 1E304000 lea edi,dword ptr ds:[<K2>]
0040115B |. 8D35 5F314000 lea esi,dword ptr ds:[<sn>]
00401161 |> 8A06 /mov al,byte ptr ds:[esi] ; sn
00401163 |. 8A1F |mov bl,byte ptr ds:[edi] ; K2
00401165 |. D0EB |shr bl,1
00401167 02C3 add al,bl
00401169 8807 mov byte ptr ds:[edi],al ; K2
0040116B |. 46 |inc esi
0040116C |. 47 |inc edi
0040116D |.^ E2 F2 \loopd X00401161 ; F(K2,sn)
这里,取注册码,最大15位,然后和K2运算并更新K2。
40116F到4011BE是分别打开Windows.iso和WinXP.iso。
004011C8 |. B9 B2000000 mov ecx,0xB2 ; 178
004011CD |. 8D3D 1E304000 lea edi,dword ptr ds:[<K2>]
004011D3 |> 51 /push ecx
004011D4 |. 6A 00 |push 0x0 ; /pOverlapped = NULL
004011D6 |. 68 76324000 |push offset <返回数据长度> ; |pBytesRead = offset <CM_One.返回数据长度>
004011DB |. 6A 01 |push 0x1 ; |BytesToRead = 1
004011DD |. 68 16304000 |push offset <ReadBuff> ; |Buffer = offset <CM_One.ReadBuff>
004011E2 |. FF35 54304000 |push dword ptr ds:[<hFile1>] ; |hFile = NULL
004011E8 |. E8 1F020000 |call <jmp.&kernel32.ReadFile> ; \ReadFile读一个字节
004011ED |. 833D 76324000>|cmp dword ptr ds:[<返回数据长度>],0x0
004011F4 |. 75 05 |jnz X004011FB ; 数据不为0
004011F6 |. E9 DD000000 |jmp <EXIT>
004011FB |> 8D35 16304000 |lea esi,dword ptr ds:[<ReadBuff>]
00401201 |. 803F 00 |cmp byte ptr ds:[edi],0x0 ; K2数据不为0
00401204 |. 75 06 |jnz X0040120C
00401206 |. 8D3D 1E304000 |lea edi,dword ptr ds:[<K2>] ; 如果为0,指针循环到[0]
0040120C |> 8A06 |mov al,byte ptr ds:[esi] ; ReadBuff win.iso
0040120E |. 3207 |xor al,byte ptr ds:[edi] ; K2
00401210 |. A2 18304000 |mov byte ptr ds:[<WriteBuff>],al
00401215 |. 47 |inc edi
00401216 |. 6A 00 |push 0x0 ; /pOverlapped = NULL
00401218 |. 68 76324000 |push offset <返回数据长度> ; |pBytesWritten = offset <CM_One.返回数据长度>
0040121D |. 6A 01 |push 0x1 ; |nBytesToWrite = 1
0040121F |. 68 18304000 |push offset <WriteBuff> ; |Buffer = offset <CM_One.WriteBuff>
00401224 |. FF35 50304000 |push dword ptr ds:[<hFile2>] ; |hFile = NULL
0040122A |. E8 E3010000 |call <jmp.&kernel32.WriteFile> ; \WriteFile
0040122F |. 59 |pop ecx
00401230 |.^ E2 A1 \loopd X004011D3
这里是一个循环,次数为0xB2。读Windows.iso的字节,异或K2后,WinXP.iso。
00401232 |. 8D3D 40304000 lea edi,dword ptr ds:[<conKEY>]
00401238 |. 8D35 16304000 lea esi,dword ptr ds:[<ReadBuff>]
0040123E |. 8D1D 1E304000 lea ebx,dword ptr ds:[<K2>]
00401244 |. EB 59 jmp X0040129F
00401246 |> 6A 00 /push 0x0 ; /pOverlapped = NULL
00401248 |. 68 76324000 |push offset <返回数据长度> ; |pBytesRead = offset <CM_One.返回数据长度>
0040124D |. 6A 01 |push 0x1 ; |BytesToRead = 1
0040124F |. 68 16304000 |push offset <ReadBuff> ; |Buffer = offset <CM_One.ReadBuff>
00401254 |. FF35 54304000 |push dword ptr ds:[<hFile1>] ; |hFile = NULL
0040125A |. E8 AD010000 |call <jmp.&kernel32.ReadFile> ; \ReadFile
0040125F |. 833D 76324000>|cmp dword ptr ds:[<返回数据长度>],0x0
00401266 |. 74 37 |je X0040129F
00401268 |. 803F 00 |cmp byte ptr ds:[edi],0x0
0040126B |. 75 0C |jnz X00401279
0040126D |. 8D1D 1E304000 |lea ebx,dword ptr ds:[<K2>]
00401273 |. 8D3D 40304000 |lea edi,dword ptr ds:[<conKEY>]
00401279 |> 8A06 |mov al,byte ptr ds:[esi] ; ReadBuff
0040127B |. 3207 |xor al,byte ptr ds:[edi] ; conKEY
0040127D |. 3203 |xor al,byte ptr ds:[ebx] ; K2
0040127F |. A2 16304000 |mov byte ptr ds:[<ReadBuff>],al
00401284 |. 47 |inc edi
00401285 |. 43 |inc ebx
00401286 |. 6A 00 |push 0x0 ; /pOverlapped = NULL
00401288 |. 68 76324000 |push offset <返回数据长度> ; |pBytesWritten = offset <CM_One.返回数据长度>
0040128D |. 6A 01 |push 0x1 ; |nBytesToWrite = 1
0040128F |. 68 16304000 |push offset <ReadBuff> ; |Buffer = offset <CM_One.ReadBuff>
00401294 |. FF35 50304000 |push dword ptr ds:[<hFile2>] ; |hFile = NULL
0040129A |. E8 73010000 |call <jmp.&kernel32.WriteFile> ; \WriteFile
0040129F |> 833D 76324000> cmp dword ptr ds:[<返回数据长度>],0x0
004012A6 |.^ 75 9E \jnz X00401246
这里是读Windows.iso的字节,异或K2,异或一个常量(conKEY)后,写入WinXP.iso。
004012A8 |. FF35 50304000 push dword ptr ds:[<hFile2>] ; /hObject = NULL
004012AE |. E8 2F010000 call <jmp.&kernel32.CloseHandle> ; \CloseHandle
004012B3 |. FF35 54304000 push dword ptr ds:[<hFile1>] ; /hObject = NULL
004012B9 |. E8 24010000 call <jmp.&kernel32.CloseHandle> ; \CloseHandle
004012BE |. 68 00304000 push 00403000 ; /FileName = "WinXP.iso"
004012C3 |. E8 3E010000 call <jmp.&kernel32.LoadLibraryA> ; \LoadLibraryA
004012C8 |. 50 push eax ; /hLibModule
004012C9 |. E8 2C010000 call <jmp.&kernel32.FreeLibrary> ; \FreeLibrary
004012CE |. 68 00304000 push 00403000 ; /FileName = "WinXP.iso"
004012D3 |. E8 16010000 call <jmp.&kernel32.DeleteFileA> ; \DeleteFileA
004012D8 >\> C3 retn
这里关闭文件句柄,用LoadLibraryA打开WinXP.iso,然后关闭并删除它。可见WinXP.iso应该是一个DLL。DLL是PE格式,根据PE格式头部都是固定的原理,得出公式:
N1=F1(name);
K2=F2(name,N1);
K2=sn+(K2>>1);
PE=K2^iWindows.iso;
以上PE和Windows.iso已知,所以可得K2,把K2代入K2=sn+(K2>>1);就可以得SN了。
K=PE^Windows.iso;
sn=K-(K2>>1);
三、实际问题
PE文件在格式上是统一的,但格式内的数据略有不同来看看两个PE文件:
00000000h: 4D 5A 50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 ; MZP...........
00000010h: B8 00 00 00 00 00 00 00 40 00 1A 00 00 00 00 00 ; ?......@.......
00000020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 ; ................
00000040h: BA 10 00 0E 1F B4 09 CD 21 B8 01 4C CD 21 90 90 ; ?...???L?悙
00000050h: 54 68 69 73 20 70 72 6F 67 72 61 6D 20 6D 75 73 ; This program mus
00000060h: 74 20 62 65 20 72 75 6E 20 75 6E 64 65 72 20 57 ; t be run under W
00000070h: 69 6E 33 32 0D 0A 24 37 00 00 00 00 00 00 00 00 ; in32..$7........
00000000h: 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 ; MZ?..........
00000010h: B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ; ?......@.......
00000020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 F0 00 00 00 ; ............?..
00000040h: 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ; ..?.???L?Th
00000050h: 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F ; is program canno
00000060h: 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 ; t be run in DOS
00000070h: 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 ; mode....$.......
以上两个PE文件就差哪么一点,特别是IMAGE_DOS_HEADER的e_cblp(第3,4个字节)。如果用前16位来异或Windows.iso可能会出现错误。注意观察会发现“50h”这一行16位基本不变。所以就好以这16个字节进行异或,得出K。
PE-50h: 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F
Windows.iso-50h: 25 E2 7D 26 F9 C6 04 27 2D FC 7D 35 EA C7 0D 3A 异或运算
结果: 4C 91 5D 56 8B A9 63 55 4D 91 5D 56 8B A9 63 55
可见,K为8位,所以sn也为8位。
四、注册机源码:
// CM_One_REG.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <wtypes.h>
DWORD g_N1=0;
BYTE g_K1[16],g_K2[16];
const int g_KEY1LEN=16;
const BYTE g_KEY1[8]={0x4C,0x91,0x5D,0x56,0x8B,0xA9,0x63,0x55};
const BYTE g_KEY2[8]={0x26, 0x32, 0x43, 0x1A, 0x2C, 0xB3, 0x5F, 0x42};
void call_0040104390(char *name)
{
g_N1=0;
ZeroMemory(g_K1,16);
ZeroMemory(g_K2,16);
int len=strlen(name),loop=0xf,i=0,temp=0;
// g_N1=(name[0]%name[1]);
// g_N1*=name[2];
// g_N1++;
// g_N1=0xFFFFFFFF/g_N1;
g_N1=0xFFFFFFFF/((name[0]%name[1])*name[2]+1);
while(loop--)
{
g_N1*=0x342ab;
g_N1+=0x269EC3;
g_K1[i]=(char)(((g_N1>>0x10)&0x7FFF)%0x10)+0x4b;
i++;
}
loop=len;
i=0;
while(loop--)
{
temp=(name[i]>>5)*0x7B;
if (temp>0)
{
while(temp--)
{
g_N1=(g_N1*0x123CD)+0x775851;
}
}
g_N1=(g_N1*0x123CD)+0x269EC3;
g_K2[i]=(char)(g_N1>>0x10)&0x7FFF;//
i++;
}
}
void call_00401136(char *sn)
{
int len=strlen(sn),loop=0,i=0,temp=0;
if (len>8)
{
printf("sn Error!");
return;
}
loop=len;
i=0;
while(loop--)
{
g_K2[i]=(g_K2[i]>>1)+sn[i];
i++;
}
}
BYTE *GetSN(char *name)
{
call_0040104390(name);
BYTE out[10]={0};
BYTE j=0;
printf("SN:");
for (int i=0;i<8;i++)
{
//j=((BYTE)g_K2[i])>>1;
out[i]=g_KEY1[i]-(g_K2[i]>>1);
//printf("%d:%d - %02X\n",i+1,out[i],out[i],out[i]);
printf("%02X ",out[i]);
}
return out;
}
// BYTE *GetN1(char *sn)
// {
// if (strlen(sn)!=8)
// {
// return NULL;
// }
// BYTE out[8]={0};
// int i=0,j=0,k=0;
// for(i=0;i<8;i++)
// {
// k=g_KEY1[i]-sn[1];
// for (j=0;j<0x100;j++)
// {
// if ((j>>1)==k)
// {
// break;
// }
// }
// out[i]=j;
// }
// return out;
// }
int main(int argc, char* argv[])
{
//printf("SN:");
IMAGE_DOS_HEADER
GetSN("lili");
return 0;
}
五、总结
1.了解PE文件格式是关键
2.算出来的sn有时为不可见字符,要怎样输入呢?我只知道用UltraEdit输入16进制数值后,转换成字符模式再复制粘贴。