[原创]wibu软授权(三)
2022-12-28 10:53:15 Author: bbs.pediy.com(查看原文) 阅读量:19 收藏

本文描述wbc文件的使用过程

  • python3
  • wbc文件

wbc主要包含了系统特征的哈希值,这些系统特征是CmAct证书私钥的生成参数。每个item节都表示一个系统特征以及CmAct证书私钥的部分结构。

以某份wbc文件为例,部分内容如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

[WIBU-SYSTEMS Control File]

Guid={000B0002-0000-1100-8005-0000C06B5161}

Description=WIBU-SYSTEMS CmAct Inventory

Version=1.00

Encoding=UTF-8

[Inventory]

Nonce=5B953B8DE30C80383851A1C4A0DCB4C08B5EA6DB5B57503C9F766B7EE75303B6

RedundancyData=4B76D4A6ADA098349EE74931AD4686F251B512998CB181346527C4625B2BA8ADEA16AEA16335F3ACC84F1C4E532D5DCC8EEE91901D9C7BCBC71506F16FF71CFBE893EBDA3FE75A0AFE8F94FB05150C26A2611DFDD9A369B326B493C2EF03A49FFA83743C2100

Version0=6

Version1=60

Version2=2869

Version3=0

Heuristic=2

Flags=0

ItemCount=3

[Item_0]

ID=1EC1787C328C4BE4883290757FB640BD6373E4C3CB3A85D5A13138A0FDFEB9E0

Position=0

Length=170

Params=AB1A401EC3FBA4070FA7CA569EB44DE8

[Item_1]

ID=CB53841BA7BF43DC643F5B8263F69FC439C7B4F1E2112B193D3AFD2B6A93F7EF

Position=170

Length=170

Params=8B4FC28F523E3B201B0A21075DA160C1

[Item_2]

ID=410912781D65FF2625587B62D115E9DB8F938B760CC33857BF4B6A54BE9CC235

Position=340

Length=172

Params=2B0E51706F32DC52323AF862ABABFE3A

系统特征码

在解说ID的生成过程前,首先需要介绍系统特征码。wibu软授权体系为每类(每种)系统特征都编了一个号码,我们姑且称之为系统特征码。每一个系统特征码都绑定着一个回调函数,调用时只要传入不同的系统特征码,最后就会调用对应的回调函数。

关于系统特征码的用途见附录。

01

ID

ID的生成过程与系统特征码和系统特征有关。如上述item_0节的ID则由系统特征码6001生成,系统特征码6001表示的是系统的MAC。

wibu首先计算了系统特征码和系统特征的哈希值。但是在该计算过程,wibu错误使用sha256函数,导致无法直接使用python的hashlib库来复现该过程。任何鲁棒无bug的sha库都无法实现该过程,必须自己手动修改sha源码才能复现该过程。

02

接着,将上步骤的输出加上wbc文件中的nonce(随机值),再次sha256后即得到id值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

>>> import SHA256

>>> import struct

>>>

>>>

>>> def wibu_sha256(code, feature):

>>>     h = SHA256.SHA256()

>>>     h.update(struct.pack("I", code))

>>>     h.update(feature)

>>>     part1 = h.final()

>>>     h.update(part1)

>>>     part2 = h.final()

>>>    

>>>    

>>>     return part1 + part2

>>>    

>>> mac = b"00:ff:52:9e:64:de"

>>> nonce = bytes.fromhex("5B953B8DE30C80383851A1C4A0DCB4C08B5EA6DB5B57503C9F766B7EE75303B6")

>>> h256_0 = wibu_sha256(6001, mac)

>>> item_id = int.from_bytes(SHA256(nonce + h256_0).final(), "big")

>>> print("%X" % item_id)

1EC1787C328C4BE4883290757FB640BD6373E4C3CB3A85D5A13138A0FDFEB9E0

私钥

首先,根据同理计算item_1和item_2的id。

1

2

3

4

5

6

7

8

9

10

>>> hostname = b"cpx1548"

>>> h256_1 = wibu_sha256(5001, hostname)

>>> item_id = int.from_bytes(SHA256.SHA256(nonce + h256_1).final(), "big")

>>> print("%X" % item_id)

CB53841BA7BF43DC643F5B8263F69FC439C7B4F1E2112B193D3AFD2B6A93F7EF

>>> fs_root = b"UUID=044527ca-a319-491b-a27f-6a25a97a44ea"

>>> h256_2 = wibu_sha256(5003, fs_root)

>>> item_id = int.from_bytes(SHA256.SHA256(nonce + h256_2).final(), "big")

>>> print("%X" % item_id)

410912781D65FF2625587B62D115E9DB8F938B760CC33857BF4B6A54BE9CC235

确认所有item节都对上后,该份wbc文件会被wibu授权系统承认。

