Вы находитесь на странице: 1из 9

+----------------------------- Assembly Para Linux i386 na sintaxe AT&T ------------------------------+

| Autor: Felipe Okumura Sanches |


| AKA: ryuuu |
| Documento criado a partir do tutorial: packetstormsecurity.org/papers/general/asm_linux-i386.pdf |
+-----------------------------------------------------------------------------------------------------+

--- Programas usados: 'as' e 'ld'.


* todos arquivos devem ser salvo na extensão '.s'
* as: Cria o object file, especificando a extensão do arquivo.
- Exemplo: # as -o code.o code.s

* ld: Linka o arquivo, ou seja, cria o executável.


- Exemplo: # ld -o code code.o

--- Modelo de memória.


* A memória trabalha com base hexadecimal.
-------------------
| .DATA |
| (Stack ou Heap) |
-------------------
| .BSS |
| (Heap) |
-------------------
| .TEXT |
| (ASM) |
-------------------

Onde:
.DATA -> É responsável por salvar o endereço de retorno da subrotinas do programa, passar parâ-
metros para funções, criar variáveis locais, ou seja, ela guarda os dados do programa.
.BSS -> É para dados indefinidos, responsável por armazenar dados alocados dinamicamente e en-
dereços de variáveis estáticas.
.TEXT -> Aqui ficam as instruções assembly, onde será passado para o processador, e o mesmo as
executará.
Stack -> (ou pilha) é uma ára da memória responsável por armazenar o endereço das subrotinas,
passar argumentos (dados) para as funções, armazenar variáveis locais, etc. A Stack
usa o conceito LIFO (Last In, First Out), ou seja, pense numa pilha de livros, o últi-
mo que você colocar na pilha, será o primeiro a sair da mesma.
Heap -> (ou pilha) é uma área da memória que é utilizada por uma aplicação e é alocada dinami-
camente em tempo de execução. Aqui são armazenados variáveis estáticas (globais, cons-
tantes e variáveis definidas "static", em linguagem C), ou seja, variáveis que permane-
cem com o mesmo valor até o final do programa.

--- Introdução aos registradores.

* O que são?
São blocos de memória do processador que são usados para armazenar informações. Antes
dos processadores i386, os registradores eram de 16 bits, agora com essa arquitetura,
foi criado novos registradores com maior capacidade, os de 32 bits.

* Os registradores de uso geral, para armazenamento de valores, são:

- EAX - Extended Acumullator ( Registrador acumulador extendido).


- EBX - Extended Base ( Registrador de base extendido).
- ECX - Extended Counter ( Registrador contador extendido).
- EDX - Extended Data ( Registrador de dados extendido).

- Esses registradores podem ser usados para armazenamento de valor, mas todos registradores tem
uma função específica, depende do seu uso.
- Esses registradores são de 32 bits, agora lhe pergunto: Como eu faço para criar um programa
de 16 bits ou 8 bits?
- É simples. Os registradores de 32 bits são feitos de dois registradores de 16 bits, e
esses de 16 bits são feitos de dois registradores de 8 bits, que são:
+ AX (16 bits, parte alta),
+ AX (16 bits, parte baixa),
+ AH (8 bits, parte alta - H de High) e
+ AL (8 bits, parte baixa - L de Low).
- Lembrando que somente a parte alta do AX é formada pelo AL e AH.
- Veja a ilustração:
* Essa ilustração vale para os outros 3
registradores, o EBX, ECX e EDX.
---------------------------------------------------------------------------------
| EAX |
---------------------------------------------------------------------------------
| | AX (parte alta) |
---------------------------------------------------------------------------------
| | AH (high) | AL (low) |
---------------------------------------------------------------------------------

- Um exemplo para entender:


- Se adicionarmos o valor 0x12345678 para EAX, ficaria:
- EAX = 0x12345678
- AX (parte alta) = 0x1234
- AH = 0x12
- AL = 0x34

--- Registradores gerais.

EAX -> Pode ser usado para armazenar dados. É usado mais em operações aritméticas, operações de
entrada e saída de dados ou para chamadas de interrupção. Ele é indispensável quando te-
mos que realizar chamadas de sistema (system call).
EBX -> Pode ser usado para armazenar dados. É usado como offset (deslocamento daquele intervalo
de memória). Esses registradores são a base dos registradores e são usados como ponteiro
base para o acesso a memória. São usados para passar argumentos de chamada de sistema.
São usados também para guardar o valor de retorno de uma interrupção.
ECX -> Pode ser usado para armazenar dados e são contadores.
EDX -> Pode ser usado para armazenar dados. Eles podem ser usados para operações aritméticas,
chamadas de interrupção e algumas operações de I/O (Input/Output).
ESI -> (Extended Source Index) -> Usado para apontar para as instruções.
EDI -> (Extended Destiny Index) -> Pode ser usado como registrador de offset.
EIP -> (Extended Instruction Pointer) -> É um offset do segmento da próxima instrução a ser
executada (não pode ser acessado por qualquer instrução).

