Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Вопрос Вызов внешней функции через shellcode — не сохраняется return value

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
602
Реакции
16
Суть проблемы

Пытаюсь реализовать внешний вызов функции (x64) в таргете через самописный шеллкод. Сама функция отрабатывает, вызов проходит успешно, но возник затык на элементарном: не получается сохранить возвращаемое значение (return value) в заранее выделенный буфер (allocation2). Данные в память просто не записываются.

Код:
Expand Collapse Copy
auto shellcode = make_shellcode(
    "\x48\x83\xEC\x10",                // sub rsp, 0x10 (reserve 16 bytes of stack space)
    "\x48\xB8", uint64_t(address),     // mov rax, address of my_malloc
    "\x48\x89\xC7",                    // mov rdi, rax (rdi holds the address of my_malloc)
    "\x48\xB9", uint64_t(size),        // mov rcx, size (first argument for my_malloc)
    "\x48\xBA", uint64_t(alignment),   // mov rdx, alignment (second argument for my_malloc)
    "\xFF\xD7",                        // call rdi (call my_malloc)
    "\x48\x89\xC7",                    // mov rdi, rax (move return value to rdi)
    "\x48\xB8", uint64_t(allocation2), // mov rax, address of allocation2
    "\x48\x89\x00",                    // mov [rax], rdi (store return value at allocation2)
    "\x48\x83\xC4\x10",                // add rsp, 0x10 (restore stack pointer)
    "\xC3"                             // ret (return from the shellcode)
);

TB5N55K.png


Технические нюансы и подозрения

  1. Shadow Space. Это классическая проблема в x64. По соглашению вызова Microsoft (fastcall), вызывающий обязан выделить минимум 32 байта (0x20) под "теневое пространство" на стеке, даже если аргументов меньше. Здесь выделено всего 16 байт (0x10), что может приводить к затиранию данных самой функцией или порче стека.
  2. Регистр RDI. Почему результат перекидывается из RAX в RDI перед записью? В коде RAX затирается адресом allocation2, и это логично, но стоит убедиться, что вызываемая функция (my_malloc) не ожидает чего-то специфического в регистрах, которые вы используете.
  3. Права доступа. Очевидный момент, но всё же — проверьте, с какими флагами выделена память под allocation2. Если нет PAGE_READWRITE, то инструкция mov [rax], rdi просто молча уйдет в небытие или вызовет исключение, которое вы не ловите.
  4. Выравнивание стека. Перед инструкцией CALL стек должен быть выровнен по границе 16 байт. На входе в шеллкод (после CALL от инжектора) стек уже смещен на 8 байт (адрес возврата). Манипуляции с sub rsp должны это учитывать.

Риски и траблшутинг

Если используете CreateRemoteThread для запуска, имейте в виду, что логика работы со стеком может отличаться от обычного вызова. Советую навесить отладчик (x64dbg) на целевой процесс, поставить бряк на точку входа шеллкода и пошагово посмотреть, что лежит в RAX после вызова и куда на самом деле указывает адрес из вашего кода.

Интересно послушать мысли тех, кто плотно сидит на внешних вызовах через шеллкоды @KVANTOR815 — хватает ли тут 16 байт под выравнивание или стоит копать в сторону корректного пролога/эпилога?
 
Суть проблемы

Пытаюсь реализовать внешний вызов функции (x64) в таргете через самописный шеллкод. Сама функция отрабатывает, вызов проходит успешно, но возник затык на элементарном: не получается сохранить возвращаемое значение (return value) в заранее выделенный буфер (allocation2). Данные в память просто не записываются.

Код:
Expand Collapse Copy
auto shellcode = make_shellcode(
    "\x48\x83\xEC\x10",                // sub rsp, 0x10 (reserve 16 bytes of stack space)
    "\x48\xB8", uint64_t(address),     // mov rax, address of my_malloc
    "\x48\x89\xC7",                    // mov rdi, rax (rdi holds the address of my_malloc)
    "\x48\xB9", uint64_t(size),        // mov rcx, size (first argument for my_malloc)
    "\x48\xBA", uint64_t(alignment),   // mov rdx, alignment (second argument for my_malloc)
    "\xFF\xD7",                        // call rdi (call my_malloc)
    "\x48\x89\xC7",                    // mov rdi, rax (move return value to rdi)
    "\x48\xB8", uint64_t(allocation2), // mov rax, address of allocation2
    "\x48\x89\x00",                    // mov [rax], rdi (store return value at allocation2)
    "\x48\x83\xC4\x10",                // add rsp, 0x10 (restore stack pointer)
    "\xC3"                             // ret (return from the shellcode)
);

TB5N55K.png


Технические нюансы и подозрения

  1. Shadow Space. Это классическая проблема в x64. По соглашению вызова Microsoft (fastcall), вызывающий обязан выделить минимум 32 байта (0x20) под "теневое пространство" на стеке, даже если аргументов меньше. Здесь выделено всего 16 байт (0x10), что может приводить к затиранию данных самой функцией или порче стека.
  2. Регистр RDI. Почему результат перекидывается из RAX в RDI перед записью? В коде RAX затирается адресом allocation2, и это логично, но стоит убедиться, что вызываемая функция (my_malloc) не ожидает чего-то специфического в регистрах, которые вы используете.
  3. Права доступа. Очевидный момент, но всё же — проверьте, с какими флагами выделена память под allocation2. Если нет PAGE_READWRITE, то инструкция mov [rax], rdi просто молча уйдет в небытие или вызовет исключение, которое вы не ловите.
  4. Выравнивание стека. Перед инструкцией CALL стек должен быть выровнен по границе 16 байт. На входе в шеллкод (после CALL от инжектора) стек уже смещен на 8 байт (адрес возврата). Манипуляции с sub rsp должны это учитывать.

Риски и траблшутинг

Если используете CreateRemoteThread для запуска, имейте в виду, что логика работы со стеком может отличаться от обычного вызова. Советую навесить отладчик (x64dbg) на целевой процесс, поставить бряк на точку входа шеллкода и пошагово посмотреть, что лежит в RAX после вызова и куда на самом деле указывает адрес из вашего кода.

Интересно послушать мысли тех, кто плотно сидит на внешних вызовах через шеллкоды @KVANTOR815 — хватает ли тут 16 байт под выравнивание или стоит копать в сторону корректного пролога/эпилога?
ты сам на свой же и вопрос ответил xd
 
Назад
Сверху Снизу