Содержание

Соглашения - Calling Conventions на x86 и x64 с примерами и схемами

Термины и основы


x86: Основные calling conventions

Convention Порядок аргументовКак передаютсяКто чистит стекВозврат значенияИмя функцииГде применяется
cdecl |Cправа налево |Стек |Caller |EAX |_func |C/varargs | |stdcall Cправа налево Стек Callee EAX _func@N WinAPI

* Аргументы: справа налево через стек.

Пример (cdecl, x86):

int sum(int a, int b) { return a + b; }
push 2          ; b
push 1          ; a
call sum
add esp, 8      ; очищаем стек
sum:
    push ebp
    mov ebp, esp
    sub esp, 4          ; локальная переменная
    mov eax, [ebp+8]    ; a
    add eax, [ebp+12]   ; b
    mov esp, ebp
    pop ebp
    ret

__stdcall

Пример (stdcall, x86):

push 2
push 1
call sum@8
; нет add esp, 8!
...
sum@8:
    push ebp
    mov ebp, esp
    sub esp, 4
    ...логика...
    mov esp, ebp
    pop ebp
    ret 8              ; callee очищает стек

__fastcall

Пример (fastcall, x86):

push 3          ; 3-й аргумент
mov edx, 2      ; 2-й
mov ecx, 1      ; 1-й
call sum@12
...             ; нет add esp
sum@12:
    push ebp
    mov ebp, esp
    ...
    mov esp, ebp
    pop ebp
    ret 0Ch

thiscall

Пример (thiscall, x86):

push 2            ; b
push 1            ; a
mov ecx, obj      ; this
call Foo::bar
add esp, 8

Детали реализации (prologue/epilogue) на x86

Prologue (вход функции)

push ebp              ; сохранить старый frame pointer
mov ebp, esp          ; установить новый frame pointer (ebp)
sub esp, X            ; выделить X байт под локальные переменные

Epilogue (выход из функции)

mov esp, ebp
pop ebp
ret / ret N           ; ret N для stdcall/fastcall

Локальные переменные и аргументы

Пример с комментариями

sum:
    push ebp                ; сохранить ebp
    mov ebp, esp            ; новый frame pointer
    sub esp, 8              ; 2 локальных переменных (например)
    mov eax, [ebp+8]        ; первый аргумент a
    add eax, [ebp+12]       ; второй b
    mov [ebp-4], eax        ; локалка c
    ...
    mov esp, ebp
    pop ebp
    ret / ret N

x64 (Win64 ABI): подробный разбор

Передача аргументов

Prologue (вход функции)

sub rsp, 28h            ; shadow space (32 байта) + выравнивание (обычно)
; если нужны локальные — sub rsp, больше

Epilogue (выход функции)

add rsp, 28h
ret

Локальные переменные

Дизассемблированный x64-пример (MSVC / x64dbg)

sum:
    sub     rsp, 28h              ; Shadow space
    mov     eax, ecx              ; 1-й аргумент
    add     eax, edx              ; 2-й
    add     eax, r8d              ; 3-й
    add     eax, r9d              ; 4-й
    shl     eax, 1                ; умножение на 2
    add     rsp, 28h
    ret

Пример вызова (caller, x64)

mov     ecx, 1        ; первый аргумент
mov     edx, 2        ; второй
mov     r8d, 3        ; третий
mov     r9d, 4        ; четвертый
sub     rsp, 28h      ; shadow space
; здесь push или mov в [rsp+40h] пятый/шестой/и т.д.
call    sum
add     rsp, 28h

Важные примечания Win64 ABI

Отличие от Linux x64 (System V ABI)

Как узнать соглашение вызова по дизассемблированному коду (x64dbg)

Краткая шпаргалка по стеку (Win64, вызов функции с 6 аргументами)

Значение Комментарий
———-————–————————————————–
rsp+0 Shadow RCX 1-й аргумент (копия)
rsp+8 Shadow RDX 2-й
rsp+16 Shadow R8 3-й
rsp+24 Shadow R9 4-й
rsp+32 5-й 5-й аргумент (через стек)
rsp+40 6-й 6-й аргумент

Мини-конспект для реверса Win64

Примеры для практики

Пример 1: Простая функция (Win64)

int foo(int a, int b) { return a+b; }
foo:
    mov eax, ecx   ; a
    add eax, edx   ; b
    ret
mov ecx, 5
mov edx, 6
sub rsp, 28h      ; выделил shadow space + выравнивание
call foo
add rsp, 28h

Пример 2: С vararg (Win64, printf)

Заключение

Win64 calling convention: