没有时间系列:由于工作需要用到该软件,于是顺便研究注册算法玩玩。又是一款歪果仁的软件,搞搞应该没事吧?文末有惊喜!
【版本】02.00.002
IrpTrace已经很久不更新了,这是我能在网上找到的最新版。。。。。
注册算法在模块TSCUST.DLL中,并且此模块保留了调试符号。第一次见在正式的商业软件中这么干的,心可真大。所有功能一目了然。
这个窗口的按钮事件如下图所示:
函数 C C u s t L i b : : R e q u e s t S e r i a l N u m b e r \textcolor{cornflowerblue}{CCustLib::RequestSerialNumber} CCustLib::RequestSerialNumber会校验用户输入的序列号,校验成功就会调用 C C u s t L i b : : R e g i s t e r P r o d u c t \textcolor{cornflowerblue}{CCustLib::RegisterProduct} CCustLib::RegisterProduct注册产品。
C C u s t L i b : : R e q u e s t S e r i a l N u m b e r \textcolor{cornflowerblue}{CCustLib::RequestSerialNumber} CCustLib::RequestSerialNumber实际调用 C C u s t L i b : : C h e c k S e r i a l N u m b e r \textcolor{cornflowerblue}{CCustLib::CheckSerialNumber} CCustLib::CheckSerialNumber校验序列号
C C u s t L i b : : C h e c k S e r i a l N u m b e r \textcolor{cornflowerblue}{CCustLib::CheckSerialNumber} CCustLib::CheckSerialNumber内部分两个流程对序列号进行校验:
下面先分析 I s S N E x p i r e d \textcolor{cornflowerblue}{IsSNExpired} IsSNExpired函数
G e t S N E x p i r a t i o n D a t e \textcolor{cornflowerblue}{GetSNExpirationDate} GetSNExpirationDate函数用序列号中某些位置的元素算出3个变量v5,v6,v7(IDA分析有些问题,并不是serial)。这些位置满足关系如下:
s e r i a l [ 0 ] \textcolor{orange}{serial[0]} serial[0]可以是任意可见字符,从而计算得到pos变量。 p o s = 3 ∗ ( s e r i a l [ 0 ] % 0 x A ) \textcolor{orange}{pos\ =\ 3*(serial[0]\ \%\ 0xA)} pos = 3∗(serial[0] % 0xA)
v5,v6,v7分别对应图中的a2,a3,a4。代表的含义是serial下标为pos对应的元素所在全局变量g_lpszCodeStr中的下标。由此可见serial中的元素包含于g_lpszCodeStr中。然后作为函数 C o m p a r e W i t h B u i l d D a t e \textcolor{cornflowerblue}{CompareWithBuildDate} CompareWithBuildDate的参数校验是否过期。
函数 G e t P r o d u c t B u i l d D a t e \textcolor{cornflowerblue}{GetProductBuildDate} GetProductBuildDate会根据字符串**“Aug 16 2015”取得v6+1**,v6+2,v6+3的值分别是0xf,8,0x10。
在 C o m p a r e D a t e s \textcolor{cornflowerblue}{CompareDates} CompareDates中会将传入的参数进行分组计算值,这里记为y1和y2,然后比较 y 1 − y 2 < 0 \textcolor{orange}{y1-y2<0} y1−y2<0则说明过期。
y 1 = C a l c D a y s ( v 5 , v 5 , v 7 ) , y 2 = C a l c D a y s ( v 6 + 3 , v 6 + 2 , v 6 + 1 ) \textcolor{orange}{y1 = CalcDays(v5,v5,v7),y2 = CalcDays(v6+3,v6+2,v6+1)} y1=CalcDays(v5,v5,v7),y2=CalcDays(v6+3,v6+2,v6+1)
令T=CalcDays(x1,x2,x3),对应的数学表达式:
T = { x 1 , x 2 ≤ 1 & x 3 ≤ 0 ( 1 ) x 1 + a [ x 2 ] , x 2 > 1 & x 3 ≤ 0 ( 2 ) x 1 + a [ x 2 ] + 0 x 16 ∗ ( x 3 − 1 ) , x 2 > 1 & x 3 ≥ 1 ( 3 ) 其中 x 1 , x 2 , x 3 分别表示 C a l c D a y s 的参数 x 1 , x 2 , x 3 T = \begin{cases}\quad{x_1,x_2\leq1\&\ x_3\leq0}\ (1) \\ \quad x_1+a[x_2],x2>1\&\ x_3\leq0\ (2)\\ \quad x_1+a[x_2]+0x16*(x_3-1),x_2>1\&\ x_3\ge1\ (3) \end{cases}\\其中x_1,x_2,x_3分别表示CalcDays的参数x1,x2,x3 T=⎩ ⎨ ⎧x1,x2≤1& x3≤0 (1)x1+a[x2],x2>1& x3≤0 (2)x1+a[x2]+0x16∗(x3−1),x2>1& x3≥1 (3)其中x1,x2,x3分别表示CalcDays的参数x1,x2,x3
C o m p a r e D a t e s \textcolor{cornflowerblue}{CompareDates} CompareDates函数要求
T ( x 1 , x 2 , x 3 ) ≥ T ( 0 x f , 0 x 8 , 0 x 10 ) ≥ 0 x 14 D A T(x_1,x_2,x_3)\ge T(0xf,0x8,0x10)\ge0x14DA T(x1,x2,x3)≥T(0xf,0x8,0x10)≥0x14DA
易知上式中的(1)不满足条件,并且T函数每个分段都是单调递增的,所以推出其中一种满足的输入
x 1 ∈ N , x 2 ≥ 0 x 8 , x 3 ≥ 0 x f x_1\in N,x_2 \ge 0x8,x_3 \ge 0xf x1∈N,x2≥0x8,x3≥0xf
也就是说 s e r i a l [ g _ l p s z C o d e S t r [ p o s ] ] \textcolor{orange}{serial[g\_lpszCodeStr[pos]]} serial[g_lpszCodeStr[pos]]可以是g_lpszCodeStr中的任意字符, s e r i a l [ g _ l p s z C o d e S t r [ p o s + 1 ] ] \textcolor{orange}{serial[g\_lpszCodeStr[pos+1]]} serial[g_lpszCodeStr[pos+1]]必须是 中 g _ l p s z C o d e S t r [ 8 : 63 ] 中\textcolor{orange}{g\_lpszCodeStr[8:63]} 中g_lpszCodeStr[8:63]的字符, s e r i a l [ g _ l p s z C o d e S t r [ p o s + 2 ] ] \textcolor{orange}{serial[g\_lpszCodeStr[pos+2]]} serial[g_lpszCodeStr[pos+2]]必须是 g _ l p s z C o d e S t r [ 0 x f : 63 ] \textcolor{orange}{g\_lpszCodeStr[0xf:63]} g_lpszCodeStr[0xf:63]中的字符。
接下来看校验和部分
G e t C h e c k s u m 3 \textcolor{cornflowerblue}{GetChecksum3} GetChecksum3函数计算一个特定字符串**“IT0200”的校验和,结果为0x090C0000**。不妨记d1为serial前4个字节的DWORD值,d2为serial接下来的4个字节的DWORD值。满足关系式:
d 1 m o d 9 = 3 d 2 m o d 0 x C = 3 d1\ mod\ 9=3\\d2\ mod\ 0xC=3\\ d1 mod 9=3d2 mod 0xC=3
通过前面的分析,我们得知serial中的字符来自于g_lpszCodeStr变量,也就是 “P7JFC5NMSXO09LDKGTQ2ZVHY8W16R3EA4BIU” 共63个,枚举满足上式的空间在 6 3 4 \textcolor{orange}{63^4} 634以内,时间不会超过1s。
通过枚举得到一种满足条件的组合PPPN7PP7,这8个字符均来自serial中的某些位置。
这8个位置的产生满足以下条件:
其中变量 M E M O R Y [ 0 x 2 F 714 ] = { 7 , 3 , 9 , 0 x a , 8 , 4 , 2 , 5 , 1 , 1 , 0 x b , 6 , 3 , 5 , 7 , 0 x b , 0 x a , 4 , 9 , 8 , 2 , 0 x a , 2 , 9 , 7 , 6 , 4 , 8 , 2 , 0 x b } \textcolor{orange}{MEMORY[0x2F714]\ =\ \{7,3,9,0xa,8,4,2,5,1,1,0xb,6,3,5,7,0xb,0xa,4,9,8,2,0xa,2,9,7,6,4,8,2,0xb\} } MEMORY[0x2F714] = {7,3,9,0xa,8,4,2,5,1,1,0xb,6,3,5,7,0xb,0xa,4,9,8,2,0xa,2,9,7,6,4,8,2,0xb}
至此,已经得到了serial的所有字符,只剩下位置pos不知道了。
而pos是由serial[0]来确定的,serial[0]可以是g_lpszCodeStr中任意位置的元素。所以可以先随机指定一个serial[0],从而确定pos,根据pos来确定剩下11个字符的位置就可以了。
下面列举一些可用的注册码:
APPPRN7PP7S0
QPPPBN7POPE7
APPPRN7PP7SA
RVYPPSPN7PP7
SOPPPN87PP7L
8PKPPN7P98P7