[CrackMe] NoREpls - Part 1

Backstory

After reviewing some of the analysis of his software from the previous thread, edgyReggie thinks he knows where he went wrong. This time, he has made sure that no one will be able to find the new serial number after invalidating the previous one. Can you prove him wrong?

Difficulty

Author Assigned Level: Newbie

Community Assigned Level:

  • Newbie
  • Wannabe
  • Hacker
  • Wizard
  • Guru

0 voters


Goal

Your goal is to achieve the “full” version of the software, i.e. removing all nags that you see and unlocking all features of the program.
Your secondary (optional) goal is to make the registration persistent (survive program restart).


Rules of Engagement

None.


Research Material

Potential spoilers in here!

Binary

Preview (look and design may not be accurate):

MediaFire - http://www.mediafire.com/file/hpm5fg3nwimomov/NoREpls1.1.exe

VirusTotal - https://www.virustotal.com/en/file/9a4bca1af543ba981c6425031f2d64f2a7c8b495915c6c63a2846e20c58a7e09/analysis/1500466217/

Good luck!

11 Likes

Hello, thanks for another NoREpls.

Similar then yesterday sample, but today is the license key “obfuscated” with base64 encoding via “CryptBinaryToString” (dwFlags == CRYPT_STRING_BASE64).

$ strings NoREpls1.1.exe | grep Tk9 | base64 -d
NOREPLS-MEKI-30H2-AMN3-49JF
2 Likes

Yop, and permament registration:

Minimal patch (only two bytes) for changing two “je” instructions to “jne” instructions:

echo -ne "\x75" | dd of=NoREpls1.1_patch.exe bs=1 seek=2490 count=1 conv=notrunc
echo -ne "\x75" | dd of=NoREpls1.1_patch.exe bs=1 seek=1776 count=1 conv=notrunc

This removes the “demo version” string from about dialog and also makes available the save functionality. But register functionality and demo string in the windows title still remains, but this is IMHO good cost-benefit patch and full functionality will survive program restart.

2 Likes

Thanks for the challenge,
Here is my “write-up” for finding the serial key :

First i opened the binary in ollydbg, then "search for… all referenced string then find “Registration successful” :

Then click on it and tind the fonction that test for the serial before displaying “Registration successfull” :

After that I look at the function and find the “encrypted” serial : Tk9SRVBMUy1NRUtJLTMwSDItQU1OMy00OUpG

Now, I put a breakpoint on the function that appears to compare the good serial with the entered one :

run the program and enter a “AAAAAAAAAAAAAAA” serial the breakpoint is hit :

Now looking at the stack on Ollydbg : The “encrypted” serial is compared to our AAAAAAA that look encrypted in base64 :

So we have to find the serial equals to the “encrypted” serial in base64 :
echo “Tk9SRVBMUy1NRUtJLTMwSDItQU1OMy00OUpG” | base64 -d

The serial is NOREPLS-MEKI-30H2-AMN3-49JF !

3 Likes

Uhm well this is kinda awkward, I just had my laptop today and no Windows to try IDA or OllyDBG so I just ran this against the binary:

Judging from they key-length last time I awk’ed for something similar.
Took the first one that looked suspicious/encoded and took my chances.
Since it’s aimed at newbies I just tried the “most easy” encode method there is and well there it is.
No patching from me this time around :stuck_out_tongue:

2 Likes

[spoiler]Now I try to activate the save function without registering :
I “fill with NOPs” the JNE before the Strings “You must registered to use this function”

Then the save function is enabled permanently.

But : I can save files but when I click on “Save” it doesn’t really save the file , is this normal ?[/spoiler]

1 Like

You’ll need to select an existing file. It will be fixed in the next release! :wink:

Was fun again! I love how freestyle this is. Keep this going!


(I also removed the register option but I don’t think I need to show that again :stuck_out_tongue: )
Like @lacike already mentioned changing the jump (although I prefer NOPing). Also some small string modifications.
(Also you probably already realised that but the last character isn’t getting saved)

3 Likes

More great work @dtm :wink:


Owing to my hella noob status; here’s a brief write-up so that any suggestions for improving are welcome:

  • Loaded into IDA
  • Check for the DialogFunc
  • Found the call to a subroutine before the licence evaluation results
  • Found the encryped pass as an offset in the subroutine
  • Traced back up the stack to find the subroutine called before, thinking it’d be for encrypting the password
  • Found a reference to CryptBinaryToStringA and looked up the function on MSDN
  • Saw the dwFlags set to 0x00000001 and saw it means Base64
  • Base64 decoded the pass from earlier

