Это старая версия документа!
Соглашения - Calling Conventions на x86 и x64 с примерами и схемами
Основные calling conventions (x86, x64 Windows)
| Convention | Порядок аргументов | Как передаются | Кто чистит стек | Возврат значения | Имя функции | Где применяется |
|---|---|---|---|---|---|---|
| cdecl |Cправа налево |Стек |Caller |EAX |_func |C/varargs | |stdcall | Cправа налево | Стек | Callee | EAX | _func@N | WinAPI |
| __fastcall | R→L, 1 ECX, 2 EDX, потом стек | Регистр+стек | Callee | EAX | @func@N | Оптимизация |
| thiscall | this via ECX, потом стек | ECX/стек | Caller | EAX | манглинг | C++ методы |
| Win64 | N/A | RCX/RDX/R8/R9/Stack | Caller | RAX/XMM0 | func | Windows x64 |
[[:Документация:Ссылки на официальные источники|Официальные источники]]
ASCII-схема: Стек и аргументы (cdecl/stdcall, x86)
; До входа в функцию (cdecl/stdcall): ; (Сверху вниз — адреса растут) [ esp+12 ] b [ esp+8 ] a [ esp+4 ] return address [ esp ] сохранённый ebp [ esp-4 ] локальная переменная 1 [ esp-8 ] локальная переменная 2
ASCII-схема: Стек и аргументы (Win64, после sub rsp,28h)
; Сразу после sub rsp,28h до вызова функции: [ rsp+40 ] 6-й аргумент (если он есть) [ rsp+38 ] 5-й аргумент (если он есть) [ rsp+30 ] shadow r9 [ rsp+28 ] shadow r8 [ rsp+20 ] shadow rdx [ rsp+18 ] shadow rcx [ rsp+10 ] выравнивание/локалки [ rsp ] нижняя граница stack frame ; rcx, rdx, r8, r9 — содержат первые 4 аргумента
Пример: Более сложная функция (x86 cdecl)
// Функция: вычисляет сумму и произведение трёх чисел, возвращает сумму int example(int a, int b, int c) { int prod = a * b * c; int sum = a + b + c; if (prod > 100) return sum * 2; else return sum; }
example: push ebp mov ebp, esp sub esp, 8 ; 2 локалки: prod ([ebp-4]), sum ([ebp-8]) mov eax, [ebp+8] ; a imul eax, [ebp+12] ; a*b imul eax, [ebp+16] ; *c mov [ebp-4], eax ; prod mov eax, [ebp+8] add eax, [ebp+12] add eax, [ebp+16] mov [ebp-8], eax ; sum mov eax, [ebp-4] cmp eax, 100 jle short .Lelse mov eax, [ebp-8] add eax, [ebp-8] ; sum*2 jmp short .Lend .Lelse: mov eax, [ebp-8] ; sum .Lend: mov esp, ebp pop ebp ret
Пример: Более сложная функция (Win64 ABI, x64)
// Функция: возвращает среднее арифметическое 6 аргументов int avg6(int a, int b, int c, int d, int e, int f) { return (a + b + c + d + e + f) / 6; }
avg6: sub rsp, 28h ; Shadow Space mov eax, ecx ; a add eax, edx ; b add eax, r8d ; c add eax, r9d ; d add eax, [rsp+40h] ; e (5-й) add eax, [rsp+48h] ; f (6-й) mov ecx, 6 cdq idiv ecx ; eax = сумма / 6 add rsp, 28h ret
ASCII-графика: схема памятных мест после входа в avg6
+----------------------+
[ rsp+48 ] ---> | f (6-й аргумент) |
[ rsp+40 ] ---> | e (5-й аргумент) |
[ rsp+28 ] ---> | shadow r9 |
[ rsp+20 ] ---> | shadow r8 |
[ rsp+18 ] ---> | shadow rdx |
[ rsp+10 ] ---> | shadow rcx |
[ rsp ] ---> | (--- нижний rsp ---) |
+----------------------+
Пример: Использование Shadow Space для сохранения регистра
Если функция делает вызовы других функций, ей иногда нужно сохранять свои аргументы из rcx/rdx/r8/r9 в shadow space (например, для их восстановления после вызова, который мог бы их затереть):
func: sub rsp, 28h mov [rsp], rcx ; сохранить rcx в shadow space mov [rsp+8], rdx mov [rsp+16], r8 mov [rsp+24], r9 ... ; вызовы других функций, которые могут менять rcx, rdx, r8, r9 ... ; можно восстановить оригинальные аргументы отсюда add rsp, 28h ret
Важные ссылки
- OSDev: Calling conventions — большая сводка
Мнемоника для запоминания Win64 convention
RCX 1st RDX 2nd
| |
+----------+---------------+
|R8 3rd |R9 4th | 5-й и далее — только на стеке!
+----------+---------------+
Минимум 32 байта shadow space до любого вызова (для callee).
FAQ и заметки для реверса
- x64dbg: Если сразу после call идёт add rsp, 28h — это Win64 calling convention.
- Если стек выделяется через sub esp, X и используются push — классическая x86.
- При анализе vararg-функций (printf) всегда смотрите стек выше shadow space — там аргументы!
- Если не виден frame pointer (rbp/ebp), локальные обычно через rsp/esp и смещение.
- Проверяйте стек на выравнивание по 16 байтам в x64.