由于工作的原因,很长时间没有写文章了。和kanxue聊下天,觉得坛里关注.net的人不少,但讨论.net的人不多,因此决定还是抽空写一点东西。质量如何倒是第二位的了,关键是能拉动一些讨论的热情。.net加解密这方面,玩儿这个的感觉发展是突飞猛进,上半年刚学的知识,下半年就有点过时了,这种情况下,.net的加解密似乎成了一个小圈子里几个人的爱好。.net保护这两年发展迅猛,个人感觉今年尤其快速。基本现在分四大类了:混淆、加密(壳)、授权、VM。这四方面都是有机的结合,使得保护的强度更上一层楼。有机会,大家应该都了解一下,其中也有很有技术含量的东西,并非早几年那样,拿到个程序,reflector反编译看下,修改了再ilasm就完事儿了。
废话说到这,今天分析是{smartassembly} v2.2,说不完全分析是因为当逆向了80%的时候,发现0day出破解了。于是没有继续搞下去,只能把所做的工作拿出来献丑,希望引起大家的兴趣,共同研究.net软件安全。
{smartassembly}是一款.net混淆软件,采用了基本上目前所有主流的混淆技术,其实也就两种:名称、流程。但混淆保护中辅助手段是不可少的:打包、强名称校验、错误元数据等。软件名字起的不错,所以安装后在Program Files目录下总是排在第一。
拿到软件先运行一下,提示还有几天试用到期,到期后则无法进入。
习惯性的,用Reflector载入,来到入口点,直接显示// This item is obfuscated and can not be translated。切换成IL,看一下流程混淆的形式,开始的几句代码如下:
看来是利用跳转表进行的混淆。名称混淆也是比较烦人的那种乱码,如下图:代码:L_0000: call bool .:: () L_0005: brtrue.s L_000c L_0007: leave L_0799 L_000c: br L_0698 L_0011: br L_06a2 L_0016: br L_06ac L_001b: brtrue.s L_0022 L_001d: leave L_0799 L_0022: br L_06b6 L_0027: ldlen
浏览一下资源,发现N个DLL,其中还有一个名叫{license}.dll,这个名称很敏感,一会再好好看。
先试一下有没有强名,用Strong Name Remove或者RE-Sign去除或者替换强名,程序都会运行出错。这时,就应该考虑是否代码有对强名称的调用了。一般调用就两个AssemblyName.GetPublicKey和GetPublicKeyToken。
用ildasm反编译成il文件,搜索上面两个方法,可以见到如下代码:
该段代码中用到了"{e75060b3-2d5b-48f0-8182-1dfcf56cfa33}"这个资源文件,初看上去像是强名参与该资源的加解密。可是该资源是干什么的呢?一会就知道了。代码:IL_0015: ldsfld class [mscorlib]System.IO.Stream ' '.' '::' ' IL_001a: brtrue IL_0067 IL_001c: call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly() IL_0021: stloc.1 IL_0022: ldloc.1 IL_0023: ldstr "{e75060b3-2d5b-48f0-8182-1dfcf56cfa33}" IL_0028: callvirt instance class [mscorlib]System.IO.Stream [mscorlib]System.Reflection.Assembly::GetManifestResourceStream(string) IL_002d: stsfld class [mscorlib]System.IO.Stream ' '.' '::' ' IL_0032: ldloc.1 IL_0033: callvirt instance class [mscorlib]System.Reflection.AssemblyName [mscorlib]System.Reflection.Assembly::GetName() IL_0038: callvirt instance uint8[] [mscorlib]System.Reflection.AssemblyName::GetPublicKeyToken()
.net混淆的另一个特征是字符被加密,寻找敏感字符串可是解密的重要线索之一。在Reflector中浏览需要用到字符串的指令,比较方便的是寻找窗体类的初始化方法,一般都要给控件命名,很快找到一段指令:
代码:L_0096: ldc.i4 0x3076 L_009b: call string . :: (int32) L_00a0: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
太明显了,那个一拐一个加号的乱码方法就是解码函数。点击浏览其代码,正是刚才调用GetPublicKeyToken的代码。这个方法被混淆了,要得出解码方法比较累。手工反混淆该方法,然后用ilasm编译(注意此时还不可以运行,并且乱码编译时会发出很不爽的响声,建议关闭扬声器)。编译之前要注意,资源里有两个文件是不可打印字符,因此无法生成文件,在ilasm编译时可以把这两项暂时屏蔽。
然后用Reflector再次浏览该字符串解码方法,反混淆得到的C#代码如下图所示:
这样很容易编写得到解码算法,源代码中的字符串也就很容易得到了。这里又出现了另个问题,强名是要参与解码的,而这个程序我们是要爆破的,如果加新的强名,则解码肯定出错。这里有两个方法:一是用新的强名通过逆算法把资源重新解密再加密,烦;二是直接插入根据老的强名算出的值,简单,可行。
强名在解码中的用处如下:
???? 因此我们只需算出num既可。根据原始强名算出num=11167,于是在IL中添加如下代码:代码:
while (num < (originalPublicKeyToken.Length - 1))
{
offset ^= (originalPublicKeyToken[num] << 8) + originalPublicKeyToken[num + 1];
num += 2;
}
???? 这时,再次用ilasm编译程序,已经可以执行了,不过还没有解决授权的问题。接下就看看负责授权的{license}.dll。Reflector载入后发现已经被混淆了,但是同刚才步骤一样,手动反混淆,方法比较少,几步搞定,这下能看到C#了。代码:
ldc.i4 11167//添加的代码
stsfld int32 ' '.' '/*02000007*/::' ' /* 04000009 */ //添加的代码
IL_0067: ldsfld class [mscorlib/*23000008*/]System.IO.Stream/*01000016*/ ' '.' '/*02000007*/::' ' /* 04000008 */
IL_006c: ldarg.0
IL_006d: ldsfld int32 ' '.' '/*02000007*/::' ' /* 04000009 */
一个名叫bool Validate()的方法引起了注意,看下代码,正是授权系统,代码如下:
???? 搞定这个简单,在开头第一句加上return true不就结了。在il中把该方法的代码改为:代码:
public static bool Validate()
{
DateTime time = DateTime.Parse("{6b25f59b-30dd-4b47-a739-10fc96df587e}");
if ((DateTime.Now > time) || (DateTime.Now < time.AddDays(-21)))
{
. mainForm = new . (string.Format("This Assembly has been built with an evaluation version of {{smartassembly}}, which has expired on {0}.\n\nYou need to purchase a license of {{smartassembly}}.", time.ToString("D")), "{smartassembly} License Exception", "error");
();
Application.Run(mainForm);
return false;
}
bool flag = false;
try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\{smartassembly}");
if (key != null)
{
if (((string) key.GetValue("Path", string.Empty)).Length > 0)
{
flag = true;
}
key.Close();
}
}
catch
{
}
if (flag)
{
return true;
}
try
{
. 2 = new . ("This application has been built with an evaluation version of {smartassembly}, and thus cannot be distributed. You can install {smartassembly} on this computer to be able to run this application.\n\nThis application will now quit.", "{smartassembly} Evaluation Version", "error");
();
2.BackColor = Color.White;
2.ShowDialog();
}
catch
{
}
return false;
}
???? 其余代码删除,既好用又减肥。ilasm重新编译,运行不出错,但是在混淆时会出错。最后得出是同目录下的{database}.mdb文件在作怪,打开这个数据库需要密码,我第一个猜的密码就是publickeytoken,果不其然。用Access打开mdb文件,取消密码,程序可以正常使用了。代码:
ldc.i4.1
return
本准备继续下去,把对mdb处理的地方找到,但这时发现了网上已有的0day破解,于是失去了斗志,也就半途而废了。把这个过程写出来,中间省略了很多繁琐的细节,希望能对大家有用,也希望更多的人来讨论.net。
附件中提供字符串解码程序,要看源码的reflector。