>>概述
你是否也在好奇Windows产品激活(WindowsProduct Activation)的内部原理?为什么Windows可以在同一台电脑上激活多次,却不能在多台电脑上激活?本文阐述了一些Windows激活的技术细节。我们以Windows XP为例,来讨论Windows产品的激活机制。
诚然,软件发行商有权利用技术手段给软件设置激活码,来管理正版软件的使用;但消费者也有权获知这些激活手段的技术细节,以及这些激活手段可能会对软件的使用造成哪些限制。
本文回答了两个关于windows激活的最重要的问题:
- 在激活过程中,用户的什么信息被传到了微软公司?
- 电脑硬件的更改,是否会使已被激活的Windows XP失效?
我们以Windows XP Release Candidate 1(build 2505)这一版本作为研究对象。之后的XP版本的激活过程可能和此版本略有不同。比如:加密时使用的密钥及数据结构的设置等等。
然而,我们推测微软公司懒得修改这些用于激活的数据结构。因此,我们有理由认为,本文所得出的结论对于之后的XP版本乃至最终版都依然适用。
本文深度阐述了Windows激活的内部机制。然而,为了防止黑客利用此文阐述的原理绕过XP激活,我们略去了一些微小但及其重要的技术细节。(译者注:作者略去了两个激活程序使用的加密公钥,和一个提取MD5结果的算法,这在后文中会提到。)
>>安装序列号(Installation ID)背后的那些事
Windows XP的激活方式有多种,我们着重研究最为直观的通过电话激活。
通过电话激活Windows XP的第一步是给打电话给激活中心,并提供你的Windows XP的安装序列号(Installation ID)。双击msoobe.exe可以得到这个安装序列号,然后它会指引你完成整个激活过程。安装序列号由五十个数字组成,六个一组,最后一组只有两位。
例如:
002666-077894-484890-114573-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX
出于安全考虑,在上面的安装序列号中,我们用X替换掉了一些数字,这些数字记录了安装Windows XP的电脑的硬件信息。
如果你顽皮地打开msoobe.exe多次,你会发现每次它提供的安装序列号都不一样。
书归正传,当你告诉激活中心你的安装序列号后,激活中心会给你一个与安装序列号相匹配的确认序列号(Confirmation ID)。把确认序列号输入进电脑,整个激活过程就完成了。
因为在整个激活过程中,我们只提供了安装序列号,所以对于上述的第一个问题,
“在激活过程中,用户的什么信息被传到了微软公司里?”
等效于,
“安装序列号是怎样产生的?”
所以,让我们追踪安装序列号的每一位数字背后的含义,来揭开它的神秘面纱。
>>校验位(Check digits)
还记得那五十位安装序列号吗?他们被分成六个一组。每组最右面的一位,也就是第六位,是校验位。校验位的作用是检测这组里的数字有没有被错误地输入。(比如你操着浓重的东北口音,但不幸激活中心的接线员是南方人,于是他很有可能听错了你的安装序列号。)把每组前五位数字加起来,再把偶数位(第二,四位)再加一次,除以七取余数就是校验位的值。太抽象了?我们举个例子,如上面提到的安装序列号的第一组数字:
1 | 2 | 3 | 4 | 5 <-位置
----------------------
0 | 0 | 2 | 6 | 6 <-数字
0 + 0 + 2 + 6 + 6 = 14 (第一步,将所有数字相加)
0 + 6 = 20 (第二步,把偶数位再加一遍)
第三步,除七取余
20 / 7 = 2 ,余数是20 - 2 * 7 = 6
->校验位是6
为什么要把偶数位加两次呢?这或许是为了检测出数字颠倒的错误。比如:
00626和 00266 (2和6颠倒)他们的校验位便不同
>>解码(Decoding)
把校验位删掉后,还剩下41位数字。这41位十进制数可以转换成136位长的二进制数,并以小字节序(Little-Endian)存在17个字节里。比如上述的安装序列号,去除校验位后,可以表示为如下的字节序列:
0xXX0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX
0x940xAA 0x46 0xD6 0x0F 0xBD 0x2C 0xC8
0x00
安装序列号中的X在上面的字节序列中仍然表示成X。0x表示后面的两位为16进制数。
(译者注:小字节序,即最低字节写在前面,最高字节写在后面。与我们平时的书写习惯正好相反。)
>>解密(Decryption)
当我们把不同的安装序列号解码成字节序列后,我们惊奇地发现它们的最高字节不是0x00就是0x01。而其他字节看起来是随机的。这是因为低十六字节是加密过的,而最高字节是明文,未被加密。
----------------------------------------分割线:前方高能----------------------------------------------------------
加密安装序列号的算法是一个四轮Feistel算法。因为被Feistel加密的明文必须被分成大小相等的两块,所以这种算法只能加密偶数个字节的数据块。在本例中,被加密的内容就是上面17个字节的前16字节。该算法的轮函数的密钥长为四字节,函数为SHA-1哈希函数。
我们用 ‘+‘表示字节串的相连;‘^‘表示异或操作;L和 R分别表示每轮计算的左侧输入的八个字节和右侧输入的八个字节;L‘和 R‘分别表示每轮的左侧输出和右侧的输出, First-8()表示SHA-1哈希字符串的前八个字节。然后每轮的解密过程可以表示为如下的公式:
L‘ = R ^ First-8(SHA-1(L + Key))
R‘ = L
(译者注:公式中的四个字节的Key便是作者略去的技术细节之一)
------------------------------分割线:高能结束--------------------------------------------------------------------
解密的结果是十六字节的明文,它和未加密的第十七字节共同组成了四个双字节长,和一个单字节长的以小字节序排序的数据块:
name | size | offset
-----+-------------+-------
H1 | double word | 0
H2 | double word | 4
P1 | double word | 8
P2 | double word | 12
P3 | byte | 16
H1和 H2表示与安装序列号相关联的硬件信息。P1、P2和P3包含了与该安装序列号关联的产品序列号(Product ID)。
>>产品序列号(Product ID)
产品序列号包含五组十进制数字:
AAAAAA-BBB-CCCCCCC-DDEEE
如果在你的注册表里搜索一个名为"Product ID"的项,你就会找到这个产品序列号。在IE浏览器的‘关于‘窗口中你也会找到这个产品序列号。
>>解码产品序列号
下表总结了产品序列号的十进制表示和它在P1、P2、P3中的二进制编码的对应关系
digits | length | encoding
--------+---------+---------------------------------------
AAAAA | 17 bits | bit 0 to bit 16 of P1
BBB | 10 bits | bit 17 to bit 26 ofP1
CCCCCCC | 28 bits | bit 27 to bit 31 ofP1 (lower 5 bits)
| | bit 0 to bit 22 of P2 (upper 23 bits)
DDEEE | 17 bits | bit 23 to bit 31 ofP2 (lower 9 bits)
| | bit 0 to bit 7 of P3 (upper 8 bits)
其中,五组数字的含义如下表:
数字 | 含义
--------+-------------------------------------------------
AAAAA | 在Windows XP RC1中,它一直是55034
BBB | 原始产品密钥的最高三位
| (见下文)
CCCCCCC | 原始产品密钥的低六位加上校验位(见下文)
DD | 验证产品密钥的公钥的索引(见下文)
EEE | 一个随机数
可以看出,原始产品密钥(Raw Product Key)在生成产品序列号中扮演着重要的作用。
>>产品秘钥(Product Key)
在Windows XP安装盘的背面有一张贴纸,贴纸上写着一串产品密钥。而原始产品密钥就隐藏在这串产品密钥中。产品密钥是一串用‘-‘隔开的五组序列,每组有五位:
FFFFF-GGGGG-HHHHH-JJJJJ-KKKKK
每位是下面24个字母或数字之一
B C D FG H J K M P Q R T V W X Y 2 3 4 6 7 8 9
和安装序列号的十进制编码类似,这25位字符便是产品的24进制表示。将这个产品密钥解码后,我们会得到115位左右的二进制数,它们也是按照小字节序存在15个字节长的序列中。将上面的产品密钥解码后,将得到如下的字节序列:
0x6F 0xFA 0x95 0x45 0xFC 0x750xB5 0x52
0xBB 0xEF 0xB1 0x17 0xDA 0xCD0x00
在这15个字节中,最低位的四个字节包含了用小字节序储存的原始产品密钥(0x4595FA6F)。将这四个字节向左移一位,即把最低1比特去除,就得到了这个原始产品密钥:
0x22CAFD37
用十进制表示为,
583728439
上面15个字节中,剩下的11个字节构成了一个数字签名,并用一个硬编码在程序中的公钥来验证这个产品密钥的真实性。
>>由产品密钥生成产品序列号
以十进制表示的产品密钥中的最高三位,也就是数字583,正好是我们上面所提到的产品序列号的 BBB 部分。
把一个校验位附在剩下的六位数字728439后面,就得到了产品序列号的CCCCCCC部分。校验位的数字是这样选择的:包括校验位在内的所有数字相加起来要能被7整除。在上面的例子中,前六个数字相加为,
7 + 2 + 8 + 4 + 3 + 9 = 33
由此我们得知校验位应为2,因为,
7 + 2 + 8 + 4 + 3 + 9 + 2 = 35
而35可以被7整除。所以,产品序列号中的CCCCCCC部分应为
7284392
很多个公钥会被依次用来验证产品密钥。如果第一个公钥验证失败了,就用第二个进行验证,依次类推。产品序列号中的DD部分就代表着成功验证产品序列号的公钥序号。
这个依次尝试验证的机制可能是为了让拥有不同私钥的多个组织都可以发行合法的产品序列号。
然而,不同的私钥也有可能代表不同的产品版本。‘专业版‘的验证密钥可能不同于与‘服务器‘版的验证秘钥。这样,DD部分就代表着产品版本。
最后,从我们给出的产品密钥中生成的产品序列号就是,
55034-583-7284392-00123
它表示第一个公钥成功验证了该产品密钥(DD=00),123选作了EEE的随机数。
还记得我们每次顽皮地打开msoobe.exe时它所生成的产品序列号都不一样吗?这是因为每次生成的随机数EEE都不同。这个微小的差别导致了最后加密过的产品序列号的巨大差异。
所以,在激活中你所使用的产品序列号的后三位极有可能和你的IE浏览器里显示的或者和你注册表中存储的序列号的后三位不一样。
>>硬件信息(Hardware Information)
上文提到,与安装序列号相关联的硬件配置信息储存在两个双字节H1和H2中。
>>位字段(Bit-fields)
为了清楚地表示各个硬件的配置信息,这两个双字节被划分成了12个字段。下表表示了电脑各个硬件和各个字段的对应关系,
double word | offset | length | bit-fieldvalue based on
------------+--------+--------+----------------------------
H1 | 0 | 10 | 硬盘驱动器序列号
| | |
H1 | 10 | 10 | 网卡物理地址
| | |
H1 | 20 | 7 | 光盘驱动器标识码
| | |
H1 | 27 | 5 | 图形适配器标识码
| | |
H2 | 0 | 3 | 未被使用,一直被置成001
H2 | 3 | 6 |CPU序列号
H2 | 9 | 7 | 硬盘驱动器标识码
| | |
H2 | 16 | 5 | SCSI主适配器识别码
| | |
H2 | 21 | 4 | IDE控制器标识码
| | |
H2 | 25 | 3 |CPU型号
H2 | 28 | 3 | 内存大小
H2 | 31 | 1 | 1 = 有扩展坞
| | | 0 = 无扩展坞
其中,H2的第31比特表示该笔记本电脑是否有扩展坞(Docking Station)。如果电脑有扩展坞,验证机制就会允许更多的硬件更改,而不触发Windows XP的重激活。因为给电脑增添一个外接设备的话就会更改它的配置信息。比如,扩展坞中内置的SCSI主适配器可能被启用。你总不想接上一个移动硬盘后就得重新激活XP吧?
H2的第0,1,2比特并没有被使用,并且一直被置为001。
如果剩下的十个字段对应的硬件信息被检测到的话,相应的字段就会被置成一个非零值来描述这个硬件。如果没有检测到硬件信息的话,相应字段就会被置成零。
激活程序从注册表中获取硬件信息,并以字符串的形式描述它们。把这些字符串进行哈希处理,就得到了相应字段的值。
>>哈希(Hashing)
将描述硬件信息的字符串用MD5哈希算法处理,并把特定位置的字符从哈希结果中取出来,就得到了相应字段的值。不同的字段截取不同位置的哈希字符。另外,为了避免出现截取出来的字符均为零的情况,激活程序作了如下处理,
Hash = (Hash % BitFieldMax)+ 1
BitFieldMax指字段中能储存的最大值。比如,10比特的字段能储存的最大值为1023,所以10比特字段的BitFieldMax值就是1023.‘x % y‘表示x除以y取余数。最后,这个在1和BitFieldMax之间的数被存在了相应的字段中。
>>内存字段(RAM bit-field)
内存字段值的计算方法略有不同,它与操作系统可调用的内存大小有关。七个值分别代表着不同的内存大小,
value | amount of RAMavailable
------+---------------------------
0 | (bit-field unused)
1 | below 32 MB
2 | between 32 MB and 63 MB
3 | between 64 MB and 127 MB
4 | between 128 MBand 255 MB
5 | between 256 MBand 511 MB
6 | between 512 MB and1023 MB
7 | above 1023 MB
必须指出,内存大小是用GlobalMemoryStatues()这个函数来获取的,而这个函数返回的值会比真实的内存值小几百KB。所以,一个128MB的内存会被分在‘在64MB和127MB之间‘这个分组中。
>>举个例子
让我们举个真实的例子。我们的一个测试系统中的硬件信息是如下八个字节,
0xC50x95 0x12 0xAC 0x01 0x6E 0x2C 0x32
将这些字节转换成H1和 H2,我们得到,
H1 = 0xAC1295C5 and H2 =0x322C6E01
将H1和 H2划分成各个字段,我们得到,
dw & | |
offset | value | derived from
-------+-------+-----------------------------------------------
H1 0| 0x1C5 | ‘1234-ABCD‘
H1 10 | 0x0A5 | ‘00C0DF089E44‘
H1 20 | 0x37 | ‘SCSI\CDROMPLEXTOR_CD-ROM_PX-32TS__1.01‘
H1 27 | 0x15 | ‘PCI\VEN_102B&DEV_0519&SUBSYS_00000000&REV_01‘
H2 0| 0x1 | (unused, always 0x1)
H2 3| 0x00 | (CPU serial number not present)
H2 9| 0x37 |‘SCSI\DISKIBM_____DCAS-34330______S65A‘
H2 16 | 0x0C | ‘PCI\VEN_9004&DEV_7178&SUBSYS_00000000&REV_03‘
H2 21 | 0x1 | ‘PCI\VEN_8086&DEV_7111&SUBSYS_00000000&REV_01‘
H2 25 | 0x1 | ‘GenuineIntel Family 6 Model 3‘
H2 28 | 0x3 | (system has 128 MB of RAM)
H2 31 | 0x0 | (system is not dockable)
>>硬件修改(Hardware Modifications)
在已经激活Windows XP的电脑上,储存在‘system32‘文件夹里的‘wpa.dbl‘文件对于检测电脑的硬件修改有着重要作用。它是一个简单的由RC4加密的数据库,它储存着如下信息:
- 由当前电脑的硬件信息所计算出的校验字段值(H1 H2)
- Windows XP激活时计算出的校验字段值(H1 H2)
以及激活过期日期,激活确认序列号等。
a)在硬件发生变动时自动更新,来反应这些变化;b)则一直保持不变。因此,b)可以看成Windows XP激活时的硬件信息的快照。
这个快照在XP激活之前并不存在于数据库中。因此,如果你比较一下‘wpa.dbl‘文件在Windows XP激活前后大小的话,你会发现它在XP激活后变大了。这是因为快照被插入到了数据库中。
当程序判断XP是否需要重激活时,它会比较a)和b)的值,也就是比较当前硬件信息和激活时的硬件信息。
>>无扩展坞的电脑
在无扩展坞的电脑中,除了那个没被使用的字段(还记得那个001吗?)和标示是否有扩展坞的字段(H2的第31比特),剩下的所有字段都会参与比较。如果这十个字段中,超过三个发生改变,也就是b)中多于三个字段与a)不同,Windows XP就需要重激活。
也就是说,在我们的例子中,我们可以在不重激活XP的情况下,更换我们的硬盘和光驱,并升级我们的内存。
然而,如果我们完全重装XP的话,b)中存储的信息就会丢失,我们就必须重新激活Windows XP,即便我们的硬件并没有发生任何改变。
>>有扩展坞的电脑
在有扩展坞的电脑中,也就是H2的第31位被置为1时,十个字段中,只有七个参与了比较。SCSI主适配器,IDE控制器和图形适配器的硬件信息并没有参与比较。但是,在剩下的参与比较的七个字段中,如果超过三个字段发生更改,我们依然得重新激活Windows XP。
>>结论
本文从技术角度探讨了Windows XP的激活机制。我们阐述了在产品激活中,用户的什么信息被传给了微软,以及硬件的升级会怎样影响已激活的Windows XP。
回顾Windows激活的技术细节,我们并不认为它像很多人想象得那样充满问题。因为这个激活机制对硬件修改有着很好的宽容度。另外,很多不同的硬件信息会被映射成一个相同的校验字段值。在上例中,我们看到PX-32TS被映射成0x37 =55。但是很多其他的光驱也会被映射成相同的字段值。因此,单纯地从字段值来推测这个光驱是否是PX-32TS是不可能的。
与许多对Windows产品激活的评论相反,但是我们认为这个激活机制并不能避免特定的硬件修改;更为严重的是,它没有尊重用户的隐私权。
>>关于作者
Fully Licensed GmbH是一个刚起步的,关注于在线软件验证方法的公司。欢迎浏览他们的网站
以获取更多信息。
他们常常研究其他公司所使用的证书验证的解决方案。
>>关于译者
首发于微博@浙大宋博,转载请注明出处。