想對一個第三方代碼庫進行優化,使用Reflector進行“Export Source Code”,結果發現代碼是被混淆了的。
使用de4dot用以下命令將代碼混淆解決了:
de4dot 程序集
可是打開清除混淆后的程序集,看到其中的字符串仍然是混淆的。
打開de4dot官網https://bitbucket.org/0xd4d/de4dot, 找到動態解密字符串的一段描述
To decrypt strings, you'll first need to figure out which method or methods decrypt strings. To get the method token of these string decrypters, you can use ILDASM with the 'show metadata tokens' option enabled. A method token is a 32-bit number and begins with 06, eg. 06012345.
按de4dot官網的方法,使用如下命令
de4dot 程序集 --strtyp delegate --strtok 06000001 --strtok 0600002
其中的06000001 06000002是從程序集中ILDASM 找出的字符串混淆方法的token。
結果仍然不行。沒辦法了。
繼續在網上搜索相關信息。
終於找到另一種解決的思路。
1.既然程序集的混淆已經解決,就先用Reflector進行“Export Source Code”。得到的源代碼中字符串被混淆,使用到字符串的地方會類似以下形式:
builder.Append(smethod_0("㼁㤃㬅㔇㜉ㄋ㌍ⴏ⼑⤓", 8));
或是:
builder.Append(a("㼁㤃㬅㔇㜉ㄋ㌍ⴏ⼑⤓", 8));
以上的smethod_0或 a就是程序集中進行動態解碼的函數。我們只需要調用以上函數,就可對字符串進行解碼。
2.因此,可編寫以下函數,負責調用解碼函數:
public static string DecodeString(string encryptedString, int key) { string assemblyPath = 程序集路徑及名稱; Assembly assembly = Assembly.LoadFile(assemblyPath); MethodInfo secretMethod = assembly.GetModules()[0] .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)[0]; string decryptedString = secretMethod.Invoke(null, new object[] { encryptedString, key }) as string; return decryptedString; }
以上函數傳入兩個參數:第一個是編碼后的字符串,第二個是一個key值,在源碼中的調用smethod_0或 a方法是會給出該參數,調用時用這個值就可以了。
3.有了以上函數,就可用以下方式調用進行解碼了。
string str = "渉渋洍猏㈑栓眕琗渙礛"; Console.WriteLine(DecodeString(str, 17));
4.可是,以上方法一次只能解出一個字符串,顯然效率太慢了。理想的是將該程序集中反編譯出來的源碼中的字符串一次處理完。這時可考慮按以下方式進行處理。
(1)首先將程序集反編譯到一個目錄中。
(2)編寫以下控制台程序:
string path = 反編譯代碼存放的目錄; string oPath = 字符串解碼后的代碼存放目錄; foreach (string filename in System.IO.Directory.GetFiles(path, "*.cs")) { string filecontent = ""; StreamReader txtStreamReader = new StreamReader(filename); filecontent = txtStreamReader.ReadToEnd(); string pattern = @"smethod_0\(.*?\)"; Regex regex = new Regex(pattern); string result = regex.Replace(filecontent, new MatchEvaluator(trans)); string outfile = GetFileName(filename); FileStream fs = new FileStream(oPath + outfile, FileMode.Create); StreamWriter sw = new StreamWriter(fs); sw.Write(result); sw.Flush(); sw.Close(); fs.Close(); }
以上代碼循環處理指定目錄中的所在.cs文件,通過正則表達式找出代碼中包含“smethod_0()”的字符串(這里smethod_0是字符串解碼函數),然后通過調trans函數進一步處理,trans函數的代碼如下:
static string trans(Match m) { string str1 = ""; int key; Regex reg1 = new Regex("\"(.*)\",\\s([0-9]+)"); Match mat = reg1.Match(m.Value); if (mat.Success) { str1 = mat.Groups[1].Value; key = Convert.ToInt32(mat.Groups[2].Value); return "\"" + DecodeString(str1, key) + "\""; } else { return m.Value; } }
以上代碼用正則表達式對傳入的類似 smethod_0("㼁㤃㬅㔇㜉ㄋ㌍ⴏ⼑⤓", 8) 的字符串進行解析,提取需要解碼的字符串和key值,然后調用DecodeString函數進行解碼,解碼后返回,以替換原字符串。
5. OK,經過以下處理,指定目錄下的cs文件都會被逐個處理,字符串將會被解碼。
不過,需要注意,在使用以上代碼進行批處理解碼之前,應先逐個檢查代碼中的key值,在混淆時,有的地方會使用變量,還有的地方會使用16進制。例如,有類似以下代碼
num2=5; smethod_0("㼁㤃㬅㔇㜉ㄋ㌍ⴏ⼑⤓", num2) ; smethod_0("㼁㤃㬅㔇㜉ㄋ㌍ⴏ⼑⤓", 0x13) ;
對於這種情況,以上代碼是沒辦法處理的,這時只有人工先檢查掃描一次代碼,將變量替換為10進制數據,將16進制數據也替換成10進制數據。然后再運行程序解碼。
當然,也可編寫程序,先掃描一篇源碼,用程序來進行人工檢查替換工作。編寫掃描程序時,必須考慮很多復雜的情況,例如:
(1)到變量的作用范圍(可能在一個函數是10,另一個函數中變成了0x13)
(2)源碼中使用的變量名變化程序(如不是用num2,而是num3,若其他)
(3)程序中其他地方可能也會使用到相同名稱的變量,而不僅僅是在解碼函數中使用。
情況比較復雜,處於少量代碼的話,用手工解決就行了。