2009年6月11日星期四

ECC加密算法入门介绍

题:ECC加密算法入门介绍
发信人:zmworm
时 间:2003/05/04 08:32pm
详细信息:

ECC加密算法入门介绍


作者  :ZMWorm[CCG]
E-Mail:zmworm@sohu.com
主页  :Http://ZMWorm.Yeah.Net/


前言

   同RSA(Ron Rivest,Adi Shamir,Len Adleman三位天才的名字)一样,ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)也属于公开密钥算法。目前,国内详细介绍ECC的公开文献并不多(反正我没有找到)。有一些简介,也是泛泛而谈,看完后依然理解不了ECC的实质(可能我理解力太差)。前些天我从国外网站找到些材料,看完后对ECC似乎懵懂了。于是我想把我对ECC的认识整理一下,与大家分享。当然ECC博大精深,我的认识还很肤浅,文章中错误一定不少,欢迎各路高手批评指正,小弟我洗耳恭听,并及时改正。文章将采用连载的方式,我写好一点就贴出来一点。本文主要侧重理论,代码实现暂不涉及。这就要求你要有一点数学功底。最好你能理解RSA算法,对公开密钥算法有一个了解。《近世代数基础》《初等数论》之类的书,最好您先翻一下,这对您理解本文是有帮助的。别怕,我尽量会把语言通俗些,希望本文能成为学习ECC的敲门砖。

一、从平行线谈起。

   平行线,永不相交。没有人怀疑把:)不过到了近代这个结论遭到了质疑。平行线会不会在很远很远的地方相交了?事实上没有人见到过。所以"平行线,永不相交"只是假设(大家想想初中学习的平行公理,是没有证明的)。既然可以假设平行线永不相交,也可以假设平行线在很远很远的地方相交了。即平行线相交于无穷远点P∞(请大家闭上眼睛,想象一下那个无穷远点P∞,P∞是不是很虚幻,其实与其说数学锻炼人的抽象能力,还不如说是锻炼人的想象力)。给个图帮助理解一下:

 

   直线上出现P∞点,所带来的好处是所有的直线都相交了,且只有一个交点。这就把直线的平行与相交统一了。为与无穷远点相区别把原来平面上的点叫做平常点。

   以下是无穷远点的几个性质。

▲直线L上的无穷远点只能有一个。
(从定义可直接得出)
▲平面上一组相互平行的直线有公共的无穷远点。
(从定义可直接得出)
▲ 平面上任何相交的两直线L1,L2有不同的无穷远点。
(否则L1和L2有公共的无穷远点P ,则L1和L2有两个交点A、P,故假设错误。)
▲平面上全体无穷远点构成一条无穷远直线。(自己想象一下这条直线吧)
▲平面上全体无穷远点与全体平常点构成射影平面




二、射影平面坐标系

   射影平面坐标系是对普通平面直角坐标系(就是我们初中学到的那个笛卡儿平面直角坐标系)的扩展。我们知道普通平面直角坐标系没有为无穷远点设计坐标,不能表示无穷远点。为了表示无穷远点,产生了射影平面坐标系,当然射影平面坐标系同样能很好的表示旧有的平常点(数学也是"向下兼容"的)。



   我们对普通平面直角坐标系上的点A的坐标(x,y)做如下改造:
   令x=X/Z ,y=Y/Z(Z≠0);则A点可以表示为(X:Y:Z)。
   变成了有三个参量的坐标点,这就对平面上的点建立了一个新的坐标体系。

   例2.1:求点(1,2)在新的坐标体系下的坐标。
   解:∵X/Z=1 ,Y/Z=2(Z≠0)∴X=Z,Y=2Z ∴坐标为(Z:2Z:Z),Z≠0。即(1:2:1)(2:4:2)(1.2:2.4:1.2)等形如(Z:2Z:Z),Z≠0的坐标,都是(1,2)在新的坐标体系下的坐标。


   我们也可以得到直线的方程aX+bY+cZ=0(想想为什么?提示:普通平面直角坐标系下直线一般方程是ax+by+c=0)。新的坐标体系能够表示无穷远点么?那要让我们先想想无穷远点在哪里。根据上一节的知识,我们知道无穷远点是两条平行直线的交点。那么,如何求两条直线的交点坐标?这是初中的知识,就是将两条直线对应的方程联立求解。平行直线的方程是:
aX+bY+c1Z =0; aX+bY+c2Z =0  (c1≠c2);
(为什么?提示:可以从斜率考虑,因为平行线斜率相同);

   将二方程联立,求解。有c2Z= c1Z= -(aX+bY),∵c1≠c2 ∴Z=0  ∴aX+bY=0;
所以无穷远点就是这种形式(X:Y:0)表示。注意,平常点Z≠0,无穷远点Z=0,因此无穷远直线对应的方程是Z=0。

   例2.2:求平行线L1:X+2Y+3Z=0 与L2:X+2Y+Z=0 相交的无穷远点。
   解:因为L1∥L2 所以有Z=0, X+2Y=0;所以坐标为(-2Y:Y:0),Y≠0。即(-2:1:0)(-4:2:0)(-2.4:1.2:0)等形如(-2Y:Y:0),Y≠0的坐标,都表示这个无穷远点。


   看来这个新的坐标体系能够表示射影平面上所有的点,我们就把这个能够表示射影平面上所有点的坐标体系叫做射影平面坐标系


练习:
      1、求点A(2,4) 在射影平面坐标系下的坐标。
      2、求射影平面坐标系下点(4.5:3:0.5),在普通平面直角坐标系下的坐标。
      3、求直线X+Y+Z=0上无穷远点的坐标。
      4、判断:直线aX+bY+cZ=0上的无穷远点 和 无穷远直线与直线aX+bY=0的交点,是否是同一个点?


三、椭圆曲线

   上一节,我们建立了射影平面坐标系,这一节我们将在这个坐标系下建立椭圆曲线方程。因为我们知道,坐标中的曲线是可以用方程来表示的(比如:单位圆方程是x2+y2=1)。椭圆曲线是曲线,自然椭圆曲线也有方程。

   椭圆曲线的定义:
   一条椭圆曲线是在射影平面上满足方程
   Y2Z+a1XYZ+a3YZ2=X3+a2X2Z+a4XZ2+a6Z3
  ----------------[3-1]
   的所有点的集合,且曲线上的每个点都是非奇异(或光滑)的。

定义详解:

   ▲ Y2Z+a1XYZ+a3YZ2 = X3+a2X2Z+a4XZ2+a6Z3是Weierstrass方程(维尔斯特拉斯,Karl Theodor Wilhelm Weierstrass,1815-1897),是一个齐次方程。

   ▲ 椭圆曲线的形状,并不是椭圆的。只是因为椭圆曲线的描述方程,类似于计算一个椭圆周长的方程(计算椭圆周长的方程,我没有见过,而对椭圆线积分(设密度为1)是求不出来的。谁知道这个方程,请告诉我呀^_^),故得名。

   我们来看看椭圆曲线是什么样的。



 

   ▲ 所谓"非奇异"或"光滑"的,在数学中是指曲线上任意一点的偏导数Fx(x,y,z),Fy(x,y,z),Fz(x,y,z)不能同时为0。如果你没有学过高等数学,可以这样理解这个词,即满足方程的任意一点都存在切线。

   下面两个方程都不是椭圆曲线,尽管他们是方程[3-1]的形式。




   因为他们在(0:0:1)点处(即原点)没有切线。

   ▲椭圆曲线上有一个无穷远点O∞(0:1:0),因为这个点满足方程[3-1]。

   知道了椭圆曲线上的无穷远点。我们就可以把椭圆曲线放到普通平面直角坐标系上了。因为普通平面直角坐标系只比射影平面坐标系少无穷远点。我们在普通平面直角坐标系上,求出椭圆曲线上所有平常点组成的曲线方程,再加上无穷远点O∞(0:1:0),不就构成椭圆曲线了么?

   我们设x=X/Z ,y=Y/Z代入方程[3-1]得到:
   y2+a1xy+a3y = x3+a2x2+a4x+a6 -------------------------[3-2]

   也就是说满足方程[3-2]的光滑曲线加上一个无穷远点O∞,组成了椭圆曲线。为了方便运算,表述,以及理解,今后论述椭圆曲线将主要使用[3-2]的形式。

   本节的最后,我们谈一下求椭圆曲线一点的切线斜率问题。
   由椭圆曲线的定义可以知道,椭圆曲线是光滑的,所以椭圆曲线上的平常点都有切线。而切线最重要的一个参数就是斜率k。

   例3.1:求椭圆曲线方程y2+a1xy+a3y=x3+a2x2+a4x+a6上,平常点A(x,y)的切线的斜率k。
   解:令F(x,y)= y2+a1xy+a3y-x3-a2x2-a4x-a6
   求偏导数
   Fx(x,y)= a1y-3x2-2a2x-a4
   Fy(x,y)= 2y+a1x +a3
   则导数为:f'(x)=- Fx(x,y)/ Fy(x,y)=-( a1y-3x2-2a2x-a4)/(2y+a1x +a3)
                   = (3x2+2a2x+a4-a1y) /(2y+a1x +a3)
   所以k=(3x2+2a2x+a4-a1y) /(2y+a1x +a3)  ------------------------[3-3]

   看不懂解题过程没有关系,记住结论[3-3]就可以了。


练习:
      1、将给出图例的椭圆曲线方程Y2Z=X3-XZ2 和Y2Z=X3+XZ2+Z3转换成普通平面直角坐标系上的方程。


四、椭圆曲线上的加法

   上一节,我们已经看到了椭圆曲线的图象,但点与点之间好象没有什么联系。我们能不能建立一个类似于在实数轴上加法的运算法则呢?天才的数学家找到了这一运算法则

   ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
   自从近世纪代数学引入了群、环、域的概念,使得代数运算达到了高度的统一。比如数学家总结了普通加法的主要特征,提出了加群(也叫交换群,或 Abel(阿贝尔)群),在加群的眼中。实数的加法和椭圆曲线的上的加法没有什么区别。这也许就是数学抽象把:)。关于群以及加群的具体概念请参考近世代数方面的数学书。
   ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

   运算法则:任意取椭圆曲线上两点P、Q (若P、Q两点重合,则做P点的切线)做直线交于椭圆曲线的另一点R',过R'做y轴的平行线交于R。我们规定P+Q=R。(如图)




