BinCrack 发表于 2020-1-15 14:05:59

PTGui离线激活验证机制分析

本帖最后由 BinCrack 于 2020-1-15 14:05 编辑

飘云阁全网首发,未经许可,禁止转载

昨天分析了一款不知道做什么的软件,我们对软件自身功能并不关心,仅作为学习交流的目的分享此次分析思路。个人认为,不管作为软件开发人员还是逆向分析人员,本软件的离线激活机制都有一定学习意义。本文将尽可能详细的阐述该软件离线激活功能相应的验证机制,并班门弄斧提出一些软件的验证缺陷。由于本人能力有限、软件本身验证也有些混乱,涉及到多种算法(RSA、AES、MD5、BASE64、BASE16)的识别,如有描述不清的地方还望见谅。
1.切入验证点

       如下图所示,当我们启用离线激活功能,软件会获取当前系统硬件信息并进行加密生成一组“激活请求码”。之后我们在激活网站上输入这串代码和合法的用户名与产品密钥,服务器便会计算出对应的“激活响应码”,此时只需输入到下图输入框即可离线激活成功。

我们此次的任务便是仅通过分析软件,去逆推出PTGui客户端与服务端的激活交互机制,实现激活服务器的功能。
       当我们随便输入响应代码时,软件会提示格式有误,根据提示很容易定位到格式校验的函数。

因此只需要构造符合格式的“响应码”即可绕过格式检验。
2.RSA解密
继续向下分析,会看到程序读取内存中的一段hex数据,并将其解密(这里的解密算法是什么完全不用理会,将其抠出即可)。


       真不清楚作者是脑子有坑还是拿我们当智障,有了私钥可以直接生成对应的公钥,RSA算法的非对称加密已经失去意义。我们可以在不改动程序一个字节的情况下写出注册码完全是拜这点所赐。
       接着就是rsa解密的相关函数,挑出需要注意的部分如下图所示。此时我们使用RSA算法加密的一个块大小刚好是0x200,程序要求数据必须大于0x200+0x10,而且解密出来的大小必须为32个字节。逆推回去就是先使用RSA加密32字节的数据,得到0x200的密文块,再填充0x10字节即可满足验证。

3.AES解密

      后续看到程序会将0x200+0x10后的数据读取到padding2充当aes算法的密文,而上面提到的32字节的数据刚好是aes密钥,0x10的数据刚好是iv。




4.激活请求码生成
       注意,这里需要打断一下,上述的aes函数第五个参数“1”代表解密,自然能联想到“0”代表加密,再联想到“激活请求码”的生成时也许用到了这个算法。我们再该函数下断点,重新运行程序生成“激活请求码”时会断下来,此时直接拿到了明文数据如下。不幸的是这次的rsa算法是公钥加密,也就是说,我们无法通过“激活请求码”的密文解密出明文(因为没有私钥)。所以在分析中我也完完全全分析了整个“激活请求码”的生成过程,流程刚好是软件解析“激活响应码”的逆过程,因此不再赘述,继续分析刚刚的aes解密后的数据验证。

{
      "productid": 2,
      "productversion": 112000,
      "minlicensedate": 1.4304384e12,
      "licenses_seatkey_preferred": null,
      "activationsubactionid": 100,
      "h": {"version":1,"os":2,"numbits":32,"osversion":,"computername":"HTKUVBW5WJSRJ5V","cpu":"Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz","disk":"a8d34713 (20478 MB)","memsize":2047,"serial":"None","gpus":[
                "RDP Encoder Mirror Driver",
                "RDP Reflector Display Driver",
                "RDPDD Chained DD",
                "VMware SVGA 3D"
      ]}
}

5.AES解密数据构造
分析到这里,大体的加解密框架都已经清晰,然而接下来的才是最费劲的时刻。因为aes解密后的明文才是程序最关心的数据,而我们完全不知道这些数据是什么。sub_57BFC0函数为数据解析函数,先上一张让人一脸懵逼的图片。



       Sub_49c470函数为ASSERT函数,一旦执行就会触发“Assertion failed:”错误。起初看到上述流程我简直怀疑正版用户这里都无法通过,耐着性子分析sub_D93CA0函数最终柳暗花明又一村。

