Target: LFalch’s 1st crackme
URL: http://www.crackmes.de/users/lfalch/lfalchs_1st_crackme/
Protection: Name / Serial
Description: Crackme with a serial protection.
Tools: Java Decompiler.
Decompile the JAR with the decompiler of your choice and take a look at the output. We end up with four obfuscated classes, Crackme.class, a.class, b.class and c.class.
Lets take a look at the Crackme.class to find out where we should look for the serial routine.
package com.lfalch.crackme1;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.io.PrintStream;
import javax.swing.JFrame;
import javax.swing.UIManager;
public class Crackme
extends JFrame
{
public static void main(String[] paramArrayOfString)
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception localException)
{
System.out.println("Couldn't get that look and feel");
}
(paramArrayOfString = new Crackme()).setTitle("LFalch's 1st crackme");
int i = 230;
i = 250;
Object localObject = paramArrayOfString;
String str1 = "password123";
String str2;
// a call to b in the c.class taking the parameters LFalch and password123
c.b(str2 = "LFalch", str1);
((JFrame)localObject).setPreferredSize(new Dimension(250, 230));
paramArrayOfString.setResizable(false);
paramArrayOfString.setDefaultCloseOperation(3);
// creates an instance of the a.class and adds that instance to the ContentPane
localObject = new a();
paramArrayOfString.getContentPane().add((Component)localObject);
paramArrayOfString.pack();
paramArrayOfString.setVisible(true);
}
}
There are references to two of the other classes from the main method, the c.class and the a.class, lets see what the contents of the a.class are.
package com.lfalch.crackme1;
import java.awt.Component;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public final class a
extends JPanel
{
private JTextField a;
private JTextField b;
public a()
{
setLayout(null);
Object localObject;
(localObject = new JLabel("LFalch 1st CRACKME")).setBounds(10, 11, 228, 27);
((JLabel)localObject).setFont(new Font("Tahoma", 1, 22));
add((Component)localObject);
(localObject = new JLabel("Name:")).setBounds(10, 79, 46, 14);
add((Component)localObject);
(localObject = new JLabel("Serial:")).setBounds(10, 117, 46, 14);
add((Component)localObject);
this.a = new JTextField();
this.a.setBounds(45, 76, 180, 20);
add(this.a);
this.a.setColumns(10);
this.b = new JTextField();
this.b.setColumns(10);
this.b.setBounds(45, 114, 180, 20);
add(this.b);
(localObject = new JButton("Verify")).setFont(new Font("Tahoma", 1, 16));
// sets the class b as the ActionListener for the button.
((JButton)localObject).addActionListener(new b(this));
((JButton)localObject).setBounds(10, 145, 215, 50);
add((Component)localObject);
}
}
This class contains the GUI setup, and as we can see the class b handles the actions for the button. Lets see what happens on a button click.
package com.lfalch.crackme1;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
final class b
implements ActionListener
{
b(a parama) {}
public final void actionPerformed(ActionEvent paramActionEvent)
{
// a call to the method a in the class c using the name textbox and serial textbox as parameters.
if (c.a(a.a(this.a).getText(), a.b(this.a).getText()))
{
JOptionPane.showMessageDialog(null, "You have entered a valid Serial!", "CRACKED", 1);
return;
}
JOptionPane.showMessageDialog(null, "The information that you've entered is invalid", "Invalid", 2);
}
}
Ok, the serial check is being made in the a method of the c class. Lets take a look at c.class.
package com.lfalch.crackme1;
import java.util.Random;
public final class c
{
private static char[] a = { '0', '1', '2', '3', '4', '5', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
private static String b; // "LFalch"
private static String c; // "password123"
public static boolean a(String paramString1, String paramString2) // paramString1 == entered name, paramString2 == entered serial
{
// make sure that the entered serial is exactly 16 chars long.
if ((paramString2 = paramString2.toLowerCase()).length() != 16) {
return false;
}
// new Random instance using the value of name.hashCode() | "LFalch".hashCode() as the seed.
paramString1 = new Random(paramString1.hashCode() | b.hashCode());
String str = "";
// generate a 16 char valid serial using the random-class.
for (int i = 0; i < 16; i++) {
str = str + a[paramString1.nextInt(a.length)];
}
// unused code, no need to look at this.
byte[] arrayOfByte = c.getBytes();
for (paramString1 = 0; paramString1 < arrayOfByte.length; paramString1++)
{
String tmp97_96 = paramString1;
byte[] tmp97_95 = arrayOfByte;
tmp97_95[tmp97_96] = ((byte)(tmp97_95[tmp97_96] ^ 0x10));
}
c = new String(arrayOfByte);
// check if the generated serial is equal to the entered serial and return the result.
return str.equals(paramString2);
}
// this is the method that is called from the main method in the Crackme.class.
public static void b(String paramString1, String paramString2)
{
b = paramString1;
c = paramString2;
}
}
Now we know how to code a keygen for this crackme, but since I want to write the keygen in C# I have to implement the Java implementation of String.hashCode and the Random class.
Thanks to the Java API Specification we can do this fairly easy.
The hashCode implementation as an extension method:
using System;
using System.Linq;
namespace LFalchs_1st_crackme_Keygen
{
public static class JavaExtensions
{
public static int JavaStringHashCode(this string input)
{
return input.Aggregate(0, (hashCode, chr) => hashCode * 31 + Convert.ToInt32(chr));
}
}
}
The implementation of the Random class (only the methods needed for this keygen):
using System;
namespace LFalchs_1st_crackme_Keygen
{
public interface IJavaRandom
{
void SetSeed(long seed);
int NextInt();
int NextInt(int n);
}
public class JavaRandom : IJavaRandom
{
private long _seed;
public JavaRandom() : this(Environment.TickCount)
{
}
public JavaRandom(long seed)
{
SetSeed(seed);
}
protected int Next(int bits)
{
_seed = (_seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int)(_seed >> (48 - bits));
}
public void SetSeed(long seed)
{
_seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
}
public int NextInt()
{
return Next(32);
}
public int NextInt(int n)
{
if (n <= 0)
throw new ArgumentException("n must be positive");
if ((n & -n) == n)
return (int)((n * (long)Next(31)) >> 31);
int bits, val;
do
{
bits = Next(31);
val = bits % n;
} while (bits - val + (n - 1) < 0);
return val;
}
}
}
And finally the keygen source:
using System;
using System.Windows.Forms;
namespace LFalchs_1st_crackme_Keygen
{
internal class Keygen
{
[STAThread]
private static void Main(string[] args)
{
Console.Write("Name: ");
var name = Console.ReadLine();
if (string.IsNullOrEmpty(name)) return;
char[] alphanumeric = { '0', '1', '2', '3', '4', '5', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
var random = new JavaRandom(name.JavaStringHashCode() | "LFalch".JavaStringHashCode());
var str = "";
for (var i = 0; i < 16; i++)
{
str = str + alphanumeric[random.NextInt(alphanumeric.Length)];
}
Clipboard.SetText(str);
Console.Write("Serial: ");
Console.WriteLine(str);
Console.WriteLine("Serial copied to the clipboard.");
Console.ReadKey();
}
}
}