Il codice che segue è da considerarsi in alpha e da non utilizzare in un ambiente di produzione , qui potete trovare il "progetto" https://sourceforge.net/projects/sourcecodesafer/
L'ipotetico scenario è quello in cui vogliamo lasciar testare la nostra applicazione .Net Windows Forms ma vogliamo evitare che sia possibile decompilarla in fase di test ,
per ottenere questo risultato crittografiamo sul nostro pc l'eseguibile da proteggere con una password (chiamiamola P , si tratta di una password simmetrica(https://it.wikipedia.org/wiki/Crittografia_simmetrica) ) ,quando ci spostiamo sulla macchina di test decrittografiamo l'eseguibile con la stessa password e lo carichiamo ed eseguiamo grazie alla reflection ,
sulla macchina di test portiamo quindi l'eseguibile crittografato , se ci venisse carpita la password P sulla macchina di test si potrebbe decrittografare l'eseguibile e risalire al codice sorgente ,per evitare che ciò accada dobbiamo evitare che possibili keylogger intercettino lo stream della tastiera o/e che uno screen recorder registri la nostra attività sulla tastiera virtuale dell'applicazione , per risolvere questa situazione potremmo utilizzare una seconda applicazione che in remoto fornisca la password P tramite un servizio WCF ( questa è ovviamente solo una delle soluzioni possibili ). Chiamando questo servizio forniamo una password asimmetrica pubblica ( https://it.wikipedia.org/wiki/Crittografia_asimmetrica ) , quest'ultima password viene utilizzata per crittografare la password P , l'array della password così crittografata viene restituito al chiamante il quale ha la chiave privata e può risalire alla password P in chiaro .
Questo "progetto" è costituito da 3 sotto-progetti :
1) un eseguibile Ethical_Hacking.exe che è l'applicazione che vogliamo non sia decompilata in fase di test
2) un applicazione WindowsFormsProtector che si occupa di :
a) crittografare Ethical_Hacking.exe
b) decrittografare Ethical_Hacking.exe
c) creare una coppia di chiavi asimmetriche e chiamare il servizio WCF di PasswordSupplier passando la chiave pubblica ed ottenendo da PasswordSupplier la password P crittografata e decrittografarla con la chiave privata quando è ritornata dal metodo GetPassword della classe Service di PasswordSupplier
d) caricare in memoria ed eseguire Ethical_Hacking.exe partendo da un array di bytes ( ovvero Ethical_Hacking.exe decrittografato )
3) l'applicazione PasswordSupplier che si occupa di :
a) scegliere l'indirizzo a cui sarà reso disponibile il servizio WCF
b) consentire di inserire la password ,dovrà essere la stessa inserita al punto 2)a)
c) rendere disponibile (Start) il servizio all'indirizzo scelto in 3)a)
d) fermare (Stop) il servizio avviato in 3)c)
e) fornire l'implementazione di GetPassword prevista dall'interfaccia IService
Segue il dettaglio dei singoli punti :
1) Ethical_Hacking.exe è un eseguibile Windows Forms si tratta di un form che ne chiama un altro,la sua funzione è solo quella di essere un eseguibile .net chiamabile tramite reflection
2) vediamo il codice relativo al punto 2)a)
{
try {
byte[] encrypted = null;
string file = this.textBox1.Text;
string pwd = this.textBox2.Text;
if (!file.Equals(""))
{
if (pwd.Length > 7)
{
FileStream fs = new FileStream(this.textBox1.Text, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
string EncryptionKey = pwd;
byte[] clearBytes = bin;
Aes encryptor = Aes.Create();
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[]
{ 0x20,0x6e, 0x20, 0x61, 0x6e, 0x20, 0x4d, 0x65,0x20, 0x61, 0x64, 0x76,0x65});
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(),
CryptoStreamMode.Write);
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
encrypted = ms.ToArray();
FileStream fs1 = new FileStream(this.folderName + "\\encryptedExe.encrypt", FileMode.Create);
BinaryWriter br1 = new BinaryWriter(fs1);
br1.Write(encrypted);
fs1.Close();
br1.Close();
} else
{
MessageBox.Show("inserire una password di almeno 8 caratteri");
}
}
else {
MessageBox.Show("inserire il nome di un file");
}
} catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
-segue il codice relativo al punto 2)b)
string dir = Directory.GetCurrentDirectory();
FileStream fs = new FileStream(this.EncryptedFile, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
byte[] exe = null;
fs.Close();
br.Close();
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[]
{0x20, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x4d, 0x65,0x20, 0x61, 0x64, 0x76,0x65 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(),
CryptoStreamMode.Write))
{
cs.Write(bin, 0, bin.Length);
cs.Close();
}
exe = ms.ToArray();
}
}
l'array exe consiste dell'eseguibile decrittografato
-passiamo al punto 2)c)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string publicKey = rsa.ToXmlString(false); // false to get the public key
string address = this.textBox5.Text;
ChannelFactory<IService> cf = new ChannelFactory<IService>(new WebHttpBinding(), address);
//WebHttpBehavior abilita il modello di programmazione Web per un servizio Windows Communication Foundation (WCF).
cf.Endpoint.Behaviors.Add(new WebHttpBehavior());
//crea il canale di comunicazione
IService channel = cf.CreateChannel();
//viene invocato il metodo GetPassword sul servizio il cui indirizzo è address
byte[] encryptedData = channel.GetPassword(publicKey);
//viene valorizzato decryptedData con l'array di bytes ritornati dal metodo RSADecrypt
byte[] decryptedData = RSADecrypt(encryptedData, rsa.ExportParameters(true), true);
UnicodeEncoding convertToString = new UnicodeEncoding();
string str = "";
str = convertToString.GetString(decryptedData);
this.textBox2.Text = str;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
segue il metodo RSADecrypt che utilizza la chiave privata:
{
try
{
byte[] decryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
return decryptedData;
}
//Catch and display a CryptographicException
//using a message box
catch (CryptographicException e)
{
MessageBox.Show(e.Message.ToString());
return null;
}
}
-punto 2)d)
{
try
{
string EncryptionKey = this.textBox2.Text;
string dir = Directory.GetCurrentDirectory();
FileStream fs = new FileStream(this.EncryptedFile, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
byte[] exe = null;
fs.Close();
br.Close();
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[]
{0x20, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x4d, 0x65,0x20, 0x61, 0x64, 0x76,0x65 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(),
CryptoStreamMode.Write))
{
cs.Write(bin, 0, bin.Length);
cs.Close();
}
exe = ms.ToArray();
}
}
Assembly assembly1 = Assembly.Load(exe);
var programType1 = assembly1.GetTypes().FirstOrDefault(c => c.Name == "Program");
MethodInfo method1=programType1.GetMethod("Start", BindingFlags.Public | BindingFlags.Static);
method1.Invoke(null, new object[] { });
}
catch(Exception ex)
{
string msg= " controllare anche che la password per la decriptazione sia corretta";
MessageBox.Show(ex.Message.ToString() + " " + msg);
}
}