In this tutorial we'll temporarily deviate from creating our own ASM programs and try to crack a binary which was created by someone else. Writing assembly is one skill, understanding it is another. The binary we'll be analysing can be found here. The keygen was created by Kwazy Webbit from the Reverse Engineering Team.
As with every tutorial we'll first cover the new instructions we'll be encountering during this reverse engineering task.
The
Finally we've made it to the interesting bit. The binary presents us with a tiny window and 2 text fields named Name and Serial, the latter of which can only be numerical. Let's try our luck ...
... anddddd ...
... nope nothing. At this point we don't know if it is expecting a specific Name and Serial, or maybe a specific Name and a derived Serial or even any Name and a Serial number which is derived from it. So let's look at it under IDA.
It was a long, bumpy road but at the end we've manage to find a Name-Serial pair that worked. As an exercise I suggest solving the keygen for a different Name or even for an arbitrary Name. The plan for the next reverse engineering tutorial is to cover one of the Flare-on challenges so stay tuned .. じゃあ、また
x86 Instructions
AND
The
AND operator performs a bitwise AND of the operands and places the result in the first operand specified. To refresh your memory, the AND operator only returns TRUE if both operands are TRUE. The following is the truth table for this operator:
+-----------+-------------------------------+
| A | 0 | 0 | 1 | 1 |
|-------------------------------------------|
| B | 0 | 1 | 0 | 1 |
+-----------+-------------------------------+
| A & B | 0 | 0 | 0 | 1 |
+-----------+-------------------------------+
Some examples:
and eax, ebx ; EAX = EAX & EBX
and eax, 0Fh ; EAX = EAX & 0xF
and [esi], esp ; [ESI] = [ESI] & ESP
and [esi], dword 0ABCh ; [ESI] = [ESI] & 0x0ABC
LEA
LEA stands for Load Effective Address and is one of the data movement instructions. Let's look at a few examples:
lea eax, [ebp+0C0h] ; if EBP = 0x0000ABCD, then EAX = 0x0000AC8D
lea eax, [ebx+ebx] ; if EBX = 0x00001234, then EAX = 0x00002468
lea ebx, [eax+ecx*4] ; if EAX = 0x00000010 & ECX = 0x0000000A then EBX = 0x00000038
The syntax used for LEA is quite misleading; even though the square brackets are used, the instruction does not reference memory but rather it evaluates the contents of the square brackets directly.
MOVZX & MOVSX
These instructions are special versions of theMOV instruction which are used to extended signed (MOVSX) and unsigned (MOVZX) registers to larger registers.
MOVZX stands for Move With Zero Extension. When data is moved from a register to a larger register, the value is prepended with zeroes. Some examples are:
movzx eax, bx ; if BX = 0x1000, then EAX = 0x00001000
movzx ebx, ax ; if AX = 0xFFFF, then EBX = 0x0000FFFF
movzx ax, bl ; if BL = 0xFF, then AX = 0x00FF
movzx bx, al ; if AL = 0x7F, then BX = 0x007F
MOVSX stands for Move with Sign Extension. This time the resultant value is prepended with either zeros or ones depending on the sign of the original value, and hence the sign is preserved. Let's take the previous examples:
movsx eax, bx ; if BX = 0x1000, then EAX = 0x00001000
movsx ebx, ax ; if AX = 0xFFFF, then EBX = 0xFFFFFFFF
movsx ax, bl ; if BL = 0xFF, then AX = 0xFFFF
movsx bx, al ; if AL = 0x7F, then BX = 0x007F
ROL & ROR
ROL stands for Rotate Left whereas ROR stands for Rotate Right. These instructions shift the bits of the destination to the left/right a number of times; the data that slides off the end of the register is inserted back from the right/left. The following table should clear things up:
+---------------+-----------------------------------------------+--------------+
| EAX | 1100 0011 1010 1110 0001 1111 0000 0101 | 0xC3AE1F05 |
+---------------+-----------------------------------------------+--------------+
| ROL EAX, 01h | 1000 0111 0101 1100 0011 1110 0000 1011 | 0x875C3E0B |
|------------------------------------------------------------------------------|
| ROL EAX, 06h | 1110 1011 1000 0111 1100 0001 0111 0000 | 0xEB87C170 |
|------------------------------------------------------------------------------|
| ROL EAX, 0Fh | 0000 1111 1000 0010 1110 0001 1101 0111 | 0x0F82E1D7 |
+---------------+-----------------------------------------------+--------------+
| ROR EAX, 01h | 1110 0001 1101 0111 0000 1111 1000 0010 | 0xE1D70F82 |
|------------------------------------------------------------------------------|
| ROR EAX, 06h | 0001 0111 0000 1110 1011 1000 0111 1100 | 0x170EB87C |
|------------------------------------------------------------------------------|
| ROR EAX, 0Fh | 0011 1110 0000 1011 1000 0111 0101 1100 | 0x3E0B875C |
+---------------+-----------------------------------------------+--------------+
It stands to reason that rolling a 32-bit register 33 times is the same as rolling it in the same direction once, hence if say the value of a register has to be rolled X times, where X is a large number, instead roll it X % 32 times.
MUL (IMUL) & DIV (IDIV)
As you've probably guessedMUL performs multiplication while DIV performs Division; IMUL and IDIV are the signed versions.
Although in real life these are basic operations, they might be tricky to handle for the CPU which has a limited number of fixed-sized registers to work with. This is because when multiplying 2 large numbers, the result can be too large to fit into a single register; when dividing, the result can be a decimal number with a decimal part which is too long to fit into a single register. To solve this issue, 2 registers are used, namely EAX, the main register, and EDX.
Let's see some examples on how these 2 registers are used to store the results:
mul ecx ; EDX:EAX = EAX * ECX (unsigned)
imul edx ; EDX:EAX = EAX * ECX (signed)
imul ecx, 1Fh ; ECX = ECX * 0x1F (signed)
div ecx ; EAX = EDX:EAX / ECX (unsigned)
; EDX = EDX:EAX % ECX (unsigned)
div cl ; AL = AX / CL (unsigned)
; AH = AX % CL (unsigned)
idiv ecx ; EAX = EDX:EAX / ECX (signed)
; EDX = EDX:EAX % ECX (signed)
Notice how both of these functions can make use of EAX, the accumulator register, without explicit declaration. As mentioned earlier, the EDX register is used as a secondary storage for overflows in multiplications and remainders in divisions. Another interesting thing to notice is that MUL always operates and stores the result in EAX/AX/AL and hence the following instruction is invalid:
mul ecx, 1Fh ; ECX = ECX * 0x1F <- INVALID
SBB
SBB is Subtract with Borrow/Carry. It differs slightly from the traditional subtract, i.e. SUB, in that if the Carry Flag is set, it subtracts 1 extra, otherwise it's exactly the same. For example:
sbb ax, 10h (CF = 1) ; if EAX was 0x0050, then EAX = 0x003F
sbb ax, 10h (CF = 0) ; if EAX was 0x0050, then EAX = 0x0040
JA(JG), JAE(JGE), JB(JL) & JBE(JLE)
These operators are different conditional jump instructions which operate on either signed or unsigned values. The following is a summary table which contains a self-explanatory description and the jump conditions for each:
+------------+---------------------------------------+--------------------+
| Mnemonic | Meaning | Jump Condition |
+------------+---------------------------------------+--------------------+
| JA | Jump if Above | CF=0 & ZF=0 |
+------------+---------------------------------------+--------------------|
| JAE | Jump if Above or Equal | CF=0 |
|------------|---------------------------------------+--------------------|
| JB | Jump if Below | CF=1 |
|------------|---------------------------------------+--------------------|
| JBE | Jump if Below or Equal | CF=1 | ZF=1 |
+------------+---------------------------------------+--------------------+
| JG | Jump if Greater (signed) | ZF=0 & SF=OF |
|------------|---------------------------------------+--------------------|
| JGE | Jump if Greater or Equal (signed) | SF=OF |
|------------|---------------------------------------+--------------------|
| JL | Jump if Less (signed) | SF!=OF |
|------------|---------------------------------------+--------------------|
| JLE | Jump if Less or Equal (signed) | ZF=1 | SF!=OF |
+------------+---------------------------------------+--------------------+
The Binary
Identifying Important Areas
Looking at the strings present in the binary, we notice 2 very interesting ones, "RIGHT" and "You got it!". We know that that's where we need to end up so lets start from there. My personal strategy when tackling binaries in which I known what the point I want to reach is, is to start from there and work my way up as much as I can. Let's apply this. Moving backwards from the desired result, we start identifying the conditions that have to be met:- [0x0040112F] jz short loc_40113D - The Zero Flag must be zero for the program to take the correct path
- [0x0040112A] test eax, eax - Since we require
ZFto be zero,EAXmust NOT be 0x00000000. - [0x00401125] call sub_401000 - This requires further investigation but we know that when it ends,
EAXcannot be zero
The Main Fuction
The function is manageable but we can still weed out some irrelevant parts so we can focus on the core functionality. Once again we start from the bottom:- [0x0040107D] inc eax -
EAXcannot be 0xFFFFFFFF (Recall:EAXcannot be 0 when the function ends) - [0x0040107E] sbb eax, eax - Subtracting a number from itself gives zero, so
CFhas to be zero, else we getEAX= -1 - [0x0040107A] neg eax - Only
EAX= 0x00000000 will not set the Carry Flag - [0x00401077] xor eax, [ebp+serial] -To end up with a
EAX= 0x00000000,EAXmust be equal to[ebp+serial]
[ebp+name]. By the end of the loop [ebp+name] holds the result. Let us name this end result Name', since it is derived from Name. To keep things simple, and also since we'll be solving the binary for a specific input, we do not need to know exactly what the operations performed on Name, to produce Name', are. The only thing we need to know is the value of Name'. By setting a break point at the end of the function, we get Name' = 0x01E55668.
With the value of Name' in hand, we can focus on the last part of the function. I've displayed this here for reference:
As a reminder from the previous analysis, we require the XOR function at address 0x00401077 to result in a 0. The XOR's operands are Name' and Serial', where Serial' is the resultant of Serial after some operations have been performed on it; we'll see what these operations are. This means that at this point, SERIAL' and NAME' have to be equal and we already know that Name' = 0x01E55668.
From the image above we can see that the operations on Serial are:
XORSerial with 0x1337C0DESUBresult with 0xBADC0DE5NOTresult
NOT (( Serial ^ 0x1337C0DE ) - 0xBADC0DE5) = 0x01E55668
=> ( Serial ^ 0x1337C0DE ) - 0xBADC0DE5 = 0xFE1AA997
=> Serial ^ 0x1337C0DE = 0xB8F6B77C
=> Serial = 0xABC177A2
=> Serial = 2881583010
Combining everything together we try our luck again ...
... and ..
... we've done it !!
Conclusion
It was a long, bumpy road but at the end we've manage to find a Name-Serial pair that worked. As an exercise I suggest solving the keygen for a different Name or even for an arbitrary Name. The plan for the next reverse engineering tutorial is to cover one of the Flare-on challenges so stay tuned .. じゃあ、また








Great, Thanks !
ReplyDeleteThe Vulnerable Space: (N)Asm101 - 0X04 - Reversing Binaries - Part I (Easycrack.Exe) >>>>> Download Now
ReplyDelete>>>>> Download Full
The Vulnerable Space: (N)Asm101 - 0X04 - Reversing Binaries - Part I (Easycrack.Exe) >>>>> Download LINK
>>>>> Download Now
The Vulnerable Space: (N)Asm101 - 0X04 - Reversing Binaries - Part I (Easycrack.Exe) >>>>> Download Full
>>>>> Download LINK is