WindowsXP序列号产生原理(椭圆曲线法)

来源  https://blog.csdn.net/zhiyuan411/article/details/5156330

参考 https://www.licenturion.com/xp/fully-licensed-wpa.txt

从Win95起,Microsoft的产品安装Key从原来的10位数字改为25位字符,这一改动,随着MicroSoft告别了简单的校验和,从此投入了椭圆曲线法的怀抱。从密码学的角度来看,这绝对是一个里程碑, 因为当时椭圆曲线法仍在研究论证阶段,MicroSoft是第一个将之实用以商业产品的厂家。 
  那么在这25个字符里到底有什么呢? 

1.Base24 
  这25个字符实际是114bits的数据用Base24进行UUCode后的结果(UUCode是早先Email对内容编码的一种方式,此处可以理解为将2进制表示的114位数字做进制转换,生成用24进制表示的一个25位数字),做为安装Key,这个Base必须绝对避免误认,所以MicroSoft选择了以下这24个字符做为UUCode的Base:  BCDFGHJKMPQRTVWXY2346789 
  所以,如果你的安装Key 有这24个字符以外的字符的话,你完全可以把它丢到垃圾筒里去了,它根本通不过了。

2.114 bits 
  UUDecode后得到的114位按Intel高位在后的格式表示如下: 
[ X XXXXXXXX XXXXXXXX XXXXXXXXXXXXXXXXXX ] Total 114 Bits
   |         |               |                            / 55 Bits Sign
   |         |               / 28 Bits Hash 
   |         / 30 Bits Serial
   / 1 Bits Flag                  
Flag: 不明标志,目前所见的各类Key中这一位总是为0。 
Serial:用户序列号,转成十进制表示为AAAABBBBBB(9-10位),对应显示为: 
零售版:xxxxx-AAA-BBBBBBx-xxxxx 
OEM版: xxxxx-OEM-0AAAABx-BBBBB 
以上31bits总称为Data,是CDKey中的基本部分。 
Hash:Data经特定处理得到的结果,见后文。 
Sign:Hash值的椭圆曲线签名,见后文。 

3.椭圆曲线签名算法 
   椭圆曲线密码学(ECC, Elliptic curve cryptography)是基于椭圆曲线数学的一种公钥密码的方法。 
所谓椭圆曲线是指这样一类曲线方程:
y^2 + a1*xy + a3*y = x^3 + a2*x^2 + a4*x + a6 
在密码学里用的是它的两个特例,而MicroSoft用的更是这两个特例中的特例:
y^2 = x^3 + a*x + b ( mod p ) 
当a、b、p选定后,就可以确定一个椭圆曲线,再选择一个生成点 G(gx,gy), 
于是,存在一个最小的整数q使得q*G=O,然后,再任意选择一个整数 k K(kx,ky)=k*G,这样椭圆曲线签名算法的Key就全生成了: 
公开密钥为:a,b,p,G(gx,gy),K(kx,ky) 
私有密钥为:a,b,p,G(gx,gy),q,k 
   要对Data签名时: 
A.先任意选择一个整数r B.将Data、rx、ry共100个字节求SHA-1,取结果中的28位得到Hash; 
C.求Sign = r - Hash * k ( mod q ); 
D.把Data、Hash、Sign三个数组合后UUCode得到25位CDKey。 
验证CDKey时: 
A.把25位CDKey先UUDecode再拆分后提到Data、Hash、Sign; 
B.求点R( rx, ry ) = Sing * G + Hash * K ( mod p ); 
C.将Data、rx、ry共100个字节求SHA-1,取结果中的28位得到Hash/'; 
D.如果Hash = Hash/',则该CDKey为有效Key。

4.BINK 
  从前面的说明可以看出,为了验证CDKey,Microsoft 必须公开椭圆曲线签名算法中的公开密钥,那么这个公开密钥放在哪里呢?答案是在pidgen.dll里的BINK资源里(其他产品如Office则被包在*.MSI),而且一共有两组,从目前已知的Key组合来看,第一组密钥是用以零售版本的,第二组则用于OEM版本。两个产品的Key能否通用就在于对应的密钥是否相同,比如中文版的Windows 2000的Pro/Srv/AdvSrv的第二组密钥也是相同的,即一个Windows 2000 Pro的OEM版的Key,可同时供 Windows 2000 Srv/Adv的OEM版使用。

5.破解及其难度 
   要破解CDKey的生成算法,必须从Microsoft公开的密钥中求出对应的私有密钥,即只要求出q和k即可。从BINK中公开的密钥来看,p 是一个384 bits的质数,看起来计算量好像至少要O(2^168)才行,但Microsoft设计中一个缺陷(?)使实际工作量降低到只有O(2^28)就可以了。 
为什么相差这么远? 
回头看看3.C中的式子: Sign = r - Hash * k ( mod q ) 
通常情况下q可以是很大的值,因此Sign应该也很大,但MicroSoft为了减少用户输入的CDKey的数量,把Sign的值限死在55 bits,因此,自然也限定了q最多也不能超过56 bits。依此类推,由于k 作者曾经在一台赛扬II 800的机器上只用6个小时就解出某组密钥的q值,最多时在一台雷鸟1G上用了28个小时才算出另一组密钥的k值, 其他平均大约都在十个小时左右就可以求出。 