--- Registradores de segmento.

Não podem ser acessados diretamente, sendo usado mais como origem. Os registradores são:
- CS (Code Segment) -> Armazena os códigos, que corresponde a uma parte da memória, a-
pontando sempre para a próxima instrução.
- DS (Data Segment) -> Representa a parte da memória onde os dados são armazenados.
- ES (Extra Segment) -> Muito usado como segmento destino em operações de transferência
e movimentação de dados.
- SS (Stack Segment) -> É o segmento onde está o stack. A CPU usa ele para armazenar
endereços de retorno de sub-rotinas.

--- Registradores de Indexação do Stack

EBP (Extended Base Pointer) -> Aponta para a base da pilha.


ESP (Extended Stack Pointer) -> Aponta para a posição atual da memória stack (topo). Ele é off-
set de SS.

--- Introdução ao Assembly.

Sintaxe: (instrução) (origem), (destino) # (comentário)

# -> Comentário.
% -> Indicador de registrador.
$ -> indicador de valor.
() -> Indica o endereço onde está armazenado a informação, exemplo: mov $0x42, (%EAX) # Endere-
ço armazenado em EAX <- 42, ou seja, não é o EAX que recebe o valor, mas sim o endereço
que está armazenado nele.

--- Instruções em Assembly.

* Sufixos de alguns comandos:


b -> byte (1 byte -> 8 bits));
w -> word (2 bytes -> 16 bits);
l -> long (4 bytes -> 32 bits);

- São utilizados para indicar o tamanho que será utilizado.

* Instruções de movimentação:
mov -> Move o valor para um endereço/registrador, ex: mov $0x00, %EDX # EDX <- 00.
lea -> Faz um add, e logo em seguida faz um mov. Ex: lea %EBX, %EDX # EDX <- EDX + EBX,
desmembrando essa operação, ficaria: add %EBX, %EDX, em seguida, mov %EBX, %EDX.
lds -> É usado para carregar dois registradores de uma vez. Ex: CS:EBX.
les -> É usado para carregar dois registradores com Dword (32 bits) para o endereço a-
pontado por DS:SI.
xchg -> Troca os valores entre dois registradores,
ex: xchg %EAX, %EBX # EBX <- EAX e EAX <- EBX (troca simultaneamente).
push -> Coloca determinado valor em cima da pilha. Ex: push $0xbf
pop -> Retira o valor que está em cima da pilha. Usando o exemplo de cima, pop $0xbf #
retirou da pilha o valor $0xbf.

* Instruções Aritméticas:
add -> Soma, ex: add $0x4, %EBX # <- EBX <- EBX + 4.
sub -> Subtração, e também pode ser usado para alocar espaço de memória no stack para
receber dados, ex: sub $0x3, %EDX # EDX <- EDX - 3 |
sub $0x5, %ESP # Reserva 5 bytes no Stack para receber dados.
mul -> Multiplicação, ex: mul $0x2, %EAX # EAX <- EAX * 2.
imul -> Multiplicação, porém leva em consideração o sinal.
div -> Divisão, ex: div $0x4, %EAX # EAX <- EAX / 4.
idiv -> Divisão, porém leva em consideração o sinal.
inc -> Incrementa, ou seja, soma um ao registrador, ex: inc %AL # AL <- AL + 1.
dec -> Decrementa, ou seja, subtrai um ao registrador, ex: dec %AH # AH <- AH -1.

* Instruções Lógicas:
Fazendo uma comparação das instruções lógicas entre C e ASM, teríamos a seguinte
tabela:

---------------------
| C | ASM |
---------------------
| && | AND |
| || | OR |
| ! | NOT |
| | XOR |
---------------------

Onde:
AND -> Significa "E" na tabela verdade. Ex: mov $0xa, %EAX # EAX <- 10, em seguida, and $4,
%EAX # Reseta o quarto bit do registrador EAX.
OR -> Significa "OU" na tabela verdade. Ex: or $0x0f, %EBX.
NOT -> Significa "NÃO" na tabela verdade. Pode ser usado para inverter os bits de um byte.
Ex: Esse byte, 01010101, negado, ficaria, 10101010. not %EAX # inverte seus bits.
XOR -> Detector de diferenças. Ex: xor %ECX.

