JIEJIE.NET - 强大的 .NET 代码混淆工具

你好,这里是 Dotnet 工具箱,定期分享 Dotnet 有趣,实用的工具和组件,希望对您有用!

JIEJIE.NET

JIEJIE.NET 是一个使用 C# 开发的开源 .NET 代码加密工具。

很多 .NET 开发人员担心他们的软件被破解,版权受到侵犯,所以他们使用一些工具来混淆 IL 代码。比如 PreEmptive dotfuscator, 但有些场景的需求,是这些工具不能满足的。.

所以作者写了 JieJie.NET,它可以深度加密.NET程序集,帮助大家保护版权。重要的是,这个工具是开源的。

界面预览
JIEJIE.NET - 强大的 .NET 代码混淆工具
img
JIEJIE.NET - 强大的 .NET 代码混淆工具
功能特性

1.类型和成员重命名

旧代码:

public abstract class XTextDocumentContentElement : XTextContentElement
{
    public override void AfterLoad(ElementLoadEventArgs args);
    public override void Clear();
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public override XTextElement Clone(bool Deeply);
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public override XTextDocument CreateContentDocument(bool includeThis);
    public XTextSelection CreateSelection(int startIndex, int length);
    public override void Dispose();
    public override void DrawContent(InnerDocumentPaintEventArgs args);
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public override void EditorRefreshViewExt(bool fastMode);
    public float FixPageLinePosition(int pos);
    public override void Focus();
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public XTextLineList GetAllLines();
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public virtual XTextRange GetRange(int StartIndex, int EndIndex);
    public void InnerGetSelectionBorderElement(ref XTextElement startElement, ref XTextElement endElement);
    public void InvalidateSpecifyLayoutElements();
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public virtual bool IsSelected(XTextElement element);
    public void RefreshParagraphListState(bool checkFlag, bool updateListIndex);
    public XTextParagraphFlagElement RootParagraphFlag();
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public bool SetSelection(int startIndex, int length);
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    public bool SetSelectionRange(int firstIndex, int lastIndex);
}

加密后:

public abstract class XTextDocumentContentElement : XTextContentElement
{
    public override void Clear();
    public override XTextElement Clone(bool Deeply);
    public override XTextDocument CreateContentDocument(bool includeThis);
    public override void Dispose();
    public override void EditorRefreshViewExt(bool fastMode);
    public override void Focus();
    public XTextLineList GetAllLines();
    public virtual XTextRange GetRange(int StartIndex, int EndIndex);
    public virtual bool IsSelected(XTextElement element);
    public bool SetSelection(int startIndex, int length);
    public bool SetSelectionRange(int firstIndex, int lastIndex);
    public XTextParagraphFlagElement z0ZzZzbmm1mO001();
    public XTextSelection z0ZzZzbmm1mO011(int startIndex, int length);
    public void z0ZzZzbmm1mO01O();
    public float z0ZzZzbmm1mOOm1(int pos);
    public void z0ZzZzbmm1mOOmn(ref XTextElement startElement, ref XTextElement endElement);
    public void z0ZzZzbmm1mOOmO(bool checkFlag, bool updateListIndex);
    public override void z0ZzZzbmmOO11nn(z0ZzZzbm0mmlm1O args);
    public override void z0ZzZzbmmOOl0nO(ElementLoadEventArgs args);
}

可以看到,一些 API 的名称被混淆了。

2. 混淆代码流程

JieJie.NET 可以分析 IL 代码,并且在不丢失任何特性的情况下随机混淆代码流程。它可以破坏 foreach/lock/using, 让代码很难阅读,有时候还会导致破解工具错误。

旧代码:

public int RemoveByControl(object control)
{
    if (control == null)
    {
        throw new ArgumentNullException("control");
    }
    if (CheckOwner() == false)
    {
        return -1;
    }
    int result = 0;
    lock (this)
    {
        for (int iCount = _Tasks.Count - 1; iCount >= 0; iCount--)
        {
            if (_Tasks[iCount].Control == control)
            {
                _Tasks.RemoveAt(iCount);
                result++;
            }
        }
        if (_CurrentTask != null && _CurrentTask.Control == control)
        {
            _CurrentTask = null;
        }
    }
    return result;
}

使用 JieJie.NET 后,在 ILSpy 中显示的代码如下:

