1

I don't know if it is asked but, I couldn't find it anything despite this question. Is it because of disassembler's fault? Or if it is right, why compiler generates this code?

 ; int __cdecl main(int argc, const char **argv, const char **envp)
 main            proc near               ; CODE XREF: start-7Bp

 var_4           = dword ptr -4
 argc            = dword ptr  8
 argv            = dword ptr  0Ch
 envp            = dword ptr  10h

                 push    ebp
                 mov     ebp, esp
                 push    ecx
                 call    ds:GetCurrentProcessId
                 mov     [ebp+var_4], eax ; <---
                 mov     eax, [ebp+var_4] ; <---
                 push    eax
                 push    offset output
                 call    printf
                 add     esp, 8

 debugger_wait:                          ; CODE XREF: main+28j
                 call    ds:IsDebuggerPresent
                 test    eax, eax
                 jnz     short debugger_present
                 jmp     short debugger_wait
 ; ---------------------------------------------------------------------------

 debugger_present:                       ; CODE XREF: main+26j
                 int     3               ; Trap to Debugger
                 xor     eax, eax
                 mov     esp, ebp
                 pop     ebp
                 retn
 main            endp

Marked lines with arrows (from IDA output) shows two MOV operations which semantically equals to nothing (or is it?). This is my source code:

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[], char *envp[])
{
    DWORD pid = GetCurrentProcessId();
    printf("%d\n", pid);
    while (!IsDebuggerPresent());
    __asm int 0x3;
    return 0;
}

Compiled with MSVC++ (19.00.24225.1):

cl.exe dnmProg.c

UPDATE: I tried other options, and both /O1 and /O2 doesn't have such structure, but /Ot has same instruction pair. When I compiled it with /Os there is:

call    ds:GetCurrentProcessId
mov     [ebp+var_4], eax ; <---
push    [ebp+var_4]      ; <---
push    offset printf_parameter
call    printf_

Thanks.

6
  • if you compile in debug mode the compilrr normally saves the return values i so it saved eax to dwird pid and then used the saved pid for printf
    – blabb
    Nov 26, 2017 at 3:12
  • You mean Visual Studio's debug mode? I examined Visual Studio modes. Indeed debug mode has same pair but with lots of other differences in code and debugging structures. In release mode there is a nop before IsDebuggerPresent which I don't know if it is related or not. Nov 26, 2017 at 8:32
  • @de6f release mode involves alot of optimisations of the naive code, there is why there is a difference in length.
    – Abr001am
    Nov 27, 2017 at 13:25
  • Understand that but, why compiler generating that code in first place? Any other advantage for that? Nov 27, 2017 at 16:31
  • 1
    @de6f you hhave a statemeny pid = blah compiler aassigned a local var pid in stack ebp-xx and stored the result of blah there then you havve a printf the compiler then reused the local it doesnt keep in mind the result is in eax already it does as told when in debug mode compilationn when you tell it to optimize it eliminates the pair also in debug mode the pair helps source level debugging
    – blabb
    Nov 27, 2017 at 17:58

1 Answer 1

1

The code uses this non-optimal form in order to match the original code. It is saving the value in the local "pid" variable so that a debugger can see it. Then it is fetching the value again in order to use it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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