* Instruções de Comparação:
test -> Verifica se está tudo ok com o registrador, ex: test %EAX, %EAX.
cmp -> Compara dados sendo usados. Ex: cmp $0x5, %EAX # Compara 5 com EAX. O cmp sempre
está junto com algum salto condicional, os JXX, onde X são opções de salto. Logo
abaixo temos os saltos.

* Instruções de desvio de programa (salto).


Todas instruções de JXX (Jump X X) não retornam para a execução do programa onde você
parou.
* Esses saltos sempre estarão logo abaixo do comando
cmp, e a leitura dos dois é junta. Ex:
cmp $0x5, %EAX # Se EAX for diferente (jne) de 5
jne 0xa # salta para a memória 0xa
JMP (Jump) -> Faz saltos incondicionais para determinado endereço de memória.
Ex: jmp 0xc # salta para o endereço de memória 0xc.
JE (Jump Equal) -> Salta se for igual.
JNE (Jump Not Equal) -> Salta se for diferente.
JAE (Jump Above Equal) -> Salta se for maior ou igual trabalhando com sinal (positivo
ou negativo).
JGE (Jump Greater Equal) -> Salta se for maior ou igual trabalhando sem sinal.
JBE (Jump Below Equal) -> Salta se for menor ou igual trabalhando com sinal. (positivo
ou negativo).
JLE (Jump Less Equal) -> Salta se for menor ou igual trabalhando sem sinal.
JA (Jump Above) -> Salta se for maior trabalhando com sinal (positivo ou negativo).
JG (Jump Greater) -> Salta se for maior trabalhando sem sinal.
JB (Jump Below) -> Salta se for menor trabalhando com sinal (positivo ou negativo).
JL (Jump Less) -> Salta se for menor trabalhando sem sinal.
JZ (Jump Zero) -> Salta se for igual a zero.
call -> Usado para chamar uma sub-rotina e para saltos como o JMP, porém com o call é
possível voltar a execução do programa de onde parou.
* Outras instruções
int (interrupt) -> é usada para fazer a interrupção do sistema, fechando assim o agrupa
mento das instruções que serão executadas. Na sintaxe AT&T, é usado
o "int 0x80" para passar todas as instruções ao kernel, fazendo
assim com que elas sejam executadas.
Ex:
* Para cada syscall, é necessário colocar o int 0x80
no final.
mov (instrução)
mov (instrução)
int 0x80 # executa todas instruções acima chamando o kernel.

ret (Return Address) -> É usado em sub-rotinas, para voltar a execução normal do pro-
grama.
NOP (NO Operation) -> Como o próprio nome já diz, não é feito nada. Ele é usado em ex-
ploits para encher o buffer de 'nada' até chegar ao shellcode.
Seu código em hexadecimal é 0x90.

--- Sessões de código.

Programas em ASM são divididos em sessões, onde assim, há uma certa organização do código, ar-
mazenamento de dados, etc.
As sessões são:
* Headers (cabeçalho):
- Assim como na linguagem C, o ASM também possui cabeçalho, podendo ser qual-
quer arquivo do sistema, sub-rotinas, etc.
Em linguagem C, seria "#include<define.h>", em ASM seria ".include define.h".
* Sessão de dados:
- Essa sessão acontece a declaração de variáveis. Para iniciar essa sessão,
basta colocar ".section .data".
- Para declarar variáveis, essa seria a sintaxe: "NOME: .TIPO VALOR" ou "NOME =
VALOR" (essa segunda sintaxe vale apenas para variáveis do tipo long), onde
"NOME" seria o nome da variável, o "TIPO" seria o tipo dela, e o "VALOR" se-
ria o valor a ser atribuído.
- Alguns exemplos de tipos de variáveis em ASM: ".long", ".string" e ".ascii"
(existem mais tipos), onde:
- .long é usado para números de até 32 bits, ex:
NUM: .long 10 # NUM é do tipo long de valor 10.
ou:
NUM = 0x7 # NUM é do tipo long de valor 0x7.
- .string é usado para variáveis de string e podem usar caracteres de
tabulação, como \t, \n, etc. Ex: STR: .string "OLÁ!\n".
Também é possível criar uma variável numérica que conterá o tamanho
da string já criada, ex: TAM: .long .-STR # Agora TAM tem a quantida-
de de caracteres de STR.
Isso sempre tem que ser feito quando for usado variáveis strings, já
que é necessário passar seu tamanho para o programa futuramente.
- .ascii é usado como o .string.

Agora um exemplo de sessão de código completo.

# Início da sessão de dados.


.section .data

# Declaração de variáveis.

num: .long 7 # num <- 7


str: .string "Isso é uma string!\n" # str <- "Isso é uma string!\n"
tam: .long .-str # tam <- tamanho de str
str2: .ascii "Outra string!\n" # str2 <- "Outra string!\n"
tam2: .long .-str2 # tam2 <- tamanho de str2

# Fim da sessão de dados.

--- Sessão de código.


* Essa sessão é OBRIGATÓRIA!!!

Nessa sessão é implementado o código do programa, informações de símbolos, etc. Aqui fica arma-
zenado o código e o binário. Sua declaração é feita da seguinte maneira: ".section .text" (ses-
são texto).
Assim como em linguagem C, o ASM também possui uma função principal (main). Para declará-la,
digite ".global _start", e para iniciá-la efetivamente, "_start".

Agora um exemplo dessa sessão agregada com o exemplo da sessão de dados dada anteriormente.
# Início da sessão de dados.
.section .data

# Declaração de variáveis.
num: .long 7 # num <- 7
str: .string "Isso é uma string!\n" # str <- "Isso é uma string!\n"
tam: .long .-str # tam <- tamanho de str
str2: .ascii "Outra string!\n" # str2 <- "Outra string!\n"
tam2: .long .-str2 # tam2 <- tamanho de str2

# Fim da sessão de dados.

# Início da sessão de código


.section .text
.global _start # Declaração da função principal
_start: # Início da função principal

mov $0xf, %EAX # EAX <- f


(...)

--- Labels.
É como se fosse uma função, um módulo, em linguagem C. Você pode criar um label e mandar o pro-
grama "pular" para aquele label, então ele executa as instruções daquele label e retorna para a
execução do programa.
Exemplo:
(...)
jmp label
(...) # pode ter mais instruções aqui ou não.
label:
movl $0x4,%eax
movl $0x0,%ecx
(...)

--- System Calls.


É uma forma usada pelo programa para requisitar funções do kernel. Por exemplo, para um progra-
ma imprimir algo na tela, usamos o system call write (escrever).
Para verificar quais syscall é possível usar, veja o arquivo "unistd.h", geralmente localizado
em /usr/include/asm, mas pode variar de distro para distro (no caso do ubuntu 9.10, está em
/usr/include/asm/unistd_32.h (ou unistd.h_64)).
Como foi dito, a instrução "int 0x80" é usada para executar as syscall. Deve-se guardar o valor
da syscall no registrador %eax. Para registradores com até 6 argumentos, eles irão nos regis-
tradores %ebx, %ecx, %edx, %esi, %edi *NESSA ORDEM*.
Agora que você já sabe o valor da syscall, para verificar seus parâmetros, digite "man 2 write"
por exemplo. O que esse comando faz: "man" é o manual do sistema, se você digitar "man cd", o
shell entrará no manual do comando cd, e assim por diante, o "2" significa que é para o manual
procurar na sessão 2 pelo comando write, para mais detalhes, digite "man man", para verificar o
manual do próprio manual, e o "write" foi posto, como dito anteriormente, para verificar seus
parâmetros (ou para obter mais informações). Caso você não consiga acessar a sessão 2 do manual
instale o seguinte pacote "manpages-dev". No manual de "write" na sessão 2, mostra que seu pa-
râmetro é:
"ssize_t write(int fd, const void *buf, size_t count);", o que isso significa?
int fd: Onde ele irá escrever
*buf: O que ele irá escrever
count: O tamanho do que ele irá escrever
Vamos ver outro exemplo, "man 2 exit". Seu parâmetro é: "void _exit(int status);". A syscall
"exit" possui apenas um parâmetro. Para uma saída normal sem erros, use "exit(0)".
Com esse conhecimento, já é possível criar um programinha básico. Vamos ver o exemplo:

*******************************************************************************************************
# begin

.section .data # Sessão de dados

str: .string "Meu primeiro código em ASM AT&T\n"


tam: .long .-str

.section .text # Sessão de código


.global _start # Declaração da função principal
_start: # Início da função principal

# syscall write (4)


