Bir .NET assembly'sini dnSpy ya da ILSpy ile açtığınızda karşınıza assembly dili çıkmaz. Okunabilir C# çıkar: sınıf adları, metot gövdeleri ve her string literal, aşağı yukarı sizin yazdığınız gibi. C# yazılım lisanslamada bu, kuralları değiştirir. Bir API anahtarından IsPro bayrağına kadar assembly'ye gömdüğünüz her şeyi, yayınladığınız gün herkese açık kabul etmelisiniz.
Bu rehberde koda gömülü sırların .NET yazılımlarından neden bu kadar kolay çıkarıldığını, obfuscation'ın tek başına sorunu neden çözmediğini ve bir C# uygulamasını, değerli kısımları binary'de hiç yer almayacak şekilde nasıl lisanslayacağınızı anlatıyoruz. Sonunda pratik bir geçiş planı ve sırlar ortadan kalktıktan sonra da önemini koruyan istemci tarafı korumaları bulacaksınız.
.NET assembly'leri koda gömülü sırları neden ele verir?
C# makine koduna derlenmez. Her türü, metodu, alanı ve özelliği tanımlayan zengin metadata ile birlikte Intermediate Language'e (IL) derlenir. Runtime'ın kodunuzu yükleyip JIT ile derlemesi için bu metadata gerekir; decompiler'lar da okunabilir kaynak kodu yeniden oluşturmak için tam olarak aynı bilgiyi kullanır.
- Yapı derlemeden sağ çıkar. Namespace'ler, sınıflar ve metot adları metadata'da saklanır; bu yüzden bir decompiler projenizi neredeyse sizin yazdığınız gibi ortaya koyabilir.
- String literal'ler açıkta durur. Her literal, assembly'nin user-strings heap'inde saklanır. Basit bir string araması, tek satır kod okumadan API anahtarlarını, URL'leri ve parolaları bulur.
- Yapılandırma da birlikte gider.
appsettings.jsonveApp.configgibi dosyalar uygulamayla birlikte okunabilir metin olarak dağıtılır; gömülü kaynaklar da aynı araçlarla çıkarılabilir. - Düzenlemek de kolaydır. dnSpy bir metot gövdesini değiştirip assembly'yi geri kaydedebilir; yani
return falseifadesinireturn trueyapmak dakikalar sürer.
Bunların hiçbiri .NET'in bir kusuru değildir. Yönetilen (managed) koda reflection'ı, JIT derlemeyi ve mükemmel araçları kazandıran bir ödünleşimdir. Yalnızca şu anlama gelir: Bir C# binary'si, bir sırrı saklamak için olabilecek en kötü yerdir ve C# lisanslaması bu gerçeğe karşı değil, bu gerçeği hesaba katarak tasarlanmalıdır.
Hangi bilgiler koda gömülü sır sayılır?
Koda gömülü sırlar nadiren bilinçli bir kararla başlar. Fark ettirmeden yerleşirler: hiç değiştirilmemiş bir test anahtarı, bir yapılandırma dosyasındaki bağlantı dizesi, Pro sürümü açan bir boolean. Olağan şüpheliler ve saldırganın her biriyle yaptıkları şunlar:
| C# uygulamanızda | Saldırganın yaptığı | Taşınması gereken yer |
|---|---|---|
const string ApiKey | String aramasıyla bulur ve yeniden kullanır | Kendi backend'iniz |
| Pro özellik bayrağı | false değerini true yapıp assembly'yi kaydeder | Girişten sonra okunan sunucu tarafı bir değer |
| Uç noktalar ve yapılandırma | Onları kendi sunucusuna yönlendirir | Sunucu tarafı string'ler |
| Premium eklenti DLL'leri | Lisanssız olarak kopyalayıp yükler | Sunucu tarafı dosyalar |
| Lisans doğrulama kodu | Bir anahtar üreticiye (keygen) kopyalar | Yalnızca lisans sunucusu |
| Yerel lisans dosyasının anahtarı | Dosyanın şifresini çözer, düzenler ve yeniden şifreler | Hiçbir yer: lisans durumunu sunucuda tutun |
Maliyet korsanlıkla sınırlı değildir. Sızan bir üçüncü taraf anahtarıyla başkasının yaptığı her istek size faturalanır. Her kurulum aynı sırrı paylaştığı için tek bir sızıntı hepsini tehlikeye atar. Gömülü bir değeri değiştirmek de yeni bir sürüm yayınlamak ve her müşterinin güncellemesini beklemek demektir; bu sırada eski değer, zaten dağıtılmış her kopyada çalışmaya devam eder.
Obfuscation koda gömülü sırlar sorununu neden çözmez?
Obfuscator'lar kullanmaya değer. Sembolleri yeniden adlandırır, string literal'leri şifreler ve kontrol akışını karıştırır; bu da decompile edilen kodu takip etmeyi çok daha zor hâle getirir. Ama istemcide çalışırlar ve istemcinin çalışması gerekir.
Şifreli bir string, kodunuz onu kullanabilmeden önce çözülmek zorundadır; bu yüzden anahtar ve şifre çözme rutini, şifreli metinle aynı assembly'de dağıtılır. Kararlı bir saldırgan bu rutini debugger'da adım adım izleyebilir ya da değerin bellekte görünmesini bekleyip onu dump edebilir. de4dot gibi otomatik deobfuscator'lar da yaygın obfuscation yöntemlerinin çoğunu hiçbir elle müdahaleye gerek kalmadan geri alır.
Yani obfuscation zaman kazandırır, gizlilik değil. Binary'nizden çıkarılamayacak tek sır, hiç orada olmamış sırdır; istemci tarafı koruma ve sunucu tarafı sırlar yazısının dayandığı ilke de budur.
Sunucu tarafı sırlar: C# lisanslama nasıl çalışmalı?
İşe yarayan yaklaşım, koda gömmenin tam tersidir. Assembly, lisans sunucusuna nasıl ulaşacağını bilir; değerli başka hiçbir şeyi bilmez. Hassas olan her şey sunucuda tutulur ve yalnızca onaylı bir makinede geçerli bir lisansa sahip olduğunu kanıtlamış bir oturuma gönderilir.
Assembly'ye gömülü
- Her decompiler'da görünür
- Her kurulumda aynı sır
- Değiştirmek yeni sürüm gerektirir
- Kırılmış derlemede de çalışır
Sunucudan gönderilen
- Derlemede hiç yer almaz
- Yalnızca lisans kontrolünden sonra gönderilir
- Panelden değişir, yeniden derleme gerekmez
- Kırılmış derleme hiçbir şey almaz
- 01
Sır içermeyen bir derleme dağıtın
Assembly; arayüzünüzü, mantığınızı ve lisans sunucusuyla konuşan kodu içerir. API anahtarı yok, premium bayrak yok, doğrulama algoritması yok.
- 02
Korumalı bir oturum açın
Uygulama açılışta sunucuyla bir anahtar değişimi yapar. Ardından trafik şifrelenir ve imzalanır; böylece bir proxy onu okuyamaz, sahte bir sunucu da gerçeğinin yerine cevap veremez.
- 03
Kararı sunucuya bırakın
Lisans anahtarı ve bir donanım parmak izi bu oturumun içinde iletilir. Sunucu; anahtarın geçerli olduğunu, süresinin dolmadığını, yasaklanmadığını ve bu makineye bağlı olduğunu kontrol eder. Bu kararı hiçbir zaman istemci vermez.
- 04
String'leri ve dosyaları alın
Sunucu, sunucu tarafı string'leri ve dosyaları oturuma ancak bundan sonra gönderir. qPapel Auth'ta güvenli string'ler disk izi bırakmadan doğrudan belleğe aktarılır; değiştirilmiş yerel dosyalar da sunucu verilerini okuyamaz.
- 05
Kullanın, sonra bırakın
Her değeri yalnızca onu gerektiren işlem sürdüğü kadar tutun. Loglamayın, diske önbelleklemeyin ve kullanıcı ayarlarına serileştirmeyin.
Bu yaklaşımda decompile edilmiş bir assembly, lisanslama çağrılarınızın yapısını gösterir ama alınmaya değer hiçbir şey göstermez. Kurallar, string'ler, offset'ler ve sabitler yeniden derleme gerekmeden panelden değiştirilebilir; böylece bir değeri değiştirmek artık müşterilerin güncelleme yapmasına bağlı olmaz. Native geliştiriciler de aynı modeli kullanır; bunu lisanslı yazılımlar için C++ kimlik doğrulama yazısında anlatıyoruz.
Mevcut bir C# uygulamasından gömülü sırlar nasıl kaldırılır?
Çoğu ekip sıfırdan başlamaz. Mevcut bir uygulamayı sunucu tarafı sırlara taşımak, sırasıyla ilerlediğinizde kapsamı belli bir projedir.
- Derlemenize bir saldırgan gözüyle bakın. Yayın assembly'nizi ILSpy veya dnSpy ile açın ve string'lerinde anahtar, token, URL, parola ve bağlantı dizesi arayın. Yapılandırma dosyalarını ve gömülü kaynakları da kontrol edin.
- Bulduklarınızı sınıflandırın. Üçüncü taraf erişim bilgileri kendi backend'inize taşınır. Yapılandırma, uç noktalar, offset'ler ve özellik anahtarları sunucu tarafı string'lere dönüşür. Premium modüller sunucu tarafı dosyalar olur.
- Açığa çıkmış her şeyi değiştirin. Önceki bir sürümde dağıtılmış her sır sızmış kabul edilmelidir. Onu sunucu tarafına taşımak gelecekteki derlemeleri korur, zaten dolaşımdaki kopyaları değil.
Application.Run'dan önce,Mainiçinde kimlik doğrulayın. Kimliği doğrulanmamış bir oturumda hiçbir pencere açılmamalı, lisans kontrolü geçilmeden değerli hiçbir şey oluşturulmamalıdır.- Sunucu değerlerini bağımlılık olarak enjekte edin. Sabitleri okumak yerine sunucunun döndürdüğü değerleri onlara ihtiyaç duyan sınıflara aktarın; böylece bulunacak bir yedek değer kalmaz.
- Hata durumunda kapanın. Ağ hatası ya da başarısız bir kontrol, uygulamanın kapanması demektir; tüm özellik setiyle kısıtlı bir modda çalışmaya devam etmesi değil.
// Before: the values ship inside every copy of the assembly.
private const string ReportsEndpoint = "https://api.example.com/v2/reports";
private const bool ProEdition = true;
// After (illustrative): nothing sensitive in the build.
// Licensing.StartSessionAsync and session.GetStringAsync are placeholders
// for your licensing SDK's calls, not qPapel Auth API names.
static async Task Main()
{
var session = await Licensing.StartSessionAsync(LoadLicenseKey());
if (!session.IsAuthenticated)
return; // fail closed
var endpoint = await session.GetStringAsync("reports-endpoint");
var app = new ReportsApp(endpoint); // injected, not hard-coded
app.Run();
}Aynı anda başka bir lisanslama servisinden de geçiş yapıyorsanız KeyAuth'tan geçiş rehberi ürünleri, anahtarları ve SDK çağrılarını kesinti olmadan eşleştirmeyi adım adım anlatıyor.
Çevrimdışı kullanım, önbellekleme ve diğer istisnai durumlar
Gerçek müşteriler uygulamayı kullanmaya başladığı anda sunucu tarafı sırlar pratik soruları da beraberinde getirir. En sık karşılaşılanlar ve tasarımı bozmadan verilecek cevaplar şunlar:
- Çevrimdışı kullanım. Müşterilerin gerçekten çevrimdışı çalışması gerekiyorsa son başarılı kontrolden itibaren kısa bir süreye izin verin ve uygulama yeniden bağlanana kadar sunucuya bağlı özellikleri kapalı tutun. Süresiz bir çevrimdışı mod, sunucuyu tamamen devre dışı bırakmanın bir yoludur.
- Sunucu değerlerini önbelleklemek. Gönderilen string'leri ya da dosyaları diske yazmaktan kaçının. Bir şeyi önbelleklemeniz şartsa geçerli kullanıcı kapsamıyla Windows DPAPI onu diğer hesaplardan ve çalınan bir diskten korur, ama uygulamanızı çalıştıran kullanıcıdan korumaz; bu yüzden yalnızca okunmasını göze alabileceğiniz şeyleri önbellekleyin.
- Loglar ve telemetri. Sunucu tarafı değerleri loglardan, hata mesajlarından ve analitik olaylarından temizleyin. Binary'de hiç dağıtılmayan bir sır bile ayrıntılı bir log dosyasından sızabilir.
- Çökme dökümleri. Tam bir bellek dökümü, process'in o anda tuttuğu her şeyi içerir. Minimal dökümleri tercih edin ve tam dökümleri otomatik olarak yüklemeyin.
- Geliştirme derlemeleri. Test erişim bilgilerini yayın yapılandırmalarının tamamen dışında tutun ve yayınlamadan önce debug derlemenizi değil, son yayın assembly'sini kontrol edin.
.NET istemcisinin kendisini korumak
Sırları kaldırmak ödülü ortadan kaldırır. İstemci yine de korunmayı hak eder, çünkü bir sırrı çalamayan saldırgan bir lisansı paylaşmayı, lisanslama çağrılarınıza yama yapmayı ya da sunucunun ne gönderdiğini izlemek için uygulamayı bir debugger altında çalıştırmayı deneyecektir.
| Araç veya teknik | .NET uygulamasına etkisi | Onu sınırlayan |
|---|---|---|
| dnSpy, ILSpy | Assembly'leri decompile eder ve düzenler | Bulunacak sır yoktur, ayrıca process kara listeleri |
| de4dot | Yaygın obfuscation yöntemlerini temizler | Sunucu tarafı string'ler hedefi ortadan kaldırır |
| Process Hacker, System Informer | Belleği inceler ve dump eder | Kara listeler ve anti-dumping |
| Fiddler, mitmproxy | Trafiği yakalar ve düzenler | Şifreli, imzalı oturum trafiği |
| Adı değiştirilmiş veya özel araçlar | İsme dayalı kara listeleri atlatır | qPAnalyzer çalışma zamanı analizi |
| Anahtar paylaşımı | Tek lisansı çok sayıda bilgisayarda çalıştırır | Sunucuda HWID bağlama |
qPapel Auth'ta process ve pencere kara listeleri, debugger yasakları ve anti-VM kontrolleri Uzaktan ayarlar üzerinden açılır; böylece yeni bir sürüm yayınlamadan bunları sıkılaştırabilirsiniz. HWID kilidi tek bir anahtarın çok sayıda makinede çalışmasını engeller; qPAnalyzer da adı değiştirilmiş araçları adlarına göre değil, görünüşlerine ve davranışlarına göre işaretler. Bir derleme sızarsa Binary Trace sızan derlemeleri izler ve müşterilerin dosyalarınızı paylaşıp paylaşmadığını tespit eder.
Obfuscation'ı da sürdürün. Sırlar ortadan kalktığında görevi, değerleri gizlemekten lisanslama çağrılarınızın yapısını gizlemeye dönüşür; bu da onlara yama yapmanın maliyetini artırmaya devam eder. Planlar ve SDK ayrıntıları qPapel Auth ürün sayfasında.
Sıkça sorulan sorular
Çalınmaya değer hiçbir şey içermeyen bir .NET uygulaması yayınlayın
Gerçek C# DRM'i sırları daha iyi saklamakla ilgili değildir, onları hiç dağıtmamakla ilgilidir. Önce kimlik doğrulayın, lisans kararını sunucuda verin, string'leri ve dosyaları yalnızca o oturuma gönderin ve en değerli erişim bilgilerini kendi backend'inizde tutun. Assembly'niz, içinde bulunacak hiçbir şey olmadığı için saldırganın aramaya gittiği yer olmaktan çıkar.
İşe mevcut derlemenize bir decompiler'da dürüstçe bakarak başlayın. On dakikada bulabildiğiniz her şeyi bir saldırgan beş dakikada bulur; o liste de geçiş planınızın ta kendisidir.
qPapel Security Team
Lisanslama ve yazılım koruması