It’s the wildly clicking subroutines based on where IDA places them that makes me feel this is more luck

2 Likes

Huh, I thought I fixed that… :confused:

1 Like

Here’s my approach featuring a persistently fully unlocked program; probably there are many better ways but damn it runs…

At first I wanted to crack the registering process, so that I would have complete access for further reversing approaches. It’s as easy as in the last part: Just NOP the jmp in front of our good boy (“Registration Successful”). Search for the string to find it.
That’s not required for the final cracked version, but it heavily helps with reversing.

The next thought of mine was that there has to be a part of the code which activates all functions and removes the annoying “demo” stuff; so I began to dig around and found this by clicking my way through the program:

It seems to be responsible for making the program look registered; but sadly it’s not enough for activating the program…

Anyway, that’s already enough for us to want it getting called at the program’s startup - this would make the program look registered persistently. Search for the place where the main MessageBox gets created; it should be similiar to this one:

You see the highlighted line? This TEST and JE instructions are not important for the program; probably a good place for injecting our own code! A JMP to the previously found register-stuff would be cool; but sadly there’s not enough space for a long JMP (It takes more bytes than a short JMP), so we have to find a near code cave where we can place it.
Let’s have a look at the code below:

You see the first two instructions? They’re probably used to jmp to the code at the bottom when an error occurs. Okay, but what? If we assume there are no errors (Because we’re cool; our program doesn’t throw errors! :stuck_out_tongue_winking_eye:) we could use this free space for our own malicious purposes! There are enough bytes for a long JMP, so our schema looks like this:

MessageBox setup -> Our injected short (!) JMP to the error handling (used as code cave) -> Long JMP to the registering stuff -> Registering stuff registers stuff (Pure black magic...) -> Finally JMP which takes us back right behind our first injection -> MessageBox continues

Cool, isn’t it? Yeah, maybe a bit complex, probably there’re better ways, but it’s always good to understand the difference between long and short JMPs and how to circumvent issues by using code caves :wink:.

We found a place to inject into, we got a code cave, but where can we jump back from the registering process? The instructions right before the RETN are a good choice:

Okay, now just patch the instructions to the JMPs I talked about and see how automagically our program gets “registered” everytime we run it :wink:.

But we’re not finished yet, now comes the easy part. If you read the other’s solutions you can stop here; I also NOPed the two JMP instructions.

I had to search for the check in front of the nag telling us that we’re not allowed to save the file when we try to. Just open this nag, pause your debugger (I use Immunity) and press Alt+K to show the recent calls; you’ll see the one responsible for the MessageBox. From there click your way up through the returns - just be patient, you’ll find it :wink:. When you got it it should look like this:

Yep, one byte gets compared to zero; if that’s true we jump right into our bad boy.

So we can tell that this byte is probably the one responsible for checking whether we’re registered - or not. Maybe it’s used at other places in the code too? Set a hardware breakpoint on access and try it; you’ll see our assumption seems correct!
Of course I thought patching this byte would be the easiest way, so I edited my code cave to INC it on startup, making it not zero and our program correctly registered (Nah, correctly without paying money :sunglasses:). But due to errors when exporting the changes to a new exe I gave up after half an hour of testing and just NOPed the two JMPs in front of this bad boy and the About dialog; then everything is working too…

5 Likes

I am the laziest hacker ever

spoiler

Open up in IDA, check the verify serial number, see it runs on a jz, find the opcode bytes in hxd, hex-edit a 75 to 74 to make it JNZ instead of JZ, input any serial ever

5 Likes

Laziest hackers make the best hackers :thumbsup:

Thanks again for the time that you spend creating such challenges for the community mate :wink:

I found a weird string referenced in the licence checking function

Then I betted on data obfuscation and tried base64 decoding

=> Challenge done

3 Likes

Thanks for the crackme. I got the key using x64dbg and Powershell (or anything can decode base64)

[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String("Tk9SRVBMUy1NRUtJLTMwSDItQU1OMy00OUpG"))
NOREPLS-MEKI-30H2-AMN3-49JF
2 Likes

Anyone who solved this has understood what this function does?

[spoiler]

I think it just encodes the the hardcoded key in base64 and maybe compares it to the key given by the user…?
[/spoiler]

1 Like

Have you tried stepping through it under a debugger and seeing what happens?

1 Like

Yes, of course, but it’s a pain to manually keep track of the variables on the stack with IDA… I’ll search for a better way and try that again :grin:

Instead of stepping in the function, perhaps see what the end result is by stepping over it, then trace its return value (if it has one) to see if it’s used with anything else after.

1 Like

2 Likes