前情提要
因为某个老项目 的 HTML to PDF 组件,有问题了需要修。
然后网上找了下解决方案,结果大部分是收费的。最终用的还是MIT的。
但是也有不少虽然不能用在项目里,但是可以拿来练手的。
受害者 来源
https://stackoverflow.com/questions/564650/convert-html-to-pdf-in-net
列表中 UERGIE1ldGFtb3JwaG9zaXMgLk5ldA== 所在系列 其中一员 RG9jdW1lbnQgLk5ldA==
虽然它不是 pdf 的,但是里面逻辑全面一点,其他库 核心逻辑 和算法一致
正篇开始
先上demo代码。
// NOTICE: Place this line firstly, before creating of the DocumentCore object.
DocumentCore.Serial = "1234567890";
// Let's create a new document by activated version.
DocumentCore dc = new DocumentCore();
dc.Content.End.Insert("Hello World!", new CharacterFormat() { FontName = "Verdana", Size = 65.5f, FontColor = Color.Orange });
// Save a document to a file in DOCX format.
string filePath = @"Result.docx";
dc.Save(filePath);
首先 扔到 dnspy 里面看看情况,发现有自定义混淆。
为了方便后续分析 找到字符串相关的 混淆的Token 用 de4dot 预先处理一下。
然后 license 的关键词是 Serial,查下引用
校验逻辑围绕 Load 和 Save,看着没啥暗桩。
那就直奔主题。
可以看到 校验完 license 之后 跟着的就是水印逻辑。
那么 只要 返回值 不在 switch 逻辑中 也就是 Enum14 不是 1-3 就不会有水印。
b( ̄▽ ̄)d 找到了 我们在这里爆破就可以收工啦!!!
开个玩笑,用Nuget的包 因为不少都有关联,不推荐去爆破它...还是逆向算法吧...
那么我们继续往里面看 Class121.smethod_1 的逻辑。
有两个函数 Class121.smethod_3 和 Class121.smethod_0
Class121.smethod_3 返回类型 Enum13
Class121.smethod_0 返回类型 Enum14
然后 Class121.smethod_0 里面又调用了 Class121.smethod_3
那么 重点应该在 Class121.smethod_0 里面
那么 license 的 核心逻辑 就是 Class121.smethod_3
额外校验在 Class121.smethod_0
其中 Class121.smethod_2(out text, out text2, out text3);
可以看到是根据 当前程序集 获取 一些 用于验证的项。
然后根据逻辑分析
if (@enum <= (Class121.Enum13)2332)
{
if (@enum == (Class121.Enum13)2329 || @enum == (Class121.Enum13)2332)
{
if (text != "Document .Net")
{
return (Class121.Enum14)3;
}
goto IL_105;
}
}
RG9jdW1lbnQgLk5ldA== 对应的 Enum13 应该为 2329 或 2332
结合 上图 Class121.smethod_3 的内容
那么也就是 我们用的 license 必须满足 下面的条件
enum = (Class121.Enum13)((string_0[0] * '\a' + string_0[10] * '\r' / (string_0[1] * '\v' + '\u0001') + string_0[2] * '_' + string_0[5] * 'X' + string_0[4] * 'T' + string_0[3] * 'E' + string_0[6] * ',' + string_0[9] * 'W' + string_0[7] * '\u0015' + string_0[8] * 'W') / (string_0[0] * 'U' - string_0[1] * 'Y' + string_0[2] * '-' - string_0[3] * 'U' - string_0[4] * '8' + string_0[5] * '\u0081' - string_0[6] * 'K' + string_0[7] * '^' - string_0[8] * 'X' + string_0[9] * 'n' - string_0[10] * 'N'));
// enum == 2329 || enum == 2332
再继续看
里面没有时间限制 但是有版本限制,结合开头部分的代码。
Enum14 为 0 则 无水印
好耶!可以在开头爆破了!(bushi...
if (major <= num)
{
enum2 = (Class121.Enum14)0;
}
return enum2;
也就是我们的 license 的 第一位 要大于等于 当前程序集 的 Version.Major
然后 如果想长期使用并且能更新的话 第一位最好是 9
结论,我们要弄出一个 长度11 (多了没啥用)并且首位是9的字符串。
然后满足上面的等式就行!
那么问题来怎么做呢?
对不起。。。我不会。。。我没看懂。。。我没想出来。。。
我偷懒去问了chatgpt,然后 它 顾左右而言他。。。
最后 穷举吧...(实在太丢人了,穷举的代码就不发了...期待算法大佬逆向下看看这玩意到底怎么算的....)
然后 我们就算出了个结果 OTAwMDIzOTM3OTg= !
然后 放到 Demo 代码中跑一下! ok 没水印了!完美(穷举)收工!
附录
该产品系列其他库对应 code
产品 |
code |
UERGIFZpc2lvbiAuTmV0 |
847 |
VXNlT2ZmaWNlIC5OZXQ= |
2131 |
RXhjZWwgdG8gUERGIC5OZXQ= |
2118 |
RG9jdW1lbnQgLk5ldA== |
2329 2332 |
UlRGIHRvIEhUTUwgLk5ldA== |
2129 |
SFRNTCB0byBSVEYgLk5ldA== |
3031 |
UERGIE1ldGFtb3JwaG9zaXMgLk5ldA== |
5128 |
UERGIEZvY3VzIC5OZXQ= |
3026 3027 3029 3028 |