winamp V2.02
程式猎人
简介:这个软件不用多说了,大家一定对它很熟悉了。
追踪:name:dahuilang
RN:01234567
以前追踪过这个程序,当时追踪的版本已经忘记了,但是那时记得追踪它很简单就将它追
踪出来了,因为只要设bpx hmemcpy后拦下后跳跃到程序中就可以了,没有想到这个版本的
程序无法跳跃到正常程序中,那么如何追踪这个程序呢?
先使用反汇编软件W32对它进行反汇编吧。
:0041C11C 50 push eax
:0041C11D 52 push edx
* Possible StringData Ref from Data Obj ->"RegisteredTo"
|
:0041C11E 683C504300 push 0043503C
:0041C123 FF35A43F4400 push dword ptr [00443FA4]
* Reference To: KERNEL32.GetPrivateProfileStringA, Ord:0111h
|
:0041C129 FF1578754400 Call dword ptr [00447578]
:0041C12F 389D00FDFFFF cmp byte ptr [ebp+FFFFFD00], bl
:0041C135 7467 je 0041C19E
* Possible StringData Ref from Data Obj ->"!!!"
|
:0041C137 6838504300 push 00435038
:0041C13C 8D8500FDFFFF lea eax, dword ptr [ebp+FFFFFD00]
:0041C142 50 push eax
:0041C143 E888AC0000 call 00426DD0
:0041C148 83C408 add esp, 00000008
:0041C14B 85C0 test eax, eax
:0041C14D 744F je 0041C19E
:0041C14F C745FCFFFFFFFF mov [ebp-04], FFFFFFFF
:0041C156 8D4DFC lea ecx, dword ptr [ebp-04]
:0041C159 8818 mov byte ptr [eax], bl
:0041C15B 51 push ecx
:0041C15C 83C003 add eax, 00000003
* Possible StringData Ref from Data Obj ->"%d"
|
:0041C15F 68C0234300 push 004323C0
:0041C164 50 push eax
:0041C165 E8A6AD0000 call 00426F10
:0041C16A 83C40C add esp, 0000000C
:0041C16D 8D8D00FDFFFF lea ecx, dword ptr [ebp+FFFFFD00]
:0041C173 51 push ecx
:0041C174 E83E670000 call 004228B7
:0041C179 83C404 add esp, 00000004
:0041C17C 3945FC cmp dword ptr [ebp-04], eax
:0041C17F 751D jne 0041C19E
:0041C181 50 push eax
:0041C182 8D8D00FDFFFF lea ecx, dword ptr [ebp+FFFFFD00]
:0041C188 51 push ecx
:0041C189 8D9500F9FFFF lea edx, dword ptr [ebp+FFFFF900]
* Possible StringData Ref from Data Obj ->"----- 授 权 给 : %s (%d) -----"
|
:0041C18F 68D8514300 push 004351D8
:0041C194 52 push edx
* Reference To: USER32.wsprintfA, Ord:0262h
|
:0041C195 FF1554774400 Call dword ptr [00447754]
:0041C19B 83C410 add esp, 00000010
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0041C135(C), :0041C14D(C), :0041C17F(C)
|
:0041C19E 80BD00F9FFFF00 cmp byte ptr [ebp+FFFFF900], 00
:0041C1A5 7515 jne 0041C1BC
我首先看到了这个字符串,"----- 授 权 给 : %s (%d) -----",在它上面有一个比
较的地方,那么程序一定是比较注册码或注册旗标吧,GetPrivateProfileStringA大家看到
这个函数了吧,那么这个函数是调用ini文件的(通常是这样的),现在使用filemon来查看
一下程序调用了什么ini文件呢?
查看后发现程序调用了在winamp.exe同目录下的winamp.ini文件,这里的内容如下:
[Winamp]
……
RegisteredTo=
分析后可知,程序是这样将注册码和名字放在一起的,为name!!!RN。这个格式,因为可
以从上面看到程序使用了!!!。好了,现在是设在
RegisteredTo=dahuilang!!!01234567,然后在下面
:0041C17C 3945FC cmp dword ptr [ebp-04], eax
设断了,下bpx 0041C17C 后运行程序,结果发现在[ebp-04]=0012d687,而eax=4E0C8C6,
这样就可以得到注册码了。
RN=4E0C8C6=81840326
这个就是dahuilang的注册码了。OK,这个版本的注册码破解就算结束了。
Winamp 2.04
启动winamp.exe
1. 把开关于”wiamp”,选Shareware.
2. 按 Enter Registration Info
3. Name: Peter Reg#:78787878
4. Ctrl+N
5. bpx getdlgitemtexta
6. 按 x 返回 再输入一个数学(我用”9”)
7. 又被拦阻,到一个”User32!Getdlgitemtexta
8. F10,22次
9. 你会看见 0167:0041EF81 PUSH EAX
10. 我用 d eax 一看有 Peter我字眼,我就定比较地方一定在不远的地方。
11. 本人汇编不好,只是知道Push eax。大概是输入字符到eax中。要输入字符不
就是要用的名字,d了一下果然是。
12. 再往下看:
0167:0041EF82 CALL 0042218 (也许是比比较的地方)
0167:0041EF87 ADD ESP,BYTE+04
0167:0041EF8A CMP EAX,ESI
0167:0041EF8C JNZ 0041EF97
13. 想一下刚才我破解winamp skin maker v1.2时,不是Sun Bird翻译了v1.05资料中的
14. ”CMP EAX,ESI” 不是用d而用?
15. 试一试:
16. ? EAX
17. DEC=67972642(会不会是注册码?)
18. HEX=40d2e22
19. ? EDI
20. DEC=787878789(不是我刚才用的假码?)
21. HEX=2ef61385
22. CMP EAX,ESI(不就是两个记存器比较?要比较就一定要有注册码吗?)
23. 好试一下,果然是。Yeah!
24. 我再试一下进入0167:0041EF82 CALL 0042218 (也许是比比较的地方)
25. 可能本人技术不行,只能白作工。
这才是本人自己破解的第一个软件,多谢我的师傅和Sun Bird大哥帮助!
我是用手打的,0167:0041EF82 CALL 0042218
……….
0167:0041EF8C JNZ 0041EF97
用trw2000 or soft-ice,能不能copy出来和抓图?
说明一下这里的破解过程以后出现Peter也就是我本人PeterChen!当然这篇也
不列外?
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的注册机制分析(二)(初学者必读)
转载本文请注明出处,并保持文章的完整性。本文不得用于任何商业场合,仅供学习和参考。
作者: Fpc (感谢直接和间接提供帮助的所有朋友)
目的: 分析注册机制
工具: SI 4.05, Wdasm 8.93, Hedit 2.0
程序段(2),输入注册信息的每次输入时执行:根据Rn计算Rc,若Irc=Rc则激活“OK”表示可以将RI写入“winamp.ini”文件中
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401F07(C)
|
:00401F0E C1EF10 shr edi, 10
:00401F11 6681FF0003 cmp di, 0300
:00401F16 7548 jne 00401F60
:00401F18 8B7D08 mov edi, dword ptr [ebp+08]
:00401F1B 8D4D80 lea ecx, dword ptr [ebp-80]
:00401F1E 6880000000 push 00000080
:00401F23 51 push ecx
:00401F24 50 push eax
:00401F25 57 push edi
:00401F26 FFD6 call esi <== 读Name文本框,将取得的Rn保存在 Ebp-80处
:00401F28 6A00 push 00000000
:00401F2A 6A00 push 00000000
:00401F2C 53 push ebx
:00401F2D 57 push edi
* Reference To: USER32.GetDlgItemInt, Ord:0103h
|
:00401F2E FF152CE34300 Call dword ptr [0043E32C] <== 读Reg#文本框,返回值在 Eax中
:00401F34 8BF0 mov esi, eax
:00401F36 8D4580 lea eax, dword ptr [ebp-80]
:00401F39 50 push eax
:00401F3A E892330200 call 004252D1 <== !! 关键Call,由Rn计算得出Rc,保存在Eax。具体分析请看(三)
:00401F3F 3BC6 cmp eax, esi <== 比较Rc与Irc
:00401F41 59 pop ecx
:00401F42 750A jne 00401F4E
:00401F44 807D8000 cmp byte ptr [ebp-80], 00
:00401F48 7404 je 00401F4E
* Possible Reference to String Resource ID=00001: "You must first exit Winamp before uninstalling."
|
:00401F4A 6A01 push 00000001 <== “OK”为Enable
:00401F4C EB02 jmp 00401F50
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401F42(C), :00401F48(C)
|
:00401F4E 6A00 push 00000000 <== “OK”为Disable
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401F4C(U)
|
* Possible Reference to String Resource ID=00001: "You must first exit Winamp before uninstalling."
|
:00401F50 6A01 push 00000001
:00401F52 57 push edi
* Reference To: USER32.GetDlgItem, Ord:0102h
|
:00401F53 FF1574E34300 Call dword ptr [0043E374]
:00401F59 50 push eax
* Reference To: USER32.EnableWindow, Ord:00B7h
|
:00401F5A FF1530E34300 Call dword ptr [0043E330] <== 修改按钮状态
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401E43(C), :00401F0C(C), :00401F16(C)
|
:00401F60 33C0 xor eax, eax
:00401F62 5B pop ebx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401E36(U)
|
:00401F63 5F pop edi
:00401F64 5E pop esi
:00401F65 C9 leave
:00401F66 C21000 ret 0010
Call 435150 比较在RI中是否含有"!!!"
入口:Ecx指向"!!!"+'\0'
出口:错误则eax=0,正确则eax指向RI的!!!123456,
* Referenced by a CALL at Addresses:
| 有N处调用,我用了 Echo y|Format C:/u
:00435150 8B4C2408 mov ecx, dword ptr [esp+08] <== Ecx=442158,指向"!!!"+'\0'
:00435154 57 push edi
:00435155 53 push ebx
:00435156 56 push esi
:00435157 8A11 mov dl, byte ptr [ecx] <== Dl取得第一个'!'
:00435159 8B7C2410 mov edi, dword ptr [esp+10] <== Edi=Ebp-200=6DC4F8,即RI首址
:0043515D 84D2 test dl, dl
:0043515F 7469 je 004351CA <== Dl=0证明比较完成,跳下去准备返回(怎么可能?!)
:00435161 8A7101 mov dh, byte ptr [ecx+01] <== 第二个'!'到Dh
:00435164 84F6 test dh, dh
:00435166 744F je 004351B7
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004351A2(C), :004351B5(U)
|
:00435168 8BF7 mov esi, edi
:0043516A 8B4C2414 mov ecx, dword ptr [esp+14] <== Ecx=442158,指向"!!!"+'\0'
:0043516E 8A07 mov al, byte ptr [edi]
:00435170 46 inc esi
:00435171 38D0 cmp al, dl <== 看Al是否为'!'
:00435173 7415 je 0043518A
:00435175 84C0 test al, al
:00435177 740B je 00435184 <== Al为空则失败
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435182(C)
|
:00435179 8A06 mov al, byte ptr [esi]
:0043517B 46 inc esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043518F(C)
|
:0043517C 38D0 cmp al, dl
:0043517E 740A je 0043518A <== 向下跳检查RI中是否含三个'!'
:00435180 84C0 test al, al
:00435182 75F5 jne 00435179 <== 直到Al取到'!';若不跳则说明在遇到"!!!"之前,RI已经结束(RI中不含!!!),失败
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435177(C)
|
:00435184 5E pop esi
:00435185 5B pop ebx
:00435186 5F pop edi
:00435187 33C0 xor eax, eax
:00435189 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00435173(C), :0043517E(C)
|
:0043518A 8A06 mov al, byte ptr [esi] <== 从RI中!之后再读一字节
:0043518C 46 inc esi
:0043518D 38F0 cmp al, dh
:0043518F 75EB jne 0043517C <== 若不等, 则在Rn中含'!'
:00435191 8D7EFF lea edi, dword ptr [esi-01]
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004351B3(C)
|
:00435194 8A6102 mov ah, byte ptr [ecx+02]
:00435197 84E4 test ah, ah
:00435199 7428 je 004351C3
:0043519B 8A06 mov al, byte ptr [esi] <== 从Rn中取第三个'!'
:0043519D 83C602 add esi, 00000002
:004351A0 38E0 cmp al, ah
:004351A2 75C4 jne 00435168
:004351A4 8A4103 mov al, byte ptr [ecx+03] <== Al应取到0
:004351A7 84C0 test al, al
:004351A9 7418 je 004351C3 <== Al为空表示正常结束
:004351AB 8A66FF mov ah, byte ptr [esi-01]
:004351AE 83C102 add ecx, 00000002 <== 若正确,这里不会被执行
:004351B1 38E0 cmp al, ah
:004351B3 74DF je 00435194
:004351B5 EBB1 jmp 00435168
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435166(C)
|
:004351B7 33C0 xor eax, eax <== 跳到这里则RI形式错误
:004351B9 5E pop esi
:004351BA 5B pop ebx
:004351BB 5F pop edi
:004351BC 8AC2 mov al, dl
:004351BE E933210000 jmp 004372F6
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00435199(C), :004351A9(C)
|
:004351C3 8D47FF lea eax, dword ptr [edi-01] <== 正确的出口,我是这里返回的
:004351C6 5E pop esi
:004351C7 5B pop ebx
:004351C8 5F pop edi
:004351C9 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043515F(C)
|
:004351CA 8BC7 mov eax, edi <== 也是正确出口
:004351CC 5E pop esi
:004351CD 5B pop ebx
:004351CE 5F pop edi
:004351CF C3 ret
上面跳来跳去的有些乱(上跳下跳,左跳右跳,原来每个条件都不一般),不知大家看懂没有,找源码跟一跟就明白啦!
对关键 Call 004252D1的分析请见(三)
WinAmp V2.11的注册机制分析(三)(初学者必读)
转载本文请注明出处,并保持文章的完整性。本文不得用于任何商业场合,仅供学习和参考。
作者: Fpc (感谢直接和间接提供帮助的所有朋友)
目的: 分析注册机制
工具: SI 4.05, Wdasm 8.93, Hedit 2.0
下面是CALL 004252D1的核心程序,00401D8D是打开菜单“Nullsoft WinAmp...”(即“关于”)时的调用,第二次是输入注册信息时的调用,第三次我也不清楚。
这段程序负责由RegisterName(Rn)计算出RegisterCode(Rc),放到Eax中。
* Referenced by a CALL at Addresses:
|:00401D8D , :00401F3A , :004254FE
|
:004252D1 55 push ebp
:004252D2 8BEC mov ebp, esp
:004252D4 81EC00010000 sub esp, 00000100
:004252DA 53 push ebx
:004252DB 56 push esi
:004252DC 57 push edi
:004252DD 6A3F push 0000003F
:004252DF 33DB xor ebx, ebx
:004252E1 59 pop ecx <== Ecx=3F
:004252E2 209D00FFFFFF and byte ptr [ebp+FFFFFF00], bl
:004252E8 33C0 xor eax, eax
:004252EA 8DBD01FFFFFF lea edi, dword ptr [ebp+FFFFFF01]
:004252F0 6800010000 push 00000100
:004252F5 F3 repz
:004252F6 AB stosd
:004252F7 FF7508 push [ebp+08]
:004252FA 66AB stosw
:004252FC AA stosb <== 从4252E2到此将 Ebp-100到Ebp处清0
:004252FD 8D8500FFFFFF lea eax, dword ptr [ebp+FFFFFF00]
:00425303 50 push eax
:00425304 E8E7010100 call 004354F0 <== 这次调用将Rn复制到Ebp-100,见分析。
:00425309 83C40C add esp, 0000000C
:0042530C 389D00FFFFFF cmp byte ptr [ebp+FFFFFF00], bl
:00425312 7428 je 0042533C
:00425314 8DB500FFFFFF lea esi, dword ptr [ebp+FFFFFF00]
:0042531A 33FF xor edi, edi
<== 至此,入口: Edi=Ebx=0
* Referenced by a (U)nconditional or (C)onditional Jump at Address: <== !! 这个循环计算中间结果,很关键
|:0042533A(C)
|
:0042531C 56 push esi
:0042531D E861000000 call 00425383 <== 从Esi开始取4字节,作运算,结果在Eax中。请看下面
:00425322 59 pop ecx
:00425323 8BCF mov ecx, edi <== 随着循环,Edi=0、2,4,6,8,A...
:00425325 83E10F and ecx, 0000000F
:00425328 D3E0 shl eax, cl <== 左移位
:0042532A 85C0 test eax, eax
:0042532C 7C04 jl 00425332 <== 是负数则向下跳
:0042532E 03D8 add ebx, eax <== 很少被执行到
:00425330 EB02 jmp 00425334 <== 循环次数为Rn长度
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042532C(C)
|
:00425332 2BD8 sub ebx, eax <== Ebx放累加结果
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425330(U)
|
:00425334 47 inc edi
:00425335 47 inc edi <== Edi=Edi+2
:00425336 46 inc esi <== Esi取Rn的下一个字节
:00425337 803E00 cmp byte ptr [esi], 00 <== 检查是否结束
:0042533A 75E0 jne 0042531C <== 未完则继续,循环次数为Rn长度
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425312(C)
|
:0042533C 8BC3 mov eax, ebx <== 中间结果送 Eax
:0042533E B940420F00 mov ecx, 000F4240 <== Ecx= 1,000,000(Dec)
:00425343 6BC025 imul eax, 00000025 <== Eax= Eax*37(Dec)
:00425346 99 cdq
:00425347 F7F9 idiv ecx
:00425349 8BCA mov ecx, edx <== 余数送Ecx保存,很重要(我跟到下面要返回的语句,向回找了半天才找到)
:0042534B 8BF2 mov esi, edx <== 余数送Esi,到 425352继续处理
:0042534D 6BC964 imul ecx, 00000064 <== Ecx=Ecx*100(Dec)
:00425350 33FF xor edi, edi
* Referenced by a (U)nconditional or (C)onditional Jump at Address: <== 到 425369的这个循环会被执行几次
|:00425369(U)
|
:00425352 85F6 test esi, esi <== Esi是否为0
:00425354 7415 je 0042536B <== 是则跳出这一循环
:00425356 8BC6 mov eax, esi <== Esi->Eax,作处理准备
:00425358 6A64 push 00000064
:0042535A 99 cdq
:0042535B 5B pop ebx
:0042535C F7FB idiv ebx <== 被100(Dec)除
:0042535E 8BC6 mov eax, esi
:00425360 53 push ebx
:00425361 5E pop esi
:00425362 03FA add edi, edx <== Edi放余数累加和
:00425364 99 cdq
:00425365 F7FE idiv esi
:00425367 8BF0 mov esi, eax <== 商被送到Esi
:00425369 EBE7 jmp 00425352
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425354(C)
|
:0042536B 8D047F lea eax, dword ptr [edi+2*edi] <== Eax=Edi*3
|
:0042536E 6A04 push 00000004
:00425370 99 cdq
:00425371 5E pop esi
:00425372 F7FE idiv esi <== 再被4除
|
:00425374 6A64 push 00000064
:00425376 5E pop esi
:00425377 5F pop edi
:00425378 99 cdq
:00425379 F7FE idiv esi <== 再被100除
:0042537B 5E pop esi
:0042537C 5B pop ebx
:0042537D 8BC2 mov eax, edx <== 余数送 Eax
:0042537F 03C1 add eax, ecx <== Eax= Eax+Ecx (看42534D),这是最终结果
:00425381 C9 leave
:00425382 C3 ret
* Referenced by a CALL at Address:
|:0042531D
|
:00425383 55 push ebp
:00425384 8BEC mov ebp, esp
:00425386 51 push ecx
:00425387 8065FC00 and byte ptr [ebp-04], 00
:0042538B 57 push edi
:0042538C 33C0 xor eax, eax
:0042538E 8D7DFD lea edi, dword ptr [ebp-03]
:00425391 66AB stosw
:00425393 6A04 push 00000004
:00425395 FF7508 push [ebp+08] <== 源字符串地址压栈
:00425398 AA stosb <== 从Ebp-04处建立了4字节的临时缓冲区
:00425399 8D45FC lea eax, dword ptr [ebp-04] <==
:0042539C 50 push eax <== 目标区地址压栈
:0042539D E84E010100 call 004354F0 <== 又一次对这个CALL的调用,不过这次是从Esi处取 4 字节到临时缓冲区,我在这里转的头晕,大家可以看一看。
:004253A2 0FB645FE movzx eax, byte ptr [ebp-02] <== 相当于 al=[Esi+2],高位是0,下同
:004253A6 0FB64DFC movzx ecx, byte ptr [ebp-04] <== cl=[Esi]
:004253AA 0FB655FD movzx edx, byte ptr [ebp-03] <== dl=[Esi+1]
:004253AE 33C1 xor eax, ecx <== Al^=Cl
:004253B0 83C40C add esp, 0000000C
:004253B3 0FB64DFF movzx ecx, byte ptr [ebp-01] <== cl=[Esi+3]
:004253B7 F7D1 not ecx <== !Ecx
:004253B9 F7D0 not eax <== !Eax
:004253BB 33CA xor ecx, edx <== Cl^=Dl
:004253BD 5F pop edi
:004253BE 03C1 add eax, ecx <== Eax+=Ecx
:004253C0 C9 leave
:004253C1 C3 ret
call 004354F0,这个CALL是从源字符串区向目标缓冲区复制字串,长度由调用前的压栈决定;出口:将目标缓冲区地址送 Eax。
里面跳转太多,看不明白关系不大,它没有别的功能。
* Referenced by a CALL at Addresses:
|:00403EF8 , :00410333 , :00410517 , :00425304 , :0042539D
|:00425B3F , :00425BF1 , :004294AF , :0042951D , :00439DCA
|
:004354F0 8B4C240C mov ecx, dword ptr [esp+0C] <== 调用此Call前的倒数第三次Push,为100或4
:004354F4 57 push edi
:004354F5 85C9 test ecx, ecx
:004354F7 747A je 00435573 <== 两处调用都不跳
:004354F9 56 push esi
:004354FA 53 push ebx
:004354FB 8BD9 mov ebx, ecx <==
:004354FD 8B742414 mov esi, dword ptr [esp+14] <== !! Esi为源字串地址,注意 @42539D调用时,每次都有Esi=Esi+1
:00435501 F7C603000000 test esi, 00000003 <== Esi低字节是否为03
:00435507 8B7C2410 mov edi, dword ptr [esp+10] <== !! 调用此Call前的最后一次Push,均为Eax,是目标地址
:0043550B 7507 jne 00435514 <== 大多数情况下不会跳
:0043550D C1E902 shr ecx, 02
:00435510 756F jne 00435581 <== 会跳走
:00435512 EB21 jmp 00435535
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0043550B(C), :00435527(C)
|
:00435514 8A06 mov al, byte ptr [esi]
:00435516 46 inc esi
:00435517 8807 mov byte ptr [edi], al
:00435519 47 inc edi
:0043551A 49 dec ecx
:0043551B 7425 je 00435542
:0043551D 84C0 test al, al
:0043551F 7429 je 0043554A
* Possible Reference to String Resource ID=00003: "Uninstall canceled."
|
:00435521 F7C603000000 test esi, 00000003
:00435527 75EB jne 00435514
:00435529 8BD9 mov ebx, ecx
:0043552B C1E902 shr ecx, 02
:0043552E 7551 jne 00435581
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043557F(C)
|
:00435530 83E303 and ebx, 00000003
:00435533 740D je 00435542
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00435512(U), :00435540(C)
|
:00435535 8A06 mov al, byte ptr [esi]
:00435537 46 inc esi
:00435538 8807 mov byte ptr [edi], al
:0043553A 47 inc edi
:0043553B 84C0 test al, al
:0043553D 742F je 0043556E
:0043553F 4B dec ebx
:00435540 75F3 jne 00435535
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0043551B(C), :00435533(C)
|
:00435542 8B442410 mov eax, dword ptr [esp+10]
:00435546 5B pop ebx
:00435547 5E pop esi
:00435548 5F pop edi
:00435549 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043551F(C)
|
:0043554A F7C703000000 test edi, 00000003
:00435550 7412 je 00435564
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435562(C)
|
:00435552 8807 mov byte ptr [edi], al
:00435554 47 inc edi
:00435555 49 dec ecx
:00435556 0F848A000000 je 004355E6
:0043555C F7C703000000 test edi, 00000003
:00435562 75EE jne 00435552
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435550(C)
|
:00435564 8BD9 mov ebx, ecx
:00435566 C1E902 shr ecx, 02
:00435569 756C jne 004355D7
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0043556F(C), :004355E4(C)
|
:0043556B 8807 mov byte ptr [edi], al
:0043556D 47 inc edi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043553D(C)
|
:0043556E 4B dec ebx
:0043556F 75FA jne 0043556B
:00435571 5B pop ebx
:00435572 5E pop esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004354F7(C)
|
:00435573 8B442408 mov eax, dword ptr [esp+08]
:00435577 5F pop edi
:00435578 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00435599(C), :004355B1(C)
|
:00435579 8917 mov dword ptr [edi], edx
:0043557B 83C704 add edi, 00000004
:0043557E 49 dec ecx
:0043557F 74AF je 00435530 <== 处理完成则跳上去
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
<== 从这里到返回处是将字串从[Esi]处复制到[Edi],长度与Ecx有关,不够则在[Edi]后面补0
|:00435510(C), :0043552E(C)
|
:00435581 BAFFFEFE7E mov edx, 7EFEFEFF <--
:00435586 8B06 mov eax, dword ptr [esi] <--
:00435588 03D0 add edx, eax
:0043558A 83F0FF xor eax, FFFFFFFF
:0043558D 33C2 xor eax, edx
:0043558F 8B16 mov edx, dword ptr [esi]
:00435591 83C604 add esi, 00000004
:00435594 A900010181 test eax, 81010100 <-- 检查 Eax取到的是否为0,手法很是怪异?!
:00435599 74DE je 00435579
:0043559B 84D2 test dl, dl
:0043559D 742C je 004355CB
:0043559F 84F6 test dh, dh
:004355A1 741E je 004355C1
:004355A3 F7C20000FF00 test edx, 00FF0000
:004355A9 740C je 004355B7
:004355AB F7C2000000FF test edx, FF000000
:004355B1 75C6 jne 00435579 <== 跳走表示edx取到的各字节均为非0
:004355B3 8917 mov dword ptr [edi], edx
:004355B5 EB18 jmp 004355CF
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004355A9(C)
|
:004355B7 81E2FFFF0000 and edx, 0000FFFF
:004355BD 8917 mov dword ptr [edi], edx
:004355BF EB0E jmp 004355CF
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004355A1(C)
|
:004355C1 81E2FF000000 and edx, 000000FF
:004355C7 8917 mov dword ptr [edi], edx
:004355C9 EB04 jmp 004355CF
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043559D(C)
|
:004355CB 33D2 xor edx, edx
:004355CD 8917 mov dword ptr [edi], edx
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004355B5(U), :004355BF(U), :004355C9(U)
|
:004355CF 83C704 add edi, 00000004
:004355D2 33C0 xor eax, eax
:004355D4 49 dec ecx
:004355D5 740A je 004355E1
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435569(C)
|
:004355D7 33C0 xor eax, eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004355DF(C)
|
:004355D9 8907 mov dword ptr [edi], eax
:004355DB 83C704 add edi, 00000004
:004355DE 49 dec ecx
:004355DF 75F8 jne 004355D9
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004355D5(C)
|
:004355E1 83E303 and ebx, 00000003
:004355E4 7585 jne 0043556B
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00435556(C)
|
:004355E6 8B442410 mov eax, dword ptr [esp+10]
:004355EA 5B pop ebx
:004355EB 5E pop esi
:004355EC 5F pop edi
:004355ED C3 ret
注册机
转载本文请注明出处,并保持文章的完整性。本文不得用于任何商业场合,仅供学习和参考。
作者: Fpc (感谢直接和间接提供帮助的所有朋友)
工具: BC++ Builder 5.0(对ASM的兼容性非常不错,可惜我用的不熟)
写教程真是很累的一件事,想起众多前辈的妙文,就知道他们付出了什么。不过在这过程中是学到了一些,尤其是那些自己原来不太明白之处,要去想清楚,否则错误连连,走在街上肯定被痛扁。有意把这一篇写的很罗嗦,只是希望大家从中学到知识,如果再写,一定会是很简练。当然水平还是基础级的,需要的只是耐心!!
下面是注册机:
//---------------------------------------------------------------------------
// KeyGen for WinAmp 2.11 by Fpc
// edtName为输入注册姓名的文本框,edtCode为显示注册码的文本框(只读)
void __fastcall TForm1::edtNameChange(TObject *Sender)
{
char name[255], code[255] = " ";
int i;
long value = 0;
for(i=0;i<255;i++)
name[i]='\0x0';
if(edtName->Text.IsEmpty()){
edtCode->Text = "";
return;
}
strcpy(name, edtName->Text.c_str());
_asm
{
lea esi, name
xor edi, edi
xor ebx, ebx
loop1:
movzx eax, byte ptr [Esi+2]
movzx ecx, byte ptr [Esi]
movzx edx, byte ptr [Esi+1]
xor eax, ecx
movzx ecx, byte ptr [Esi+3]
not ecx
not eax
xor ecx, edx
add eax, ecx
mov ecx, edi
and ecx, 0x0000000F
shl eax, cl
test eax, eax
jl iflittle
add ebx, eax
jmp nextchar
iflittle:
sub ebx, eax
nextchar:
inc edi
inc edi
inc esi
cmp byte ptr [esi], 00
jne loop1 // the first loop
mov eax, ebx
mov ecx, 0x000F4240
imul eax, 0x00000025
cdq
idiv ecx
mov ecx, edx
mov esi, edx
imul ecx, 0x00000064
xor edi, edi
loop2:
test esi, esi
je over
mov eax, esi
push 0x00000064
cdq
pop ebx
idiv ebx
mov eax, esi
push ebx
pop esi
add edi, edx
cdq
idiv esi
mov esi, eax
jmp loop2 // the second loop
over:
lea eax, dword ptr [edi+2*edi]
push 0x00000004
cdq
pop esi
idiv esi
push 0x00000064
pop esi
cdq
idiv esi
mov eax, edx
add eax, ecx
mov value,eax
}
wsprintf(code, "%ld", value);
edtCode->Text = code;
}
//---------------------------------------------------------------------------
// 以上经编译通过,没有问题
Fpc @2001/03/26
<DIV ALIGN='right'>----------------------------------------------
没有最好,只有最差!
希望FCG不会最差!DIV>