手工解码混淆字符串

想对一个第三方代码库进行优化,使用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)程序中其他地方可能也会使用到相同名称的变量,而不仅仅是在解码函数中使用。

情况比较复杂,处于少量代码的话,用手工解决就行了。

 

 

 

 

posted on 2014-01-19 12:14  一江春水  阅读(4107)  评论(0编辑  收藏  举报

导航