结合以上函数,我们果断推测AES解密后的数据正是JSON格式,而刚刚让我们蒙蔽的那些变量正是为了验证JSON中相关字段存在与否。如果有正版用户的“响应码”,我们可以毫不费力的拿到JSON数据。可惜没有,所以后续的分析是一个攻坚战,我只能在N次错误中去断定哪些字段该存在以及其对应的数据类型,而且JSON数据是嵌套的,当程序解析某个字段又会递归调用一次JSON解析函数,总之秀到你头皮发麻,后续会直接提供合法的JSON数据。如果你感兴趣可以自己尝试将JSON数据逆推出来。
6. BASE16编码识别

       在我们解析到‘rec’时,程序会验证数据的格式,对应函数sub_8BD020,由合法字符范围推测可能是base16编码。


7.合法JSON数据构造



可能有朋友会问,请求码的密文我们无法解密,那么请求码明文我们该如何获得呢。一种是通过调试程序获得,结果准确但适用性差;第二种是自行读取当前系统的信息,因为这些数据都很好获取,也不存在什么随机数。
8.MD5校验
       最终程序还会在sub_57FAB0函数校验signature,通过下图红框的数据(MD5的链接变量)秒认出为md5加密算法。Md5校验的数据正好是rec的json明文,奇葩的是要求数据必须大于16字节,因此我们计算出md5后在后面在填充一字节00即可。




9.手算激活响应码

       最后带着大家手算一次响应码,加深一下印象(其实是懒得写程序了)。
将RSA私钥保存为private.pem,使用命令生成公钥:
openssl rsa -in private.pem -pubout -out public.pem
随便取两组hex数据作为AES算法的key和iv:
Key:
5A 18 C9 08 53 8B 2F 47 54 71 65 76 A4 28 4B E6
1E 1B F8 42 0A 55 71 DB 0B C8 5B F7 F8 A4 29 12

Iv:
6D 1B 4D 4F 0C A3 FD B0 20 65 F7 12 41 A2 48 62

将key以hex的方式存储到message.txt,使用rsa公钥加密
openssl rsautl -encrypt -inkey public.pem -in message.txt -pubin -out enc.txt
将iv对应的hex填充到enc.txt后面
将计算出的json数据保存为plain.txt
使用上述key,iv进行aes-cbc加密:
openssl enc -aes-256-cbc -in plain.txt -out encrypt.txt -iv 6D1B4D4F0CA3FDB02065F71241A24862 -K 5A18C908538B2F4754716576A4284BE61E1BF8420A5571DB0BC85BF7F8A42912
将aes密文再次填充到enc.txt后面,最后将其进行base64编码并填上响应码的开头结尾格式即可。

10.软件验证设计缺陷

1. 客户端内置RSA私钥,使得RSA发挥不出存在的意义。
2. 硬件配置校验生硬,虽然“请求码”攻击者无法解密,但完全可以在不利用“激活请求码”的情况下,攻击者自行读取。
3. 提示字符串过多,RSA和AES完全可以通过字符串提示猜到,而md5,base16,base64凭经验也可识别。
4. rec中签名校验未在注册时触发,不排除是作者设置的暗桩。






不破不立 发表于 2020-1-15 14:10:28

前排收藏学习

Rooking 发表于 2020-1-15 14:14:19

前排膜拜学习 试试看能不能手动算一组

pentium450 发表于 2020-1-15 14:34:35

非常期待的文章!前排就座学习,谢谢!

tree_fly 发表于 2020-1-15 18:54:23

膜拜~ 自带公钥了啊,多么痛的领悟~

lhglhg 发表于 2020-1-15 20:07:46

自带私钥了啊

wgz001 发表于 2020-1-16 08:23:09

666
虽然自带钥匙   但是我还是不懂

xiaoji 发表于 2020-1-16 14:06:01

不错不错哦~~~~~~~~~~~

a1421117 发表于 2020-1-16 17:03:27

精品,感谢分享!!!!

qiudan098 发表于 2020-1-17 17:17:13

牛逼,分析思路值得学习
页: [1] 2
查看完整版本: PTGui离线激活验证机制分析