吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7446|回复: 22
收起左侧

[.NET逆向] aspose.cell无注册码破解研究

  [复制链接]
qands 发表于 2021-4-1 04:20
本帖最后由 qands 于 2021-4-1 04:48 编辑

写的目的1是记录自己的研究过程,2是有人问问题,我还是想着写详细点点,不过我其实也是新手,共同学习吧.
申明:仅学习逆向过程,不发成品.

首先还是搞没虚拟的版本19.1以前随便吧,我用的7.1.0.写上3行代码dnspy跑起来
[C#] 纯文本查看 复制代码
 Workbook wb = new Workbook();
wb.Worksheets[0].Cells[1, 1].PutValue("test");
wb.Save("123.xls");

在save上下断,然后跟下去.思路是观察worksheet数量,变2就停下(第二个是试用版水印的sheet).
最终找到了这个位置
[C#] 纯文本查看 复制代码
 public void Save(Stream stream, SaveOptions saveOptions)
{
...
...
...
if (stream.CanSeek)
{
new global::\u0002.\u001F(this).\u0008\u0002(stream);
}
...
...
...
}

跟进去第三行
[C#] 纯文本查看 复制代码
this.\u0001 = A_1;
this.\u0001 = A_1.Worksheets;
this.\u0001.\u000E\u0002();


再跟进去
[Asm] 纯文本查看 复制代码
internal void \u000E\u0002()
{
if (global::\u0017.\u007F.\u0008\u0002() != global::\u0017.\u001F.\u0001)
{
return;
}

这里要是不return,下面就是干加水印的事.
进来之后右键return设为下一条语句,观察下生成的文件,没水印了.
点下\u0008\u0002() 进去看看,末尾2个return,不同的2个值
(注:记住这句4096,下面用的着,)
[C#] 纯文本查看 复制代码
        if (\u007F.\u0001 != null && \u007F.\u0001.\u0001 != \u001F.\u0001 && \u001D.\u0008\u0002() != 4096)
{
return \u001F.\u0002;
}
return \u001F.\u0001;
}

继续运行跑到了下面的return,那么正确的应该是if为true.
观察一下这个IF语句,点击\u007F.\u0001,它被定义static,类型和我们所处的类是一样的.然后看看第二个\u007F.\u0001.\u0001,是个emun,第三个目前看不出来啥.
(注:第三个的类其实是校验注册信息中Signature和Data加密值是否匹配的,不要问我为什么知道的,我在一堆乱码中绕了几天了...)
然后观察下这个类,在上面找到了注册时校验的过程.
[C#] 纯文本查看 复制代码
        // Token: 0x06001A96 RID: 6806 RVA: 0x001AC3C0 File Offset: 0x001A89C0
internal void \u0008\u0002(Stream A_1)
{
if (A_1 == null)
{
throw new ArgumentNullException(\u007F.\u0001(30740));
}
\u001D.\u0008\u0002(0);
XmlDocument xmlDocument = new XmlDocument();
XmlDocument xmlDocument2;
if (true)
{
xmlDocument2 = xmlDocument;
}
xmlDocument2.Load(A_1);
this.\u0008\u0002(xmlDocument2);
Hashtable u = \u007F.\u0001;
Hashtable hashtable;
if (true)
{
hashtable = u;
}
if (hashtable == null)
{
Hashtable hashtable2 = \u007F.\u0008\u0002();
if (true)
{
hashtable = hashtable2;
}
\u007F.\u0001 = hashtable;
}
if (hashtable.ContainsKey(this.\u0001))
{
throw new InvalidOperationException(\u007F.\u0001(30749));
}
if (\u001D.\u000E\u0002() > 0)
{
return;
}
bool flag = false;
bool flag2;
if (true)
{
flag2 = flag;
}
string[] u2 = this.\u0001;
string[] array;
if (true)
{
array = u2;
}
int num = 0;
int i;
if (true)
{
i = num;
}
while (i < array.Length)
{
string text = array[i];
if (text.Equals(\u007F.\u0001(30846)) || text.Equals(\u007F.\u0001(30863)) || text.Equals(\u007F.\u0001(14954)) || text.Equals(\u007F.\u0001(30892)) || text.Equals(\u007F.\u0001(30929)) || text.Equals(\u007F.\u0001(30966)))
{
flag2 = true;
break;
}
i++;
}
if (!flag2)
{
throw new InvalidOperationException(\u007F.\u0001(30983));
}
DateTime t = DateTime.ParseExact(\u007F.\u0001(31040), \u007F.\u0001(31057), CultureInfo.InvariantCulture);
if (t > this.\u0001)
{
throw new InvalidOperationException(string.Format(\u007F.\u0001(31074), this.\u0001.ToString(\u007F.\u0001(31340), CultureInfo.InvariantCulture), t.ToString(\u007F.\u0001(31340), CultureInfo.InvariantCulture)));
}
if (DateTime.Now > this.\u0002)
{
throw new InvalidOperationException(\u007F.\u0001(31357));
}
this.\u0001 = \u001F.\u0002;
\u007F.\u0001 = this;
}


重点还是最后两句,是不是和上面的if很像.好了,我们现在翻译一下这个if:如果注册信息不为空,且注册成功,那么你很屌
这就简单了.在if哪里的最后一个return右键,IL指令
[Asm] 纯文本查看 复制代码
66        00E4        ldc.i4.1
67        00E5        ret
68        00E6        ldc.i4.0
69        00E7        ret

ldc.i4.0改ldc.i4.1,dnspy自动变代码了,然后保存模块,验证,问题无.收工.

好了下一个版本,19.1,同样的方法,找第二个sheet,到这里
(注:这里也记下,要考, using 和他的下一句)
[C#] 纯文本查看 复制代码
using (\u000E\u200B u000E_u200B = \u0008\u2004\u2000.\u0002(this, fileFormatType, saveOptions))
                                {
                                        u000E_u200B.\u000E\u200B\u2005\u200B\u0002(stream);
                                }
                                if (saveOptions.ClearData)
                                {
                                        this.\u0002();
                                }

然后这里
[C#] 纯文本查看 复制代码
this.\u0002 = \u0002;
                this.\u0003 = \u0002.Worksheets;
                this.\u000E = \u0003;
[color=#ff8c00]                \u0002\u2005\u2004.\u0003(this.\u0003);[/color]
                \u000F\u2001\u2002 u = this.\u0003.\u0002(\u0003);

这里
[C#] 纯文本查看 复制代码
        string text = \u0002.\u0006\u2002().\u0008\u2000();

这里
[C#] 纯文本查看 复制代码
if (\u000E\u2009\u2009.\u0002(this.Settings.\u0003\u2008))
                        {
                                return Workbook.\u0002(Workbook.\u000E\u2001);
                        }


[C#] 纯文本查看 复制代码
        public static bool \u0002(bool \u0002)
        {
                object[] u = new object[]
                {
                        \u0002
                };
                return (bool)\u000F\u200A\u2008\u2003.\u0005\u200B\u2008\u2003().\u0002(\u000F\u200A\u2008\u2003.\u0008\u200B\u2008\u2003(), "r;HV%q\"a^^", u);
        }

然后小样虚化了.  我就不带着大家跟进去绕了,感兴趣可以进去看看,不过最终你会得到个结论:System.Reflection.RuntimeMethodInfo的UnsafeInvokeInternal里下断
下断后F5,观察this的值,要是你认识的,那多半正常操作,不认识的,就得进去看看.不过第一次F5,就不认识了...
image.png

于是跟进去,然后就我x了.你好歹虚化的认真点....
[C#] 纯文本查看 复制代码
internal static \u000E\u2008\u2002\u2000 \u0002()
        {
                switch (0)
                {
                case 0:
                {
                        string text = \u0006\u200A\u2008\u2003.\u0002(1761156178);
                        break;
                }
                case 1:
                {
                        string text = \u0006\u200A\u2008\u2003.\u0002(1761156287);
                        break;
                }
                default:
                        throw new InvalidOperationException(\u0006\u200A\u2008\u2003.\u0002(1761156238));
                }
                switch (0)
                {
                case 0:
                {
                        string text2 = \u0006\u200A\u2008\u2003.\u0002(1761156178);
                        break;
                }
                case 1:
                {
                        string text2 = \u0006\u200A\u2008\u2003.\u0002(1761156287);
                        break;
                }
                default:
                        throw new InvalidOperationException(\u0006\u200A\u2008\u2003.\u0002(1761156238));
                }
                bool flag = \u0006\u2008\u2002\u2000.\u000F == null || \u0006\u2008\u2002\u2000.\u000F.\u000E == (\u000E\u2008\u2002\u2000)0 || \u000F\u2002\u2002\u2002.\u0003() == 4096;
                if (!flag)
                {
                        DateTime t = string.IsNullOrEmpty(\u0006\u2008\u2002\u2000.\u000F.\u0006) ? DateTime.MaxValue : DateTime.ParseExact(\u0006\u2008\u2002\u2000.\u000F.\u0006, \u0006\u200A\u2008\u2003.\u0002(1761155646), CultureInfo.InvariantCulture);
                        flag = (t < DateTime.Now);
                }
                if (!flag)
                {
                        return (\u000E\u2008\u2002\u2000)1;
                }
                return (\u000E\u2008\u2002\u2000)0;
        }


if里面那个return右键设置成下一条,跑起来,点开Excel文件,没问题,这个版本收工了...

下一个版本19.2,调试起来依旧是熟悉的味道,依旧找到了
[C#] 纯文本查看 复制代码
if (!flag)
{
return (\u000E\u2008\u2002\u2000)1;
}
return (\u000E\u2008\u2002\u2000)0;

可是问题来了,与上个版本不同,这里被执行了2次,然后,2次跳过判断后生成的文件是错误的.
捡个懒,有注册码时我不是已经破了一边了,那么看看正确的应该怎么走.
vs里的代码把注册的信息加上,string.compare里下断,flag是0还是1那个地方也下断,还有保存文件加sheet的下一句也下断(就是提示要考的那句),跑起来,就到了比较函数里,值改成2099年
image.png
继续跑,到了,2次断在flag,均返回的1,和我们跳2次的操作一样,那肯定下面有坑.
继续跑,就到了加sheet的下一句
[Asm] 纯文本查看 复制代码
u000E_u200B.\u000E\u200B\u2005\u2009\u0002(stream);

然后F10,程序断在了string的比较函数里面.让他返回,然后又到了熟悉的代码,这是虚拟化以前版本的注册判定过程.
[C#] 纯文本查看 复制代码
                string text2 = array[0] + array[1] + array[2];
                if (string.Compare(text2, this.\u0008) > 0)
                {
                        return;
                }
                if (this.\u0006 != null)
                {
                        string strA = DateTime.UtcNow.ToLocalTime().ToString(\u0005\u2001\u2009\u2003.\u0002(-1118133632));
                        if (string.Compare(strA, text2) < 0 || string.Compare(strA, this.\u0006) > 0)
                        {
                                return;
                        }
                }
                this.\u000E = (\u0005\u2002\u2002)1;
                \u0003\u2002\u2002.\u000F = this;

嗯,catch you baby!
然后继续F10,返回到了这里
[Asm] 纯文本查看 复制代码
internal sealed class \u0003\u200B\u2004
{
        // Token: 0x06003589 RID: 13705 RVA: 0x001EA35C File Offset: 0x001E855C
        internal static bool \u0002()
        {
                if (\u0006\u200A\u2002\u2000.\u0002 != null)
                {
                        if (\u000F\u2008\u2002\u2000.\u0003)
                        {
                                lock (\u0006\u200A\u2002\u2000.\u0002)
                                {
                                        new \u0003\u2002\u2002().\u0002();
                                        \u000F\u2008\u2002\u2000.\u0003 = false;
                                }
                        }
                        return \u0003\u2002\u2002.\u0003() == (\u0005\u2002\u2002)0;
                }
                return false;
        }

大致猜猜他的作用,没注册信息,直接返回false,有注册信息,就再判断一次注册了没.
vs里删除注册那句,再跑到这里.好吧,我猜错了...他跑到了if里.然后到了这里
[C#] 纯文本查看 复制代码
return \u0003\u2002\u2002.\u0003() == (\u0005\u2002\u2002)0;

跟进去吧
[C#] 纯文本查看 复制代码
...
...
...
if (\u0003\u2002\u2002.\u000F != null && \u0003\u2002\u2002.\u000F.\u000E != (\u0005\u2002\u2002)0 && \u0006\u2005\u2002\u2002.\u0003() != 4096)
                {
                        return (\u0005\u2002\u2002)1;
                }
                return (\u0005\u2002\u2002)0;

熟悉不?怎么改不用说了吧,然后让程序跑起来,结束,检查Excel文件,没问题了.
所以,它虚拟化判断次注册没,再不虚拟判断次注册.

该下个版本了,但别急着收工,要每次加注册信息找位置好麻烦啊...找找特征,.一个程序块返回后到这里.
[C#] 纯文本查看 复制代码
                if (!this.\u0005.\u0005\u2000 && \u0003\u200B\u2004.\u0002())
                {
                        byte[] array2 = Convert.FromBase64String(\u0006\u2002\u2002.\u0002);
                        \u0002.\u0002(array2, array2.Length);
                        return;
                }

有个Convert.FromBase64String,记下来,估计用得着.

按照有注册码搞它的时候,19.2和最新版本应该差不多.我们就直接最新版本21.3了
还是同样的方法,跑起来和19.2没区别,熟门熟路的着到了第一个地方.然后再跑到进虚化的下一句.
按照刚才的经验,base64那里去下断,果然断在了想断的地方,代码都一样.base64句的上句点几下,就又来到了熟悉的第二个地方,代码就不贴了,和上面的一模一样.
2个地方return前的IL指令,ldc.i4.0改ldc.i4.1 ,最新版收工.

最后,请大家还是以学习为主,好用请支持正版.

免费评分

参与人数 7吾爱币 +6 热心值 +7 收起 理由
lpy628 + 1 + 1 热心回复!
夜泉 + 1 + 1 用心讨论,共获提升!
我怎么睡得着 + 1 我很赞同!
0hMyGod + 1 + 1 我很赞同!
azcolf + 1 + 1 热心回复!
?Ustinian + 1 + 1 谢谢@Thanks!
混曌大魔王 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

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

Christia 发表于 2021-4-1 07:07
牛啊,老哥
vipcrack 发表于 2021-4-1 07:51
fly19930620 发表于 2021-4-1 08:06
tj288 发表于 2021-4-1 08:10
厉害啊,感谢分享楼主
chen4321 发表于 2021-4-1 08:21
楼主牛p,分析的很透彻
pdcba 发表于 2021-4-1 08:30

谢谢分享,谢谢楼主
syp12138 发表于 2021-4-1 08:39
还是强大的老哥
orisine 发表于 2021-4-1 08:42
厉害了,学到了
zhuyanxiang 发表于 2021-4-1 09:27
非常棒的学习知识,谢谢大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-27 10:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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