0

I have some questions about prologue and call instruction.

1) Do I understand correctly that:

Call instruction always use to call a function, although the function call is not always implemented by using the call (it may be realized by using push+jmp)

Most functions have a prologue, and it often looks like this:

push ebp
mov ebp, esp
sub esp, xx

And prologue for optimizing compilers:

sub esp, xxx

Also I know about naked functions, which havent prologue and epilogue. Does it exists another situations that function havent prologue? Or all that samples of disassembly code is calls of naked functions?

call from :   0x77b6f6f0 ntdll.dll!RtlImageDirectoryEntryToData+0x236 ??:0
        to :   0x77b62869 ntdll.dll!RtlEnterCriticalSection+0xb559 ??:0

  0x77b6f6f0 call   $0x77b62869 %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b62869 mov    0xfffffff0(%ebp)[4byte] -> %ecx
  0x77b6286c mov    %ecx -> %fs:0x00[4byte]
  0x77b62873 pop    %esp (%esp)[4byte] -> %ecx %esp
  0x77b62874 pop    %esp (%esp)[4byte] -> %edi %esp
  0x77b62875 pop    %esp (%esp)[4byte] -> %edi %esp
  0x77b62876 pop    %esp (%esp)[4byte] -> %esi %esp
  0x77b62877 pop    %esp (%esp)[4byte] -> %ebx %esp
  0x77b62878 mov    %ebp -> %esp
--------------------------------------------------------------------
call from :   0x77b74d5e ntdll.dll!RtlGetCurrentTransaction+0x63a ??:0
        to :   0x77b6f5cf ntdll.dll!RtlImageDirectoryEntryToData+0x115 ??:0

  0x77b74d5e  call   $0x77b6f5cf %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b6f5cf  push   $0x0000005c %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b6f5d1  push   $0x77b608e8 %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b6f5d6  call   $0x77b62824 %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b6f5db  xor    %edi %edi -> %edi
  0x77b6f5dd  mov    %edi -> 0xffffffdc(%ebp)[4byte]
  0x77b6f5e0  mov    0x08(%ebp)[4byte] -> %eax
  0x77b6f5e3  mov    0x18(%eax)[4byte] -> %edx
  0x77b6f5e6  mov    %edx -> 0xffffffd8(%ebp)[4byte]

call from :   0x77b6f85f ntdll.dll!LdrGetProcedureAddressEx+0x162 ??:0
        to :   0x77b66f58 ntdll.dll!RtlRunOnceBeginInitialize+0xf9 ??:0 

  0x77b6f85f  call   $0x77b66f58 %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b66f58  mov    0xffffffe4(%ebp)[4byte] -> %ecx
  0x77b66f5b  xor    %ebp %ecx -> %ecx
  0x77b66f5d  call   $0x77b62f0c %esp -> %esp 0xfffffffc(%esp)[4byte]
  0x77b66f62  jmp    $0x77b62869
  0x77b66f67  nop

2) After the call instruction, when calling someth system functions (such as memcpy), the command jmp ds:address goes that redirects to the func in system library which copying memory. What does this mean, why does not the redirection immediately to the address of the func in the library, if it is loaded to the prog and the address is known?

3) And how I can detect that call - is function call? For example, when I using indirect call I need to make sure, that I am jump to begin of the func. But func haven`t prologue. Can you advise any information resources about that?

  • One question per question please. – Ajay Brahmakshatriya Aug 31 '17 at 11:13
  • `sub esp, xxx` without `push ebp` is only when the local data area (automatic variables) is limited to some number of bytes (128 I believe) and depends on the CPU and compiler. It has nothing to do with optimizing compiler. I forgot the details. – Paul Ogilvie Aug 31 '17 at 11:16
  • See also https://stackoverflow.com/questions/43013693/compiler-using-local-variables-without-adjusting-rsp – Paul Ogilvie Aug 31 '17 at 11:23
  • Basically "no". Functions are higher abstraction [by programmer], but not required by the machine. If anyone dares to write weird piece of code, it can use `call`, `ret`, etc instructions in a way, which will make little sense when you will try to think about that piece of code as "functions". Most of the high-language compiled code will probably work close to your expectations in question, but hand written code can break any rules you can think of. So 3) would first require you to declare precisely what is "function call" for you. Then detecting **all** of them is NP-complete type of problem. – Ped7g Aug 31 '17 at 11:28
  • For example of non-standard usage of `call` (not as function call, but to load string address into register) check: https://stackoverflow.com/a/24438505/4271923 – Ped7g Aug 31 '17 at 11:30
  • 1
    @PaulOgilvie You are confusing things here. These 128 bytes form the *red zone,* but this is not relevant here as no red zone is used in 32 bit mode. You don't need to manipulate `ebp` to establish a stack frame, decrementing `esp` suffices to allocate memory on the stack. – fuz Aug 31 '17 at 11:33
  • @fuz, what about his statement "And prologue for optimizing compilers: `sub esp, xxx`? – Paul Ogilvie Aug 31 '17 at 19:06
  • @PaulOgilvie: With `-fno-omit-frame-pointer`, gcc will always make a stack frame even if it doesn't need any space to spill locals. https://godbolt.org/g/kZx2L7. gcc and clang only enable `-fomit-frame-pointer` at `-O1` or higher, so for those compilers it's not wrong to say that "unoptimized" code will always make stack frames. It is wrong to say that `sub esp, xxx` always appears in optimized code; you're right about that. (It's somewhat rare for 32-bit code to not need any space to spill anything, though, since gcc prefers not to reuse arg-passing memory as temporary storage.) – Peter Cordes Aug 31 '17 at 20:14
  • And no, it doesn't depend on the CPU and compiler; the availability of the red-zone depends on the target ABI. Different compilers targeting the same ABI have to agree on how everything works, regardless of what specific CPU (Haswell vs. Core2 vs. Ryzen). (Although in this case a compiler might not take advantage of the red-zone even if the OS does avoid clobbering it with signal handlers, since the whole point of the red-zone is that nothing else steps on it. It's not like struct layout or arg-passing that compilers have to do the same way. So I'm just being pedantic >.<) – Peter Cordes Aug 31 '17 at 20:17

0 Answers0