標 題:
【原創】{smartassembly}不完全分析
作 者:
tankaiha
時 間:
2007-08-17,23:43:26
鏈 接:
http://bbs.pediy.com/showthread.php?t=49865
由於工作的原因,很長時間沒有寫文章了。和kanxue聊下天,覺得壇裡關注.net的人不少,但討論.net的人不多,因此決定還是抽空寫一點東西。質
量如何倒是第二位的了,關鍵是能拉動一些討論的熱情。.net加解密這方面,玩兒這個的感覺發展是突飛猛進,上半年剛學的知識,下半年就有點過時了,這種
情況下,.net的加解密似乎成了一個小圈子裡幾個人的愛好。.net保護這兩年發展迅猛,個人感覺今年尤其快速。基本現在分四大類了:混淆、加密
(殼)、授權、VM。這四方面都是有機的結合,使得保護的強度更上一層樓。有機會,大家應該都瞭解一下,其中也有很有技術含量的東西,並非早幾年那樣,拿
到個程式,reflector反編譯看下,修改了再ilasm就完事兒了。
廢話說到這,今天分析是{smartassembly} v2.2,說不完全分析是因為當逆向了80%的時候,發現0day出破解了。於是沒有繼
續搞下去,只能把所做的工作拿出來獻醜,希望引起大家的興趣,共同研究.net軟體安全。
{smartassembly}是一款.net混淆軟體,採用了基本上目前所有主流的混淆技術,其實也就兩種:名稱、流程。但混淆保護中輔助手段
是不可少的:打包、強式名稱校正、錯誤中繼資料等。軟體名字起的不錯,所以安裝後在Program Files目錄下總是排在第一。
拿到軟體先運行一下,提示還有幾天試用到期,到期後則無法進入。
查看次數: 507
檔案大小: 15.5 KB" src="http://bbs.pediy.com/attachment.php?attachmentid=7364&d=1187365039" border="0" alt="名稱: 1.JPG
查看次數: 507
檔案大小: 15.5 KB">
習慣性的,用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
看來是利用跳轉表進行的混淆。名稱混淆也是比較煩人的那種亂碼,如:
查看次數: 504
檔案大小: 17.3 KB" src="http://bbs.pediy.com/attachment.php?attachmentid=7365&d=1187365049" border="0" alt="名稱: 2.JPG
查看次數: 504
檔案大小: 17.3 KB">
瀏覽一下資源,發現N個DLL,其中還有一個名叫{license}.dll,這個名稱很敏感,一會再好好看。
先試一下有沒有強名,用Strong Name Remove或者RE-Sign去除或者替換強名,程式都會運行出錯。這時,就應該考慮是否代碼
有對強式名稱的調用了。一般調用就兩個AssemblyName.GetPublicKey和GetPublicKeyToken。
用ildasm反編譯成il檔案,搜尋上面兩個方法,可以見到如下代碼:
代碼:
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()
該段代碼中用到了"{e75060b3-2d5b-48f0-8182-1dfcf56cfa33}"這個資源檔,初看上去像是強
名參與該資源的加解密。可是該資源是幹什麼的呢?一會就知道了。
.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#代碼如所示:
查看次數: 503
檔案大小: 110.7 KB" src="http://bbs.pediy.com/attachment.php?attachmentid=7366&d=1187365086" border="0" alt="名稱: 3.JPG
查看次數: 503
檔案大小: 110.7 KB">
這樣很容易編寫得到解碼演算法,原始碼中的字串也就很容易得到了。這裡又出現了另個問題,強名是要參與解碼的,而這個程式我們是要爆破的,如果加
新的強名,則解碼肯定出錯。這裡有兩個方法:一是用新的強名通過逆演算法把資源重新解密再加密,煩;二是直接插入根據老的強名算出的值,簡單,可行。
強名在解碼中的用處如下:
代碼:
while (num < (originalPublicKeyToken.Length - 1))
{
offset ^= (originalPublicKeyToken[num] << 8) + originalPublicKeyToken[num + 1];
num += 2;
}
因此我們只需算出num既可。根據原始強名算出num=11167,於是在IL中添加如下代碼:
代碼:
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 */
這時,再次用ilasm編譯器,已經可以執行了,不過還沒有解決授權的問題。接下就看看負責授權的{license}.dll。
Reflector載入後發現已經被混淆了,但是同剛才步驟一樣,手動反混淆,方法比較少,幾步搞定,這下能看到C#了。
一個名叫bool Validate()的方法引起了注意,看下代碼,正是授權系統,代碼如下:
代碼:
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;
}
搞定這個簡單,在開頭第一句加上return true不就結了。在il中把該方法的代碼改為:
代碼:
ldc.i4.1
return
其餘代碼刪除,既好用又減肥。ilasm重新編譯,運行不出錯,但是在混淆時會出錯。最後得出是同目錄下的
{database}.mdb檔案在作怪,開啟這個資料庫要求輸入密碼,我第一個猜的密碼就是publickeytoken,果不其然。用Access開啟
mdb檔案,取消密碼,程式可以正常使用了。
本準備繼續下去,把對mdb處理的地方找到,但這時發現了網上已有的0day破解,於是失去了鬥志,也就半途而廢了。把這個過程寫出來,中間省略
了很多繁瑣的細節,希望能對大家有用,也希望更多的人來討論.net。
附件中提供字串解碼程式,要看源碼的reflector。