Target: timeleg’s CrackMe3
URL: http://www.crackmes.de/users/timeleg/crackme3/
Protection: Serial number
Description: Crackme with a serial protection.
Tools: .NET Decompiler.

Load the crackme in your .NET decompiler and lets take a look at what happens when we press the ‘Check’ button.

    private void button1_Click_1(object sender, EventArgs e)
    {
      string text1 = this.textBox1.Text;
      string text2 = this.textBox2.Text;
      if (string.IsNullOrEmpty(text1))
      {
        int num1 = (int) MessageBox.Show("      Please enter a name");
      }
      else
      {
        if (this.reg.Testuj(text1, text2))  //text1 == name, text2 == serial
        {
          this.button1.Visible = false;
          this.label2.Visible = false;
          this.label3.Visible = false;
          this.textBox1.Visible = false;
          this.textBox2.Visible = false;
          Label label = this.label4;
          string str = label.Text + this.reg.ZistiMeno();
          label.Text = str;
          this.label4.Visible = true;
        }
        else
        {
          int num2 = (int) MessageBox.Show("     Wrong password !", "Unregistered", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
        if ((int) this.reg.VratPocetPokusov() == 0)
          this.Close();
        else
          this.reg.Odober();
      }
    }

If the reg.Testuj method returns true we succeed, else it’s an invalid serial. Lets check out the reg.Testuj method.

    public bool Testuj(string meno, string heslo)
    {
      this.meno = meno;
      return string.Compare(heslo, this.KodujHeslo(meno), false) == 0;
    }

Here we see that a string comparison is being made on the entered serial and the output of the KodujHeslo method.

    public string KodujHeslo(string meno)
    {
      byte[] bytes = Encoding.Unicode.GetBytes(meno);
      PasswordDeriveBytes passwordDeriveBytes = new PasswordDeriveBytes(meno, new byte[8]
      {
        (byte) 68,
        (byte) 105,
        (byte) 112,
        (byte) 108,
        (byte) 111,
        (byte) 109,
        (byte) 107,
        (byte) 65
      });
      return Convert.ToBase64String(this.HashFunkcia(bytes, passwordDeriveBytes.GetBytes(32), passwordDeriveBytes.GetBytes(16)));
    }

    private byte[] HashFunkcia(byte[] hesloPomocna, byte[] Key, byte[] IV)
    {
      MemoryStream memoryStream = new MemoryStream();
      Rijndael rijndael = Rijndael.Create();
      rijndael.Key = Key;
      rijndael.IV = IV;
      CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
      cryptoStream.Write(hesloPomocna, 0, hesloPomocna.Length);
      cryptoStream.Close();
      return memoryStream.ToArray();
    }

This routine encrypts the name and returns the encrypted name as a Base64String which then is compared to our entered serial.
When writing our keygen we can simply copy those methods and send the entered name as input to the KodujHeslo method to get a valid serial.

Following is the C# code for a keygen.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace timelegs_CrackMe3
{
    class Keygen
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.Write("Name: ");
            var name = Console.ReadLine();
            if (string.IsNullOrEmpty(name))
                return;
            var serial = GenKey(name);
            Console.WriteLine("Serial: " + serial);
            Clipboard.SetText(serial);
            Console.WriteLine("Serial copied to the clipboard.");
            Console.ReadKey();
        }

        private static string GenKey(string name)
        {
            var bytes = Encoding.Unicode.GetBytes(name);
            var passwordDeriveBytes = new PasswordDeriveBytes(name, new byte[] { 68, 105, 112, 108, 111, 109, 107, 65 });
            return Convert.ToBase64String(Hash(bytes, passwordDeriveBytes.GetBytes(32), passwordDeriveBytes.GetBytes(16)));
        }

        private static byte[] Hash(byte[] name, byte[] key, byte[] iv)
        {
            var memoryStream = new MemoryStream();
            var rijndael = Rijndael.Create();
            rijndael.Key = key;
            rijndael.IV = iv;
            var cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
            cryptoStream.Write(name, 0, name.Length);
            cryptoStream.Close();
            return memoryStream.ToArray();
        }
    }
}