timeleg's CrackMe5 Tutorial

July 16, 2015 - Reading time: 4 minutes

Target: timeleg's CrackMe5
URLhttp://www.crackmes.de/users/timeleg/crackme6/
Protection: Name / Serial
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 the check button is clicked.

    private void button1_Click(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))
        {
          this.casovac.Stop();
          this.label2.Visible = false;
          this.label3.Visible = false;
          this.label4.Visible = false;
          this.textBox1.Visible = false;
          this.textBox2.Visible = false;
          this.button1.Visible = false;
          Label label = this.label5;
          string str = label.Text + this.reg.ZistiMeno();
          label.Text = str;
          this.label5.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();
      }
    }

Here we see that the method reg.Testuj takes our entered name and serial as arguments, lets take a look at that method.

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

A string.Compare call is made to compare the entered serial and the return value of the KodujHeslo method. So lets check that method out.

    private string KodujHeslo(string meno)
    {
      string retazec = "";
      foreach (byte num in new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(meno)))
        retazec = retazec + num.ToString("x2").ToLower();
      return this.OdstranPismeno(retazec);
    }

All this method does is generating an MD5-hash from the entered name and converts it to a string, this string is then passed to the method OdstranPismeno and the return value from that method is the return value for KodujHeslo.
What does the OdstranPismeno method do then?

    private string OdstranPismeno(string retazec)
    {
      List<char> list = new List<char>((IEnumerable<char>) retazec);
      for (int index = 0; index < list.Count; ++index)
      {
        if ((int) list[index] >= 65 && (int) list[index] <= 122)
        {
          list.RemoveAt(index);
          --index;
        }
      }
      retazec = "";
      foreach (char ch in list)
        retazec = retazec + (object) ch;
      return retazec;
    }

This method strips the input string of [a-zA-Z[/]^_`] and since the MD5-hash supplied is a string representation of hex values all we end up with is numerical characters.

So the serial routine is generating a string containing the MD5-hash for the entered name, then stripping it to only numerical characters and then does the check.

Following is the C# code for a keygen.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

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

        private static string GenerateSerial(string name)
        {
            var hash = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(name)).Aggregate("", (current, num) => current + num.ToString("x2").ToLower());
            return StripStringOfAtoZ(hash);
        }

        private static string StripStringOfAtoZ(string input)
        {
            var list = new List<char>(input);
            for (var index = 0; index < list.Count; ++index)
            {
                if (list[index] < 65 || list[index] > 122) continue;
                list.RemoveAt(index);
                --index;
            }
            return list.Aggregate("", (current, ch) => current + ch);
        }
    }
}