吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17295|回复: 135
收起左侧

[.NET逆向] 某Excel插件逆向分析注册码(注册机)【终极篇】

    [复制链接]
ps122 发表于 2023-4-16 14:00
本帖最后由 ps122 于 2023-4-17 08:50 编辑

前情提要

最近@pjy612大佬接连几篇精华帖看得真是过瘾,掀起了一波学习的小高潮,我也算是入了点门,尤其是使用Harmony(X)的骚操作,下面接着大佬的帖子(https://www.52pojie.cn/thread-1771932-1-1.html),继续分析该插件的注册码(注册机)。

学习总结

乍一看大佬的帖子一头雾水,相信也吓退了一部分小白,下面先总结一下,提取一下关键点,前贴总共介绍了2种Patch的方法:

1、使用内部自带的注册码

此时license是“GrapeCity-Internal-License”开头,需要验证调用程序的签名,如果不是指定范围内的程序,则会触发异常。

//amj.l(A_0)
internal bool a(string A_0)
{
    return A_0 != null && A_0.StartsWith("GrapeCity-Internal-License,", StringComparison.Ordinal);
}

//amj.j(A_1)
internal bool a(object A_0)
{
    Assembly assembly = A_0 as Assembly;
    if (A_0 == null)
    {
            return false;
    }
    byte[] publicKeyToken = assembly.GetName().GetPublicKeyToken();
    ulong num = 0UL;
    if (publicKeyToken == null)
    {
            return false;
    }
    for (int i = 0; i < publicKeyToken.Length; i++)
    {
            num = (num << 8) | (ulong)publicKeyToken[i];
    }
    return new HashSet<ulong> { 8757299328925775578UL, 10685601461268105732UL, 15833087347736823688UL, 11201956651387526890UL, 7968347106218921853UL }.Contains(num);
}

Patch此处函数a,返回true即可。

2、通过伪造key

internal static string GetLicense(Type t)
{
    string name = "52pojie";
    var temp = new
    {
        S = Convert.ToBase64String(Encoding.UTF8.GetBytes("0")),
        D = new
        {
            Id = name,
            Prd = new[]
            {
                new {N = "WU5D", C = "WU5D"}
            },
            Crt = DateTime.Now.ToString("yyyyMMdd hhmmss", CultureInfo.InvariantCulture)
        }
    };
    string json = JsonConvert.SerializeObject(temp);
    string lic = (string)t.Assembly.GetType("GrapeCity.Documents.Excel.sp").GetMethod("g").Invoke(null, new object?[] { json }); ;
    return $"{name}#A0{lic}";
}

其中的参数“S”就是数据的签名信息,因为“S”不正确,验签时VerifyData函数不能通过,此时Patch该函数返回true也可以达到目的。

// this.d('s').a() -> so("wE+VWE4exHP+ieziZg+Cgf7sJslBhVzJbPXZQwfGUfU27NqODPzCpizjAPz6NnKw8GCiHpug6D+bUxmutcBmUw==", "AQAB").a() -> RSA鉴权
internal class so
{
    // Token: 0x060031AD RID: 12717 RVA: 0x001EE44C File Offset: 0x001EC64C
    public so(string A_0, string A_1)
    {
            this.a.ImportParameters(new RSAParameters
            {
                    Modulus = Convert.FromBase64String(A_0),
                    Exponent = Convert.FromBase64String(A_1 ?? "AQAB")
            });
    }

    // Token: 0x060031AE RID: 12718 RVA: 0x001EE4A1 File Offset: 0x001EC6A1
    public bool a(string A_0, string A_1)
    {                
            return !string.IsNullOrEmpty(A_1) && this.a.VerifyData(Encoding.UTF8.GetBytes(A_0), "SHA1", Convert.FromBase64String(A_1));
    }

    // Token: 0x04001503 RID: 5379
    private readonly RSACryptoServiceProvider a = new RSACryptoServiceProvider();
}

终极目标---注册码(注册机)

1、RSA加解密介绍

从验证方式看,本插件用的是RSA非对称加解密。数据加签是RSA加解密的基本操作,钥匙对包含公钥和私钥,通常私钥加签,公钥解密,所以通常公钥是公开的,私钥作者持有,不会公开。

2、本软件的公钥信息
("wE+VWE4exHP+ieziZg+Cgf7sJslBhVzJbPXZQwfGUfU27NqODPzCpizjAPz6NnKw8GCiHpug6D+bUxmutcBmUw==", "AQAB"),看看长度:

        string m = "wE+VWE4exHP+ieziZg+Cgf7sJslBhVzJbPXZQwfGUfU27NqODPzCpizjAPz6NnKw8GCiHpug6D+bUxmutcBmUw==";
        RSACryptoServiceProvider rsp = new RSACryptoServiceProvider();
        rsp.ImportParameters(new RSAParameters
        {
            Modulus = Convert.FromBase64String(m),
            Exponent = Convert.FromBase64String("AQAB")
        });
        int keysize = rsp.KeySize;//512位

根据网上大佬的经验总结,512及以下位数的key是可以通过因式分解求得私钥的(超过512位就先止步吧)。经过三七四十九天的分解,终于分解成功拿到私钥了。有了私钥是生成注册码的关键和基础。

3、分析加签过程

由上面分析可知,要想生成注册码,只需要对注册数据加签并写到参数“S”中即可。
原内部key:"GrapeCity-Internal-License,296578427539473#A0GSMwWZjhXRgI7bmBCduVWb5N6bEByQHJiOi8kI1tlOiQmcQJCLiQDN9UjMwASNxATMwIDMyIiOiQncDJCLikHdpNUZwFmcHJiOiEmTDJCLiMzN4kzM5cjM4gzN5YTOyIiOiQWSisnOiQkIsISP3cXVqtiN9kFWwc5LVZTV5VmZFdWM5xUOolWNrdHNpxUV7MzSnhDWEllS8oHc6V5T8AzMmhjSxJVMUpWeZZlQyYnY6BTVHZzS6RnS9clTYJUcht6dDJEaiojITJCL5AzN8EjM6MTN0IicfJye#4Xfd5nINJDOZJiOiMkIsICN6BCdl9kLgwWZjhXRgI7bmBCduVWb5N6bEByQHJiOi8kI1xSfiM5VNNlI0IyQiwiI4YHI4VmTuAyZul6Zh5WSgI7bmBCduVWb5N6bEByQHJiOi8kI1xSfig4UXFlI0IyQiwiI4YHI4VmTuAiZkBFIy3mZgQnbl5Wdj3GRgM4RiojIOJyes4nI4IUULJiOiMkIsICN6BCdl9kLgQmcvdFIy3mZgQnbl5Wdj3GRgM4RiojIOJyes4nIFx4REJiOiMkIsICN6B3YsF"
key_.png
将key用字符串“#A0”分割成2部分,后一部分解密:
key1_.png

{
    "_r": 536218705,
    "S": "hBCwkaqBXNW9JtvK6GU0vbv2BVYyjT1RqJ8f308OUvpz8JYDX8gK37ULi4wk5ih9Lu1gEfeuU6U/W0XY96+jUw==",
    "D": {
        "Id": "296578427539473",
        "CNa": "GrapeCity",
        "Crt": "20201015 025944",
        "Prd": [{
            "N": "GC Document for Excel Java v4",
            "C": "DGLE"
        }, {
            "N": "GC Document for Word .Net v4",
            "C": "KQB4"
        }, {
            "N": "GC Document for Pdf .Net v4",
            "C": "QWSH"
        }, {
            "N": "GC Document for Imaging .Net v4",
            "C": "SMWS"
        }, {
            "N": "GC Document for Excel .Net v4",
            "C": "Y82M"
        }]
    }
}

其中“S”为签名信息,“D”为授权信息。

授权信息构成:

    public static string a(this sj A_0)
    {
        if (A_0 == null)
        {
            return "";
        }
        StringBuilder stringBuilder = new StringBuilder();
        using (StringWriter stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture))
        {
            stringWriter.Write("{");
            if (A_0.a() != null)
            {
                stringWriter.Write("\"{0}\":", "Anl");
                stringWriter.Write("{");
                stringWriter.Write("\"{0}\":{1}", "dsr", Convert.ToString(A_0.a().Value).ToLowerInvariant());
                stringWriter.Write("},");
            }
            stringWriter.Write("\"{0}\":\"{1}\",", "Id", A_0.k());//必须项
            if (A_0.j())
            {
                stringWriter.Write("\"{0}\":{1},", "Evl", Convert.ToString(A_0.j()).ToLowerInvariant());
            }
            if (!string.IsNullOrEmpty(A_0.i()))
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "OId", A_0.i());
            }
            if (!string.IsNullOrEmpty(A_0.h()))
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "CNa", A_0.h());
            }
            if (!string.IsNullOrEmpty(A_0.g()))
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "CId", A_0.g());
            }
            if (!string.IsNullOrEmpty(A_0.f()))
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "Dms", A_0.f());
            }
            if (!string.IsNullOrEmpty(A_0.e()))
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "Ips", A_0.e());
            }
            if (A_0.d() != null)
            {
                stringWriter.Write("\"{0}\":\"{1}\",", "Exp", A_0.d().Value.ToString("yyyyMMdd", CultureInfo.InvariantCulture));
            }
            stringWriter.Write("\"{0}\":\"{1}\",", "Crt", A_0.c().ToString("yyyyMMdd hhmmss", CultureInfo.InvariantCulture));//必须项
            stringWriter.Write("\"{0}\":[{1}]", "Prd", string.Join(",", A_0.b().Select(new Func<sk, string>(sn.<>c.<>9.a))));//必须项
            stringWriter.Write("}");
        }
        return stringBuilder.ToString();
    }

