WinAmp V2.11的注册机制分析(一)(初学者必读)
转载本文请注明出处,并保持文章的完整性。本文不得用于任何商业场合,仅供学习和参考。
作者: Fpc (感谢直接和间接提供帮助的所有朋友)
目的: 分析注册机制
工具: SI 4.05, Wdasm 8.93, Hedit 2.0
软件名称:WinAmp Ver 2.11
作 者: Nullsoft,Inc. Apr 13,1999
文件大小:612KB
编译器 :VC++ 6.0
授权方式:共享软件
使用平台:Win95/98
软件简介:著名的MP3播放器
软件来源:不清楚
下载地址:请到各大专的下载网站自己查找。
首先引用 WinAmp (WA) 关于中的一段:“Winamp is shareware, and may be previewed
for 14 days. Continued use of Winamp after 14 days requires that you register
it. winamp will neither cease functioning nor warn you of the end of the 14
day trial period. It is your responsibility to register.”。说的很明白,也是说到做到。共享软件作成这样,都不忍心去CRAK,它也的确没有任何加密、干扰手段,真是不知该说些什么。所以本文仅限于CRAK教学之用。
这个程序我看了三天(“当兵的,你不看我了”,“我都看了三天了”),其中有二天是在跟踪。程序写的很规范,也没有加壳,所以需要的只是耐心。现在让我给各位分析一下*COUGH*
1、 在WA菜单中选择“Nullsoft Winamp....”,显示“About”对话框,选“Shareware”->“Enter
Registration Info”进入注册页面。
2、 输入: Name = Fpc ,Reg# = 123456 。“OK”竟未被激活,应该是每输入一次就作一次判断。
3、 删除Reg#中的‘6’,^D到SI中,下 bpx hmemcpy(万能中断,也是我最常用的);
4、 F5回来,在Reg#后加‘6’,立刻中断到SI中,下 BD *阻断,按F12 N次,发现始终在USER中,最后直接就返回到WA中,有点奇怪;
5、 还是删除‘6’,^D到SI中,下 be hmemcpy 激活中断;
6、 再F5回来,仍输入‘6’,立刻中断到SI中。这次按F5,一次,二次,三次,返回到WA中。猜想第一次不知做什么,第二次取
RegisterName(Rn),第三次是取InputedRegisterCode(Irc),OK;
7、 重复上面5、6两步,不过这次在SI中按一次F5,N次F12,正确的回到WA领空,地址是 00401F28,向下看
@00401F2E是一个外部的 CALL调用(其实是GetDlgItemInt),是取 Irc的。再向下是一个 Call+ Cmp+ Jmp的程序段,这个
CALL 4252D1就是计算注册码的核心。要问我是怎么知道的,就是:静态分析+动态分析+尝试+耐心。
上面简要介绍了 SI动态拦截方式,若用 WDASM静态分析需要查找“Licensed to”字符串,它不在 String资源中
:-( ,而是在 DATA中找到。分析的角度不同,得到的结果是类似的,我们当然要多了解和尝试。这个串会在 @00401DA5 处找到,再向上还是对关键
Call 4252D1的调用。根据这一调用返回的注册成功与否,程序用 wsprintfA打印不同的字串:“LICENSED TO:%s(%d)”或“UNREGISTERED
SHAREWARE ......”,最后显示在About对话框中。整个这一段代码是在选择菜单的第一项时执行,向上翻页,记下的它的入口地址:401CCE。
关闭WA,打开SI的 Symbol Loader,调入WA,然后 Load,这时在SI设断:Bpx 401CCE。^D执行
WA。注意SI不会被激活,除非你选择了菜单的第一项,下面借助“程式”作分析,希望大家耐心看下去。
;程序段(1),在显示“About”时执行
* Referenced by a CALL at Addresses:
|:00401B9B , :00401CB1
|
:00401CCE 55
push ebp
:00401CCF 8BEC
mov ebp, esp
:00401CD1 81EC00060000 sub esp, 00000600
:00401CD7 A050914400 mov al,
byte ptr [00449150] <== 0->al
:00401CDC 53
push ebx
:00401CDD 56
push esi
:00401CDE 57
push edi
:00401CDF 888500FAFFFF mov byte ptr
[ebp+FFFFFA00], al <== Ebp-600=6DC6F8-600
:00401CE5 B9FF000000 mov ecx,
000000FF
:00401CEA 33C0
xor eax, eax
:00401CEC 8DBD01FAFFFF lea edi, dword
ptr [ebp+FFFFFA01] <== 准备将内存清0
:00401CF2 F3
repz
:00401CF3 AB
stosd
:00401CF4 66AB
stosw
* Reference To: KERNEL32.GetPrivateProfileStringA, Ord:013Ah
|
:00401CF6 8B3534E14300 mov esi, dword
ptr [0043E134] <== 准备功能调用
* Possible StringData Ref from Data Obj ->"winamp.ini"
<== 文件名
|
:00401CFC 687C214400 push
0044217C
:00401D01 AA
stosb
<== 到此将Ebp-600到Ebp-200清为0
:00401D02 8D8500FEFFFF lea eax, dword
ptr [ebp+FFFFFE00] <== 缓冲区1 首址(句柄?)
:00401D08 6800020000 push
00000200
<== 缓冲区长度为200
:00401D0D BB50914400 mov ebx,
00449150
:00401D12 50
push eax
* Possible StringData Ref from Data Obj ->"RegisteredTo"
|
:00401D13 BF6C214400 mov edi,
0044216C <== 读内容
:00401D18 53
push ebx
:00401D19 57
push edi
* Possible StringData Ref from Data Obj ->"WinampRegInfo"
|
:00401D1A 685C214400 push
0044215C <==
小节名
:00401D1F FFD6
call esi
这个Call将注册信息(RI)读到 Ebp-200处,后面以0补足至 Ebp。当然,WA没有注册时,RI是不存在的,自己输入的方式是:
在WIN9X系统目录下,找“winamp.ini”文件并打开(什么,没有?不会自己生成一个!),添加[WinampRegInfo]小节,输入RI。我的“winamp.ini”相关内容如下:
[WinampRegInfo]
RegisteredTo=Fpc!!!123456
注意RI中,Fpc是Rn,123456是Irc,中间的“!!!”是必须的,在下面的分析中你会找到原因。
:00401D21 80BD00FEFFFF00 cmp byte ptr [ebp+FFFFFE00],
00 <== 检查RI是否为空
:00401D28 7524
jne 00401D4E
<== 正确则向下跳
:00401D2A 68A0794500 push
004579A0
:00401D2F 8D8500FEFFFF lea eax, dword
ptr [ebp+FFFFFE00]
:00401D35 6800020000 push
00000200
:00401D3A 50
push eax
:00401D3B 53
push ebx
:00401D3C 57
push edi
:00401D3D FF35E0594500 push dword
ptr [004559E0]
:00401D43 FFD6
call esi
<== 不用关心是在读什么
:00401D45 80BD00FEFFFF00 cmp byte ptr [ebp+FFFFFE00],
00
:00401D4C 7466
je 00401DB4
<== 这里千万不能去
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401D28(C)
|
:00401D4E 8D8500FEFFFF lea eax, dword
ptr [ebp+FFFFFE00]<== 缓冲区1 首址
* Possible StringData Ref from Data Obj ->"!!!"
|
:00401D54 6858214400 push
00442158 <==
准备检查RI中是否含有"!!!"
:00401D59 50
push eax
:00401D5A E8F1330300 call
00435150 <==
这个Call有意思,错误则eax=0,正确则eax指向RI的!!!123456,我这里返回值是6DC4FB
:00401D5F 59
pop ecx
<== 对这个Call的分析见(二)
:00401D60 85C0
test eax, eax
:00401D62 59
pop ecx
:00401D63 744F
je 00401DB4
<== Eax=0就去死
:00401D65 802000
and byte ptr [eax], 00 <== 使得Rn以'\0'结束
:00401D68 33F6
xor esi, esi
:00401D6A 83C003
add eax, 00000003 <==
Eax指向Irc:123456
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401D84(U)
|
:00401D6D 8A08
mov cl, byte ptr [eax] <==
取一个字符
:00401D6F 80F930
cmp cl, 30
<== 数字为有效字符,非数字表示完成
:00401D72 7C12
jl 00401D86
:00401D74 80F939
cmp cl, 39
:00401D77 7F0D
jg 00401D86
:00401D79 0FBEC9
movsx ecx, cl
<== Ecx中无用位清0
:00401D7C 8D14B6
lea edx, dword ptr [esi+4*esi] <= Edx=5*Esi
:00401D7F 40
inc eax
<== Edx的初始值为0,这三条指令将字符串变为数字
:00401D80 8D7451D0
lea esi, dword ptr [ecx+2*edx-30] <= 很有意思
:00401D84 EBE7
jmp 00401D6D
这一段以C来表示:
char Irc[6]="123456" /* 当然注册码不止6位
*/
long Temp=0;
int i;
for( i=0; i<6; i++)
{
if( !isdigit( Irc[i] ) )
break;
Temp*=10;
Temp+=Irc[i]-'0';
}
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401D72(C), :00401D77(C)
|
:00401D86 8D8500FEFFFF lea eax, dword
ptr [ebp+FFFFFE00]<== Eax指向Rn
:00401D8C 50
push eax
:00401D8D E83F350200 call
004252D1 <==
!! 关键Call,由Rn计算得出Rc,保存在Eax。具体分析请看(三)
:00401D92 3BC6
cmp eax, esi
<== 比较Rc与Irc
:00401D94 59
pop ecx
:00401D95 751D
jne 00401DB4
<== 不相等则GAME OVER,大家将此处NOP掉,看会发生什么有趣的事情
:00401D97 50
push eax
:00401D98 8D8500FEFFFF lea eax, dword
ptr [ebp+FFFFFE00]
:00401D9E 50
push eax
:00401D9F 8D8500FAFFFF lea eax, dword
ptr [ebp+FFFFFA00]
* Possible StringData Ref from Data Obj ->"----- LICENSED TO: %s (%d) -----"
|
:00401DA5 6834214400 push
00442134
:00401DAA 50
push eax
* Reference To: USER32.wsprintfA, Ord:02ACh
|
:00401DAB FF1568E34300 Call dword
ptr [0043E368] <== 打印授权信息
:00401DB1 83C410
add esp, 00000010
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401D4C(C), :00401D63(C), :00401D95(C)
|
:00401DB4 80BD00FAFFFF00 cmp byte ptr [ebp+FFFFFA00],
00
:00401DBB 5F
pop edi
:00401DBC 5E
pop esi
:00401DBD 5B
pop ebx
:00401DBE 751C
jne 00401DDC
:00401DC0 6A00
push 00000000
:00401DC2 6A00
push 00000000
* Possible Reference to String Resource ID=00018: "UNREGISTERED SHAREWARE
VERSION - PLEASE REGISTER"
|
:00401DC4 6A12
push 00000012
:00401DC6 E8C5A10200 call
0042BF90
:00401DCB 50
push eax
:00401DCC 8D8500FAFFFF lea eax, dword
ptr [ebp+FFFFFA00]
:00401DD2 50
push eax
* Reference To: USER32.wsprintfA, Ord:02ACh
|
:00401DD3 FF1568E34300 Call dword
ptr [0043E368] <== 打印未授权信息
:00401DD9 83C414
add esp, 00000014 <==
向下很长一段与分析无关
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401DBE(C)
|
:00401DDC 8D8500FAFFFF lea eax, dword
ptr [ebp+FFFFFA00]
:00401DE2 50
push eax
* Possible Reference to Dialog: DialogID_009C, CONTROL_ID:0483, "License"
|
:00401DE3 6883040000 push
00000483
:00401DE8 FF7508
push [ebp+08]
* Reference To: USER32.SetDlgItemTextA, Ord:022Ch
|
:00401DEB FF1534E34300 Call dword
ptr [0043E334]
:00401DF1 C9
leave
:00401DF2 C3
ret
.........
* Possible StringData Ref from Data Obj ->"!!!"
|
:00401EAC 6858214400 push
00442158
:00401EB1 50
push eax
:00401EB2 E829330300 call
004351E0
:00401EB7 59
pop ecx
:00401EB8 8D8580FBFFFF lea eax, dword
ptr [ebp+FFFFFB80]
:00401EBE 59
pop ecx
:00401EBF 57
push edi
:00401EC0 50
push eax
:00401EC1 E8FA330300 call
004352C0
:00401EC6 59
pop ecx
:00401EC7 8D840580FBFFFF lea eax, dword
ptr [ebp+eax-00000480]
:00401ECE 50
push eax
:00401ECF 53
push ebx
:00401ED0 FF7508
push [ebp+08]
:00401ED3 FFD6
call esi
:00401ED5 8D8580FBFFFF lea eax, dword
ptr [ebp+FFFFFB80]
* Possible StringData Ref from Data Obj ->"winamp.ini"
|
:00401EDB 687C214400 push
0044217C
:00401EE0 50
push eax
* Possible StringData Ref from Data Obj ->"RegisteredTo"
|
:00401EE1 686C214400 push
0044216C
* Possible StringData Ref from Data Obj ->"WinampRegInfo"
|
:00401EE6 685C214400 push
0044215C
* Reference To: KERNEL32.WritePrivateProfileStringA, Ord:02E5h
|
:00401EEB FF1530E14300 Call dword
ptr [0043E130] <==
这一段会在点击“OK”时执行,向ini文件写RI
* Possible Reference to String Resource ID=00001: "You must first exit Winamp
before uninstalling."
|
:00401EF1 6A01
push 00000001
:00401EF3 FF7508
push [ebp+08]
* Reference To: USER32.EndDialog, Ord:00B9h
|
:00401EF6 FF151CE44300 Call dword
ptr [0043E41C]
:00401EFC 8B7D10
mov edi, dword ptr [ebp+10]
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401E6C(C)
|
* Possible Reference to Dialog: DialogID_00BF, CONTROL_ID:048B, ""
|
:00401EFF B88B040000 mov eax,
0000048B
:00401F04 663BF8
cmp di, ax
:00401F07 7405
je 00401F0E
:00401F09 663BFB
cmp di, bx
:00401F0C 7552
jne 00401F60
对关键 Call 004252D1的分析请见(三)
- 标 题:WinAmp V2.11的注册机制分析(一)(初学者必读) (12千字)
- 作 者:Fpc
- 时 间:2001-4-8 14:40:28
- 链 接:http://bbs.pediy.com