6.产品ID(ProductID)
  产品ID是由五组十进制数组成,如下:
AAAAA        BBB        CCCCCCC        DD        EEE
52273        005        6861993        09        146

  如果你用“ProductID”搜索注册表,你会发现一个与你安装的软件有关的产品ID。在Windows的控制面板里的系统里,你可以找到Windows 操作系统的产品ID。

每组数字所代表的意义如下表:
数字        意义
AAAAA        产品编号,例:55661为windows pro版 55660为HOME版
BBB        初级产品序列号的最高有效三位数字
CCCCCCC        初级产品序列号的最低有效六位数字以及校验数位的和
DD        用来验证产品序列号的公开密匙索引.例:PRO版为22,VLK版为23
EEE        随机值(用于电话激活时,产生不同的安装ID)
在上面的CCCCCCC部分中,由一个校验数位和六个数字组成。校验数位是这样计算得到的:将所有数位相加,包含一个检验数位,可以被七整除。

例:初级产品序列号的最低有效六位数字是728439
7 + 2 + 8 + 4 + 3 + 9 = 33
所以检验数位为2,因为
7 + 2 + 8 + 4 + 3 + 9 + 2 = 33 + 2 = 35
所得到的结果35可被七整除。所以产品ID中的CCCCCCC部分的结果为7284392。

7.OEM机制

对OEM授权的产品,从Windows XP开始微软引入了SLP(System-Locked Preinstallation)技术, 即将预装系统时候同电脑主板锁定。Windows XP采用的是SLP 1.0版,其原理是检测BIOS中是否存在由OEM硬件厂家设置的特定字串,这种方法易于伪造,但于存在更方便的VLK破解方法这没有普遍使用。
从 Vista 系统开始,SLP 验证技术升级为2.0。主要是加入了利用密钥对信息进行签名保护以防伪造的过程,需要在BIOS中专门的SLIC(Software Licensing Internal Code)表来支持。Windows 7系列中,采用了SLP 2.1技术,并没有对SLP技术进行大的改动,只是将SLIC中的Marker版本变成了2.1。

SLIC是SLP的重要部分,一般是烧写在BIOS ROM芯片中,作为操作系统激活鉴权的“最底层”标识,SLIC内部有OEM厂家的签名,在激活操作系统时,上层的OEM证书厂家必须跟SLIC厂家信息对应。SLIC一般是写在SLDT(Software Licensing Description Table,软件许可描述表)中的,SLDT长374字节。而SLDT写在ACPI(Advanced Configuration and Power Management Interface,高级配置和电源管理接口)。

OEM证书是微软根据OEM厂家提供的一些必要信息颁发的一个验证文件,扩展名为xrm-ms。OEM证书里集成了OEM厂家的身份信息及微软的数字签名。激活时必须与SLIC对应。

OEM密钥跟普通密钥看起来无异,但OEM密钥只能用于OEM激活方式,并且识别Windows版本。

Windows Vista,取消了其他大客户版本,仅保留OEM版激活。其并使用的是微软SLP 2.0,公钥取代了明文,给破解造成一定的困难。SLP 2.0技术的验证具体过程如下:
  1、当Vista安装的时候,零售版本用户需要输入光盘盒上的序列号(CD-KEY)。对于随机购买了OEM版本Vista的用户,可以在主机上找到一个相应版本的标签,作为购买Vista OEM版的凭证。标签上面有一个带有象征意义的序列号,因为OEM版本的用户并不需要输入序列号。系统根据序列号识别Vista的不同版本,如基本家庭版、高级家庭版、商业版、旗舰版等。安装完毕后,序列号会被转换为四组字母或数字,即在“系统属性”里看到的“产品ID”。其中第二组是“OEM”的,即为OEM版本的序列号(CD-KEY)。从这里开始,产品ID代替了CD-KEY。同时,安装程序为OEM版本的安装生成一个OEM证书。
  2、当每次系统启动时,BIOS里的信息就会被加载到内存中。
  3、当登录Vista系统之后,系统调用SLP服务,验证操作系统的许可权,尤其是激活状态。开始根据产品ID来识别系统的授权状态。如果没有检测到产品ID或者没有检测到合法的零售版产品ID,则视为未激活。如果检测到合法的零售版产品ID,则视为成功激活。如果检测到OEM版的产品ID,则继续验证。
  4、如果检测到OEM版产品ID,验证过程启动,并检查已安装的OEM证书是否正确。主要是用先前从BIOS加载到内存里的SLIC的公钥验证产品证书的数字签名。如果验证失败,则视为未激活。
  5、验证ACPI里SLIC与RSDT(Root System Description Table,根系统描述表)的OEM ID字段比较,以及用ACPI里SLIC标志和XSDT(Extended System Description Table,扩展系统描述表)中的OEM ID和OEM Table ID字段比较,如果不一致,则视为未激活。
  6、经过以上重重关卡之后,方视为正确的OEM授权,否则视为未激活并按照相关流程处理,例如要求激活。
  Windows 7仍然保留OEM激活策略,采用的是SLP 2.1版。