法则详解:
   ▲这里的+不是实数中普通的加法,而是从普通加法中抽象出来的加法,他具备普通加法的一些性质,但具体的运算法则显然与普通加法不同。

   ▲根据这个法则,可以知道椭圆曲线无穷远点O∞与椭圆曲线上一点P的连线交于P',过P'作y轴的平行线交于P,所以有 无穷远点 O∞+ P = P 。这样,无穷远点 O∞的作用与普通加法中零的作用相当(0+2=2),我们把无穷远点 O∞ 称为 零元。同时我们把P'称为P的负元(简称,负P;记作,-P)。(参见下图)

 

   ▲根据这个法则,可以得到如下结论 :如果椭圆曲线上的三个点A、B、C,处于同一条直线上,那么他们的和等于零元,即A+B+C= O∞

   ▲k个相同的点P相加,我们记作kP。如下图:P+P+P = 2P+P = 3P。



   下面,我们利用P、Q点的坐标(x1,y1),(x2,y2),求出R=P+Q的坐标(x4,y4)。

   例4.1:求椭圆曲线方程y2+a1xy+a3y=x3+a2x2+a4x+a6上,平常点P(x1,y1),Q(x2,y2)的和R(x4,y4)的坐标。
   解:(1)先求点-R(x3,y3)
   因为P,Q,-R三点共线,故设共线方程为y=kx+b,其中
   若P≠Q(P,Q两点不重合) 则
       直线斜率k=(y1-y2)/(x1-x2)
   若P=Q(P,Q两点重合) 则直线为椭圆曲线的切线,故由例3.1可知:
       k=(3x2+2a2x+a4 -a1y) /(2y+a1x+a3)

   因此P,Q,-R三点的坐标值就是方程组:
       y2+a1xy+a3y=x3+a2x2+a4x+a6    -----------------[1]
       y=(kx+b)                     -----------------[2]
   的解。

   将[2],代入[1] 有
      (kx+b)2+a1x(kx+b)+a3(kx+b) =x3+a2x2+a4x+a6    --------[3]
   对[3]化为一般方程,根据三次方程根与系数关系(当三次项系数为1时;-x1x2x3 等于常数项系数, x1x2+x2x3+x3x1等于一次项系数,-(x1+x2+x3)等于二次项系数。)
   所以-(x1+x2+x3)=a2-ka1-k2
   x3=k2+ka1+a2+x1+x2;---------------------求出点-R的横坐标
   因为k=(y1-y3)/(x1-x3) 故
   y3=y1-k(x1-x3);-------------------------------求出点-R的纵坐标

  (2)利用-R求R
   显然有 x4=x3= k2+ka1+a2+x1+x2; ------------求出点R的横坐标
   而y3 y4 为 x=x4时 方程y2+a1xy+a3y=x3+a2x2+a4x+a6的解
   化为一般方程y2+(a1x+a3)y-(x3+a2x2+a4x+a6)=0 , 根据二次方程根与系数关系得:
       -(a1x+a3)=y3+y4
   故y4=-y3-(a1x+a3)=k(x1-x4)-y1-(a1x4+a3); ---------------求出点R的纵坐标
   即:
       x4=k2+ka1+a2+x1+x2;
       y4=k(x1-x4)-y1-a1x4-a3;

   本节的最后,提醒大家注意一点,以前提供的图像可能会给大家产生一种错觉,即椭圆曲线是关于x轴对称的。事实上,椭圆曲线并不一定关于x轴对称。如下图的y2-xy=x3+1


五、密码学中的椭圆曲线

   我们现在基本上对椭圆曲线有了初步的认识,这是值得高兴的。但请大家注意,前面学到的椭圆曲线是连续的,并不适合用于加密;所以,我们必须把椭圆曲线变成离散的点。
   让我们想一想,为什么椭圆曲线为什么连续?是因为椭圆曲线上点的坐标,是实数的(也就是说前面讲到的椭圆曲线是定义在实数域上的),实数是连续的,导致了曲线的连续。因此,我们要把椭圆曲线定义在有限域上(顾名思义,有限域是一种只有由有限个元素组成的域)。

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
   域的概念是从我们的有理数,实数的运算中抽象出来的,严格的定义请参考近世代数方面的书。简单的说,域中的元素同有理数一样,有自己得的加法、乘法、除法、单位元(1),零元(0),并满足交换率、分配率。
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

   下面,我们给出一个有限域Fp,这个域只有有限个元素。
   
   Fp中只有p(p为素数)个元素0,1,2 …… p-2,p-1;
   Fp 的加法(a+b)法则是 a+b≡c (mod p);即,(a+c)÷p的余数 和c÷p的余数相同。
   Fp 的乘法(a×b)法则是  a×b≡c (mod p);
   Fp 的除法(a÷b)法则是  a/b≡c (mod p);即 a×b-1≡c  (mod p);(b-1也是一个0到p-1之间的整数,但满足b×b-1≡1 (mod p);具体求法可以参考初等数论,或
我的另一篇文章)。
   Fp 的单位元是1,零元是 0。

   同时,并不是所有的椭圆曲线都适合加密。y2=x3+ax+b是一类可以用来加密的椭圆曲线,也是最为简单的一类。下面我们就把y2=x3+ax+b 这条曲线定义在Fp上:

   选择两个满足下列条件的小于p(p为素数)的非负整数a、b
      4a3+27b2≠0 (mod p)
   则满足下列方程的所有点(x,y),再加上 无穷远点O∞ ,构成一条椭圆曲线。
     y2=x3+ax+b  (mod p)
   其中 x,y属于0到p-1间的整数,并将这条椭圆曲线记为Ep(a,b)。

   我们看一下y2=x3+x+1  (mod 23)的图像
   


   是不是觉得不可思议?椭圆曲线,怎么变成了这般模样,成了一个一个离散的点?
   椭圆曲线在不同的数域中会呈现出不同的样子,但其本质仍是一条椭圆曲线。举一个不太恰当的例子,好比是水,在常温下,是液体;到了零下,水就变成冰,成了固体;而温度上升到一百度,水又变成了水蒸气。但其本质仍是H2O。

   Fp上的椭圆曲线同样有加法,但已经不能给以几何意义的解释。不过,加法法则和实数域上的差不多,请读者自行对比。

   1 无穷远点 O∞是零元,有O∞+ O∞= O∞,O∞+P=P
   2 P(x,y)的负元是 (x,-y),有P+(-P)= O∞
   3 P(x1,y1),Q(x2,y2)的和R(x3,y3) 有如下关系:
     x3≡k2-x1-x2(mod p)
     y3≡k(x1-x3)-y1(mod p)
     其中若P=Q 则 k=(3x2+a)/2y1  若P≠Q,则k=(y2-y1)/(x2-x1)


   例5.1 已知E23(1,1)上两点P(3,10),Q(9,7),求1)-P,2)P+Q,3) 2P。
   解 1)  �P的值为(3,-10)
      2)  k=(7-10)/(9-3)=-1/2,2的乘法逆元为12 因为2*12≡1 (mod 23)
          k≡-1*12 (mod 23) 故 k=11。
          x=112-3-9=109≡17 (mod 23);
          y=11[3-(-6)]-10=89≡20 (mod 23)
          故P+Q的坐标为(17,20)
      3)  k=[3(32)+1]/(2*10)=1/4≡6 (mod 23)
          x=62-3-3=30≡20 (mod 23)
          y=6(3-7)-10=-34≡12 (mod 23)
          故2P的坐标为(7,12)
 
   
   最后,我们讲一下椭圆曲线上的点的阶。
   如果椭圆曲线上一点P,存在最小的正整数n,使得数乘nP=O∞,则将n称为P的 ,若n不存在,我们说P是无限阶的。
   事实上,在有限域上定义的椭圆曲线上所有的点的阶n都是存在的(证明,请参考近世代数方面的书)


练习:
  1 求出E11(1,6)上所有的点。
  2 已知E11(1,6)上一点G(2,7),求2G到13G所有的值。


六、椭圆曲线上简单的加密/解密

   公开密钥算法总是要基于一个数学上的难题。比如RSA 依据的是:给定两个素数p、q 很容易相乘得到n,而对n进行因式分解却相对困难。那椭圆曲线上有什么难题呢?

   考虑如下等式:
   K=kG  [其中 K,G为Ep(a,b)上的点,k为小于n(n是点G的阶)的整数]
   不难发现,给定k和G,根据加法法则,计算K很容易;但给定K和G,求k就相对困难了。
   这就是椭圆曲线加密算法采用的难题。我们把点G称为基点(base point),k(k<n,n为基点G的阶)称为私有密钥(privte key),K称为公开密钥(public key)。

   现在我们描述一个利用椭圆曲线进行加密通信的过程:

   1、用户A选定一条椭圆曲线Ep(a,b),并取椭圆曲线上一点,作为基点G。
   2、用户A选择一个私有密钥k,并生成公开密钥K=kG。
   3、用户A将Ep(a,b)和点K,G传给用户B。
   4、用户B接到信息后 ,将待传输的明文编码到Ep(a,b)上一点M(编码方法很多,这里不作讨论),并产生一个随机整数r(r<n)。
   5、用户B计算点C1=M+rK;C2=rG。
   6、用户B将C1、C2传给用户A。
   7、用户A接到信息后,计算C1-kC2,结果就是点M。因为
          C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M
      再对点M进行解码就可以得到明文。

   在这个加密通信中,如果有一个偷窥者H ,他只能看到Ep(a,b)、K、G、C1、C2 而通过K、G 求k 或通过C2、G求r 都是相对困难的。因此,H无法得到A、B间传送的明文信息。



   密码学中,描述一条Fp上的椭圆曲线,常用到六个参量:
       T=(p,a,b,G,n,h)。
   (p 、a 、b 用来确定一条椭圆曲线,
   G为基点,
   n为点G的阶,
   h 是椭圆曲线上所有点的个数m与n相除的整数部分)

   这几个参量取值的选择,直接影响了加密的安全性。参量值一般要求满足以下几个条件:

   1、p 当然越大越安全,但越大,计算速度会变慢,200位左右可以满足一般安全要求;
   2、p≠n×h;
   3、pt≠1 (mod n),1≤t<20;
   4、4a3+27b2≠0 (mod p);
   5、n 为素数;
   6、h≤4。


七、椭圆曲线在软件注册保护的应用

   我们知道将公开密钥算法作为软件注册算法的好处是Cracker很难通过跟踪验证算法得到注册机。下面,将简介一种利用Fp(a,b)椭圆曲线进行软件注册的方法。


   软件作者按如下方法制作注册机(也可称为签名过程)

   1、选择一条椭圆曲线Ep(a,b),和基点G;
   2、选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG;
   3、产生一个随机整数r(r<n),计算点R=rG;
   4、将用户名和点R的坐标值x,y作为参数,计算SHA(Secure Hash Algorithm 安全散列算法,类似于MD5)值,即Hash=SHA(username,x,y);
   5、计算sn≡r - Hash * k (mod n)
   6、将sn和Hash作为 用户名username的序列号