由上可知仅有Id,Crt和Prd三项必须,其余可无,若有必须按照上面的顺序。这也是之前大佬构造key的基础。

    string name = "52pojie";
    var temp = new
    {
        S = Convert.ToBase64String(Encoding.UTF8.GetBytes("0")),
        D = new
        {
            Id = name,
            Prd = new[]
            {
                new {N = "WU5D", C = "WU5D"}
            },
            Crt = DateTime.Now.ToString("yyyyMMdd hhmmss", CultureInfo.InvariantCulture)
        }
    };

4、注册码(注册机)

根据以上分析可以写出注册机,生成注册码:
验签数据=第一部分数据 + 分隔符("#A0") + D(授权信息),然后根据RSA的加签函数(此处需要用到前面提到的私钥)生成签名信息S。最后再加密生成注册码。
sign_.png

sn.PNG

5、可用key及使用方法

提供一组可用key,设置了有效期:2023-12-31,直接使用如下:

string license=@"52pojie#A0iiW34TQxo7NQ9EMUNUV9pmY5M6c7gEexJXQrIkbpdlbvIXO7l5ZtFUcPlldHZ6MzdnSJpXe8dTe92mbrMGaQdjUQlGehVWTD5kbTNXaWlnNVV6RqVDbRljcnJiOiMlIsIiN6YjN6YjI0IicfJye&Qf35VfiQUNVdlI0IyQiwiIsV6Y8VkI0IiTis7W0ICZyBlIsICMwADMyEDI5EDNwMjMwIjI0ICdyNkIsISMzITMzIDMyIiOiAHeFJCLiIjMxMHciojIh94QiwiI8gDO8gDO8gjI0ICZJJye0rCii";