movl $0x4, %eax # eax <- syscall de valor 4 (write)
movl $0x1, %ebx # Onde será escrito, no caso na saída padrão (primeiro argumento)
leal str, %ecx # O que será escrito, no caso nossa variável str (segundo argumento)
movl tam, %edx # O tamanho da nossa variável str (terceiro argumento)
int $0x80 # Executa todas as instruções da syscall acima

# syscall exit (1)


movl $0x1, %eax # eax <- syscal de valor 1 (exit)
movl $0x0, %ebx # Status do exit, no caso exit(0)
int $0x80 # Executa todas as instruções da syscall acima

# end

Para compilar:
# as -o imprime.o imprime.s # Cria o objective file do programa
# ld -o imprime imprime.o # Faz a linkagem do obj file, tornando-o executável
# ./imprime # Executa o programa
*******************************************************************************************************

Vamos ver outro exemplo, agora usando a syscall read, que capta o que o usuário digitar.
Primeiramente vamos verificar os parâmetros que ela precisa, para isso, "man 2 read", "ssize_t
read(int fd, void *buf, size_t count);". Apesar de necessitar de 3 parâmetros, precisa-se pas-
sar apenas o tamanho do que será lido pelo programa. Segue o exemplo:

*******************************************************************************************************
# begin

.section .data # Sessão de dados

str: .string "Digite alguma coisa\n"


tam: .long .-str

.section .text # Sessão de código


.global _start # Declaração da função principal
_start: # Início da função principal

# syscall write (4)


movl $0x4, %eax # eax <- syscall write
movl $0x1, %ebx # Onde será escrito (primeiro argumento)
leal str, %ecx # O que será escrito (segundo argumento)
movl tam, %edx # O tamanho do que será escrito (terceiro argumento)
int $0x80 # Execução do write

# Salva o Stack Pointer em %ecx


movl %esp, %ecx

# Reserva 10 bytes para o usuário digitar no Stack


subl $0xa, %esp

# syscall read (3)


movl $0x3, %eax # eax <- syscall read (O que o usuário digitar será armazenado em %eax)
movl $0xa, %edx # edx <- 10 (terceiro argumento) *repare que foi usado %edx
int $0x80 # Execução do read

movl %eax, %edx # edx <- eax, ou seja, move o que o usuário digitou para %edx

# syscall write (4)


movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # ebx <- 1 (saída padrão)
int $0x80 # Execução do write

# syscall exit (1)


movl $0x1, %eax # eax <- syscall exit (1)
movl $0x0, %ebx # ebx <- 0 (saída normal do programa)
int $0x80 # Execução do exit

# end

Para compilar:
# as -o leia.o leia.s # Cria o objective file do programa
# ld -o leia leia.o # Faz a linkagem do obj file, tornando-o executável
# ./leia # Executa o programa
*******************************************************************************************************

Como dito anteriormente, para passar os parâmetros da syscall, basta inserir os valores para os
registradores BX, CX, DX, SI e DI. Mas e se a syscall necessitar de mais que seis argumentos?
Então, temos que jogar os valores para a stack de traz para frente (lembre-se que seu método de
inserção é LIFO, Last In - First Out), sendo que em %ebx, ficará o endereço do primeiro argu-
mento. Também pode-se copiar os argumentos para um espaço de memória alocado e armazenar o en-
dereço do primeiro argumento em %ebx.

--- Usando instruções de decisão e repetição


Em linguagem C, as instruções de decisão são: if e if else, e as de repetição são: while, for e
do. Em assembly também é possível criar loops e instruções de decisão, basta utilizar o comando
CMP juntamente com alguma instrução de jump (JXX, onde X são as opções).
Abaixo segue um exemplo de cada.

*******************************************************************************************************
# Estrutura de decisão (if else)
# begin

.section .data # Sessão de dados

# Frase de interação com o usuário


str1: .string "O programa irá comparar dois registradores \n"
tam1: .long .-str1

# Frase mostrada caso os registradores forem iguais


str2: .string "Os registradores são IGUAIS! \n"
tam2: .long .-str2

# Frase mostrada caso os registradores forem diferentes


str3: .string "Os registradores são DIFERENTES! \n"
tam3: .long .-str3

.section .text # Sessão de código


.global _start # Declaração do programa principal
_start: # Início do programa principal

# syscall write (4)


movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # Onde será escrito (primeiro argumento)
leal str1, %ecx # O que será escrito (segundo argumento)
movl tam1, %edx # Tamanho do que será escrito (terceiro argumento)
int $0x80 # Executa o write

movl $0xa, %eax # eax <- 10


movl $0x5, %ebx # ebx <- 5

