
Unlike Windows, Linux cannot boast a large number of different tricks against debugging. Opensource and all that played their share in this. Among debuggers
there is gdb, but it is very easily detected by the simplest techniques of anti-debugging.
Accordingly, there are not many known protectors capable of greatly complicating the debugging of a binary, among which one can only recall 'shiva', but it has “died” long ago.
Today, the
lincrackme3 program with crackmes.de will serve as a
toy for us, it is not difficult and at the same time, it has several anti-debugging tricks. We will only use the disassembler - no debuggers, ltracer-s, and especially patch-binaries. The disassembler will be IDAPro 5.5.
So, load ... There are no problems with loading: the ELF specification fields are correct. Next, it is most logical to start with searching something interesting in string or import or, in the case of small binaries, look at the assembler listing of the “sheet” in search of information about the compiler, possible obfuscation, encryption, etc. We will go the first way and look for imports. So, the ptrace challenge will be interesting for us - it will lead to a defense mechanism.
')

Here, the code tries to "debug itself" and if it fails (someone already does this without it, for example, a debugger), then ptace returns -1, the sign flag is raised, and ... the program ends its execution. But that is not all. A cross reference to the debugger string also leads here:

Another trick against debugging: at runtime, the program opens the standard streams STDIN = 0, STDOUT1, STDERR = 2, and if you try to close the file descriptor, then if no one has opened it before (for example, debugger), an error will occur and the result of close (3) = - 1, in the case of the presence of a previously open file descriptor: close (3) = 0.
So, with tricks against debugging - everything. Further, only check serial. Cross references to interesting "lines will lead us here:

IDA recognized the runtime functions and everything really becomes very simple: the sub_804862C function simply translates the “XXXX-XXXX-XXXX-XXXX” format password to “XXXXXXXXXXXXXXXX” and compares the length of the received password with 0x10 = 16.
If the password length is 16 characters then jump here:

In this thread, we are waiting for a password comparison with the string “It's not that easy, ...”, but the result is not “interesting” for the program and the next step is to play against the disassembler, using the debugger (after tracing) everything would be much easier, but try trace in mind. Pay attention to the command
from all loc _8048847 , which is located at
08048841. When it is executed, the address of the command following it will be placed on
"Top of the stack." Next, we return the return address to the
eax register, add the offset, put it back on the top of the stack, and return. But where?
This is where:
08048846 h +09 Dh = 080488 E 3 h
Another trick: a hidden procedure call whose address is located somewhere in the depths of the stack. The situation is complicated by the fact that the addressing is done by means of the
esp register, the value of which changes as the program runs. If we worked in the debugger, then finding the address of the desired procedure is a simple matter, but in this case (working only with the disassembler) it is necessary to trace all references to the stack section. All right, we will proceed from the fact that for the program to work properly, you need to have a balanced stack. Therefore, an interesting team immediately comes under suspicion at the beginning of the “greeting procedure”:

Another verification call with ptrace ().
If everything went well with the test, then jump here:

In this cycle, a password is being converted from ASCII format to HEX, but the implementation requires only numeric values ​​among the characters of the password. The resulting values ​​are sequentially recorded in the "stack depth": the first four characters of the serial number are written here
[ esp +36] ,
the second is here
[ esp +34] , the third is
[ esp +32] , the fourth is
[ esp +30] . Next - the most interesting verification of the password and the "desired transition.

As you can see there are 5 conditional jumps per block, which can probably lead us to a message about the wrong password. Then it is necessary, in turn, "correctly" - without jumping, to go through all the checks. So, let's begin…

The sum of the first and second four values ​​of the password should be equal to twice the sum of the third and fourth.

The value of the second four is greater than the third.

The sum of the first and fourth quarters is a pair.

The value of the first four is more than 5 and less than 24.

And here is the last transition. This time it is not necessary to jump. And this is possible only under the condition that the value of the last (fourth) four is unpaired. What, provided that the sum of the first and fourth fours is a pair,
tells us that the value of the first four is also unpaired. If all conditions are fulfilled correctly, then we will have to “decrypt” (dexoring) the lines informing you about the correct password and see the following picture:

I think writing keygen is easy.
Thank you all for your attention.
PS: Greets fly to Alexa Nachtigal.
PPS Article published at the request of the author, Paul H.
PPPS Sorry for the code in Zhipeg, I'm not guilty :-)