Hello to all Habrovchanam! My name is Darwin and today I will tell you about how I solved the quacks from ZeroNights 2013 and Kaspersky Lab for the r0 Crew forum and its semi-private Kiev meeting.
Some information about quacks:
- File: ZeroNightsCrackME.exe
- Platform: Windows 7 (64 bit)
- Packer: None
- Anitotladka: Do not stumble
- Solution: Valid Mail / Serial Pair
Instruments:
- OllyDbg SnD 2.2
- IDA Pro 6.1
- Some gray matter
Let's get down to solving ...
Analysis
Hacking an application always begins with a launch and a superficial investigation of its behavior. Let's run our cracks and see how it responds to data entry:
')
Fig. oneAs might be expected, we are informed about the incorrectly entered serial code.
Now let's open it in IDA Pro, search through the rows and try to find a place in which this tablet is displayed.
Go to the tab "Strings windows" (if you do not have it, then open it) and look for the line "Fail, Serial is invalid !!!":
Fig. 2Under the line of interest to us we see another equally interesting. Click on any of the lines and go to the referring code. As a result, we get here:
Fig. 3From which it becomes clear - the check takes place in the
sub_4012D0 function. If you scroll up a bit (literally on the floor of the screen), you will see another interesting point:
Fig. fourHere we see two calls to the
GetWindowTextA function. Considering the previous figure, it becomes obvious that both functions are used to retrieve data from the fields of the
Email / Serial form.
Based on the data obtained, there are two scenarios for further development:
- Start from the beginning. Put breakpoints on the GetWindowTextA functions and continue to monitor data changes. The method is long and you can get lost.
- Start from the end. Find the code that influences the result (in our case, show the success / failure window) and spin it in the opposite direction. The method is fast, hardcore and interesting.
When I decided to quack, I chose the second way and not only because it is faster (IMHO), but also because I wanted to cheat and spy on a valid pair. In some cracks, this works, but where it works it is usually asked to restore the algorithm itself, and not just to provide one valid pair. Looking ahead to say that with a valid pair under review (as expected), I turned around =)
But let's take it in order ...
Looking for key details
Since we decided to go from the end, the first thing to do is to determine what influences the result of the success or failure of the entered serial?
Validation table
To answer the question, let's first look at the lines of code immediately following the call to sub_4012D0. Based on Fig. 3 it is visible that there is a check of the register EAX. If it is zero, then go to "Fail", if not then to "Good".
Okay, then to call a positive result, the
sub_4012D0 function in the EAX register should return a non-zero result.
Knowing this, let's go into the sub_4012D0 function itself, find its end and try to determine what affects the non-zero value setting in EAX. No sooner said than done:
Fig. fiveIda does not give a two-digit meaning that the function has at least
two outputs (highlighted with a blue frame). Moreover, the output in the green frame
clears the EAX register, and the output in the red frame, on the contrary,
sets the unit to one.
Determine this, now let's figure out what code precedes the block in the red frame? For this we use Olka (OllyDbg). Why is Olya, firstly: because she gives a clearer idea (specifically in our task) than Ida (various gaps and cross-references are distracting in her), and secondly: we will gradually proceed to debugging quacks.
Fig. 6Note: Before opening OllyDbg, it is advisable to disable ASLR, the protection mechanism that is responsible for randomizing the address space. This is done in the registry, in the branch: "HKLM \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ Memory Management", create the key "MoveImages" (DWORD) with the value "0", and then restart the OS. If this is not done, after each application restart, the addresses will change ...So, in Fig. 6 that the installation of the register EAX = 1 is preceded by a kind of double cycle (addresses 0x401440-0x401464). This is our first key element and a starting point to resolve the quack.
If you put a bryak on the address 0x401440 and go through to the first check:
Note: It is worth saying that if in the input field you use a line other than "111122223333444455", you will have to face two more problems. The first is in length , and the second is in valid characters that can be used in the serial code. The definition of the code responsible for these restrictions, I will leave for you.
Fig. 7You will see that in line 0x401449 the unit is checked with some value that is located at the address (in my case) 0x18FB28. Further, if you successfully go through the entire cycle (by manipulating the branches or values at the required addresses; otherwise, you understand, you will understand) you will find that there are a total of 9 elements (each 4 bytes in size):
Fig. eightI gave these 9 elements the name "Validation Table". If you, like me, customized the values of the elements (during their testing in the cycle), you and I should have got this result:
Fig. 9That is, in the end (abbreviated form) we should have something like: 100010001.
We continue the definition of key elements and try to determine:
Who writes at 0x18FB28-0x18FB48?
Code responsible for completing the "Validation Table"
To detect this code, do the following:
- We continue the implementation of quacks (click F9 in Olka);
- We put the bryak on the sub_4012D0 call (or he should already be there);
- Switch to quacks and in the pop-up window (indicating success or non-success), click "OK" (thus continuing the work of the quacks);
- Next, click on the "Check" button to start the recalculation of the serial, after which you should stop at the call to sub_4012D0;
- Standing on the call sub_4012D0, put bryak on the record, at the address 0x18FB28;
- And again press F9.
If you did everything right, you will find yourself here:
Fig. tenHere, the calculation and saving of the first element of the “Validation Table” is highlighted with a red frame, the continuation of the calculation of the remaining eight elements is highlighted with a blue frame.
The code itself occupies the region of addresses 0x4011D4 - 0x4012BB and is a loop with three iterations. At each iteration, the values of the three elements of the table are calculated and stored. The presentation of this code on Python is as follows:
for out_index in range(3): for inner_index in range(3): quotient, remainder = 0, 0 for index in range(3): x, y = index + (out_index*3), index + (inner_index*3) byte = first_serial[x] * second_serial[y] quotient = int(byte / 7) remainder += byte % 7 remainder = remainder % 7 result_table.append(remainder)
What mean first_serial and second_serial will become clear a little later. Now we will continue to analyze Fig. ten.
What we have:
- Address 0x657020 (with incomprehensible data);
- Address 0x18fadc (with incomprehensible data);
- Address 0x18faec (with a value of 0x7);
- Address 0x18fae8 (with a value of 0x18fb30).
Fig. eleven
Fig. 12Carefully reviewing the code, it turns out that:
- By calculating the table of the assignment, the address 0x18fae8 has nothing to do;
- Arrays with 9 elements are located at addresses 0x657020 and 0x18fadc. At array 0x657020, each element is 1 byte, and at array 0x18fadc, four bytes;
- The value 0x7 from the address 0x18faec is quite actively used when calculating each of the elements of the validation table.
Having this data, let us try to determine what arrays represent at addresses 0x657020 and 0x18fadc. The most attentive probably already noticed that if you add 9 + 9 = 18. The size of the serial is also equal to 18. Therefore, we can assume that this is it. But it clearly does not correspond to the one we introduced initially. Although the most attentive could additionally notice that the elements from the array 0x657020, albeit vaguely, but somehow still resemble the first half of the sequence of elements from the serial number entered by us (111122223).
Looking ahead, I’ll say that the entered serial code is converted into an internal representation, so that it is a little more difficult to determine at a glance or by searching for a dump.
What is behind the address 0x657020?
We continue our analysis and try to determine what is all the same contained in the array 0x657020?
For this:
- We remove all the breakpoints that were installed on the memory:
- We continue the implementation of quacks (click F9 in Olka);
- On the sub_4012D0 call there should be a breakpoint;
- Switch to quacks and in the pop-up window (indicating success or non-success), click "OK" (thus continuing the work of the quacks);
- Next, click on the "Check" button to start the recalculation of the serial, after which you should stop at the call to sub_4012D0;
- Standing on the call sub_4012D0, put bryak on the record, at the address 0x657020;
- Next, click on F9 2 times (the first time you will find yourself somewhere in the library code, and the second in the right place).
If everything is done correctly, we find ourselves here:
Fig. 13Having traced the code highlighted in red, find out the following:
- At the address 0x657020, the first 9 bytes of our serial code (111122223) are written;
- The serial code is taken from the address 0x18FBC4 (in my case).
Our suspicions about the array 0x657020 are beginning to be confirmed slowly. Regarding the address 0x18FBC4 (which stores all the serials entered by us), then you should have got to know him at the very beginning, here:
Fig. 14Here, the red frame highlighted information relating to the serial, blue, the place where the e-mail is stored.
However, let's go back to our serial, otherwise we are a little distracted. After we have traced the instructions from pic. 13, at 0x657020, we have half of our serial number stored:
Fig. 15But, as we know, slightly different bytes should be stored here. It means that, most likely, some manipulations will be carried out over this half of the serial. To find out which, press F9 again. After which we find ourselves here:
Fig. sixteenHere, the red frame highlights the calculation and storage of the new value for the first element of the array 0x657020, the blue frame highlights the continuation of the calculations of the remaining eight elements.
The code itself occupies the region of addresses 0x401070 - 0x401165. The same calculations are performed for each element. The equivalent code for these calculations on Python is presented below:
def find_index(byte): byte_1 = byte >> 4 byte_2 = byte_1 << 3 res = (byte - byte_2) res = res & 0x0f return res
If you look closely at Pic. 16, notice some address 0x40CB28. To its contents, we will return later.
After performing all the transformations, we get a result very similar to what we had in Fig. eleven.
Fig. 17If compare Fig. 11 with Fig. 17, you will notice that the bytes are slightly mixed ... For clarity, I will duplicate the eleventh figure below:
Fig. 18To find the code that is engaged in mixing bytes, press F9 again.
After that, we will get to Fig. 13th place:
Fig. nineteenAfter analyzing the code, it becomes clear that new values are taken from the address 0x656EC8:
Fig. 20The code responsible for populating the 0x656EC8 array is shown below. How I found it, I think you will figure it out. The article is still not a rubber ...
Fig. 21What's going on here?
The EBX register contains the address 0x657020 from which the direct copying of elements into the array 0x656EC8 takes place. Looking to copy, you can see that some elements are mixed.
0x657020: 0, 1, 2, 3, 4, 5, 6, 7, 8
0x656EC8: 0, 1, 2, 4, 5, 3, 8, 6, 7
Now that we have answered our question “What is behind the address 0x657020?”, We can continue the study and answer other questions:
- What lies behind the address 0x40CB28 (was just discovered when analyzing the array 0x657020);
- What is hidden behind the address 0x18FADC (an array that stores 9 elements of 4 bytes each).
Derivative ASCII table
Parsing the data hiding behind the 0x18FADC array will be left to independent exploration. We need to act by analogy with the array 0x657020. I can only say that the array 0x18FADC stores the second half of the serial code.
Immediately we will analyze what the address 0x40B28 is. To do this, carry out the following manipulations:
- Turning off all the breakpoints set for memory;
- We continue the implementation of quacks (click F9 in Olka);
- On the sub_4012D0 call there should be a breakpoint;
- Switch to quacks and in the pop-up window (indicating success or non-success), click "OK" (thus continuing the work of the quacks);
- Next, click on the "Check" button to start the recalculation of the serial, after which you should stop at the call to sub_4012D0;
- Standing on the call sub_4012D0, put bryak on the record, at the address 0x40B28;
- Next, click on F9.
If everything is done correctly, we find ourselves here:
Fig. 22The code in the red frame creates an ASCII table, which is stored at 0x40B28. In the blue frame, on the basis of your mail, the created table is converted. I called the end result of this conversion the "Table of Substitutions."
The presentation of this code on Python is as follows:
mail = "support@reverse4you.org" def get_table(mail): ascii_table = [] for index in range(256): ascii_table.append(index) mail = list(mail) len_mail = len(mail) index = 0 accumulate_index = 0 while(index < 256): mail_index = index % len_mail byte_ascii = ascii_table[index] byte_mail = ord(mail[mail_index]) accumulate_index += byte_mail accumulate_index += byte_ascii accumulate_index = accumulate_index & 0xFF byte = ascii_table[accumulate_index] ascii_table[accumulate_index] = byte_ascii ascii_table[index] = byte index += 1 return ascii_table
I am glad to congratulate you, having reached this point, we revealed all the moments unknown to us and found all the key elements necessary for creating our own mail / serial pair.
Putting it all together
What we have?
- Valid range. The serial code should be equal to 18 characters, which in turn should consist of the characters [0-9], [az], [AZ];
- Conversion table Used to convert the entered serial code to an internal representation;
- Algorithm for turning the serial code into an internal representation;
- Algorithm for filling the validation table;
- Validation table: 100010001.
What do you need?
Create a serial code so that after passing through all the transformations, we get the required "Validation Table" (100010001).
How did I do?
I have to admit that I had to turn a little down ... Instead of manipulating the "zeros" and "ones" (I came to this a few hours later), I began to pick up the password based on the internal representation of the serial code, I tried to select the zeros and ones I needed using such values as, for example, 0xc0, 0x28, ..., 0xff (i.e. the first 16 characters of the "Replacement Tables"). Which of course was a mistake. I must say that I was looking for a complex task, which simply did not exist. Plus, I also threw out a few very important bytes, which constantly interfered with me (when dividing by any number, they gave “zero”) ... In general, for a long time to tell ...
Initially, I tried to determine the dependence of the effects of one byte on another, which you eventually led me to similar tables (the initial version, later it was painted in all the colors of the rainbow, unfortunately I deleted it, but this one remained):
Fig. 23Later, at the end of the evening (right after dinner), I was visited by several sensible thoughts, which ultimately led me to use “zeros” and “ones”, and not the raw bytes “0xc0, 0x28, ..., 0xff. As soon as this idea occurred to me, I immediately remembered those unfortunate bytes that constantly annoyed me and disturbed me (those who, when interacting with any number, gave me a “zero”). Having understood this, I instantly found myself at the computer and literally in 1-2 minutes I gathered for myself a valid pair.
During my failed attempts, I wrote several functions that allowed me to quickly test various theories about what bytes I needed to be. Thanks to them, I was able to make a valid pair in 1-2 minutes and I used them to write my keygen. It should be said that I did not try to somehow optimize or shorten the code, so it turned out to be rather cumbersome (and I honestly did not immediately understand that I was there on the second day after writing it). Therefore, if that is not much swear.
The following link can be found three keygen
Pashkin 'a,
BoRoV ' a and my favorite
Darwin 'a (the keygen of BoRoV'a I liked the most).
Passwords from archives:
- Pashkin: reverse4you.org_4_r00t
- Darwin: AggGgAgaGcGGggCGCG
- BoRoV: available in its pure form without an archive
That's all, thank you all for your attention.
***
The continuation of the article can be found
here .