下面是细致的验证过程。
1. 激活程序启动以后,如果检测到正确的SLP Key,开始OEM激活过程,否则进行WPA方式激活(在线或者电话激活)。
2. 检测OEM证书,并且利用OEM证书的数字签名,验证OEM证书的正确性(微软私钥签名,公钥验证),如果验证通过继续OEM激活,否则进行WPA激活。
3. 将SLIC中的OEM公钥、OEMID(OEM厂家身份信息)等信息与OEM证书中的信息相比较,如果匹配正确(表明OEM公钥等信息正确)则继续进行OEM激活,否则进行WPA激活。
4. 利用OEM公钥,对SLIC中Marker数字签名进行验证,如果 验证通过(表明Marker中Message信息正确)则继续OEM激活,否则WPA激活。
5. 验证Marker中的Windows旗标,如果旗标存在,则OEM激活过程继续,否则进行WPA激活。
6. 验证Marker的版本,如果(至少)为0x20001,则OEM激活过程继续,否则OEM激活失败,进行WPA激活。
7. 获取Marker中的OEM ID和OEM Table ID信息,将它与所有ACPI表头中的对应信息进行比较,如果一致则OEM激活成功,如果不一致则触发WPA激活过程。

过程要点概述:(并不符合全部细节信息,只是强调微软加密的关键点)
1. OEM证书含有【数据(OEMID等信息,MS公钥等)】+【散列值(对数据进行Hash)】+【签名数据(MS的私钥对散列值进行签名)】
2. SLIC中含有【数据(OEMID,Marker版本等信息,MS公钥等)】+【散列值(对数据进行Hash)】+【签名数据(MS的私钥对散列值进行签名)】
3. 首先,使用公钥验证OEM和SLIC的签名是否正确;然后,对比OEM和SLIC中的OEMID、公钥等信息是否相同;验证Marker版本等是否和当前安装的系统类型相符。

===========================

Srv2003KGmain.cpp

