椭圆曲线加密分析FLEXLM ECC问答Word文档格式.docx
《椭圆曲线加密分析FLEXLM ECC问答Word文档格式.docx》由会员分享,可在线阅读,更多相关《椭圆曲线加密分析FLEXLM ECC问答Word文档格式.docx(45页珍藏版)》请在冰点文库上搜索。
椭圆曲线加密。
从此,它一直是flexlm的金钟罩。
包括v9(2003-)
v10(2004-)
v11(2007-),
到最新的版本
v11.10
(2012)
。
FlexLm的ECC没有它自己的东西,完全采购自椭圆曲线加密系统的专利拥有者:
Certicom公司。
换而言之,
flexlm
ecc是购买自certicom的一套代码,相当于在腐朽的木门上套一层黄金甲。
OK,
从此它是不破金身了。
3.
Certicom公司是何方神圣?
RSA和ECC是两大主流的公钥密码算法体系。
相比RSA,ECC晚出生10多年,推广不如前者。
Certicom公司是ECC的主要商业支持者,它拥有多项专利。
其地位可与RSA公司匹敌(RSA于2006年被EMC公司收购)。
有一则消息说,2003年美国国家安全局(NSA)以2500万美元支付了Certicom的26项技术许可。
另外有一则消息,
Certicom
2007年起诉索尼公司,要求其支付PS3,DVD播放器等涉及加密技术侵权的专利费用。
Certicom的创办人Scott
Vanstone,是加拿大滑铁卢大学的数学系教授和皇家科学院院士。
以前是研究椭圆曲线加密的,后来创办Certicom公司,努力把ECC从数学界推广到工业界。
Scott写过一本《椭圆曲线密码学导论》,
颇有名气。
4.
如何用的ECC?
说来话长。
一句话说:
在license验证上,主要用的ECDSA(椭圆曲线数字签名算法)。
具体的讲:
flexlm针对ECDSA有一些自己定义的东西以抵抗破解。
后面我会说到,这些自定义的
东西也不是那么牢固。
5.
ECC的通用破解方法?
ECC的通用破解方法就是:
完全按照它的ECDSA算法签名,
只替换公钥和checksum,
写一个keygen生成license。
从逻辑上讲,生成license的方法和原厂的完全一致。
具体该怎么做呢:
首先,
实现标准的ECDSA签名算法。
椭圆曲线的具体算法,可以用现成的miracl,cryptopp等加密库代码。
只需要搞清楚ECDSA的使用方法就可以。
从早期版本
v9.2
(2004)
到最新版本v11.10
它的ecc公钥都是有checksum的。
替换公钥,首先得搞清楚checksum。
它只有一个函数,不算复杂,后面我会给出代码。
计算这个checksum,
对所有版本都是适合的。
其次,
公钥在文件里是加密打散的,有大量垃圾代码。
这些干扰使得flexlm可读性很差。
获取公钥需要调试。
在我写SlickEdit的patch
keygen,我曾经采用调试的方法,非常不方便。
那么,有没有更方便的方法获取公钥呢?
ECC验证代码在它执行过程中有其自身的特点。
正如游戏辅助工具可以搜索血量,我们也可以写一个辅助工具从内存中获取公钥。
在很多次研究之后,我找出一个通用的方法。
不需要再拘泥于具体的代码,可以dump出公钥
,然后用ecctool生成自己的公私钥对,替换公钥,然后可以就写keygen。
由此写了一个工具,对windows平台可直接操作。
对其它平台,把内存镜像出来操作即可。
对于非Windows平台,可以在VMware里面操作,制作一份snapshot,得到内存镜像。
算法上经过多次优化和排除错误数据,从2GB的内存镜像里找出正确的公钥,平均只需要6秒钟。
我对它的性能非常满意了。
总算是磨刀不误砍柴工。
所以,对于纯粹采用flexlm
ecc
sign的license,是可以做出通用的patch_keygen的,
不需要去考虑它有没有反调试,也不用管具体的细节。
只要dump
公钥,
替换公钥和checksum,
写patch_keygen就可以了。
6.
研究flexlm主要需要哪些资料?
flexlm相对来说资源丰富,基本上每个版本的sdk都有泄漏。
如果想深入研究flexlm的加密算法,flexlm
sdk是必需的。
写kg是必须要读sdk的,值得注意的是v9.2
sdk
sourcecode泄漏。
这个网上可搜索到。
sdk有一部分是c代码,里面最有用的是l_prikey.c,
这里有ECDSA验证的函数。
这个文件的尾部有300行comments,其中有一封email很值得一读。
ECC核心库没有源文件,只有lib文件。
在certicom目录下的lib里面,主要为libsb.lib等(Certicom
的加密库:
Security
Builder)。
lib是混淆过了的,但是不影响ida反编译,只是不便于做sig文件。
主要依靠人脑识别函数,需要经验和时间。
7.
flexlm的key加密强度有哪些?
以flexlm
v9为例,用宏定义表示LM_SIGN_LEVEL。
#define
LM_SIGN2
2
/*
SIGN2=
*/
LM_SIGN
1
SIGN=
the
default
LM_NO_SIGN
0
license
key
v9以后,默认就采用ECC
PUBKEY加密。
SIGN支持ECC,所以大部分情况下用SIGN比较多,有些用SIGN2。
是传统的license
key,
强度最弱,不建议使用。
ECC
PUBKEY
只有3种类别,
113,
163,
239
bits。
对应的sign长度(字符数)
字节为(
bits
+
7
)/8
字节数
打印出来,
用hex
digits表示,ECC的SIGN长度分别是一对
30,42,60
[0-9,A-F]。
采用ECC的,pubkey_strength
必须定义为下面的一个类别,否则l_pubkey_verify会出错:
LM_STRENGTH_113BIT,LM_STRENGTH_163BIT,LM_STRENGTH_239BIT。
LM_STRENGTH_LICENSE_KEY
LM_STRENGTH_DEFAULT
1
LM_STRENGTH_113BIT
2
LM_STRENGTH_163BIT
3
LM_STRENGTH_239BIT
4
LM_STRENGTH_PUBKEY
LM_STRENGTH_113BIT
LM_STRENGTH_VERYHIGH
LM_STRENGTH_239BIT
在l_pubkey_verify
有这么一段初始化代码,
判断pubkey_strength。
代码:
switch(pubkey_strength)
{
case
LM_STRENGTH_LICENSE_KEY:
return
0;
LM_STRENGTH_113BIT:
ellipticCurve
=
&
LM_PUBKEY_CURVE113BIT;
break;
LM_STRENGTH_163BIT:
LM_PUBKEY_CURVE163BIT;
LM_STRENGTH_239BIT:
LM_PUBKEY_CURVE239BIT;
default:
fprintf(stderr,
"
LM_STRENGTH
in
lm_code.h
has
invalid
value
%d\n"
pubkey_strength);
Use
only
LM_STRENGTH_[113|163|239]BIT,
LM_STRENGTH_DEFAULT,
OR
LM_STRENGTH_LICENSE_KEY,
exiting\n"
);
exit
(1);
}
下面,演示
bits的ECDSA签名一段最简单的文本,
msg:
123"
sha
hash:
40BD001563085FC35165329EA1FF5C5ECBDBBEEF
LM_SEED1
0x47d381a0
LM_SEED2
0x4fadf97c
LM_SEED3
0xc4ae244c
l_genkeys:
seed[3]=A081D3477CF9AD4F4C24AEC4
LM_PUBKEY_CURVE113BIT
prvlen=15,
prv=00CFDF0247BF6EC0C8D1AA16DD505F
publen=16,
pub=0301523DD4646BB65FE4238B8AB44D01
>
l_prikey_sign_dbg
start
signing
hash=40BD001563085FC35165329EA1FF5C5ECBDBBEEF
done
siglen=30
sig.r=0048D5DD2A57B1A1B357E98C193E63
sig.s=000A6FFDF76899F05ABFD2EDD9E065
LM_PUBKEY_CURVE163BIT
prvlen=21,
prv=03DC603CB1683D43FF5631BBEEC5396D7BD4067300
publen=22,
pub=0300368FE93082E1ACDD35222AD76782DBA8237B66EC
siglen=42
sig.r=039283F2FEA664BE7628F89BBA9D014E89E3868D2C
sig.s=017DA34A68C3FC64CB6EBE2B13676B04BE97EB5C20
LM_PUBKEY_CURVE239BIT
prvlen=30,
prv=13C0E251A5130072A8D2D953EB2C94FAD487C0141B3197863BCC115D7B7E
publen=31,
pub=035875A53B693A2861837E08FC6A7C58529DF52B565111C3DF55F18E34C9FA
inputlen=3,
input=123
siglen=60
sig.r=1589FCFE91F988D28F7072DBF129424F0D71FA5E7AAC39258F3C408A656A
sig.s=0B50642E8ED77FC6A1E6F805CFA0299F44BC7B8035FE17142812B79EA576
sig.r
sig.s组成一个完整的SIGN,
输出lic的时候,为了可读性,
一般切分为16
bit的分组(4个hex
char)。
比如:
sig.r=038B9BE995B887B2665C02940C00155DD557C278AC95EADC1BD668DF185B
sig.s=1C50F25E81044E4DD9AD072699AAB4A63F4C99249AC8C091F476A6C73682
转化为:
SIGN=“
038B9BE995B887B2665C02940C00155DD557C278AC95EADC1BD668DF185B
1C50F25E81044E4DD9AD072699AAB4A63F4C99249AC8C091F476A6C73682”
或者:
SIGN="
038B
9BE9
95B8
87B2
665C
0294
0C00
155D
D557
C278
AC95
EADC
1BD6
68DF
185B
1C50
F25E
8104
4E4D
D9AD
0726
99AA
B4A6
3F4C
9924
9AC8
C091
F476
A6C7
3682"
对flexlm的程序来说,是没有差别的。
都能处理。
8.
flexlm用到的椭圆曲线有哪些?
flexlm用到三条椭圆曲线,都是有来历的,
名字分别为sect113r1,
sect163k1,
sect239k1。
具体参数可以参看:
//en.wikipedia.org/wiki/SECG
SEC
2:
Recommended
Elliptic
Curve
Domain
Parameters
(Version
2.0)
在flexlm
lib里面,
它是写死的静态变量。
位置在
certicom\libcrvs.lib
保存为3个结构体:
struct
ellipticCurveParameters
sect113r1
ec163a02
ec239a03
头文件在
erticom\i86_n3\include\curves.h
/*===
Curves
Definitions
==================
*
*
sect113r1
(K-163
NIST),
ec163a02
(SEC2,
sect163k1)
ec239a03
(sec2,
sect239k1)
LM_PUBKEY_CURVE113BIT
LM_PUBKEY_CURVE163BIT
LM_PUBKEY_CURVE239BIT
MAXIM_OID_CHARS
31
#pragma
pack(4)
unsigned
char
oid[
];
major[
minor[
}
version;
checksum[
4
fieldSize[
fieldSizeOctets[
octets
basisType[
modulus[
32
const
*ident1;
*ident2;
a[
b[
curveParameter;
value[
64
generatingPoint;
size[
pointOrder;
cofactor;
curveOrder;
pack
(2)
A[
B[
reserved;
;
9.
结构体中
checksum的怎么计算的?
带有4字节checksum,
每次初始化ECC计算时,都会校验checksum。
一方面为了避错,
另一方面为了反对篡改。
修改其中的公钥,必须重新计算checksum。
从libsb.lib里面分析反汇编代码,
逆向为c代码,经测试无误的代码:
int
checksum(unsigned
len,
void
*src
*val)
i;
h;
c;
*p
(unsigned
*)src;
if((p
==
0)
||
(val
(len
0))
1;
i
c
*val;
while(i
<
len)
p[i]
16;
h
0xF0000000;
if
(
!
0)
^=
(h
24);
~h;
i++;
*val
getbits(unsigned
*p,
val;
val
while(len
--)
8;
+=
*p++;
getbytes(unsigned
getbits(p
len);
((bits
7)
3);
do_ecp_checksum(struct
*e,
*sum1,
*sum2)
getbytes(e->
fieldSize,
2);
bytes
*)e;
#if
checksum(sizeof(e->
oid),
e->
oid,
sum2);
version.major),
version.major,
version.minor),
version.minor,
fieldSize),
fieldSizeOctets),
fieldSizeOctets,
basisType),
basisType,
#endif
checksum(32u,
p,
sum1);
checksum(1u,
p
32,
33,
checksum(2u,
38,
40,
41,
s