Hello world ассемблер linux

Разработка на ассемблере в Linux

В папке у нас будет бинарный файл fasm, который мы можем использовать для компиляции. Для удобства вы можете создать симлинк на него:

sudo ln -s /home/username/fasm/fasm /usr/local/bin

ald и shed устанавливаются не сложнее:

$ ./configure
$ make
# make install

В итоге у нас будет 3 полезных инструмента для программирования на ассемблере.

Системные вызовы

Как и большинство других операционных систем, Linux предоставляет т.н. API — набор полезных для программиста функций. В большинстве случаев вызов системной функции производится с помощью прерывания 80h. Следует отметить, что Linux используется fastcall-конвенция передачи параметров. Согласно ей параметры передаются через регистры (в windows, например, используется stdcall, где параметры передаются через стек). Номер вызываемой функции кладется в eax, а параметры в регистры:

1 / ebx
2 / ecx
3 / edx
4 / esi
5 / edi
6 / ebp

Как видите все не так сложно. Узнать номер системной функции, ее описание и параметры можно, хотя бы здесь. Возьмем, к примеру sys_exit . Как можно увидеть на той странице у нее есть один параметр — код возврата и она имеет порядковый номер 1. Таким образом мы можем вызвать ее следующим кодом:

mov eax, 1 ; 1 — номер системной функции
sub ebx, ebx ; Обнуляем регистр (можно было записать mov ebx, 0)
int 80h ; Вызываем прерывание 80h

Hello, World!

Ну что же. Писать мы ничего не будем, т.к. за нас все написано 🙂 В папке fasm/examples/elfexe есть файл hello.asm, в котором находится следующий код:

; fasm demonstration of writing simple ELF executable

format ELF executable 3
entry start

segment readable executable

mov eax,4
mov ebx,1
mov ecx,msg
mov edx,msg_size
int 0x80

mov eax,1
xor ebx,ebx
int 0x80

segment readable writeable

msg db ‘Hello world!’,0xA
msg_size = $-msg

Как видите здесь вызываются 2 системных функции — sys_write (с порядковым номером 4) и sys_exit . sys_write принимает 3 параметра — дескриптор потока вывода (1 — stdout), указатель на строку и размер строки. Сам номер функции, как уже говорилось, мы должны положить в eax. Функцию sys_exit мы уже разобрали. Скомпилировать это чудо можно так: fasm hello.asm (но не обязательно, т.к. там же, где лежит исходник, есть и бинарник).

Посмотрим, что внутри

Думаю, что самое время заглянуть в наш бинарник. Для начала воспользуемся шестнадцатеричным редактором, чтобы посмотреть что у нас получилось. Выполним команду:

image

Мы видим всю нашу программу, данные, elf-заголовок. Неплохо? Теперь мы посмотрим на нашу программу в отладчике. Наберем в консоли:

Нас должна поприветствовать строка с предложением ввести команду. Список команд вы можете узнать, набрав help или получить помощь по отдельной команде, набрав help command . Дизассемблировать нашу программу можно командой disassemble (или ее алиас — » d «). Вы увидете дизассемблированный листинг вашей программы. Слева — адрес, справа — сама команда, а посередине — опкод команды.

Читайте также:  Skipfish on kali linux

Получить дамп можно командой dump (странно, но ее нет в выводе команды help ).

image

Теперь попробуем поработать с командой next . Выполните ее и в ответ вам покажут значения регистров, установленные флаги, а так же адрес, опкод и дизассемблированную команду, которая должна выполниться следующей. Попробуйте выполнять команды и следите за изменением флагов и регистров. После вызова первого прерывания у вас на экране должна появиться надпись «Hello world!».

Целью данной статьи было показать основы программирования на ассемблере в linux, а не программирования на ассемблере в общем. Надеюсь, что вы подчерпнули для себя что-то полезное от сюда.

PS. Первая статья на хабре.

Источник

Записки программиста

Написание и отладка кода на ассемблере x86/x64 в Linux