#include <stdio.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <assert.h> typedef unsigned char U8;
typedef unsigned long U32; U8 cset[] = "BCDFGHJKMPQRTVWXY2346789"; #define FIELD_BITS_2003 512
#define FIELD_BYTES_2003 64 void unpack2003(U32 *osfamily, U32 *hash, U32 *sig, U32 *prefix, U32 *raw)
{
osfamily[] = raw[] & 0x7ff;
hash[] = ((raw[] >> ) | (raw[] << )) & 0x7fffffff;
sig[] = (raw[] >> ) | (raw[] << );
sig[] = ((raw[] >> ) | (raw[] << )) & 0x3fffffff;
prefix[] = (raw[] >> ) & 0x3ff;
} void pack2003(U32 *raw, U32 *osfamily, U32 *hash, U32 *sig, U32 *prefix)
{
raw[] = osfamily[] | (hash[] << );
raw[] = (hash[] >> ) | (sig[] << );
raw[] = (sig[] >> ) | (sig[] << );
raw[] = (sig[] >> ) | (prefix[] << );
} static void endian(U8 *x, int n)
{
int i;
for (i = ; i < n/; i++) {
U8 t;
t = x[i];
x[i] = x[n-i-];
x[n-i-] = t;
}
} void unbase24(U32 *x, U8 *c)
{
memset(x, , );
int i, n; BIGNUM *y = BN_new();
BN_zero(y); for (i = ; i < ; i++)
{
BN_mul_word(y, );
BN_add_word(y, c[i]);
}
n = BN_num_bytes(y);
BN_bn2bin(y, (U8 *)x);
BN_free(y); endian((U8 *)x, n);
} void base24(U8 *c, U32 *x)
{
U8 y[];
int i; BIGNUM *z;
memcpy(y, x, sizeof(y));
for (i = ; y[i] == ; i--) {} i++;
endian(y, i);
z = BN_bin2bn(y, i, NULL); c[] = ;
for (i = ; i >= ; i--) {
U8 t = BN_div_word(z, );
c[i] = cset[t];
}
BN_free(z);
} void print_product_key(U8 *pk)
{
int i;
assert(strlen(pk) == );
for (i = ; i < ; i++) {
putchar(pk[i]);
if (i != && i % == ) putchar('-');
}
} void verify2003(EC_GROUP *ec, EC_POINT *generator, EC_POINT *public_key, char *cdkey)
{
U8 key[];
int i, j, k; BN_CTX *ctx = BN_CTX_new(); for (i = , k = ; i < strlen(cdkey); i++) {
for (j = ; j < ; j++) {
if (cdkey[i] != '-' && cdkey[i] == cset[j]) {
key[k++] = j;
break;
}
assert(j < );
}
if (k >= ) break;
} U32 bkey[] = {};
U32 osfamily[], hash[], sig[], prefix[];
unbase24(bkey, key);
printf("%.8x %.8x %.8x %.8x\n", bkey[], bkey[], bkey[], bkey[]);
unpack2003(osfamily, hash, sig, prefix, bkey); printf("OS Family: %u\nHash: %.8x\nSig: %.8x %.8x\nPrefix: %.8x\n", osfamily[], hash[], sig[], sig[], prefix[]); U8 buf[FIELD_BYTES_2003], md[];
U32 h1[];
SHA_CTX h_ctx; /* h1 = SHA-1(5D || OS Family || Hash || Prefix || 00 00) */
SHA1_Init(&h_ctx);
buf[] = 0x5d;
buf[] = osfamily[] & 0xff;
buf[] = (osfamily[] & 0xff00) >> ;
buf[] = hash[] & 0xff;
buf[] = (hash[] & 0xff00) >> ;
buf[] = (hash[] & 0xff0000) >> ;
buf[] = (hash[] & 0xff000000) >> ;
buf[] = prefix[] & 0xff;
buf[] = (prefix[] & 0xff00) >> ;
buf[] = buf[] = ;
SHA1_Update(&h_ctx, buf, );
SHA1_Final(md, &h_ctx);
h1[] = md[] | (md[] << ) | (md[] << ) | (md[] << );
h1[] = (md[] | (md[] << ) | (md[] << ) | (md[] << )) >> ;
h1[] &= 0x3FFFFFFF;
printf("h1: %.8x %.8x\n", h1[], h1[]); BIGNUM *s, *h, *x, *y;
x = BN_new();
y = BN_new();
endian((U8 *)sig, );
endian((U8 *)h1, );
s = BN_bin2bn((U8 *)sig, , NULL);
h = BN_bin2bn((U8 *)h1, , NULL); EC_POINT *r = EC_POINT_new(ec);
EC_POINT *t = EC_POINT_new(ec);
/* r = sig*(sig*generator + h1*public_key) */
EC_POINT_mul(ec, t, NULL, generator, s, ctx);
EC_POINT_mul(ec, r, NULL, public_key, h, ctx);
EC_POINT_add(ec, r, r, t, ctx);
EC_POINT_mul(ec, r, NULL, r, s, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, r, x, y, ctx); U32 h2[];
/* h2 = SHA-1(79 || OS Family || r.x || r.y) */
SHA1_Init(&h_ctx);
buf[] = 0x79;
buf[] = osfamily[] & 0xff;
buf[] = (osfamily[] & 0xff00) >> ;
SHA1_Update(&h_ctx, buf, ); memset(buf, , FIELD_BYTES_2003);
BN_bn2bin(x, buf);
endian((U8 *)buf, FIELD_BYTES_2003);
SHA1_Update(&h_ctx, buf, FIELD_BYTES_2003); memset(buf, , FIELD_BYTES_2003);
BN_bn2bin(y, buf);
endian((U8 *)buf, FIELD_BYTES_2003);
SHA1_Update(&h_ctx, buf, FIELD_BYTES_2003); SHA1_Final(md, &h_ctx);
h2[] = (md[] | (md[] << ) | (md[] << ) | (md[] << )) & 0x7fffffff;
printf("Calculated hash: %.8x\n", h2[]); if (h2[] == hash[]) printf("Key VALID\n");
else printf("Key invalid\n"); BN_free(s);
BN_free(h);
BN_free(x);
BN_free(y);
EC_POINT_free(r);
EC_POINT_free(t);
BN_CTX_free(ctx);
} void generate2003(U8 *pkey, EC_GROUP *ec, EC_POINT *generator, BIGNUM *order, BIGNUM *priv, U32 *osfamily, U32 *prefix)
{
BN_CTX *ctx = BN_CTX_new(); BIGNUM *k = BN_new();
BIGNUM *s = BN_new();
BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
BIGNUM *b = BN_new();
EC_POINT *r = EC_POINT_new(ec); U32 bkey[];
U8 buf[FIELD_BYTES_2003], md[];
U32 h1[];
U32 hash[], sig[]; SHA_CTX h_ctx; for (;;) {
/* r = k*generator */
BN_pseudo_rand(k, FIELD_BITS_2003, -, );
EC_POINT_mul(ec, r, NULL, generator, k, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, r, x, y, ctx); /* hash = SHA-1(79 || OS Family || r.x || r.y) */
SHA1_Init(&h_ctx);
buf[] = 0x79;
buf[] = osfamily[] & 0xff;
buf[] = (osfamily[] & 0xff00) >> ;
SHA1_Update(&h_ctx, buf, ); memset(buf, , FIELD_BYTES_2003);
BN_bn2bin(x, buf);
endian((U8 *)buf, FIELD_BYTES_2003);
SHA1_Update(&h_ctx, buf, FIELD_BYTES_2003); memset(buf, , FIELD_BYTES_2003);
BN_bn2bin(y, buf);
endian((U8 *)buf, FIELD_BYTES_2003);
SHA1_Update(&h_ctx, buf, FIELD_BYTES_2003); SHA1_Final(md, &h_ctx);
hash[] = (md[] | (md[] << ) | (md[] << ) | (md[] << )) & 0x7fffffff; /* h1 = SHA-1(5D || OS Family || Hash || Prefix || 00 00) */
SHA1_Init(&h_ctx);
buf[] = 0x5d;
buf[] = osfamily[] & 0xff;
buf[] = (osfamily[] & 0xff00) >> ;
buf[] = hash[] & 0xff;
buf[] = (hash[] & 0xff00) >> ;
buf[] = (hash[] & 0xff0000) >> ;
buf[] = (hash[] & 0xff000000) >> ;
buf[] = prefix[] & 0xff;
buf[] = (prefix[] & 0xff00) >> ;
buf[] = buf[] = ;
SHA1_Update(&h_ctx, buf, );
SHA1_Final(md, &h_ctx);
h1[] = md[] | (md[] << ) | (md[] << ) | (md[] << );
h1[] = (md[] | (md[] << ) | (md[] << ) | (md[] << )) >> ;
h1[] &= 0x3FFFFFFF;
printf("h1: %.8x %.8x\n", h1[], h1[]); /* s = ( -h1*priv + sqrt( (h1*priv)^2 + 4k ) ) / 2 */
endian((U8 *)h1, );
BN_bin2bn((U8 *)h1, , b);
BN_mod_mul(b, b, priv, order, ctx);
BN_copy(s, b);
BN_mod_sqr(s, s, order, ctx);
BN_lshift(k, k, );
BN_add(s, s, k);
BN_mod_sqrt(s, s, order, ctx);
BN_mod_sub(s, s, b, order, ctx);
if (BN_is_odd(s)) {
BN_add(s, s, order);
}
BN_rshift1(s, s);
sig[] = sig[] = ;
BN_bn2bin(s, (U8 *)sig);
endian((U8 *)sig, BN_num_bytes(s));
if (sig[] < 0x40000000) break;
}
pack2003(bkey, osfamily, hash, sig, prefix);
printf("OS family: %u\nHash: %.8x\nSig: %.8x %.8x\nPrefix: %.8x\n", osfamily[], hash[], sig[], sig[], prefix[]);
printf("%.8x %.8x %.8x %.8x\n", bkey[], bkey[], bkey[], bkey[]);
base24(pkey, bkey); BN_free(k);
BN_free(s);
BN_free(x);
BN_free(y);
BN_free(b);
EC_POINT_free(r); BN_CTX_free(ctx); } int main()
{
BIGNUM *a, *b, *p, *gx, *gy, *pubx, *puby, *n, *priv;
BN_CTX *ctx = BN_CTX_new(); a = BN_new();
b = BN_new();
p = BN_new();
gx = BN_new();
gy = BN_new();
pubx = BN_new();
puby = BN_new();
n = BN_new();
priv = BN_new(); /* Windows Sever 2003 VLK */
BN_set_word(a, );
BN_set_word(b, );
BN_hex2bn(&p, "C9AE7AED19F6A7E100AADE98134111AD8118E59B8264734327940064BC675A0C682E19C89695FBFA3A4653E47D47FD7592258C7E3C3C61BBEA07FE5A7E842379");
BN_hex2bn(&gx, "85ACEC9F9F9B456A78E43C3637DC88D21F977A9EC15E5225BD5060CE5B892F24FEDEE574BF5801F06BC232EEF2161074496613698D88FAC4B397CE3B475406A7");
BN_hex2bn(&gy, "66B7D1983F5D4FE43E8B4F1E28685DE0E22BBE6576A1A6B86C67533BF72FD3D082DBA281A556A16E593DB522942C8DD7120BA50C9413DF944E7258BDDF30B3C4");
BN_hex2bn(&pubx, "90BF6BD980C536A8DB93B52AA9AEBA640BABF1D31BEC7AA345BB7510194A9B07379F552DA7B4A3EF81A9B87E0B85B5118E1E20A098641EE4CCF2045558C98C0E");
BN_hex2bn(&puby, "6B87D1E658D03868362945CDD582E2CF33EE4BA06369E0EFE9E4851F6DCBEC7F15081E250D171EA0CC4CB06435BCFCFEA8F438C9766743A06CBD06E7EFB4C3AE");
BN_hex2bn(&n, "4CC5C56529F0237D"); // from mskey 4in1
BN_hex2bn(&priv, "2606120F59C05118"); EC_GROUP *ec = EC_GROUP_new_curve_GFp(p, a, b, ctx);
EC_POINT *g = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, g, gx, gy, ctx);
EC_POINT *pub = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, pub, pubx, puby, ctx); assert(EC_POINT_is_on_curve(ec, g, ctx) == );
assert(EC_POINT_is_on_curve(ec, pub, ctx) == ); U8 pkey[];
U32 osfamily[], prefix[]; osfamily[] = ;
RAND_pseudo_bytes((U8 *)prefix, );
prefix[] &= 0x3ff;
generate2003(pkey, ec, g, n, priv, osfamily, prefix);
print_product_key(pkey); printf("\n\n");
verify2003(ec, g, pub, pkey); BN_CTX_free(ctx); return ;
}

