【标题:Tag&Rename 1.7 文章二 :此软件的注册码算法。( 进来看看
8^) )】
==========================================================================================
软件 :Tag&Rename 1.7
软件简介 :一个可以修改MP3 和 VQF 音乐文件中的TAG说明的程序。目前尚未支持MP3最新的ID3v2
但是,仍然是一个很好用的编辑工具。
下载处 :软件主页: http://www.softpointer.com/tr.htm
==========================================================================================
本文作者 :McNy@Work
日期 :2000年11月11日 --> 2000年11月13日
Email :mcny_work@yahoo.com
(邮件主题请以"WANTED:McNycn"开始,注意英文字母大小写,否则我将收不到喔!)
-------------------------
我写了一个注册码测试器(若你输入的S/N为正确的注册码,它会告诉你,否则只会显示你的"注册码
生成串")。 实在没多大用途,不过有兴趣的话,可以试看看。会一击即中也说不得! (请使用
下面地址下载。)
(不过,可以改编成穷举法算码器,不过不知道要算多久才有结果(日月可计吧!)。
想要源程序可与我联系)
<无法从浏览器直接下载!!需要使用Flashget,Netants,...等工具进行下载(但不支持断点续传)>
网址 : http://www.geocities.com/mcny_work (只有两个页面,正在建造中,施工缓慢)
下载地址: http://www.geocities.com/mcny_work/file/2000/tagren17_kt.zip
(使用方法:请看readme.txt)
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
~ ~ ~ ~ ~ ~ ~
终于将本文输完了(有点神经衰退了 8^) )。看雪学院、论坛给我的帮助,实在是太多太多了!!
在此,感谢看雪与来访论坛的各位大侠。并以【 Tag&Rename 1.7 文章一】、【二】 献给大家。。。
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
~ ~ ~ ~ ~ ~ ~
【目录】
§========§
§ 一、前言 §
§ 二、正文 §
§ 三、后记 §
§ 四、附录 §
§========§
----------------------------------【一、前言】--------------------------------------------
下面代码是取自『Tag&Rename 1.7 文章一』的『第二部分』,有见过那篇文章的朋友或许会有印象。
本文的算法,是从代码 **(C3)** 处的call逐渐向上追踪所得。起始点是call内部深处所调用的
CompareStringA 。(注:直接bpx CompareStringA 并不能截住!因为实际上是调
用CompareStringA + 0D 的地址。 所以bpx应该是设在CompareStringA再加 0D 的地址。)
由于涉及算法的核心代码冗长(不少于600行)。经笔者整理以后,本文用C++来演示。
(由于笔者编程水平实在有限,希望大家能够帮忙优化。。。,不胜感激。)
...
017F:0045655F
MOV EAX,ESI
017F:00456561
CALL 00402F68
017F:00456566
LEA EDX,[EBP-04]
017F:00456569
LEA EAX,[EBP-18]
017F:0045656C
CALL 00456460 ///由输入S/N,产生"注册码生成串"的
///主要调用。
017F:00456571
MOV DL,01
017F:00456573
MOV EAX,[00410060]
017F:0045657D
MOV [EBP-08],EAX
017F:00456580
LEA EAX,[EBP-08]
017F:00456583
CALL 00456404
017F:00456588
MOV EDX,[EBP-04]
017F:0045658B
MOV EAX,[EBP-08]
017F:0045658E
MOV ECX,[EAX]
**(C3)** 017F:00456590 CALL NEAR [ECX+50]
//比较"注册码生成串'和两百余个
//"正确的串"。
//若全部不匹配返回EAX=FFFFFFFF
//(内部会调用 Kernel32!CompareStringA)
017F:00456593
INC EAX
**(A3)** 017F:00456594 JZ 0456598
/// EAX=0 时跳转。我们改这里!!!
==> 017F:00456598 MOV BL,01
/// 若上一行不跳转,则注册码正确。
017F:0045659B
CALL 00402F68
017F:004565A0
XOR EAX,EAX
....
----------------------------------【二、正文】--------------------------------------------
Tag&Rename 1.7 使用了一个不可逆的注册码算法。我们输入的注册码S/N,运用该算法产生一个
“注册码生成串”。再将我们的“注册码生成串”与程序中的“内部比较串”(实质上是编程时,事先
由正确的注册码所产生的“注册码生成串”)进行比较,若有其中一个“内部比较串”与之相同,则注
册成功!
(一)产生“注册码生成串”的步骤:< 我们输入S/N: 1234567890 >
1)读入用户输入的S/N (并去除 S/N 的空格前缀与后缀!) // eg: S/N = " SN-0001
45678-8890 "
a)S/N可以是任何的英文字母、数字、标点符号
// ==> S/N = "SN-0001 45678-8890"
b)S/N长度<=45
2)将S/N 放入char sn_area[0] 中,并以 0x80 作为串结束符。在 sn_area[0x38] 处放入(串长*
8 ) 。
( 见 **(D1)** )
3)调用不可逆算法,产生“注册码生成串”:
A)进行位操作.
B)进行'和'操作 //!!! 是这里造成本算法的不可逆!!!
// 见**(D4)**
C)改变Ap4[0] (指向A[] ) , 重复A)到C)直到FOR 循环结束
// 见**(D3)**
D)将B[]与改变后的A[]相加(注意相加时的指针),结果放入A[] // 见**(D5)**
E)人为地将A[]分成四组(每一组4个字节〕。逐个将每个小组变成字符串,将结果加入outputsn 串尾。
最后一组字符串必须加入outputsn两次。(最后产生一个近似四十个字符的字符串)
F) outputsn 就是我们求得的"注册码生成串",我们就是用它与程序内所有的"内部比较串'比较。
(二)为什么此算法不可逆?
A) 首先必须阐明: 整个算法中,只有两个主要变量: sn_area[] ,A[] 。
sn_area[] 的内容,在我们输入S/N 后便确定下来了(不会再改变) 。
只有 A[] 的内容,会在算法中不断的改变!!
(其余的常量都是TagRename 程序的内定值,不可擅自篡改! 8^) )
B)假设现在seed=63, 这是最后一个循环了。我们会进行以下操作.
// 见 **(XX)** 处
但是,为了方便阐述,我们临时将几个变量名做了一点调整:
//这是正向过程的最后一个循环。
buf1= Ap3[0] | (~ Ap2[0]);
buf1= buf1 ^ Ap1[0];
buf2=buf1+Ap4_old[0]+sna[0]+mydef[seed]+Ap3[0];
// <=关键
Ap4_new[0]=buf2;
// Ap4_new[0], Ap4_old[0] 皆指向A[]
// sna[0]包含我们所输入的S/N
可见,这一次循环中只有Ap4_new[0]被改变.
若我们要作一个逆过程(从一个'内部比较串"找其正确注册码),则必须从已知的
Ap4_new[0]求得原来的Ap4_old[0],好让程序不断的循环下去。逆过程如下:
//这是逆过程的第一个循环。
buf1= Ap3[0] | (~ Ap2[0]);
//位操作不变。
buf1= buf1 ^ Ap1[0];
Ap4_old[0]=Ap4_new[0]-( buf1+sna[0]+mydef[seed]+Ap3[0]
); // <=关键
我们知道,这个逆过程中 Ap4_old[0] 是未知的。必须算出它的值,我们才
能继续算下去,一直到循环结束,得到正确的注册码。
然而,这里有一个矛盾!!因为 sna[0] 中存放的是我们输入的注册码(在逆过程中,
当然是指正确的注册码。 即:若此处的注册码与当初生成“内部比较串”时的注册码不同,
则必定不会得到我们所期望的结果!)。可是,我们并不知道正确的注册码是什么,
所以 sna[0]在逆过程中也应该是未知的!!
只有一个等式,却有两个未知数,当然无解啦! (本想写个注册机,才花那么多时间去分析
代码,这回可真是 '偷鸡不着蚀把米' 8^( )
(三)VC++代码(局部):〔想要源程序可与我联系〕
//++++++++++++++++++++++++Tag&Rename 1.7 算法(VC++)++++++++++++++++++++++++++++++++++++++++++++++++++
///Compile successful in VC++6.0
///假定 int 和 char 的长度都为 4 个字节
//我们输入的S/N串会放入sn_area[]中。
//假设我们输入的S/N 为 1234567890 ,则sn_area[]如下。
//注意:必需以0x80作为串的结束符!! 而且 sn_area[0x38]处必需放入'串长x8'
//此列中 sn_area[0x38]=0x50
//我们人为的将它分成16个区(用十六进制数0-F表示)
**(D1)**
char sn_area[64]={
0x31,0x32,0x33,0x34,
0x35,0x36,0x37,0x38, 0x39,0x30,0x80,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x50,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
};
//都是常数,用来选择sn_area的某一区
int sna_select[64]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
0x1,0x6,0xB,0x0,0x5,0xA,0xF,0x4,0x9,0xE,0x3,0x8,0xD,0x2,0x7,0xC,
0x5,0x8,0xB,0xE,0x1,0x4,0x7,0xA,0xD,0x0,0x3,0x6,0x9,0xC,0xF,0x2,
0x0,0x7,0xE,0x5,0xC,0x3,0xA,0x1,0x8,0xF,0x6,0xD,0x4,0xB,0x2,0x9
};
//都是常数,用来进行四则运算.
int mydef[64]= {0xD76AA478,0xE8C7B756,0x242070DB,0xC1BDCEEE,0xF57C0FAF,0x4787C62A,0xA8304613,0xFD469501,
0x698098D8,0x8B44F7AF,0xFFFF5BB1,0x895CD7BE,0x6B901122,0xFD987193,0xA679438E,0x49B40821,
0xF61E2562,0xC040B340,0x265E5A51,0xE9B6C7AA,0xD62F105D,0x02441453,0xD8A1E681,0xE7D3FBC8,
0x21E1CDE6,0xC33707D6,0xF4D50D87,0x455A14ED,0xA9E3E905,0xFCEFA3F8,0x676F02D9,0x8D2A4C8A,
0xFFFA3942,0x8771F681,0x6D9D6122,0xFDE5380C,0xA4BEEA44,0x4BDECFA9,0xF6BB4B60,0xBEBFBC70,
0x289B7EC6,0xEAA127FA,0xD4EF3085,0x04881D05,0xD9D4D039,0xE6DB99E5,0x1FA27CF8,0xC4AC5665,
0xF4292244,0x432AFF97,0xAB9423A7,0xFC93A039,0x655B59C3,0x8F0CCC92,0xFFEFF47D,0x85845DD1,
0x6FA87E4F,0xFE2CE6E0,0xA3014314,0x4E0811A1,0xF7537E82,0xBD3AF235,0x2AD7D2BB,0xEB86D391
};
//我们人为的将A[]、B[]各分成四组.(每4个字节一组)
**(D2)**
//A[]和B[]的初始值都一样, 但是A[]会在循环 **(D1)** 中不断改变.
char A[16]={0x01,0x23,0x45,0x67, 0x89,0xAB,0xCD,0xEF, 0xFE,0xDC,0xBA,0x98, 0x76,0x54,0x32,0x10};
char B[16]={0x01,0x23,0x45,0x67, 0x89,0xAB,0xCD,0xEF, 0xFE,0xDC,0xBA,0x98, 0x76,0x54,0x32,0x10};
CString outputsn; //所求得的'注册码生成串'。
int p1,p2,p3,p4; //使用它们来指定要使用A[]的哪个部分。
__inline bool CTagRen17snDlg::checkfor1targ()
{
int i,buf1,seed;
int *Ap1,*Ap2,*Ap3,*Ap4,*sna; //
p1=0x08; //Initialize the Started A[n] order
p2=0x0c;
p3=0x04;
p4=0;
for (seed=0;seed<64;seed++) //seed=0
--> 63 **(D3)**
{
Ap1=(int*)&A[p1];
//注意:这里强制将char指针变成int指针,所以看到的数据会颠倒。
//举例,A[p1]的内容=
01 23 45 67
//
Ap1[0] = 67 45 23 01
Ap2=(int*)&A[p2];
Ap3=(int*)&A[p3];
Ap4=(int*)&A[p4];
sna=(int*)&sn_area[sna_select[seed]*4];
//注意:这里也强制将char指针变成int指针。
//Here start
if(seed<16)
//这里就不用多说了吧,都是一些位操作.
{
buf1= Ap3[0] & Ap1[0];
buf1= ((~Ap3[0])& Ap2[0]) | buf1;
}
else if(seed<32)
{
buf1= Ap2[0] & Ap3[0];
buf1= ((~Ap2[0])& Ap1[0]) | buf1;
}
else if (seed<48)
{
buf1= Ap3[0] ^ Ap1[0];
buf1= buf1 ^ Ap2[0];
}
else
{
buf1= Ap3[0] | (~ Ap2[0]); //
**(XX)**
buf1= buf1 ^ Ap1[0];
}
buf1=buf1+Ap4[0]+sna[0]+mydef[seed]+Ap3[0];
//<=该死的东西!! **(D4)**
Ap4[0]=buf1;
//整个for循环只改变A[]中的某一组
///改变p1,p2,p3,p4
if (p1==0) p1=0x0c;
else p1=p1-4;
if (p2==0) p2=0x0c;
else p2=p2-4;
if (p3==0) p3=0x0c;
else p3=p3-4;
if (p4==0) p4=0x0c;
else p4=p4-4;
}//for_end
///
int *pa,*pb;
for (i=0;i<4;i++) //Final Add
{
pa=(int*)&A[i*4];
//注意:这里也强制将char指针变成int指针。**(D5)**
pb=(int*)&B[i*4];
pa[0]=pa[0]+pb[0];
}
///将pa[]的内容(共十六字节,分成四字节一组)转换成字符串(四十个字符/或近四十个字符)。
char aaaa[50];
outputsn.Empty();
for (i=0;i<4;i++)
{
pa=(int*)&A[i*4];
_itoa(pa[0],aaaa,16);
//注意:若pa[0]中的内容以0为前导,前导0不转换!
//举例:pa[0]=00A5D41E
,则aaaa='A5D41E'
outputsn=outputsn+aaaa;
}
outputsn=outputsn+aaaa;
//A[]的最后一组要多加一次!!(这样便构成
//
四十个字符/或近四十个字符)
outputsn.MakeUpper();
m_disbox1.SetWindowText(outputsn);
//此时,outputsn=注册码生成串
//举例: 我们输入S/N="1234567890" ,则 outputsn='EA4DC7F7799A0A3742C7EB089D297F529D297F52'
// 若S/N="AF00-12345-0000-1234" , 则 outputsn='5D94F02772BF9C521E723D981E74F9391E74F939'
//
//下面省略。。。--比较注册码生成串与所有的内部比较串(两百余个),若有其中一个内部比较串与
// 注册码生成串相同,则所输入的
S/N 正确。注册成功。
...
...
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
----------------------------------【正文完】----------------------------------------------
----------------------------------【三、后记】--------------------------------------------
其实,并非只有 Tag&Rename 1.5/1.6/1.7 使用这个算法。SoftPointer 这个公司的另一个软件
Advanced Ra-Renamer v.1.1 ( 软件功能:修改Realplayer音乐文件(*.ra)的TAG )也使用同一个算法,
是一摸一样的算法!!所不同的唯有“内部比较串”!!
另一个破解方法:我们只需要将众多内部比较串中的其中一个,改成我们自己的比较串,那么用
自己的SN 便能注册成功了!(然而,我怀疑会有几个人这样做。这毕竟有
点画蛇添足。既然要对程序本身作修改,改变跳转不是更加简单省事??!)
----------------------------------【四、附录】--------------------------------------------
//(仅供参考)
//几个Tag&Rename1.5/1.6/1.7的内部比较串。1.7版本有两百余个比较串。(这里不按原顺序排列)
target_TAG[0] ="6BD7B460BF475A6B5415F1F97F19F2CD7F19F2CD"; //<=在程序的第一次比较,可以看到本串。
target_TAG[1] ="30FE82633BADBA6CFC08C0033DABE1E83DABE1E8";
target_TAG[2] ="E1E93D3A7BEF3F49B2A4AFC19A50F9B59A50F9B5";
target_TAG[3] ="603D7D9D717D8C9023A94720FC9345E0FC9345E0";
target_TAG[4] ="CD8816613549E28B2B12DB1D7B70DCF47B70DCF4";
target_TAG[5] ="458FC6FF176FAB84A09166ECA5D41EA5D41E";
//<=串长不一定是四十个字符!!
target_TAG[6] ="9298C14667EE91844C2E8AD8AA14FEF6AA14FEF6";
target_TAG[7] ="CEC148A772B83DE7C0E5F77EBA087B4EBA087B4";
target_TAG[8] ="A59469F69BBCF2E4276506C8E227368AE227368A";
target_TAG[9] ="EFF5E1F96337AAA215CA0F98E64DCE1DE64DCE1D";
target_TAG[10]="FA221E4FEC7662907D3BDE10A20B0074A20B0074";
target_TAG[11]="BFB419C3679775538D5B24282BD3FDA52BD3FDA5";
target_TAG[12]="A2F2A07D860EA1B2542236D87D317FE57D317FE5";
target_TAG[13]="9F5E527C3BA802D4AB41E6BD89F36EC189F36EC1";
target_TAG[14]="510718DA8965364AD3AFA44144D297044D2970";
target_TAG[15]="E296E8B3B6EC79FBBF686E98E8849A28E8849A2";
//几个RA-Renamer1.1 的内部比较串,约有 180 个比较串。
target_RA[0] ="728F0122AB2B16CBAF6BD4C7F3ACE41EF3ACE41E";
target_RA[1] ="5711D97055A3AA85DBB00844AC126969AC126969";
target_RA[2] ="D70BB2A0156DA6ED975853569B1741969B174196";
==========================================================================================
全文结束(有不对的地方,还望大家多多指正与包涵!)
- 标 题:【标题:Tag&Rename 1.7 文章二 :此软件的注册码算法。( 进来看看 8^) )】
- 作 者:mcny@work
- 时 间:2000-11-14 14:31:20
- 链 接:http://bbs.pediy.com