end of file – ebook asm x86 - · pdf filearquiteturas padroniza aquele determinado...

131
END OF FILE – EBOOK ASM X86 Autor: kodo no kami ( fb/hacker.fts315 ) discord: kodo#0010 Forum: https://eofclub.in/forum Pagina: https://www.facebook.com/forumeof Discord: https://discordapp.com/invite/cmzeMPv (convite) Greetz: hefest0, sir.rafiki, s1m0n, neofito, 0x4xh4x, gjunnior, 0racle, lend4pop, refell, fascist, jhonydk, v4p0r, angley, hchild, onezer0, melots Data: 08/01/2017 ~ 25/08/2017 (sem revisão) E ae galera eu sou o kõdo no kami (コードの神) sendo esse o meu quinto ebook de programação e nele vamos aprender a programar na linguagem assembly, sendo essa linguagem uma linguagem complexa e chata de inicio devido ser uma linguagem de baixo nível (low level), como ela é uma linguagem de baixo level não fica limitada a um sistema operacional porém é possivel ser usada para programar para um determinado sistema operacional ou ate mesmo desenvolver um sistema operacional para alguma arquitetura especifica por ela, sua limitação seria não ser portável entre arquiteturas estando limitada para aquela arquitetura para qual foi criada. devido a linguagem assembly ser complexa e trabalhosa não é muito usada para fins comerciais sendo substituida por linguagenas de mais alto nivel que ela como a própria linguagem C, sendo ela uma linguagem mais usada para fins academicos para ensinar sobre a própria arquitetura da computação (asm é ensinado em areas mais especificas como ciencia e engenharia da computação, muito dificilmente ensinada em cursos de programação embora exista alguns inclusive voltados a eletronica ou que envolva programação a baixo level), o recomendado para aprender assembly é ter uma boa base em outra linguagem de programação principalmente na linguagem C. como nos ebooks anteriores (c/c++, pascal, perl e php) esse ebook não pode ser vendido sendo a sua distribuição livre sem fins lucrativos ^^ Indice 1.0 – Introdução ao Assembly 1.1 – Baixo e Alto nível 1.2 – Arquitetura CISC e RISC 1.3 – Sintaxe INTEL e AT&T 1.4 – Agrupamento de bits 1.5 – Little endian e Big endian 1.6 – Tipos de dados 1.7 – Compiladores 1.8 – Comentario 2.0 – Familia x86 2.1 – x86: Registradores 2.2 – x86: Flags 2.3 – x86: instrução de movimento 2.4 – x86: instrução aritmetica 2.5 – x86: instrução de pulo 2.6 – x86: Subrotina 2.7 – x86: Acesso a memoria 2.8 – x86: Interrupção 2.9 – x86: IO 3.0 – x86: FPU 3.1 – x86: MMX 3.2 – x86: 3DNow

Upload: haxuyen

Post on 16-Mar-2018

232 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

END OF FILE – EBOOK ASM X86Autor: kodo no kami ( fb/hacker.fts315 ) discord: kodo#0010

Forum: https://eofclub.in/forumPagina: https://www.facebook.com/forumeof

Discord: https://discordapp.com/invite/cmzeMPv (convite)Greetz: hefest0, sir.rafiki, s1m0n, neofito, 0x4xh4x, gjunnior, 0racle, lend4pop,

refell, fascist, jhonydk, v4p0r, angley, hchild, onezer0, melotsData: 08/01/2017 ~ 25/08/2017 (sem revisão)

E ae galera eu sou o kõdo no kami (コードの神) sendo esse o meu quinto ebook de programação e nele vamos aprender a programar na linguagem assembly, sendo essa linguagem uma linguagem complexa e chata de inicio devido ser uma linguagem de baixo nível (low level), como ela é uma linguagem de baixo level não fica limitada a um sistema operacional porém é possivel ser usada para programar para um determinado sistema operacional ou ate mesmo desenvolver um sistema operacional para alguma arquitetura especifica por ela, sua limitação seria não ser portável entre arquiteturas estando limitada para aquela arquitetura para qual foi criada. devido a linguagem assembly ser complexa e trabalhosa não é muito usada para fins comerciais sendo substituida por linguagenas de mais alto nivel que ela como a própria linguagem C, sendo ela uma linguagem mais usada para fins academicos para ensinar sobre a própria arquitetura da computação (asm é ensinado em areas mais especificas como ciencia e engenharia da computação, muito dificilmente ensinada em cursos de programação embora exista alguns inclusive voltados a eletronica ou que envolva programação a baixo level), o recomendado para aprender assembly é ter uma boa base em outra linguagem de programação principalmente na linguagem C. como nos ebooks anteriores (c/c++, pascal, perl e php) esse ebook não pode ser vendido sendo a sua distribuição livre sem fins lucrativos ^^

Indice

1.0 – Introdução ao Assembly1.1 – Baixo e Alto nível1.2 – Arquitetura CISC e RISC1.3 – Sintaxe INTEL e AT&T1.4 – Agrupamento de bits1.5 – Little endian e Big endian1.6 – Tipos de dados1.7 – Compiladores1.8 – Comentario2.0 – Familia x862.1 – x86: Registradores2.2 – x86: Flags2.3 – x86: instrução de movimento2.4 – x86: instrução aritmetica2.5 – x86: instrução de pulo2.6 – x86: Subrotina2.7 – x86: Acesso a memoria2.8 – x86: Interrupção2.9 – x86: IO3.0 – x86: FPU3.1 – x86: MMX3.2 – x86: 3DNow

Page 2: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

3.3 – x86: SSE4.0 – x86: Modo Protegido4.1 – x86: libc4.2 – x86: asm web (cgi)4.3 – x86: asm inline4.4 – x86: disassembler5.0 – EOF

1.0 – Introdução ao Assembly

assembly é uma linguagem de baixo nível considerada como linguagem de 2º geração sendo ela uma linguagem de montagem e sua primeira aparição foi por volta de 1949, sua compilação do código fonte para o executável final não tem tantas conversões e o seu código na maior parte das vezes são instruções equivalentes ao próprio código na linguagem da máquina, sendo essas instruções equivalente aos códigos binários em uma forma textual sendo chamados de instrução mnemônicos ou códigos mnemônicos, um executável em assembly é centenas de vezes menor que um executável em outras linguagens de alto nível, por outro lado o código fonte de um programa em assembly é bem maior do que outras linguagens em alto nível, a linguagem assembly não é uma linguagem portável entre arquiteturas já que está limitada aos opcodes e ao assembler de uma determinada arquitetura ou plataforma especifica, além do próprio compilador que pode influenciar a forma de escrita daquele código seja naquele sistema ou naquela arquitetura, a linguagem assembly pode ser usada tanto para programar para baixo nível para uma arquitetura especifica quanto para alto nível para um sistema operacional especifico, como também é possível ser usada para programar para web no lado do servidor de forma semelhante a linguagem PHP usando o seu executável criado em assembly como CGI naquele servidor web, alguns compiladores em linguagens de alto nível permite inserir códigos assembly de forma linear na própria linguagem comisso você pode programar em assembly diretamente naquela outra linguagem de programação, alguns compiladores em outras linguagens de programação gera primeiro o código em assembly para depois gerar o codigo na linguagem da máquina um bom exemplo disso é a linguagem C usadoo próprio gcc que inclusive tambem é um compilador para a linguagem assembly

1.1 – Baixo e Alto nível

sempre escutamos que determinada linguagem é alto nível ou baixo nível sendo algumas consideradas ate como médio nível embora esse termo alto e baixo nivel seja muito relativo, uma linguagem muitas vezes é considerada de baixo nível quando está mais perto da linguagem da máquina no caso do binário puro, já uma linguagem de alto nivel é considerada mais longe da linguagem da máquina podendo ser uma linguagem com uma abstração muito grande, uma linguagem de alto nível pode ser compilada sobre uma linguagem de baixo nivel como a linguagem C com o compilador gcc que gera um códigos em assembly antes do executável final, uma linguagem de alto nivel pode usar recursos do sistema como funções APIs alem de chamadas de sistemas enquanto uma linguagem de baixo nivel usa recursos direto da arquitetura e processador alem do próprio hardware, as linguagens de alto nivel são portaveis entre arquiteturas e sistemas, ja linguagens de baixo nivel não são portaveis entre arquiteturas (o printf na linguagem C vai ser sempre o mesmo independente da arquitetura ou sistema, ele sempre sera uma função para imprimiralgo na tela, por outro lado a interrupção de vídeo 0x10 que permite imprimir algo na tela da arquitetura x86 só vai funcionar apenas nessa arquitetura e não em outra)

1.2 – Arquitetura CISC e RISC

uma arquitetura é um padrão a ser seguido na criação de um computador, processador ou chip, levando em conta tanto o seu hardware quanto o seu software como também o seu firmware, as

Page 3: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

arquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente iguais e tenha as mesmas possibilidades lógicas como as mesmas instruções e recursos, em uma arquitetura com instrução CISC (Complex Instruction Set Computer) temos um conjunto de instruções complexas e de tamanho variável, a quantidade de tipos de instruções são grandes, a vantagem disso que o binário gerado pelo compilador é menor porém mais lento em sua execução, a quantidade de operando em uma instrução CISC é no máximo de dois, os computadores pessoais hoje que usa arquitetura x86/64 sãoCISC (embora meio hibrido), além das arquiteturas baseada em CISC tambem temos as arquiteturasbaseada em RISC (Redunced Instruction Set Computer), o RISC é um conjunto de instruções reduzidas de forma simples, a quantidade de bytes por opcodes no RISC são fixos, o RISC tem umaquantidade pequena de instruções e com isso o binario gerado pelo compilador fica muito maior porém sua execução mais rapida já que são instruções e operações simples, RISC tem a possibilidade de executar mais de uma instrução por ciclo, esse tipo de instrução é muito usado paramicrocontroladores e chips como por exemplo a arquitetura do playstation que é a arquitetura MIPSsendo ela RISC

1.3 – Sintaxe INTEL e AT&T

as sintaxes é uma forma do compilador enxerga o código ou toda estrutura lógica que compõe a linguagem naquele codigo, as sintaxes de uma linguagem em boa parte das vezes é independente daarquitetura e sistema ou ate mesmo do compilador, isso tornar um codigo em uma determinada linguagem sempre igual independente da arquitetura ou do sistema, porém na linguagem assembly pode variar muito a sua sintaxe dependendo não apenas da arquitetura ou do próprio sistema como tambem do proprio compilador, isso torna o mesmo codigo as vezes incompatível entre compiladores mesmo sendo para a mesma arquitetura. cada compilador assembly pode conter uma sintaxe única embora possa gerar o mesmo código binário ou equivalente para aquela determinada arquitetura ou sistema depois de ser compilado, existem duas sintaxes que são muito usadas em compiladores para arquitetura x86 sendo elas a sintaxe Intel e a sintaxe AT&T, muitos compiladorestende a seguir um desses dois tipos de padrões, como exemplo temos o nasm que usa sintaxe Intel e o gas usa sintaxe AT&T, uma das diferenças entre as duas sintaxes que na sintaxe Intel as operações são atribuidas da direita para esquerda enquanto que na sintaxe AT&T são da esquerda para direita

sintaxe IntelDestino Origem

sintaxe AT&TOrigem Destino

outra diferença que os numeros na sintaxe Intel são representado apenas com os numeros, já na sintaxe AT&T os numeros deve conter um cifrão indicando passagem imediata

sintaxe Intel315

sintaxe AT&T$315

os registradores na sintaxe Intel são apenas os nomes deles já na sintaxe AT&T deve conter o símbolo de porcentagem antes (não precisa se prender aos registradores por enquanto vamos abordar sobre eles mais para frente isso é apenas um exemplo)

Page 4: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

sintaxe Inteleax

sintaxe AT&T%eax

o acesso indexado na sintaxe Intel devemos usar o colchete e dentro o registrador junto as operações, já na sintaxe AT&T usamos o valor e depois entre parênteses usamos o registrador

sintaxe Intel[esp+8]

sintaxe AT&T8(%esp)

como podemos ver existem diferenças entre a sintaxe Intel e a AT&T, com isso determinado compilador que use a sintaxe Intel não vai compilar um codigo na sintaxe AT&T ou vice-versa, mesmo com essa diferença entre as sintaxes é possivel converter de uma sintaxe para outra quando se trata da mesma arquitetura, outros compiladores de outras arquiteturas diferente pode ser um pouco diferente desse padrões ou ter alguma semelhança com eles, exemplo temos a arquitetura MIPS dependendo do compilador o cifrão é usado para indicar um registrador diferente do que ocorre na sintaxe AT&T que é usado o sinal de porcentagem, compilar asm para os microcontroladores PIC com o MPLab a origem e destino é da esquerda para direita enquanto compilar asm para microcontroladores AVR é da direita para esquerda

1.4 – Agrupamento de bits

a máquina é capaz de acessar uma sequencia de bits de uma única vez de uma forma paralela, essa sequencia pode ser fixa ou relativa dependendo da arquitetura, muitas vezes a gente define uma determinada arquitetura por essa quantidade de leitura e escrita por barramento, um processador de em um computador pessoal hoje contem barramentos de 32 ou 64bits (arquitetura x86 e x64), em processadores mais antigos como o 8080 era barramento de 8bits em outros mais antigos ainda como 4040 era capaz de acessar apenas 4bits por vez por ter um barramento de apenas 4bits, hoje temos placas de videos com barramento de 128bits como tambem existem registradores no próprio processador com tamanho de 128bits mesmo sendo arquitetura de 32bits ou 64bits, existem muitas arquiteturas hoje que ainda usam 8bits como vários tipos de microcontroladores e chips, na informática é usado alguns nomes para definir a quantidade de dados manipulado por vez isso tambem é conhecido como palavra, os dados de 4bits são chamado de nibble, dados de 8 bits chamado de byte, dados de 16bits é word, os de 32bits é o dword, os de 64bits sendo os qword

1.5 – Little endian e Big endian

alem do agrupamento de bits existe a ordenação do byte seja na memória do computador ou ate mesmo a sua passagem pelo barramento, no dia a dia a gente usa em nosso sistema numérico os digitos menos significando para direita e o mais significativo para esquerda (como exemplo podemos imaginar o numero 159, sendo que o numero 1 vale muito mais que o 5, e o numero 5 valemais que o numero 9), por outro lado um computador pode fazer o inverso dependendo da sua arquitetura ou ate mesmo do sistema, o numero mais significativo pode ser o da direita e não o da esquerda dependendo da arquitetura (naquele exemplo do numero 159 seria o 951, sendo o 1 ainda maior que o numero 5 e o 9), sistemas onde o numero menos significativo fica para direita são chamado de little endian enquanto que sistemas que o numero menos significativo fica para

Page 5: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

esquerda são chamado de big endian, os computadores usa agrupamento de pelo menos 16bits ou mais para aplicar esse conceito do little e big endian, sendo que a cada 8bits é a própria separação, então em um computador que tem o barramento de 16bits se a gente transferir o numero “0315” e ele usa little endian será enviado o numero “03 15”, em um computador que use big endian sera enviado o valor “15 03” sendo nos dois casos os mesmo numeros

1.6 – Tipos de dados

dependendo da arquitetura ou ate da plataforma a linguagem assembly pode não ter tipos de dados específicos como ocorre nas linguagens de alto nivel onde temos tipos especificos de dados, em uma linguagem de alto nivel fortemente tipada criamos variaveis de tipos especificos como o tipo numerico inteiro em C para armazenar valor do mesmo tipo sendo o numerico inteiro, na linguagemassembly dependendo da arquitetura apenas temos que alocar aquela quantidade especifica de espaço na memoria que seria equivalente ao tamanho daquele tipo especifico de dado, em linguagens de alto nivel a propria declaração de uma variavel seria equivalente a alocar na memoria aquele determinado espaço para ser armazenado nele sendo que isso é trabalho do compilador de uma forma mais automatica, por outro lado em assembly as vezes precisamos criar esse determinado espaço de forma manual as vezes subtraindo o próprio endereço da pilha de memoria, porem isso não é uma regra dependendo da arquitetura em alguns casos ate mesmo do próprio compilador o assembly pode ter tipos específicos de dados, já em outros casos o tipo especifico de dado em si é apenas a palavra dele sendo o agrupamento de bits

1.7 – Compiladores

os compiladores transforma o código da linguagem de programação para a linguagem da maquina, isso acontece em vários passos dependendo para qual sistema ele está gerando aquele código ou para qual arquitetura, em um compilador assembly essa conversão é muito simples apenas convertendo entre as instruções mnemônica da linguagem assembly para o código binário equivalente aquela determinada instrução, alguns compiladores para a linguagem assembly são

gas – https://www.gnu.org/software/binutils/ (multi)nasm – http://www.nasm.us/ (x86)yasm – http://yasm.tortall.net/ (x86)flasm – https://flatassembler.net/ (x86)goasm – http://www.godevtool.com/ (x86)tasm – https://sourceforge.net/projects/guitasm8086/ (x86, z80)masm – http://www.masm32.com/ (x86)jwasm - https://sourceforge.net/projects/jwasm/ (x86)

dependendo do sistema ou ate mesmo do compilador para gerar determinado tipo de arquivo binárioou executavel especifico é necessario alem do compilador tambem de um lincador (link), os lincadores pega o codigo objeto gerado pelo compilador junta com outros arquivos do sistema e monta o executável final para aquele sistema, um exemplo são os programas para windows da aquitetura x86 onde compilamos com o gas e depois lincamos como ld (esse mesmo passo é feito pelo próprio gcc quando compilamos em C porém isso é feito por trás dos panos)

ld – https://www.gnu.org/software/binutils/tlink – https://sourceforge.net/projects/guitasm8086/ golink – http://www.godevtool.com/link – http://www.masm32.com/ jwlink - https://sourceforge.net/projects/jwlink/

Page 6: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

alem dos compiladores tambem existem as IDEs que são ambientes de programação permitindo criar o codigo de forma mais simples embora o uso de IDE seja opcional podendo ser usado qualquer editor de texto de sua preferencia, boa parte das IDEs são apenas editores de texto com destacamento de sintaxe, exibição de erros e debugação, opções que facilita na hora da compilação as vezes com um compilador embutido, e outras opções (inclusive existem IDEs para programação grafica usando janelas em asm como easy code)

GUI Tasm Editor – https://sourceforge.net/projects/guitasm8086/Easy Code Visual Asm – http://www.easycode.cat/qeditor – http://www.masm32.com/ notepad++ – https://notepad-plus-plus.org/download/v7.3.2.htmlgeany – https://www.geany.org/nano – https://www.nano-editor.org/vim – http://www.vim.org/emul8086 – http://www.emu8086.com/ (8086)

quando compilamos um determinado codigo para uma outra arquitetura ou plataforma diferente esse processo é chamado de cross-compile, muitas vezes é bem mais simples ou necessario compilar o codigo em outra arquitetura e jogar naquela devido a sua limitação ou ate mesmo processamento para tal coisa, alem ser possível compilar o codigo para outro tipo de arquitetura em uma arquitetura diferente, também é possível rodar o executável de arquitetura ou plataforma diferente através de maquinas virtuais (vm), emuladores ou ate simuladores. uma maquina virtual emula um computador completo a nivel de arquitetura podendo inclusive instalar sistemas operacionais diferente de outra arquitetura, um emulador por outro lado tambem emula aquela arquitetura porém são bem mais especificas e limitadas já que emula apenas uma plataforma especifica como por exemplo um emulador de super nintendo que é possível rodar jogos de super nintendo porém não vai rodar um programa para o chip 65c816 diretamente no emulador, o simulador é bem parecido com o emulador a sua diferença que ele simula aquela arquitetura (um emulador tambem pode ser considerado um simulador ou vice versa dependendo do contexto)

qemu – http://www.qemu-project.org/ (x86/64, arm, 68k, mips, sparc, s390, powerpc, outros)virtualbox – https://www.virtualbox.org/ (x86/64)vmware – https://www.vmware.com/ (x86/64)emul8086 – http://www.emu8086.com/ (8086)oshonsoft – http://www.oshonsoft.com/downloadspage.php (z80, pic, 8085, avr)mars – http://courses.missouristate.edu/KenVollmar/mars/ (mips)jubin 8085 simulator – https://8085simulator.codeplex.com/ (8085)gnu8085 – https://gnusim8085.github.io/ (8085)j51 – http://www.viara.eu/en/j51/ (8051)easy68k – http://www.easy68k.com/ (68k)exifpro 6502 simulator – http://exifpro.com/utils.html (6502)hercules – http://www.hercules-390.eu/ (system370, esa390, z/achiteture)simhv – http://simh.trailing-edge.com/ (pdp, s3, altair, vax, sds940, ibm, outros)dosbox – https://www.dosbox.com/ (x86: msdos)openmsx - https://openmsx.org/ (z80: msx)yaze-ag – http://www.mathematik.uni-ulm.de/users/ag/yaze-ag/ (z80: cp/m)vice – http://vice-emu.sourceforge.net/ (6502: C64/128, PET, CBM II, VIC20, PLUS/4)pce – http://www.hampa.ch/pce/ (msdos, pcdos, cp/m, mac, minix, xenix)

tambem existem os disassembly e debuggers que são usados para dissecar um executavel de forma

Page 7: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

estatica direto no binario ou de forma dinamica em tempo de execução

objdump – https://www.gnu.org/software/binutils/gdb – https://www.gnu.org/software/gdb/ollydbg – http://www.ollydbg.de/radare2 – http://rada.re/r/ida – https://www.hex-rays.com/

1.8 – Comentario

em boa parte das linguagens de programação (se não todas), é possível comentar determinado trecho no codigo, quando comentamos um determinado trecho, esse mesmo trecho de codigo sera ignorado pelo compilador, servindo apenas para o programador, sendo que os comentário podem serusado para excluir temporariamente um determinado codigo, podem ser usado para destacar um trecho no codigo, ou podem ser usados mostrar alguma informação sobre o codigo ou quem o criou aquele codigo. os comentarios na linguagem assembly podem variar dependendo do compilador ou ate da arquitetura, normalmente os comentarios em asm em muitos compiladores são representado por ponto e virgula, quando o compilador encontra o ponto e virgula ele automaticamente ignora tudo depois dele

;isso é um comentario

no compilador gas é usado duas vezes o barra

//isso é um comentario

é sempre bom comentar no começo do codigo com algumas informações como o autor, arquitetura, compiladores e etc

;autor: kodo no kami:arquitetura: x86;compilador: nasm

podemos usar o comentário depois da instrução para informar o que exatamente ela faz

mov eax,10 ;vai mover 10 para eax

tambem podemos usar o comentário para excluir um determinado trecho de codigo

;mov eax,10

2.0 – Família x86

quando nos referimos a x86 estamos nos referindo a toda família de processadores que seguem o padrão x86, deis do antigo 8086 ate os mais atuais processadores x86 ou x64 (x86_64), o 8086 surgiu em 1978 e também era chamado de iAPX86 sendo ele um processador de 16bits que endereçava no máximo 1MB de memória devido ter barramento de 20bits de memoria e barramentode 16bits de dados, o 8086 é sucessor da arquitetura 8080 e 8085 com isso muitas de suas instruçõesforam mantida ou são parecidas embora não seja compativel entre essas arquiteturas, depois do 386

Page 8: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

(80386) em 1989 os processadores x86 são de 32bits podendo endereçar ate 4GB de memoria em teoria, mesmo sendo o processador de 32bits para manter uma compatibilidade com maquinas e sistema antigos de 16bits o proprio computador inicia lendo apenas 1MB de memoria isso acontece ate hoje em computadores x86 atuais, em 2003 teve uma nova geração de processadores x86 sendo eles os de 64bits como o athlon64 e Opterom

2.1 – x86: Registradores

os registradores são memorias internas dos processadores sendo uma das suas diferença a velocidade de acesso a ela, um processador acessa os dados nos registradores em uma velocidade superior as memórias no computador podendo ser ate milhares de vezes mais rapida que na memoria primaria, os registradores são usados para diversos fins sendo alguns para fins especificos como operações aritmética feita pelo processador, operações logica, contador de instrução, endereçoda pilha de memoria, passagem de argumento para chamadas externas como interrupções ou retornode subrotinas, na arquitetura x86 existem 4 registradores de uso geral que são chamados de AX, BX, CX e DX, embora esses registradores seja de uso geral eles também podem ser usados para finsespecíficos sendo o registrado AX usado como acumulador, o registrado BX como base, o registrador CX como contador e o registrado DX como dado, esses registradores são registradores de 16bits porem podemos usar eles apenas como 8bits sendo que sera dividido em dois novos registradores cada um de 8bits totalizando um único registrador de 16bits, o registrador AX sendo usado como registrador de 8bits são os registradores AH e AL os dois juntos forma o proprio regitrador AX sendo o AH o byte mais significativo do registrador AX e o AL o menos significativodele, o mesmo vale para os outros registradores onde o BX é um registrador de 16bits porem pode ser acessado como registrador de 8bits com o BH e BL, o registrador CX de 16bits e CH e CL de 8bits, o DX com o DH e DL, então se a gente armazenar o valor 0315 no registrador AX o numero 03 estaria armazenado no AH e o 15 no AL

AX AH AL

0315 03 15

alem dos registradores de uso geral existem os registradores apontadores como os registradores SI eDI, eles são usados como registradores de indice de dados sendo eles usados principalmente na manipulação de strings, o registrador SI seria a fonte e o DI o destino daquele dado, tambem temos dois registradores para manipulação da pilha de memoria que são os registrador SP e BP, sendo que o registrador SP aponta para o topo da pilha de memoria e o registrador BP é usado como base para correr aquele determinado trecho da pilha, outro registrador é o IP que aponta para execução daquela determinada instrução sendo o IP importante para o próprio processador saber onde sera a próxima instrução que sera executada, alem dos registradores gerais e apontadores tambem existem os de segmento que são usados em conjuntos com outros registradores entre esses regitradores temos o CS que aponta para o segmeto do codigo ele é usando em conjunto com o registrador IP (CS:IP) e indica onde esta o offset da memoria atual da instrução, o registrador de segmento DS seria segmento de dados e é usado em conjunto com o registrador SI (DS:SI) e DI (DS:DI), o registrador SS seria o registrador de segmento da pilha de memoria ele é usado em conjuto com o registrador SP (SS:SP) e BP (SS:BP), existem outros registradores de segmentos extras de dados como o ES, FS e GS e seu uso é parecido ao registrador de segmento DS, quando estamos no modo real a maquina só pode usar instruções e registradores de 16bits, quando passamo para modo protegido a maquina já pode usar instruções e registradores de 32bits sendo que esses registradores são os mesmos registradores de 16bits com tamanho de 32bits, para indicar que estamos manipulando registradores de 32bits usamos a letra “E” na frente do registrador sendo eles o EAX, EBX, ECX, EDX, ESP, EIP, EBP, ESI e EDI, para arquitetura x86 de 64bits podemos acessar os mesmo registradores em 64bits usando o R sendo eles RAX, RBX, RCX, RDX, RSI, RDI, RSP,