public int RemoveByControl(object control)
{
    //Discarded unreachable code: IL_000b, IL_0073
    //IL_000b: Incompatible stack heights: 1 vs 0
    //IL_0073: Incompatible stack heights: 1 vs 0
    int num = z0ZzZzgw.z0kh;
    bool flag = default(bool);
    int num4 = default(int);
    int result = default(int);
    while (true)
    {
        switch (num)
        {
        default:
        {
            if (control == null)
            {
                throw new ArgumentNullException(z0ZzZzow.z0rj);
            }
            if (!z0rk())
            {
                goto IL_0049;
            }
            int num2 = 0;
            z0ZzZzjw.z0uk(this);
            try
            {
                int num3 = z0ZzZzgw.z0ah;
                while (true)
                {
                    switch (num3)
                    {
                    default:
                        num2++;
                        goto IL_0097;
                    case 3:
                        if (flag)
                        {
                            z0ik = null;
                        }
                        break;
                    case 4:
                    case 5:
                        {
                            num4 = z0bk.Count - 1;
                            goto IL_009e;
                        }
                        IL_009e:
                        if (num4 < 0)
                        {
                            flag = z0ik != null && z0ik.Control == control;
                            num3 = z0ZzZzgw.z0wj;
                            continue;
                        }
                        if (z0bk[num4].Control == control)
                        {
                            z0bk.RemoveAt(num4);
                            num3 = z0ZzZzgw.z0sh;
                            continue;
                        }
                        goto IL_0097;
                        IL_0097:
                        num4--;
                        goto IL_009e;
                    }
                    break;
                }
            }
            finally
            {
                Monitor.Exit(this);
            }
            result = num2;
            break;
        }
        case 0:
        case 1:
        case 3:
            break;
        }
        break;
        IL_0049:
        result = -1;
        num = z0ZzZzgw.z0wj;
    }
    return result;
}

现在代码流程已经被破坏了。

3. 加密所有字符串值

JieJie.NET 可以收集程序集中定义的所有字符串值,然后把它们转换为新类中的静态只读字段,并对它们的值进行加密。

旧代码:

private string GetLicenseMessage()
{

    return "这是一个密钥 :" + Environment.UserName;
}

加密后:

private string GetLicenseMessage()
{
    string text = _0._6 + Environment.UserName;
    return text;
}
//  also create a new class, contains all string value in assembly in random order.
internal static class _0
{
    public static readonly string _0;
    public static readonly string _1;
    public static readonly string _2;
    public static readonly string _3;
    public static readonly string _4;
    public static readonly string _5;
    public static readonly string _6;
    public static readonly string _7;
    public static readonly string _8;
    public static readonly string _9;
    public static readonly string _10;
    public static readonly string _11;
    public static readonly string _12;
    public static readonly string _13;
    public static readonly string _14;
    public static readonly string _15;
    public static readonly string _16;
    public static readonly string _17;
    public static readonly string _18;
    public static readonly string _19;
    public static readonly string _20;
    public static readonly string _21;

    static _0()
    {
        byte[] datas = _BytesContainer__._0();
        _11 = GetStringByLong(datas, 151732605047602L);
        _20 = GetStringByLong(datas, 450799767951810L);
        _7 = GetStringByLong(datas, 101155071172227L);
        _4 = GetStringByLong(datas, 47279000500949L);
        _15 = GetStringByLong(datas, 415615395474299L);
        _5 = GetStringByLong(datas, 54975582493063L);
        _2 = GetStringByLong(datas, 17592187197342L);
        _14 = GetStringByLong(datas, 206708198516324L);
        _8 = GetStringByLong(datas, 124244814685054L);
        _21 = GetStringByLong(datas, 459595860893446L);
        _6 = GetStringByLong(datas, 72567769190975L);
        _13 = GetStringByLong(datas, 182518931688172L);
        _18 = GetStringByLong(datas, 433207581847376L);
        _16 = GetStringByLong(datas, 417814419099513L);
        _3 = GetStringByLong(datas, 36283884381871L);
        _1 = GetStringByLong(datas, 9895605165436L);
        _9 = GetStringByLong(datas, 136339442622330L);
        _19 = GetStringByLong(datas, 440904163377248L);
        _17 = GetStringByLong(datas, 426610511995160L);
        _0 = GetStringByLong(datas, 598562L);
        _10 = GetStringByLong(datas, 148434069970387L);
        _12 = GetStringByLong(datas, 158329675868829L);
    }
    private static string GetStringByLong(byte[] datas, long key)
    {
        int num = (int)(key & 0xFFFF) ^ 0xEF83;
        key >>= 16;
        int num2 = (int)(key & 0xFFFFF);
        key >>= 24;
        int num3 = (int)key;
        char[] array = new char[num2];
        int num4 = 0;
        while (num4 < num2)
        {
            int num5 = num4 + num3 << 1;
            array[num4] = (char)(((datas[num5] << 8) + datas[num5 + 1]) ^ num);
            num4++;
            num++;
        }
        return new string(array);
    }
}