WinAmp V2.11的注册机制分析(一)(初学者必读) (12千字)
WinAmp V2.11的注册机制分析(一)(初学者必读)转载本文请注明出处,并保持文章的完整性。本文不得用于任何商业场合,仅供学习和参考。
目的: 分析注册机制
工具: 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的分析请见(三)
