Basic division with negative numbers in Linux x86 Assembly and spanning over multiple registers

28 Mar 2013

So I'm doing the SLAE32 course by SecurityTube, it's quite interesting and in my honest opinion a very good bang for your buck. I normally don't make publicity, but I must say, for a 150 dollar course you get quite a lot in return. It even comes with the SecurityTube Gnu Debugger Expert to make sure you understand how the gdb works (since it's used a lot throughout the course). One of the assignments in the course was to discover how division and multiplication works. I was really interested in division as it poses some caveats to watch our for.

The source code for our example:

global _start           
section .text
_start:
    mov edx,0x00000001
    mov eax,0x00000000
    mov ecx,0x00000002
    div ecx
    mov edx,0x00000000
    mov eax,0x7FFFFFFF
    mov ecx,0x00000002
    idiv ecx
    ;divide with negative number
    mov edx,0
    mov eax,0x0000000A
    ;move -2 in hex into ecx
    mov ecx,-0x00000002
    idiv ecx
    ; exit program
    mov eax, 1
    mov ebx, 10
    int 0x80

My GDB hook stop is:

(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
 print/x $eax
 print/x $edx
 print/x $ecx
 print $eflags
 disassemble $eip+10
 end
(gdb) break _start

div: Unsigned Division

Let's look at unsigned division first. Unsigned means the registers we will use can only contain positive values. This means the numbers can range from 0 to 4 294 967 295 Have a look:

    mov edx,0x00000001   ;move 1 into edx
    mov eax,0x00000000   ;move 0 into eax
    mov ecx,0x00000002   ;move 2 into ecx
    div ecx              ;divide EDX:EAX by the value in ecx

The number we have is spanned over EDX:EAX giving us 0x10000000 or 4 294 967 296 \( 2^{32} \) (this doesn't fit a 32-bit register anymore, so we span it over EDX:EAX). What we want to do is divide this by 2, so \( \frac{4 294 967 296}{2}=2 147 483 648 \) . Let's see what's happening in the registers with GDB after we execute the div:

$17 = 0x80000000 ;EAX
$18 = 0x0        ;EDX
$19 = 0x2        ;ECX
As we can see EAX contains 0x80000000, which is indeed 2 147 483 648 decimal (unsigned).

idiv: Signed Division

Now idiv is an instruction which looks at signed divisions. Meaning we also need to take negative numbers into account. This means our numbers are limited from -2 147 483 648 (0x80000000) up to 2 147 483 647 ( 0x7FFFFFFF). If you try using bigger numbers (which I did during my quest to understand division in asm) you will run into exceptions. We will now do \(\frac{2 147 483 647}{2}\) which yields 1073741823.5, but since we aren't with floats will result in 1073741823 and a rest of 1. Let's have a look at the source code:

    mov edx,0x00000000   ;move 0 into EDX
    mov eax,0x7FFFFFFF   ;move 2 147 483 647 into EAX
    mov ecx,0x00000002   ;move 2 in ECX
    idiv ecx             ;Signed division by 2

GDB:

$33 = 0x3fffffff ;EAX
$34 = 0x1        ;EDX
$35 = 0x2        :ECX
As we can see we have 0x3FFFFFFF in EAX (1 073 741 823) and a rest of 1 in EDX. Let's continue and divide with negative 2:

    ;divide with negative number
    mov edx,0           ;move 0 into EDX
    mov eax,0x0000000A  ;move 10 into EAX
    mov ecx,-0x00000002 ;move -2 in hex into ECX
    idiv ecx

You can also write 0xFFFFFFFE instead of -2. Let's see what happens in GDB:

$49 = 0xfffffffb ;EAX
$50 = 0x0        ;EDX
$51 = 0xfffffffe ;ECX
We have -5 in EAX, no rest in EDX and -2 in ECX. That's it, part of the SLAE32 is exploring yourself, so I hope you found this interesting and that this clarifies some further questions you might be having about dividing in ASM with signed integers. If you have questions, remarks or constructive criticism, feel free to leave a comment below or email me (my email is on the about page).