XPKGmain.cpp

/*
Windows XP CD Key Verification/Generator v0.03
by z22 Compile with OpenSSL libs, modify to suit your needs.
http://gnuwin32.sourceforge.net/packages/openssl.htm History:
0.03 Stack corruptionerror on exit fixed (now pkey is large enough)
More Comments added
0.02 Changed name the *.cpp;
Fixed minor bugs & Make it compilable on VC++
0.01 First version compilable MingW */ #include <stdio.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/sha.h>
#include <assert.h> #define FIELD_BITS 384
#define FIELD_BYTES 48
unsigned char cset[] = "BCDFGHJKMPQRTVWXY2346789"; static void unpack(unsigned long *pid, unsigned long *hash, unsigned long *sig, unsigned long *raw)
{
// pid = Bit 0..30
pid[] = raw[] & 0x7fffffff; // hash(s) = Bit 31..58
hash[] = ((raw[] >> ) | (raw[] << )) & 0xfffffff; // sig(e) = bit 58..113
sig[] = (raw[] >> ) | (raw[] << );
sig[] = (raw[] >> ) | (raw[] << );
} static void pack(unsigned long *raw, unsigned long *pid, unsigned long *hash, unsigned long *sig)
{
raw[] = pid[] | ((hash[] & ) << );
raw[] = (hash[] >> ) | ((sig[] & 0x1f) << );
raw[] = (sig[] >> ) | (sig[] << );
raw[] = sig[] >> ;
} // Reverse data
static void endian(unsigned char *data, int len)
{
int i;
for (i = ; i < len/; i++) {
unsigned char temp;
temp = data[i];
data[i] = data[len-i-];
data[len-i-] = temp;
}
} void unbase24(unsigned long *x, unsigned char *c)
{ memset(x, , );
int i, n; BIGNUM *y = BN_new();
BN_zero(y); for (i = ; i < ; i++)
{
BN_mul_word(y, );
BN_add_word(y, c[i]);
}
n = BN_num_bytes(y);
BN_bn2bin(y, (unsigned char *)x);
BN_free(y); endian((unsigned char *)x, n);
} void base24(unsigned char *c, unsigned long *x)
{
unsigned char y[];
int i;
BIGNUM *z; // Convert x to BigNum z
memcpy(y, x, sizeof(y)); // Copy X to Y; Y=X
for (i = ; y[i] == ; i--) {} i++; // skip following nulls
endian(y, i); // Reverse y
z = BN_bin2bn(y, i, NULL); // Convert y to BigNum z // Divide z by 24 and convert remainder with cset to Base24-CDKEY Char
c[] = ;
for (i = ; i >= ; i--) {
unsigned char t = BN_div_word(z, );
c[i] = cset[t];
} BN_free(z);
} void print_product_id(unsigned long *pid)
{
char raw[];
char b[], c[];
int i, digit = ; // Cut a away last bit of pid and convert it to an accii-number (=raw)
sprintf(raw, "%d", pid[] >> ); // Make b-part {640-....}
strncpy(b, raw, );
b[] = ; // Make c-part {...-123456X...}
strcpy(c, raw + ); // Make checksum digit-part {...56X-}
assert(strlen(c) == );
for (i = ; i < ; i++)
digit -= c[i] - ''; // Sum digits while (digit < )
digit += ;
c[] = digit + '';
c[] = ; printf("Product ID: 55274-%s-%s-23xxx\n", b, c);
} void print_product_key(unsigned char *pk)
{
int i;
assert(strlen((const char *)pk) == );
for (i = ; i < ; i++) {
putchar(pk[i]);
if (i != && i % == ) putchar('-');
}
} void verify(EC_GROUP *ec, EC_POINT *generator, EC_POINT *public_key, char *cdkey)
{
unsigned char key[];
int i, j, k; BN_CTX *ctx = BN_CTX_new();
// remove Dashs from CDKEY
for (i = , k = ; i < strlen(cdkey); i++) {
for (j = ; j < ; j++) {
if (cdkey[i] != '-' && cdkey[i] == cset[j]) {
key[k++] = j;
break;
}
assert(j < );
}
if (k >= ) break;
} // Base24_CDKEY -> Bin_CDKEY
unsigned long bkey[] = {};
unsigned long pid[], hash[], sig[];
unbase24(bkey, key); // Output Bin_CDKEY
printf("%.8x %.8x %.8x %.8x\n", bkey[], bkey[], bkey[], bkey[]); // Divide/Extract pid_data, hash, sig from Bin_CDKEY
unpack(pid, hash, sig, bkey);
print_product_id(pid); printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[], hash[], sig[], sig[]); BIGNUM *e, *s; /* e = hash, s = sig */
e = BN_new();
BN_set_word(e, hash[]);
endian((unsigned char *)sig, sizeof(sig));
s = BN_bin2bn((unsigned char *)sig, sizeof(sig), NULL); BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
EC_POINT *u = EC_POINT_new(ec);
EC_POINT *v = EC_POINT_new(ec); /* v = s*generator + e*(-public_key) */
EC_POINT_mul(ec, u, NULL, generator, s, ctx);
EC_POINT_mul(ec, v, NULL, public_key, e, ctx);
EC_POINT_add(ec, v, u, v, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, v, x, y, ctx); unsigned char buf[FIELD_BYTES], md[];
unsigned long h;
unsigned char t[];
SHA_CTX h_ctx; /* h = (fist 32 bits of SHA1(pid || v.x, v.y)) >> 4 */
SHA1_Init(&h_ctx);
t[] = pid[] & 0xff;
t[] = (pid[] & 0xff00) >> ;
t[] = (pid[] & 0xff0000) >> ;
t[] = (pid[] & 0xff000000) >> ;
SHA1_Update(&h_ctx, t, sizeof(t)); memset(buf, , sizeof(buf));
BN_bn2bin(x, buf);
endian((unsigned char *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf)); memset(buf, , sizeof(buf));
BN_bn2bin(y, buf);
endian((unsigned char *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf)); SHA1_Final(md, &h_ctx);
h = (md[] | (md[] << ) | (md[] << ) | (md[] << )) >> ;
h &= 0xfffffff; printf("Calculated hash: %.8x\n", h);
if (h == hash[]) printf("Key valid\n");
else printf("Key invalid\n");
putchar('\n'); BN_free(e);
BN_free(s);
BN_free(x);
BN_free(y);
EC_POINT_free(u);
EC_POINT_free(v); BN_CTX_free(ctx);
} void generate(unsigned char *pkey, EC_GROUP *ec, EC_POINT *generator, BIGNUM *order, BIGNUM *priv, unsigned long *pid)
{
BN_CTX *ctx = BN_CTX_new(); BIGNUM *k = BN_new();
BIGNUM *s = BN_new();
BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
EC_POINT *r = EC_POINT_new(ec);
unsigned long bkey[]; // Loop in case signaturepart will make cdkey(base-24 "digits") longer than 25
do {
BN_pseudo_rand(k, FIELD_BITS, -, );
EC_POINT_mul(ec, r, NULL, generator, k, ctx);
EC_POINT_get_affine_coordinates_GFp(ec, r, x, y, ctx); SHA_CTX h_ctx;
unsigned char t[], md[], buf[FIELD_BYTES];
unsigned long hash[];
/* h = (fist 32 bits of SHA1(pid || r.x, r.y)) >> 4 */
SHA1_Init(&h_ctx);
t[] = pid[] & 0xff;
t[] = (pid[] & 0xff00) >> ;
t[] = (pid[] & 0xff0000) >> ;
t[] = (pid[] & 0xff000000) >> ;
SHA1_Update(&h_ctx, t, sizeof(t)); memset(buf, , sizeof(buf));
BN_bn2bin(x, buf);
endian((unsigned char *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf)); memset(buf, , sizeof(buf));
BN_bn2bin(y, buf);
endian((unsigned char *)buf, sizeof(buf));
SHA1_Update(&h_ctx, buf, sizeof(buf)); SHA1_Final(md, &h_ctx);
hash[] = (md[] | (md[] << ) | (md[] << ) | (md[] << )) >> ;
hash[] &= 0xfffffff; /* s = priv*h + k */
BN_copy(s, priv);
BN_mul_word(s, hash[]);
BN_mod_add(s, s, k, order, ctx); unsigned long sig[] = {};
BN_bn2bin(s, (unsigned char *)sig);
endian((unsigned char *)sig, BN_num_bytes(s));
pack(bkey, pid, hash, sig);
printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[], hash[], sig[], sig[]);
} while (bkey[] >= 0x62a32); base24(pkey, bkey); BN_free(k);
BN_free(s);
BN_free(x);
BN_free(y);
EC_POINT_free(r); BN_CTX_free(ctx);
} int main()
{
// Init
BIGNUM *a, *b, *p, *gx, *gy, *pubx, *puby, *n, *priv;
BN_CTX *ctx = BN_CTX_new(); // make BigNumbers
a = BN_new();
b = BN_new();
p = BN_new();
gx = BN_new();
gy = BN_new();
pubx = BN_new();
puby = BN_new();
n = BN_new();
priv = BN_new(); // Data from pidgen-Bink-resources
/* Elliptic curve parameters: y^2 = x^3 + ax + b mod p */
BN_hex2bn(&p, "92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9");
BN_set_word(a, );
BN_set_word(b, ); /* base point (generator) G */
BN_hex2bn(&gx, "46E3775ECE21B0898D39BEA57050D422A0AF989E497962BAEE2CB17E0A28D5360D5476B8DC966443E37A14F1AEF37742");
BN_hex2bn(&gy, "7C8E741D2C34F4478E325469CD491603D807222C9C4AC09DDB2B31B3CE3F7CC191B3580079932BC6BEF70BE27604F65E"); /* inverse of public key */
BN_hex2bn(&pubx, "5D8DBE75198015EC41C45AAB6143542EB098F6A5CC9CE4178A1B8A1E7ABBB5BC64DF64FAF6177DC1B0988AB00BA94BF8");
BN_hex2bn(&puby, "23A2909A0B4803C89F910C7191758B48746CEA4D5FF07667444ACDB9512080DBCA55E6EBF30433672B894F44ACE92BFA"); // Computed data
/* order of G - computed in 18 hours using a P3-450 */
BN_hex2bn(&n, "DB6B4C58EFBAFD"); /* THE private key - computed in 10 hours using a P3-450 */
BN_hex2bn(&priv, "565B0DFF8496C8"); // Calculation
EC_GROUP *ec = EC_GROUP_new_curve_GFp(p, a, b, ctx);
EC_POINT *g = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, g, gx, gy, ctx);
EC_POINT *pub = EC_POINT_new(ec);
EC_POINT_set_affine_coordinates_GFp(ec, pub, pubx, puby, ctx); unsigned char pkey[];
unsigned long pid[];
pid[] = << ; /* <- change */ // generate a key
generate(pkey, ec, g, n, priv, pid);
print_product_key(pkey); printf("\n\n"); // verify the key
verify(ec, g, pub, (char*)pkey); // Cleanup
BN_CTX_free(ctx); return ;
}

