能力值:
( LV2,RANK:10 )
2 楼
Originally posted by netwind 【文章标题】: ty123 Crackme 6破解分析 【文章作者】: netwind 【下载地址】: http://bbs.pediy.com/attachment.php?s=&attachmentid=4377 【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教! -------------------------------------------------------------------------------- ........
好快的剑!祝贺netwind大侠!
源代码解压密码:Hola a todos, me llamo ty123
Until next time...
能力值:
( LV9,RANK:330 )
3 楼
最初由 netwind 发布 我们将0xff01+0x3c重复8次作为keyfile前0x10个字符
并不会都是0xff01,这个数是和硬盘序号有关的,将所得的硬盘序号各字节累加才得到这个数~!,如果你的硬盘序号是:0x4454BE8E,则0x44+0x54+0xBE+0x8e=0x01E4,该数为0xE401
能力值:
( LV9,RANK:330 )
4 楼
具体是这里:
......
0040104D . 6A 00 PUSH 0 ; /pFileSystemNameSize = NULL; Case 110 (WM_INITDIALOG) of switch 00401046
0040104F . 6A 00 PUSH 0 ; |pFileSystemNameBuffer = NULL
00401051 . 6A 00 PUSH 0 ; |pFileSystemFlags = NULL
00401053 . 6A 00 PUSH 0 ; |pMaxFilenameLength = NULL
00401055 . 8D45 FC LEA EAX, DWORD PTR SS:[EBP-4] ; |
00401058 . 50 PUSH EAX ; |pVolumeSerialNumber
00401059 . 6A 00 PUSH 0 ; |MaxVolumeNameSize = 0
0040105B . 6A 00 PUSH 0 ; |VolumeNameBuffer = NULL
0040105D . 68 08214000 PUSH 00402108 ; |c:\unregistered versionregistered user!
00401062 . E8 BF010000 CALL <JMP.&kernel32.GetVolumeInformat>; \GetVolumeInformationA
00401067 . 48 DEC EAX
00401068 . 6A 04 PUSH 4
0040106A . 59 POP ECX
0040106B . 33D2 XOR EDX, EDX
0040106D . 8D75 FC LEA ESI, DWORD PTR SS:[EBP-4]
00401070 > AC LODS BYTE PTR DS:[ESI]
00401071 . 03D0 ADD EDX, EAX
00401073 .^ E2 FB LOOPD SHORT 00401070
00401075 . 66:8915 59304>MOV WORD PTR DS:[403059], DX
......
能力值:
( LV2,RANK:10 )
5 楼
Originally posted by dewar 并不会都是0xff01,这个数是和硬盘序号有关的,将所得的硬盘序号各字节累加才得到这个数~!,如果你的硬盘序号是:0x4454BE8E,则0x44+0x54+0xBE+0x8e=0x01E4,该数为0xE401
dewar大侠说得有道理!
原本今天再发一个2004年写的Reverseme,结果提示一天只能发3个贴,控制太严了吧,呵呵。
再次感谢各位大侠关注。
能力值:
(RANK:520 )
6 楼
非常感谢赐教.
开始看到GetVolumeInformationA
这个函数了,在程序开头,没怎么留心它,
曾尝试找0xff01哪里算的,
我把断点下在了GetDlgItemTextA
每次到这 0xff01就已经算出来了,
我就以为是个固定的数字
感谢,加上这段才完整.
能力值:
( LV2,RANK:10 )
9 楼
netwind破这个估计花了不到30分钟吧。。。今天才看到,恰好没看到教程,所以就自己研究了一下。。。现把报告贴这里应该没问题吧。。。
请诸位大侠赐教。
有几年没有玩crack了,才发现已经很生疏了,于是找了一个简单的Crackme玩玩,没想到花了近3小时……
很多基础知识不熟悉……后来用C++写KeyGen也不顺利……汇编很简单的语言用c要用指针倒来倒去的……
下面是简单的报告:
拿到ty123 Crackme6.exe,先用IDA反汇编。
代码很清晰,很有可能是用Win32Asm写的。
看关键的处理对话框消息的函数:
.text:0040103D ; *************** S U B R O U T I N E ***************************************
.text:0040103D
.text:0040103D ; Attributes: bp-based frame
.text:0040103D
.text:0040103D ; BOOL __stdcall DialogFunc(HWND,UINT,WPARAM,LPARAM)
.text:0040103D DialogFunc proc near ; DATA XREF: start+Eo
.text:0040103D
.text:0040103D String = byte ptr -20h
.text:0040103D var_8 = dword ptr -8
.text:0040103D VolumeSerialNumber= dword ptr -4
.text:0040103D hDlg = dword ptr 8
.text:0040103D arg_4 = dword ptr 0Ch
.text:0040103D arg_8 = dword ptr 10h
.text:0040103D
.text:0040103D push ebp
.text:0040103E mov ebp, esp
.text:00401040 add esp, 0FFFFFFE0h
.text:00401043 mov eax, [ebp+arg_4]
.text:00401046 cmp eax, 110h
.text:0040104B jnz short loc_401081
.text:0040104B ;应该是初始化函数
.text:0040104D push 0 ; nFileSystemNameSize
.text:0040104F push 0 ; lpFileSystemNameBuffer
.text:00401051 push 0 ; lpFileSystemFlags
.text:00401053 push 0 ; lpMaximumComponentLength
.text:00401055 lea eax, [ebp+VolumeSerialNumber]
.text:00401058 push eax ; lpVolumeSerialNumber
.text:00401059 push 0 ; nVolumeNameSize
.text:0040105B push 0 ; lpVolumeNameBuffer
.text:0040105D push offset RootPathName ; "c:\\"
.text:00401062 call GetVolumeInformationA ;获得C盘的序列号(在格式化时被操作系统赋值)
.text:00401062
.text:00401067 dec eax
.text:00401068 push 4
.text:0040106A pop ecx
.text:0040106B xor edx, edx
.text:0040106D lea esi, [ebp+VolumeSerialNumber]
.text:0040106D
.text:00401070
.text:00401070 loc_401070: ; CODE XREF: DialogFunc+36j
.text:00401070 lodsb
.text:00401071 add edx, eax ;将序列号的4个字节相加
.text:00401073 loop loc_401070
.text:00401073
.text:00401075 mov word_403059, dx ;保存到这里
.text:0040107C jmp loc_401211
.text:0040107C
.text:00401081 ; ---------------------------------------------------------------------------
.text:00401081
.text:00401081 loc_401081: ; CODE XREF: DialogFunc+Ej
.text:00401081 cmp eax, 111h
.text:00401086 jnz loc_4011F7
.text:00401086
.text:0040108C mov eax, [ebp+arg_8] ;关于菜单
.text:0040108F and eax, 0FFFFh
.text:00401094 cmp eax, 3EFh
.text:00401099 jnz short loc_4010B4
.text:00401099
.text:0040109B push 40h ; uType
.text:0040109D push offset Caption ; "About..."
.text:004010A2 push offset Text ; "Simple crackme for newbie.\r\nYou have to"...
.text:004010A7 push [ebp+hDlg] ; hWnd
.text:004010AA call MessageBoxA
.text:004010AA
.text:004010AF jmp loc_401211
.text:004010AF
.text:004010B4 ; ---------------------------------------------------------------------------
.text:004010B4
.text:004010B4 loc_4010B4: ; CODE XREF: DialogFunc+5Cj
.text:004010B4 cmp eax, 3ECh
.text:004010B9 jnz loc_4011EC
.text:004010B9
.text:004010BF push 18h ;注册按钮
.text:004010C1 lea eax, [ebp+String]
.text:004010C4 push eax
.text:004010C5 call RtlZeroMemory ;将18h个单位的空间清零
.text:004010C5
.text:004010CA push 18h ; nMaxCount
.text:004010CC lea eax, [ebp+String]
.text:004010CF push eax ; lpString
.text:004010D0 push 3EBh ; nIDDlgItem
.text:004010D5 push [ebp+hDlg] ; hDlg
.text:004010D8 call GetDlgItemTextA ;获取文本框中的文字,保存到String变量
.text:004010D8
.text:004010DD test eax, eax ;如果文字长度为0
.text:004010DF jz loc_4011CC ;失败
.text:004010DF
.text:004010E5 cmp al, 12h ;是否大于12h=18
.text:004010E7 jg loc_4011CC ;若大于,失败
.text:004010E7
.text:004010ED lea esi, [ebp+String]
.text:004010F0 xchg eax, ecx
.text:004010F1 xor eax, eax
.text:004010F3 xor edx, edx
.text:004010F3
.text:004010F5
.text:004010F5 loc_4010F5: ; CODE XREF: DialogFunc+BBj
.text:004010F5 lodsb
.text:004010F6 add edx, eax
.text:004010F8 loop loc_4010F5 ;将输入的文字按字节相加
.text:004010F8
.text:004010FA mov word_40305B, dx ;保存于此
.text:00401101 lea esi, s_Ty123DfcgTy12 ; "ty123[DFCG]ty123"
.text:00401107 lea edi, unk_403011
.text:0040110D mov cl, 0Bh ;操作0Bh=11个字符
.text:0040110D
.text:0040110F
.text:0040110F loc_40110F: ; CODE XREF: DialogFunc+D6j
.text:0040110F lodsb
.text:00401110 xor al, [edi] ;把"ty123[DFCG]"的第i个字符和目标字符串的第i个数据做异或
.text:00401112 stosb
.text:00401113 loop loc_40110F ;通过OllyIce在此处下端点,知异或最后结果是playboy.dat
.text:00401113
.text:00401115 push offset unk_403078 ; int ;下面的函数返回文件长度于此
.text:0040111A lea eax, [ebp+var_8] ;返回文件内容于此
.text:0040111D push eax ; int
.text:0040111E push offset unk_403011 ; lpFileName
.text:00401123 call sub_4012C0 ;函数,打开playboy.dat
.text:00401123
.text:00401128 test eax, eax ;如果文件不存在
.text:0040112A jz loc_4011CC ;失败
.text:0040112A
.text:00401130 mov esi, [ebp+var_8]
.text:00401133 mov hMem, esi ;hMem=文件内容
.text:00401139 dec eax ;eax=0
.text:0040113A jnz loc_4011CC
.text:0040113A
.text:00401140 sub ecx, 30h ;减48
.text:00401143 jnz loc_4011CC ;若不等于0失败,所以文件长度=48
.text:00401143
.text:00401149 mov cl, 8
.text:0040114B push ecx
.text:0040114B
.text:0040114C
.text:0040114C loc_40114C: ; CODE XREF: DialogFunc+11Dj
.text:0040114C lodsw ;对每两个字符
.text:0040114E sub ah, 3Ch ;对第二个字符减3Ch=60
.text:00401151 cmp ax, word_403059 ;把两个字节拼起来,和这个变量比较(其实就是C盘的序列号)
.text:00401151
.text:00401158
.text:00401158 loc_401158: ; DATA XREF: DialogFunc+14Ar
.text:00401158 jnz short loc_4011CC ;不等于就失败
.text:00401158
.text:0040115A loop loc_40114C ;循环8次,也就是读取16个字符
.text:0040115A
.text:0040115C lea edi, s_Ty123DfcgTy12 ; "ty123[DFCG]ty123"
.text:00401162 lea ebx, s_RegisteredUse ; "Registered User!"
.text:00401168 mov cl, 10h
.text:00401168
.text:0040116A
.text:0040116A loc_40116A: ; CODE XREF: DialogFunc+136j
.text:0040116A lodsb
.text:0040116B xor al, [edi] ;把下一个输入字符挨个和"ty123[DFCG]ty123"做异或
.text:0040116B
.text:0040116D
.text:0040116D loc_40116D: ; DATA XREF: DialogFunc+155r
.text:0040116D cmp al, [ebx] ;判断是否等于"Registered User!"对应的字符
.text:0040116F jnz short loc_4011CC ;不等于,失败
.text:0040116F
.text:00401171 inc ebx
.text:00401172 inc edi
.text:00401173 loop loc_40116A ;一共16个字符
.text:00401173 ;这里可以算出这16个字符应该是什么
;只要把上面两个字符串相应位置作异或
.text:00401175 mov cl, 8
.text:00401175
.text:00401177
.text:00401177 loc_401177: ; CODE XREF: DialogFunc+148j
.text:00401177 lodsw ;下面16个字节,每两个字节一组
.text:00401179 sub ah, 2Ch ;高位减2Ch后
.text:0040117C cmp ax, word_40305B ;应该等于文本框输入字串和
.text:0040117C
.text:00401183
.text:00401183 loc_401183: ; DATA XREF: DialogFunc+161r
.text:00401183 jnz short loc_4011CC
.text:00401183
.text:00401185 loop loc_401177
.text:00401185
.text:00401187 mov eax, dword ptr ds:loc_401158
.text:0040118C xor dword ptr s_Ty123DfcgTy12+2, eax
.text:00401192 xor eax, dword ptr ds:loc_40116D
.text:00401198 xor dword ptr s_Ty123DfcgTy12+6, eax
.text:0040119E xor eax, dword ptr ds:loc_401183
.text:004011A4 xor dword ptr s_Ty123DfcgTy12+0Bh, eax
.text:004011AA push dword_40305D
.text:004011B0 push offset s_Ty123DfcgTy12 ; "ty123[DFCG]ty123"
.text:004011B5 push dword_403061
.text:004011BB push offset unk_40301D
.text:004011C0 call sub_401260 ;解密成功信息(具体怎么解密的不高兴看了)
.text:004011C0
.text:004011C5 push offset unk_40301D
.text:004011CA jmp short loc_4011D1
.text:004011CA
.text:004011CC ; ---------------------------------------------------------------------------
.text:004011CC
.text:004011CC loc_4011CC: ; CODE XREF: DialogFunc+A2j
略
.text:00401217
.text:00401217 DialogFunc endp
根据上面的分析,写keygen.cpp,g++编译通过。
#include <stdio.h>
#include <windows.h>
const char filename[]="playboy.dat";
const char str1[]="ty123[DFCG]ty123";
const char str2[]="Registered User!";
DWORD serial;
int main()
{
FILE *datf=fopen(filename, "wb");
if (!GetVolumeInformation("C:\\",0,0,&serial,0,0,0,0)) exit(1);
char *p = (char *)&serial;
unsigned short data1=0;
for (int i=0; i<4;i++)
data1 += (unsigned short)(unsigned char)*(p+i);
p = (char*)&data1;
*(p+1) += 0x3C;
for (int i=0;i<8;i++)
fprintf(datf, "%c%c",*p,*(p+1));
for (int i=0;i<16;i++)
fprintf(datf,"%c",str1[i] ^ str2[i]);
char name[19];
printf("Input your name(later enter this name to Crackme): ");
scanf("%s", &name);
unsigned short data2=0;
for (int i=0;name[i];i++) data2+=name[i];
p = (char*)&data2;
*(p+1) += 0x2C;
for (int i=0;i<8;i++)
fprintf(datf,"%c%c", *p, *(p+1));
return 0;
}