📜 ⬆️ ⬇️

Break me down completely (ZeroNights 2013)

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:

Instruments:

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:
')
image
Fig. one

As 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 !!!":

image
Fig. 2

Under 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:

image
Fig. 3

From 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:

image
Fig. four

Here 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:

  1. 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.
  2. 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:

image
Fig. five

Ida 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.

image
Fig. 6

Note: 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.

image
Fig. 7

You 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):

image
Fig. eight

I 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:

image
Fig. 9

That 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:

If you did everything right, you will find yourself here:

image
Fig. ten

Here, 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:

image
Fig. eleven

image
Fig. 12

Carefully reviewing the code, it turns out that:

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:

If everything is done correctly, we find ourselves here:

image
Fig. 13

Having traced the code highlighted in red, find out the following:

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:

image
Fig. 14

Here, 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:

image
Fig. 15

But, 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:

image
Fig. sixteen

Here, 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 #       0x657020 # byte = 0x657020[0] = 0x31 # index = find_index(byte) # 0x657020[0] = 0x40CB28[index] 


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.

image
Fig. 17

If compare Fig. 11 with Fig. 17, you will notice that the bytes are slightly mixed ... For clarity, I will duplicate the eleventh figure below:

image
Fig. 18

To find the code that is engaged in mixing bytes, press F9 again.
After that, we will get to Fig. 13th place:

image
Fig. nineteen

After analyzing the code, it becomes clear that new values ​​are taken from the address 0x656EC8:

image
Fig. 20

The 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 ...

image
Fig. 21

What'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:

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:

If everything is done correctly, we find ourselves here:

image
Fig. 22

The 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?

  1. Valid range. The serial code should be equal to 18 characters, which in turn should consist of the characters [0-9], [az], [AZ];
  2. Conversion table Used to convert the entered serial code to an internal representation;
  3. Algorithm for turning the serial code into an internal representation;
  4. Algorithm for filling the validation table;
  5. 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):

image
Fig. 23

Later, 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:

That's all, thank you all for your attention.

***

The continuation of the article can be found here .

Source: https://habr.com/ru/post/204432/


All Articles