0

I'm a noob with assembly, I understand some things, but it's still very convoluted and difficult to me at the moment.

There's a binary that I'm trying to look at in GDB but there is a section of the code that for the life of me I can't figure out what it's doing. I have an idea of what it might be doing, but I don't know for sure.

The part that's throwing me off is:

Dump of assembler code for function main:
   0x08048647 <+0>:     lea    0x4(%esp),%ecx
   0x0804864b <+4>:     and    $0xfffffff0,%esp
   0x0804864e <+7>:     pushl  -0x4(%ecx)
   0x08048651 <+10>:    push   %ebp
   0x08048652 <+11>:    mov    %esp,%ebp
   0x08048654 <+13>:    push   %ebx
   0x08048655 <+14>:    push   %ecx
=> 0x08048656 <+15>:    sub    $0x10,%esp
   0x08048659 <+18>:    mov    %ecx,%ebx
   0x0804865b <+20>:    movb   $0x0,-0x9(%ebp)
   0x0804865f <+24>:    sub    $0xc,%esp
   0x08048662 <+27>:    push   $0x0
   0x08048664 <+29>:    call   0x8048400 <time@plt>
   0x08048669 <+34>:    add    $0x10,%esp
   0x0804866c <+37>:    sub    $0xc,%esp
   0x0804866f <+40>:    push   %eax
   0x08048670 <+41>:    call   0x8048460 <srand@plt>
   0x08048675 <+46>:    add    $0x10,%esp
   0x08048678 <+49>:    call   0x8048480 <rand@plt>
   0x0804867d <+54>:    mov    %eax,%ecx
   0x0804867f <+56>:    mov    $0x51eb851f,%edx
   0x08048684 <+61>:    mov    %ecx,%eax
   0x08048686 <+63>:    imul   %edx
   0x08048688 <+65>:    sar    $0x5,%edx
   0x0804868b <+68>:    mov    %ecx,%eax
   0x0804868d <+70>:    sar    $0x1f,%eax
   0x08048690 <+73>:    sub    %eax,%edx
   0x08048692 <+75>:    mov    %edx,%eax
   0x08048694 <+77>:    imul   $0x64,%eax,%eax
   0x08048697 <+80>:    sub    %eax,%ecx
   0x08048699 <+82>:    mov    %ecx,%eax
   0x0804869b <+84>:    mov    %al,-0x9(%ebp)
...
...

I think it's seeding the random number generator with time, and then generating a random number, but there's also a local variable of some sort $0x51eb851f, which looks also like a time, and then down at the bottom it seems like the random number gets truncated down to just 8 bits by using %al.

Could someone break this down for me?

part 2 - What would the equivalent C code look like?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
A. Trevelyan
  • 136
  • 5
  • 3
    yeah, looks like srand(time(NULL)); rand(). The pattern of shifts after that multiply is [Why does GCC use multiplication by a strange number in implementing integer division?](https://stackoverflow.com/q/41183935) - signed division has some extra wrinkles, hence the `sar $0x1f, %eax` / `sub`. Probably a debug build, given that GCC(?) used EBP as a frame pointer. – Peter Cordes Nov 14 '22 at 08:47

1 Answers1

3

It seems assembly code for:

 char s = 0;
 srand(time(NULL));
 s = rand() % 100;

When reverse engineering a piece of assembly code (or asking about it) it's always worth to break it apart and comment each pieces.

The first part should be clear:

lea    0x4(%esp),%ecx       ;ecx = esp + 4 = ptr to return addr + 4
and    $0xfffffff0,%esp     ;align the stack
pushl  -0x4(%ecx)           ;push return address

push   %ebp         
mov    %esp,%ebp            ;frame pointer

push   %ebx
push   %ecx                 ;preserved reg

sub    $0x10,%esp           ;allocate 16B
mov    %ecx,%ebx            ;ebx = esp at entry + 4

movb   $0x0,-0x9(%ebp)      ;s = 0

sub    $0xc,%esp            ;Keep the stack aligned at the call site
push   $0x0                 ;NULL
call   0x8048400 <time@plt> ;time(NULL)
add    $0x10,%esp           ;cdecl cleaning

sub    $0xc,%esp            ;alignment
push   %eax                 ;eax = time(NULL)
call   0x8048460 <srand@plt>;eax = srand(time(NULL))
add    $0x10,%esp           ;cdecl

call   0x8048480 <rand@plt> ;rand()
mov    %eax,%ecx            ;ecx = rand()

The next part is a bit confusing unless one has already came across this compiler trick.
It's a division by d through the fixed-point reciprocal 1/d that employs a trick to increase the precision (see this).
1/d is first represented as a 32-bit fixed point number with the dot right before the left-most digit (this is a fancy way of saying that we regards the weights of the bits as negative powers of two, from left to right, just like we usually do with decimal numbers).
Multiplying this number by an integer will generate a 64-bit fixed point number with the dot in the middle (32 bits to the left of it, 32 bits to the right). Since we are only interested in the integer part, we take only the upper 32-bits.
To increase the precision, all the leading zeros in the floating point representation of 1/d are removed with a shift to the left, the result must be then compensated with a shift to the right.
Finally, the result may be off by one if the dividend is negative so we may need to add one.

Let's look at the code:

mov    %eax,%ecx        ;ecx = rand() [from previous block]

mov    $0x51eb851f,%edx ;edx = 1/d (shifted)
mov    %ecx,%eax        ;eax = dividend
imul   %edx             ;edx:eax = dividend * (1/d) (shifted)
                        ;edx = integer part, eax = fractional part
sar    $0x5,%edx        ;undo the shift, edx = integer part of dividend * (1/d)
mov    %ecx,%eax        ;eax = dividend
sar    $0x1f,%eax       ;eax = 0xff..f = -1 if dividend negative else 0
sub    %eax,%edx        ;edx = dividend * (1/d) - (-1 if dividend negative else 0) 
mov    %edx,%eax        ;eax = dividend / d

To find what divisor 0x51eb851f and a shift by 5 represent, you can:

  1. Find the shifted divisor with 0x51eb851f/0x100000000 (this works exactly like 0.99 = 99 / 100), in this case 0.32, and then scale it by dividing it by 2^5. This gives: 0.32 / 32 = 0.01 = 1/100.
  2. Take a big smooth number (a number with a lot of small prime divisors), say 2310000, multiplying it by 0x51eb851f and then shifting the result right by 32+5 bits. (2310000 * 0x51eb851f) >> 37 = 23100, so you can see that the result is 1/100 of the dividend. Here's you need to remember that integer division is not injective.

You can always check with godbolt.org.

We can now finish the reverse engineering:

mov    $0x51eb851f,%edx
mov    %ecx,%eax
imul   %edx
sar    $0x5,%edx        
mov    %ecx,%eax        
sar    $0x1f,%eax       
sub    %eax,%edx        
mov    %edx,%eax            ;eax = rand() / 100

imul   $0x64,%eax,%eax      ;eax = (rand() / 100) * 100

sub    %eax,%ecx            ;ecx = rand() - (rand() / 100) * 100 = rand() % 100
mov    %ecx,%eax            ;eax = rand() % 100

mov    %al,-0x9(%ebp)       ;s = rand() % 100
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124