Page 9: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

RBP e RIP

64bits (longo) 32bits (protegido) 16bits 8bits (H) 8bits (L)

rax eax ax ah al

rbx ebx bx bh bl

rcx ecx cx ch cl

rdx edx dx dh dl

rsi esi si

rdi edi di

rbp ebp bp

rsp esp sp

rip eip ip

na arquitetura de 64bits foram colocacados alguns novos registradores denominados de R, esses registradores são representados por uma numeração que vai de 0 ate 15, sendo que os numeros de 0 a 7 são os registradores já citados (rax, rcx, rdx, rbx, rsp, rbp, rsi rdi), os registradores de 8 a 15 não tem nomes especificos e são utilizados principalmente para passagem de argumentos em chamadas de sistema de 64bits (normalmente do R6 ate o R10), tambem é possível acessar uma quantidade de bits especificos nesses novos registradores colocando a letra equivalente ao seu bit no final dele, a sua representação seria o “d” para 32bits, “w” para 16bits e “l” para 8bits

64bits 32bits 16bits 8bits

r0 (rax) r0d (eax) r0w (ax) r0l (al)

r1 (rcx) r1d (ecx) r1w (cx) r1l (cl)

r2 (rdx) r2d (edx) r2w (dx) r2l (dl)

r3 (rbx) r3d (ebx) r3w (bx) r3l (bl)

r4 (rsp) r4d (esp) r4w (sp)

r5 (rbp) r5d (ebp) r5w (bp)

r6 (rsi) r6d (esi) r6w (si)

r7 (rdi) r7d (edi) r7w (di)

r8 ~ r15

outro tipo de registrador da arquitetura x86 são as flags que sinaliza alguma alteração durante a execução de uma determinada instrução e podem ser usadas em algumas instruções especificas como por exemplo comparação e pulos, esses registradores citados são os registradores padrões da arquitetura x86 existem outros registradores como os de FPU que são registradores usados para operações com pontos flutuantes na arquitetura x86 em cima de um coprocessador o x87 (8087) ou internamente a partir do 80486, alem de outros registradores como os do MMX, SSE e de controle como o CR

2.2 – x86: Flags

as flags sinaliza alguma alteração feita por uma instrução sendo que algumas flags serve para o

Page 10: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

controle, elas são constantemente utilizadas para operações aritmeticas pelo processador, diferente dos demais registradores citados as flags são apenas um unico registrador de 16bits que cada bit dele corresponde a uma determinada flag, nem todo bit do registrador flag é uma flag sendo alguns são apenas bits reservados para usos futuros e não tem utilidade nenhuma e outras flags usadas a partir de algum processador especifico como o IOPL e NT que são flags usadas por maquinas 286+,como cada bit é uma flag então a própria flag só pode assumir dois estados sendo o numero 0 ou 1, podemos ver abaixo as flags correspondente em uma tabela e sua ordem de bit sendo o menos significativo o da direita

15bit 0bitNT IOPL IOPL OF DF IF TF SF ZF AF PF 1 CF

a arquiteturas x86 de 32bits o registrador da flag é de 32bits ele tambem é chamado de eflag e tem algumas flags novas que não tem em arquitetura x86 de 16bits, em arquitetura de 64bits o registrador das flags (rflag) é de 64bits porem não tem nenhuma flag nova comparado ao de 32bits

32bit 0bitNT IOPL IOPL OF DF IF TF SF ZF AF PF 1 CF

ID VIP VIF AC VM RF

x64 reservado

a flag CF (Carry Flag) é setada em 1 quando acontece um estouro no limite maximo de um registrador em uma determinada operação sem sinal, como já sabemos os registradores tem um tamanho maximo em bits que pode ser armazenado naquele registrador, em registradores de 16bits só podem ser armazenados numeros de 0 ate 65535 (0 ate ffff em hexadecimal) que é equivalente a 16bits (256 ^ 2), se em um registrador estiver o numero 65535 e a gente somar mais 1 a ele vai acontecer esse estouro no registradores sendo o resultado o numero 0 e sera setado a flag CF em 1 indicando esse estouro, o mesmo acontece quando o numero no registrador é 0 e subtraimos ele em 1 sendo o resultado final 65535 e a flag CF sera 1, outra flag é o PF (Parity Flag) que é utilizada para paridade em determinadas operações indicando se o resultado do numero foi par ou impar, sendo setado em 1 caso seja par ou em 0 caso seja impar, a flag AF tambem chamada de Auxiliary carry Flag é paracida com a carry flag (CF) sua diferença que ela funciona apenas nos 4 primeiros bits sendo no nibble do registrador muito em numeros do tipo BCD, a flag ZF (Zero Flag) é setada em 1 quando a operação resultar no numero 0, a flag SF (Signal Flag) sera setada em 1 quando o resultado de uma operação for numero negativo exemplo a subtração do numero 2 pelo 5, a flag OF (Overflow Flag) é parecida com a flag CF a sua diferença que ela atua em cima de numeros com sinais enquanto no CF os numeros são sem sinais, em registradores de 16bits armazenando numerossem sinais o numero que pode ser armazenado no registrador é entre 0 a 65535 já com sinal o numero é entre -32768 ate 32767, dependendo da operação pode ser setado varias flags ao mesmo tempo

1111111111111111 (65535) 1 (1)-------------------------- + 0 (0) flags setada = CF PF AF ZF

101 (5) 111 (7)

Page 11: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

-------------------------- –1111111111111110 (65534) flags setada = CF PF AF SF

a flag de controle DF (Directional Flag) controla a direção que sera lido e armazenado pelos registradores SI e DI, quando ela esta setada em 0 a leitura e escrita é da esquerda para direita e quando esta setada em 1 sera da direita para esquerda tanto na fonte quanto no destino, a flag TF (Trap Flag) é uma flag de controle ela é usada para executar o codigo passo a passo quando acontece uma exceção é usada para debugação, a IF (Interrupt Flag) é uma flag de controle das interrupções sendo ela usada para habilitar ou desabilitar as interrupções mascaradas, a flag IOPL (I/O Privilege Level) é usada a partir do 286 sendo ela a flag de controle de nivel de permissão tantodo modo protegido quando do modo longo, a flag NT (Nested Task) é usada para tarefas do modo protegido a partir do 286, a flag RF só é valida a partir do 386 e ela é usada para desativar temporariamente a debugação, a flag VM (Virtual 8086 Mode) só existe no 386 a diante e é usada para executar programas de forma virtual como se estivesse em modo real só que dentro do modo protegido. diferente dos demais registradores não é possível acessar diretamente uma flag porem existem instruções para modificar determinadas flags, são poucas instruções que permite modificar flag entre elas temos stc e clc que modifica o estado da flag CF, a instrução sti e cli modifica o estado da flag IF, a instrução std e cld modifica o estado da flag DF

Flag afetada Seta em 1 Seta em 0

CF stc clc

IF sti cli

DF std cld

outra forma de modificar o estado das flags seria usar a pilha de memoria (stack), para fazer isso jogamos a flag na pilha com a instrução pushf ou pushfd depois modificamos o valor nele e depois bastaria jogar na flag novamente com popf ou popfd

2.3 – x86: instrução de movimento

vamos sair um pouco da teoria e começar digitar alguns codigos nessa bagaça ;) , as primeira instruções que temos que aprender da linguagem assembly são as instruções de movimentos que permite atribuir valores aos registradores ou ate mesmo diretamente a memoria, uma dessas instruções de movimento na linguagem assembly da arquitetura x86 é a instrução mov, para a genteusar essa instrução temos que especificar o registrador e o valor que sera atribuido a ele ambos separados por virgula, se a gente atribuir um numero diretamente ao um registrador então estamos atribuido esse numero de forma imediata sendo que muitas arquiteturas não permite essa atribuição imediata principalmente as baseada em RISC, quando estamos usando um compilador que usa a sintaxe intel como o compilador nasm o registrador deve ser especificado antes do valor (instrução destino,fonte)

mov ax,10

quando estamos usando um compilador que a sintaxe é at&t como o compilador gas o valor que sera atribuido vem antes do registrador (instrução fonte, destino), outra diferença que a sintaxe at&t usa o cifrão para indicar que é um numero imediato e tambem usamos o porcentagem para indicar que é um registrador

mov $10,%ax

Page 12: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

caso o sistema ou arquitetura permita usar registradores maiores, podemos atribuir da mesma forma que o exemplo anterior um valor a um registrador de 32bits ou 64bits

mov rax,10

como o valor atribuido nos exemplos foram apenas o numero 10, e o próprio valor poderia ser armazenado em um registrador de apenas 8bits, nesse caso então podemos atribuir ele para um registrador de 8bits e depois o valor poderia ser lido normalmente no registrador 16bits ainda sendo o numero 10 (embora isso não seja muito recomendado)

mov al,10

se no exemplo anterior a gente armazenar ele no registrado “ah” ao inves do registrador “al” o resultado no registrador “ax” seria outro valor e não o valor 10, isso porque o registrador “ah” fica onumero mais significativo daquele registrador (sendo o valor final lido no registrador “ax” o numero 2560 e não o numero 10)

mov ah,10

o problema é quando tentamos atribuir valores maiores que o proprio limite daquele registrador, sendo que alguns compiladores não aceita retornando erro na compilação outros podem tratar o valor atribuido uma pequena parte dele, exemplo seria atribuir o valor 300 ao registrador al (8bits) não vai funcionar já que registradores de 8bits só permite numeros de 0 ate 255 (nesse caso teriamos que usar o registrador ax que é 16bits ou armazenar uma parte do valor no al e a outra no ah para totalizar o valor 300)

mov al,300

podemos atribuir da mesma forma valores para outros registradores gerais (a,b,c,d), sendo todos esses registradores de uso geral e podem ser usados para qualquer fim tirando algumas exceções

mov dx,500

na linguagem assembly usamos uma linha do codigo para cada nova instrução, ou seja a cada nova linha do nosso codigo é uma nova instrução (linhas vazias são ignoradas pelo compilador)

mov ax,80mov cx,60mov dx,90mov bx,10

se a gente atribuir mais de um valor ao registrador o valor antigo que estava armazenado nele sera substituido pelo novo valor

mov eax,10mov eax,50

dependendo do compilador o numero é tratado por padrão como decimal (base10) e em outros o

Page 13: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

numero por padrão é hexadecimal (base16) como acontece com o debug do windows, alguns compiladores como o nasm para a gente manipular o numero como hexadecimal temos que especificar 0x antes do próprio numero indicando que é um numero hexadecimal

mov bx,0xf5

outros compiladores para tratar o numero como hexadecimal usamos um h no final do numero (as vezes deve ser usado um 0 antes do numero), como acontece com o emu8086 e o nasm (sim o nasmpode usar as duas formas 0x e o h para indicar numeros hexadecimais)

mov ax,0f5h

alguns compiladores permite tratar numeros em binario (base2) com o 0b antes do numero

mov ch,0b110001

como tambem existem compiladores que usa b no final do numero indicando o binario

mov ch,110001b

boa parte dos compiladores tambem permite enviar caracteres ascii entre aspas, sendo ele convertido automaticamente para o tipo numerico equivalente

mov dx,'k'

para numeros negativos basta a gente usar o sinal antes do numero

mov ax,-10

o processador pode tratar tanto o numero com sinal ou numero sem sinal equivalente a ele, o numero -10 seria nada mais nada menos que o numero 0xfff6 sem sinal

mov ax,0xfff6

podemos atribuir um valor de um registrador para outro registrador, bastando especificar ele no lugar do valor (o seu valor sera mantido e ambos os registradores tera o mesmo valor)

mov ax,0x30mov bx,ax

lembrando que esses mesmos exemplos tambem vale para sintaxe at&t

mov $0x30,%axmov %ax,%bx

quando precisamos manipular uma quantidade de bytes exatos ou especificar a quantidade bytes enviado então devemos usar algumas palavras reservadas do compilador indicando que estamos transferido aquela determinada quantidade de bytes, compiladores baseado em sintaxe intel a palavra deve ser usada em conjunto com o valor que sera transferido, já em sintaxe at&t é usado

Page 14: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

uma letra em conjunto com a propria instrução, para transferir apenas byte devemos especificar a palavra byte

mov ah, byte 10

na sintaxe at&t devemos usar o b em conjunto como o mov indicando a transferencia do byte

movb $10,%ah

para indicar que estamos manipulando 16bits usamos a palavra reservada word na sintaxe intel

mov ax,word 0x1050

usamos o w em conjunto com a instrução mov na sintaxe at&t para o word

movw $0x1050,%ax

em 32bits usamos a palavra reservada dword na sintaxe intel

mov eax,dword 0x12345678

na sintaxe at&t usamos o l para indicar o dword

movl $0x12345678,%eax

para 64bits usamos a palavra reservada qword na sintaxe intel

mov rax,qword 0x123456789012345

no at&t usamos a letra q para o qword

movq $0x123456789012345,%rax

na sintaxe at&t existe uma instrução extra a movabs que seria equivalente ao movq

movabs $0x123456789012345,%rax

quando estamos mexendo com a arquitetura de 64bits podemos manipular os registradores extras sendo eles os registradores r8 ate r15

mov r8,315

o mesmo pode ser feito na sinataxe at&t

mov $315,%r8

os registradores de r0 ate r7 dependendo do compilador deve ser usado com o nome padrão, no compilador nasm podemos usar eles declarando a diretiva “%use altreg” sendo que sem essa

Page 15: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

diretiva o nasm não reconhece o r0 como rax

%use altregmov r0,100

outra instrução que podemos usar é o xchg que troca os valores entre dois registradores

mov ax,100mov bx,200xchg ax,bx

2.4 – x86: instrução aritmetica

tambem podemos fazer operações matematica usando a linguagem assembly, uma dessas operações é a adição que pode ser feita usando a instrução add, da mesma forma que a instrução mov temos que especificar o registrador que vamos utilizar e o valor que sera adicionado a ele, então se no registrador ax tiver o valor 300 e a gente adicionar mais 15 a ele, o valor final dentro do registrador vai ser 315

mov ax,300add ax,15

o mesmo pode ser feito na sintaxe at&t porem mudando a ordem da fonte e destino

mov $300,%axadd $15,%ax

podemos fazer o mesmo usando dois registradores

mov ax,300mov bx,100add ax,bx

tambem é possível fazer varias operações em sequencia

mov ax,300add ax,15add ax,20add ax,8

operações com registradores maiores como os de 32 e 64bits

mov ecx,5000mov edx,1000add ecx,edx

é possivel fazer operações com bases diferentes tambem

mov ax,0b1111

Page 16: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov bx,0x50add ax,bx

a instrução mov não afeta nenhuma flag por outro lado a instrução add sim, então se em um operação der como resultado par a flag PF sera setada, se em uma operação for numero negativo a flag SF sera setada, se em uma operação der um estouro com um resultado maior que o próprio registrador a flag CF sera setada (AF ou ate OF dependendo)

mov ax,0xffffmov bx,0x1add ax,bx ; flag setada = C Z P A

outra operação é a de subtração com a instrução sub, o seu uso é parecido com a instrução add

mov ax,50sub ax,30

o mesmo pode ser feito na sintaxe at&t

mov $50,%axsub $30,%ax

podemos subtrair um registrador de outro

mov ax,60mov cx,5sub ax,cx

tambem é possível subtrair por um numero maior sendo que o resultado sera negativo (alem de setara flag SF e outras flags)

mov ax,40mov dx,60sub ax,dx

podemos fazer operações de multiplicação com a instrução mul, sendo uma das diferenças da instrução mul que obrigatoriamente deve ser usado o registrador ax, temos que passar outro registrador que sera multiplicado pelo registrador ax, o resultado tambem sera salvo no próprio registrador ax

mov ax,15mov bx,2mul bx

outra instrução que permite fazer a multiplicação é o imul, o uso dessa instrução é parecida com adde sub permitindo escolher o registrador e o valor

mov bx,10imul bx,6

Page 17: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

tambem podemos multiplicar valores armazenado em outro registrador sem ser de forma imediata

mov bx,10mov ax,6imul bx,ax

outra forma de usar o imul seria especificar apenas um registrador como na instrução mul, sendo usado e armazenado obrigatoriamente no registrador ax

mov ax,15mov bx,2imul bx

outra diferença entre mul e imul que a instrução mul é uma multiplicação sem sinal enquanto o imulé uma multiplicação com sinal, em ambos os casos é usado o registrador dx como segmento em conjunto com o ax (dx:ax), caso o numero seja muito maior que o registrador ax sera armazenado a parte mais significativa dele no dx (tambem sera setado a flag CF caso isso ocorra com a instrução mul ou a flag OF na instrução imul)

mov ax,65535mov bx,1000mul bx ; dx:ax = 65535000

tambem podemos fazer a divisão usando a instrução div e o seu uso é parecido com o mul, então armazenamos um valor no registrador ax depois fazemos a divisão de um outro registrador por ele sendo que sera armazenado no próprio registrador ax

mov ax,100mov bx,5div bx tambem existe a instrução idiv que permite a gente escolher os dois registradores que vamos dividir

mov bx,100mov cx,5idiv bx,cx

da mesma forma que o imul podemos apenas especificar um registrador no idiv que e sera dividido e armazenado obrigatoriamente o registrador ax

mov ax,100mov bx,5idiv bx

o resto de uma divisão tambem chamado de modulo sera armazenado no registrador dx

mov ax,10mov bx,3

Page 18: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

div bx ;ax = 3, dx = 1

não é possível fazer uma divisão onde o divisor é maior que o dividendo, o resultado final sera 0

mov ax,10mov bx,12div bx

podemos incrementar um valor em um registrador com a instrução inc, seria equivalente a adicionaro valor em mais 1

mov ax,100inc ax

tambem podemos decrementar um valor em um registrador com o dec, essas instruções subtrair o registrador em menos 1

mov ax,100dec ax

a instrução adc faz a adição em conjunto com carry, o seu diferencial da instrução add seria se a flagCF estiver setada em 1 sera incrementado o registrador destino em 1 tambem

mov ax,1mov bx,1stcadc ax,bx

tambem existe a instrução sbb que faz a subtração em conjunto com o carry, o sbb alem de fazer a subtração como a instrução sub ele tambem decrementa o registrador destino em 1 caso a flag CF estiver setada em 1

mov ax,5mov bx,2stcsbb ax,bx

é possivel fazer operações bit a bit em assembly entre elas temos a operação and, a operação and testa bit por bit entre os dois numeros formando um terceiro numero com base nos dois bits dos doisnumeros, os bits do primeiro numero é testado com os bits do segundo na sua posição equivalente, se os dois bits do primeiro e do segundo forem iguais a 1 (verdadeiro) sera gerado um bit tambem igual a 1 verdadeiro, se os dois bits forem 0 (falso) o bit gerado sera 0, se os dois forem diferentes um do outro tambem sera falso, no assembly podemos fazer a operação usando a instrução com o mesmo nome no caso o “and”, seu funcionamento é parecido com as outras instruções como add, sub e imul com o diferencial que a operação feita sera a and

mov ax,153mov bx,247and ax,bx

Page 19: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

existe a operação bit a bit or, nessa operação se um dos bits for verdadeiro ou seja 1, o resultado sera um bit verdadeiro, se ambos forem verdadeiros o bit tambem sera verdadeiro, sera falso apenas quando os dois bits forem falsos ou seja quando ambos bits estiver setados em 0

mov ax,153mov bx,247or ax,bx

a operação bit a bit xor ou “e exclusive” tera o bit verdadeiro se os dois bits forem diferente um do outro ou seja se em um numero o bit for verdadeiro e no outro numero o bit for falso

mov ax,153mov bx,247xor ax,bx

o xor é usado constantemente para zerar um determinado registrador usando ele proprio

mov ax,153xor ax,ax

outro uso para o xor é a troca de valores entre dois registradores sendo igual a instrução xchg

mov ax,153mov bx,247

xor ax,bxxor bx,ax ;bx = 153xor ax,bx ;ax = 247

outra operação bit a bit é o not ou negação que tambem é chamada de complemento de um, diferente das outras operações bit a bit essa funciona em apenas um único numero sendo os bits invertido, caso o bit seja verdadero sera falso como resultado ou se era falso sera verdadeiro

mov ax,153not ax

outra forma de fazer a operação not é subtrair o maior numero aceito no registrador pelo numero que sera invertido, no caso dos registradores ax o maior numero é 65535 (0xffff) já que é um registrador de 16bits

mov ax,65535mov bx,153sub ax,bx

tambem existe a operação bit a bit neg que seria o complemento de dois, na operação neg é feito a operação not e somado mais um bit ao numero menos significativo dele

mov ax,153

Page 20: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

neg ax

outra forma de fazer o complemento de dois como já citado é usar a operação not e adicionar mais um bit a ele isso seria equivalente a operação neg

mov ax,153not axadd ax,1b

com a shl podemos usar a operação bit a bit shift left (deslocar para esquerda), essa operação deslocas os bits a esquerda adicionando o bit 0 a direita do numero, o seu uso é bem simples bastando especificar o registrador e quantidade de deslocamento a esquerda

mov ax,15shl ax,2

tambem podemos deslocar os bits a direita com a instrução shr, no deslocamento a direita o bit menos significativo sera perdido

mov ax,15shr ax,2

para deslocamento com sinal para esquerda usamos a instrução sal

mov ax,15sal ax,2

e para o deslocamento com sinal para direita usamos a instrução sar

mov ax,15sar ax,2

as vezes estamos trantando de um numero de 8bits (byte) e precisamos tratar desse mesmo numero como 16bits (word), o problema de fazer isso é quando estamos mexendo com numeros com sinais já que os bits mais significativo é aqueles que indica se o numero é positivo ou negativo, a função cbw converte o registrador al para 16bits formando o novo valor no registrador ax, dependendo do valor que esta no registrador al o valor de ah sera 0x0 caso o numero em al for de 0 a 127 sendo o numero final positivo, se o no registrador al estiver o numero entre 128 e 255 o valor em ah sera o numero 0xff indicando negativo no ax

mov al,130cbw

tambem é possível converter de 16bits para 32bits usando cwd, a diferença que na instrução cwd sera usado todo o registrador ax e sera armazenado o restante no registrador dx, caso o valor for de 0 a 32767 sera positivo ou seja o numero armazenado em dx sera igual a 0, caso o valor seja de 32768 ate 65535 o numero sera 0xffff indicando o numero negativo em dx:ax

mov ax,40000

Page 21: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

cwd

o mesmo pode ser feito no registrador de 32bits para 64bits usando a instrução cdq, a instrução cdq só existe em processadores a partir do 80386

mov eax,0x123456cdq

podemos converter um numero que esteja como formato caracter ascii para tipo numerico usando a instrução aaa

mov ax,'5'aaa

2.5 – x86: instrução de pulo

existem instruções que permite pular para determinada parte do codigo permitindo abstrair ou criar um determinado fluxo de execução, podemos pular caso ocorra determinada condição como uma flag esta setada ou ate mesmo um valor especifico em determinado registrador para que ocorra esse pulo sobre aquela determinada condição, uma instrução que não tem grande utilidade é a instrução nop (no operation), essa instrução pula para a próxima instrução não faz nada alem disso, ela apenasé usada para gastar ciclos de maquina sendo muito usadas para criar temporizadores como o delay

nop

podemos usar a instrução jmp para pular para um determinado endereço de codigo na memoria de forma incondicional, a instrução jmp é uma instrução absoluta ou seja vamos pular para aquele trecho absoluto da memoria que a gente especificou nela como endereço, quando passamos apenas o endereço para a instrução jmp o segmento é mantido no caso o registrador CS não sera afetado apenas o registrado IP

jmp 0x200

o mesmo pode ser feito na sintaxe at&t porem para endereços não precisamos colocar o cifrão comoacontece em valores imediatos

jmp 0x200

tambem pode ser feito com um registrador ao inves de um endereço

mov ax,0x200jmp ax

tambem é possível dar um pulo com a instrução jmp entre segmentos o famoso far jump, para fazer isso basta especificar o segmento seguido do offset onde vamos pular, quando é o far sera alterado tanto o registrador de segmento CS como o registrador IP

jmp 0x710:0x100 ;cs = 0x710, ip = 0x100

Page 22: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

para a gente pular para um trecho especifico temos que usar uma label como referencia daquele determinado trecho na memoria, isso tambem seria uma especie de pulo relativo no codigo já que não precisamos saber o real endereço para onde temos que pular apenas o nome da label, para criar uma label escrevemos qualquer nome sendo que não pode conter espaços ou caracteres especiais e deve terminar com dois pontos

kodo:

na instrução jmp a gente especifica o nosso label que vamos pular

jmp kodokodo:

como foi dito o jmp é um pulo incondicional então no exemplo abaixo nenhuma instrução depois dojmp e antes do label sera executada

jmp kamimov ax,10add ax,50kami:

quando pulamos para tras temos que tomar cuidado para não prender o programa em um loop infinito

kami:jmp kami

dependendo do nosso programas as vezes precisamos prender ele em um loop infinito, como por exemplo um contador infinito que o programa nunca termina

mov ax,0kami:inc axjmp kami

outra forma de fazer um pulo relativo em alguns compilador é usando o cifrão que indica a posição atual (lembrando que o $ é usado na sintaxe at&t indicando numero imediato), se a gente der um pulo para o cifrão seria um pulo para a própria instrução jmp ficando preso nela

jmp $

alguns compiladores permite somar um valor ao cifrão indicando um pulo relativo a partir da posição atual

jmp $ + 5

tambem é possível subtrair o valor do cifrão para dar um pulo para antes dele porem lembrando que nesse caso vai ficar preso em um loop infinito já que vai executar o jmp novamente e ficar voltando

Page 23: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

jmp $ - 5

