Have You heard / read about CONfidence? It’s two days IT security conference, organized annualy in Cracow, Poland. (Here you can read more…). It used to be a tradition, that ESET company prepares a CrackMe, for atendees of the conference. This year the rules were different: they published the CrackMe before, and decided to reward first 5 solvers by CONfidence tickets ^^. And it happend – I was the one of them … So, now I have a pleasure to describe for You what the CrackMe was about and how did i aproached it.
Here You can find original CrackMe: CONfidence CrackMe2012 (password to rar: ESET)
If you are not interested to unpack it, but just to play with algo, here i prepared unpacked version: Unpacked CrackMe
The CrackMe was writen in assembler and packed with MPRESS. It uses 2 custom hashing procedures – one of them was generated on the base of username.
Unpacking it was very easy – i did it just by stepping in OllyDbg, and then dumping the memory. No external tools for imports rebuilding were required.
- PEid (optional)
- ImmunityDbg / OllyDbg - with OllyDump plugin (for unpacking)
- Tiny self-made tools writen in C++ (i will describe them further)
What CrackMe does
- gets the username (length of username: from 4 to 31)
- gets the password (must be exactly 24 characters long, containing characters [A-Za-o])
- generates 3 MD5 hashes in following manner:
#1 -> md5(username)
#2 -> md5(#1)
#3 -> md5(#2)
hash#1 = D88EB947A504FCF6C3D9DCA5F84DE42A
hash#2 = 12EB2430F671103B94D11F34D375CC0D
hash#3 = B23D343E81CEE206B9D180B7B0189010
Hashes are used to generate an MMX code, used in password verification.
The most (the only?) challenging part was to crack this MMX hash. It’s easy to spot this function, because it is called just before the decision, whether the password is correct or not. It’s result is compared with hash#1, and when it is equal, then our password is accepted. The address of the function is hidden in EAX…
The function is placed at memory address 0×403090 (till 0×404355). 1536 MMX instructions… Looks long and messy? Think so…
First impresion was to brutforce it… But never mind, first impressions are often just delusions Obviously, brutforcing this would take ages! So there must be some other way…
If You look closer, You see, that it’s not such a mess as it seems to be.
I always like to start by sorting out what’s searched and what’s given
Searched and given:
MM0 and MM1 are filled with some “mysterious” hash – by simple experiments you can see that it is related with given password, but now we will not go into details of it…
In the registers [MM2, MM3] hash#2 is placed and in [MM4, MM5] hash#3. Note, that these registers are not changing till the end of the procedure. (When i noticed it, i got sure that it is reversable )
The registers MM7, MM8 are used as helpers in calculation…
The correct output should be the hash#1 placed in MM0, MM1…
In short words, we must input into MM0, MM1 something, which after all this operations will let us have hash#1 in MM0, MM1…
No other option – this long function must be reversed.
Let’s take a closer look.
There are 256 blocks of 6 instructions, which follows similar logic. See the samples below:
It gives us plenty of information! For every operation, we can easly calculate the value, with which MM0/MM1 is modified. And we KNOW the last value of MM0, MM1! It’s hash#1.
If take a closer look, you notice, that the only things we must do is:
- reverse the last instruction in every block (change from PADDB to PSUBB and so on…)
- set the blocks in opposit order (the last block must be the first one)
- then – if you give hash#1 as an input (in MM0, MM1) – you will get as an output the searched value (the encoded password )
How to do it? There are many ways, and i am gona demontrate some more interesting examples later on. But, actually, the task specified by ESET was just to find the proper key for one’s name and surname (not to write a keygen). So i did it in very fast and dirty way – on the original exe. I coppied the piece of memory containing all these instruction, then reversed it by my small parsing tool (written in C++), and copied again at the place.
When i inserted hash#1 as the initial value of MM0 and MM1, it gave me at the end the searched – means – encoded password!
MM0 = AFEC FFD1 F7D1 9AE0
MM1 = 8597 249E 0642 1F2D
Now it’s not a big deal to decode it.
Password encoding goes along with password verification. It is very simple. Every character of the password is processed in a following way (let’s denote the processing function by f1: )But not only they are processed, they are also added into a polynominal… Password is divided into chunks of 3 characters :
3 chars of password ->
( f1(pass[n]) * 0×29 +f1( pass[n+1] ) ) * 0×29 + f1(pass[n+2]) –>
4 bytes of the “encoded password”
To ilustrate, how it can be reversed, I placed here a source of my Simple Chunk Decoder.
MM0 = AFEC FFD1 F7D1 9AE0
MM1 = 8597 249E 0642 1F2D
9AE0 F7D1 FFD1 AFEC 1F2D 0642 249E 8597
Now just copy the password into the field… And it’s done!
- Due to the fact, that some people are interested, I am prepairing a new post on keygenning this crackme – including analysis of the MMX instructions generating code.
- If You want to see alternative solution, take a look on Vnd’s homepage