cmp %eax, %ebx # eax = ebx? Se sim,


je IGUAL # pula para o label IGUAL
jmp DIFERENTE # senão pula para o label DIFERENTE

# Label IGUAL
IGUAL:
# syscall write (4)
movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # Onde será escrito (primeiro argumento)
leal str2, %ecx # O que será escrito (segundo argumento)
movl tam2, %edx # Tamanho do que será escrito (terceiro argumento)
int $0x80 # Executa o write

jmp EXIT # Pula para a Label EXIT

# Label DIFERENTE
DIFERENTE:
# syscall write (4)
movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # Onde será escrito (primeiro argumento)
leal str3, %ecx # O que será escrito (segundo argumento)
movl tam3, %edx # Tamanho do que será escrito (terceiro argumento)
int $0x80 # Executa o write

# Label EXIT
EXIT:
# syscall exit (1)
movl $0x1, %eax # eax <- syscall exit (1)
movl $0x0, %ebx # saída padrão
int $0x80 # Executa o exit
# end

Para compilar:
# as -o if.o if.s # Cria o objective file do programa
# ld -o if if.o # Faz a linkagem do obj file, tornando-o executável
# ./if # Executa o programa
*******************************************************************************************************
*******************************************************************************************************
# Estrutura de repetição
# begin

.section .data # Sessão de dados

# Frase de interação com o usuário


str1: .string "O programa irá demonstrar um loop usando ASM! \n"
tam1: .long .-str1

# Frase de demonstração do loop


str2: .string "Repetindo...! \n"
tam2: .long .-str2

# Frase de término do loop


str3: .string "Término do loop! \n"
tam3: .long .-str3

# Contador
cont: .long 0

.section .text # Sessão de código


.global _start

_start:

# syscall write (4)


movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # onde será escrito (primeiro argumento)
leal str1, %ecx # o que será escrito (segundo argumento)
movl tam1, %edx # tamanho do que será escrito (terceiro argumento)
int $0x80 # executa o syscall write

movl cont, %esi # esi <- cont

# Label INICIO
INICIO:
# syscall write (4)
movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # onde será escrito (primeiro argumento)
leal str2, %ecx # o que será escrito (segundo argumento)
movl tam2, %edx # tamanho do que será escrito (terceiro argumento)
int $0x80 # Executa a syscall write (4)

inc %esi # esi <- esi + 1


int $0x80 # Executa as instruções acima

cmp $0xa, %esi # esi!=10? Se sim,


jne INICIO # chama o Label INICIO,
jmp EXIT # senão pula para Label EXIT

# Label EXIT
EXIT:
# syscall write (4)
movl $0x4, %eax # eax <- syscall write (4)
movl $0x1, %ebx # onde será escrito (primeiro argumento)
leal str3, %ecx # o que será escrito (segundo argumento)
movl tam3, %edx # tamanho do que será escrito (terceiro argumento)
int $0x80 # Executa a syscall write (4)
# syscall exit (1)
movl $0x1, %eax # eax <- syscall exit (1)
movl $0x0, %ebx # saída padrão
int $0x80 # executa a syscall exit (1)

# end

Para compilar:
# as -o repetition.o repetition.s # Cria o objective file do programa
# ld -o repetition repetition.o # Faz a linkagem do obj file, tornando-o executável
# ./repetition # Executa o programa
*******************************************************************************************************

--- GCC Inline


Com o GCC é possível utilizar códigos ASM usando linguagem C. Para utilizá-lo, basta usar a fun
ção "asm()". Uma das utilidades do GCC Inline é a possibilidade de escrever shellcodes e ex-
ploits, mas pode-se usar para vários outros fins.
Vejamos alguns exemplos:
* Para compilar um arquivo .c use o parâmetro -o. Ex: $ gcc -o shellcode shellcode.c
*******************************************************************************************************
// Exemplo 1:
main(){
__asm("nop;nop;nop \n");
}
*******************************************************************************************************
// Exemplo 2:
main(){
__asm(
"nop\n"
"nop\n"
);
}
*******************************************************************************************************
// Exemplo 3:
main(){
asm("nop;nop;nop");
}
*******************************************************************************************************
// Exemplo 4:
main(){
__asm__(
"movl $0x4, %eax \n"
"int $0x80 \n"
);
}
*******************************************************************************************************
Pode ser usado qualquer tipo de declaração citada acima. Todas surtem o mesmo efeito, ou seja,
executam ASM utilizando linguagem C.

Вам также может понравиться