uma forma simples de contornar o problema anterior de voltar sem ficar preso em um loop seria alguma coisa parecida com isso (o mesmo poderia ser feito com label tambem)

jmp inicio ;pula para o inicio usando o labelmov ax,1jmp $ + 4 ;pula para o add de forma relativa tambeminicio:jmp $ - 5 ;pula para o mov, veja que pulamos de forma relativa aquiadd ax,1

temos que tomar cuidado quando usar o jmp para não ir em uma parte da memoria desconhecida que a gente não tenha criado nada nela ainda já que pode conter uma instrução aleatoria naquela parte inclusive instruções valida que sera executada pelo computador, para evitar isso pule apenas para endereços conhecidos ou com label que você tenha criado e se certifique que o programa vai ser finalizado corretamente dependendo do sistema ou ficar preso em um loop infinito no final da execução evitando executar instruções aleatorias da memoria (embora boa parte dos compiladores pra determinado sistema já corrige esse pequeno problema)

mov ax,10add ax,5

fim:jmp fim

tambem existem os pulos condicionais que permite pular para determinado trecho caso seja satisfeita determinada condição ou se alguma determinada flag especifica estiver setada, entre esses pulos condicionais temos a instrução je (jump if equal) que permite pular caso a condição seja igual (para definir uma condição como igual a flag ZF deve esta setada com o valor 1 ou seja basta fazer uma operação que o resultado final seja zero)

mov ax,5xor ax,axje igualmov ax,100igual:mov ax,200

caso a flag ZF não estiver setada em 1 seria equivalente a uma condição diferente e nesse caso não aconteceria o pulo pelo je passando para próxima instrução depois dele

mov ax,5xor ax,3je igualmov ax,100igual:mov ax,200

se a gente der uma analisada no codigo anterior não faz tanto sentido, já que se o valor não for igual

Page 24: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

ele não vai pular com isso o registrador ax recebe 100 só que em seguida o mesmo registrador ax recebe 200 apagando o valor anterior, para contonar isso damos um pulo incondicional para depois da instrução para evitar a segunda atribuição a ele

mov ax,5xor ax,3je igualmov ax,100jmp proximoigual:mov ax,200proximo:

tambem é possível pular de forma relativa ou para um endereço absoluto, o je do próximo exemplo seria equivalente ao do anteriror pulando ambos para o “mov ax,200” caso seja igual (ZF = 1)

mov ax,5xor ax,axje $ + 7mov ax,100jmp proximomov ax,200proximo:

como podemos ver nos exemplos anteriores a operação bit a bit xor é perfeita para comparar dois valores e saber se são iguais, isso acontece na operação xor devido os bits ser os mesmos com isso zerando todos eles e dando o resultado final 0

mov ax,315mov bx,315xor ax,bxje pulo

pulo:

o problema de usar o xor que ele altera o registrado usado perdendo o valor, outra instrução semelhante e ate especifica para esse caso é a instrução cmp usado para comparar dois valores sem afetar os registradores, o seu uso normalmente é feito antes dos pulos condicionais e ele afeta apenas as flags e a comparação é feita do lado do destino para a fonte (na sintaxe intel seria da esquerda para direita e na sintaxe at&t da direita para esquerda)

mov ax,315mov bx,315cmp ax,bxje pulo

pulo:

outro tipo de pulo condicional é o jne (jump if not equal), sendo que essa instrução apenas vai pular para aquele trecho se os valores forem diferentes ou seja se a flag ZF estiver setado em 0, caso o numero seja igual mão vai pular continuando a execução do codigo depois da instrução

Page 25: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ax,100mov bx,315cmp ax,bxjne pulo

pulo:

tambem podemos comparar se o numero em questão é maior que o outro usando a instrução jg (jump if greater), caso o numero seja maior ele ira pular para o endereço na instrução jg caso não seja ira continuar a execução depois dela

mov ax,200mov bx,100cmp ax,bxjg pulo

pulo:

na comparação entre o igual e diferente é usado a flag ZF indicando igual caso ela esteja setada em 1 ou diferente caso ela esteja setado em 0, na comparação para o maior usamos três flags sendo a SFe OF que devem estar setadas com o mesmo valor e a flag ZF deve esta setada com o valor 0, se as duas flags SF e OF estiver setadas em 1 e a ZF em 0 sera equivalente a comparação maior, ou se todas as tres flags estiver setada em 0 tambem sera equivalente a comparação do maior

mov ah,127mov bh,2add ah,bh jg pulo

pulo: com a instrução jl (jump if lesser) comparamos se o numero é menor que o outro e pulando na instrução jl caso seja

mov ax,200mov bx,500cmp ax,bxjl pulo

pulo:

na comparação do menor as flags usadas são as mesmas que a comparação maior (SF, OF e ZF), o diferencial da comparação menor são as flags SF e OF que devem ser diferentes uma da outra ou seja enquanto uma precisa esta setada em um a outra precisa esta setada em 0

mov ah,-100mov bh,2sub ah,bhjl pulo

Page 26: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pulo:

tambem temos a instrução que compara se o valor é maior ou igual ao segundo sendo essa instruçãoo jge (jump if greader or equal), essa instrução seria equivalente ao jg e je junto, sera pulado caso o numero seja maior ou igual

mov ax,500mov bx,315cmp ax,bxjge pulo

pulo:

as flags usada na comparação são SF, OF e ZF, nesse caso tanto faz se a flag SF e OF forem iguais indicando que é maior ou se a flag ZF for igual a 1 indicando que é igual

xor ax,axjge pulo

pulo:

da mesma forma tambem temos a instrução jle (jump if lesses or equal) que define se o valor é menor ou igual, caso seja igual sera pulado na instrução jle para aquele endereço

mov ax,315mov bx,500cmp ax,bxjle pulo

pulo:

na comparação do jle as flags usadas são as mesmas que jge com o diferencial que as flags SF e OF devem ser diferentes uma da outra indicando que o numero é menor ou então a flag ZF deve esta setada em 1 indicando que é igual

mov ah,-100mov bh,2sub ah,bhjle pulo

pulo:

existe a comparação sem sinal tambem, para comparar se um numero é maior do que o outro sem sinal usamos a instrução ja (jump if above)

mov ax,65000mov bx,10000cmp ax,bxja pulo

Page 27: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pulo:

na instrução ja é usado as flags CF e a ZF para testa se a condição é satisfeita, então se ambas as flags estiver setadas em 0 sera considerado o numero sendo maior

clcja pulo

pulo:

para comparar se um numero é menor do que o outro usamos a instrução jb (jump if below), caso numero seja menor sera pulado na instrução jb

mov ax,1000mov bx,5000cmp ax,bxjb pulo

pulo:

da mesma forma é usado a flag CF na instrução jb, com a diferença que se o CF estiver setado em 1 sera feito o pulo

stcja pulo

pulo:

tambem podemos comparar se é maior ou igual com a instrução jae (jump if above or equal)

mov ax,5000mov bx,1000cmp ax,bxjae pulo

pulo:

nessa comparação é usado tanto a flag CF quanto ZF em 0 ou apenas a flag ZF em 1 para que ocorra o pulo

xor ax,axjae pulo

pulo:

outra instrução é o jbe (jump if below or equal) que compara se o numero é menor

mov ax,1000

Page 28: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov bx,5000cmp ax,bxjbe pulo

pulo:

no jbe é comparado se a flag CF esta setada em 1 ou se a flag ZF esta setada em 0

mov ax,0xffffadd ax,100jae pulo

pulo:

tambem podemos pular se a flag CF estiver setada em 1 com a instrução jc

stcjc pulo

pulo:

ou se a flag CF estiver com o valor 0 usando a instrução jnc

clcjnc pulo

pulo:

é possível pular caso a flag OF estiver setada em 1 usando a instrução jo

mov ah,127add ah,10jo pulo

pulo:

ou pular caso a flag OF estiver setada em 0 usando a instrução jno

mov ah,100add ah,10jno pulo

pulo:

outro tipo de pulo é o jp que permite pular caso a flag PF estiver com valor 1

mov ah,9add ah,1jp pulo

Page 29: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pulo:

com o jnp podemos pular caso a flag PF esteja com o valor 0

mov ah,10add ah,1jnp pulo

pulo:

com a instrução js podemos pular caso a flag SF estiver setada em 1

mov ax,100sub ax,200js pulo

pulo:

tambem podemos pular se a flag SF estiver setada em 0 usando a instrução jns

mov ax,100add ax,200jns pulo

pulo:

na flag ZF usamos a instrução jz para pular caso essa flag estiver setada em 1

xor ax,axjz pulo

pulo:

como tambem podemos usar o jnz para pular caso a flag ZF estiver em 0

mov ax,5mov bx,10add ax,bxjnz pulo

pulo:

esses pulos condicionais são parecidos com as estruturas condicionais em linguagens de alto nivel como o if na linguagem C, não apenas parecido já que linguagens de alto nivel quando são compiladas para a linguagem da maquina são criado esses pulos condicionais no próprio binario

int var = 10; //mov ax,10if(var == 10) //cmp ax,10

Page 30: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

