๐ xchg rax, rax
Forewords
There is an online and free version of the book.
I will try to understand each 64 pages of this awesome work, in order to maintain my assembly knowledge and to learn new tricks.
Of course I can be wrong in my interpretations of certain code or instructions, if so, feel free to help me improve!
0x00
|
|
The first page is pretty straightforward. As you can see every line sets one register to 0
.
Every instruction here is very common and easy to understand, but one thing caught my attention: loop $
. So let’s dig a bit here.
loop <label>
can be resumed like so:
|
|
As you can see, it decrements cx
at each iteration until it is 0
. So a loop $
would simply set cx
to 0
, as the label to loop through is $
, the symbol for the actual address.
Normally with loop label
you would have some code between label
and the loop instruction, but here there is none, so the loop only decrements cx
.
Pretty sneaky trick.
0x01
|
|
This one is a little trick and pretty obfuscated but was very quick to understand. In fact, this loop produces the fibonacci sequence for the first cx
elements.
As we learnt about loop
at 0x00, it loops until cx
equals 0
, and here there is one instruction which gets exectued within the loop.
xadd
is quite uncommon, this instruction can be coded as:
|
|
One experienced programmer can spot the algorithm to compute the fibonacci sequence here, which calculate the sum of the two previous elements of the sequence.
0x02
|
|
This code was a bit tricky, after discussing with friends ( xThaz &
SoEasY) we agreed to say that this code tells if
rax
is different than 0
.
At first I said that it tells the sign of rax
, but I missunderstood the sbb
instruction, but I was wrong and overlooked the computing of the sbb rax, rax
.
This code requires to look at neg
at first, despite of switching the sign of the target, it also sets the carry flag CF
accordingly with the sign.
We can now dig sbb
, its code can be:
|
|
By looking at the possible output values, we van see that it is a substraction by either rax + 0
or rax + 1
of rbx
. Given that in this code, we have sbb rax, rax
, we can sse that we will only get rax = rax - (rax + CF)
, with CF
at 0
or 1
, thus the output of the instruction is either 0
or 1
, corresponding to the initial sign of rax
.
0x03
|
|
With the previous experiences we now know about all the above instructions, we can deduce the behaviour of the code, which can be coded in a more high-level language like Python:
|
|
Yes, this is that simple. The tricky part comes from the sbb
and and
part.
sub rdx, rax
stores the difference inrdx
, and sets theCF
if needed.sbb rcx, rcx
setsrcx
toneg CF
(either0
or0xffffffff
)and rcx, rdx
- If
rax
was lower thanrdx
at line 1:rcx
would be0
and then theand
sill still setrcx
to0
- If
rax
was greater thanrdx
at line 1:rcx
would be0xffffffff
and so,rcx
becomes the value ofrdx
- If
add rax, rcx
is trivial now
So 0x03
adds rdx
to rax
if rax > rdx
.
0x04
|
|
This code was pretty simple to test, I was unsure of the exact behaviour of it, so I tested it:
|
|
And as you can see by executing this code, the printable characters have their case swapped: lowercase letters become upperccase, and vice-versa.
0x05
|
|
This code is so tiny that it seems complex, but fear not, I think I have the solution for it. This code shows the similarities and difference beteween sub
and cmp
.
sub rax, 5
actually overwrites the value ofrax
cmp rax, 4
computessub rax, 4
and only stores the result in the flags,rax
is not modified
0x06
|
|
This code teaches the trick for those instructions:
not rax
does the two’s-complement for the value ->not 0x5 = 0xfffa
inc rax; neg rax
does the same ->neg 0x6 = 0xfffa
0x07
|
|
This code shows us that the inc; neg
is symetric: doing it twice leads us to the original value. Which is pretty logic as inc; neg
is the same as neg
.
0x08
|
|
rcr
is not quite common, it rotates the bits to the right.
In fact, there is stuff hapenning with the CF
but I will skip this part as it is too long.
0x09
|
|
shr
is a right shift of bits, with storing into CF
for overflowing last bit. This does the same as dividing by 2^n
, with shr rax, n
.
adc
is add
with the CF
in addition:
|
|
So here the code divides rax
by 8 (2^3)
and if the 3rd bit was 1, it increases rax
, we can rewrite this code in Python to make things clearer:
|
|
0x0a
|
|
Now it gets a bit interesting.
add byte [rdi], 1
increases the value of what is pointed by the address at rdi
.
inc rdi
adds 1
to rdi
without modifing the CF
.
adc byte [rdi], 0
writes the content of CF
at the address pointed by rdi
.
loop .loop
-> dec cx; jmp short .loop
.
My guess here is that it sets every bytes in memory (at the address pointed by rdi
) to the content of CF
. The size of the memory is rcx
, and the first byte is set to 1
in order to identify the chunck (like a start bit in telecommunications).
0x0b
|
|
Here I got stuck for at least one hour, and because we had already seen all instructions, I did not bothered re-reading the documentation. It is pointless to read something twice, right?
So after calling the joker “coworker help”, we saw that neg
does not only the 2-complement of the register, but also sets CF
to 1
if the register is not 0
. Remember to always double check what you read, especially documentation.
So this code invert all bits of rdx
, and if rax
is not 0
, it will decrement rdx
by 1
.
0x0c
|
|
Despite the number of lines, this code was very easy to understand, in fact I was even able to guess (CTF player strenght here) the behaviour of it within a few seconds.
The code acts in two steps, and demonstrates that a ror
+ xor
is the same as a xor
+ ror
.
The first 3 lines copies rax
into rcx
and xor rcx, rbx
, and then does a ror rcx, 0xd
. The other next 3 lines does the opposite: ror rax, 0xd
(and also ror rbx, 0xd
) and xor rax, rbx
.
Finally we see that both rax
and rcx
have the same value. Proving that ror;xor
is commutative.
0x0d
|
|
This does the same as 0x0c but for xor; and
& and; xor
. Nothing really outstanding.
0x0e
|
|
Same goes for and; not
& not; or
.
0x0f
|
|