私钥主要来源于两部分数据,分别记为part1和part2,下面分别介绍这两部分的生成。

part1

我们按照item节的pos和length取上述代码中的h256_0、h256_1和h256_2。

最终取完的结果长度为512比特,在取的过程中再次发生不符合程序员理解的过程。

wibu将h256_0按8比特为单位进行处理,每个8比特从右往左处理,在组合过程中,当组合的长度不是8的倍数时,从h256_0中抽取的比特存在一段小空档,如下图白色部分。

我不清楚这是wibu故意为之还是开发者头脑一热弄出来的操作,但我认为一个简单的取前N比特的操作弄成这样子不符合我的程序之美,开发说明里还要特意描述这样结构。

wibu取比特示意

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

>>> print(h256_0.hex())

2949fb78084c95a87ade867c4ab4f9b5418798269a01e6e8b3adcf660e02cfa2542fb60d640ab1868b8fb5d23b529b4fffdffd2bc1a38f7ea116888f394036ad

>>> print(h256_1.hex())

8d5cf77bc7d35c74d0596f2d2cbed1cfdd678778db400385c1e896fdbccc2272c4966f75d09e8e1f00867205954fbd3588d5443044643a3c5525e12fbd526537

>>> print(h256_2.hex())

b960bc22180606ff1fdc651947caab6df0f9adc4076b4cfe07fc1ee5393c4edfd7f1ce8f432cf2beb099e6f4262cb64506eec8eaa360b38e40e0856b353f2b65

>>>

>>> def wibu_get_bits(h256, length):

>>>     mask = 1

>>>     ret = ""

>>>     for i in range(length):

