字符串混淆技術應用 設計一個字符串混淆程序 可混淆.NET程序集中的字符串

關於字符串的研究,目前已經有兩篇。css

原理篇:字符串混淆技術在.NET程序保護中的應用及如何解密被混淆的字符串 html

實踐篇:字符串反混淆實戰 Dotfuscator 4.9 字符串加密技術應對策略算法

今天來說第三篇,如何應用上面所學內容,設計一個字符串混淆程序。ide

先設計一個控制檯程序,它是將要被我混淆的程序集文件:單元測試

public static void Main()
{
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", 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。ui

先設計混淆算法,是個很簡單的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#代碼spa

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:\Users\Administrator\Desktop\CPP\Default.exe";
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(path);
MethodDefinition entry_point = asm.EntryPoint;

path = @"C:\Users\Administrator\Desktop\CPP\DefaultSecury.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真是好用,能夠節省大量的代碼,一個方法便可做爲入口程序啓動運行。

給開發和測試帶多不少方便。

相關文章
相關標籤/搜索