Сегодня мы поговорим о программировании на ассемблере. Вопрос «зачем кому-то в третьем тысячелетии может прийти в голову писать что-то на ассемблере» раскрыт в заметке Зачем нужно знать всякие низкоуровневые вещи, поэтому здесь мы к нему возвращаться не будем. Отмечу, что в рамках поста мы сосредоточимся на вопросе компиляции и отладки программ на ассемблере. Сам же язык ассемблера заслуживает отдельного большого поста, а то и серии постов.

Если вы знаете ассемблер, то любая программа для вас — open source.

Народная мудрость.

Введение

Существует два широко используемых ассемблерных синтаксиса — так называемые AT&T-синтаксис и Intel-синтаксис. Они не сильно друг от друга отличаются и легко переводятся один в другой. В мире Windows принято использовать синтаксис Intel. В мире *nix систем, наоборот, практически всегда используется синтаксис AT&T, а синтаксис Intel встречается крайне редко (например, он используется в утилите perf). Поскольку Windows, как известно, не существует, далее мы сосредоточимся на правильном AT&T-синтаксисе 🙂

Компиляторов ассемблера существует много. Мы будем использовать GNU Assembler (он же GAS, он же /usr/bin/as). Скорее всего, он уже есть вашей системе. К тому же, если вы пользуетесь GCC и собираетесь писать ассемблерные вставки в коде на C, то именно с этим ассемблером вам предстоит работать. Из достойных альтернатив GAS можно отметить NASM и FASM.

Наконец, язык ассемблера отличается в зависимости от архитектуры процессора. Пока что мы сосредоточимся на ассемблере для x86 (он же i386) и x64 (он же amd64), так как именно с этими архитектурами приходится чаще всего иметь дело. Впрочем, ARM тоже весьма распространен, главным образом на телефонах и планшетах. Еще из сравнительно популярного есть SPARC и PowerPC, но шансы столкнуться с ними весьма малы. Отмечу, что x86 и x64 можно было бы рассматривать отдельно, но эти архитектуры во многом похожи, поэтому я не вижу в этом большого смысла.

«Hello, world» на int 0 x80

Рассмотрим типичный «Hello, world» для архитектуры x86 и Linux:

.data
msg :
. ascii «Hello, world!\n»
. set len , . — msg

. globl _start
_start :
# write
mov $ 4 , % eax
mov $ 1 , % ebx
mov $msg , % ecx
mov $len , % edx
int $ 0x80

# exit
mov $ 1 , % eax
xor % ebx , % ebx
int $ 0x80

Читайте также:  Настроить виртуальную машину virtualbox linux

# Или: gcc -m32 -c hello-int80.s
as —32 hello-int80.s -o hello-int80.o
ld -melf_i386 -s hello-int80.o -o hello-int80

Коротко рассмотрим первые несколько действий, выполняемых программой: (1) программа начинает выполнение с метки _start, (2) в регистр eax кладется значение 4, (3) в регистр ebx помещается значение 1, (4) в регистр ecx кладется адрес строки, (5) в регистр edx кладется ее длина, (6) происходит прерывание 0 x80. Так в мире Linux традиционно происходит выполнение системных вызовов. Конкретно int 0 x80 считается устаревшим и медленным, но из соображений обратной совместимости он все еще работает. Далее мы рассмотрим и более новые механизмы.

Нетрудно догадаться, что eax — это номер системного вызова, а ebx, ecx и edx — его аргументы. Какой системный вызов имеет какой номер можно подсмотреть в файлах:

# для x86
/ usr / include / x86_64-linux-gnu / asm / unistd_32.h
# для x64
/ usr / include / x86_64-linux-gnu / asm / unistd_64.h

Следующая строчка из файла unistd_32.h:

… как бы намекает нам, что производится вызов write. В свою очередь, из man 2 write мы можем узнать, какие аргументы этот системный вызов принимает:

ssize_t write ( int fd , const void * buf , size_t count ) ;

То есть, рассмотренный код эквивалентен:

Затем аналогичным образом производится вызов:

В общем случае системный вызов через 0 x80 производится по следующим правилам. Регистру eax присваивается номер системного вызова из unistd_32.h. До шести аргументов помещаются в регистры ebx, ecx, edx, esi, edi и ebp. Возвращаемое значение помещается в регистр eax. Значения остальных регистров при возвращении из системного вызова остаются прежними.