软件验证过程如下:(软件中存有椭圆曲线Ep(a,b),和基点G,公开密钥K)

   1、从用户输入的序列号中,提取sn以及Hash;
   2、计算点R≡sn*G+Hash*K ( mod p ),如果sn、Hash正确,其值等于软件作者签名过程中点R(x,y)的坐标,因为
      sn≡r-Hash*k (mod n)
      所以
       sn*G + Hash*K
      =(r-Hash*k)*G+Hash*K
      =rG-Hash*kG+Hash*K
      =rG- Hash*K+ Hash*K
      =rG=R ;
   3、将用户名和点R的坐标值x,y作为参数,计算H=SHA(username,x,y);
   4、如果H=Hash 则注册成功。如果H≠Hash ,则注册失败(为什么?提示注意点R与Hash的关联性)。

   简单对比一下两个过程:
   作者签名用到了:椭圆曲线Ep(a,b),基点G,私有密钥k,及随机数r。
   软件验证用到了:椭圆曲线Ep(a,b),基点G,公开密钥K。
   Cracker要想制作注册机,只能通过软件中的Ep(a,b),点G,公开密钥K ,并利用K=kG这个关系获得k后,才可以。而求k是很困难的。


练习:
   下面也是一种常于软件保护的注册算法,请认真阅读,并试回答签名过程与验证过程都用到了那些参数,Cracker想制作注册机,应该如何做。

   软件作者按如下方法制作注册机(也可称为签名过程)
   1、选择一条椭圆曲线Ep(a,b),和基点G;
   2、选择私有密钥k(k<n),利用基点G计算公开密钥K=kG;
   3、产生一个随机整数r(r<n),计算点R(x,y)=rG;
   4、将用户名作为参数,计算Hash=SHA(username);
   5、计算 x'=x  (mod n)
   6、计算sn≡(Hash+x'*k)/r (mod n)
   7、将sn和x'作为 用户名username的序列号

   软件验证过程如下:(软件中存有椭圆曲线Ep(a,b),和基点G,公开密钥K)
   1、从用户输入的序列号中,提取sn以及x';
   2、将用户名作为参数,计算Hash=SHA(username);
   3、计算 R=(Hash*G+x'*K)/sn,如果sn、Hash正确,其值等于软件作者签名过程中点R(x,y),因为
      sn≡(Hash+x'*k)/r (mod n)
      所以
       (Hash*G+x'*K)/sn
      =(Hash*G+x'*K)/[(Hash+x'*k)/r]
      =(Hash*G+x'*K)/[(Hash*G+x'*k*G)/(rG)]
      =rG*[(Hash*G+x'*K)/(Hash*G+x'*K)]
      =rG=R (mod p)
   4、v≡x (mod n)
   5、如果v=x' 则注册成功。如果v≠x' ,则注册失败。


八、结语

   历经半个多月断断续续的写作,这篇拙作终于算告一段落了。为写这篇文章,我查了大量的资料,但为了使文章更通俗易懂,我尽量避免涉及专业术语,F2n 域上的椭圆曲线本文也没有涉及。不过,一些名词描述的可能还不太精确,希望众读者对文章的问题,多多批评指正。我也仅仅把这篇文章作为初稿,我会不断修订他的。最后感谢看雪、Sunbird、CCG以及看雪论坛所有成员对我的支持,感谢一切帮助过我的人,没有你们的鼓励,这篇文章我是没有动力写完的,谢谢,谢谢大家!


2003-5-3 初稿,于看雪论坛
2004-7-11二稿,修正一张图片

<全文完>


主要参考文献

张禾瑞,《近世代数基础》,高等教育出版社,1978
闵嗣鹤 严士健,《初等数论》,高等教育出版社,1982
段云所,《网络信息安全》第三讲,北大计算机系
Michael Rosing ,chapter5《Implementing Elliptic Curve Cryptography》,Softbound,1998
《SEC 1: Elliptic Curve Cryptography》,Certicom Corp.,2000
《IEEE P1363a / D9》,2001


┌───┐  �������������������������
│\   �│  �  ☆        ECC加密算法入门介绍           ★  �
│ \�/ │  �������������������������
│�∨  │  �  ☆版权所有 ZMWorm[CCG], 转载请保留完整性★  �
└─┴─┘  �������������������������

FLEXlm9.2的破解教学三

FLEXlm9.2的破解教学三
信息来源:看雪学院技术论坛
翻译者: newsearch


翻译第三篇 On Software Reverse Engineering - 3
Reverse Engineering, FLEXlm, IMSL

揭示加密钥和加密种子

从实际的观点出发我们现在可以止步不前了,因为有了许可的拷贝我们可以自由的使用目标程序了。但是,软件破解的最终目的是反向(逆向)工程的所有相关算法并重新创建它们,那就好像我们是原始作者一样。在我们的实例中,最低限度的要求是揭示VNI的5个vendor密钥和3个随机种子。不用说,这要求我们更仔细的阅读FLEXLM SDK源代码,因此我们简单的回顾一下它是怎样组织的。下面的列表显示了包含最关键文件的文件夹。

src lmgr.lib或lmgr9a.dll的源文件
app lmgras.lib的源文件
server lmgrs.lib的源文件
master lmgrd.exe的源文件
utils 实用工具的源文件
machind 机器独立文件(源 & 头)
i86_n3 对于x86 平台的最终的二进制文件
h 头文件
certicom 来自Certicom公司的库和头文件
ulite FLEXLM的ultralite版本

此外,文件和函数根据其角色被指定了不同的前缀。例如,目录app, server和master以ls_开始,而在utils中是以lm为前缀。

l_ 许可证, 用于内部函数
lc_ 许可证客户端, 用于客户端API
ls_ 许可证服务器, 用于服务器API
lm_ 许可证管理器, 用于实用工具和一般的程序(原为材料)

现在,我们回到文献[2]和[3]中关于Vendor怎样实用FLEXLM SDK的指导中。基本地,Vendor需要将许多FLEXLM二进制与其自己的产品结合并创建若干实用工具-一些作为内部使用,一些用于最终用户发布。在这些Vendor-所产生的文件之间有很亲密的联系,下面是其关系表:

文件 由...产生 #include
lmrand1.exe lmrand1.c + lmgr.lib
lmcode.c, lsrvend.c lmrand1.exe + lsvendor.c lm_code.h
lmappfil.c, lmkeyfil.c lmrand1.exe
lmnewgen.exe lmnewgen.c + lmcode.c + lmgr.lib lm_code.h
lm_new.c lmnewgen.exe seeds & pubkey
lmseeds.h lmnewgen.exe
lmprikey.h, lmpubkey.h lmnewgen.exe
lmcrypt.exe lmcrypt.c + lmgr.lib lm_code.h,
lmseeds.h,
lmprikey.h
makekey.exe makekey.c + lmgr.lib lm_code.h,
lmseeds.h,
lmprikey.h
vendor application vendor code + lm_new.obj + lmgr.lib + seeds & pubkey

libsb.lib + libcrvs.lib
vendor daemon lsvendor.obj + lm_new.obj + lmgr.lib + lm_code.h,
lsserver.h,
lmgras.lib + lmgrs.lib + libsb.lib + seeds & pubkey
libcrvs.lib

注意,并非上述的所有文件都对我们是重要的。lmappfil.c和lmkeyfil.c是Vendor确定的滤波器,它们是附加的安全测试但通常未用。类似的,lmprikey.h 和lmpubkey.h仅用于CRO密钥,在我们的例子中可以忽略。通过编辑lsrvend.c 或lsvendor.c,Vendor可定制后台程序,但很少有人那样做。
有一件值得讨论的事情是Certicom产品,也即libsb.lib和libcrvs.lib 。它们几乎涵盖了从SHA到DSA的整个加密领域,而且FLEXLM采用的API几乎为ECC(椭圆曲线加密技术)和RGN(随机数字产生)。ECC用于在CRO中的公开/专用密钥对,但在我们的目标程序中关闭了;而RGN用于Vendor种子的转换,我们不可忽略。
正如先前声明的,Vendor选择了3个随机种子(LM_SEED1, LM_SEED2, LM_SEED3),但并不直接使用它们。实际上,基于前3个,lmnewgen.c!main()

2009年6月10日星期三

翻译第一篇 On Software Reverse Engineering - 1
Reverse Engineering, FLEXlm, IMSL
本文讨论了软件反向工程方法和FLEXLM系统的学习实例。
问题描述
我下载了IMSL_CNL5.5(C数字库),但它由FLEXLM保护。对于不同的OS和编译器有不同的二进制下载,但通过平台可采用通用的破解技术(在我们的例子中,FLEXLM许可证文件是当然的独立平台)。该产品主要包含了数学和统计库(静态和动态的)子程序,FLEXLM的FEATURE 名称为"CMATH"和"CSTAT"。X86的发布版还有一个CMATH的优化版本叫做"CMPERF",它通过捆绑Intel MKL6.1以实现更有效的运行。对于CMPERF没有额外的许可证。因为CMATH和CSTAT使用了相同的许可证机制,从现在开始,我们将聚焦在CMATH上。CSTAT 的(破解)过程是完全类似的。
出于有效的目的,该步提供了一个简单的程序"cmath.c",将它和目标文件"cmath_s.lib"链接以得到可执行文件。它调用 imsl_f_lin_sol_gen()并检查许可证文件,如果许可证文件不正确,将报告错误。从网上,我们找到IMS_CNL5.0的如下许可证文件:
SERVER hostname hostid 27000
DAEMON VNI "<vni_dir>\license\bin\bin.i386nt\vni.exe"
FEATURE CMATH VNI 5.0 permanent uncounted 3F23BE3056E4 HOSTID=ANY
FEATURE CSTAT VNI 5.0 permanent uncounted 2C60CD4570B0 HOSTID=ANY
我们试用它并如预料一样的得到"Version incorrect(版本不正确)"的错误;用5.5替换5.0,得到 "incorrect softkey code(不正确的软件加密代码)"错误,因此很明显的该方法不会工作。事实上,它采取了相当复杂的工作以应对 FLEXLM。软件破解有不同的级别,其相应的复杂范围从相对简单到相当困难-我们将在后面看到。现在,我们在下面列出了我们的任务和使用的工具。
对象:Visual Numerics IMSL CNL 5.5
保护:Macrovision FLEXlm 9.2
工具:Microsoft Visual Studio 7.1 (CL, NMAKE, DUMPBIN, LIB, …)
RedHat Cygwin 1.3.5
IDM UltraEdit 9.0
Datarescue IDA Pro 4.3 (FLAIR, …)
URSoft W32Dasm 8.9
Sysinternals File Monitor 6.0
资源:Macrovision FLEXlm 9.2 SDK source code
Macrovision FLEXlm 8.1 SDK binary release
初步试探
所有的破解起步于信息收集。例如,通过在UltraEdit中搜索cmath_s.lib很容易发现目标程序采用了FLEXLM9.2进行许可证管理。阅读相关的文献也是非常有帮助的。在[4]中有一些优秀的论文,讲述了以前攻击早期的FLEXLM版本,它们包含了许多珍贵的知识。
在实践中,我加载cmath.exe到W32Dasm调试器中并很快开始了漂亮的追踪。通过反汇编代码集追踪需要惊人的经验,可能你花费了数小时进行来回的跳转而没有得到程序实际运行的任何线索。但我试图找出一些(有用的)东西(在File Monitor的帮助下):
004133DE E89D2F0100 call 00426380 ;读 license.dat
... ...
00413411 ? 0041345B ... ... ;循环读取"CMATH"
... ...
004138CD E83E670000 call 0041A010 ;必须返回0以通过检查
004138D2 83C41C add esp, 0000001C
004138D5 8945F0 mov dword ptr [ebp-10], eax ;返回值存储在EAX中
004138D8 837DF000 cmp dword ptr [ebp-10], 00000000
004138DC 0F848C070000 je 0041406E ;进行imsl_f_lin_sol_gen()
... ...
00413907 8B8D18F7FFFF mov ecx, dword ptr [ebp+FFFFF718]
0041390D 33C0 xor eax, eax
0041390F 8A81A7444100 mov al, byte ptr [ecx+004144A7]
00413915 FF24857F444100 jmp dword ptr [4*eax+0041447F] ;跳到错误信息
很明显,子程序0041A010是关键。步入里面以揭示更多复杂的结构-在它里面有一些复杂的调用链。在实践中,该过程返回 FFFFFFF8 (or ?8),它将是错误代码。因此,我们在004138D5处设置一个断点,使用W32Dasm"Modify Data(修改数据)"按键修改EAX寄存器的值为0,并让它继续运行。哇噻!程序被迷惑了,并且输出了正确的结果就好像我们已有了正确的许可证数据。
现在,事情变得很明朗了,我们可以在代码段004138D2 ? 004138DC上打补丁以设置返回值总是为0。为了达到该目的,我们可以参照 [5]中关于MOV和JMP的详细X86指令格式,剩下的事情就是修改代码了。注意,添加NOP用于补缀更改留下的特别的字节以使修改的可执行文件和原文件的长度相同。事实上,我们改变的仅有三行不同:
004138CD E83E670000 call 0041A010 ;返回必须为0以通过检查
004138D2 83C41C add esp, 0000001C ;堆栈指针调整
004138D5 C745F000000000 mov [ebp-10], 00000000 ;假装返回值为O
004138DC 90 nop ;补缀特别的字节以保持代码排列
004138DD E98C070000 jmp 0041406E ;无条件跳转到imsl_f_lin_sol_gen()
当然补丁cmath.exe仅是概念的证据,实际事情是补丁库自身。cmath.c per se是非常短的,事实上cmath.exe的所有内容来自cmath_s.lib,包括IMSL函数和FLEXLM代码。为了定位上面的代码,我们在UltraEdit[2]中搜索二进制串 837DF0000F848C070000 (关于CMP和JE行的代码),它将引导我们到在cmath_s.lib中代码放置的唯一位置(文件偏移 00106F70 ? 00106F80)。照着更改字节,我们将得到一个补丁库。通过链接程序到新的补丁库进行测试,除了许可证文件无效外,所有功能都运行良好。
因此,我们已经取得了第一个成功。接着,我们对库的DLL版本做相同的事情,并且理想的开发一个工具自动进行而不是手工进行,但现在我们要省略它。在这点上,补丁通常是最容易的也是破解的第一步,它仅要求对保护方案有很少的洞察。此外,补丁仅针对一个特定的二进制目标工作,补丁工具对不同的版本或在不同的平台上无效。尽管补丁是强有力和有效的,但它距完全的反向工程还很远。
进一步分析
我们采用通过一个假的"OK"来迷惑FLEXLM,但是我们仍然不知道真的许可证代码。许多软件采用这种保护方式:基于用户信息(名称,组织,购买的特征等)和某种算法,程序计算一些hash/checksum/license code(许可证代码)/signature(签名),并将它和一个用户提供的(数据)进行比较。如果它们匹配,用户将被授权。对于我们所见的所有软件,该机制几乎是通用的,FLEXLM也不例外。在我们的许可证文件中,代码3F23BE3056E4是"SIGN="签名。
关于FLELXELM更多的一点:之所以叫FLEXLM是因为它声称对商业软件许可证管理提供了一个灵活的解决方案。它作了许多努力以适应各种情况-counted/uncounted, feature/incremental, server /local, borrowing, trial, mobile, …(保持原文) ?但是我们对这些并不感兴趣。我们所要的是正确的签名以使我们能在任何时间、任何地点运行具体的目标程序。
因为我们已经有了FLEXLM SDK9.2的源代码,接下来的事情就是阅读它。FLEXLM SDK是Macrovision提供给客户的所谓的" 守护神",并帮助他们封装他们自己的"守护神软件"给"最终用户"。在我们的例子中,Visual Numerics(可视数字)是Vendor(守护神),IMSL CNL 5.5是守护神软件,我们是最终用户。每个Vendor有一个唯一的名字或Vendor ID。正如在许可证中所见,这里是"VNI"。通常,Vendors仅能得到二进制版本或FLEXLM SDK的部分源(代码),但我们很幸运的得到了关于FLEXLM的完整的源代码。
但是,事实证明所有的C源代码(非C++)并非易读的。 该工程演变超过了十年(1988的第一版);新旧函数叠加/缠绕在一起,常常伴有不必要的多余;它的注释很少并且一些旧类型代码习惯很差;宏的和处理指令的过多使用令人非常烦恼。最初是在UNIX平台上开发,后来通过NMAKE工具过渡到 Windows环境。为有效的建立和调试如此大的一个应用程序,我们需要一个好的IDE,但在Windows下没有如此的IDE能直接容纳 Makefile。在Windows上,Visual Studio可能是最好的IDE并具有"Makefile Project"能力。我们为 FLEXLM SDK着手创建一个VS7工程。它花了我一些时间完成-需要修改在makefiles(制作文件)中的一些错误-但当它能用时,确实很方便。
FLEXLM的核心组件是lmgr.lib(或lmgr9a.dll),所有的其他组件都取决于它。Vendors和最终用户更熟悉 lmgrd.exe,lmtools.exe,lmnewgen.exe, makekey.exe等。在成功的建立后,我们试图产生VNI许可证文件但失败了,因为我们并没有其vendors和seeds。通过[2], [3], [4],每个Vendor从Macrovision接收到了5个 vendor密码(VENDOR_KEY1,… VENDOR_KEY5),并且他们自身选择3个随机 seeds (LM_SEED1, LM_SEED2, LM_SEED3)。这8个数放置在lm_code.h中并被加密、模糊,最后嵌入到目标程序和产生许可证的工具中。我们的工作当然是回复这些数,但现在?
或许我们可以尝试容易一点的方法:因为目标程序将计算真正的签名并和从许可证中读取的进行对比,我们可以在对比发生时找到真正的签名而无需知道 Vendor密码和seeds。这并不是如看起来的那么简单,它要求我们在正确的时间、正确的位置设置断点。在数小时的追踪后,我们确定的仅是沿着指令流是一个死结,它无助于定位对比代码,除非FLEXLM使用一些标准的API如strncmp()或Win32CompareString()(实际上 FLEXLM在l_privat.h中定义它自己的宏STRNCMP)。
这使我们产生了一个关于反向工程的基本问题:我们怎样能理解混乱的、高熵(?)的反汇编代码呢?这里没有完整的答案,我们可以参考[7]中关于一些严肃的理论讨论。但是这里我们想聚焦于一个特殊的技术:由IDR Pro介绍的FLAIR(a.k.a. FLIRT, c.f. [6])。
我们都知道,如果应用程序由调试版本建立且符号文件(.PDB, .DBG文件) 可获得时,调试将非常容易。符号是关于程序包括标识(变量、函数)名称和内存偏移、源代码行号等的信息。在一个调试建立编译器/链接器中,保存这些到应用程序二进制或单独的符号文件中。它们可以给调试者展现用户代码,将更接近于源代码(甚至更好,就象在VS中的源代码调试)。很显然,在我们的例子中,提交给最终用户的软件发布版本中的符号也被剥离出来了。
但这并没有结束。即使没有符号,我们仍然可以从二进制文件、特别是库(文件)中得到一些有用的东西。在Windows上,EXE和DLL是PE格式,而 OBJ和LIB是COFF格式(LIB不仅仅是一堆OBJ堆放在一起);在两种格式库中的调用通过函数名称和参数进行,它们必须公开可视[3]。为达到该目的,PE有输入和输入段,COFF有符号表列。VS提供了一组命令找寻它们。
F:\>dumpbin /disasm %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /rawdata %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /imports %vni_dir%\cnl55\cmath.exe
F:\>dumpbin /exports %vni_dir%\cnl55\bin\cmath.dll
F:\>dumpbin /imports %vni_dir%\cnl55\bin\cmath.dll
F:\>dumpbin /exports %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /symbols %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /linkermember %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /linkermember %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /archivemembers %vni_dir%\cnl55\lib\cmath.lib
F:\>dumpbin /archivemembers %vni_dir%\cnl55\lib\cmath_s.lib
F:\>lib /list %vni_dir%\cnl55\lib\cmath.lib
F:\>lib /list %vni_dir%\cnl55\lib\cmath_s.lib
F:\>lib /extract:vc++\flexlm.obj %vni_dir%\cnl55\lib\cmath_s.lib
F:\>dumpbin /symbols /disasm flexlm.obj
在此,我们必须缩小LIB和DLL之间的差异,它仅仅是静态链接VS动态链接的差异。开发一个程序通常需要三步:
初步:Source File (C, H, C++…), ASCII format/源文件 ASII格式
中间:Object File (OBJ, LIB…), COFF format /目标文件 COFF格式
最后:Image File (EXE, DLL, SYS…), PE format /映像文件 PE格式
编译器处理源文件为目标文件,链接器将目标文件链接到输出的映像文件,加载器将映像文件从磁盘加载到内存。注意DLL已经被链接器处理了(在链接后),转换标识为内存地址或"被替换的数字字符"。相反的,目标文件是"链接前(的文件)"并保留了原始的符号;否则链接器将不能解析它们。因此DLL比 LIB文件更接近于EXE,哪怕(LIB)的名称为"库"。实际上,"动态链接"部分是在运行时由系统加载器进行的(一些修正/重定位),而不通过链接器。你可以说这是微软用了误导术语、迷惑用户并隐藏其技术要点的花招,但他们确实擅长于这些。
从一个破解者(原为黑客,我认为破解者更好)的视点看,这意味这越到后面,熵更高,信息更少。特别的,LIB比DLL提供了更多的线索。DLL仅输出了公开的API并隐藏了专用的(如在cmath.dll中仅IMSL API是输出的,而FLEXLM函数内部保留),但是LIB符号包含了两者。事实上,为了建立一个程序以调用DLL API,我们需要传递其输入库到链接器。输入库是一个COFF LIB文件,它作为一个符号参考进行服务(通过指向 DLL),并不包含函数体。
现在,我们可以发现在库文件中的函数,找出我们关心的函数,甚至可以提取相应的目标文件(仅针对LIB)。下面是一个 imsl_f_lin_sol_gen()的例子。在我们讨论之前,驻留在PE调试段或单独的符号文件中的调试信息最好靠近于源代码(调试信息位于第一和第二步之间)。此外,我们在此获得的东西在反向工程中仍然是非常重要的(调试-建立符号是COFF符号的一个扩展集):
F:\>dumpbin /archivemembers /symbols %vni_dir%\cnl55\lib\cmath_s.lib|egrep "member|imsl_f_lin_sol_gen"
... ...
Archive member name at 1C8CEE: vc++\gmres.obj/
00B 00000000 SECT3 notype () External | _imsl_f_lin_sol_gen_min_residual
... ...
Archive member name at 1DAAAA: vc++\fspgen.obj/
00B 00000000 SECT3 notype () External | _imsl_f_lin_sol_gen_coordinate
05A 00000000 UNDEF notype () External | _imsl_f_lin_sol_gen
... ...
Archive member name at 1FC10E: /3128 vc++\fdmbndg.obj
007 00000000 SECT2 notype () External | _imsl_f_lin_sol_gen_band
... ...
Archive member name at 58AC3E: /6132 vc++\flinslg.obj
007 00000000 SECT2 notype () External | _imsl_f_lin_sol_gen
... ...
F:\>dumpbin /exports %vni_dir%\cnl55\bin\cmath.dll | grep -i imsl_f_lin_sol_gen
438 1B5 000432C0 imsl_f_lin_sol_gen
439 1B6 0023B130 imsl_f_lin_sol_gen_band
440 1B7 0024AE30 imsl_f_lin_sol_gen_coordinate
441 1B8 00259C30 imsl_f_lin_sol_gen_min_residual
F:\>lib /extract:vc++\flinslg.obj %vni_dir%\cnl55\lib\cmath_s.lib
IDA FLAIR将带入更深入的一步。虽然在源代码中API调用看起来好像 "x = imsl_f_lin_sol_gen(n, a, b, 0);",在反汇编中它显示为"call 004033B0" (静态链接) or "call [00402054]" (动态链接)。沿着其内存地址的恰当的函数名称标记将使汇编分析容易得多,而这恰恰是FLAIR所能做到的。许多调试器有类似的注解功能,但通常限于输出的API。IDA试图扩展到包含尽可能多的函数。
FLAIR的思想是为每个可标识的库函数创建一个"签名",以使IDA在分析汇编代码时能认知和标记它。在名称指示时,它基本上就是一个认知的问题。再次,由于内容的不同,它仅为LIB工作而不针对发布的DLL版本。我们必须看到DLL有其优势诸如代码共享和主程序简化。例如,cmath.exe 的静态链接大小约700KB,但其动态链接小于4KB。但破解所关心的是,LIB是比DLL更好的方式(当追踪DLL链接的应用程序时,我们的大多数时间都是在10000000+ 或 80000000+ 区而不是熟悉的00400000+ 区; 在DLL版本的cmath.exe中,指令 "102D12BA: call 102D7980" 返回FFFFFFF8)。
IDA FLAIR并非完美的-它不能操作DLL,一些函数不能被识别,可能发生错误的认知等。但它仍然是切实可行的。它最初的目的是分离样板 API(诸如Win32,MFC,ATL等[6]),因此我们可以聚焦在主程序算法上而不是标准的库函数上。在我们的实例中,我们更感兴趣的是得到这些重点的FLEXLM函数,因此我们不必步入每个调用以得到全部代码迷惑的一个大(结构)图。在实际中,我们创建了cmath_s.lib和lmgr.lib 的签名文件,在IDA中将它们应用到cmath.exe,发现FLAIR做得很好-它认知了大多数FLEXLM函数。作为一个优秀的静态分析工具,IDA 也提供了一个WinGraph32特征称作"显示流程图表"。我发现它在对照源代码时特别有助于代码的理解。

翻译FLEXlm9.2的破解教学二

翻译FLEXlm9.2的破解教学二

newsearch 当前离线 添加 newsearch 的声望 反映此帖
标 题: 翻译FLEXlm9.2的破解教学二
作 者: newsearch
时 间: 2004-12-06,12:44
链 接: http://bbs.pediy.com/showthread.php?t=8021

On Software Reverse Engineering - 2 选择自 zxg32 的 Blog
Reverse Engineering, FLEXlm, IMSL

翻译第二篇 FLEXlm 结构
在配备有VS+FLEXLM源代码、W32Dasm+cmath.exe以及IDA+cmath.exe(具签名)后,现在我们将能够揭示 FLEXLM的核心了。下面是我们的一些发现,在这里lm_ckout.c!lc_checkout()表示"在lm_ckout.c模块/文件中的函数 lc_checkout()",箭头符号指示函数调用。注意,由于程序分支的原因,仅部分代码被追踪和展示,但通常情况下,我们感兴趣的仅是这些分支。

0047D0C6: push 00000018 ;程序入口点
0047D22D: call 00401000 ;调用cmath.exe!main()
0047D240: call 0047F20D ;调用链接到ntdll.dll!NtTerminateProcess()
sub_401000: cmath.exe!main()
0040101D: call 004033B0 ;调用vc++\flexlm.obj!imsl_f_lin_sol_gen()
00401039: call 00401050 ;调用vc++\fwrimat.obj!imsl_f_write_matrix()
sub_004033B0: vc++\flexlm.obj!imsl_f_lin_sol_gen()
004033C8: call 00408F24 ;调用vc++\error.obj!imsl_e1psh()
0040342C: call 004034A0 ;调用vc++\flinslg.obj!l_lin_sol_gen()以进行真正的工作
sub_00408F24: vc++\error.obj!imsl_e1psh()
00408F3A: call 0040A850 ;调用链接vc++\single.obj!imsl_once() &reg; vc++\error.obj!l_error_init()
;&reg; vc++\flexlm.obj!imsl_flexlm()
00408F76: call 00414AFD ;调用imsl_highland_check() &reg; l_check.c!lc_timer() &reg; l_timer_heart()
;&reg; l_check() &reg; l_reconnect() &reg; lm_ckout.c!l_checkout() as heartbeat
sub_00413290: vc++\flexlm.obj!imsl_flexlm()
004132EF: call 004294A0 ;调用lm_njob.c!lc_new_job()
004133A4: call 00426380 ;设置LM_A_DISABLE_ENV 为1
004133DE: call 00426380 ;设置LM_A_LICENSE_FILE_PTR 为许可证文件位置
00413486: call 00426380 ;设置LM_A_CHECK_INTERVAL为 -1
004134C0: call 00426380 ;设置LM_A_RETRY_INTERVAL为 -1
004134FB: call 00426380 ;设置LM_A_RETRY_COUNT为 -1
004135A7: call 00426380 ;设置LM_A_LINGER为 0
004136A6: call 0042420C ;调用l_check.c!lc_status(), 返回LM_NEVERCHECKOUT
004138CD: call 0041A010 ;调用lm_ckout.c!lc_checkout()
00414099: call 0047FA9B ;返回当前日期和时间
004141C6: call 0042563D ;调用lm_config.c!lc_get_config()
0041434E: call 0047F8F0 ;检查许可证十分过期,如果没有返回0
sub_0041A010: lm_ckout.c!lc_checkout()
0041A093: call 0041A14B ;调用lm_ckout.c!l_checkout(),返回FFFFFFF8
sub_0041A14B: lm_ckout.c!l_checkout()
0041A2E8: call [004AA01C] ;调用lm_ckout.c!lm_start_real(),返回FFFFFFF8
sub_0041A875: lm_ckout.c!lm_start_real()
0041AA47: call 0041B4A5 ;调用lm_ckout.c!l_local_verify_conf(), 返回1=成功
0041AC01: call 0041BD89 ;调用lm_ckout.c!l_good_lic_key(), 返回0=失败
sub_0041BD89: lm_ckout.c!l_good_lic_key()
0041BE30: call 00433D15 ;调用l_getattr.c!l_xorname()
0041BE4D: call 0041DB9E ;调用lm_ckout.c!l_sg()
0041C202: call 0041EBE3 ;调用vc++\lm_ckout.obj!l_crypt_private(), 返回0
sub_0041DB9E: lm_ckout.c!l_sg()
0041DBF7: call [004AD064] ;调用lm_new.c!l_n36_buff()
0041DC16: call 00443283 ;调用l_key.c!l_key()
sub_0041EBE3: vc++\lm_ckout.obj!l_crypt_private()
0041EC07: call 0041EE44 ;调用vc++\lm_ckout.obj!real_crypt(), 返回0
sub_0041EE44: vc++\lm_ckout.obj!real_crypt()
0041F9B6: call 00420AF6 ;调用vc++\lm_ckout.obj!l_string_key(), 返回0
sub_00420AF6: vc++\lm_ckout.obj!l_string_key()
00420E94 - 00421156 ;调用宏XOR_SEEDS_INIT_ARRAY(xor_arr)
00421247: call 0047F250 ;调用strcpy(lkey, license_key)
0042145E: call 004803A0 ;调用memcpy(newinput, input, inputlen)
0042191D: call 00421D66 ;调用l_strkey.c!our_encrypt()
00421A13 - 00421B27 ;for{}循环许可证密码匹配
00421B34: call 00421C22 ;调用l_strkey.c!atox()以转换二进制串为ASCII文本
sub_00426380: lm_set_attr.c!lc_set_attr()
004263E4: call 0042641E ;调用lm_set_attr.c!l_set_attr()以设置配置结构中的属性
sub_0042641E: lm_set_attr.c!l_set_attr()
00427045: call 00427DBC ;如果设置LM_A_LICENSE_FILE_PTR,调用lm_set_attr.c!l_set_license_path()
;&reg; lm_config.c!l_flush_config() &reg; l_init_file.c!l_init_file()
;&reg; l_allfeat.c!l_allfeat() &reg; l_allfeat.c!l_parse_feature_line()
;&reg; l_allfeat.c!oldkey() &reg; vc++\l_allfeat.obj!l_crypt_private()
sub_004294A0: lm_njob.c!lc_new_job()
004294C0: call [004A5A98] ;调用lm_new.c!l_n36_buf(), 返回 1
004294D2: call [004A5A98] ;调用具有所有的O参数的lm_new.c!l_n36_buf(),返回 0
004294E8: call 0044357F ;调用链接lm_init.c!lc_init() &reg; lm_init.c!l_init()
004294FD ? 00429516 ;打开LM_OPTFLAG_CUSTOM_KEY5 标志
sub_0043873B: vc++\l_allfeat.obj!l_crypt_private()
0043875F: call 00438771 ;调用vc++\l_allfeat.obj!real_crypt(), 返回 21D5B6E8572E
sub_00438771: vc++\l_allfeat.obj!real_crypt()
004392DC: call 0043A41C ;调用vc++\l_allfeat.obj!l_string_key(), 返回21D5B6E8572E
sub_0043A41C: vc++\l_allfeat.obj!l_string_key()
sub_0044359E: lm_init.c!l_init() ;接收 VENDORCODE 和 VENDORNAME 以初始化 job 结构
00443E8F ? 00444354 ;一些有效性测试,可能报告错误
004441F9: call 0041DB9E ;调用lm_ckout.c!l_sg()
sub_0044A110: lm_new.c!l_n36_buf() ;初始化VENDORCODE 结构和 VENDORNAME
0044B503: push 00450BE0 ;压入 lm_new.c!l_n36_buff() 地址
0044B508: call 00444B11 ;调用 lm_init.c!l_x77_buf() 以设置 L_UNIQ_KEY5_FUNC
sub_00450BE0: lm_new.c!l_n36_buff()
00450C34 ? 00450EB0 ;模糊化在job中的mem_ptr2_bytes[]
00450EB5 ? 00450FF8 ;模糊化在 VENDORCODE中的data[0]和 data[1]
sub_77F8DD80: ntdll.dll!NtTerminateProcess()
77F8DD8B: ret 00000008 ;程序退出

正如我们所看到的,我们先前提及的子程序0041A010实际上是lm_ckout.c!lc_checkout(),它是一个真正的核心函数。如果许可证检查成功,它将返回0;否则,它将返回错误代码(在lmclient.h中FFFFFFF8被定义为LM_BADCODE )。由于是核心,l_checkout()可能将被调用几次,但我们不关心(它)。记住,我们的目标是找到checksum对比代码的位置和恢复真正的签名。
一个快速的搜索告诉我们: STRNCMP(在l_privat.h中定义的宏, 如果串匹配则设置结果=0)仅在 lm_ckout.c!l_good_lic_key()和l_crypt.c!l_crypt_private()中出现。注意l_crypt.c 和 l_strkey.c 并不直接被编译为目标文件,而是被包含进模块lm_ckout.obj 和 l_allfeat.obj中。除了这两个之外,lm_crypt.obj也包含了l_crypt.c并揭示其函数到外部作为了API,其下的许多(函数)具有不同的别名,比如 lc_crypt()。但是,在cmath.exe中并未用该(代码)拷贝:lc_crypt()的地址是00469960,但我们在该处设置断点却什么也没有发生。

lm_ckout.c
... ...
#define l_crypt_private l_ckout_crypt
#define l_string_key l_ckout_string_key
... ...
/* Include l_crypt.c, so that these functions won't be global. */
#define LM_CKOUT
#include "l_crypt.c"
l_allfeat.c
... ...
#define LM_CRYPT_HASH
#include "l_crypt.c"
l_crypt.c
#include "l_strkey.c"
l_strkey.c
#include "l_strkey.h"
lm_crypt.c
#define l_crypt_private lc_crypt
... ...
#define LM_CRYPT
... ...
#include "l_crypt.c"

由于包含了超过一次,因为编译指令的原因,l_crypt/l_strkey函数的多个拷贝将不再相同。下面的 l_crypt_private()代码说明了这点(在这里,STRNCMP不是真正的问题)。取决于LM_CKOUT是否被定义,0041EBE3 (长版本)和0043873B (短版本)在两个模块中将有所不同。IDA FLAIR认知后者而非前者-实践中,我们必须在 lm_ckout.obj中手工识别这些函数。

ret = real_crypt(job, conf, sdate, code);
#ifdef LM_CKOUT
if (!(job->user_crypt_filter) && !(job->lc_this_keylist) && valid_code(conf->code))
{
if (job->flags & LM_FLAG_MAKE_OLD_KEY)
{
STRNCMP(conf->code, ret, MAX_CRYPT_LEN, not_eq);
}
else
{
STRNCMP(conf->lc_sign, ret, MAX_CRYPT_LEN, not_eq);
}
if (not_eq && !(job->options->flags & LM_OPTFLAG_STRINGS_CASE_SENSITIVE))
{
job->options->flags |= LM_OPTFLAG_STRINGS_CASE_SENSITIVE;
ret = real_crypt(job, conf, sdate, code);
job->options->flags &= ~LM_OPTFLAG_STRINGS_CASE_SENSITIVE;
}
}

制作Flexlm license总结!

旧 默认 【原创】制作Flexlm license总结!
laoqian 当前离线 添加 laoqian 的声望 反映此帖

标 题: 【原创】制作Flexlm license总结!
作 者: laoqian
时 间: 2005-12-15,17:45
链 接: http://bbs.pediy.com/showthread.php?t=19435

作者:  laoqian [FCG]
时 间:2005-12-15 
软件名称: Zendenc (FLEXlm 7.1d)
软件类别:Nolan Blender提供的经典例子 
软件介绍: FLEXlm 7.1d加密
破解工具:ollydbg 1.10(FLY修改) ,w32Dasm_2002828_pll621,UltraEdit10.0,Flexlm7.2 SDK,calcseed,lmkg7,lmcryptgui,flexlm9.2 

sdk的源码等
 
破解目的:制作无限制使用license文件

通过这篇文章我们将向你介绍如何制作FlexLm的license,并总结经验

第一部分: 废话

Zendenc这个软件是Nolan Blender提供的经典例题,crackZ或网上有下载和分析文章。玩了几个Flexlm, 想总结一下, 就拿它来开刀方便!本

文是关于7.x的,但有部分适用于8.x和9.x,甚至10.x。

第二部分: 转入正题

破解FlexLm最主要的是找到4样东西.

1. 确定版本号
2. 找vendor
3. 计算ENCRYPTION_SEED
4. 找FEATURE

有了这几个,就可以使用FlexLm SDK(如果你有)或工具做出注册机了.
btw:FlexLm SDK一般是通用兼容的的,比如7.2就可以制作7.1的,甚至有时7.1也可以制作7.2的,关键是参数设置。

1. 确定FlexLm版本号 BEHAVIOR Version

方法一:
* 用二进制编辑器,推荐使用HexWorkshop(我用UltraEdit10.0,呵呵),它的查找功能很强,特别是Find All Instances更是我最最常用的.
在查找对话框Type中选择类型Text String,Value中输入"@(#) FLEXlm v",查找,"@(#) FLEXlm v"后面的就是版本号.
【以上是tulipfan[CCG]大虾说的】
方法二:
用Flexlm SDK里的lmtools(在\flexlm\v7.1\i86_n3目录里),运行进入Utilities,点击"Browse"查找你的Flexlm加密的程序主exe或dll,

然后"find version",在下面就会显示你的Flexlm版本,用v7.1版甚至可以看到8.3版以后,是通用的。有个别程序可能此法不行!
方法三:在反汇编以后,搜索"87654321",在前面设断点
在Flexlm SDK里lm_code.h文件里你可以看到一下几句:
代码:
 *      Vendor's private seeds, -- replace with 32-bit numbers that  *                                 you make up.  */ #define ENCRYPTION_SEED1 0x87654321 #define ENCRYPTION_SEED2 0x12345678 /*  *      FLEXlm vendor keys -- enter as received from Globetrotter.  *      Changing these keys has NO impact on license files (unlike  *      the ENCRYPTION_SEEDs).  */ /*-  *      Generate these keys with: lmvkey -v demo -d (+3 months) -p ALL -c DEMO  *              (Use a date approx 3 months out)  */ 
这说明demo的SEED1=0x87654321,SEED2=0x12345678
对应Zendenc里是:
代码:
0043391D    E8 184B0400       call <jmp.&MSVCRT.memcpy> 00433922    83C4 0C           add esp,0C 00433925    8D8D 40FFFFFF     lea ecx,dword ptr ss:[ebp-C0]  ;我们可以在这里设断点! 0043392B    51                push ecx 0043392C    8B95 30FFFFFF     mov edx,dword ptr ss:[ebp-D0]  ;ebp-D0地址是以后要用的! 00433932    81C2 4C010000     add edx,14C                    ;计算得到VENDOR地址 00433938    52                push edx                       ;VENDOR入栈!(ASCII "zend") 00433939    8B85 30FFFFFF     mov eax,dword ptr ss:[ebp-D0] 0043393F    50                push eax 00433940    E8 95560000       call zendenc.00438FDA   ;这个call就是著名的所谓7648B98E标志call!后面要进去的!!! 00433945    83C4 0C           add esp,0C 00433948    81BD 44FFFFFF 214>cmp dword ptr ss:[ebp-BC],87654321  ;此处比较是否是demo的seed1,当然在此seed1已被加密! 00433952    74 0C             je short zendenc.00433960           ;此处比较是否是demo的seed2,当然在此seed2已被加密! 00433954    81BD 48FFFFFF 785>cmp dword ptr ss:[ebp-B8],12345678 0043395E    75 5D             jnz short zendenc.004339BD 00433960    8B8D 30FFFFFF     mov ecx,dword ptr ss:[ebp-D0] 00433966    8379 14 00        cmp dword ptr ds:[ecx+14],0 0043396A    74 17             je short zendenc.00433983 
我们在断点停下d ebp-c0
代码:
0012D404  00000004  ... 0012D408  E0AAA4A0  _お?  此seed1已被加密 0012D40C  C0121579  y    此seed2已被加密! 0012D410  3F9F6A79  yj?    此VENDOR_KEY1已被加密! 0012D414  25DC750E  u?    此VENDOR_KEY2已被加密! 0012D418  B8B046C5  牌案 0012D41C  0B2EAC4E  N? ......向下拉看 0012D4A8  00000000  .... 0012D4AC  00010007  .. 0012D4B0  37300064  d.07   此处显示7.1d 0012D4B4  0000312E  .1..   7.1 0012D4B8  00000000  .... 
这之后,等我们第一次走过著名的所谓7648B98E标志call,"00433940   call zendenc.00438FDA"以后,会发现被加密seed又改变了,其实

就是简单的xor,可是这个xor我们不得而知!
我们得到BEHAVIOR Version  V7.1d
btw:87654321此处,在6.x版时,可以得到真seed,
参考文章"Ansoft Serenade v8.5 - Tutorial"

http://www.woodmann.com/crackz/Tutorials/Serenade.htm
代码:
I also remembered that the checks for the default seed codes (shipped with the SDK) was a good place to fish the correct   seeds, a simple disassembly search for '87654321' finds this code :-  :00429C4C CMP D, [EBP-34], 87654321 <-- Check for encryption_seed1. :00429C53 JZ 00429C5E <-- Jump to Error. :00429C55 CMP D, [EBP-30], 12345678 <-- Check for encryption_seed2. :00429C5C JNZ 00429C85 <-- Good jump. :00429C5E MOV EDI, FFFFFFA5 <-- Error Code (-91).  Routines inside Ser85.exe detect bpx type breakpoints and patching of key files, the checking code starts at 0040EFD3, here   you'll see the names of the files that are checked and the rather obvious 'PUSH 7' instructions which produce a cryptic error   message box asking you to call Ansoft for assistance. The files verified should give us a good idea where to look for other   parts of the protection, Ansoft's developers evidently tried (as is good policy) to identify possible points of attack. Using   bpmb style breakpoints we can quickly recover (what we think are valid) encryption_seed1 (0x7CB2B081) & encryption_seed2   (0x2DFE22B6). ..... 
最后用方法一、二可以确定Zendenc版本号是7.1d,这一步的目的有两个,一是在lmkg.exe中生成Vendor信息时用,二是在使用FlexLm SDK制作

FlexLm的license时使用。

2. 先找FEATURE

方法一:
不用IDA,太麻烦了,既然有SDK和前人的经验,我偷个懒。我偷懒的办法,还是用安装Flexlm SDK里的lmtools!打开"Server status"项,

点击"Perform status Equiry",你就会看到服务器端的正版license.dat内容!前提是你要配置好,而且有正版程序和license,废话!
比如显示如下:
101: SERVER main 001234567890     (我的服务器网卡号,我隐去正确的,避免麻烦啊!)
 80: VENDOR adskflex port=8080
150: INCREMENT 41100ACD_2002_0F adskflex 1.000 permanent 100 VENDOR_STRING=(中间有很多参数,省略避免麻烦)SIGN=787878787878

(我隐去正确的,避免麻烦,为什么是78 ?) 
     关联信息: 省略
170: (overall file checksum)

看到了吗!

VENDOR adskflex
INCREMENT 41100ACD_2002_0F,这个INCREMENT其实就是FEATURE,后面那个1.000就是Version
permanent 是用户数
VENDOR_STRING=  这个也参与计算验证的
中间有很多参数也参与计算验证的,省略避免麻烦
SIGN=787878787878 我就不说了

如果你没有像我的条件就只好去看别的资料学习,很简单的!

btw:如果你的选项得当,现在你甚至就可以在客户端控制license服务器,你可以关闭license服务��慎用!!!,我曾在几次无意的操作中

关闭了license服务,结果单位的所有机器cad罢工,管理员还不知道怎么回事,只好重启服务器,那几天可忙坏了她!这是flexlm的一个bug吧


方法二:

用IDA+相应版本的sig+sdk,找到_lc_checkout函数,设断点,你会找到FEATURE。我采用的是7.2得sig,显示是_lp_checkout
代码:
00478043    68 00400000       push 4000 00478048    51                push ecx 00478049    6A 00             push 0 0047804B    6A 01             push 1 0047804D    68 ECDB4900       push zendenc.0049DBEC       ;FEATURE版本 ASCII "1.0" 00478052    56                push esi                    ;FEATURE id, ASCII "Zend_Encoder" 00478053    52                push edx 00478054    E8 57DFFBFF       call zendenc.00435FB0       ; lp_checkout 00478059    83C4 4C           add esp,4C 0047805C    85C0              test eax,eax                ; 0,jmp 0047805E    74 3F             je short zendenc.0047809F 00478060    8A8424 18090000   mov al,byte ptr ss:[esp+918] 00478067    84C0              test al,al 00478069    75 12             jnz short zendenc.0047807D 0047806B    8B4424 10         mov eax,dword ptr ss:[esp+10] 0047806F    68 DCDB4900       push zendenc.0049DBDC        ; ASCII "Checkout failed"一般搜索这个字符,上面的就是checkout了! 00478074    50                push eax 
在00478054中断,你会看到堆栈窗口里出现了FEATURE id和版本,或者d esi。



3. 找vendor以及计算seed的关键数据

方法一同前,不说了。

方法二:
下面引用自tulipfan[CCG]:
********
"要找vendor_id就需要找到l_sg这个函数了(至于为什么要找这个函数建议看一些更基础的文章),在这儿我主要是介绍一下如何定位l_sg函数,

这个是FlexLm的一个内部使用的函数. lc_init,lc_checkout都会调用它下面是它们的调用关系,括号里面的数字是调用的次数。

a. lc_init -> l_init
b. l_init -> l_sg(1)

a. lc_checkout -> l_checkout
b. l_checkout -> lm_start_real(2)
c. lm_start_real -> l_good_lic_key(3)
d. l_good_lic_key -> l_sg(2)

从上面可以看到它们的调用关系. 

我们可以通过l_sg确定vendor_id和ENCRYPTION_SEED,通过lc_checkout可以确定FEATURE

IDA通过FlexLm的sig文件可以确定上面的大部分函数,再通过他们的调用关系,很容易找到其他函数.

制作FlexLm的批处理文件和工具包可去CrackZ去下载.

最后定位到l_sg函数"

************

我们有9.2的source,我们看到lm_ckout.c中有下面一段,那几个数就是确定l_sg函数上很有用的,针对
0x7648b98e;                   v7.x */
0x6f7330b8;                 /*- v8.x,9.x*/
下面给出参考:
V7
glseed = 0x788F71D2
seedval = 0x7648B98E

V8
glseed = 0x3CDE3EBF
seedval = 0x6F73330B8

V9
glseed = 0x72346B53
seedval = 0x6F7330B8

v10

glseed = 0x5332322F
seedval = 0x6F7330B8

关于这一段,zhanzixin 有一点小小的补充。
0x7648b98e这个数字,被称为seedval的,
在版本4、5、6中是 0xa8f38730
在版本7.0-8.0c中是 0x7648b98e
在版本8.0d以后,一直没变,是0x6f7330b8。

代码:
/*-  *  Also used by flexcrypt -- notify if API changes.  */ void l_sg(   LM_HANDLE *    job,   char *      vendor_id,   VENDORCODE *  key) /*- l_sg means "signature vendor_key5" */ {   unsigned long  keys[4];   char      sig[SIGSIZE] = {'\0'};   /*- If you change this, you must change it also in utils/lmnewgen.c */   /*- unsigned long x = 0xa8f38730;                   v3.1 */   /*- unsigned long x = 0x7648b98e;                   v7.0 */   unsigned long  x = 0x6f7330b8;                   /*- v8.x */   extern void    (*L_UNIQ_KEY5_FUNC)();   unsigned long  d0 = 0, d1 = 0;   int        i = SIGSIZE-1;    if (( job->options->flags & LM_OPTFLAG_CUSTOM_KEY5) && L_UNIQ_KEY5_FUNC)   {     (*L_UNIQ_KEY5_FUNC)(job, vendor_id, key);     return;   } 
废话少说,我们直接反汇编zendenc.exe,我们查找"7648B98E",来到下面,好像就这么1,2处

。前人的经验就是宝贵啊,我省了很多事!

代码:
.text:00438FDA ; =============== S U B R O U T I N E ?===================================== .text:00438FDA .text:00438FDA ; Attributes: bp-based frame .text:00438FDA .text:00438FDA sub_438FDA      proc near               ; CODE XREF: sub_432CC4+C7Cp .text:00438FDA                                         ; sub_437621+B9p ... .text:00438FDA .text:00438FDA                 push    ebp               ====先在这个地方下断 .text:00438FDB                 mov     ebp, esp .text:00438FDD                 sub     esp, 30h .text:00438FE0                 mov     [ebp+var_10], 7648B98Eh    ====就是这个数在确定l_sg函数上很有用 .text:00438FE7                 mov     [ebp+var_14], 3 .text:00438FEE                 mov     eax, [ebp+arg_0] .text:00438FF1                 mov     ecx, [eax+6Ch] .text:00438FF4                 mov     edx, [ecx+1D4h] .text:00438FFA                 and     edx, 8000h .text:00439000                 test    edx, edx .text:00439002                 jz      short loc_439027    ====这个跳前面分析了,第一次肯定会跳的!第二次再来! .text:00439004                 cmp     dword_49E5EC, 0 .text:0043900B                 jz      short loc_439027 .text:0043900D                 mov     eax, [ebp+arg_8] .text:00439010                 push    eax .text:00439011                 mov     ecx, [ebp+arg_4] .text:00439014                 push    ecx .text:00439015                 mov     edx, [ebp+arg_0] .text:00439018                 push    edx .text:00439019                 call    dword_49E5EC          ====在这个地方下断,F8之后就是我们要的! .text:0043901F                 add     esp, 0Ch .text:00439022                 jmp     loc_43913A .text:00439027 ; --------------------------------------------------------------------------- .text:00439027 loc_439027:                             ; CODE XREF: sub_438FDA+28j .text:00439027                                         ; sub_438FDA+31j .text:00439027                 push    4 .text:00439029                 lea     eax, [ebp+var_28] .text:0043902C                 push    eax .text:0043902D                 mov     ecx, [ebp+arg_8] .text:00439030                 add     ecx, 0Ch .text:00439033                 push    ecx .text:00439034                 mov     edx, [ebp+arg_4] .text:00439037                 push    edx .text:00439038                 call    sub_451F26 
*************************
别急,先做一下准备工作:
*************************
前面我们在00433938 处可以得到VENDOR为 "zend",而0043392C处的ebp-D0是初始化VENDORkey的地方!
代码:
0043391D    E8 184B0400       call <jmp.&MSVCRT.memcpy> 00433922    83C4 0C           add esp,0C 00433925    8D8D 40FFFFFF     lea ecx,dword ptr ss:[ebp-C0]  ;我们可以在这里设断点! 0043392B    51                push ecx 0043392C    8B95 30FFFFFF     mov edx,dword ptr ss:[ebp-D0]  ;ebp-D0地址是以后要用的! 00433932    81C2 4C010000     add edx,14C                    ;计算得到VENDOR地址 00433938    52                push edx                       ;VENDOR入栈!(ASCII "zend") 00433939    8B85 30FFFFFF     mov eax,dword ptr ss:[ebp-D0] 0043393F    50                push eax 00433940    E8 95560000       call zendenc.00438FDA   ;这个call就是著名的所谓7648B98E标志call!后面要进去的!!! 00433945    83C4 0C           add esp,0C 00433948    81BD 44FFFFFF 214>cmp dword ptr ss:[ebp-BC],87654321  ;此处比较是否是demo的seed1,当然在此seed1已被加密! 00433952    74 0C             je short zendenc.00433960           ;此处比较是否是demo的seed2,当然在此seed2已被加密! 00433954    81BD 48FFFFFF 785>cmp dword ptr ss:[ebp-B8],12345678 0043395E    75 5D             jnz short zendenc.004339BD 00433960    8B8D 30FFFFFF     mov ecx,dword ptr ss:[ebp-D0] 00433966    8379 14 00        cmp dword ptr ds:[ecx+14],0 0043396A    74 17             je short zendenc.00433983 
但是问题又来了,我们并没有license.dat,这就要我们做一个假的,否则第二次不会来到text:00438FDA,那就前功尽弃了!
license.dat格式我不说了看雪的书上以及很多资料说得很清楚了。
我们根据我们上面得到的正版license.dat内容,做一个假的license.dat,放在zendenc目录里下:
***************
FEATURE Zend_Encoder zend 1.0 permanent uncounted VENDOR_STRING=www.FCGchina.com \
  HOSTID=ANY SIGN=787878787878  (怎么又是78 !?)
***************
因为断开网络,所以运行zendenc.exe会找不到license,我们把它指向我们的假的license.dat,当然是出错退出!经过仔细跟踪还会发现

license文件名必须是Zend_Encoder.dat!

好,工作开始:
下面我启动ollydbg 1.10调入zendenc.exe,在入口停下,直接下bp 00438FDA ,bp 00439019
F9执行,中断在00438FDA。

这时第一次来到00439002  jz short loc_439027时它是要跳过去的,注意要跳就让他跳吧。
若开始第一次时强制不跳,倒是能来到00439019,结果跟出了错误的seed1和seed2,而且多少次结果还都一样。
再次F9,我们又回来中断在00438FDA,F8单步执行,这次不跳了,直到过了00439019,暂停!
我们在命令栏下如下命令,可以看到:(可以纪录了多次的调试结果)
代码:
输入 d [esp+4]  在内存窗口看到,【长型��ASCII 转存】 ********** 003983AC  646E657A  zend  输入 d [esp+8] 在内存窗口看到,【长型��ASCII 转存】 ****1****  0012CF98  00000004  ... 0012CF9C  B68ACAC8  仁?     data[0] 0012CFA0  96327B11  {2      data[1] 0012CFA4  534D01FB  ?MS 0012CFA8  2D607B98  ?`- 0012CFAC  B069483D  =Hi 0012CFB0  67FCD8B6  敦? 0012CFB4  00000000  .... ********1************  输入d [esp] 【长型��ASCII 转存】 ******2***** 00398260  00000066  f... 00398264  00F400E6  ?? 00398268  B9DEDAA8  ㄚ薰   job+08 0039826C  EB6E4C33  3Ln    job+0c 00398270  BEF9AA16  ?    job+10  ********2*********** 
这些数据就是我们计算seed1和seed2的关键!

4. 计算ENCRYPTION_SEED

使用工具calcseed.exe,输入data[0],data[1],job+08,job+0c,job+10,VENDORname为 "zend"上述信息,计算

ENCRYPTION_SEED1 0xfa5410de
ENCRYPTION_SEED2 0xdaeca107

多次结果是一样的,注意是第二次自然来到时!

至此,分析已经基本完成,可以使用SDK写license.dat

第三部分: 制作license.dat

方法一:

我们得到数据如下:
代码:
Feathure  Zend_Encoder FeathureVersion  1.0 ENCRYPTION_SEED1 0xfa5410de ENCRYPTION_SEED2 0xdaeca107 VENDOR Zend BEHAVIOR Version  V7.1d 
使用lmkg.exe,选择version 7.0,在vendor_id输入"Zend"得到:
代码:
/* Version 7 keys */ #define VENDOR_KEY1 0xdb5c129b #define VENDOR_KEY2 0x7c9d919a #define VENDOR_KEY3 0x1ee3b786 #define VENDOR_KEY4 0x01745090 #define VENDOR_KEY5 0x2c2dd7f7 #define CRO_KEY1 0xdf7c1093 #define CRO_KEY2 0x3abff31c  #define VENDOR_NAME "Zend" 
按照上面的信息,编辑\flexlm\v7.2\machind\lm_code.h
代码:
********************************** #ifndef LM_CODE_H #define LM_CODE_H #include "lm_cro.h"  /*  *      Pick an LM_STRENGTH.  *  *              If you're not using CRO public-key, then leave this as  *              LM_STRENGTH_DEFAULT.  *              If you're upgrading from pre-v7.1, and want no changes,  *              set this to LM_STRENGTH_LICENSE_KEY.  */  #define LM_STRENGTH LM_STRENGTH_DEFAULT   /*注意12位的SIGN要这个参数 */  /*  * LM_STRENGTH Options are  *      LM_STRENGTH_DEFAULT             Public key protection unused  *                                      Use SIGN= attribute  *                                      sign length = 12  * Public key:  *       LM_STRENGTH_113BIT, LOW        sign length = 58 chars  *       LM_STRENGTH_163BIT, MEDIUM     sign length = 84 chars  *       LM_STRENGTH_239BIT, HIGH       sign length = 120 chars  *  * Use pre-v7.1, non-CRO  *       LM_STRENGTH_LICENSE_KEY        Use pre-v7.1 license-keys.  *                                      Doesn't use SIGN= attribute.  */  /*  *      Vendor's private seeds, -- replace with 32-bit numbers that  *                                 you make up.  */  #define ENCRYPTION_SEED1 0xfa5410de   /*我们找到的SEED1 */ #define ENCRYPTION_SEED2 0xdaeca107   /*我们找到的SEED2 */ #define ENCRYPTION_SEED3 0x22222222     /*没用,不变 */ #define ENCRYPTION_SEED4 0x32323232   /*没用,不变 */  /*  *      FLEXlm vendor keys -- enter as received from Globetrotter.  *      Changing these keys has NO impact on license files (unlike  *      the ENCRYPTION_SEEDs).  */ /*-  *      Generate these keys with: lmvkey -v demo -d (+3 months) -p ALL -c DEMO  *              (Use a date approx 3 months out)  */ #define VENDOR_KEY1 0xdb5c12db  /*我们算出的 */ #define VENDOR_KEY2 0x7c9d91ba #define VENDOR_KEY3 0x1ee3b7c6 #define VENDOR_KEY4 0x017450b0 #define VENDOR_KEY5 0x0c2dd7f7  /*我们算出的 */ #define CRO_KEY1 0xdf7c10d3        /* Used to enable CRO -- turned off by default */ #define CRO_KEY2 0x3abff33c        /* Be sure to reset LM_STRENGTH above if                                CRO_KEY is non-zero */   /*  *      FLEXlm vendor name.  Leave as "demo" if evaluating FLEXlm.  Otherwise  *                           set to your vendor daemon name.  */  #define VENDOR_NAME "Zend"  /*我们找到的VENDOR */  /*  *      Older customers with newer versions may want to set  *      behavior defaults to previous version, though this is usually  *      discouraged.  Behaviors can be changed individually using  *      LM_A_xxx in the flexible API.  New customers should use the  *      current default, as set below  *  *      Valid settings include:  *              LM_BEHAVIOR_V2, _V3, _V4, _V5, _V5_1, _V6, _V7, _V7_1  */  #define LM_VER_BEHAVIOR LM_BEHAVIOR_V7_1 

刚从网上看到的破解绿坝方法

刚从网上看到的破解绿坝方法(真没想到,4千万的软件,密码竟然用MD5保存,如果是真的,汗S~~~~~):

   直接翻看C:\WINDOWS\system32目录下的文件:kwpwf.dll
   用记事本打开。不管里面内容是什么,一律替换成
    D0970714757783E6CF17B26FB8E2298F
   然后再开绿爸的时候密码自然就是112233了
   或者
   直接google下载一个密码为112233或者是密码已知的kwpwf.dll,覆盖一下就行了。原来那个备份好,搞完了再覆盖回去

Flexlm管理的license格式介绍

Flexlm管理的license格式介绍
作者:未知 时间:2007-10-21 08:04:51 来自:网上转载 浏览次数:946 文字大小:【大】【中】【小】

许多EDA软件的加密采用Flexlm(Flexible License
Manager)方法,这是由Globetrotter公司发明的软件加密方法。Globetrotter公司向软件厂商出售相关开发软件,软件厂商把此加密程序集成到自己的软件中,该方法是目前最为流行的EDA软件加密方法,已被80%以上的EDA公司所采用。该方法可以锁定机器的硬盘号、网卡号、使用日期、支持加密狗等,以保护软件的知识产权。

下面简单介绍Flexlm软件管理的license的文件格式。

License文件由注释行、Server行、Daemon行以及Feature行构成,另外使用续行符号''用来表示一行的继续。


注释行
当一行的开始是'#'符号时,该行被认为是注释行。

Server行
Server行用来标识一台特定的主机,其格式如下:
SERVER host hostid [port]
SERVER是该行的关键字,标识该行为Server行,不能更改;
host表示主机的名称,在UNIX/LINUX系统中,可以用命令"hostname"或"uname -n"获得;在Windows
NT/2000/XP系统中,可以通过命令"ipconfig /all"获得;
hostid表示主机的ID号,使用Flexlm License管理工具的lmhostid命令获得;
port表示使用的TCP/IP的端口号,在UNIX/LINUX系统下,该数值需要大于1024,因为小于1024的端口号已被操作系统使用;如果没有指定port,则使用27000~27009之间的一个有效端口号。一般使用27000端口号。

Daemon(或Vendor)行
Daemon行用来标识不同的软件提供商,该行的格式如下:
DAEMON daemon_name [daemon_path]
DAEMON是该行的关键字,标识该行为Daemon行,不能更改;
daemon_name是实际运行的License管理工具的名称,如lmgrd等;
daemon_path是该工具的存放路径。

Feature行
Feature行用来对软件特定的功能进行限制,常见格式如下:
FEATURE feature vendor feat_version exp_date num_lic SIGN=sign
[optional_attributes]
FEATURE是该行的关键字,表示该行为Feature行,不能更改;
feature是软件提供的特定功能的名称;
vendor表示软件提供商的名字;
feat_version表示该feature的版本号;
exp_date表示该license的过期日期,格式为dd-mm-yy,例如31-04-2007。另外,如果该字段为"permanent"或yy位为0(或者00,000,0000),那么该license永不过期;
num_lic表示该许可的终端用户个数;
SIGN=sign是用于验证该feature的签名,是根据以上条件和Flexlm加密算法计算出来的密码,一般和机器的硬件设备相关,譬如硬盘号,网卡号等;
[optional_attributes]有众多选项,详细的可参考Flexlm的End-User-Manual。

续行符号''
一行太长需要续行的时候,在行末尾用''符号表示续行。