一:背景
1. 講故事
前幾天在除錯物聯櫃終端上的一個bug時發現 app.config 中的資料庫連線串是加密的,因為除錯中要切換資料庫,我需要將密文放到專門的小工具上解密,改完連線串上的資料庫名,還得再加密貼到 app.config 中,煩的要死,內容如下:
<appSettings>
<!-- 資料庫連線字串 -->
<add key="OLEDBConnStr" value="XfES27am6Muw48iB1GlMVqvUbq7/Pp9n4XbZJsDu19YDr/Zdb3m7KT6haD7f9HLj/ZEvIiZbmSU4O5L9g03Y5IUB6KLCZI7s3nDLwTIC+bXLf5quu/r8ZAI+rgNnsNZdwoDfquRLQy5Cf2X8/MFDOcMNaZYMpTYeHsZoEERU/TP9t3n5QllJTihrmDFbiGHLqe1kfN3uB3g1kgs0oobIEfNPr09kQ/pFgzZi/kZCrK10PLZZ0pFj1YU5ReFqBsdBlecV3D2Zl3lx1Ibls24t7w==" />
</appSettings>
改完bug之後,我就想這玩意能防的了誰呢? 私以為搞這麼麻煩也就防防君子,像我這樣的 曉人
,加不加密都是等於沒加密,照樣給你脫庫。。。???
二:使用 ILSpy 去脫庫
1. 從DAL/Repository層去反編譯程式碼
要想得到明文的資料庫連線串,可以從程式碼中反推,比如從 DAL
或者 Repository
中找連線串欄位 ConnectionString
,我這邊的終端程式是用 wpf 寫的,採用的是經典的三層架構,所以在 bin
下可以輕鬆找到,如下圖:
接下來用 ILSPy
反編譯這個 dll。
從上圖中可以看出,連線串的明文是存放在: OleDbHelper.ConnectionString
中的,然後可以看到,程式中定義了一個 Decrypt
方法專門用來解密連線串,哈哈,有了這個演算法,是不是就可以脫庫啦??? 如下程式碼所示:
class Program
{
static void Main(string[] args)
{
var str = "XfES27am6Muw48iB1GlMVqvUbq7/Pp9n4XbZJsDu19YDr/Zdb3m7KT6haD7f9HLj/ZEvIiZbmSU4O5L9g03Y5IUB6KLCZI7s3nDLwTIC+bXLf5quu/r8ZAI+rgNnsNZdwoDfquRLQy5Cf2X8/MFDOcMNaZYMpTYeHsZoEERU/TP9t3n5QllJTihrmDFbiGHLqe1kfN3uB3g1kgs0oobIEfNPr09kQ/pFgzZi/kZCrK10PLZZ0pFj1YU5ReFqBsdBlecV3D2Zl3lx1Ibls24t7w==";
Console.WriteLine(Decrypt(str));
}
public static string Decrypt(string str)
{
if (!string.IsNullOrEmpty(str))
{
DESCryptoServiceProvider descsp = new DESCryptoServiceProvider();
byte[] key = Encoding.Unicode.GetBytes("Oyea");
byte[] data = Convert.FromBase64String(str);
MemoryStream MStream = new MemoryStream();
CryptoStream CStream = new CryptoStream(MStream, descsp.CreateDecryptor(key, key), CryptoStreamMode.Write);
CStream.Write(data, 0, data.Length);
CStream.FlushFinalBlock();
return Encoding.Unicode.GetString(MStream.ToArray());
}
return "";
}
}
不過還好,資料庫也是在客戶那邊獨立部署的,不存在走外網的情況,不然就玩大了。。。 接下來我們來看看如何去防範。
2. 加殼/混淆/加密狗
現在市面上商業版和免費版都提供了給C#程式碼進行加密和混淆,不過我沒用過,我想最多在反編譯程式碼後閱讀性上增加了一些障礙,這也不過是時間問題罷了,畢竟SqlConnection,SqlCommand 這些FCL的類你是沒法混淆的,我從這些類上反推可以很輕鬆的就能找到明文的 ConnectionString ,所以這條路我覺得是走不通的。
3. 將解密演算法放在 server 端
既然 解密演算法
埋在客戶端你都能挖出來,那把它放在 server 端不就可以啦? 在程式啟動的時候,呼叫一下 webapi 進行解密,這樣你總沒轍了吧 ??? 哈哈,大家可以開動腦子想一想,這種方法可行不可行?誠然,解密演算法搬走了,再用 ILSpy 去挖已經沒有任何意義了,但這裡有一個重要突破點,不管是用什麼形式解密的,最後的連線串明文都是存放在 OleDbHelper.ConnectionString
這個靜態變數中,對吧!接下來的問題就是有沒有辦法把程式中的這個靜態變數給挖出來? 你說的對,就是抓程式的 dump檔案 用 windbg 去挖。
三:使用 windbg 去脫庫
1. 思路
要想挖出 OleDbHelper.ConnectionString
,其實也很簡單,在 CLR via C#
第四章中關於物件型別和型別物件的解讀有這麼一張圖,很經典。
從上圖中可以看到,靜態欄位是在 Manager 型別物件
中,例項欄位都是在 Manager 物件
中,對照這張圖,我只需要通過 windbg 找到 OleDbHelper 型別物件
,也就是所謂的 EEClass
。
2. windbg 挖礦實戰
- 使用 !name2ee 找到 Decrypt 方法描述符(MethodDesc)
0:000> !name2ee xxx.Utilities.dll xxx.Utilities.Database.OleDbHelper.Decrypt
Module: 08ed7cdc
Assembly: xxx.Utilities.dll
Token: 060002aa
MethodDesc: 08ed83b0
Name: xxx.Utilities.Database.OleDbHelper.Decrypt(System.String)
JITTED Code Address: 048b6af0
上面的 MethodDesc: 08ed83b0
就是方法描述符的地址。
- 使用 !dumpmd 匯出方法描述符的詳細資訊,找到 OleDbHelper型別物件 的 EEClass 地址
0:000> !dumpmd 08ed83b0
Method Name: xxx.Utilities.Database.OleDbHelper.Decrypt(System.String)
Class: 08ecab30
MethodTable: 08ed8468
mdToken: 060002aa
Module: 08ed7cdc
IsJitted: yes
CodeAddr: 048b6af0
Transparency: Critical
上面的 Class: 08ecab30
就是 OleDbHelper型別物件 在堆上的記憶體地址。
- 使用 !dumpclass 匯出
Class: 08ecab30
,從而找到 OleDbHelper類的靜態欄位
0:000> !dumpclass 08ecab30
Class Name: xxx.Utilities.Database.OleDbHelper
mdToken: 02000033
File: D:\code\A18001\Source\Main\TunnelClient\bin\Debug\xxx.Utilities.dll
Parent Class: 795115b0
Module: 08ed7cdc
Method Table: 08ed8468
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100081 Abstract,
Transparency: Critical
NumInstanceFields: 0
NumStaticFields: 2
MT Field Offset Type VT Attr Value Name
799bfd60 4000152 74 System.String 0 static 04c28270 ConnectionString
799bfd60 4000153 78 System.String 0 static 04c299e8 SecurityConnectionString
從上面匯出資訊中可以看到 OleDbHelper類中 有兩個靜態欄位: ConnectionString
和 SecurityConnectionString
。
- 使用 !do 列印出兩個靜態欄位
看到沒有,上圖中的兩個紫色框框就是明文的 ConnectionString 哈,怎麼樣? ?不?。
四: 總結
當認識到上面的兩種脫庫方式,你應該就能想到,其實你在程式中連線資料庫,這本身就是一種錯,作業系統都能給你盜版,何況你這區區一個小軟體? 個人覺得完全杜絕的方式那應該就是:滅掉本地的sqlserver,讓所有的資料獲取都由遠端的 webapi 提供,當然這又是在脫離業務聊技術啦!???