Выполнение системного вызова через sysenter

Начиная с i586 появилась инструкция sysenter, специально предназначенная (чего нельзя сказать об инструкции int) для выполнения системных вызовов.

Рассмотрим пример использования ее на Linux:

.data
msg :
. ascii «Hello, world!\n»
len = . — msg

_start :
# write
mov $ 4 , % eax
mov $ 1 , % ebx
mov $msg , % ecx
mov $len , % edx
push $write_ret
push % ecx
push % edx
push % ebp
mov % esp , % ebp
sysenter

write_ret :
# exit
mov $ 1 , % eax
xor % ebx , % ebx
push $exit_ret
push % ecx
push % edx
push % ebp
mov % esp , % ebp
sysenter

Сборка осуществляется аналогично сборке предыдущего примера.

Как видите, принцип тот же, что при использовании int 0 x80, только перед выполнением sysenter требуются поместить в стек адрес, по которому следует вернуть управление, а также совершить кое-какие дополнительные манипуляции с регистрами. Причины этого более подробно объясняются здесь.

Инструкция sysenter работает быстрее int 0 x80 и является предпочтительным способом совершения системных вызовов на x86.

Выполнение системного вызова через syscall

До сих пор речь шла о 32-х битных программах. На x64 выполнение системных вызовов осуществляется так:

.data
msg :
. ascii «Hello, world!\n»
. set len , . — msg

. globl _start
_start :
# write
mov $ 1 , % rax
mov $ 1 , % rdi
mov $msg , % rsi
mov $len , % rdx
syscall

# exit
mov $ 60 , % rax
xor % rdi , % rdi
syscall

Собирается программа таким образом:

Принцип все тот же, но есть важные отличия. Номера системных вызовов нужно брать из unistd_64.h, а не из unistd_32.h. Как видите, они совершенно другие. Так как это 64-х битный код, то и регистры мы используем 64-х битные. Номер системного вызова помещается в rax. До шести аргументов передается через регистры rdi, rsi, rdx, r10, r8 и r9. Возвращаемое значение помещается в регистр rax. Значения, сохраненные в остальных регистрах, при возвращении из системного вызова остаются прежними, за исключением регистров rcx и r11.

Интересно, что в программе под x64 можно одновременно использовать системные вызовы как через syscall, так и через int 0 x80.

Читайте также:  Gigabyte rgb fusion linux

Отладка ассемблерного кода в GDB

Статья была бы не полной, если бы мы не затронули вопрос отладки всего этого хозяйства. Так как мы все равно очень плотно сидим на GNU-стэке, в качестве отладчика воспользуемся GDB. По большому счету, отладка не сильно отличается от отладки обычного кода на C, но есть нюансы.

Например, вы не можете так просто взять и поставить брейкпоинт на процедуру main. Как минимум, у вас попросту нет отладочных символов с информацией о том, где эту main искать. Решение заключается в том, чтобы самостоятельно определить адрес точки входа в программу и поставить брейкпоинт на этот адрес:

Источник

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

A simplistic Hello World for x64 nasm w/ intel syntax

KyleNehman/Linux-ASM-Hello-World

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

A simplistic Hello World for nasm w/ intel syntax

The makefile assumes you’ve got nasm and ld installed, and uses the ‘-m elf_i386’ linker flag for my own pc, you’ll probably have to adjust this.

git clone https://github.com/KyleNehman/Linux-ASM-Hello-World.git cd Linux-ASM-Hello-World/ make make run 

What’s this crazy code do? (Breakdown)

This program is more or less the equivalent of the following python:

Let’s go line by line, since this is for total beginners.

 section .data msg: db "Hello, world!", 0x0a msgLen: equ $-msg 
 section .text global _start _start: mov ecx, msg mov edx, msgLen call print call exit 
 print: push eax push ebx mov eax, 4 mov ebx, 1 int 0x80 pop ebx pop eax ret 
 exit: mov eax, 1 mov ebx, 0 int 0x80 

About

A simplistic Hello World for x64 nasm w/ intel syntax

Источник

Оцените статью
Adblock
detector