>>>         if((h256[i//8] & mask) != 0):

>>>             ret += '1'

>>>         else:

>>>             ret += '0'

>>>         mask *= 2

>>>         if(mask == 0x100):

>>>             mask = 1

>>>     return ret

>>> part1_bin = wibu_get_bits(h256_0, 170) + wibu_get_bits(h256_1, 170) + wibu_get_bits(h256_2, 170)

>>> part1 = ''

>>>

>>> for i in range(0, len(part1_bin), 8):

>>>     part1 += "%x" % int(part1_bin[i:i+8][::-1], 2)

>>> print("part1: %s" % part1)

part1: 2949fb7884c95a87ade867c4ab4f9b5418798269a3572ddef1d4f73d14167bdb5b0f8463f779f1de26d93bc62b826160f0ffc15d9671a4bcda69fdf4a7c30

part2

part2是对wbc文件的哈希,过程很简单。部分代码见下图:

03

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

>>> import SHA256

>>> import struct

>>> import configparser

>>> import common

>>>

>>> cf = configparser.ConfigParser()

>>> wbc_content = common.read_file("test.wbc").replace("\x00", "")

>>> cf.read_string(wbc_content)

>>>

>>> h = SHA256.SHA256()

>>> h.update(bytes.fromhex("17051401040C14020402134414021346"))

>>> h.update(bytes.fromhex(cf.get("Inventory","nonce")))

>>> h.update(bytes.fromhex(cf.get("Inventory","redundancydata"))[0:33])

>>> h.update(struct.pack("I", int(cf.get("Inventory","version0"), 10)))

>>> h.update(struct.pack("I", int(cf.get("Inventory","version1"), 10)))

>>> h.update(struct.pack("I", int(cf.get("Inventory","version2"), 10)))

>>> h.update(struct.pack("I", int(cf.get("Inventory","version3"), 10)))

>>> h.update(struct.pack("I", int(cf.get("Inventory","Heuristic"), 10)))

>>> h.update(struct.pack("I", int(cf.get("Inventory","flags"), 10)))

>>>

>>> for i in range(cf.getint("Inventory","itemcount")):

>>>     item_name = "Item_%d" % i

>>>     h.update(bytes.fromhex(cf.get(item_name,"id")))

>>>     h.update(struct.pack("I", int(cf.get(item_name,"position"), 10)))

>>>     h.update(struct.pack("I", int(cf.get(item_name,"length"), 10)))

>>>     h.update(bytes.fromhex(cf.get(item_name,"params")))

>>>    

>>> h.update(struct.pack("I", int(cf.get("Inventory","itemcount"), 10)))

>>> part2 = ''

>>> for i in h.final():

>>>     part2 += "%x" % i

>>> print("part2: %s" % part2)

part2: 57eceb1aa5768647a7ef6b775af2353d45f497321a2d2773ee551f3de35ca5

计算私钥

1

2

3

4

5

6

7

8

9

10

11

12

13

14

>>> data = "[%s-%s]" % (part1, part2)

>>> h = SHA256.SHA256()

>>> h.update(data.encode())

>>> h.update(struct.pack("I", 0x04000000))

>>> h.update(b"{1F4C32E0-34BB-414F-B1D6-C8D59E4B8004}\x00")

>>> out = h.final()

>>> out += SHA256.SHA256(out).final()[0:8]

>>>

>>> h = SHA256.SHA256()

>>> h.update(out)

>>> h.update(bytes.fromhex("C3649C300C6444438AB4DFDE047D25E1"))

>>> k = h.final()[0:28]

>>> print("private: %s" % k.hex())

private: 33f0c397747ec06066c9943a2f1440d4bd7b589983bda88ee2f0b67a

由于CmAct证书使用的是ECC-224,所以其私钥为224比特(即28字节),故最后k只取了前28字节。

可以通过Q=dG来确认CmAct证书私钥是否正确。

得到CmAct证书私钥后,就可以去解析RAU和DYN文件。

私钥的计算是wibu软授权系统中十分重要的一个步骤,它的计算过程很复杂,并且具有多个分支路径,本文的代码只节选了其中一条路径。

更加详细的代码可以参考github仓库wibu

系统特征码 功能描述
5001 读取/proc/sys/kernel/hostname
5002 读取/sys/bus/i2c/drivers/i2c_adapter/module/srcversion
5003 使用getfsfile函数获取挂载在/目录下的文件系统名,原理是读取/etc/fstab
6001 使用ioctl的获取MAC
6101 读取/sys/class/dmi/id/product_name
6102 读取/sys/class/dmi/id/product_version
6103 读取/sys/class/dmi/id/sys_vendor
6104 读取/sys/class/dmi/id/board_version
6105 读取/sys/class/dmi/id/board_vendor
6106 读取/sys/class/dmi/id/board_name
6107 读取/sys/class/dmi/id/bios_date
6108 读取/sys/class/dmi/id/bios_vendor
6109 读取/sys/class/dmi/id/bios_version
6110 机器序列号,见补充说明1
7001 vendor,硬盘相关,见补充说明3
7002 rev,硬盘相关,见补充说明3
7003 model,硬盘相关,见补充说明3
8001 读取/proc/cpuinfo信息,输出结构(`cpu family model stepping`)重复4次
8002 遍历/sys/class/power_supply/BAT*,输出结构为`manufacturer model_name serial_number`
8003 读取/sys/class/drm/card*LVDS*/edid,注意*为通配符
8004 遍历/sys/bus/usb/devices/(非usb开头)文件夹,输出结构为`idVendor idProduct serial`
8005 内存条相关,见补充说明2

补充说明1

运行以下其中一条命令,这三条命令理论上输出一样。

  • /usr/bin/codemeter-info -Z d6c11a123ca6b9e6290e1b85542d9a7ebf15a1f8c17e455ebc3ca734292b15e6

    该命令实际是读取/sys/class/dmi/id/product_serial

  • /usr/bin/codemeter-info -Z 83f924b2cba312b3383aeaebd16d16af704a35ba0e8a83d8de1244b4c20e9de6

    该命令实际是运行/usr/sbin/dmidecode -s system-serial-number

  • /usr/bin/codemeter-info -Z 825fabfb0a32da7981a7e3a4a0469c59c99d49b974ebc443e94fa9f5a96467b6

    该命令实际是运行/usr/sbin/dmidecode -s system-serial-number

补充说明2

运行以下其中一条命令,这两条命令理论上输出一样。

  • /usr/bin/codemeter-info -Z efe29c248ac07cdb68fe09cc0f2913366406e5b4a47a9e6bda3eda14cc471f69

    该命令实际是运行/usr/sbin/dmidecode -t 17,然后分析每一行输出,最后构成以下输出结构。

  • /usr/bin/codemeter-info -Z 9b0855c03c8977a941be48d9422fef6aa9821d7481e94ffe2d9d3737470a604e

    该命令实际是运行/usr/sbin/dmidecode -t 17,然后分析每一行输出,最后构成以下输出结构。

输出结构为"%d|%s|%s|%s" % (size,Manufacturer,Part_Number,Serial_Number)

补充说明3

hd

若/目录挂载源为/dev/hd*(通过/etc/fstab确定),则只有7003有效,读取/proc/ide/hd*/model

sd

若/目录挂载源为/dev/sd*(通过/etc/fstab确定),则遍历/sys/bus/scsi/drivers/sd/*/block/*是否存在sd*相关的目录,若存在,则

7001有效,读取/sys/bus/scsi/drivers/sd/*/vendor

7002有效,读取/sys/bus/scsi/drivers/sd/*/rev

7003有效,读取/sys/bus/scsi/drivers/sd/*/model

其他情况

先忽略,暂未处理。

[2022冬季班]《安卓高级研修班(网课)》月薪两万班招生中~

最后于 2天前 被bluefish蓝鱼编辑 ,原因: 更新文章


文章来源: https://bbs.pediy.com/thread-275657.htm
如有侵权请联系:admin#unsafe.sh