1

I'm trying to get into reverse engineering and am beginning with .NET, attempting various CrackMes and KeygenMes that I've found on the internet. Until now, I haven't really struggled but this latest one is driving me mad:

https://tuts4you.com/download.php?view.1894

VT link if needed: https://www.virustotal.com/en/file/9bd07d7cbd053f6ad27792487679b18b2f72b589440d8ab81f9cdc4d84301178/analysis/1454947890/

Decompiling with ILSpy reveals the license check performed:

    FileStream fileStream = new FileStream("key.dat", FileMode.Open, FileAccess.Read);
    StreamReader streamReader = new StreamReader(fileStream);
    string text = streamReader.ReadToEnd();
    byte[] bytes = Encoding.Unicode.GetBytes(this.TextBox1.Text);
    SHA512 sHA = new SHA512Managed();
    sHA.ComputeHash(bytes);
    if (Operators.ConditionalCompareObjectEqual(this.CodeCrypt(text), Convert.ToBase64String(sHA.Hash), true))
    {
        Interaction.MsgBox("Good job, make a keymaker", MsgBoxStyle.Information, "Done");
    }
    else
    {
        Interaction.MsgBox("Try again, it is very simple", MsgBoxStyle.Critical, "No ....");
    }
    streamReader.Close();
    fileStream.Close();

Here is the CodeCrypt method:

Key = "AoRE";
public string CodeCrypt(string text)
{
    string text2 = "";
    int arg_0F_0 = 1;
    int num = Strings.Len(text);
    checked
    {
        for (int i = arg_0F_0; i <= num; i++)
        {
            int num2 = i % Strings.Len(this.Key);
            if (num2 == 0)
            {
                num2 = Strings.Len(this.Key);
            }
            text2 += Conversions.ToString(Strings.Chr(Strings.Asc(Strings.Mid(this.Key, num2, 1)) ^ Strings.Asc(Strings.Mid(text, i, 1)) - 6));
        }
        return text2;
    }
}

Seemed quite straight forward to reverse, so I generated my own key generation method:

    private static string Key(string name)
    {
        string key = "";

        SHA512 sHA = new SHA512Managed();
        string hash = Convert.ToBase64String(sHA.ComputeHash(Encoding.Unicode.GetBytes(name)));


        for (int i = 1; i <= hash.Length; i++)
        {
            int num2 = i % 4;
            if (num2 == 0)
            {
                num2 = 4;
            }
            var test = Convert.ToChar(("AoRE"[num2 - 1] ^ hash[i - 1]) + 6);
            key += test;
        }
        return key;

    }

Then I wrote my license:

 using (StreamWriter sw = new StreamWriter(File.Open("key.dat", FileMode.Create), Encoding.Unicode))
        sw.Write(Key("Tom"));

But it fails. I set a breakpoint to see what the output from CodeCrypt() and the SHA512 hash was, and saw this:

CodeCrypt: 8dmVQYHqap7MbFngePjLSxvaC9kVgaDiyR2p550IFO2kzGAuC9yWufBs5LZGbKeR/KAFGVTBb47z4sa686eBTA== SHA512: 8dmVQYHqap7MbFngePjLSxvaC9/VgaDiyR2p550IFO2kzGAuC9yWufBs5LZGbKeR/KAFGVTBb47z4sa686eBTA==

The 27th character differs in these outputs and I just don't understand why. What am I missing here?

Thanks in advance.

3
  • may be you have some problems implementing the mid thingy powershell returns this for "Tom" [convert]::ToBase64String(([system.security.cryptography.SHA512Managed]::Create("SHA512")).ComputeHash([Text.encoding]::Unicode.GetBytes("Tom"))) 8dmVQYHqap7MbFngePjLSxvaC9/VgaDiyR2p550IFO2kzGAuC9yWufBs5LZGbKeR/KAFGVTBb47z4sa686eBTA==
    – blabb
    Feb 8, 2016 at 17:17
  • Thanks for your reply. In the answer below, someone managed to identify that this was an encoding issue with my usage of Convert.ToChar().
    – tom982
    Feb 9, 2016 at 21:42
  • yeah saw that answer by mike nice find indeed
    – blabb
    Feb 10, 2016 at 7:16

1 Answer 1

8

Your algorithm calculates the correct byte for each character of the base64 encoded hash, however your implementation of that byte's string encoding is not correct.

Convert.ToChar() simply casts the byte to a char.

VB's Strings.Chr() converts the byte to unicode, using the system's current default code page. This is most likely Windows-1252 for US/Western Europe.

For bytes 0x00-0x7F, UTF-8 and Windows-1252 have the same binary representation. But things are different for bytes 0x80-0xFF, and it just so happens that the / character is the only character XOR'd to a value greater than 0x7F (it's 0x83).

In Windows-1252, 0x80-0xFF represent single-byte extended characters. In UTF-8, these extended characters require two bytes of storage: 0x01 0x92.

This means that the crackme is buggy, because the license file depends on the system's character encoding (which could change). The license file should have used unicode consistently.

To fix your code, replace Convert.ToChar() with Encoding.Default.GetString()

var test = Encoding.Default.GetString(
               new[] { (byte)(("AoRE"[num2 - 1] ^ hash[i - 1]) + 6) });

Edited to add

One thing I want to point out is that a char in C# is 2 bytes and stores a 16-bit unicode character (unlike C where a char is 1 byte). This is why casting and using the default encoding are different operations.

1
  • Thank you so much! This worked perfectly. My first thought was that it was an encoding problem and I was careful to make everything unicode, but obviously missed this line. Thanks again for restoring my sanity.
    – tom982
    Feb 9, 2016 at 21:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.