=========== End

WindowsXP序列号产生原理(椭圆曲线法)的更多相关文章

  1. 七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)

    http://blog.csdn.net/xw20084898/article/details/17564957 一.工具:VC+OpenCV 二.语言:C++ 三.原理 otsu法(最大类间方差法, ...

  2. 【转】七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)

    http://blog.csdn.net/xw20084898/article/details/17564957 一.工具:VC+OpenCV 二.语言:C++ 三.原理 otsu法(最大类间方差法, ...

  3. 欧几里得(Euclid)与拓展的欧几里得算法

    欧几里得(Euclid)与拓展的欧几里得算法 欧几里得(Euclid)与拓展的欧几里得算法 欧几里得算法 原理 实现 拓展的欧几里得算法 原理 递归求解 迭代求解 欧几里得算法 原理 欧几里得算法是一 ...

  4. 升级正版win10及保持yosemite双操

    因为有同事升级了正版的win10,心头长草了,本来x230的win7就是正版,现在win10可以免费升级,为何不做? 为此跑了2趟lenovo的维修站,诸多限制,最终决定自己搞定.据说,需要恢复到原厂 ...

  5. Renascence架构介绍——文件夹

    这一系列文章是为个人项目作一个介绍.有兴趣的朋友能够关注一下. https://github.com/jxt1234/Renascence 先写个文件夹.以后按文件夹更新 1.自己主动编程体系设想 2 ...

  6. ECC 算法

    一.简介 1)椭圆曲线密码学的初级读本 http://8btc.com/thread-1240-1-1.html 2)ECC加密算法入门介绍 http://www.pediy.com/kssd/ped ...

  7. 我的Java开发学习之旅------>Java经典排序算法之插入排序

    一.算法原理 插入排序法:所谓插入排序法乃是将一个数目插入该占据的位置. 假设我们输入的是 "53,27,36,15,69,  42" 我们从第二个数字开始,这个数字是27,我们的 ...

  8. 【C/C++】n皇后问题/全排列/递归/回溯/算法笔记4.3

    按常规,先说一下我自己的理解. 递归中的return常用来作为递归终止的条件,但是对于返回数值的情况,要搞明白它是怎么返回的.递归的方式就是自己调用自己,而在有返回值的函数中,上一层的函数还没执行完就 ...

  9. K近邻法(KNN)原理小结

    K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...