Workbook workbook = new GrapeCity.Documents.Excel.Workbook(license);
IWorksheet worksheet = workbook.Worksheets[0];
worksheet.Cells[0].Value = "Hell0 worsddf";
workbook.Save("test.xlsx");

搞定~~~

注意事项

仅限于学习交流,请勿用于商业或非法用途。

免费评分

参与人数 29威望 +2 吾爱币 +122 热心值 +28 收起 理由
恰似清风吹过 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
b12312312 + 1 我很赞同!
leeqysz + 1 + 1 我很赞同!
Mrj756 + 1 + 1 谢谢@Thanks!
chelseacool + 1 用心讨论,共获提升!
bt8641 + 1 + 1 用心讨论,共获提升!
jymolong + 1 热心回复!
lookat + 1 + 1 谢谢@Thanks!
qq61230056 + 1 支持破破
X1a0 + 1 + 1 用心讨论,共获提升!
maweiwuai + 1 + 1 我很赞同!
b12312312 + 1 + 1 我很赞同!
huawei518 + 1 + 1 谢谢@Thanks!
N1san + 1 + 1 热心回复!
1MajorTom1 + 1 热心回复!
yp17792351859 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
dongxing123 + 1 + 1 我很赞同!
silencewd + 1 + 1 我很赞同!
qrqt35 + 1 + 1 我很赞同!
wangshu2010 + 1 我很赞同!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Mark2022 + 1 热心回复!
5omggx + 1 + 1 用心讨论,共获提升!
qaz003 + 1 + 1 用心讨论,共获提升!
莫奇 + 1 + 1 我很赞同!
pjy612 + 1 + 1 这才是大佬,直接算私钥
chengdragon + 1 + 1 谢谢@Thanks!
shen12wang + 1 + 1 谢谢@Thanks!
zhubing0622 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

pjy612 发表于 2023-4-17 15:36
本帖最后由 pjy612 于 2023-4-17 15:43 编辑

楼主更牛 直接 把私钥给逆出来了。。。

那啥 既然私钥弄出来了。。。
直接把 内部Key 里面的 Prd 塞进去。。。
弄个 Java 也能用的 Key 呗。。。
wk380299125 发表于 2023-4-17 11:45
xiawan 发表于 2023-4-17 10:44
3yu3 发表于 2023-4-17 11:15
大牛无处不在,学习了。。
fin618 发表于 2023-4-17 11:30
向大佬学习
mdictfan 发表于 2023-4-17 11:41
学习一下,感谢!
dlzc 发表于 2023-4-17 11:44
向大佬学习,但不知是什么插件
chengdragon 发表于 2023-4-17 11:57
顶技术贴
gksj 发表于 2023-4-17 12:35
作者看到之后估计直接把RSA加到4096了,哈哈
 楼主| ps122 发表于 2023-4-17 12:46
dlzc 发表于 2023-4-17 11:44
向大佬学习,但不知是什么插件

帖子开头提到的前一篇有介绍
aHR0cHM6Ly93d3cuZ3JhcGVjaXR5LmNvbS9kb2N1bWVudHMtYXBpLWV4Y2Vs
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-4 00:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表