• 字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串


    关于字符串的研究,目前已经有两篇。

    原理篇:字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串 

    实践篇:字符串反混淆实战 Dotfuscator 4.9 字符串加密技术应对策略

    今天来讲第三篇,如何应用上面所学内容,设计一个字符串混淆程序。

    先设计一个控制台程序,它是将要被我混淆的程序集文件:

    public static void Main()
    {
            try
            {
                RunSnippet();
            }
            catch (Exception e)
            {
                string error = string.Format("---
    The following error occurred while executing the snippet:
    {0}
    ---", e.ToString());
                Console.WriteLine(error);
            }
            finally
            {
                Console.Write("Press any key to continue...");
                Console.ReadKey();
            }
    }
     

    代码是Snippet Compiler 的标准模板,在控制台上打印一段字符串。这里,有二个字符串常量,是我需要对它进行加密的地方。

    image

    不能直接改源代码,而且要以程序的方式来操作程序集。要能修改.net程序集,现在能找到的方法是Mono.Cecil,最新的版本是0.9.5.0。

    先设计混淆算法,是个很简单的Base64代码转换,这一步可以深入挖掘,做更复杂的混淆算法。

    public static string StringDecode(string text1)
    {
        return Encoding.UTF8.GetString(Convert.FromBase64String(text1));
    }

    要把这段代码转化为IL代码,用Mono.Cecil来注入到制定的程序集中,先来看看翻译成IL之后的代码

    .method public hidebysig static string StringDecode(string) cil managed
    {
        .maxstack 8
        L_0000: call class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_UTF8()
        L_0005: ldarg.0 
        L_0006: call uint8[] [mscorlib]System.Convert::FromBase64String(string)
        L_000b: callvirt instance string [mscorlib]System.Text.Encoding::GetString(uint8[])
        L_0010: ret 
    }

    再对比Mono.Cecil的语法例子,把上面的IL代码,翻译成C#代码

    MethodDefinition new_method = new MethodDefinition("StringDecode", attr, asm.MainModule.Import(typeof(string)));
    ParameterDefinition para = new ParameterDefinition(asm.MainModule.Import(typeof(string)));
    new_method.Parameters.Add(para);
    tp.Methods.Add(new_method);
    
    new_method.Body.MaxStackSize = 8;
    MethodReference mr;
    ILProcessor worker = new_method.Body.GetILProcessor ();
    
    mr = asm.MainModule.Import(typeof(Encoding).GetMethod("get_UTF8"));
    worker.Append(worker.Create(OpCodes.Call, mr));
    
    worker.Append(worker.Create(OpCodes.Ldarg_0));
    
    mr = asm.MainModule.Import(typeof(Convert).GetMethod("FromBase64String"));
    worker.Append(worker.Create(OpCodes.Call, mr));
    
    mr = asm.MainModule.Import(typeof(Encoding).GetMethod("GetString", new Type[] { typeof(Byte[]) }));
    worker.Append(worker.Create(OpCodes.Callvirt, mr));
    worker.Append(worker.Create(OpCodes.Ret));

     

    再次,我样要搜索目标程序集中的所有字符串,把它转化成Base64的字符串编码,于是遍历IL指令,进行转化

    List<Instruction>  actionInsert=new List<Instruction> ();
    foreach (Instruction ins in entry_point.Body.Instructions)
    {
           if (ins.OpCode.Name == "ldstr")
           {
                    Console.WriteLine("Find target instruction, start modify..");
                    byte[] bytes = System.Text.Encoding.UTF8.GetBytes (Convert.ToString (ins.Operand));
                    ins.Operand = Convert.ToBase64String (bytes);
                        
                    actionInsert.Add(ins);
          }
    }
     
     

    最后,我们把原来的指令替换成字符串混淆算法调用

    for (int i = 0; i < actionInsert.Count; i++)
    {
             mr = asm.MainModule.Import(new_method);
             worker = entry_point.Body.GetILProcessor();
             worker.InsertAfter(actionInsert[i], worker.Create(OpCodes.Call, mr));
    }
     
     

    最后保存程序集,用.net Reflector 载入程序集,如下图所示,字符串常量已经变成了方法调用:

    image

    这样,增加了代码反编译的难度,字符串的含义完全被替换成一堆无意义的字符串。

    Mono.Cecil最新的版本中,API有变化,本篇程序代码中应用到的读取程序集和写入程序集

    string path = @"C:UsersAdministratorDesktopCPPDefault.exe";
    AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(path);
    MethodDefinition entry_point = asm.EntryPoint;
    
    path = @"C:UsersAdministratorDesktopCPPDefaultSecury.exe";
    asm.MainModule.Write(path);

    关于字符串混淆算法,下面列举几个我找到的混淆算法,加密强度会高一些:

    static string stringEncrypt(string string_0)
    {
        char[] chArray;
        char[] chArray1 = chArray = string_0.ToCharArray();
        while (true)
        {
            int num;
            int length = chArray1.Length;
            if (length <= 0)
            {
                break;
            }
            chArray1[num = length + -1] = (char) (chArray[num] - 'ᑩ');
        }
        return string.Intern(new string(chArray));
    }
     

    下面是Dotfuscator的混淆算法,还加了盐,强度提升不少。

    static string GetString(string source, int salt)
     {
         int index = 0;
         char[] data = source.ToCharArray();
         salt += 0xe74d6d7; // This const data generated by dotfuscator
         while (index < data.Length)
         {
             char key = data[index];
             byte low = (byte)((key & 'x00ff') ^ salt++);
             byte high = (byte)((key >> 8) ^ salt++);
             data[index] = (char)((low << 8 | high));
             index++;
         }
         return string.Intern(new string(data));
     }
     

    经过混淆后的字符串,完全看不出原文的含义。比如下面的代码片段,都有些怀疑它使用的字符集,有点像中东国家的语言

    image 

    再来看一个代码中有中文的例子,这是一段用户登陆代码,它加密后的字符看起来更不可理解。

    image

    如果要给字符串混淆加盐,只需要简单的修改上面的代码,添加一个临时变量,再增加到调用的混淆算法中。

    全文代码以NUnit测试方法写成,单元测试配合Resharper真是好用,可以节省大量的代码,一个方法即可作为入口程序启动运行。

    给开发和测试带多很多方便。

  • 相关阅读:
    算法(第四版)C# 习题题解——2.4
    算法(第四版)C# 习题题解——2.3
    .NET编译的目标平台(AnyCPU,x86,x64)
    Windows无法访问局域网内共享文件夹[0x800704cf,0x80070035]解决方案
    ASP.NET 网站部署到IIS上如何进行调试
    ASP.NET 前台Javascript调用后台代码 / 后台调用前台Javascript
    C#反射-Assembly.Load、LoadFrom与LoadFile
    Entity Framework Context上下文管理(CallContext 数据槽)
    Entity Framework(EF的Code First方法)
    Entity Framework(EF的Model First方法)
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/3168786.html
Copyright © 2020-2023  润新知