{ //jne kodo } else //kodo:{}

os pulos condicionais e incondicionais pode ser usados tambem para criar loops usando contadores, uma forma simples seria atribuir um numero a um registrador incrementando e comparando ate chegar a determinado valor especifico

mov ax,0mov bx,100

inicio:cmp ax,bxjge fiminc axjmp inicio

fim:

não necessariamente precisa ser um contador que se incrementa, podemos criar um contado que se decrementa ou que testa qualquer outra condição ou qualquer outro tipo de flag

mov ax,0xffff

inicio:jz fimdec axjmp inicio

fim:

o codigo anterior de repetição é bem semelhante com uma estrutura de repetição while já que vai repetir enquanto uma condição for satisfeita ou seja enquanto a codição não for 0 (falso)

int x = 0xffff; //mov ax,0xffff //inicio:while(x == 0){ //jz fim x--; //dec ax} //jmp inicio //fim:

outra forma da gente fazer uma repetição de determinado trecho do codigo em assembly seria usar instruções especificas da própria linguagem como o loop, essa instrução vai repetir um determinadotrecho enquanto o registrador cx não for igual a 1, toda vez que o programa chega na instrução loop ele vai decrementar o registrador cx e pular para determinado endereço caso o cx seja diferente do valor 1 (o loop se assemelha bem a estrutura de repetição for em linguagens de alto nivel)

mov cx,20

Page 31: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo:loop kodo

da mesma forma podemos modificar o registrador cx dentro da repetição e cessar o loop a qualquer momento (o famoso break em linguagens de alto nivel)

mov cx,20kodo:mov cx,1 ;breakloop kodo

a instrução loopz permite usar o registrador cx como contador e tambem usar a flag ZF para cessar o loop, caso a flag ZF estiver setado em 0 o loop sera cessado, só fazendo o loop enquanto ela estiver setada em 1

mov cx,20xor ax,axkodo:loopz kodo

tambem existe a instrução loopnz que permite o contador no cx e sera cessado o loop apenas se a flag ZF estiver setada em 0

mov cx,20mov ax,1add ax,1kodo:loopnz kodo

2.6 – x86: Subrotina

em algumas situações precisamos executar os mesmo codigos no nosso programa varias vezes seguidas em trechos diferente do programa, para facilitar podemos criar um pequeno trecho de codigo e reaproveitar ele bastando pular para esse trecho quando precisar executar aquele determinado trecho de codigo, isso evitaria gastos desnecessario tanto para memoria quanto para tempo de criar o mesmo codigo varias vezes no nosso programa, para criar uma subrotina não bastaria apenas pular para aquele determinado trecho já que a execução depois do pulo seria dali emdiante ou seja o programa ia se perder depois daquele pulo, para que uma subrotina seja criada temos que voltar para o ultimo ponto da onde pulamos no final da execução dela, a forma de fazer isso seria pegar o registrador IP antes do pulo sendo que isso é uma parte do problema já que não manipulamos o IP diretamente diferente de outros registradores que podemos mover o valor, em alguns compiladores é possivel de forma bem simples já que podemos pegar o endereço atual como acontece no nasm com o cifrão embora isso seja estatico e não uma instrução da arquitetura, se a gente tem o endereço atual armazenado podemos pular para a subrotina e voltar dela para ultimo ponto assim o programa não se perdendo depois do pulo, como programa vai voltar para o ultimo ponto tambem temos que atribuir ao endereço a quantidade de bytes da própria instrução que estamos pegando o endereço e a instrução de pulo para não executar ela novamente e fica preso em um loop, o exemplo abaixo a gente pula para subrotina e depois volta para o ultimo endereço

lea ax,$

Page 32: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

add ax,8jmp kodo

kodo:jmp ax

a subrotina anterior é apenas um exemplo arcaico de uma subrotina criado na marra por pulos e usando os registradores para armazenar o endereço, existem metodos mais simples de se criar uma subrotina inclusive instruções da própria arquitetura que permite criar subrotinas, a forma mais simples que a anterior seria usar a instrução call para pular para subrotina já com endereço armazenado na memoria e para voltar usamos a instrução ret (bem mais simples não é? )

call kodo

kodo:ret

se a gente analisar uma função em alto nivel seria equivalente a uma subrotina em baixo nivel com suas diferenças e semelhanças, uma delas que podemos chamar a subrotina quantas vezes a gente quiser

call kodocall kodocall kodo

kodo:ret

normalmente em linguagens de alto nivel as funções devem ser criadas antes de ser usadas já na linguagem assembly podemos criar todas as subrotinas no final do codigo sem problema, nesse casodevemos tomar cuidado para não deixar o programa executar elas colocando um loop infinito no final do codigo antes da subrotina

call kodo

jmp $

kodo:ret

tambem podemos criar elas no começo do codigo nesse caso usamos um pulo incondicional para não executar ela

jmp main

kodo:ret

main:call kodo

Page 33: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

podemos criar quantas subrotinas a gente quiser ou precisar

jmp main

kodo:ret

kami:ret

main:call kamicall kodo

podemos passar e retornar valores nas subrotinas usando os registradores

jmp main

kodo:add ax,bxret

main:mov ax,50mov bx,10call kodo

podemos usar a pilha de memoria para passar valores usando a instrução push para empilhar e o poppara desempilhar da memoria, a vantagem de passar valores usando a pilha que seria possível passaruma quantidade maior de valores, o primeiro a ser empilhado sera sempre o ultima e ser desempilhado seguindo essa ordem (LIFO)

jmp main

kodo: pop dx ;desempilha endereço para dxpop ax ;desempilha 10 para axpop bx ;desempilha 50 para bxadd ax,bx ;soma ax e bxpush ax ;empilha o ax push dx ;empilha endereço para voltarret

main:push 50push 10call kodopop ax ;desempilha o 60 no ax

como podemos reparar no exemplo anterior é possível pegar o endereço de retorno, sendo que isso

Page 34: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

seria uma forma de manipular o registrador IP indiretamente

jmp main

kodo:pop axpush axret

main:call kodo

inclusive podemos ate forçar o retorno para outro endereço

jmp main

kodo:pop axmov ax,0x600 push axret

main:call kodo

tambem é possível chamar uma subrotina dentro da outra, nesse caso ele vai voltar sempre para o ultimo call que foi chamado e dele para o anterior

jmp main

kodo:call kamiret

kami:ret

main:call kodo

em linguagens de alto nivel quando uma função chama ela mesma isso é chamado de função recursiva, em asm tambem podemos criar uma subrotina que chama ela mesmo

jmp main

kodo:call kodoret

main:call kodo

Page 35: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

subrotinas recursivas pode ser um grande problema já que é armazenado o endereço atual na memoria do computador e com isso tambem lotando toda a memoria do computador com os endereços, uma forma simples de usar rotinas recursivas e evitar esse problema seria criar um contador e quando ultrapassar determinado limite vai parar a chamada e retornar para anterior que por sua vez tambem vai voltar para o anterior e o anterior ate voltar na primeira chamada

jmp main

kodo:dec cxjz kamicall kodokami:ret

main:mov cx,100call kodo

é muito comum o uso do registrador bp para correr a pilha de memoria em uma subrotina para faciltar o acesso aos dados nela, para o uso do base pointer temos que criar o stack frame bastando empilhar o registrador bp, depois mover o registrador sp para dentro do registrador bp, depois o trecho do nosso codigo da subrotina

jmp main

kodo:push bpmov bp,sp ; codigo da subrotinaret

main:

call kodo

depois do codigo da nossa subrotina temos que fazer o inverso destruir o stack frame, para isso basta mover o registrador bp para sp, depois desempilhar o bp e retornar

jmp main

kodo:push bpmov bp,sp ; codigo da subrotinamov sp,bppop bpret

main:

Page 36: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call kodo

com o ponteiro base podemos acessar a pilha com maior facilidade de uma forma indexada pelo registrador bp sem precisar desempilhar para o acesso

jmp main

kodo:push bpmov bp,spmov cx,[bp + 4] ; acesso ao valor 0x5 sem precisar desempilharmov ax,[bp + 6] ; acesso ao valor 0x30 sem precisar desempilharmov bx,[bp + 8] ; acesso ao valor 0x10 sem precisar desempilharmov sp,bppop bpret

main:push 0x30push 0x5push 0x10call kodo

a posição no acesso indexado depende exclusivamente do tamanho do registrador que estamos manipulando, então se a gente estiver manipulando registradores maiores como 32bits o acesso indexado sera a cada 4 bytes e não apenas 2 bytes

jmp main

kodo:push ebpmov ebp,espmov eax,[ebp + 8]mov ebx,[ebp + 12]mov esp,ebppop ebpret

main:push 0x30push 0x80call kodo

lembrando que o mesmo pode ser feito em compiladores que usa a sintaxe at&t

jmp main

kodo:push %ebpmov %esp,%ebp

Page 37: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov 8(%ebp),%eaxmov %ebp,%esppop %ebpret

main:push $0x40call kodo

tambem pode ser usado a instrução enter para criar o stack frame, e a instrução leave para destruir o stack frame sendo essas instruções validas a partir do 80286

jmp main

kodo:enter 0,0mov eax,[ebp + 8]leaveret

main:push 0x30call kodo

dependendo do compilador podemos escrever parte do nosso codigo em outro arquivo e depois incluir esse arquivo no codigo por alguma diretiva, podemos criar subrotinas em arquivos separadose reaproveitar elas em outros codigos futuros, no compilador nasm usamos a diretiva %include seguido do arquivo que vamos incluir (sera incluido o codigo do arquivo exatamente onde declaramos ele)

%include “kodo.inc”

no compilador gas usamos a diretiva .include seguido do arquivo que vamos incluir

.include “kodo.inc”

2.7 – x86: Acesso a memoria

podemos ler e escrever diretamente ou indiretamente na memoria do computador, a memoria é usada para diversos fins como armazenamento de dados seja eles dinamico como as variaveis ou estatico como as constantes, a memoria pode ser usada para passar dados para as subrotinas ou retornar dados das subrotinas, a memoria é usada para executar os codigos do programa de forma sequencial ou seja todo codigo assembly é jogado na memoria do computador e executado a partir dela, a memoria é usada para ter acesso direto aos perifericos do computador já que são mapeados diretamente em algum endereço de memoria, ela tambem é usada para manipular uma grande quantidade de dados entre outras coisas, como já sabemos quando ligamos o computador ele inicia em modo real lendo apenas no maximo 1MB de memoria depois que ele passa para modo protegidoele consegue acessar mais de 1MB, uma das diferenças entre o modo real e o protegido seria a forma de acesso a memoria, no modo real a gente acessa os dados na memoria diretamente pelo endereço que seria o segmento e o offset ambos de 16bits, já no modo protegido isso funciona por tabela de descritores que pode ter um tamanho relativo, no modo real o segmento e o offset aponta

Page 38: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

para um endereço absoluto da memoria ou seja aquele endereço absoluto onde sera acessado vai depender tanto do segmento quanto do offset (segmento:offset), a cada novo segmeto seria equivalente a 16 offsets sendo o segmento uma forma de acesso alto para os offsets, para a gente saber o endereço absoluto onde estamos acessando pelo segmento e offset basta multiplicar o segmento por 16 (0x10) e por fim somar com o offset que vamos obter o endereço absoluto (quandoestamos tratando do numero em formato hexadecimal podemos apenas adicionar um 0 no final do segmento sem precisar multiplicar ele por 0x10)

endereço absoluto = segmento * 16 + offset é possível ter o acesso ao mesmo endereço absoluto com segmentos e offsets diferentes, lembrando que tanto o segmento quanto o offset são de 16bits ou seja só existe 65535 (0xffff) tanto para o segmento quanto para o offset, os dois com valor mais alto totaliza quase 1mb de memoria por isso que é possível acessar apenas 1MB de memoria em modo real

Endereço absoluto segmento: offset

16 (0x10) 0:16

16 (0x10) 1:0

3.700 (0xe74) 200:500

112.315 (0x1b6bb) 7000:315

1.114.095 (0x10ffef) 65535:65535

podemos mover um valor de um endereço da memoria para um registrador usando a instrução mov, na sintaxe intel para especificar que estamos manipulando um endereço de memoria temos que colocar entre colchetes dependendo do compilador, quando manipulamos valores na memoria isso échamado de acesso direto

mov ax, [500]

na sintaxe at&t para a gente especificar que estamos mexendo com valores diretamente da memoria basta apenas colocar o endereço sem o cifrão

mov 500,%ax

tambem é possível mover um valor de um registrador para a memoria

mov ax,100mov [500], ax

podemos mover um valor imediato para memoria

mov [500], 0x61

é possível armazenar um valor em um determinado registrador depois acessar aquele valor como endereço diretamente no registrador colocando entre colchetes

mov ax,500

Page 39: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov bx, [ax]

na sintaxe at&t o registrador deve ficar entre parênteses

mov $500,%axmov (%ax),%bx

não tem como mover um valor da memoria diretamente para memoria deve ser passado para um registrador geral antes

mov ax,[500]mov [600],ax

tambem não é possível acessar diretamente outro segmento apenas especificando ele, para fazer issotemos que usar registradores de segmentos como o DS ou ES em conjunto com o endereço

mov ax,[ds:500]

da mesma forma é possível armazenar um valor na memoria para um segmento diferente

mov ax,100mov [ds:500],ax

não é possível atribuir um valor diretamente a um registrador de segmento, temos que jogar o valor em um registrador de uso geral e dele para o registrador de segmento

mov ax,600mov ds,axmov ax,[ds:500] ; 600:500

quando a gente muda o segmento de dados ds qualquer outro endereço da memoria que a gente acessar sem especificar o segmento sera o mesmo do segmento ds já que ele é o segmento de dados principal do programa

mov ax,600mov ds,axmov ax,[400] ; 600:400

as vezes precisamos acessar outro segmento sem ter que mudar o próprio segmento ds, para fazer isso podemos usar os segmentos extras de dados como o es, fs e gs

mov ax,600mov es,axmov bx,[es:300]

o registrador de segmento cs aponta para onde o nosso codigo esta sendo executado a sua modificação seria simplesmente um far jump, podemos usar o registrador cs para criar sub codigos no nosso programa ou diversos programas de forma modular no nosso sistema

Page 40: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

jmp 600:100

todo o nosso codigo assembly é convertido para bytes equivalente aos opcodes e jogado na memoria dessa forma para ser executado, o registrador cs aponta para esses bytes e dele é executadosequencialmente cada um dos opcodes, como os opcodes são sequencais de bytes isso nos permite criar codigos na memoria para ser executado de forma dinamica em tempo de execução

mov ax,0x500mov ds,ax

mov [ds:0x100],0xb8 ;mov ax,10mov [ds:0x101],0x0amov [ds:0x102],0x0

mov [ds:0x103],0xbb ;mov bx,20mov [ds:0x104],0x14mov [ds:0x105],0x0

mov [ds:0x106],0x3 ;add ax,bxmov [ds:0x107],0xc3

mov [ds:0x108],0xeb ;jmp 0x108mov [ds:0x109],0xfe

jmp 0x500:0x100 o registrador de segmento ss aponta para o segmento de memoria onde esta a pilha (stack) e o registrador sp aponta para o topo da pilha, a pilha de memoria funciona com base em LIFO sendo o primeiro a entrar sera o ultimo a sair seguindo essa ordem, podemos imaginar a pilha como uma pilha de cartas onde empilhamos uma carta em cima da outra, quando precisamos tirar uma carta especifica temos que retirar todas as de cima dela primeiro, para empilhar usamos a instrução push

mov ax,315push ax

tambem podemos empilhar um determinado valor de forma imediata

push 315para desempilhar usamos a instrução pop seguido do registrador onde vamos armazenar

pop ax

quando empilhamos dois ou mais valores os ultimos a ser empilhados sera os primeiros a ser desempilhados assim como os primeiros a ser empilhado sera os ultimos a ser desempilhados

push 315push 100push 200pop ax ;desempilha o 200pop bx ;desempilha o 100

Page 41: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pop cx ;desempilha o 315

quando empilhamos alguma coisa o registrador sp é decrementado pela quantidade de bytes que o sistema esta manipulando, então por isso que é dito que a pilha cresce para baixo devido essa decrementação do sp, quando estamos mexendo com uma arquitetura de 16bits a pilha sera decrementada em 2bytes, quando mexemos com arquitetura de 32bits a pilha sera decrementada em4bytes, esse tamanho da decrementação da pilha é fixo não pode ser alterado inclusive o tamanho da pilha as vezes é limitado ao tamanho de algum registrador, quando desempilhamos alguma coisa sera feito o inverso incrementado o registrador sp, o registrador sp sempre vai estar apontando para o topo da pilha que é o ultimo dado empilhado nela

| 315 | 100V 200 ← SP

como a pilha são os bytes armazenado naquela parte da memoria e segmento podemos recriar o proprio push e pop, para refazer a instrução push bastaria decrementar pelo tamanho dos dados e armazenar naquele novo endereço, no caso do pop bastaria apenas incrementar o sp pelo tamanho

sub sp,2 ;push 315mov bx,spmov [ss:bx],315

mov bx,sp ;pop axmov ax,[ss:bx]add sp,2

podemos modificar o registrador de segmento ss fazendo ele apontar para qualquer segmento com isso podemos ter a nossa pilha em qualquer outro segmento, o registrador sp normalmente é o offsetmais alto daquele segmento já que ele sera subtraido

mov ax,0x500mov ss,axmov sp,0xfffe da mesma forma podemos ter varias pilhas no nosso programa bastando manipular o registrador ss, o problema de ter duas ou mais pilhas que precisaria de uma referecia para cada sp de cada uma das pilhas, para fazer isso podemos usar um endereço na memoria para isso (nunca vi em nenhum lugar ensinado asm com multi stack ^^ )

mov ax,0x600 ;carrega a pilha 1 no segmento 600mov ss,axmov sp,0xfffe

push 315 ;empilha 315 na pilha 1mov ax,0x500 mov ds,axmov [ds:0],sp ;salva o sp da pilha 1 na memoria 500:0

mov ax,0x601 ;carrega a pilha 2 no segmento 601

Page 42: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ss,axmov sp,0xfffepush 100 ;empilha 100 na pilha 2

mov ax,0x500 ;volta para pilha 1mov ds,axmov sp,[ds:0] ;recupera o sp da pilha 1 de 500:0mov ax,0x600 ;carrega a pilha 1 no segmento 600mov ss,axpop ax ;desempilha o 315 da pilha 1

quando precisamos empilhar todos os registradores usamos a instrução pusha, sera empilhado na ordem ax, cx, dx, bx, sp, bp, si e di

pusha

para desempilhar usamos a instrução popa

popa

podemos usar isso para salvar os registradores na memoria e recuperar eles depois ou simplesmente uma forma de modificar todo os registradores diretamente pela memoria empilhando eles

mov ax,0x5mov bx,0x6pusha

mov ax,0x20mov bx,0x30popa ;recupera o ax=5 e bx=6

alem do pusha temos o pushad e o popad que é usado para os registradores de 32bits

mov eax,0x200mov ecx,0x100pushad

mov eax,0x500popad

tambem existe o pushf que empilha a própria flag sendo dois bytes que representa as flags (já que a flag é um registrador de 16bits), o valores nesse word seria equivalente a cada posição da flag ou seja se tiver setado em 1 as flag CF, PF e AF sera armazenado na pilha o valor 0x17 (10111)

pushf

para recuperar da pilha usamos o popf

popf

Page 43: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

a instrução pushf e popf (pushfd e popfd para os de 32bits) permite modificar as flags de forma indireta usando a pilha

push 0b1000111 ;ZF, PF e CFpopf

as vezes precisamos alocar dinamicamente uma quantidade de espaço na pilha para isso basta subtrair o valor dela, para liberar o espaço novamente basta adicionar o valor ao sp (apenas libere o espaço alocado se não tiver empilhado mais nada depois dela se não sera perdido)

sub sp,20 ;aloca 20 bytes na pilhaadd sp,20 ;libera os 20 bytes na pilha

o registrador bp é usado como base de indice para correr a stack de cima para baixo, como já sabemos a stack cresce para baixo então o bp deve ser atribuido ao offset mais alto da stack e não para o topo da pilha assim podemos correr ela da parte mais alta ate o topo dela (sp)

| 315 ← BP | 100V 200 ← SP

para usar o bp a gente deve atribui o valor do sp a ele antes de começar a empilhar, assim sera a base mais alta com isso basta acessar os valores de formar indexada subtraindo ao bp

mov bp,sp ;bp (base)push 315 ;bp - 2push 100 ;bp - 4push 200 ;bp - 6

mov ax,[bp - 4] ;acessa o valor 100

esse regitrador pode ser usado em conjunto com as subrotinas para ter acesso tanto ao empilhamento antes da chamada para aquela subrotina quanto os empilhamento da própria subrotina, quando atribuimos o registror sp ao bp na subrotina podemos acessar os empilhamentos que foram feito na subrotina como indice bastando subtrair o registrador bp, ou então adicionando para ter acesso os empilhamento que foram empilhados antes da chamada dela (se a gente analisar linguagens de alto nivel as passagem de argumentos nas funções e as variaveis locais dentro das funções são nada mais nada menos que um ponteiro base como esse)

push 315 ;bp + 4push 600 ;bp + 2call kodo ;endereço atual (base)

kodo:mov bp,sp push 800 ;bp - 2push 200 ;bp - 4mov ax,[bp + 4] ;acessa o valor 315 mov bx,[bp - 2] ;acessa o valor 800

Page 44: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

muitas vezes usamos o bp em subrotinas diferentes então para evitar a perda do valor do bp antigo temos que criar um stack frame, para fazer isso a cada nova chamada de subrotina temos que salvar o bp para pilha antes de modificar e depois recuperar ele destruindo o stack frame

call kodo

kodo:push bp ;salva o bp antigo na pilhamov bp,sp ;move o sp atual para o bp para ter a nova base

mov sp,bp ;move o valor do bp para o sppop bp ;recupera o bp antigoret ;retorna

quando pensamos em memoria e programação vem a cabeça as variaveis não é? uma variavel é um espaço alocado na memoria com um tamanho fixo que podemos usar para armazenar dados diretamente por um nome, diferente de outras linguagens de alto nivel que declaramos uma variavelde um tipo especifico, o assembly a gente precisa criar as variaveis de forma manual alocando o espaço, a forma mais simples de criar uma variavel seria criar um label e a quantidade de bytes vazios da nossa variavel

kodoint: nopnop

podemos armazenar os dados na variavel usando o próprio label dela

mov [kodoint],0x315

kodoint: nopnop

como tambem é possível ler a nossa variavel pelo próprio label

mov [kodoint],0x315mov ax,[kodoint]jmp $

kodoint: nopnop

podemos criar quantas variaveis a gente quiser dessa forma

mov [kodoint],0x315mov [kodoint2],0x100

kodoint:

Page 45: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

nopnop

kodoint2:nopnop

ao inves de usar o nop para alocar aquele espaço podemos usar a diretiva db para especificar um determinado byte em alguns compiladores

kodoint: db 0x0db 0x0

no compilador gas o db seria .byte

kodoint:.byte 0x0.byte 0x0

a gente poderia especificar qualquer byte, exemplo seria o 0x90 que seria equivalente usar a instrução nop

kodoint: db 0x90db 0x90

podemos colocar um único db e os bytes separados por virgulas

kodoint: db 0x0,0x0

alem do db existe o dw que aloca 2bytes sendo um word (16bits)

kodoint: dw 0x0

no gas o dw seria a diretiva .word ou .short

kodoint: .word 0x0

como o word é composto por dois bytes se a gente quiser especificar dois bytes 0x90 em um único word tambem é possível bastando colocar 0x9090

kodoint: dw 0x9090

com o dd especificamos um dword que seria equivalente a 4 bytes (32bits)

Page 46: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodoint:dd 0x0

no compilador gas usamos .int ou .long para especificar o dd

kodoint:.int 0x0

tambem temos o dq que seria equivalente a 64bits ou 8 bytes

kodoquad:dq 0x0

no compilador gas usamos .quad para reprensentar 64bits

kodoquad:.quad 0x0

uma array em uma linguagem de alto nivel é uma alocação com varias posições com tamanhos especificos, em assembly tambem é possível criar array bastando alocar a quantidade certa com tamanho do dado multiplicada pela quantidade de posições daquela array, exemplo uma array do tipo short int que tem 2 bytes (16bits) para cada posição dela, se a gente pretende criar 10 posições então temos que alocar na memoria 20 bytes em sequencia (10*2)

kodoarray: dw 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0

o acesso a uma array é indexado de acordo com o tamanho da posição dela ou seja uma array do tipo short int temos que acessar as posições a cada 2 bytes

mov bx, kodoarraymov [bx], 0x315 ;1º posiçãomov [bx + 2], 0x100 ;2º posiçãomov [bx + 4], 0x500 ;3º posição

kodoarray:dw 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0

lembrando que o acesso indexado na sintaxe at&t é um pouco diferente da sintaxe intel

mov kodoarray,%bxmov $315, (%bx)mov $100, 2(%bx)mov $500, 4(%bx)

kodoarray:.short 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0

Page 47: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

alguns compiladores tem diretivas especificas para criar uma sequencia bytes como o resb, resw, resd e o resq do nasm

kodoarray: resw 10

no compilador gas podemos usar a diretiva byte seguido da sequencia de bytes que vamos criar

kodoarray: .byte10 0x0

da mesma forma que as arrays podemos criar as strings porem usando os caracteres

kodostr: db "k","o","d","o"

a maioria dos compiladores permite escrever as string entre aspas sendo cada caracter um byte

kodostr: db "kodo"

no compilador gas podemos usar a diretiva .ascii para definir uma string como no exemplo anterior

kodostr: .ascii "kodo"

as vezes precisamos colocar um caracter no final da string indicando a sua finalização assim quandoo programa ler essa determinada string vai saber que ali é o final dela e não vai ler parte buffer descohnecido da memoria, programas de msdos normamente usa o cifrão e outros usa o caracter nulo (agora você entende porque as strings em C termina com caracter nulo ne? )

kodostr: .ascii "kodo",0x0

para a manipulação das strings ou ate mesmo das arrays temos os registradores si e di, podemos atribuir o endereço de memoria ou label para o registrador si e acessar a string de forma indexada

mov si,kodostrmov ah,[si + 0] ;acesso a letra kmov ah,[si + 1] ;acesso a letra omov ah,[si + 2] ;acesso a letra dmov ah,[si + 3] ;acesso a letra o

kodostr: db "kodo"

o mesmo vale para o registrador di

mov di,kodostrmov ah,[di + 1] ;acesso a letra o

kodostr: db "kodo"

o real uso dos registradores si e di é o movimento de uma sequencia de bytes de uma parte da memoria para outra parte da memoria sendo do si para o di, podemos usar a instrução movsb para mover um unico byte do si para o di

Page 48: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov si,kodostrmov di,kodonovomovsb

kodostr: db "kodo"kodonovo: db 0x0,0x0,0x0,0x0

toda vez que a gente usa o movsb sera movido um byte do si para o di então precisamos usar a quantidade equivalente aos bytes que sera movido para mover todos

mov si,kodostrmov di,kodonovomovsbmovsbmovsbmovsb kodostr: db "kodo"kodonovo: db 0x0,0x0,0x0,0x0

tambem tem o movsw que move 2 bytes (16bits), movsd que move 4 bytes (32bits) e movsq 8 bytes(64bits)

mov si,kodomov di,kodonovomovswmovsw

kodo: dw 0xffff,0x315kodonovo: dw 0x0,0x0

podemos mover o dado do registrador fonte si para o registrador al com a instrução lodsb

mov si,kodostrlodsb

kodostr: db "kodo"

com a instrução lodsw movemos 16bits da fonte para o registrador ax, tambem existem o lodsd e lodsq para mover bytes mais altos que usa registradores eax e rax

mov si,kodolodsw

kodo: dw 0xf315

é possível mover do registrador al para o registrador de destino di com a instrução stosb (stosw, stosd e stosq)

Page 49: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov al,0x61mov di,kodostosb

kodo: db 0x0

tanto o movsb, lodsb e stosb incrementa automaticamente o di e si com isso não precisamos ficar incrementado ele para acessar o próximo byte já que é automatico

mov di,kodo

mov al,0x61stosb

mov al,0x62stosb

kodo: db 0x0,0x0

boa parte dos compiladores usa a diretiva org para alinhar a posição de memoria onde esta aquele determinado codigo, sendo o org uma diretiva do compilador e não da propria arquitetura podendo existir ou não existir em alguns compiladores ou ter um uso diferente dependendo do compilador, no nasm o org não tem tanta utilidade pelo menos não para alinhar o codigo na memoria

org 100 no compilador gas o org é usado para alinhar o codigo na memoria ou no proprio binario, quando definimos uma posição no org os codigos depois dele vai esta sendo jogado na memoria a partir daquela determinada posição

.org 0 nopjmp 100.org 100mov $10,%axmov $20,%bx

no nasm podemos usar a diretiva times que permite repetir uma certa quantidade de bytes, com isso podemos conseguir o mesmo resultado do codigo anterior bastando subtrair a posição que queremospela quantidade de bytes atual e o byte ou a instrução que vai se repetir ate la

nopjmp 100times (100 - ($-$$)) db 0x0mov ax,10mov bx,20

muitos sistemas inicia programas em parte especificas da memoria algum segmento especifico ou ate mesmo algun offset especifico, como exemplo temos os programas de boot que deve ser inciadono segmento 0x0 e offset 0x7c00, executaveis de msdos do tipo COM inicia no segmento 0x700 e o

Page 50: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

seu offset é o 0x100, esses endereços depende muito do sistema operacional sendo que muitos compiladores usa a diretiva org para especificar isso

org 0x7c00

existem endereços da memoria usados para fins especificos que podem ser mapeamento do próprio hardware naquele endereço de memoria tal endereços são chamados de DMA (Direct Memory Access), nos endereços absolutos de 0x0 ate 0x3ff são os vetores de interrupções e o seu uso é apontar para uma determinada parte da memoria quando acontece alguma interrupção especifica, osde 0x400 ate 0x4ff são endereços usados pela BIOS para diversos fins inclusive retorno de informação do hardware, endereços absolutos de 0x500 ate o 0x9ffff são endereços livres para qualquer uso, os de 0xa0000 ate 0xbffff são usados para o buffer de video onde podemos imprimir algo na tela apenas manipulando tal endereço, 0xc0000 ate 0xeffff usados para roms de expansão como perifericos extras, 0xf0000 ate 0xfffff é a rom da BIOS inclusive o POST é executado nesses endereços mais altos

Uso Inicio do endereço Fim do endereço

Vetor de interrupção 0x0 0x3ff

BIOS 0x400 0x4ff

Memoria livre 0x500 0x9ffff

Buffer de video 0xa0000 0xbffff

Rom de expansão 0xc0000 0xeffff

Rom da BIOS 0xf0000 0xfffff

no exemplo abaixo é imprimido o texto “kodo” na tela usando o endereço absoluto 0xb8000, como já sabemos o endereço absoluto é o segmento e o offset então 0xb8000 pode ser acessado por b800:0000 (eita 50 paginas depois e finalmente conseguimos imprimir algo na tela '-' )

mov ax,0xb800mov ds,ax

mov [ds:0],”k”mov [ds:2],”o”mov [ds:4],”d”mov [ds:6],”o”

se reparar no exemplo anterior colocamos o caracter a cada 2 offset sendo que o byte seguinte seria a cor ou fundo que sera exibido, no exemplo abaixo imprime o texto em vermelho

mov ax,0xb800mov ds,ax

mov [ds:0],”k”mov [ds:1], 12mov [ds:2],”o”mov [ds:3], 12mov [ds:4],”d”mov [ds:5], 12

Page 51: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov [ds:6],”o”mov [ds:7], 12

2.8 – x86: Interrupção

uma interrupção é uma parada que o processador faz na execução de determinado codigo para executar outra coisa naquele momento quando ocorre aquela determinada interrupção, existem diversos tipos de interrupções sendo que algumas interrupções são causadas por hardware e outras por software, algumas interrupções podem ser desativadas pelo usuario ou sistema e outras interrupções não podem ser desativadas, algumas interrupções já são predefinidas ou usa endereços já predefinidos na memoria, uma interrupção causada por hardware podem ser mascaradas ou não mascardas (NMI), podem ser geradas por um controlador de interrupção para sinalizar um pedido de interrupção como os IRQs ou ate mesmo por um pulso ou a falta de corrente em algum pino especifico do processador ou do microcontrolador, uma interrupção por software pode ser apenas a execução de uma determinada instrução no próprio codigo como a instrução int do x86 (essas interrupções por software tem o nome de trap ou syscall em algumas arquiteturas como é o caso da arquitetura 68k que usa a instrução trap para interrupção por software ou do mips que usa o syscall),quando acontece uma interrupção o programa para o seu fluxo de execução e pula para algum outro endereço de memoria executando aquele codigo daquela interrupção, depois de finalizado ele volta para o fluxo original do programa. esses endereços de memoria das interrupções depende do tipo deinterrupção e do tipo de arquitetura, todos os endereços usados pelas interrupções no x86 estão listados no vetor de interrupção (endereços entre 0x0 ate 0x3ff), as interrupções costumam ter varias funções já definidas onde precisamos especificar qual sera a função que estaremos utilizando e os argumentos dela usando os registradores de usos gerais, uma das interrupções da arquitetura x86 é o int10 que é uma chamada de video da BIOS, uma das vantagem das interrupções de BIOS que elas funcionam sem precisar de um sistema operacional sendo utilizadas para criar programas de boot e ate sistemas operacionais, a sua desvantagem que não funciona diretamente em sistema operacionais principalmente os que estão em modo protegido, uma das funções da interrupção int10é alterar o modo de video onde setamos no registrador ah o numero 0 indicando essa função e no registrador al seria o numero equivalente ao modo de video que vamos setar, por fim usamos a instrução int 0x10 para chamar a interrupção com aqueles argumentos (sendo essa uma interrupção de software)

mov ah,0x0 ;funcão usada: modo de videomov al,0x1 ;argumento: 80x32 16 cores textualint 0x10 ;int de software

o mesmo pode ser feito usando a sintaxe at&t tambem

mov $0x0,%ahmov $0x1,%alint $0x10

outra função da interrupção int10 é a função 0xe, essa função permite imprimir um caracter na tela bastando especificar o codigo desse caracter no registrador al

mov ah,0x0emov al,0x6bint 0x10

podemos colocar o caracter ascii tambem

Page 52: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ah,0xemov al,'k'int 0x10

é possivel usar as interrupções quantas vezes a gente quiser, exemplo seria imprimir vários caracteres na tela

mov ah,0xemov al,'k'int 0x10

mov ah,0xemov al,'o'int 0x10

mov ah,0xemov al,'d'int 0x10

mov ah,0xemov al,'o'int 0x10

lembrando que podemos sempre melhorar o nosso codigo, um bom exemplo seria criar uma subrotina para imprimir uma string usando o int10, uma subrotina nesse caso seria uma boa devido não ser possível imprimir string com int10

mov si,textocall printf

mov si,texto2call printf

jmp $

;minha subrotina que imprime uma stringprintf:cmp [si],0x0 ;compara se o caracter for igual a 0jz fim ;se for igual a 0 finaliza pulando para fimmov ah,0xe ;função para imprimirmov al,[si] ;o caracter em siint 0x10 ;interrupção de videoinc si ;incrementa o si para o próximo caracterjmp printf ;volta para o printf para imprimir o proximofim: ret

texto: db "kodo no kami ",0x0texto2: db "ebook de asm",0x0

Page 53: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

a função 0xe tem outro argumento que é a cor do texto que definimos no registrador bl

mov ah,0xemov al,'k'mov bl,0x2 ;verdeint 0x10

usando a função 0x2 da interrupção int10 mudamos a posição do cursor e imprimir o caracter em qualquer outra parte tela, especificamos no registrador dh linha e no dl a coluna

mov ah,0x2mov dh,0x3 ;linhamov dl,0x10 ;colunaint 0x10

mov ah,0xemov al,'k' ;imprime 'k' na posição 3x10int 0x10

para apagar caracteres imprimidos na tela usamos a função 0x2 bastando sobrescrever eles

mov ah,0xemov al,'k'int 0x10 ;imprime o k na posição 0x0

mov ah,0x2mov dh,0x0mov dl,0x0int 0x10 ;volta para posição 0x0

mov ah,0xemov al,'f'int 0x10 ;imprime o f na posição 0x0 apagando o k

podemos pegar a posição atual do cursor usando a função 0x3 da interrupção int10, sera atribuido a linha e a coluna nos registradores dh e dl

mov ah,0x3int 0x10 ;dh=linha, dl=coluna

outra função que permite imprimir algo na tela é a função 0x9 do int10, nessa função temos que especificar o caracter que sera imprimido no registrador al, a cor no registrador bl, e no registrador cx a quantidade de caracteres repetidos que ira ser imprimido

mov ah,0x9mov al,'k'mov cx,3 ;imprime 3 caracteres k repetidomov bl,0x5int 0x10

Page 54: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

um dos problemas de usar a função 0x9 que ele não alinha o cursor, então temos que fazer esse alinhamento manualmente pelo 0x2

mov ah,0x9mov al,'k'mov cx,3mov bl,0x4 ; vermelhoint 0x10

mov ah,0x3int 0x10 ;pega posição atual

mov ah,0x2add dl,3 ;incrementa a coluna em mais 3int 0x10

podemos ler o caractere na posição atual com a função 0x8, vai retornar no registrador ah a cor e noal o caracter (isso funciona apenas em modo de video textual)

mov ah,0x0mov al,0x1int 0x10 ;80x32 textual

mov ah,0xemov al,'f'mov bl,0x3int 0x10 ;imprime o caracter na posição 0x0

mov ah,0x2mov dh,0x0mov dl,0x0int 0x10 ;aponta o cursor para o caracter

mov ah,0x8int 0x10 ;ah = 0x3, al = 'f'

é possível mudar a cor do background com a função 0xb da interrupção int10, a cor deve ser definida no registrador bl, o registrador bh deve esta com o valor 0

mov ah,0xbmov bh,0x0mov bl,0x9 ;azul claroint 0x10

com a função 0x5 a gente manipula a pagina, uma pagina é semelhante ao um livro onde cada pagina seria equivalente a parte visual do nosso programa, então podemos permutar a pagina para mudar a parte visual do nosso programa sem precisar limpar a tela

mov ah,0xemov al,'k' ;imprime ‘k’ na primeira paginaint 0x10

Page 55: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ah,0x0int 0x16 ;aguarda uma tecla ser apertada

mov ah,0x5mov al,0x1 ;pula para pagina 1int 0x10

mov ah,0xemov al,'o' ;imprime ‘o’ na segunda paginaint 0x10

mov ah,0x0int 0x16 ;aguarda uma tecla ser apertada

mov ah,0x5mov al,0x0 ;volta para pagina 0 mostrando o k novamenteint 0x10

tambem podemos desenhar um pixel na tela com a função 0xc da interrupção int10, a cor definimos no registrador al, a posição horizontal no registrador cx, e a posição vertical no registrador dx, tambem é necessario esta usando um modo de video grafico

mov ah,0x0mov al,0x13int 0x10

mov ah,0xcmov al,0x10 ;cor verde claromov cx,100 ;xmov dx,100 ;yint 0x10

um exemplo de um quadrado desenhado pixel por pixel com a função 0xc do int10 em um loop

mov ah,0x0mov al,0x13int 0x10

mov cx,200 ;x começa no 200mov dx,100 ;y comela no 100

kodox:mov ah,0xc mov al,10 ;cor verde claroint 0x10 ;cria o pixel na posição do cx e dxcmp cx,100 ;se cx for igual a 100 pula para kodoyje kodoyloop kodox ;instrução loop decrementa o cx ou seja e desenhado invertidamentekodoy:dec dx ;decrementa o dx

Page 56: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov cx,200 ;volta o cx para 200 para desenha a nova linhacmp dx,20 ;compara dx com 20 se for igual pula para o fim terminadoje fimjmp kodox ;volta para kodoy para desenhar nova linhafim:

podemos entrar com dados digitados do teclado com a interrupção int16 que é interrupção que manipula o keyboard sendo ela outra interrupção da BIOS, a função 0x0 dela permite ler um caracter apertado do teclado que sera armazenado no registrado al (quando usamos essa função o programa vai ficar preso nela ate que uma tecla seja apertada)

mov ah,0x0int 0x16 a gente pode capturar a tecla pressionada pelo int16 e exibir ela pelo int10 (nesse caso não é necessario setar o caracter no registrador al devido o próprio registrador al esta com o caracter a ser exibido depois do int16)

mov ah,0x0int 0x16

mov ah,0xeint 0x10

o int16 só captura caracter por caracter que foi pressionado do teclado, para a gente capturar uma sequencia de caracteres (string), temos que fazer um loop e armazenar na memoria

mov cx,10 ;quantidade de teclas capturadasmov si,kodo ;endereço da nossa variavel

repetir:mov ah,0x0int 0x16 ;captura a tecla pressionada

mov [si],al ;joga a tecla apertada na memoriainc si ;incrementa o si ou seja o endereço da memorialoop repetir ;repete a quantidade do cx

jmp $

kodo: db 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 ;nossa alocação de 10 bytes (variavel)

o problema no exemplo anterior que precisamos digitar exatamente a quantidade de loops, para evitar isso podemos checar uma tecla especifica e sair do loop quando essa tecla for pressionada, como exemplo a tecla ENTER (0xd)

mov cx,10mov si,kodo

Page 57: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

repetir:mov ah,0x0int 0x16 ;captura a tecla pressionada

cmp al,0xd ;compara para ver se o enter foi pressionadoje sair ;se sim finaliza pulando para o sair

mov [si],alinc si

loop repetirsair:

jmp $

kodo: db 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0

é possivel manipular um disco seja um HD ou um disquete com a interrupção int13, o disco sera manipulado de forma bruta sem a leitura de partições ou seja sem nenhum tipo de formatação (fat, ntfs, ext e etc), com a função 0x0 do int13 resetamos o drive colocando a cabeça de leitura dele na posição 0, para usar a função temos que colocar no registrador dl o drive (0x0=floppy a: , 0x1=floppy b: , 0x80=hd 1, 0x81=hd 2), uma coisa interessante quando damos o boot na maquina ele chama a mbr e é setado o numero do drive atual no registrador dx

mov ah,0x0mov dl,0x80int 0x13

quando usamos a função anterior caso o procedimento não seja possível ser feito como por exemplonão exista aquele determinado drive a flag CF sera modificada para 1 indicando a falha ou estara setada em 0 indicando o sucesso

mov ah,0x0mov dl,0x81int 0x13

jc falhamov ah,0xemov al,'S' ;caso seja sucesso exibe Sint 0x10jmp fim

falha:mov ah,0xemov al,'N' ;caso falhe exibe o Nint 0x10fim:

com a função 0x1 podemos chegar o estado do disco pelo seu controlador que sera retornado no registrador ah depois de uma determinada interrupção int13, sendo o seu retorno 0x0 para sucesso,

Page 58: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

0x4 para setor não encontrado, 0x5 para falha de resetamento, 0x6 disquete removido, 0xa setor danificado, 0xb problema na trilha, essa função é perfeita para criar programas de checagem de erros como o chkdsk do windows

mov ah,0x0mov dl,0x80int 0x13

mov ah,0x1mov dl,0x80int 0x13

cmp ah,0x0je sucesso

para a gente ler o hd ou disquete usamos a função 0x2 do int13, nessa função temos que especificar varias coisas entre elas a quantidade de setores que sera lido no registrador al, o setor a ser lido naquele no registrador cl (a contagem do setor começa no 1 sendo ele o setor 0), o cilindro ou a trilha a ser lido no registrador ch, a cabeça da onde sera lido no registrador dh, o drive a ser lido especificamos no registrador dl, e por fim o endereço de memoria para onde vamos jogar o setor que sera lido no registrador bx, no caso a leitura é por setores do hd e não por bytes sendo que cada setor é equivalente a 512 bytes, depois a gente pode acessar os bytes diretamente naquele endereço de memoria

mov ah,0x2 ;leitura do hdmov al,0x1 ;quantidade de setores lidomov bx,0x500 ;endereço da memoria que sera armazenadomov ch,0x0 ;trilha 0mov cl,0x2 ;setor 1 (1 = setor 0, 2 =setor 1, 3 = setor 2 ...)mov dh,0x0 ;cabeça 0mov dl,0x80 ;hd 1 int 0x13

mov al,[0x500] ;vai ler o primeiro byte que estava no hdmov al,[0x501] ;vai ler o segundo byte que estava no hdmov al,[0x502] ;vai ler o terceiro byte que estava no hd

da mesma forma podemos adicionar codigos para ser executado naquele endereço de memoria bastando pular para ele (isso seria equivalente a ter programas ou modulos no nosso sistema)

mov ah,0x2mov al,0x1mov bx,0x500mov ch,0x0mov cl,0x2mov dh,0x0 mov dl,0x80int 0x13

jmp 0x500 ;vai executar o bytes carregados em tempo de execução

Page 59: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

o registrador bx onde especificamos o endereço de memoria que sera armazenado, ele é usado em conjunto com o registrador de segmento es, com isso podemos colocar em outro segmento de memoria mudando o registrador es antes da interrupção sendo que isso é recomendado para evitar a sobrescrita do codigo no próprio segmento

mov ax,600mov es,ax

mov ah,0x2mov al,0x1mov bx,0x500 ;es:bx (600:500)mov ch,0x0mov cl,0x2mov dh,0x0 mov dl,0x80int 0x13

nos exemplos anteriores sera lido apenas 512 bytes do hd por causa do registrador al onde especificamos apenas a leitura de um setor, tambem sera lido o setor numero 1 de acordo com o registrador cl, em outras palavras sera lido do byte 512 do hd ate o byte 1023, a gente pode alterar esses dois valores para ler uma quantidade maior exemplo do byte 2048 ate 3071

mov ah,0x2mov al,0x2 ;quantidade de setores lido (1023 = 2 * 512 - 1)mov bx,0x500mov ch,0x0mov cl,0x5 ;setor 4 (2048 = 512 * 4)mov dh,0x0 mov dl,0x80int 0x13

da mesma forma é possível armazenar no hd ou no disquete com a função 0x3 do int13, o seu uso é parecido com o 0x2 sendo o registrado al a quantidade de setores lidos da memoria, bx o endereço de memoria da onde vamos ler para armazenar no drive, o registrador ch sendo a trilha ou cilindro onde vamos armazenar, o registrador cl o setor onde vamos armazenar no drive, o registrador dh sendo a cabeça no hd, e o registrador dl sendo o drive que vamos armazenar

mov [0x500],'k'mov [0x501],'o'mov [0x502],'d'mov [0x503],'o'

mov ah,0x3 ;escrita do hdmov al,0x1 ;quantidade de setores a ser gravadomov bx,0x500 ;endereço da memoria que sera gravadomov ch,0x0 ;trilha 0 no hdmov cl,0x2 ;setor 1 no hdmov dh,0x0 ;cabeça 0mov dl,0x80 ;hd 1int 0x13

Page 60: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

lembrando que sera gravado a quantidade de setores da memoria para o hd, no caso do exemplo anterior foi armazenado no hd 512 bytes da memoria, com isso tambem foi sobrescrito os 512 bytes do hd pelos 512 bytes que estava na memoria, mesmo que tenha adicionado a palavra kodo na memoria foi armazenado todos os 512 bytes independente do que estava neles, as vezes precisamos apenas armazenar uma quantidade exata sem sobrescrever os bytes do hd alem do necessario, para agente fazer isso é bem simples bastando ler o setor do hd para a memoria modificar ele e depois armazenar assim não sera armazenado bytes aleatorios e sim os mesmos

mov ah,0x2mov al,0x1mov bx,0x500 mov ch,0x0mov cl,0x2mov dh,0x0mov dl,0x80int 0x13 ;ler o setor no hd e joga na memoria

mov [0x500],'k' mov [0x501],'o'mov [0x502],'d' ;modifica os bytes desejado na memoriamov [0x503],'o'

mov ah,0x3 mov al,0x1mov bx,0x500mov ch,0x0mov cl,0x2mov dh,0x0mov dl,0x80int 0x13 ;escreve o setor da memoria no hd

a interrupção int14 permite enviar e receber dados via porta serial, pela função 0x0 a gente configura a conexao da porta usada, o baud, paridade entre outros, no registrador dx a gente especifica a porta que vamos configurar (0 = com1, 1 = com2 …), no registrador al especificamos oparamentro em um único byte sendo que a cada bit desse byte tem uma representação no parametro (os bits 0 e 1 é o tamanho do byte enviado ou recebido, o bit 2 é o stop bit, o 3 e 4 é a paridade, o bit5 6 e 7 é o baud)

mov ah,0x0mov al,11100010b ;111 = 9600 (baud), 00 = none (par), 0 = 0 (stop), 10 = 7bits mov dx,0x0 ;com1int 0x14

para enviar um byte via serial usamos a função 0x1, setamos no registrado al o byte que sera enviado e no registrador dx a porta

mov ah,0x0mov al,11100010bmov dx,0x0int 0x14

Page 61: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ah,0x1mov al,'k'mov dx,0x0int 0x14

para o recebimento usamos a função 0x2, nela temos que especificar a porta no registrador dx, o byte sera retornado no registrador al, caso o byte seja recebido com sucesso o registrador ah sera igual a 0 sendo possível identificar quando o byte for recebido

mov ah,0x0mov al,11100010bmov dx,0x0int 0x14

mov ah,0x2mov dx,0x0int 0x14

com a interrupção int19 a gente reinicia o sistema

int 0x19

no sistema operacional MSDOS é possível usar a interrupção int21 sendo ela uma interrupção exclusiva desse sistema operacional e não da BIOS, com isso não é possível usar essa interrupção caso não esteja usando o sistema MSDOS porem podemos emular o próprio sistema dentro de maquinas virtuais ou emuladores como o dosbox para usar essa interrupção e os programas desse sistema em outro (não confundir os programas do msdos com programas não graficos para o prompt), uma das funções do int21 é o 0x0 que finaliza o programa atual

mov ah,0x0int 0x21

outra função com o mesmo efeito é o 0x4c sendo que nessa função podemos especificar o retorno no registrador al

mov ah,0x4cmov al,0x0int 0x21

para exibir um caracter usando a função 0x2, especificamos o caracter a ser exibido no registrador dl

mov ah,0x2mov dl,'k'int 0x21

tambem é possivel exibir uma string usando a função 0x9, o endereço da string colocamos no registrador dx sendo que a string deve terminar com o caracter cifrão indicando o final dela

Page 62: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ah,0x9mov dx,textoint 0x21

texto: db “kodo no kami$”

podemos digitar um caracter do teclado com a função 0x1, sera armazenado no registrador al e sera exibida na tela tambem (echo)

mov ah,0x1int 0x21

para entrar com um caracter do teclado sem exibir na tela usamos a função 0x8

mov ah,0x8int 0x21

é possível entrar com uma string do teclado usando a função 0xa, no registrador dx especificamos o endereço de memoria que sera armazenado sendo que nesse endereço o primeiro byte seria equivalente ao tamanho de bytes maximo que podemos adicionar na string (recomendado é colocar os outros bytes como cifrão e tambem não é necessario armazenar exatamente aquele tamanho maximo podendo ser ate que o enter seja pressionado)

mov ah,0xamov dx,kodoint 0x21

kodo: db 10,”$$$$$$$$$”

um outro exemplo de um programa para msdos onde escrevemos o nome e depois ele é exibido

mov ah,0x9mov dx,kodoint 0x21 ;exibe “digite seu nome: ”

mov ah,0xamov dx,variavelint 0x21 ;armazena o nome digitado em variavel

mov ah,0x9mov dx,kamiint 0x21 ;exibe “\nnome digitado foi ”

mov ah,0x9mov dx,variavel+2int 0x21 ;exibe a variavel pulando dos caracteres que seria o tamanho

mov ah,0x0int 0x21 ;finaliza o programa

Page 63: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo: db "digite seu nome: $"kami: db 0xd,0xa,"nome digitado foi $"variavel: db 20,"$$$$$$$$$$$$$$$$$$"

podemos retornar o drive atual com a função 0x19, sera retornando no registrador al (0 = a: , 1 = b: ,2 = c: …)

mov ah,0x19int 0x21

ou mudar o drive atual com a função 0xe, especificamos o drive no registrador al

mov ah,0xemov al,0x3 ; d:int 0x21

para criar um diretorio usamos a função 0x39 (o sistema msdos usa partições do tipo fat sendo a interrupção int21 tendo o acesso ao hd em cima desse tipo de partição e não de forma bruta como no int13), no registrador dx dessa função a gente especifica o endereço de uma string que tem o nome do diretorio

mov ah,0x39mov dx,kodoint 0x21

kodo: db “nudes”

podemos remover um diretorio com a função 0x3a sendo seus argumentos parecido com o anterior

mov ah,0x3amov dx,kodoint 0x21

kodo: db “nudes”

é possível mudar de directorio com a função 0x3b

mov ah,0x3bmov dx,kodoint 0x21

kodo: db “nudes”

alem das interrupções do sistema operacional msdos tambem temos as interrupções para o sistema unix (linux) que é a interrupção int80, diferente do sistema msdos que é um sistema ultrapassado a plataforma linux é usada constamente inclusive por servidores no mundo todo, os registradores usados nas interrupções do linux é de 32bits (64bits) ou seja é modo protegido e todas essas interrupções tambem são chamadas de sistemas na própria linguagem C ou em outra de linguagem de mais alto nivel, umas dessas funções é o 0x1 que seria api exit no linux que finaliza o programa atual sendo o seu argumento de retorno definido no registrador ebx

Page 64: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov eax,0x1 ;exitmov ebx,0x0 ;retorno 0int 0x80

podemos exibir uma string usando a função 0x4 ou a api write do linux, no registrador ebx a gente define o descritor (0 – stdin, 1 – stdout, 2 – stderr … ), no registrador ecx setamos o endereço da nossa string, no registrador edx seria o tamanho da nossa string que sera exibida

mov eax,0x4 ;writemov ebx,0x1 ;stdoutmov ecx,kodo ;variavel kodomov edx,12 ;tamanhoint 0x80

kodo: db “kodo no kami”

alguns compiladores permite subtrair endereços de memoria com isso podemos usar para retornar o tamanho entre dois labels que seria o inicio e o fim da string que por sua vez tambem é o tamanho da string

mov eax,0x4mov ebx,0x1mov ecx,kodomov edx,kodofim - kodo ;tamanhoint 0x80

kodo: db “kodo no kami”kodofim:

é possível a gente ler uma string do teclado com a função 0x3 ou read do linux, seu argumento é parecido com a anterior sendo o registrador ebx o descritor, o registrador ecx o endereço e o registrador edx o tamanho da alocação

mov eax,0x3 ;readmov ebx,0x0 ;stdinmov ecx,kami ;variavel kamimov edx,30 ;tamanhoint 0x80

kami: resb 30

para a gente criar um arquivo usamos a função creat ou 0x8, no registrador ebx passamos o nome do arquivo e no ecx a permissão do arquivo criado

mov eax,0x8 ;creatmov ebx,kodo ;animes.txtmov ecx,0x0 ;000int 0x80

Page 65: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo: db “animes.txt”

os valores das permissões são as mesmas listadas nos defines do arquivo stat.h do kernel do sitema linux bastando usar a operação or individualmente para cada um dos seus valores setados para formar a permissão final

mov ecx,256 ;usuario ror ecx,128 ;usuario rwor ecx,64 ;usuario rwxor ecx,32 ;usuario rwx, grupo ror ecx,8 ;usuario rwx, grupo rxor ecx,4 ;usuario rwx, grupo rx, outros ror ecx,1 ;usuario rwx, grupo rx, outros rx (rwxr-xr-x = 755)

mov eax,0x8 ;creatmov ebx,kodo ;animes.txtint 0x80

kodo: db “animes.txt”

podemos deletar um arquivo com a função 0xa (unlink)

mov eax,0xa ;unlinkmov ebx,kodo ;animes.txtint 0x80

kodo: db “animes.txt”

para renomear um arquivo com a função 0x26, especificamos o nome do arquivo antigo no registrador ebx e o novo nome no registrador ecx

mov eax,0x26mov ebx,kodomov ecx,kamiint 0x80

kodo: db “railgun.py”kami: db “misaka.py”

com a função 0x27 (mkdir) a gente cria um novo diretorio, no registrador ebx especificamos o diretorio e no ecx a permissão

mov eax,0x27 ;mkdirmov ebx,kodo ;filmesmov ecx,493 ;perm = 755int 0x80

kodo: db “filmes”

por outro lado com a função 0x28 a gente remove um diretorio (rmdir)

Page 66: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov eax,0x28 ;rmdirmov ebx,kodo ;filmesint 0x80

kodo: db “filmes”

para ler ou escrever em um arquivo usamos a função 0x5 (open), especificamos no registrador ebx oendereço da string do arquivo, no registrador ecx a permissão para leitura ou escrita (0 = leitura, 1 =escrita, 2 = leitura e escrita), e no registrador edx a permissão do arquivo, sera retornado no registrador eax o descritor do arquivo

mov eax,0x5 ;openmov ebx,kodo ;arquivo.txtmov ecx,0x1 ;escritamov edx,0x0 ;0 ~ não é usado caso o arquivo já existaint 0x80

kodo: db “arquivo.txt”

tambem podemos fechar o arquivo com a função 0x6 (close) sendo que para ser armazenado no arquivo é necessario que ele seja fechado, nessa função temos que especificar o descritor que vamosfechar no registrador ebx

mov eax,0x5mov ebx,kodomov ecx,0x1mov edx,0x0int 0x80 ;abre o arquivo para escrita, retorna o descrito em eax

mov ebx,eax ;copia o descrito do eax para o ebxmov eax,0x6 ;closeint 0x80

kodo: db “arquivo.txt”

para escrever no arquivo usamos a função 0x4 (write), a diferença que no registrador ebx definimos o descritor do arquivo e não do stdio

mov eax,0x5mov ebx,kodomov ecx,0x1mov edx,0x0int 0x80 ;open ~ escrita

mov ebx,eaxmov eax,0x4mov ecx,kamimov edx,kamif - kamiint 0x80 ;write

Page 67: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov eax,0x6int 0x80 ;close

kodo: db “arquivo.txt”kami: db “programando em asm”kamif:

a leitura do arquivo é bem parecido mudando a permissão na abertura e tambem utilizando a função0x3 para ler o arquivo

mov eax,0x5mov ebx,kodomov ecx,0x0mov edx,0x0int 0x80 ;open ~ leitura

mov ebx,eaxmov eax,0x3mov ecx,kamimov edx,50int 0x80 ;read

mov eax,0x6int 0x80 ;close

kodo: db “arquivo.txt”kami: resb 50

podemos pular para determinado trecho do arquivo usando a função 0x13 (lseek), no registrador ebx especificamos o descritor, no registrador ecx a posição onde vamos pular, no registrador edx sera da onde ira partir a contagem da posição do pulo (0 = inicio, 1 = atual, 2 = fim), o lseek pode ser usado para diversos fins como recomeçar a leitura do arquivo sem precisar fechar, leitura do arquivo byte por byte ou ate mesmo uma quantidade de bytes por vez, leitura ou escrita em uma posição especifica do arquivo entre outras coisas

mov eax,0x5mov ebx,kodomov ecx,0x0mov edx,0x0int 0x80 ;open ~ leitura

mov ebx,eaxmov eax,0x13 ;lseekmov ecx,10 ;10 posiçãomov edx,0 ;inicio do arquivoint 0x80

para criar um link de um arquivo com a função 0x9, seu argumento é o arquivo que vamos criar o link no registrador ebx e onde sera criado o arquivo especificamos no registrador ecx

mov eax,0x9 ;link

Page 68: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

mov ebx,kodo ;arquivomov ecx,kami ;atalhoint 0x80

kodo: db “arquivo.txt”kami: db “link.txt”

com o chmod é possivel modificar a permissão do arquivo usando o 0xf, no registrador ebx colocamos o arquivo e no registrador ecx a nova permissão

mov eax,0xf ;chmodmov ebx,kodo ;arquivo.txtmov ecx,488 ;perm = 750int 0x80

kodo: db “arquivo.txt”

tambem é possível a gente criar a nossa propria interrupção customizada no codigo, no modo real para criar uma interrupção bastaria modificar os primeiros bytes da memoria sendo ele o endereços do vetor de interrupção que fica entre 0x0 ate 0x3ff, no modo protegido o sistema já usa um descrito para a tabela de interrupção o IDT, para modificar o vetor de interrupção no modo real bastaria definir diretamete o endereço da rotina nele, sendo 4 bytes no total 2 bytes para o endereço da rotina e 2 bytes para o segmento da rotina da interrupção, o endereço maximo do vetor de interrupção é o endereço 0x3ff então se a gente dividir ele por 4 daria 255 interrupções diferentes, cada interrupção no vetor é composto por 4 bytes em sequencia ou seja se a gente quiser criar a interrupção int50 temos que descobrir o endereço do vetor onde seria esse int50 e armazenar o endereço e o segmento da rotina naquele determinado endereço do vetor com isso a interrupção ja estaria criada, o calculo para o endereço do vetor é bem simples sendo o numero da interrupção multiplicado por 4 para armazenamento do endereço e para o segmento seria o endereço anterior somado mais 2

offset no vetor do int50 = 0x140 (0x50 * 4)

segmento no vetor do int50 = 0x142 (0x50 * 4 + 2)

como já sabemos bastaria simplesmente a gente colocar o endereços e o segmento da subrotina no vetor de interrupção para criar a nossa interrupção

mov ax,0x0mov es,axmov [es:0x140], kodomov [es:0x142], cs

kodo:

é sempre recomendado desativar as interrupções quando for modificar a tabela de interrupção e ativar elas depois da sua modificação com as instruções cli e sti

mov ax,0x0mov es,ax

Page 69: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

climov [es:0x140], kodomov [es:0x142], cssti

kodo:

na subrotina usamos a instrução iret para retornar de uma interrupção, diferente das subrotinas que chamadas pelo call que usamos o ret para o retorno

mov ax,0x0mov es,axclimov [es:0x140], kodomov [es:0x142], cssti

kodo:iret

depois de criada a interrupção podemos chamar ela normalmente

mov ax,0x0mov es,axclimov [es:0x140], kodomov [es:0x142], cssti

int 0x50

kodo:iret

quando a gente usa a instrução int é jogado na pilha o segmeto e o endereço atual para que seja possível o retorno para ultima posição, podemos manipular esse endereço e força a volta para qualquer outro endereço bastando empilhar o segmento e o endereço

mov ax,0x0mov es,axclimov [es:0x140], kodomov [es:0x142], cssti

int 0x50nopnopnop ;vai retorna da interrupção direto para esse endereço

kodo:

Page 70: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push 0x0push 0x7c17iret

na rotina da interrupção podemos manipular os valores passado pelos registradores para criar funções diferentes na mesma interrupção ou usar os registradores para retornar valores das interrupções, inclusive tambem podemos usar uma interrupção dentro da outra criando novas funcionalidades de outras fuções de outras interrupções, o exemplo abaixo seria a leitura ou a exibição de um caracter em uma única interrupção int50 (0x0 leitura do caracter do teclado e 0x1 sendo a exibição na tela)

mov ax,0x0mov es,axclimov [es:0x140], kodomov [es:0x142], cssti

mov ah,0x0 ;leitura do caracter pelo tecladoint 0x50 ;minha interrupção

mov ah,0x1 ;exibição do caracter int 0x50 ;minha interrupção

jmp $

kodo:cmp ah,0x0je tecladocmp ah,0x1je exibirjmp sair

teclado:mov ah,0x0int 0x16jmp sair

exibir:mov ah,0xeint 0x10

sair:iret

2.9 – x86: IO

alem do acesso direto aos perifericos mapeados na memoria ou por alguma interrupção, tambem é possível o acesso a eles pelas portas de entrada e saida IO, essas portas de entradas e saidas são utilizadas para o acesso direto a determinado DMA ou barramento especifico do computador como o controlador do hd, a porta serial e paralela, os perifericos conectados via PCI, teclado via PS/2,

Page 71: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

impressora via LPT, os timers (pit) e o pic interno, muitas arquiteturas usam o IO para acesso externo tanto de entrada quanto de saida por algum barramento sendo que algumas arquiteturas usam o IO como pinos especificos como é o caso dos microcontroladores, na arquiteuta x86 podemos enviar e receber dados por esses barramentos usando as instruções out e in, tanto a instrução out e a instrução in podem ser usada sem restrição em modo real porem quando o sistema esta em modo protegido depende exclusivamente do nivel de privilegio, a instrução out é usada paraenviar dados para algum barramento especifico e a instrução in é usada para receber os dados passados por um barramento especifico, para usar a instrução out setamos o valor que vamos passar no registrador ax (é preciso que seja exatamente o registrador A ~ al, ax ou eax), depois passamos para a instrução out a porta usada e o registrador com o dado que sera enviado por ela

mov al,202out 0x92,al ;envia 202 para porta 0x92

o mesmo vale para a instrução in porem invertemos o registrador e a porta

in al,0x92 ;recebe o valor da porta 0x92

podemos especificar apenas portas com valores de 8 bits no maximo quando passamos elas de forma imediata, para portas de 16bits usamos o registrador dx com a porta

mov dx,0x3f8out dx,al

da mesma forma para usar portas de 16bits na instrução in temos que usar o registrador dx

mov dx,0x3f8in al,dx

podemos por exemplo pegar a tecla pressionada por uma interrupção 0x16 como vimos anteriomente ou por uma entrada de PS/2 na porta 0x60 usando a instrução in, a diferença que a interrupção usa os caracteres já formatados de acordo com o ascii já a entrada IO usa o codigo conforme a ordem das teclas o famoso scancode que é gerado pelo teclado que pode variar de acordo com o equipamento usado (no meu teclado por exemplo as teclas numericas é 1 = 02, 2 = 03, 3 = 04, 4 = 05 … )

mov dx,0x60 ;porta IO PS/2in al,dx ;leitura do barramento daquela porta

cmp al,0x2 ;compara para ver se a tecla pressionado é igual ao valor 2je tecla1 ;se for igual pula para tecla1cmp al,0x3 ;compara para ver se a tecla pressionado é igual ao valor 3je tecla2 ;se for igual pula para tecla3

tecla1: ;pressionamento da tecla 1

tecla2: ;pressionamento da tecla 2

diferente da interrupção o programa não vai ficar parado esperando apertar a tecla então temos que fazer essa opção sendo uma das formas comparar o registrador al caso nenhum valor tenha sido

Page 72: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

atribuido da tecla especifica ele continua no loop capturando as teclas

kodoloop:

mov dx,0x60in al,dx

cmp al,0x2je tecla1cmp al,0x3je tecla2

jmp kodoloop ;se nenhuma tecla foi pressionada volta para kodoloop

tecla1:

tecla2:

para enviar via porta serial COM1 usamos a porta 0x3f8 para os dados

mov dx,0x3f8 ;porta COM1

mov al,'k'out dx,al ;envia k via serial

mov al,'o'out dx,al ;envia o via serial

mov al,'d'out dx,al ;envia d via serial

mov al,'o'out dx,al ;envia o via serial

tambem é possível receber dados pela porta serial COM1 com a instrução in

mov dx,0x3f8 ;porta COM1mov al,0x0 ;seta o valor 0

repetir:cmp al,0x0 ;compara o al com o valor 0jne sair ;se for diferente de 0 ele pula para sair

in al,dx ;se não ele recebe o valor da porta COM1jmp repetir ;volta para o repetir

sair:

abaixo tem uma lista de algumas das portas usadas

porta descrição

Page 73: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

0x0 - 0x1f primeiro controlador DMA

0x20 - 0x21 primeiro PIC

0x40 - 0x47 PIT

0x60 - 0x64 contralador PS/2 (8042)

0x70 - 0x71 CMOS e RTC

0x80 - 0x8f DMA (paginação)

0x92 A20

0xa0 - 0xa1 segundo PIC

0xc0 - 0xdf segundo controlador DMA

0x170 - 0x177 segundo controlador de disco ATA

0x1f0 - 0x1f7 primeiro controlador de disco ATA

0x278 - 0x27a porta paralela

0x2f8 - 0x2ff segunda porta serial (COM2)

0x3b0 - 0x3df VGA

0x3f0 - 0x3f7 controlador de disquete

0x3f8 - 0x3ff primeira porta serial (COM1)

3.0 – x86: FPU

os processadores alem de fazer operações aritmeticas com numeros inteiros, boa parte deles tambemfazem operações com pontos flutuantes que são os numeros com virgula ou numeros com casas decimais (numeros reais), o problema nesse tipo de numero é forma que ele é armazenado ou seja a forma que o processador deve interpretar o próprio numero em formato binario, a sua representação é complexo comparado aos numeros inteiros em formato binario já que o numero inteiro é apenas o numero em formato binario (com exceção os numeros negativos onde tem uma representação um pouco diferente e de forma invertida lendo do maior para o menor para representar aquele valor negativo), no ponto flutuante a sua representação é em notação cientifica em binario onde os primeiros bits representa o sinal seguido de uma sequencia de bits representando o expoente e o restante dos bits sendo a mantissa, na arquitetura x86 para as operações de ponto flutuante foi utilizado um co-processador externo chamado 8087 (x87) que futuramente seria integrado dentro dopróprio processador principal, nas operações de ponto flutuante são usados instruções especificas chamadas instruções de fpu (float point unit), alem de novos registradores para operações com ponto flutuante entre eles temos os registradores de st0 ate o st7, uma das diferenças dos registradores de fpu que não podemos mover os valores livremente de forma imediata como nos registradores de uso gerais, nesses registradores de fpu temos que carregar eles da memoria usando a instrução fld

fld [kodo]

kodo dq 31.5

o funcionamento dos registradores de fpu st0 ate o st7 são parecidos com a pilha de memoria, o registrador st0 sempre aponta para o topo ou seja o ultimo dado carregado pela instrução fld que sempre estara armazenado no st0, conforme os dados são carregados são jogado para o topo da pilha do fpu que seria o registrador st0 e os outros são movidos para os registradores mais alto que

Page 74: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

o registrador atual deles (quando o registrador st7 estiver com um valor carregado o mesmo sera movido para o registrador st0 novamente)

fld [kodo] ;1.68 (st1) ← era o st0 fld [kami] ;989.223 (st0) ← ultimo dado carregado é atualmente o st0

kodo dq 1.68kami dq 989.223

é possível usar numeros negativos tambem

fld [kodo]

kodo dq -10.8

tambem é possível carregar valores valores dword

fld [kodo]

kodo dd 5.5

podemos carregar um valor zerado com a instrução fldz para o topo da pilha do fpu

fldz

para carregar o valor 1.0 para o topo da pilha de fpu usamos a instrução fld1

fld1

tambem é possível carregar o valor de PI para o topo da pilha do fpu com a instrução fldpi

fldpi

da mesma forma podemos armazenar o valor em um endereço da memoria usando a instrução fst, sera armazenado o registrador st0 na memoria e sera descartado o valor, lembrando que o valor armazenado não sera em formato inteiro e sim em formato flutuante

fld [kodo] ;carrega 5.6 para o registrador st0fst [kami] ;salva o valor do registrador st0 na memoria

kodo dq 5.6kami dq 0

podemos carregar um numero em formato inteiro para o topo do fpu usando a instrução fild, o numero carregado sera armazenado no st0 como formato flutuante

fild [kodo]

kodo dq 50

Page 75: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

se a gente armazenar o exemplo anterior com a instrução fst sera armazenado em formato flutuante, essa seria uma boa forma de converter numeros em formato inteiros para formato flutuante

fild [kodo]fst [kami]

kodo dq 50kami dq 0 tambem podemos armazenar o numero em formato inteiro com a instrução fist, da mesma forma isso pode ser usado para converter de float para inteiro

fld [kodo]fist [kami]

kodo dq 5.98kami dq 0

podemos zerar toda pilha do fpu usando a instrução finit

fld [kodo]fld [kami]finit

kodo dq 10.6kami dq 58.4

é possivel somar o registrador st0 e o registrador st1 com a instrução fadd, o resultado sera armazenado no registrador st0

fld [kodo]fld [kami]fadd

kodo dq 7.8kami dq 3.5

podemos especificar os registradores de fpu usados na operação da instrução fadd, porem é necessario que sempre tenha o registrador st0 na operação

fld [kodo]fld [kami]fld [fts315]fadd st0,st2

kodo dq 7.8kami dq 3.5fts315 dq 8.9

Page 76: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

alem da adição tambem podemos fazer a subtração com a instrução fsub, a subtração é feita invertida do st1 - st0 e armazenado no st0

fld [kodo]fld [kami]fsub

kodo dq 35.3kami dq 12.2

da mesma forma podemos especificar os registradores de fpu com qual vamos subtrair

fld [kodo]fld [kami]fsub st1,st0

kodo dq 35.3kami dq 12.2

podemos usar esse recursos da subtração e adição para mover valores dos registradores, um exemplo seria se eu quiser desempilhar o registrador st2

fsub st0,st0 ;zera o registrador st0 subtraindo ele por ele mesmofadd st0,st2 ;adiciona o valor ao registrador st0fst kodo ;salva o registrador st0 na memoria

kodo dq 0

tambem temos a instrução fmul para multiplicação

fld [kodo]fld [kami]fmul

kodo dq 5.2kami dq 2.0

na instrução fmul tambem pode ser especificado os registradores

fld [kodo]fld [kami]fmul st0,st1

kodo dq 5.2kami dq 2.0

para a gente fazer a divisão usamos a instrução fdiv, da mesma forma que o fsub o fdiv é feito invertido o st1 / st0

fld [kodo]

Page 77: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

fld [kami]fdiv

kodo dq 9.0kami dq 4.0

podemos especificar os registradores de fpu na instrução fdiv sendo que o dividendo é sempre o primeiro registrador e o divisor o segundo

fld [kodo]fld [kami]fdiv st1,st0

kodo dq 9.0kami dq 4.0

é possível retornar a raiz quadrada usando a instrução fsqrt, a operação sera feita no registrador st0

fld [kodo]fsqrt

kodo dq 9.0

com a instrução fabs conseguimos o valor absoluto do numero

fld [kodo]fabs

kodo dq -1.5

podemos retornar o seno com a instrução fsin sendo o valor passado em radiano e não em grau

fld [kodo]fsin

kodo dq 0.78539816 ;45º

para o coseno usamos fcos

fld [kodo]fcos

kodo dq 0.87266463 ;50º

é possível comparar valores no formato flutuante com a instrução fcom, sera armazenado no registrador de status do fpu

fld [kodo]fld [kami]

Page 78: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

fcom

kodo dq 56.0kami dq 30.2

tambem é possível especificar o segundo registrador na comparação porem o st0 deve ser sempre o primeiro

fld [kodo]fld [kami]fld [fts315]fcom st0,st2

kodo dq 56.0kami dq 30.2fts315 dq 40.5

o registrador status do fpu (fstat) é um registrador de 16bits e o seu funcionamento é parecido com oregistrador das flags sendo usado para sinalizar, na comparação é utilizado os bits 8,9,10 e 14 que são chamados de flags CR de status do fpu (não confundir com o registrador de controle CR da cpu)

15bit 0bitCR3 CR2 CR1 CR0

em uma comparação quando o registrador st0 é maior que o outro então é setado o cr0, cr2 e cr3 com o valor 0

fld [kodo]fld [kami]fcom ;cr0=0, cr2=0, cr3=0

kodo dq 1.0kami dq 20.2

quando o segundo registrador é maior que o st0 então a flag cr0 é setada em 1 e o resto em 0

fld [kodo]fld [kami]fcom ;cr0=1, cr2=0, cr3=0

kodo dq 200.5kami dq 8.3

se os dois numeros forem iguais então o cr3 vai esta setado em 1 e o restante em 0

fld [kodo]fld [kami]fcom ;cr0=0, cr2=0, cr3=1

kodo dq 3.15

Page 79: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kami dq 3.15

não é possível ter acesso direto ao registrador de status do fpu para isso temos armazenar ele na memoria com instrução fstsw seguido do endereço

fld [kodo]fld [kami]fcomfstsw [variavel]

kodo dq 31.5kami dq 31.5variavel dw 0

tambem podemos armazenar no registrador ax

fld [kodo]fld [kami]fcomfstsw ax

kodo dq 31.5kami dq 31.5

com a instrução sahf sera armazenado o resultado nas flags da cpu direto do registrador ax, com issopodemos usar as instruções ja, jb e jz para pular sobre determinada condição, sendo o ja ira pular caso o registrador st0 seja o maior, jb caso o registrador st0 seja o menor ou jz quando os dois registradores forem iguais

fld [kodo]fld [kami]fcomfstsw axsahf

jz igualja maiorjb menor

igual:maior:menor:

kodo dq 10.0kami dq 30.0

podemos comparar e setar direto na flag com a instrução fcomi

fld [kodo]fld [kami]

Page 80: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

fcomi

jz igualja maiorjb menor

igual:maior:menor:

kodo dq 30.0kami dq 10.0

é possível especificar o registrador na instrução fcomi tambem

fld [kodo]fld [kami]fcomi st0,st1

kodo dq 30.0kami dq 10.0

3.1 – x86: MMX

as instruções mmx são instruções de empacotamento de dados de tamanho variado sem grande perda de velocidade, sendo usada nela a metodologia de SIMD (única instrução, multiplos dados), ommx surgiu a partir dos processadores da família pentium em 1996 e o significado da sigla mmx é MultiMedia eXtension, ele trouxe consigo 8 novos registradores gerais de 64bits que foram denominado de mm que vai do mm0 ate o mm7, o mmx permite saturação do numero evitando o overflow e underflow alem da manipulação com sinal ou sem sinal do numero e a sua quantidade debytes manipulado naquele determinado momento de forma paralela, o mmx usa os registradores de fpu para o armazenamento dos registradores de mmx então não é possível usar instruções de fpu enquanto estiver usando os de mmx ou vice versa, podemos atribuir um valor ao registrador do mmx usando a instrução movq

movq mm0,[kodo]

kodo dq 10

o mesmo poderia ser feito na sintaxe at&t como no compilador gas

movq (kodo),%mm0

kodo .quad 10

diferente do fpu que se limita a atribuição ao topo da pilha sendo ela o registrador st0, no mmx podemos atribuir para qualquer registrador dele como acontece nos registradores de uso geral da propria cpu

movq mm4,[kodo]

Page 81: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo dq 10

tambem podemos atribuir o valor de um registrador mmx para outro registrador mmx

movq mm1,[kodo]movq mm2,mm1

kodo dq 100

ou de um registrador mmx direto para memoria

movq [kodo],mm0

kodo dq 0

com a instrução movd passamos valores de 32bits para o registrador mmx

movd mm0,[kodo]

kodo dd 10

podemos armazenar tambem numeros negativos

movq mm0,[kodo]

kodo dq -80

tambem é possível passar valores no formato flutuante para os registradores, orem as operações ṕsera feita como numeros inteiros e não operações como ponto flutuante

movq mm0,[kodo]

kodo dq 50.6

o mmx permite a gente manipular o numero como um todo ou dividir ele em partes sendo uma quantidade especifica daquele numero em um certo agrupamento de bits, se a gente mover o valor 0x9052781251671287 para um registrador mmx podemos manipular esse valor como o numero completo de 64bits ou dois valores separados de 32bits, o mesmo pode ser feito com 4 valores separados de 16bits ou 8 valores de 8bits

64bits 9052781251671287

32bits 90527812 51671287

16bits 9052 7812 5167 1287

8bits 90 52 78 12 51 67 12 87

para a gente somar dois registradores mmx usando os 64bits dele basta utilizar a instrução paddq, assim a operação sera feita com o numero como um todo sendo um único registrador de 64bits

Page 82: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movq mm0,[kodo]movq mm1,[kami]paddq mm0,mm1 ;0x9052781251671287 + 0x5873579612458965

kodo dq 0x9052781251671287kami dq 0x5873579612458965

outra forma seria somar um registrador direto a um valor em um endereço de memoria

movq mm0,[kodo]paddq mm0,[kami]

kodo dq 0x9052781251671287kami dq 0x5873579612458965

embora as operações seja feito usando todos os bits do registrador não precisamos usar todos os bitscolocando um valor menor (isso seria equivalente a ter o numero e vários zeros a esquerda)

movq mm0,[kodo]movq mm1,[kami]paddq mm0,mm1

kodo dq 0x90kami dq 0x58

para somar dois registradores mmx usando agrupamento de 32bits usamos a instrução paddd, nesse caso os primeiros 32bits da fonte sera somados aos primeiros 32bits do destino, e os outros 32bits restante da fonte tambem sera somado aos 32bits restantes do destino de forma separada ou seja 2 grupos de 32bits sendo somados de forma paralela

movq mm0,[kodo]movq mm1,[kami]paddd mm1,mm0 ;0x33333333 + 0x22222222, 0x44444444 + 0x88888888

kodo dq 0x3333333344444444kami dq 0x2222222288888888

podemos fazer o mesmo com 4 grupos de 16bits usando a instrução paddw para somar

movq mm0,[kodo]movq mm1,[kami]paddw mm1,mm0 ;0x2222 + 0x5615, 0x8888 + 0x9874, 0x9999 + 0x6786, 0x1111 + 0x4320

kodo dq 0x2222888899991111kami dq 0x5615987467864320

para somar em 8 grupos de 8bits usamos a instrução paddb

movq mm0,[kodo]

Page 83: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movq mm1,[kami]paddb mm1,mm0 ;0x01 + 0x35, 0x48 + 0x47, 0x75 + 0x82, 0x78 + 0x45, 0x62+ 0x65 ...

kodo dq 0x0148757862483541kami dq 0x3547824565687521

quando somamos um numero e o seu resultado é acima do tamanho maximo do agrupamento o acontece o overflow o mesmo vale para o oposto quando decrementamos um numero abaixo de zero, no próximo exemplo o resultado é 0x02 e não 0x102 já que estamos trabalhando com byte onde o seu maior numero é 0xff

movq mm0,[kodo]movq mm1,[kami]paddb mm1,mm0 ;02

kodo dq 0xf2kami dq 0x10

os registradores mmx permite a saturação com isso não é possível o overflow e nem o underflow, quando o valor ultrapassar o valor maximo ou valor minimo ele fica com esse valor maximo ou com valor minimo, para especificar que a operação esta sendo usado a saturação usamos o “s” na instrução (paddsb e paddsw), essa operação sera feita com numeros com sinais

movq mm0,[kodo]movq mm1,[kami]paddsb mm1,mm0 ;7f

kodo dq 0x70kami dq 0x20

tambem podemos fazer usando numeros sem sinais com a instrução paddusb e paddusw

movq mm0,[kodo]movq mm1,[kami]paddusb mm1,mm0 ;ff

kodo dq 0xf2kami dq 0x10

para a subtração com um único grupo de 64bits usamos a instrução psubq

movq mm0,[kodo]movq mm1,[kami]psubq mm0,mm1

kodo dq 50kami dq 8

com a instrução psubd subtraimos com agrupamento de 32bits sendo 2 grupos de 32bits

Page 84: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movq mm0,[kodo]movq mm1,[kami]psubd mm0,mm1

kodo dq 0x9054903284537920kami dq 0x2158458641254687

para subtrair com quatro grupos de 16bits usamos a instrução psubw

movq mm0,[kodo]movq mm1,[kami]psubw mm0,mm1

kodo dq 0x5098711234870569kami dq 0x1548937658219647

tambem temos a instrução psubb que subtrair grupos de 8bits sendo 8 grupos ao todo

movq mm0,[kodo]movq mm1,[kami]psubb mm0,mm1

kodo dq 0x3728468216549872kami dq 0x2458745213547899

é possível usar saturação nas operações de substração com sinal com as instruções psubsb e psubsw

movq mm0,[kodo]movq mm1,[kami]psubsb mm0,mm1

kodo dq 0x3728468216549872kami dq 0x2458745213547899

ou na subtração com saturação sem sinal com a instrução psubusb e psubusw

movq mm0,[kodo]movq mm1,[kami]psubusb mm0,mm1

kodo dq 0x3728468216549872kami dq 0x2458745213547899

a multiplicação do mmx deve ser feita com agrupamento de 16bits apenas, na multiplicação usamosa instrução pmullw

movq mm0,[kodo]movq mm1,[kami]pmullw mm0,mm1

Page 85: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo dq 0x0201350213240002kami dq 0x0002010050100003

podemos fazer operações bit a bit com o mmx entre entre essa operações temos a and com a instrução mmx pand, essas instruções bit a bit é feito em um único agrupamento de 64bits

movq mm0,[kodo]movq mm1,[kami]pand mm0,mm1

kodo dq 0x6000kami dq 0x505

outra instrução bit a bit é o por, essa instrução permite a operação or

movq mm0,[kodo]movq mm1,[kami]por mm0,mm1

kodo dq 0x1125kami dq 0x1252

tambem podemos fazer a operação bit a bit xor usando a instrução pxor

movq mm0,[kodo]movq mm1,[kami]pxor mm0,mm1

kodo dq 0x987654kami dq 0x50

para o deslocamento bit a bit a esquerda usamos a instrução psllq, sera deslocado um único grupo de 64bits

movq mm0,[kodo]movq mm1,[kami]psllq mm0,mm1

kodo dq 0x5kami dq 1

para deslocar a direita em agrupamentos de 64bits usamos a instrução psrlq

movq mm0,[kodo]movq mm1,[kami]psrlq mm0,mm1

kodo dq 0x5056kami dq 2

Page 86: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

com a instrução pslld deslocamos a esquerda usando agrupamentos de 32bits

movq mm0,[kodo]movq mm1,[kami]pslld mm0,mm1

kodo dq 0x5kami dq 1

ou com psrld deslocamos a direita com agrupamento de 32bits

movq mm0,[kodo]movq mm1,[kami]psrld mm0,mm1

kodo dq 0x5056kami dq 2

em agrupamentos de 16bits o deslocamento para esquerda deve ser usado a instrução psllw

movq mm0,[kodo]movq mm1,[kami]psllw mm0,mm1

kodo dq 0x5kami dq 1

já o deslocamentos a direita em 16bits usamos a instrução psrlw

movq mm0,[kodo]movq mm1,[kami]psrlw mm0,mm1

kodo dq 0x5056kami dq 2

podemos comparar dois registradores mmx para ver se os bytes neles são iguais usando a instrução pcmpeqb, nessa comparação sera usado agrupamento de 8bits, caso o primeiro numero seja igual aosegundo sera gerado o byte 0xff no destino caso seja diferente sera gerado o valor 0x0 no destino naquele determinado byte

movq mm0,[kodo]movq mm1,[kami]pcmpeqb mm0,mm1 ;0xffff00ff00

kodo dq 0x5010876405kami dq 0x5010606412

podemos fazer a comparação usando 4 agrupamentos de 16bits com a instrução pcmpeqw

Page 87: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movq mm0,[kodo]movq mm1,[kami]pcmpeqw mm0,mm1 ;0xffff0000

kodo dq 0x10208590kami dq 0x10205090

ou dois agrupamentos de 32bits com pcmpeqd para a comparação

movq mm0,[kodo]movq mm1,[kami]pcmpeqd mm0,mm1 ;0xffffffff00000000

kodo dq 0x3290647810208590kami dq 0x3290647851020309

podemos juntar os bytes de dois registradores mmx em um único registrador mmx, para que isso seja possível temos que fazer uma pequena conversão tratando um determinado agrupamento de bits como um agrupamento menor com saturação, a instrução packuswb converte agrupamentos de 16bits em agrupamentos de 8bits, caso o valor no agrupamento ultrapasse o valor maximo de 8bits sera mantido o valor maximo de 8bits que é o valor saturado (0xff), sera armazenado no regitrador destino a metade dos bytes para um registrador e a outra metade para o outro registrador

movq mm0,[kodo]movq mm1,[kami]packuswb mm0,mm1 ;0x8577ff05ff564732 = 0x8577ff05, 0xff564732

kodo dq 0x1011005600470032kami dq 0x0085007710500005

quando usamos registradores mmx os registradores de fpu fica inutilizavel naquele momento, o problema que depois do uso do mmx para a gente usar o fpu temos que limpar os registradores de mmx com a instrução emms, nunca tente utilizar mmx e fpu junto ou o fpu sem limpar o mmx antes

movq mm0,[kodo]movq mm1,[kami]emmsfld [flavio]

kodo dq 0x50050kami dq 0x315flavio dq 50.0

3.2 – x86: 3DNow

o 3dnow é uma tecnologia adicionada a partir dos computadores k6-2 em 1998, essa tecnologia trouxe novas instruções que são usados em conjunto com o mmx para instruções com pontos flutuantes, o 3dnow teve alguma melhorias ao decorrer do tempo como “3dnow extended” e o “3dnow pro”, trazendo algumas instruções novas e permitindo a sua interação com algumas tecnologias, para o 3dnow usamos as mesmas instruções do mmx, como exemplo podemos mover o

Page 88: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

valor da memoria para os registrador mmx com o movd

movq mm0,[kodo]movq mm1,[kami]

kodo dd 10.7kami dd 51.92

uma das instruções do 3dnow é o pfadd que permite somar dois registradores mmx usando a operação com ponto flutuante

movd mm0,[kodo]movd mm1,[kami]pfadd mm0,mm1

kodo dd 10.7kami dd 51.92

lembrando que o mesmo pode ser feito usando a sintaxe at&t

movd (kodo),%mm0movd (kami),%mm1pfadd %mm1,%mm0

kodo .float 10.7kami .float 51.92

para subtração usando o 3dnow usamos a instrução pfsub

movd mm0,[kodo]movd mm1,[kami]pfsub mm0,mm1

kodo dd 10.6kami dd 5.3

podemos fazer a multiplicação com pfmul

movd mm0,[kodo]movd mm1,[kami]pfmul mm0,mm1

kodo dd 10.5kami dd 2.0

é possível comparar usando a instrução pfcmpeq, caso os numeros sejam iguais então sera atribuido para o destino o numero 0xffffffff, caso seja diferente sera atribuido 0x00000000

movd mm0,[kodo]movd mm1,[kami]

Page 89: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pfcmpeq mm0,mm1

kodo dd 15.12kami dd 15.12

para comparar se o numero é maior usamos a instrução pfcmpgt, sera atribuido ao destino 0xfffffff caso seja maior ou 0x00000000 caso seja menor

movd mm0,[kodo]movd mm1,[kami]pfcmpgt mm0,mm1

kodo dd 20.1kami dd 15.12

podemos converter um numero no formato inteiro para o formato float usando a instrução pi2fd

movd mm0,[kodo]pi2fd mm1, mm0

kodo dd 10 tambem pode ser feito o oposto um numero float para inteiro com a instrução pf2id

movd mm0,[kodo]pf2id mm1, mm0

kodo dd 10.5

como o 3dnow utiliza os registradores de mmx, não é possível usar instruções fpu ao mesmo tempo,para utilizar a fpu temos que usar a instrução emms, tambem existe a instrução femms junto ao 3dnow que tem o mesmo proposito do emms porem melhorado e mais rapido

movd mm0,[kodo]movd mm1,[kami]pfadd mm0,mm1movd [flavio],mm0femmsfld [kodo]

kodo dd 5.5kami dd 2.0flavio dd 0

3.3 – x86: SSE

o sse (Streaming Simd Extensions) é um conjunto de instruções que tem o seu funcionamento bem semelhante ao mmx, ele foi introduzido a partir do pentium 3 em 1999, com o tempo teve novas melhorias (sse2, sse3, ssse3, sse4 e sse5), sendo uma das diferenças entre o sse e o mmx que os valores nos registradores sse não são armazenados nos registradores da fpu como acontece com os

Page 90: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

do mmx, outra diferença seria no tamanho dos registradores do sse que são registradores de 128bits de tamanho enquanto os do mmx são apenas de 64bits, existem 8 registradores sse para uso geral que são os registradores xmm, que vão do registrador xmm0 ate o registrador xmm7, em processadores com arquitetura de 64bits são 16 registradores de sse que vão do xmm0 ate o xmm15,para a gente mover um determinado valor de 64bits da memoria para um registrador sse usamos a instrução movq

movq xmm0,[kodo]

kodo dq 900

o mesmo pode ser feito na sintaxe at&t

movq (kodo),%xmm0

kodo .quad 900

da mesma forma podemos mover 32bits usando a instrução movd

movd xmm4,[kodo]

kodo dd 900

podemos mover 64bits do registrador sse para outro registrador sse com a instrução movq

movq xmm0,[kodo]movq xmm1,xmm0

kodo dq 900

tambem podemos mover 64bits dos registradores sse para memoria

movq [novo],xmm3

novo dq 0

da mesma forma que o registrador mmx o sse tambem aceita numeros negativos

movq xmm0,[kodo]

kodo dq -50

ou numeros de ponto flutuantes

movq xmm0,[kodo]

kodo dq 48.987

outra instrução que permite mover 32bits para os registradores sse é a instrução movss

Page 91: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movss xmm0,[kodo]

kodo dd 0x50505050

com a instrução movsd movemos dois valores de 32bits cada para o registrador sse

movsd xmm0,[kodo] ;0x5000000010000000

kodo dd 0x10000000,0x50000000

pelo movsd tambem é possivel mover um único valor de 64bits

movsd xmm0,[kodo] ;0x5000000010000000

kodo dq 0x5000000010000000

podemos mover 4 valores de 64bits não alinhados da memoria para um determinado registrador sse com a instrução movups, esses quatros valores junto totaliza os 128bits do registrador sse

movups xmm0,[kodo] ;0x11111111222222223333333344444444

kodo dd 0x44444444,0x33333333,0x22222222,0x11111111

ou dois valores de 64bits para ter o mesmo resultado de um único valor de 128bits

movups xmm0,[kodo] ;0x11111111222222223333333344444444

kodo dq 0x3333333344444444,0x1111111122222222

tambem podemos mover valores alinhados da memoria com a instrução movaps, para o uso dessa instrução temos que especificar o alinhamento sendo que alguns compiladores usa a diretiva align para fazer isso (lembrando align é uma diretiva do compilador e não uma instrução da linguagem), quando usamos a instrução movaps sem esse alinhamento vai da falha de segmentação no programa

movaps xmm0,[kodo]

align 16kodo dq 0x3333333344444444,0x1111111122222222

no compilador gas que usa a sintaxe at&t é a diretiva .align

movaps (kodo),%xmm0

.align 16kodo: .int 0x33333333,0x44444444,0x11111111,0x22222222

podemos mover um valor de 64bits para um registrador sse na parte menos significativa dele com a instrução movlps

Page 92: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movlps xmm1,[kodo] ;0x00000000000000006090801087801247

kodo dq 0x6090801087801247

para a gente mover um valor de 64bits para a parte mais significativa usamos instrução movhps

movhps xmm1,[kodo] ;0x60908010878012470000000000000000

kodo dq 0x6090801087801247

podemos usar o movhps e o movlps para gerar um único valor de 128bits usando valores de 64bits separados na memoria

movhps xmm0,[kodo] ;0x54898785187924540000000000000000movlps xmm0,[kami] ;0x54898785187924543845827721254668

kodo dq 0x5489878518792454kami dq 0x3845827721254668

o sse tambem funciona com agrupamentos igual o mmx, a diferença que o sse permite um unico agrupamento de 128bits, dois agrupamentos de 64bits, quatro agrupamentos de 32bits, oito agrupamentos de 16bits, e dezesseis agrupamentos de 8bits, então se a gente colocar em um registrador sse o numero 0x81977314951795218752311314789514 que é um numero de 128bits o computador pode interpretar ele como aquele numero ou separadamente como grupos menores

128bits 81977314951795218752311314789514

64bits 8752311314789514 8197731495179521

32bits 87523113 14789514 81977314 95179521

16bits 8752 3113 1478 9514 8197 7314 9517 9521

8bits 87 52 31 13 14 78 95 14 81 97 71 14 95 17 95 21

para a gente somar em agrupamentos de 64bits usamos a instrução pmovq, nesse caso sera somado dois agrupamentos seprados

movups xmm0,[kodo] ;0x6080906875910856|9987568905896912movups xmm1,[kami] ;0x1090956991285178|a815f58012574689paddq xmm0,xmm1 ;0x711125d206b959ce|419d4c0917e0af9b

kodo dq 0x9987568905896912,0x6080906875910856kami dq 0xa815f58012574689,0x1090956991285178

para a adição com agrupamentos de 32bits usamos a instrução paddd

movups xmm0,[kodo] ;0x60809068|75910856|99875689|05896912movups xmm1,[kami] ;0x10909569|91285178|a815f580|12574689paddd xmm0,xmm1 ;0x711125d1|06b959ce|419d4c09|17e0af9b

Page 93: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kodo dq 0x9987568905896912,0x6080906875910856kami dq 0xa815f58012574689,0x1090956991285178

com a instrução paddw fazemos operações de adição em agrupamentos de 16bits

movups xmm0,[kodo] ;0x6080|9068|7591|0856|9987|5689|0589|6912movups xmm1,[kami] ;0x1090|9569|9128|5178|a815|f580|1257|4689paddw xmm0,xmm1 ;0x7110|25d1|06b9|59ce|419c|4c09|17e0|af9b

kodo dq 0x9987568905896912,0x6080906875910856kami dq 0xa815f58012574689,0x1090956991285178

o mesmo pode ser feito com a instrução paddb para somar em grupos de 8bits

movups xmm0,[kodo] ;0x60|80|90|68|75|91|08|56|99|87|56|89|05|89|69|12movups xmm1,[kami] ;0x10|90|95|69|91|28|51|78|a8|15|f5|80|12|57|46|89paddb xmm0,xmm1 ;0x70|10|25|d1|06|b9|59|ce|41|9c|4b|09|17|e0|af|9b

kodo dq 0x9987568905896912,0x6080906875910856kami dq 0xa815f58012574689,0x1090956991285178

podemos fazer a operação de adição levando em conta a saturação para agrupamentos de 16bits e agrupamentos de 8bits com as instruções paddsw e paddsb para operações com sinal ou com as instruções paddusb e paddusw para as operações sem sinal, ou seja se nesse agrupamento acontecer um overflow vai ficar com o maior numero daquela palavra

movups xmm0,[kodo] ;0x6080|9068|7591|0856|9987|5689|0589|6912movups xmm1,[kami] ;0x1090|9569|9128|5178|a815|f580|1257|4689paddusw xmm0,xmm1 ;0x7110|ffff|ffff|59ce|ffff|ffff|17e0|af9b

kodo dq 0x9987568905896912,0x6080906875910856kami dq 0xa815f58012574689,0x1090956991285178

é possivel somar valores de ponto flutuantes de única precisão (32bits) com a instrução addps

movups xmm0,[kodo]movups xmm1,[kami]addps xmm0,xmm1

kodo dd 1.5, 689.2, 78.2 10.64kami dd 2.6, 56.3, 43, 23.5

para operações de subtração usamos a instrução psubq para subtrair em grupos de 64bits ou seja 2 grupos de 64bits totalizando 128bits

movups xmm0,[kodo] ;0x0000000000000080|0000000000000050movups xmm1,[kami] ;0x0000000000000035|0000000000000010psubq xmm0,xmm1 ;0x0000000000000045|0000000000000040

kodo dq 0x50,0x80

Page 94: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kami dq 0x10,0x35

para subtrair em grupos de 32bits usamos a operação psubd

movups xmm0,[kodo] ;0x80589125|47233254|80538901|68712547movups xmm1,[kami] ;0x35257814|65891254|56782547|89521351psubq xmm0,xmm1 ;0x4b331911|e19a2000|29db63ba|df1f11f6

kodo dq 0x8053890168712547,0x8058912547233254kami dq 0x5678254789521351,0x3525781465891254

na subtração em agrupamentos de 16bits usamos a instrução psubw

movups xmm0,[kodo]movups xmm1,[kami]psubw xmm0,xmm1

kodo dq 0x8053890168712547,0x8058912547233254kami dq 0x5678254789521351,0x3525781465891254

com a instrução psubb fazemos a operação de subtração com agrupamentos de 8bits

movups xmm0,[kodo]movups xmm1,[kami]psubb xmm0,xmm1

kodo dq 0x8053890168712547,0x8058912547233254kami dq 0x5678254789521351,0x3525781465891254

operações de subtração com saturação e com sinal usamos as instruções psubsw para agrupamentos de 16bits e psubsb para operações de 8bits, para a subtração com saturação para numeros sem sinaisusamos psubusw para agrupamentos de 16bits e psubusb para agrupamentos de 8bits

movups xmm0,[kodo]movups xmm1,[kami]psubusb xmm0,xmm1

kodo dq 0x8053890168712547,0x8058912547233254kami dq 0x5678254789521351,0x3525781465891254

podemos subtrair numeros com ponto flutuante com a instrução subps, sera feito a operação em agrupamentos de 32bits

movups xmm0,[kodo]movups xmm1,[kami]subps xmm0,xmm1

kodo dd 56.322, 86.231, 123.2, 12.0kami dd 10.5, 23.23, 21.8, 10.0

Page 95: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

na multiplicação usamos a instrução pmullw sera feito em agrupamentos de 16bits

movups xmm0,[kodo]movups xmm1,[kami]pmullw xmm0,xmm1

kodo dq 0x5060708090102010,0x5420021245328914kami dq 0x0002000300040050,0x0003010000030002

tambem podemos fazer a multiplicação com pontos flutuantes usando a instrução mulps sera feito em agrupamentos de 32bits

movups xmm0,[kodo]movups xmm1,[kami]mulps xmm0,xmm1

kodo dd 10.2, 550.0, 987.12, 6.987kami dd 2.0, 5.2, 4.7, 8.2

podemos fazer a divisão com a instrução divps em agrupamentos de 32bits para numeros de ponto flutuantes

movups xmm0,[kodo]movups xmm1,[kami]divps xmm0,xmm1

kodo dd 10.0, 20.3, 200.54, 5058.234kami dd 2.0, 10.0, 4.6, 0.23

para retornar a raiz quadrada usamos a instrução sqrtps para ser feito em agrupamentos de 32bits

movups xmm0,[kodo]sqrtps xmm0,xmm0

kodo dd 9.0, 100.0, 31.5, 8.5

na operação logica and usamos a instrução pand

movups xmm0,[kodo]movups xmm1,[kami]pand xmm0,xmm1

kodo dq 0x5871258315978514,0x3785189753841479kami dq 0x9125875815633211,0x8745147854789514

outra instrução que permite a logica and é o andps que permite operação logica and em numeros usando agrupamento de 32bits

movups xmm0,[kodo]movups xmm1,[kami]

Page 96: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

pand xmm0,xmm1

kodo dd 10, 15, 665, 158kami dd 20, 15, 7844, 10

para as operações logicas or usamos a instrução por

movups xmm0,[kodo]movups xmm1,[kami]por xmm0,xmm1

kodo dq 0x5871258315978514,0x3785189753841479kami dq 0x9125875815633211,0x8745147854789514

tambem podemos fazer a operação or com a instrução orps, sera feito em agrupamentos de 32bits

movups xmm0,[kodo]movups xmm1,[kami]orps xmm0,xmm1

kodo dd 10, 15, 665, 158kami dd 20, 15, 7844, 10

com o pxor a gente faz a operação logica xor

movups xmm0,[kodo]movups xmm1,[kami]pxor xmm0,xmm1

kodo dq 0x5871258315978514,0x3785189753841479kami dq 0x9125875815633211,0x8745147854789514

tambem podemos fazer em agrupamentos de 32bits a operação xor com a instrução xorps

movups xmm0,[kodo]movups xmm1,[kami]xorps xmm0,xmm1

kodo dd 10, 15, 665, 158kami dd 20, 15, 7844, 10

podemos deslocar o numero bit a bit a esquerda usando a instrução psllq, sera deslocado dois grupos de 64bits o operador usado para o deslocamento sera apenas os primeiro bytes e não um agrupamento

movups xmm0,[kodo] ;0x00000000000005100000000000000111movups xmm1,[kami] ;2psllq xmm0,xmm1 ; 0x510 << 2 | 0x111 << 2

kodo dq 0x111,0x510

Page 97: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

kami dq 2, 0

ou deslocar o numero bit a bit a direita usando a instrução pslrq para agrupamentos de 64bits

movups xmm0,[kodo]movups xmm1,[kami]psrlq xmm0,xmm1

kodo dq 0x111,0x510kami dq 2, 0

para deslocar o numero a esquerda com agrupamento de 32bits usamos a instrução pslld

movups xmm0,[kodo]movups xmm1,[kami]pslld xmm0,xmm1

kodo dq 0x3000000012, 0x510500000012kami dq 2, 0

para o deslocamento a direita usando agrupamentos de 32bits usamos a instrução psrld

movups xmm0,[kodo]movups xmm1,[kami]psrld xmm0,xmm1

kodo dq 0x331500001287, 0x331500001287kami dq 2, 0

com a instrução psllw deslocamos a esquerda usando agrupamentos de 16bits

movups xmm0,[kodo]movups xmm1,[kami]psllw xmm0,xmm1

kodo dq 0x35003001, 0x85202001kami dq 2, 0

como tambem podemos usar o psrlw para deslocar a direita com agrupamento de 16bits

movups xmm0,[kodo]movups xmm1,[kami]psrlw xmm0,xmm1

kodo dq 0x35003001, 0x85202001kami dq 2, 0

tambem podemos comparar em agrupamentos de 64bits usando a instrução pcmpeqq, caso ambos os numeros seja igual naquele determinado agrupamento o resultado sera 0xffffffffffffffff caso seja diferente o resultado sera 0x0000000000000000 naquele agrupamento

Page 98: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

movups xmm0,[kodo]movups xmm1,[kami]pcmpeqq xmm0,xmm1 ;0x0000000000000000ffffffffffffffff

kodo dq 0x1111222233334444,0x5555333344448888kami dq 0x1111222233334444,0x5555333344441111

podemos fazer a comparação usando agrupamentos de 32bits com a instrução pcmpeqd

movups xmm0,[kodo]movups xmm1,[kami]pcmpeqq xmm0,xmm1 ;0xffffffff00000000ffffffff00000000

kodo dq 0x1111222233334444,0x5555333344448888kami dq 0x1111222200004444,0x5555333344441111

para comparar usando agrupamentos de 16bits usamos a instrução pcmpeqw

movups xmm0,[kodo]movups xmm1,[kami]pcmpeqw xmm0,xmm1 ;0xffffffffffff0000ffffffff0000ffff

kodo dq 0x1111222233334444,0x5555333344448888kami dq 0x1111222200004444,0x5555333344441111

com a instrução pcmpeqb comparamos com agrupamentos de 8bits

movups xmm0,[kodo]movups xmm1,[kami]pcmpeqb xmm0,xmm1 ;0xffffffffffff0000ffffffff0000ffff

kodo dq 0x1111222233334444,0x5555333344448888kami dq 0x1111222200004444,0x5555333344441111

4.0 – x86: Modo Protegido

quando o 286 (ibm-at) foi lançado ele podia acessar ate 16MB de memoria diferente dos processadores x86 anteriores como o 8086 que acessava no maximo 1MB de memoria, para a gente conseguir acessar mais de 1MB no 286 ou ate mesmo nos processadores atuais é necessario habilitar o A20 sendo ele um chaveamento que permite o acesso a mais de um 1MB de memoria, o problema em habilitar o A20 no 286 que não é possivel desabilitar sem reiniciar a maquina, a partir do 386 alem de permitir o acesso a 4GB de memoria podemos ativar e desativar o A20 livremente, existem inumeras formas de habilitar o A20 sendo uma dela pelo próprio controlador do teclado ou simulando ele, outra forma seria por portas IO sendo ela a porta 0x92, outra forma de habilitar o A20 é por interrupção com o int15, para a gente ativar o A20 pela porta IO basta receber o valor atual da porta 0x92 e alterar o segundo bit e depois enviar novamente para a porta 0x92 o valor

in al,0x92 ;pega o valor atual na porta 0x92or al,2 ;altera o segundo bit 1

Page 99: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

out 0x92,al ;envia novamente para a porta 0x92

para desativar o A20 pela porta IO basta a gente alterar o segundo bit para 0

in al,0x92 ;pega o valor atual na porta 0x92and al,0xfd ;altera o segundo bit para 0out 0x92,al ;envia novamente para a porta 0x92

pela interrupção int 15 podemos ativar o A20 bastando usar a função 0x24 dele e no registrador al colocamos 0x1 para tivar

mov ah,0x24mov al,0x1int 0x15

tambem é possível desativar o A20 setando 0x0 no registrador al

mov ah,0x24mov al,0x0int 0x15

para a gente habilitar o modo protegido temos que ativar uma flag no CR0 sendo ela o primeiro bit do registrador CR0 (PE), para fazer isso atribuimos ele para o registrador eax e depois alteramos o bit com a operação logica or e depois basta enviar novamente para o registrador CR0

mov eax,CR0or al,1mov CR0,eax da mesma forma para a gente desativar o pmode e voltar para modo real basta setar a flag em 0

mov eax,CR0and al,0xfemov CR0,eax

depois de passar para modo protegido a forma de gerenciar o acesso a memoria e a proteção dela é por tabela de descritores sendo uma dessas tabelas o gdt (global descriptor table), para acessar uma determinada tabela de descritor gdt é usado um seletor de indice que é carregado pela instrução lgdt,um seletor de indice é uma tabela que tem como dado o tamanho da tabela de descritor gdt menos 1 (esse tamanho é um valor do tipo word), seguido do endereço dela na memoria (esse valor é um dado do tipo dword), normalmente é usado label para calcular tudo colocando um label no final da tabela gdt e um label no começo da tabela gdt depois subtrair ambos para conseguir o tamanho da tabela e o seu endereço

kodo_gdtr:dw kodo_fim - kodo_inicio - 1 ;tamanhodd kodo_inicio ;endereço

para carregar o seletor usamos a instrução lgdt com o endereço do seletor

Page 100: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

lgdt [kodo_gdtr]

na tabela gdt temos um segmento de 64bits que contem os bits zerados sendo chamado de null descriptor, temos outro segmento de 64bits para o nosso codigo e outro segmento de 64bits para os dados

kodo_inicio:kodo_null:kodo_codigo:kodo_dados:kodo_fim:

como o null descriptor é 64bits com valores nulos podemos alocar dois dados do tipo dword zeradosou apenas um do tipo qword,

kodo_inicio:kodo_null:dq 0x0kodo_codigo:kodo_dados:kodo_fim:

a estrutura do codigo e a dos dados são identicas sendo ambas de 64bits, os 16 primeiros bits indica o limite sendo a parte menos significativa dele (aqui é um dado do tipo word), os 24bits seguintes indica o endereço base sendo a parte menos significativa dele (normalmente é dividido em dois dados um do tipo word e outro do tipo byte), os 8 bits seguinte são flags conhecida como access byte (os 3 primeiros bits dessa flag é o TYPE, o bit seguinte é o DT indica se é um descritor global ou local, temos 3 bits seguintes sendo eles o DPL que seria o nivel de acesso o ring, o ultimo bit indica se tem segmentação), os 8 bits depois do access byte é uma flag conhecida como granularity (os 4 primeiros bits dela é o tamanho de limite, os dois bits seguinte são zero, o próximo bit indica otamano sendo 0 para 16bits e 1 para 32bits, o próximo bit é o bit de granualidade), e por fim temos mais 8 bits que seria a parte mais significativa da base, o segmento dos dados é parecido com o codigo

kodo_inicio:kodo_null:dq 0x0kodo_codigo:dw 0xffff ;limite dw 0x0 ;base lowdb 0x0 ;base middledb 0b10011010 ;access bytedb 0b11001111 ;granularity db 0x0 ;base highkodo_dados:dw 0xff ;limitedw 0x0 ;base lowdb 0x0 ;base middledb 0b10010010 ;access bytedb 0b11001111 ;granularity

Page 101: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

db 0x0 ;base highkodo_fim:

normalmente depois de carregar o gdt é dado um pulo incondicional para modificar o registrador cs,depois é modificado tambem os registradores ds,es,fs,gs e ss, veja um exemplo passando para modoprotegido e exibindo algo na tela

[bits 16]org 0x7c00

cli;habilita o a20in al,0x92or al,2out 0x92,al

;carrega o gdtlgdt [kodo_gdtr]

;passa para modo protegidomov eax,cr0or eax,1mov cr0,eax

jmp 0x8:kodo32

;seletor do descritorkodo_gdtr:dw kodo_fim - kodo_inicio -1dd kodo_inicio

;meu descritorkodo_inicio:kodo_null:dq 0x0 ;nullkodo_codigo:dw 0xffff ;limitedw 0x0 ;basedb 0x0 ;basedb 0b10011010 ;accessdb 0b11001111 ;granunalitydb 0x0 ;basekodo_dados:dw 0xffff ;limitedw 0x0 ;basedb 0x0 ;basedb 0b10010010 ;accessdb 0b11001111 ;granunalitydb 0x0 ;basekodo_fim:

Page 102: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

[bits 32]kodo32:mov eax,0x10mov ds,eaxmov es,eaxmov fs,eaxmov gs,eaxmov ss,eax

;imprime na tela ja em modo protegidomov eax,0xb8000mov [eax],byte "k"mov [eax+1],byte 1mov [eax+2],byte "o"mov [eax+3],byte 1 mov [eax+4],byte "d"mov [eax+5],byte 1 mov [eax+6],byte "o"mov [eax+7],byte 1

jmp $

times 510 - ($-$$) db 0x0dw 0xaa55

bom galera o modo protegido é bem extenso apenas ele seria suficiente para um livro inteiro, sem dizer que eu não sei muito sobre modo protegido para escrever alguma coisa mais extensa que isso. quem sabe futuramente se eu editar esse ebook talvez eu escreva um pouco mais sobre pmode colocando algumas coisas novas (sem dizer que você ira apenas mexer nessa mudança de modo realpara protegido quando for criar algum kernel ou alguma coisa desse tipo, tirando isso já vai estar programando em modo protegido por cima de um sistema operacional especifico)

4.1 – x86: libc

o libc é uma biblioteca que nos permite usar as funções que usamos na linguagem C direto no assembly ou em qualquer outra linguagem, o libc normalmente é usado sobre uma plataforma especifica já em modo protegido, a forma mais simples de usar o libc no assembly é compilar o nosso codigo gerando o arquivo objeto e lincar ele usando o gcc assim sera lincado de forma automatica com o libc. dependendo do sistema ou do tipo de executavel existe um ponto de entrada os famosos entry point, esse ponto de entrada seria onde fica o codigo que sera executado naquele binario, no sistema linux esse entry point aponta para onde esta o label _start na maior parte das vezes, em programas escrito em C no entry point existe uma chamada de função que chama a função principal main onde é sobrescrito com o nosso codigo e quando damos um return no final docodigo em C seria para voltar para ele e o mesmo se encarrega de finalizar o programa, para a genteusar o libc quando lincamos com o gcc sem precisar colocar argumentos a mais temos que colocar ocodigo depois do label main e especificar ele usando a diretiva global no nasm

;nasm -f elf32 kodo.asm -o kodo.o ;gcc kodo.o -o kodo.out

global main

Page 103: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

section .textmain:ret

no gas a diretiva global seria o .globl

;as kodo.asm -o kodo.o;gcc kodo.o -o kodo.out

.globl main

.textmain:ret

na plataforma windows tambem é possível fazer o mesmo porem todo label de função no gcc do windows tem um underline antes

;nasm -f win32 kodo.asm -o kodo.o;gcc kodo.o -o kodo.exe

global _main

section .text_main:ret

o mesmo vale para o gas no windows

.globl _main

.text_main:ret

é recomendado empilhar o ebp atual e armazenar ele para não ter o risco dele se perder e com isso perder a referencia para onde voltar, para isso empilhamos ele e salvamos o stack pointer no base pointer atual, quando terminar basta fazer o oposto movendo o base pointer novamente e retornar

global main

section .textmain:push ebpmov ebp,esp

mov esp,ebppop ebpret

Page 104: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

para a gente usar a função do libc no nasm temos que especificar ela como chamada externa usando a diretiva extern (caso exista mais de uma função externa podemos separar elas por virgula), os argumentos da função no libc deve ser empilhados antes da chamada da função, um exemplo seria afunção exit que finaliza o programa sendo que nessa função passamos como argumento o numero do retorno

global mainextern exit

section .textmain:push ebpmov ebp,esp

push 0x0call exit

mov esp,ebppop ebpret

no windows tambem é possível porem lembrando que o label tem um underline antes (isso é regra não se esqueça)

global _mainextern _exit

section .text_main:push ebpmov ebp,esp

push 0x0call _exit

mov esp,ebppop ebpret

no compilador gas não precisamos especificar a função como externa já que por padrão ele já entende que tal função é do libc

.globl main

.textmain:push %ebpmov %esp,%ebp

push $0x0

Page 105: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call exit

mov %ebp,%esppop %ebpret

quando usamos a função exit para finalizar no codigo não precisamos retornar para o entry point pela logica do nosso codigo, então nesse caso não precisamos armazenar o ebp atual embora seja recomendado

global mainextern exit

section .textmain:push 0x0call exit

podemos usar a função puts para exibir uma string na tela, bastando empilhar a string a ser exibida echamar a função puts (ou _puts no windows)

global mainextern puts, exit

section .textmain:push kodocall puts

push 0x0call exit

section .datakodo db "ola mundo",0x0

tambem podemos exibir quantas strings a gente quiser bastando chamar a função quantas vezes a gente precisar

global mainextern puts, exit

section .textmain:push kodocall puts

push kamicall puts

push 0x0call exit

Page 106: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

section .datakodo db "ola mundo",0x0kami db "hello world",0x0

o mesmo pode ser feito usando a função printf

global mainextern printf, exit

section .textmain:push kodocall printf

push 0x0call exit

section .datakodo db "ola mundo",0x0

com o printf podemos entrar com vários valores diferentes para ser formato por ele, quando passamos mais de um argumento deve ser passado de forma invertida do ultimo argumento ate o primeiro

global mainextern printf, exit

section .textmain:mov eax,[idade]push eaxpush nomepush formatocall printf

push 0x0call exit

section .datanome db "kodo no kami",0x0idade dd 25formato db "nome: %s idade: %d",0x0

o programa anterior em asm seria equivalente ao próximo em c, veja que a ordem que passamos os argumentos em C é invertido diferente do que ocorre em asm

#include <stdio.h>

int main(void){ char nome[] = "kodo no kami";

Page 107: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

int idade = 25; printf("nome: %s idade: %d",nome,idade); exit(0);} outra função onde podemos imprimir um único caracter é o putchar

global mainextern putchar, exit

section .textmain:push 'k'call putchar

push 0x0call exit

podemos entrar com dados digitados pelo usuario com a função gets (viva o buffer overflow em asm tambem kkkk), quando manipulamos dados dinamicos temos que usar a section bss para armazenar eles e não o section .data

global mainextern printf, gets, exit

section .textmain:push kamicall gets

push kamipush kodoformatocall printf

push 0x0call exit

section .datakodoformato db "texto digitado foi %s",0x0

section .bsskami resb 30

com o scanf entramos com dados de valores especificos, o scanf tem dois argumentos o primeiro é oendereço da memoria da variavel e o outro uma string que contem o formato do dado

global mainextern printf, scanf, exit

section .textmain:

Page 108: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push kamipush kodoscancall scanf

mov eax,[kami]push eaxpush kodoformatocall printf

push 0x0call exit

section .datakodoscan db "%d",0x0kodoformato db "numero digitado foi %d",0x0

section .bsskami dd 0

com a função getchar entramos com um único caracter do teclado que sera armazenado no registrador eax

global mainextern putchar, getchar, exit

section .textmain:call getchar

push eaxcall putchar

push 0x0call exit

é possível formatar uma nova string usando entradas de tipos diferentes com a função sprintf, nela passamos como empilhamos os valores, depois uma string de formatação como o printf e por fim a variavel onde vamos armazenar

global mainextern printf, sprintf, exit

section .textmain:mov eax,[kodonum]push eaxmov eax,[kodonum2]push eaxpush kodofmtpush kamicall sprintf

Page 109: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push kamicall printf

push 0x0call exit

section .datakodonum dd 315kodonum2 dd 100kodofmt db "primeiro %d - segundo %d ",0x0

section .bsskami resb 100

com a função strstr podemos encontrar um determinado trecho em uma string, o endereço desse trecho sera retornado no registrador eax, os argumentos passado é a string que vamos buscar, e a string onde estamos buscando

global mainextern printf, strstr, exit

section .textmain:push kodobuscapush kodotextocall strstr

push eaxcall printf

push 0x0call exit

section .datakodobusca db "programador",0x0kodotexto db "era uma vez um programador fim da historia",0x0

para copiar uma string de uma variavel para outra variavel podemos usar a função strcpy, passamos como argumento a variavel a ser copiada seguido do endereço para onde vamos copiar

global mainextern printf, strcpy, exit

section .textmain:push kodopush kamicall strcpy

push kami

Page 110: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call printf

push 0x0call exit

section .datakodo db "Ponpon way way way",0x0

section .bsskami resb 100

podemos pegar o tamanho de uma string usando a função strlen bastamdo empilhar o endereço dela,o retorno dela sera no registrador eax sendo ele o tamanho da string

global mainextern printf, strlen, exit

section .textmain:push kodotexto call strlen

push eaxpush kodofmtcall printf

push 0x0call exit

section .datakodofmt db "tamanho %d",0x0kodotexto db "cade minhas waifu",0x0

para comparar duas strings usamos a função strcmp, empilhamos o endereço das duas strings a ser comparadas caso ambas as strings sejam iguais o retorno sera 0 no registrador eax

global mainextern printf, strcmp, exit

section .textmain:push kodo1push kodo2call strcmp

cmp eax,0jz igual

push textodiferentecall printfjmp sair

Page 111: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

igual:push textoigualcall printf

sair:push 0x0call exit

section .datakodo1 db "eofclub",0x0kodo2 db "eofclub",0x0textoigual db "igual",0x0textodiferente db "diferente",0x0

para a gente pegar a hora em segundos usamos a função time sera armazenado no registrador eax o segundo deis de 1/1/1970, empilhamos como argumento para ele o valor nulo

global mainextern printf, time, exit

section .textmain:push 0x0call time

push eaxpush formatocall printf

push 0x0call exit

section .dataformato db "%d ",0x0

outra forma de usar o time seria passar como argumento para ele o endereço onde vamos armazenar

global mainextern printf, time, localtime, exit

section .textmain:push kodocall time

mov eax,[kodo]push eaxpush formatocall printf

Page 112: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push 0x0call exit

section .dataformato db "%d ",0x0

section .bsskodo resd 1

podemos converter o tempo do time para o tempo atual em segundos, minutos, horas, dias e anos usando a função localtime, nela passamos o endereço onde armazenamos o tempo do time, com issoele vai nos retornar um endereço no registrador eax onde podemos acessar de forma indexada para cada valor (esse seria o trabalho da estrutura tm na linguagem C u.u )

global mainextern printf, time, localtime, exit

section .textmain:push kodocall time

push kodocall localtime

mov ebx,[eax] ;segundopush ebxmov ebx,[eax + 4] ;minutopush ebxmov ebx,[eax + 8] ;horapush ebxpush formatocall printf

push 0x0call exit

section .dataformato db "%d:%d:%d ",0x0

section .bsskodo resd 1

com a função sleep damos um delay no programa, o seu argumento seria a quantidade de segundos (sleep no windows deve ser passado o tempo em milesimos e não em segundos)

global mainextern printf, sleep, exit

section .textmain:

Page 113: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push 5call sleep

push kodocall printf

push 0x0call exit

section .datakodo db "volta ao trampo vagabundo ",0x0

podemos manipular um arquivo usando a função fopen para abrir ele, nessa função passamos uma string como argumento co tipo de acesso se é leitura (r), escrita (w) ou concatenação (a). depois passamos outra string com o local do arquivo, o retorno dessa funçao sera o descritor do arquivo no registrador eax

global mainextern fopen, exit

section .textmain:push permpush arqcall fopen

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0

tambem podemos usar a função fclose para fechar o arquivo (isso deve ser feito para salvar a alteração feita nele ou para ele ser lido por outro programa)

global mainextern fopen, fclose, exit

section .textmain:push permpush arqcall fopen

push eaxcall fclose

push 0x0call exit

Page 114: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

section .dataarq db "kodo.txt",0x0perm db "r",0x0

para a gente ler um único byte no arquivo usamos a função fgetc, temos que passar como argumentoo descritor do arquivo que vamos ler sendo que sera armazenado no registrador eax conforme a gente for usando a função fgetc sera lido o arquivo byte a byte (esse seria melhor forma para se ler um arquivo do tipo binario)

global mainextern fopen, fclose, fgetc, putchar, exit

section .textmain:push permpush arqcall fopenmov [descritor],eax

push eaxcall fgetc

push eaxcall putchar

mov eax,[descritor]push eaxcall fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0

section .bssdescritor resd 1

a função fgets permite a gente ler o arquivo linha por linha, para isso passamos como argumento o descritor do arquivo, o tamanho de uma variavel, e uma variavel onde sera armazenado aquela linha, conforme a gente usar o fgets sera lido as próximas linhas

global mainextern fopen, fclose, fgets, printf, exit

section .textmain:push permpush arq

Page 115: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call fopenmov [descritor],eax

push eaxpush 100push linhacall fgets

push linhacall printf

mov eax,[descritor]push eaxcall fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0

section .bssdescritor resd 1linha resb 100

com a função fread a gente ler uma quantidade especifica de bytes no arquivo (seria uma leitura por bloco de bytes), o seu argumento é o descritor do arquivo a ser lido, a quantidade de blocos a ser lido, o tamanho do bloco a ser lido, e a variavel onde vamos armazenar

global mainextern fopen, fclose, fread, printf, exit

section .textmain:push permpush arqcall fopenmov [descritor],eax

push eaxpush 1push 100push linhacall fread

push linhacall printf

mov eax,[descritor]push eax

Page 116: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0

section .bssdescritor resd 1linha resb 100

para a gente escrever alguma coisa no arquivo temos que abrir o arquivo com permissão de escrita (w) com isso ira sobrescrever o arquivo caso ele contenha alguma coisa dentro ou ira criar um arquivo novo caso não exista, ou a permissão para concatenar (a) que ira armazenar no final do arquivo, para escrever usamos a função fprintf sendo o seu funcionamento paracido com printf com o diferencial que temos que passar o descritor do arquivo como ultimo argumento (tambem é possível formatar com outros formatos como o printf sendo o penultimo argumento a string de formatação e os anteriores os valores)

global mainextern fopen, fclose, fprintf, exit

section .textmain:push permpush arqcall fopenmov [descritor],eax

push textopush eaxcall fprintf

mov eax,[descritor]push eaxcall fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "w",0x0texto db "isso sera salvo no arquivo",0x0

section .bssdescritor resd 1

com a função ftell pegamos a posição atual do descritor naquele arquivo, seu argumento é o

Page 117: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

descritor do arquivo e seu retorno é a posição no registrador eax

global mainextern fopen, fclose, ftell, printf, exit

section .textmain:push permpush arqcall fopenmov [descritor],eax

push eaxcall ftell

push eaxpush formatocall printf

mov eax,[descritor]push eaxcall fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0formato db "%d ",0x0

section .bssdescritor resd 1

com a função fseek pulamos para uma parte especifica do arquivo, seu argumento seria a origem da onde estamos lendo o arquivo se é do começo do arquivo (0), da posição atual (1) ou do final do arquivo (2). o segundo argumento seria o offset apartir daquela origem, o ultimo argumento seria o descritor do arquivo

global mainextern fopen, fclose, fseek, exit

section .textmain:push permpush arqcall fopenmov [descritor],eax

push 0 ;do começo do arquivopush 20 ;pula para o offset 20

Page 118: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

push eaxcall fseek

mov eax,[descritor]push eaxcall fclose

push 0x0call exit

section .dataarq db "kodo.txt",0x0perm db "r",0x0

com a função getenv pegamos as variaveis de ambiente

global mainextern printf, getenv, exit

section .textmain:push kodocall getenv

push eaxcall printf

push 0x0call exit

section .datakodo db "PATH"

podemos executar algum comando do terminal usando a função system (eu ja fiz um compilador para batch/bash usando o system em asm e c, fico mo gambiarra esse trem kkkk)

global mainextern system, exit

section .textmain:push cmdcall system

push 0x0call exit

section .datacmd db "cat /etc/passwd",0x0

4.2 – x86: asm web (cgi)

Page 119: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

ate parece brincadeira a linguagem assembly tambem da para ser usada para construir sites e sistemas webs semelhante ao PHP, para usar o asm em um servidor web o próprio servidor deve aceitar CGI, no apache existem modulos como fastcgi que permite executar cgi em diversas liguagens no servidor web seja elas linguagens de scripts como python e perl ou seja programas compilados feito em C, Pascal, Assembly ou qualquer outra linguagem existente. Para um CGI funcionar a primeira saida dele deve ser o cabeçalho sendo o argumento principal e o obrigatorio nocabeçalho o “Content-Type: text/html”, depois temos duas quebras de linhas indicando o fim do cabeçalho e a saida com o nosso html (dependendo de como você configurou o apache ou servidor web o executavel deve esta com a extensão .cgi com permissão de execução e as vezes limitado em um diretorio especifico daquele servidor web)

global mainextern printf

section .textmain:push ebpmov ebp,esp

push cabecalhocall printf

push htmlcall printf

mov esp,ebppop ebpret

section .datacabecalho db "Content-Type: text/html",0xd,0xa,0xd,0xa,0x0html db "<html><body>by kodo no kami</body></html>",0x0

outro exemplo de uma pagina mais dinamica que exibe a hora atual quando o usuario entra nela

global mainextern printf, time, localtime

section .textmain:push ebpmov ebp,esp

push cabecalhocall printf

push horacall time

push hora

Page 120: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

call localtime

mov ebx,[eax]push ebxmov ebx,[eax + 4]push ebxmov ebx,[eax + 8]push ebxpush htmlcall printf

mov esp,ebppop ebpret

section .datacabecalho db "Content-Type: text/html",0xd,0xa,0xd,0xa,0x0html db "<html><body><center><h1>hora %d:%d:%d</h1></center></body></html>",0x0

section .bsshora resd 1

podemos usar a função getenv para ler os parametros passados por formularios para o cgi com QUERY_STRINGS, variaveis de ambiente do servidor web como o IP do cliente que conecto no site (REMOTE_ADDR), ler os cookies (HTTP_COOKIE) e etc

global mainextern printf, getenv

section .textmain:push ebpmov ebp,esp

push cabecalhocall printf

push parametrocall getenv

push eaxpush htmlcall printf

mov esp,ebppop ebpret

section .datacabecalho db "Content-Type: text/html",0xd,0xa,0xd,0xa,0x0html db "<html><body><center>%s</center></body></html>",0x0

Page 121: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

parametro db "QUERY_STRING",0x0

no cabeçalho podemos adicionar um cookie com parametro Set-Cookie

global mainextern printf

section .textmain:push ebpmov ebp,esp

push cabecalhocall printf

push htmlcall printf

mov esp,ebppop ebpret

section .datacabecalho db "Content-Type: text/html",0xd,0xa,"Set-Cookie: kodo=315",0xd,0xa,0xd,0xa,0x0html db "<html><body><center>cookie criado</center></body></html>",0x0

4.3 – x86: asm inline

muitas linguagens como tambem muitos compiladores permite inserir codigos assembly diretamente naquela linguagem, em programas em C/C++ usando o compilador gcc podemos usar afunção asm para adicionar um codigo em assembly (o gcc usa a sintaxe at&t)

#include <stdio.h>

int main(void){ asm("nop");}

usamos a função sempre quando a gente precisar adicionar uma instrução assembly

#include <stdio.h>

int main(void){ asm("nop"); asm("nop");}

outra forma seria adicionar a instrução na mesma string separando eles por quebra de linha (tambempode ser separado por ponto e virgula)

Page 122: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

#include <stdio.h>

int main(void){ asm("nop\nnop");}

podemos colocar em strings seperadas para melhorar

#include <stdio.h>

int main(void){ asm( "nop \n" "nop \n" );}

no compilador gcc é usado a sintaxe at&t

#include <stdio.h>

int main(void){ asm( "mov $315, %eax \n" "mov $100, %ebx \n" "add %ebx, %eax \n" );}

podemos chamar as funções diretamente em asm inline tambem

#include <stdio.h>

void eofebookasm(){ printf("by kodo no kami");}

int main(void){ asm( "call eofebookasm \n" );}

no sistema windows as chamadas de funções via asm deve conter um underline no nome

#include <stdio.h>

void eofebookasm(){ printf("by kodo no kami");}

Page 123: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

int main(void){ asm( "call _eofebookasm \n" );}

tambem podemos criar elas em asm inline e chamar em c (é recomendado criar um prototipo dela senão o compilador pode acusar um warning dizendo que ela esta sendo chamada de forma implicita)

#include <stdio.h>

int kodosomar(int,int); //prototipo dela

int main(void){ int x;

asm ("jmp pulo \n"); //isso aqui pula ela evitando executar o ret e buga o trem ^^ asm ( "kodosomar:" "push %ebp \n" "mov %esp,%ebp \n" "mov 8(%ebp),%eax \n" "mov 12(%ebp),%edx \n" "add %edx,%eax \n" "mov %ebp,%esp \n" "pop %ebp \n" "ret \n" ); asm("pulo: \n"); x = kodosomar(300,100); //chamando ela via C printf("%d",x);}

para a gente sair com o valor de um registrador para uma variavel, temos que especificar ele no final da string depois de dois pontos, passamos uma string com o simbolo de igual seguido do registrador que vamos atribuir para aquela determinada variavel e entre parênteses a variavel que vai receber o valor

#include <stdio.h>

int main(void){ int kodo; asm( "nop \n" : "=a"(kodo) //atribui a variavel x o valor do registrador eax );}

quando a gente atribui a uma variavel dessa forma temos que especificar os registradores dentro da

Page 124: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

função asm com dois porcentagem e não apenas um

#include <stdio.h>

int main(void){ int kodo; asm( "mov $115, %%eax \n" "mov $200, %%ebx \n" "add %%ebx, %%eax \n" : "=a"(kodo) ); printf("%d", kodo);}

para atribuir em mais de uma variavel separamos por virgula

#include <stdio.h>

int main(void){ int kodo, kami, fts315; asm( "mov $115, %%eax \n" "mov $200, %%ebx \n" "mov $500, %%ecx \n" : "=a"(kodo), "=b"(kami), "=c"(fts315) ); printf("%d %d %d", kodo,kami,fts315);}

para a gente entrar com um valor de uma variavel para um registrador basta colocar outro dois ponto depois daquele, sendo o primeiro dois ponto especifico para saida dos valores e o outro para aentrada dos valores, as entradas para os registradores são atribuidas no começo da chamada da função asm antes de executar qualquer instrução asm, por outro lado a saida sera atribuido no final depois do ultimo codigo asm ser executado

#include <stdio.h>

int main(void){ int kodo, kami, fts315; asm( "nop \n" //codigo asm : //saida : //entrada ); printf("%d %d %d", kodo,kami,fts315);}

na entrada do valor para um registrador basta a gente coloca a letra do registrador como string e entre parênteses especificamos a variavel ou o valor que vamos passar para ele

Page 125: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

#include <stdio.h>

int main(void){ int kodo = 2000; asm( "nop \n" : : "a"(kodo) //atribui 2000 ao registrador eax );}

da mesma forma podemos entrar com vários valores e manipular no asm e depois sair com eles

#include <stdio.h>

int main(void){ int kodo = 2000, kami = 500, resu; asm( "add %%ebx,%%eax \n" : "=a"(resu) : "a"(kodo), "b"(kami) ); printf("%d ",resu);}

alem do registrador podemos armazenar um valor para a memoria stack usando a letra “m”, para acessar o valor entrado usamos o porcentagem seguido da posição

#include <stdio.h>

int main(void){ int kodo = 315; asm( : "mov %0,%%eax \n" : : "m"(kodo) );}

tambem podemos ler ou modificar o valor diretamente na memoria pelo endereço da variavel

#include <stdio.h>

int main(void){ int kodo; asm( "mov $15,%%ebx \n" //move 15 ao ebx "mov %%ebx,(%%eax) \n" //joga o valor do ebx no endereço de memoria da variavel : : "a"(&kodo) //entramos com o endereço de memoria da variavel para eax );

Page 126: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

printf("%d ",kodo);}

podemos executar os opcodes em uma string byte a byte como os shellcodes dos exploits, para isso a string deve ser uma constante e deve ser global

#include <stdio.h>

//mov eax,0x1; mov ebx,0x0; int 0x80const char kodo[] = "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80";

int main(void){ asm("jmp kodo");}

4.4 – x86: disassembler

quando compilamos algum codigo em alguma linguagem é gerado o nosso executavel sendo ele o nosso binario, uma vez que o codigo fonte naquela linguagem de programação é compilado e gerado o executavel, é dificil conseguir aquele mesmo codigo fonte original pelo próprio executaveljá compilado, embora seja possível usando a engenharia reversa para decompilar o executavel e retornar o codigo fonte daquele programa naquela linguagem. a engenharia reversa não traz o codigo fonte original e sim o mais próximo a ele, sendo que em muitas vezes a diferença entre o codigo fonte original e o decompilado é gritante. Uma vez que o codigo fonte é compilado ele é convertido para aquela instrução naquele arquitetura para que o processador possa entender o que deve ser executado ou seja é convertido para codigo na linguagem da maquina (codigo assembler), com isso podemos refazer o codigo fonte apenas vendo o padrão que foi construido naquele binario.Vamos supor que a gente compile o seguinte codigo em um compilador ficticio de pascal

program kodo;begin writeln(‘exemplo decompile’); writeln(‘by kodo no kami’); exit(0);end.

supondo que o exemplo anterior nos gera o seguinte binario depois de compilado (sendo esse um binario direto para arquitetura x86 aberto em editor um hex)

00000000 68 11 00 E8 2B 00 68 22 00 E8 25 00 h...+.h"..%.0000000C 6A 00 E8 21 00 65 78 65 6D 70 6C 6F j..!.exemplo00000018 20 64 65 63 6F 6D 70 69 6C 65 62 79 decompileby00000024 20 6B 6F 64 6F 20 6E 6F 20 6B 61 6D kodo no kam00000030 69 i

podemos pegar cada byte ou uma sequencia de bytes do nosso binario ficticio e os converter para uma instrução valida da arquitetura x86, com base nos opcodes do binario podemos ver as mesmas instruções que sera executadas pelo processador em codigo assembly, da mesma forma podemos descobrir qual função foi usada se a gente conhecer como aquele determinado compilador gera aquele binario (qual endereço de memoria que esta armazenado a função, endereços das variaveis, aestrutura do binario e etc)

Page 127: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

00000000 681100 push word 0x11 ;endereço do ‘exemplo decompile’00000003 E82B00 call word 0x31 ;chadama da função writeln00000006 682200 push word 0x22 ;endereço do ‘by kodo no kami’00000009 E82500 call word 0x31 ;chamada da função writeln0000000C 6A00 push byte +0x0 ;passagem do valor 00000000E E82100 call word 0x32 ;chamada da função exit...

o interessente que usando um editor hexadecimal podemos hackear esse programa ou qualquer outro fazendo ele executar qualquer coisa que a gente quiser apenas modificando os bytes no binario. no exemplo anterior se a gente modificar os bytes “68 11 00” no começo do binario para os bytes “68 22 00”, o meu programa vai exibir duas vezes a string “by kodo no kami”. Em teoria os cracks de programas são feitos dessa forma alterando o fluxo original do programa e burlando ele

00000000 68 22 00 E8 2B 00 68 22 00 E8 25 00 h"..+.h"..%.0000000C 6A 00 E8 21 00 65 78 65 6D 70 6C 6F j..!.exemplo…

um disasembly legal para dissecar binarios puros para arquitetura x86 é o ndisasm (o problema é o alinhamento um byte na posição errada pode desencadear uma sequencia de bytes na posição erradagerando instruções diferente)

kodonokami@debian:~$ ndisasm kodo.bin 00000000 681100 push word 0x1100000003 E82B00 call word 0x3100000006 682200 push word 0x2200000009 E82500 call word 0x31...

outro disassembly para arquitetura x86 é o gnu objdump sendo uma das ferramentas do pacote binutils (tambem existe objdump para outras arquitetura entre elas a mips, arm, avr entre outras), o objdump é um bom dissecador para executaveis de sistemas operacionais como windows (pe) e linux (elf) já que identifica bem os labels, funções e section dependendo do compilador

kodonokami@debian:~$ objdump -d kodo.out

kodo.out: file format elf32-i386

Disassembly of section .text:

08048060 <_start>: 8048060: b8 01 00 00 00 mov $0x1,%eax 8048065: bb 00 00 00 00 mov $0x0,%ebx 804806a: cd 80 int $0x80

alem dos dissembly tambem existem os debuggers. a diferença entre o disassembly e um debugger que o debugador roda o programa e o desmonta em tempo de execução, podendo colocar alguns breakpoint em determinados ponto do codigo e com isso parar a sua execução e analisar tanto a memoria quanto os registradores afetados naquele breakpoint, boa parte dos debugadores tambem podem ser usado para dissecar o programa sem precisar rodar ele igual os dissembly, um debugger

Page 128: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

que eu recomendo aprender a mexer é o gdb (gnu debbuger), para a gente usar o gdb basta digita o comando gdb seguido do nome do programa, com isso ira abrir o gdb já com o programa especifico que vamos debugar embora parado sem esta em execução

kodonokami@debian:~$ gdb kodo.out(gdb)

podemos digita apenas gdb para abrir o gdb, depois dentro dele podemos usar o comando target exec seguido do nome do programa para especificar o programa que vamos debugar

kodonokami@debian:~$ gdb(gdb) target exec kodo.out

o gdb tambem permite debugar programas já em execução especificando -p seguido do pid do processo, quando o gdb abre o processo ele é parado naquele ponto

kodonokami@debian:~$ gdb -p 315(gdb)

ou podemos abrir o gdb e usar o comando attach seguido do pid

kodonokami@debian:~$ gdb(gdb) attach 315

depois de aberto podemos executar ou restarta o programa usando o comando run, se o programa termina a execução podemos usar esse comando para executar ele novamente

kodonokami@debian:~$ gdb kodo.out(gdb) run

caso você tenha aberto o programa já em execução direto pelo pid, você pode usar o comando cont para continuar a sua execução já que estara pausado

kodonokami@debian:~$ gdb -p 315(gdb) cont

com o disass seguido do nome da função podemos dissecar o executavel naquela função especifica

kodonokami@debian:~$ gdb kodo.out(gdb) disass *mainDump of assembler code for function main: 0x80000620 <+0>: lea 0x4(%esp),%ecx 0x80000624 <+4>: and $0xfffffff0,%esp 0x80000627 <+7>: pushl -0x4(%ecx) 0x8000062a <+10>: push %ebp 0x8000062b <+11>: mov %esp,%ebp...

podemos colocar um breakpoint usando o comando break seguido do endereço da onde vamos

Page 129: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

colocar o breakpoint

kodonokami@debian:~$ gdb kodo.out(gdb) break *0x8000066c

outra forma de colocar um breakpoint é pelo nome da função

kodonokami@debian:~$ gdb kodo.out(gdb) break *printf

tambem podemos atribuir um valor ao nome da função para ele parar a partir daquele endereço incrementado com o valor

kodonokami@debian:~$ gdb kodo.out(gdb) break *printf+7

se a gente rodar o programa ou continar a sua execução (caso esteja parado), ele ira parar no breakpoint voltando para o gdb

(gdb) break *0x8000066c(gdb) run

para continuar a sua execução usamos o comando cont

(gdb) break *0x8000066c(gdb) runBreakpoint 1, 0x8000066c in main ()(gdb) cont

podemos exibir os breakpoint usando o comando info break

(gdb) info breakNum Type Disp Enb Address What1 breakpoint keep y 0x8000066c <main+76>

para remover um breakpoint com o comando delete break seguido do Num dele (se não digitar o Num sera removido todos)

(gdb) delete break 1

com o comando disable desativamos um breakpoint

(gdb) disable break 1

tambem podemos ativar ele novamente com o comando enable

(gdb) enable break 1

para a gente exibir os registradores e os valores atuais deles usamos o comando “info registers”

Page 130: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

(gdb) info registerseax 0xbffff55c -1073744548ecx 0xfbad2288 -72539512edx 0xb7fae87c -1208293252...

o comando anterior só ira exibir os registradores principais, podemos utilizar o “info all-registers” para exibir todos os registradores incluido os de fpu, mmx e sse

(gdb) info all-registers eax 0xbffff55c -1073744548ecx 0xfbad2288 -72539512edx 0xb7fae87c -1208293252...

podemos exibir um único registrador usando o comando print seguido do registrador

(gdb) print $eip$1 = (void (*)()) 0x8000066c <main+76>

tambem podemos exibir um endereço de memoria com o print

(gdb) print *0x8000073d$1 = 1768384868

para exibir o valor em hex usamos o simbolo de barra seguido do x depois do print

(gdb) print/x *0x8000073d$1 = 0x69676964

outro comando que podemos usar para exibir um determinado endereço é o x

(gdb) x 0x8000073d0x8000073d: 0x69676964

quando é uma string podemos usar o /s para exibir ela

(gdb) x/s 0x8000073d0x8000073d: "digite seu nome: "

podemos exibir todas as funções usadas e seus endereços com o comando “info functions”

(gdb) info functionsAll defined functions:

Non-debugging symbols:0x8000041c _init0x80000450 strcmp@plt

Page 131: END OF FILE – EBOOK ASM X86 - · PDF filearquiteturas padroniza aquele determinado modelo de máquina para que todas as demais máquinas que segue a mesma arquitetura seja fisicamente

0x80000460 printf@plt0x80000470 gets@plt0x80000480 puts@plt

quando o programa esta rodando poidemos utilizar o comando “info proc” para exibir as informações do processo do programa em execuçao

(gdb) info proc process 19793cmdline = '/home/kodonokami/Desktop/kodo.out'cwd = '/home/kodonokami/Desktop'exe = '/home/kodonokami/Desktop/kodo.out'

com o “info threads” exibimos informações das threads

(gdb) info threads Id Target Id Frame * 1 process 19793 "kodo.out" 0xb7fd8d40 in __kernel_vsyscall ()

para a gente exibir informações das bibliotecas compartilhadas como as DLL do windows ou SO dolinux usamos o comando “info sharedlibrary”

(gdb) info sharedlibrary From To Syms Read Shared Object Library0xb7fdb860 0xb7ff46bd Yes (*) /lib/ld-linux.so.20xb7e107f0 0xb7f3d59f Yes (*) /lib/i386-linux-gnu/libc.so.6(*): Shared library is missing debugging information.

5.0 – EOF

bom galera aqui termina mais um ebook, futuramente esse ebook pode acabar sofrendo algumas modificações para sua correção e para adição de novos conteudos. e como sempre não posso deixar de agradecer aos membros do grupo eof como o s1m0n sempre postando bons conteudos la, o hefest0 que manja pra caramba de asm tambem (seu nick antigo era …. kkk), o sir.rafiki aquele viciado em java que não para de jogar dota (joga terraria mano bem melhor), neofito que sempre merecomenda algum anime, o nosso coder de malware o 0x4xh4x, a melots uma amiga que considero muito, angley e jhonydk os manos viciados em games do grupo (jogar terraria ninguém quer ne u.u), refell que ate hoje dificilmente vejo ele online quando estou online ‘-’, lend4pop que aparece no discord as vezes e depois sai na mesma hora kkk, o 0racle e fascist que estão perdidos nos confins do irc, o mano gjunnior e os tuto de arch linux, os membros mais novos v4p0r, hchild e onezer0. tambem não posso deixar citar alguns grupos embora não seja possível citar todos membros como os do fabrica de noobs, brasil blackhat, gamemods, caveira tech, gray hat (face), entre outros.