Target: timeleg’s CrackMe1

URL: http://www.crackmes.de/users/timeleg/crackme1/

Protection: Serial number

Description: Crackme with a serial protection.

Tools: .NET Decompiler.

First decompile the crackme with your .NET decompiler of choice. Then lets have a look at what happens when we press the ‘Check’ button.

private void button1_Click(object sender, EventArgs e)
    {
      if (this.reg.Testuj(this.textBox1.Text))
      {
        this.label2.Visible = false;
        this.button1.Visible = false;
        this.textBox1.Visible = false;
        this.label3.Visible = true;
      }
      if ((int) this.reg.VratPocetPokusov() == 0)
        this.Close();
      else
        this.reg.Odober();
    }

Here we see that method calls from the ‘reg’ class are being made, lets take a look at that class.

using System;

namespace CrackMe1
{
  internal class Reg
  {
    private byte pocetPokusov;

    public bool Testuj(string heslo)
    {
      if (heslo.Length != 4)
        heslo = "9231456";
      for (int index = heslo.Length - 1; index > -1; --index)
      {
        if (this.Xor(this.Prepocet(Convert.ToInt32(heslo[index]))) != 0)
          return false;
      }
      return true;
    }

    public byte VratPocetPokusov()
    {
      return this.pocetPokusov;
    }

    public void Odober()
    {
      --this.pocetPokusov;
    }

    public void NastavPocetPokusov()
    {
      this.pocetPokusov = (byte) 2;
    }

    private int Prepocet(int znak)
    {
      znak *= 80;
      znak -= 3915;
      znak ^= 325;
      znak *= znak + 4091;
      return znak;
    }

    private int Xor(int znak)
    {
      return znak ^= 1411520;
    }
  }  
}

It seems to be the Testuj method that is our serial check. The other two methods called from button1_Click checks if we have pressed the button three times and closes the program if we have.

So lets take a look at what’s going on in the check routine.

    public bool Testuj(string heslo)
    {
  // if the lenght of the entered serial isn't 4, set the entered serial to "9231456"
      if (heslo.Length != 4)
        heslo = "9231456";
  // loop through each char in the entered serial
      for (int index = heslo.Length - 1; index > -1; --index)
      {
    // do some calculations to see if the result is 0
        if (this.Xor(this.Prepocet(Convert.ToInt32(heslo[index]))) != 0)
          return false; // if not 0 the check fails
      }
      return true; // if all chars passed the calculations, return true
    }

So now we know that each char is converted to an integer and then some calculations are made on that value. Lets look at the Prepocet and Xor methods to see what kind of calculations are being made.

    private int Prepocet(int znak)  // znak = int value of the current char
    {
      znak *= 80;
      znak -= 3915;
      znak ^= 325;
      znak *= znak + 4091;
      return znak;
    }

    private int Xor(int znak)
    {
      return znak ^= 1411520;
    }

Ok, now we know that after the calculations in the Prepocet routine the result has to be 1411520 to pass the check. All we have to do now is to calculate what value is valid for those calculations.
Since we have to solve a quadratic equation for the fourth line in the Prepocet method, lets write a small program that generates a valid key for us instead.

namespace timelegs_CrackMe1
{
    class Keygen
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Key: " + GenKey());
            Console.ReadKey();
        }

        private static string GenKey()
        {
            const int magicNumber = 1411520;
            const int discriminant = (4091 * 4091) - (4 * -magicNumber);
            var x1 = (int) (-4091 + Math.Sqrt(discriminant)) / 2;
            var x2 = (int) (-4091 - Math.Sqrt(discriminant)) / 2;
            x1 ^= 325;
            x2 ^= 325;
            x1 += 3915;
            x2 += 3915;
            x1 /= 80;
            x2 /= 80;
            if (x1 > 32 && x1 < 126)
                return KeyFromInt(x1);
            if (x2 > 32 && x2 > 126)
                return KeyFromInt(x2);
            return "No key generated";
        }

        private static string KeyFromInt(int number)
        {
            var key = new char[4];
            for (var i = 0; i < 4; i++)
            {
                key[i] = Convert.ToChar(number);
            }
            return new string(key);
        }
    }
}

After running this we get the serial 1111.