Я недавно пытался реализовать динамические функции на C++ с использованием шестнадцатеричных эквивалентов буфера и RAW разных операторов сборки. Для иллюстрации простого прыжка:Встроенная сборка - cdecl и подготовка стека
byte * buffer = new buffer[5];
*buffer = '0xE9'; // Hex for jump
*(uint*)(buffer + 1) = 'address destination';
Я не испытывал в сборке, но я знаю достаточно, чтобы создать самые простые функции. Сейчас я создаю функции cdecl в необработанной памяти. Проблема в том, что я не знаю, сколько я хочу нажать на стек (для памяти) с sub
. Давайте рассмотрим эту функцию в качестве примера:
int MyTest(int x, int y) { return x + y; }
long TheTest(int x, int y)
{
return MyTest(x, 5);
}
08048a20 <_Z6TheTestii>:
_Z6TheTestii():
8048a20: 55 push %ebp
8048a21: 89 e5 mov %esp,%ebp
8048a23: 83 ec 18 sub $0x18,%esp
8048a26: c7 44 24 04 05 00 00 movl $0x5,0x4(%esp)
8048a2d: 00
8048a2e: 8b 45 08 mov 0x8(%ebp),%eax
8048a31: 89 04 24 mov %eax,(%esp)
8048a34: e8 c2 ff ff ff call 80489fb <_Z6MyTestii>
8048a39: c9 leave
8048a3a: c3 ret
Как вы можете видеть, первый является C++ код и ниже является ASM из функции «TheTest». Можно сразу заметить, что стек вытолкнут на 24 (0x18) байта (как уже упоминалось ранее, я не испытываю использования сборки, поэтому я не могу использовать правильные термины и/или быть полностью прав). Для меня это не имеет никакого смысла. Почему требуется 24 байта, когда используются только 2 разных целых числа? Используется переменная «x», которая равна 4 байтам, и значение «5», которое также использует 4 байта (помните, что это cdecl, поэтому вызывающая функция берет память о функции аргументов) не составляет 24. ...
Теперь вот дополнительный пример, который делает меня действительно задуматься о выходе сборки:
int NewTest(int x, char val) { return x + val; }
long TheTest(int x, int y)
{
return NewTest(x, (char)6);
}
08048a3d <_Z6TheTestiiii>:
_Z6TheTestiiii():
8048a3d: 55 push %ebp
8048a3e: 89 e5 mov %esp,%ebp
8048a40: 83 ec 08 sub $0x8,%esp
8048a43: c7 44 24 04 06 00 00 movl $0x6,0x4(%esp)
8048a4a: 00
8048a4b: 8b 45 08 mov 0x8(%ebp),%eax
8048a4e: 89 04 24 mov %eax,(%esp)
8048a51: e8 ca ff ff ff call 8048a20 <_Z7NewTestic>
8048a56: c9 leave
8048a57: c3 ret
Единственное различие здесь (за исключением значений) является тот факт, что я использую «полукокса '(1 байт) вместо целого. Если мы посмотрим на код сборки, это заставит указатель стека только на 8 байтов. Это разница в байт из предыдущего примера. Как человек из C++ и вне его, не знаю, что происходит. Я был бы очень признателен, если бы кто-нибудь мог просветить меня по этому вопросу!
ПРИМЕЧАНИЕ: Причина, по которой я размещаю здесь вместо чтения книги ASM, заключается в том, что мне нужно использовать сборку для этой одной функции. Так что я не хочу, чтобы прочитать целую книгу для 40 строк кода ...
EDIT: Я также не заботиться о платформе-зависимости, я только заботу о Linux 32bit :)
Почему вы не используете libffi? –
@DavidHeffernan, это не весело :) –
@Elliott Почему вы не используете отладчик и не видите, как выглядит стек стека внутри 'TheTest', и посмотрите, для чего используется дополнительное пространство? –