3

I'm trying to reverse a function and I came across these lines inside the function:

1. call    ds:HeapAlloc
2. mov     [ebp+var_4], eax
3. mov     eax, [ebp+var_4]

Why is line 3 needed? If I need to save the result, line 2 saved that and EAX already contains the result.

  • 1
    It might be that the binary is not optimized and the line 3 is part of the next instruction that uses the HeapAlloc’s result – Paweł Łukasik Sep 30 '18 at 13:57
  • @PawełŁukasik even if it's in used in the next instruction EAX already has the result/ – Moshe D Sep 30 '18 at 14:03
3

It's not needed at all.

However, some compilers may generate that assembly when compiled without optimization.

For example, gcc -O0 generates: (from godbolt)

f():
  push rbp
  mov rbp, rsp
  sub rsp, 16
  call fake_heapalloc()
  mov DWORD PTR [rbp-4], eax
  mov eax, DWORD PTR [rbp-4]
  mov edi, eax
  call g(int)
  nop
  leave
  ret

from C++ source code

int fake_heapalloc();
void g(int i);
void f(){
    int i;
    i=fake_heapalloc();
    g(i);
}

Because it's not optimized, the i is stored on the stack (instead of in a register) and the redundant move to/from stack code is generated.


Alternatively, some programmer may manually insert the assembly instruction there to... I don't know, it's unlikely.

  • 1
    Third alternative is if the variable is marked volatile: volatile int i;. A bit rare to have volatile variables on stack though. – jpa Sep 30 '18 at 19:26
2

This is to save the value on the stack and continue to work with that value in further instruction(s) as already mentioned in the comments. This happens if no optimization is done.

You have to look at it separately. Consider this in pseudocode.

var_4 = HeapAlloc()
func1(var_4)

Then the result would be similar to your assembler output namely:

1. call    ds:HeapAlloc
2. mov     [ebp+var_4], eax
3. mov     eax, [ebp+var_4]
4. push    eax
5. call    func1

And in that case you are right, the value is still in eax.

But now consider that:

var_4 = HeapAlloc()
between_func()
func1(var_4)

The assembler output would be similar to this

1. call    ds:HeapAlloc
2. mov     [ebp+var_4], eax
3. call    between_func
4. mov     eax, [ebp+var_4]
5. push    eax
6. call    func1

You can refer a pseudocode line to a section of the assembler code.

var_4 = HeapAlloc()

Corresponds to:

1. call    ds:HeapAlloc
2. mov     [ebp+var_4], eax

between_func()

Corresponds to:

3. call    between_func

func1(var_4)

Corresponds to:

4. mov     eax, [ebp+var_4]
5. push    eax
6. call    func1

And if you omit the between_func() then you will get to the result you have. Concluded, the two instructions are compiled independently.

  • Apparently, it is not the presented case. There is no call between_func in OP's question. Unless I'm missing something, there's no reason for omitting the between_func(). – Megabeets Sep 30 '18 at 21:02
  • Sorry for the vagueness, I maybe have to edit that. The between_func is inserted by me to illustrate the case where something is in between the two operations (that changes eax). It should clarify the separation and independence of the instructions in case of no optimization. – pudi Sep 30 '18 at 21:26
  • Which is not the case presented in the original question. I mean, while your answer is true and well structured (I really liked it tbh), it doesn't answers the asked question except the "This happens if no optimization is done" part. – Megabeets Sep 30 '18 at 21:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.