Hey Mates,
as I said in my last post we’re coming to license key cracking. Note: I also said to make a challenge about nag removing but I think I cancel this because it’s just too easy (Think of a program with a message box at start; just remove the MessageBox() in code…). When we come to C++ this topic becomes interesting so you’ll see it then.
Description of the RE Challenge
Today we’ve got our first License algorithm (Yeah, everyone loves maths ). It’s not a very complex one, so I think it’s a good start for keyGens. KeyGens? Yep, this time you don’t have to find the password (Or a valid license in this case), but you should write a simple tool for finding valid ones.
Difficulty: 3.5/10
Requirements
-C# Reversing (Think I’ll remove this point in the next posts…)
-Maybe a bit better knowledge of programming with C#; if you worked with other languages you can of course also take python, etc (Would be interesting to see it in other languages too), but I think it can’t hurt to learn the language you are reversing
I thought of adding a new section for helping without divulge everything because the board got spoilers! At this point a BIG thanks for the admins here on 0x00sec! They added the feature at the same day I asked for it!
Hint 1: Just Copy the license algorithm out of the decompiled program
Hint 2: Brute-Force the license key
Hint 3: Start with “11111111” and loop through it with incrementing it everytime
This time you can build your own KeyGen which is a great step . As last thing I ask you whether you are interested in harder challenges. Currently I’m working on a BIG project (More than just password cracking: Different cracking protections, requires much more time and thinking, different exercises; Should be 7.5/10 Difficulty) and I don’t know if I should use my time to post more basic articles first or if you have the fun and time for a bigger challenge. So you can chose between more basic articles in the next days and one bigger challenge.
Recognize that the string is parsed as individual elements (element int_0 of the key of length 1) with the following method:
private static int smethod_1(string string_0, int int_0)
{
return int.Parse(string_0.Substring(int_0, 1));
}
should then be interpreted as follows:
(i) Key[0] = (Key as integer) % Key[0] == 0; note that Key[0] cannot be 0 else a divide by zero exception will occur
(ii) Key[1] = Key[2] + Key[5]
(iii) (Key[3] * Key[7]) % 2 == 0
(iv) Key[6] - Key[5] == (.NET_Framework_version as integer)[0] (for me, it was 4)
(v) Key[1] and Key[5] can be defined a custom value as required which will derive Key[2] and Key[6]
(vi) Key[3] and Key[7] can be defined a custom value accordingly such that part (i) is true, i.e. Key[7] should be even
(vii) Key[4] can be any value
Thanks for the challenge. Had a lot of fun writing a keygen for it.
keygen:
//
// keygenerator.cpp
//
#include <string>
#include <iostream>
#include <random>
using namespace std;
// Random number generation function
int random(int min, int max, std::mt19937 gen)
{
std::uniform_int_distribution<> num_random(min, max);
return num_random(gen);
}
void generate_key(int version, int number_array[])
{
// Setup the random number generator
std::random_device rd_source; // obtain a random number from hardware
std::mt19937 gen(rd_source()); // seed the generator
// Set the first digit to one, so that the whole number can always be divided by it
number_array[0] = 1;
number_array[3] = random(0, 4, gen) * 2; // Single digit multiple of 2
number_array[4] = random(0,9,gen); // completly random
number_array[5] = random(0,9 - version,gen); // stop 6 from being larger then 9
number_array[6] = number_array[5] + version; // digit 6 - digit 5 has to equal common runtime version
number_array[7] = random(0, 4, gen) * 2; // Single digit multiple of 2
number_array[2] = random(0, 9 - number_array[5], gen); // make sure digit 1 isn't larger then 9
number_array[1] = number_array[5] + number_array[2];
}
int main(int argc, char *argv[])
{
int runtime_version = 4;
int key_number = 10;
cout << "Generating " << key_number << " keys:" << endl;
for (int i = 1; i <= key_number; i++) {
int key_array[8];
generate_key(runtime_version, key_array);
for (int e = 0; e < 8; ++e)
cout << key_array[e];
cout << endl;
}
cout << "Key generation finshed!" << endl;
getchar();
}
Not the best solution, simple and works from what I can tell. Sick as a dog today, let me know if I screwed up somewhere. Took 1hr 20(ish)mins (with interruptions; at work)
[spoiler]using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var alphabet = “1234567890”;
var q = alphabet.Select(x => x.ToString());
int size = 8;
for (int i = 0; i < size-1; i++)
q = q.SelectMany(x => alphabet, (x, y) => x + y);
foreach (var item in q)
{
//Console.WriteLine(item);
{
int num = int.Parse(item);
bool flag = num % Program.smethod_1(item, 0) == 0 && (Program.smethod_1(item, 2) + Program.smethod_1(item, 5) == Program.smethod_1(item, 1) && (Program.smethod_1(item, 3) * Program.smethod_1(item, 7) % 2 == 0 && Program.smethod_1(item, 6) - Program.smethod_1(item, 5) == Program.smethod_1(Environment.Version.ToString(), 0)));
if (flag)
{
Console.WriteLine(num);
Console.Read();
}
}
}
Console.Read();
}
private static int smethod_1(string item, int int_0)
{
return int.Parse(item.Substring(int_0, 1));
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CrackMe5KeyGen
{
class Program
{
static List serials = new List();
int num = 0;
static string path = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase), “serials.txt”).Substring(6);
static void Main(string[] args)
{
Console.Title = “CrackMe 5 KeyGen by Geilokowski”;
Console.WriteLine(“CrackMe 5 KeyGen by Geilokowski”);
Console.WriteLine(“Generating serial keys…”); //Console.WriteLine(path);
for (int i = 10000000; i < 100000000; i++)
{
if (checkIfvalid(i.ToString(), i))
{
serials.Add(i.ToString());
}
}
Console.WriteLine("Finished generating. " + serials.Count + " serial keys found");
Console.Write("Do you want to write all the serials to a file or output one? [one/all]: ");
Console.WriteLine();
string input = Console.ReadLine().ToLower();
if (input.Equals("all"))
{
Console.WriteLine("Saving serials to File...");
using (StreamWriter file = new StreamWriter(path))
{
foreach (string line in serials)
{
file.WriteLine(line);
}
}
Console.Clear();
Console.WriteLine("All serials saved serials.txt. You can find the file in the same folder as the excecutable.");
}else if (input.Equals("one"))
{
Random r = new Random();
Console.Clear();
Console.WriteLine("Your serial key is: " + serials[r.Next(0, serials.Count)]);
}
Console.ReadLine();
}
public static bool checkIfvalid(string serial, int serialInt)
{
return
serialInt % getIntAtPos(serial, 0) == 0 &&
getIntAtPos(serial, 2) + getIntAtPos(serial, 5) == getIntAtPos(serial, 1) &&
getIntAtPos(serial, 3) * getIntAtPos(serial, 7) % 2 == 0 &&
getIntAtPos(serial, 6) - getIntAtPos(serial, 5) == getIntAtPos(Environment.Version.ToString(), 0);
}
private static int getIntAtPos(string number, int position)
{
return int.Parse(number.ToString().Substring(position, 1));
}
}
}
it generates all possible serial keys and can write them to a file or just output one. Its written in c# and you could probably improve the “algorithm” that is being used to try all the possible serial keys. If someone wants i could make a detailed tutorial on how it i got my solution and how your algorithm to check the serials works.