Невозможно зарегистрировать несколько выходных данных с помощью WriteConsoleA в программе сборки Windows 10 (64-разрядной версии)

Я изучаю разработку ассемблера в Windows 10 (64-разрядной версии) и использую IntelliJ IDEA для написания ассемблерного кода. Я пытаюсь зарегистрировать несколько выходных данных с помощью WriteConsoleA, но появляется только первое сообщение журнала. Второе сообщение журнала игнорируется.

Сначала я попытался реализовать арифметическую логику, чтобы получить сумму двух чисел. Это не сработало, и я попытался жестко запрограммировать значение и получить результат журнала, а затем понял эту проблему.

Вот моя подробная настройка и код, который я использую.

Среда:

  • Операционная система: Windows 10 (64-разрядная версия).
  • Ассемблер: НАСМ
  • Компилятор/компоновщик: MinGW-Win64

Код:

section .data
    sum_msg db 'Sum: ', 0
    sum db '15', 0

section .bss
    num_bytes_written resq 1

section .text
    global _start

    extern GetStdHandle
    extern WriteConsoleA
    extern ExitProcess

_start:
    ; Get the handle for stdout
    mov ecx, -11           ; STD_OUTPUT_HANDLE
    call GetStdHandle

    ; Check if handle is valid
    cmp rax, -1
    je .exit

    ; Write the "Sum: " message to stdout
    mov rcx, rax           ; handle to stdout
    lea rdx, [rel sum_msg] ; pointer to message
    mov r8d, 5             ; message length
    lea r9, [rel num_bytes_written]  ; pointer to number of bytes written
    call WriteConsoleA

    ; Write the hardcoded sum to stdout
    mov rcx, rax           ; handle to stdout
    lea rdx, [rel sum]     ; pointer to sum string
    mov r8d, 2             ; sum string length (including null terminator)
    lea r9, [rel num_bytes_written]  ; pointer to number of bytes written
    call WriteConsoleA

    ; Infinite loop to prevent immediate exit
.loop:
    nop
    jmp .loop

.exit:
    ; Exit
    xor ecx, ecx           ; exit code 0
    call ExitProcess

Команды: Собрать код:

nasm -f win64 sum.asm -o sum.obj

Код ссылки:

gcc -nostdlib -o sum.exe sum.obj -lkernel32

Ожидаемый результат: Sum: 15

Фактический результат: Sum:

Попытки исправить:

  • Проверено, что исходное сообщение («Сумма:») регистрируется правильно.
  • Добавлен цикл, гарантирующий, что программа не завершится немедленно, подтверждающий, что второй журнал игнорируется.
  • Попробовал изменить длину второго сообщения.
  • Подтверждено, что функция WriteConsoleA вызывается, но второе сообщение не печатается.
  • Протестировано с жестко запрограммированными значениями для второго сообщения журнала.
  • Пытался скомпилировать программу, переупорядочив WriteConsoleA (сверху: «15», снизу: «Сумма:»), и она записала результат «15».

Что именно мне нужно знать

  • Почему второй вызов WriteConsoleA не регистрирует сообщение?
  • Существуют ли особые требования или ограничения для многократного вызова WriteConsoleA в 64-разрядной программе сборки Windows?

Любая помощь или предложения будут очень признательны.

Похоже, вы передаете в WriteConsoleA только 4 параметра, но функция принимает 5. Кроме того, WriteConsoleA возвращает значение в eax, что означает, что он удаляет значение, сохраненное там GetStdHandle.

David Wohlferd 28.07.2024 04:24

Вы также вызвали все функции в смещенном стеке, не выделили теневое пространство и не объявили коды очистки.

Raymond Chen 28.07.2024 06:07

Соглашение о вызовах Windows x64 описано здесь.

David Wohlferd 28.07.2024 06:53

Спасибо, что указали на это. Я обновил свой код, чтобы гарантировать, что все пять параметров передаются в WriteConsoleA. Кроме того, я сохранил значение GetStdHandle, чтобы избежать его перезаписи возвращаемым значением WriteConsoleA. Эта настройка решила проблему.

kavi castelo 28.07.2024 07:54
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Превращаем комментарии в ответ:

Здесь есть несколько проблем. Вероятно, наиболее важным является то, что возвращаемое значение вашего первого вызова WriteConsoleA перезаписывает значение eax, в котором вы сохраняете дескриптор, возвращенный из GetStdHandle. В результате второй вызов WriteConsoleA использует неверный дескриптор.

Во-вторых, вы передаете только 4 параметра в процедуру, которая принимает 5. Хотя это может сработать, поскольку параметр 5 зарезервирован (и, следовательно, вероятно, полностью не используется), это риск, поскольку невозможно узнать, что вызываемая функция может делать с это или пространство стека, где оно должно храниться.

В-третьих, вы не можете выделить теневое пространство для параметров (обсуждается здесь). Опять же, вам это может сойти с рук в зависимости от того, как работает вызываемая функция. Но если он потерпит неудачу, сбой может произойти через сотни инструкций, что очень затруднит его отслеживание.

Аналогично: стек всегда будет поддерживаться по 16-байтовому выравниванию, за исключением пролога (например, после отправки адреса возврата). Вы вызываете функции со стеком, не выровненным по границам 16 байт.

Рэймонд также ссылается на коды очистки. Хотя я сам их не указываю, я обычно не использую обработку исключений. Вероятно, ужасные вещи произойдут, если вы опустите их в коде, который выполняет throw, или даже находится в стеке вызовов при срабатывании исключения.

Написание кода, который не соответствует должным образом соглашениям о вызовах, — ужасная идея. Может показаться, что это работает, но это портит вещи, которые вы не можете легко увидеть. Проще сделать все правильно с первого раза, чем потом тратить время на отслеживание.

Спасибо за организованный и разъясненный ответ :)

kavi castelo 29.07.2024 22:06

Другие вопросы по теме

Похожие вопросы

Не удалось скомпилировать точный листинг сборки, созданный компилятором
Окно не получает фокус даже при перемещении вверх
После перемещения файла в другое место и последующего создания файла с тем же именем в исходном месте время создания неверно
Java Graphics2D.drawImage() отображает размытое изображение только в Windows с масштабированием 125% и только в версиях Java после Java 8
Кто-нибудь знает, почему моя переменная (x) изменила свое значение с 3 на 0 (1 после x++) в этой программе? Я пытаюсь создать систему регистрации, но она перезаписывает
Как я могу установить текстовое значение в соответствии с позицией
Можно ли определить функцию C++, в которой один параметр передается через регистр EAX?
Получение имени пользователя Windows в формате «Имя Фамилия», когда пользователь является частью группы
Мой чат-сервер Python не отправляет сообщения должным образом
Собственный диалог открытия файла в Windows Ruby