随机推荐

  1. 显卡,显卡驱动,nvcc, cuda driver,cudatoolkit,cudnn到底是什么?

    在使用深度学习框架的过程中一定会经常碰到这些东西,虽然anaconda有时会帮助我们自动地解决这些设置,但是有些特殊的库却还是需要我们手动配置环境,但是我对标题上的这些名词其实并不十分清楚,所以老是被 ...

  2. centos安装安全狗提示Need system command 'locate' to install safedog for linux的解决方法

    今天为客户的centos服务器安装安全狗时提示Need system command 'locate' to install safedog for linux.Installation aborte ...

  3. Tomcat8 访问 manager App 失败

    Tomcat8 访问 manager App 失败 进入 tomcat 8 的下面路径 修改 上面 的 context.xml 注释了下面的框框 保存退出.重启tomcat

  4. redis使用摘要

    一.redis使用: 在下载安装好redis后,pycharm内也需要安装redis工具包.cmd窗口运行pip install redis后才可在pycharm 内导入import redis来使用 ...

  5. 关于qt creator各种假死的问题

    来自CSDN网友( qq191329827)内容,亲自尝试,且经历一致: 我有两个笔记本,1个台式机,都装了qt, 然后,我的thinkpad x1c,装上之后creator各种假死,网上看了好多解决 ...

  6. SEDA 架构

    参考文档: https://blog.csdn.net/zhihui1017/article/details/50502825

  7. Spring Security教程之Jsp标签(八)

    目录 1.1     authorize 1.2     authentication 1.3     accesscontrollist Spring Security也有对Jsp标签的支持的标签库 ...

  8. 基于ELK 7.50搭建elastalert 监控报警和权限控制

    ELK+监控报警全步骤 需求: 公司要求对出在windows服务器上的日志进行日志分析并根据关键字进行报警,并配置kibana权限控制.下面为详细步骤 环境: centos 7.6 elk版本7.50 ...

  9. docker 学习操作记录 4

    记录3 [BEGIN] // :: Connecting to ... Connection established. To escape to local shell, press Ctrl+Alt ...

  10. centos下导出docx为html

    yum -y install libreoffice.x86_64 libreoffice --invisible --convert-to html --outdir /root/demo_html ...