Olá a todos...
Bom...antes de começarmos com o nosso "breve" tutorial de assembly, preciso fazer algumas considerações. Assembly é uma linguagem de baixo nível (só lembrando: quanto mais ALTO for o nível de uma linguagem, mais perto da escrita humana, portanto, mais FÁCIL de entende-la e utiliza-la). Portanto, talvez você tenha que ler várias vezes esse tópico pra entender. Mas acreditem, Assembly é MUITO útil.
Outra coisa que gostaria de esclarecer é que algumas pessoas escrevem (e falam) o nome da linguagem de maneira errada, o nome da linguagem é ASSEMBLY, muitas pessoas a chamam de Assembler. Assembler é o "compilador" e não a linguagem.
>>>Conhecendo a linguagem<<<
Assembly é uma linguagem de programação extremamente poderosa e relativamente de difícil implementação, pois seus comandos e suas estruturas são bem mais próximos da linguagem computacional. Raramente encontramos programas puramente feitos em Assembly, uma vez que tal linguagem exige tempo e muito raciocínio. Geralmente essa poderosa linguagem é implementada junto a alguma outra, funciona como auxiliar. Por exemplo, em programas que exigem uma interação mais profunda com o hardware de uma máquina, podemos implementar códigos Assembly junto ao código em C, C++ ou qualquer que seja a linguagem utilizada. Outro exemplo prático do uso de Assembly são os famosos ShellCodes presentes nos exploits. E ainda, como último exemplo, temos os nossos amigos “Reversers” (popularmente chamados de “crackers”, pois crackeiam programas) que usam muito o Assembly. Um bom “reverser” (“cracker”) sabe profundamente Assembly
Todos os processadores possuem uma "linguagem" própria embutida em cada um deles e cada instrução (comando) dessa linguagem produz um efeito diferente no processador, através de seus registradores. Essas instruções são representadas por números como, por exemplo, a famosa instrução MOV DL é representada pelo número B2 em hexadecimal (178 em decimal). Esses números, em Assembly, são trasnformados nos chamados mnemônicos para uma maior facilidade. Por exemplo, ao invés de você escrever B2 e ter de decorar o que é cada número, você vai escrever o mnemônico dele que, nesse caso, é o MOV DL. Onde MOV significa MOVer e DL é o chamado byte baixo de um outro registrador (DX).
"Meu Deus! Fiquei espantado com essa introdução!!! Assembly é difícil demais!!"
Tenha calma...Assembly não é aquele bicho de sete cabeças como todos dizem(e como parece ser também)...no começo parece que você não vai entender nada, mas depois as coisas começam clarear e você vai entendendo cada vez mais, até que começa a realmente utilizá-lo em seus projetos.
Bom...vamos em frente...temos bastante chão ainda
>>> Registradores <<<
Quem já deu uma fuçada nessa linguagem de programação, com certeza ouviu falar dos famosos Registradores...
Mas, Black-Hat, o que são os tais registradores??
Bom...uma das maneiras mais fáceis de explicar um registrador é compará-lo a uma variável, eles são como "espacinhos" reservados dentro do processador para guardar os códigos operacionais (MOV, JMP, NOP...), são uma espécie de "memória" temporária do processador.
Existem vários registradores, deles, os mais usados são 8, que são denominados Registradores de Uso Geral. Mas desses 8, os que realmente se utiliza bastante são apenas 4, são eles: AX, BX, CX e DX.
Um registrador armazena 16 bits, o que é correspondente a 2 bytes. Nas máquinas de 32 bits, temos o EAX, o EBX, o ECX e o EDX que são registradores que podem guardar 32 bits (segundo a matemática avançada, o equivalente a 4 bytes, pois 32/8 = 4 uahSIUHauhs).
Os nomes dos registradores sugerem pra que servem, o AX é o Acumulador, o BX é a Base, o CX é o Contador e o DX possui um D que deriva de Dados. Esses nomes surgiram devido ao uso mais comum deles, o AX é mais utilizado como acumulador, BX como base e etc...
"Aff Black-Hat, não aguento mais ler..." ... Calma, tá acabando (a parte de registradores ;P)
Bom...como eu havia dito, os registradores armazenam 2 bytes, que são chamados de Byte Alto e Byte Baixo, cada registrador possui o seu BA e o seu BB. Se quisermos acessar o byte alto do registrador AX, por exemplo, vamos utilizar o AH e observem que, mais uma vez o nome não está aí a toa...Alto em inglês é High e por isso AH. Se quisermos acessar o byte baixo de AX, utilizamos o AL (baixo em inglês é Low).
Pronto!! Agora SÓ falta vermos as instruções, as variáveis, saltos...e mais umas coisinhas
Prepare-se psicologicamente, vá beber uma água ou namorar um pouco...porque agora começa a parte mais importante (e mais legal tbm xD).
Vamos falar sobre as instruções mais utilizadas: a MOV e a INT.
>>> A Instrução MOV <<<
A instrução MOV, uma das mais utilizadas, deriva da palavra MOVe, então, já da pra saber o que ela faz, né? Ela move dados de um lugar para outro(de um registrador para outro, por exemplo) ou insere dados nos registradores. Alguns exemplos de utilização da nossa querida instrução MOV:
Simples... =)
>>> A Instrução INT <<<
A instrução INT chama uma interrupção de software(calma, não se desespere com o nome...vc não vai precisar desse nome).
Tá, mas, pra que serve isso afinal??
O DOS e a BIOS do seu computador e de todos têm várias funções interessantes que podemos acessar com interrupções de software.
Por exemplo:
Agora um programa BEMMMM básico(não chega nem a ser um Hello World):
O que esse programa faz? Bom...ele simplesmente termina (não, eu não estou bebado...ele nao faz nada alem de terminar)
Bom pessoal...vocês devem estar se perguntando porque eu chamei a interrupção 21 e não a 55, por exemplo. Bom...pra explicar isso vou precisar da ajuda de um "livrinho virtual", uma referência, que se chama HelpPC...
Vou deixar o link de download pra vcs baixarem, ok? (não vou anexar porque o limite maximo de anexo Zip é menor do que o tamanho do arquivo)
Assim podemos continuar...
Link: Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar...
Continuando....
Ao abrirmos o HelpPC, caimos em uma tela inicial, chamada de Main Topic Menu. Vá até o item "Interrupt Services DOS-BIOS-EMS-Mouse" e de um enter nele. Agora vá em "DOS Functions" e aperte enter novamente. Procure pela função que utilizamos acima, a "INT 21,4C".
Bom...agora estamos na tela que nos mostra informações detalhadas sobre tal função, podemos observar que essa função recebe o nome de "Terminate Process With Return Code", traduzindo: Terminar Processo Com Código de Retorno. Olhando melhor na tela, percebemos que o HelpPC nos indica que o byte alto de AX deve ter o valor 4C e que o valor baixo deve ter o código de retorno do nosso programa. Quem programa em C/C++ já sabe o que é o código de retorno do programa...é um código, um número, que o programa retorna ao ser finalizado. Observamos também qeu a função 21 não retorna nenhum valor (o que seria chamado de rotina em outras linguagens).
Resumindo...para usar essa função, basta você colocar os valores que são exijidos no registrador AX e depois disso é só chamar uma interrupção de software 21 (INT 0x21).
Caso você esteja super curioso pra ver o nosso "programa" funcionar, baixe o NASM e compile-o, como? simples...
Salve o código fonte dele como Terminator.asm no bloco de notas ou com qualquer outro nome que queira...
Depois vá no DOS e digite:
nasm -o Terminator.com Terminator.asm
e então você obterá o seu super programa compilado no arquivo Terminator.com =)
Tá...vamos fazer um programa um pouco melhorzinho...ele vai escrever um caractere na tela...
Se procurarmos no HelpPC a função que escreve um caractere na tela vamos achar a INT 21,2.
Observem que precisamos do valor 02 no byte alto de AX (AH) e precisamos que o caractere a ser mostrado esteja no byte baixo de DX (DL). Pronto! Agora é só fazer isso...Let's Go
Bom...nosso programa precisa terminar ... basta adicionar o código do programa anterior...Ficando assim:
Agora é só compilar e boa! =)
Obs.: Caso você tenha algum problema com a compilação, retire os comentários do código-fonte.
Bom...Chegamos na metade!!! UHULLL
uahSUHauihsuihs
Sim...o tópico está enorme e cansativo, mas vc quer aprender? vc tem que ler né?? ;P
Vamos em frente...
Até aqui foi fácil né? Que bom...vamos "engrossar o caldo" agora xD
>>> As Variáveis <<<
Bom...o conceito de variável em assembly é o mesmo das outras linguagens de programação, com pouca diferença, então acho que não teremos problemas com elas. Em Assembly, os dados são inseridos diretamente no programa e são rotulados com um nome que você quiser (semelhante às outras linguagens xD). Os tipos de variáveis são:
Byte(B) = 8 bits = 1 byte
Word(W) = 16 bits = 2 bytes
DWord(D) = 32 bits = 4 bytes
Agora vamos ver como "declarar" as variáveis. Iremos utilizar DB, DW e DD (Byte, Word e Double Word)
Alguns exemplos:
Não tem segredo
Agora vamos ver como manipular nossas variáveis:
Quando quisermos pegar o valor das variáveis, tiramos os colchetes, assim:
--Vamos escrever uma string na tela--
Essa parte parece perda de tempo, mas não é, ela será essencial para a próxima parte....portanto, vamos escrever a nossa string na tela xD
Se procurarmos no HelpPC a função que imprime caracteres na tela, vamos encontrar a INT 21,9 (serviço 9 da interrupção 21).
Então, de repente, você olha pra tela e ve que o DSX tem que apontar para uma string terminada em $ (MEU DEUSSS!!! O QUE É ISSO??). Bom...vamos ignorar o tal do DSX por enquanto, nao se assuste, eu avisei que ia complicar um pouquinho...mas nada que um cérebro não resolva...
Bom...o DX precisa conter o endereço da string que vamos exibir(precisa ser um ponteiro para a string).
Bom...como vocês sabem...esse programa é um programa DOS, roda no "console" do windows...entao temos algumas coisas que devemos lembrar:
Todo programa DOS é carregado no endereço 0x100. O NASM precisa de um endereço para calcular o valor dos ponteiros no programa, entao temos que dizer ao NASM que o nosso endereço base (endereço onde o programa será carregado) é o 0x100, como? usando o comando ORG (derivado de ORiGem), assim:
pronto...agora o NASM sabe o endereço base e pode calcular o valor dos ponteiros =)
Então agora MÃO NA MASSA...
Uffa...acabamos variáveis =)
Tá começando a ficar mais interessante né?
Então vamos complicar mais, para ficar mais interessante...
>>>Entrada do teclado<<<
Abrimos o HelpPC felizes e contentes, procuramos por uma funçao que pegue as teclas digitadas...e então encontramos a função DOS 21,A. Oba! vamos entrar nela...PELAS BARBAS DO PROFETA!!!(expressão de véio) O QUE SÃO ESSES "BAGULHOS" ESCRITOS AQUI???
Haha...contenha-se...eu sei seu sei...parece complicado, mas você é um hacker, você vai entender...
Bom...olhando para a tela de informações sobre a função, DSX deve ser um ponteiro para um buffer com 3 partes:
-MAX : o numero maximo de caracteres que devem ser lidos
-COUNT : o número de caracteres retornados
-BUFFER : a parte aonde os dados serão guardados
Dessa vez vai ser diferente, primeiro o código para ajudar-nos:
Calma...eu sei que parece uma coisa estranha...vou explicar
Na área max definimos o número máximo de teclas que o buffer pode guardar, que no nosso caso é 20. E como colocamos 20 como tamanho máximo, colocamos também 20 zeros na área data. Para o programa parar, aperte Enter.
Ok...agora vamos ver mais uma coisa bem importante =)
>>> Os Jumps (Saltos) <<<
Os jumps, como o próprio nome diz, servem para fazer saltos de uma parte do código para outra (esse processo é conhecido como "devio da linha de execução"). Geralmente são condicionais, ou seja, funcionam mais ou menos como se fossem um comando "If", utilizado para checar se certas condições são verdadeiras ou falsas, mas também há o chamado salto incondicional, que não depende de nenhuma condição para ser tomado.
Os mais utilizados:
Bom...para fazer as comparações, precisamos utilizar uma outra instrução, a instrução CMP (derivada de CoMPare). Nada como um exemplo para entendermos melhor a integração do CMP com o JMP.
Se você pensar e observar um pouco, você descobre o que o programa faz...Vamos analisar juntos...
Primeiramente o programa escreve a string msg na tela, em seguida adiciona 1 no valor do contador CX e então, compara o valor de CX com 5, caso não seja 5, escreve a string de novo e adiciona mais 1 ao contador CX...E assim vai até que isso tenha sido feito 5 vezes, daí o contador terá valor 5 e o salto não será executado, o programa continuará executando o código normalmente, que no caso, fecha o programa.
Legal né? Então agora vamos conversar um pouco sobre as Funções =)
>>> As Funções <<<
No exemplo acima, definimos uma parte do código chamada "Escreve", isso se chama marcador. As funções em Assembly são parecidas com marcadores, porém, têm instrução de retorno e não se utiliza jumps mas, sim, a chamamos no código. Uma função pode exigir parâmetros e é para isso que serve a famosa Pilha e também os Registradores, para passarmos os parâmetros exijidos na função. Funções são comumente usadas em tarefas repetitivas.
Aqui um breve exemplo de como usar este recurso:
Pode até parecer complicado, mas é fácil. Conforme você for praticando, você vai "pegando o jeito" de trabalhar com funções, comparações, saltos...
=)
Ei!! Está pensando o mesmo que eu?? Podemos melhorar aquele nosso programa que grava as teclas digitadas, utilizando jumps e comparações...
Vamos lá...vou colocar o código comentado aqui, analise-o com calma e entenda o que cada instrução faz, não será preciso eu ficar explicando pra que serve cada instrução...você já as conhece...
O código está bem comentado e acho que vocês não terão problemas em interpreta-lo...Vamos ao nosso próximo assunto...
>>> A Pilha <<<
A pilha é um lugar na memória do processador, que serve para guardar dados. Usamos a instrução PUSH para colocar dados na pilha e POP para retirar. Lembra daquela famosa frase: "Os últimos serão os primeiros" ?
Pois é...essa é a regra quando se usa a Pilha...os dados são retirados na ordem inversa que foram colocados.
Imagine que você tem 4 pratos, um laranja, um azul, um verde e um amarelo.
Você vai fazer uma pilha com esses pratos...então primeiro você coloca o amarelo, depois o verde, depois o azul e em cima de todos, o laranja. Agora você vai retirar de 1 em 1, quando você começar a retirar, o primeiro a ser retirado é o que está em cima, o laranja (o último que foi colocado). Assim funciona a pilha...
Lembra da instrução CALL? Aquela que chama uma função? Espero que sim...
Lembra também que toda função, no seu final, possui a instrução RETN?
Então...um exemplo de uso da pilha é o seguinte: quando você utiliza a função CALL, ela guarda na pilha o endereço de retorno, sendo assim, quando a instrução RETN for atingida, o programa saberá pra qual endereço deve voltar...
Como sempre, vamos a um exemplo básico:
Observe que colocamos o valor 456 e, em cima, colocamos o valor 789... na hora que retiramos da pilha, primeiro retiramos o valor 789(ques estava no topo da pilha) e dps tiramos o que estava embaixo, o 456, que havia sido o primeiro a ser colocado.
Super simples né? xD
Vamos continuar complicando mais as coisas então...só faltam mais 2 assuntos para acabarmos...
>>> Os Segmentos <<<
Bom...como sabemos, os pentes de memória de hoje em dia conseguem armazenar até milhares de megabytes, um número estupendamente maior do que o que é possível com 16 bits...
Em 16 bits, temos 2 bytes e , cada byte é um conjunto de 8 números binários(8 bits), logo, o número máximo que conseguimos com 2 bytes em binário é: 1111 1111 1111 1111, este número é o 65535 em decimal, que corresponde a 64 Kb, muitas vezes inferior ao número de megabytes que as memórias de hoje possuem...
A memória tem suas posições, mas desde que as memórias melhoraram e ficaram capazes de armazenar muitos MB, surgiu um problema...como fazer com as posições que ficam acima dos 64 Kb, isto é, o que fazer com as posições acima da posição 65535?
A solução foi dividir a memória em "lotes" (segmentos) de 65536 bytes (de 0 a 65535).
Primeiramente os segmentos foram colocados um após o outro: o Segmento 1 começa na posição 0 e vai até a 65535, o Segmento 1 começa em 65536 e vai até a 131071...e assim por diante...
Quando quisessemos acessar uma posição, utilizariamos 2 números, um para indicarmos o segmento que iriamos acessar e o outro para indicarmos a posição dentro desse segmento...
Só que houve um segundo problema...ao colocarmos os segmentos um após o outro, haviam programas que quando utilizavam a memória, não utilizavam todo o segmento...então a memória ficava cheia de "buracos" sem dados gravados...
Foi aí que surgiu uma nova idéia...colocar os segmenos um em cima do outro, dando um espaço de 16 bits entre o começo de um e o começo do outro...
Então agora o Segmento 0 começa na posição 0 e vai até 65535, O Segmento 1 começa na posição 16 e vai até 65551...
O Segmento x começa na posição x*16 e vai até a posição x*16 + 65535
Pronto...agora você sabe(ou não IUaHSIUAHS) como a memória do seu pc é dividida...não é fantástico? ;P
Finalmente...chegamos ao último assunto...
>>> Deslocamento <<<
O deslocamento, chamado de OffSet é um endereço que muda de acordo com o segmento e serve para indicar um byte dentro das 65536 posições disponíveis (lembre-se que as posições vão de 0 a 65535 e temos de incluir o 0, o que resulta em 65536 posições possíveis).
Mesmo depois da mudança na divisão da memória, ainda usamos dois endereços para definir uma posição na memória...
Vamos a um exemplo de como indicar posições na memória...
Endereço 2345:6789 --> Segmento 2345 , Deslocamento 6789
Ou seja, é a posição 6789 dentro do segmento 2345
O segmento começa na posição 37520 (2345*16) da memória e o nosso deslocamento dentro do segmento é de 6789, ou seja, 6789 posições à frente do começo do segmento. Se o segmento começa em 37520, a posição que queremos (6789 posições à frente) fica no endereço 44309 da memória... xDD
Bom...o seu processador, muito esperto, possui registradores especiais para lidar com os segmentos, o principais são: CS(Code Segment) , DS(Data Segment) , ES(Extra Segment)...O CS gerencia aonde está o código do programa, o DS gerencia aonde estão os dados do programa e o ES, como o nome diz, é um adicional.
Também há um registrador para gerenciar o deslocamento...o SI(Segment Index)
Quando queremos acessar um dado do programa, utilizamos a dupla DS:SI, onde DS diz o segmento que está o dado e o SI diz o deslocamento dentro do segmento em questão....
Não há instruções que alterem diretamente esses registradores...se um programador distraído bagunçar esses registradores ou se um programador inexperiente resolver "xeretar" eles e alterar de forma indevida, os resultados serão EXTREMAMENTE DANOSOS...entao é justamente por isso que, caso o programador queira altera-los, terá de fazer de forma indireta, oq eu certamente vai exigir mais atenção e evitar erros acidentais...
Vamos ao exemplo de como alterar os valores... (lembre-se: nunca brinque com assembly por conta própria, principalmente quando se trata de operações com memória)
FINALMENTE!!! Chegamos ao fim do nosso artigo...
Gostaria de deixar claro que este artigo é de exclusiva autoria MINHA para o Guia do Hacker, tudo o que está aí saiu da minha cabeça...portanto não tenho créditos a colocar.
Se você for copiar, coloque os créditos... =)
Você vai encontrar outros artigos e referências que seguem a mesma sequencia que este, assim como utiliza exemplos bem parecidos. A razão disso é clara, existe uma sequencia mais didática para se fazer um artigo qualquer...por exemplo, eu não poderia falar sobre a instrução INT antes de lhe falar o que e pra que serve um registrador...quanto aos exemplos...é claro que alguem que nem conheço em algum outro artigo deve ter feito exemplos parecidos...isso é normal...nao tem muito o que variar nesses exemplos básicos...
Bom...acho que depois de ter tido paciência (e muita!) e lido esse artigo, você agora viu que Assembly não é algo de outro mundo...obviamente é uma linguagem difícil, mas não é impossível de se aprender.
Aqui coloquei as coisas básicas de Assembly, o necessário para você começar...a partir de agora a responsabilidade de aprender mais é sua, tente fazer programas com outras funções, para isso use SEMPRE o HelpPC...
Depois que você dominar BEM esses conceitos básicos, passe para uma parte mais avançada da linguagem, com intruções e interrupções mais complicadas e até tente aprender a programar ShellCodes...Mas tenha calma, o segredo de um bom programador é fazer as coisas em passos para alcançar seus objetivos...não se atropele, siga o seu ritmo e chegue aonde você quer!
Espero que gostem do meu artigo, deu um trabalho legal para escrever ele, mas espero que seja de grande utilidade para vocês.
Abraço a todos...
Flws
Bom...antes de começarmos com o nosso "breve" tutorial de assembly, preciso fazer algumas considerações. Assembly é uma linguagem de baixo nível (só lembrando: quanto mais ALTO for o nível de uma linguagem, mais perto da escrita humana, portanto, mais FÁCIL de entende-la e utiliza-la). Portanto, talvez você tenha que ler várias vezes esse tópico pra entender. Mas acreditem, Assembly é MUITO útil.
Outra coisa que gostaria de esclarecer é que algumas pessoas escrevem (e falam) o nome da linguagem de maneira errada, o nome da linguagem é ASSEMBLY, muitas pessoas a chamam de Assembler. Assembler é o "compilador" e não a linguagem.
>>>Conhecendo a linguagem<<<
Assembly é uma linguagem de programação extremamente poderosa e relativamente de difícil implementação, pois seus comandos e suas estruturas são bem mais próximos da linguagem computacional. Raramente encontramos programas puramente feitos em Assembly, uma vez que tal linguagem exige tempo e muito raciocínio. Geralmente essa poderosa linguagem é implementada junto a alguma outra, funciona como auxiliar. Por exemplo, em programas que exigem uma interação mais profunda com o hardware de uma máquina, podemos implementar códigos Assembly junto ao código em C, C++ ou qualquer que seja a linguagem utilizada. Outro exemplo prático do uso de Assembly são os famosos ShellCodes presentes nos exploits. E ainda, como último exemplo, temos os nossos amigos “Reversers” (popularmente chamados de “crackers”, pois crackeiam programas) que usam muito o Assembly. Um bom “reverser” (“cracker”) sabe profundamente Assembly
Todos os processadores possuem uma "linguagem" própria embutida em cada um deles e cada instrução (comando) dessa linguagem produz um efeito diferente no processador, através de seus registradores. Essas instruções são representadas por números como, por exemplo, a famosa instrução MOV DL é representada pelo número B2 em hexadecimal (178 em decimal). Esses números, em Assembly, são trasnformados nos chamados mnemônicos para uma maior facilidade. Por exemplo, ao invés de você escrever B2 e ter de decorar o que é cada número, você vai escrever o mnemônico dele que, nesse caso, é o MOV DL. Onde MOV significa MOVer e DL é o chamado byte baixo de um outro registrador (DX).
"Meu Deus! Fiquei espantado com essa introdução!!! Assembly é difícil demais!!"
Tenha calma...Assembly não é aquele bicho de sete cabeças como todos dizem(e como parece ser também)...no começo parece que você não vai entender nada, mas depois as coisas começam clarear e você vai entendendo cada vez mais, até que começa a realmente utilizá-lo em seus projetos.
Bom...vamos em frente...temos bastante chão ainda
>>> Registradores <<<
Quem já deu uma fuçada nessa linguagem de programação, com certeza ouviu falar dos famosos Registradores...
Mas, Black-Hat, o que são os tais registradores??
Bom...uma das maneiras mais fáceis de explicar um registrador é compará-lo a uma variável, eles são como "espacinhos" reservados dentro do processador para guardar os códigos operacionais (MOV, JMP, NOP...), são uma espécie de "memória" temporária do processador.
Existem vários registradores, deles, os mais usados são 8, que são denominados Registradores de Uso Geral. Mas desses 8, os que realmente se utiliza bastante são apenas 4, são eles: AX, BX, CX e DX.
Um registrador armazena 16 bits, o que é correspondente a 2 bytes. Nas máquinas de 32 bits, temos o EAX, o EBX, o ECX e o EDX que são registradores que podem guardar 32 bits (segundo a matemática avançada, o equivalente a 4 bytes, pois 32/8 = 4 uahSIUHauhs).
Os nomes dos registradores sugerem pra que servem, o AX é o Acumulador, o BX é a Base, o CX é o Contador e o DX possui um D que deriva de Dados. Esses nomes surgiram devido ao uso mais comum deles, o AX é mais utilizado como acumulador, BX como base e etc...
"Aff Black-Hat, não aguento mais ler..." ... Calma, tá acabando (a parte de registradores ;P)
Bom...como eu havia dito, os registradores armazenam 2 bytes, que são chamados de Byte Alto e Byte Baixo, cada registrador possui o seu BA e o seu BB. Se quisermos acessar o byte alto do registrador AX, por exemplo, vamos utilizar o AH e observem que, mais uma vez o nome não está aí a toa...Alto em inglês é High e por isso AH. Se quisermos acessar o byte baixo de AX, utilizamos o AL (baixo em inglês é Low).
Pronto!! Agora SÓ falta vermos as instruções, as variáveis, saltos...e mais umas coisinhas
Prepare-se psicologicamente, vá beber uma água ou namorar um pouco...porque agora começa a parte mais importante (e mais legal tbm xD).
Vamos falar sobre as instruções mais utilizadas: a MOV e a INT.
>>> A Instrução MOV <<<
A instrução MOV, uma das mais utilizadas, deriva da palavra MOVe, então, já da pra saber o que ela faz, né? Ela move dados de um lugar para outro(de um registrador para outro, por exemplo) ou insere dados nos registradores. Alguns exemplos de utilização da nossa querida instrução MOV:
Código:
MOV AX, 01 //Insere o valor 1 no registrador AX MOV BX, AX //Move o que estava em AX para BX MOV AH, 0x20 //Insere o valor 20 em HEXA no byte alto de AX MOV BL, 0x43 //Insere o valor 43 em Hexa no byte baixo de BX
>>> A Instrução INT <<<
A instrução INT chama uma interrupção de software(calma, não se desespere com o nome...vc não vai precisar desse nome).
Tá, mas, pra que serve isso afinal??
O DOS e a BIOS do seu computador e de todos têm várias funções interessantes que podemos acessar com interrupções de software.
Por exemplo:
Código:
INT 0x21 ;Chama a interrupção de software 21 Hexa
Código:
MOV AX, 0x4C00 ; Insere 4C00 no registrador AX, que armazena 4 bytes(em máquinas de 32 bits) INT 0x21 ; chama a interrupção 21 de software
Bom pessoal...vocês devem estar se perguntando porque eu chamei a interrupção 21 e não a 55, por exemplo. Bom...pra explicar isso vou precisar da ajuda de um "livrinho virtual", uma referência, que se chama HelpPC...
Vou deixar o link de download pra vcs baixarem, ok? (não vou anexar porque o limite maximo de anexo Zip é menor do que o tamanho do arquivo)
Assim podemos continuar...
Link: Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar...
Continuando....
Ao abrirmos o HelpPC, caimos em uma tela inicial, chamada de Main Topic Menu. Vá até o item "Interrupt Services DOS-BIOS-EMS-Mouse" e de um enter nele. Agora vá em "DOS Functions" e aperte enter novamente. Procure pela função que utilizamos acima, a "INT 21,4C".
Bom...agora estamos na tela que nos mostra informações detalhadas sobre tal função, podemos observar que essa função recebe o nome de "Terminate Process With Return Code", traduzindo: Terminar Processo Com Código de Retorno. Olhando melhor na tela, percebemos que o HelpPC nos indica que o byte alto de AX deve ter o valor 4C e que o valor baixo deve ter o código de retorno do nosso programa. Quem programa em C/C++ já sabe o que é o código de retorno do programa...é um código, um número, que o programa retorna ao ser finalizado. Observamos também qeu a função 21 não retorna nenhum valor (o que seria chamado de rotina em outras linguagens).
Resumindo...para usar essa função, basta você colocar os valores que são exijidos no registrador AX e depois disso é só chamar uma interrupção de software 21 (INT 0x21).
Caso você esteja super curioso pra ver o nosso "programa" funcionar, baixe o NASM e compile-o, como? simples...
Salve o código fonte dele como Terminator.asm no bloco de notas ou com qualquer outro nome que queira...
Depois vá no DOS e digite:
nasm -o Terminator.com Terminator.asm
e então você obterá o seu super programa compilado no arquivo Terminator.com =)
Tá...vamos fazer um programa um pouco melhorzinho...ele vai escrever um caractere na tela...
Se procurarmos no HelpPC a função que escreve um caractere na tela vamos achar a INT 21,2.
Observem que precisamos do valor 02 no byte alto de AX (AH) e precisamos que o caractere a ser mostrado esteja no byte baixo de DX (DL). Pronto! Agora é só fazer isso...Let's Go
Código:
MOV AH,2 ; Serviço 2 da interrupção 21 MOV DL,'A' ; Caractere que deve ser impresso int 0x21 ; Chama a interrupção 21 e imprime na tela
Código:
MOV AH,2 ; Serviço 2 da interrupção 21 MOV DL,'A' ; Caractere que deve ser impresso INT 0x21 ; Chama a interrupção 21 e imprime na tela MOV AX, 0x4C00 INT 0x21
Obs.: Caso você tenha algum problema com a compilação, retire os comentários do código-fonte.
Bom...Chegamos na metade!!! UHULLL
uahSUHauihsuihs
Sim...o tópico está enorme e cansativo, mas vc quer aprender? vc tem que ler né?? ;P
Vamos em frente...
Até aqui foi fácil né? Que bom...vamos "engrossar o caldo" agora xD
>>> As Variáveis <<<
Bom...o conceito de variável em assembly é o mesmo das outras linguagens de programação, com pouca diferença, então acho que não teremos problemas com elas. Em Assembly, os dados são inseridos diretamente no programa e são rotulados com um nome que você quiser (semelhante às outras linguagens xD). Os tipos de variáveis são:
Byte(B) = 8 bits = 1 byte
Word(W) = 16 bits = 2 bytes
DWord(D) = 32 bits = 4 bytes
Agora vamos ver como "declarar" as variáveis. Iremos utilizar DB, DW e DD (Byte, Word e Double Word)
Alguns exemplos:
Código:
varbyte DB 0 ; 1 byte chamado "varbyte" é inserido varword DW 0 ; 1 word chamado "varword" é inserido vardouble DD 0 ; 1 duplo chamado "vardouble" é inserido bb DB 0,1 ; 2 bytes chamados "bb" são inseridos word3 DW 1,1,1 ; 3 words chamados "word3" são inseridos strg DB 'abcd' ; uma string de 4 bytes chamada "strg" é inserida
Agora vamos ver como manipular nossas variáveis:
Código:
MOV AL, 15 ; Insere 15 no byte baixo de AX MOV [varbyte], AL ; Move 15 para a variável varbyte MOV BX, 0x8765 ; Insere 8765 hexa em BX MOV [varword], BX ; Move o valor de BX para a variavel varword MOV [bb], BX ; O valor de "bb" se torna 0x8765 MOV BH, [bb] ; BH = Primeiro byte em "bb" = 0x65 MOV BL, [bb+1] ; BL = Segundo byte em "bb" = 0x87 MOV DL, [strg] ; DL = Primeiro byte em "strg" = 'a' MOV DL, [strg+1] ; DL = Segundo byte em "strg" = 'b' MOV DL, [strg+2] ; DL = Terceiro byte em "strg" = 'c' MOV DL, [strg+3] ; DL = Quarto byte em "strg" = 'd'
Código:
MOV DX, strg ; DX = Coloca em DX o valor de "strg"(na verdade DX se torna um ponteiro para strg, cada vez que nos referimos a ele, ele aponta pra strg) MOV AL, [DX] ; AL = Primeiro byte em "strg" = 'a'
--Vamos escrever uma string na tela--
Essa parte parece perda de tempo, mas não é, ela será essencial para a próxima parte....portanto, vamos escrever a nossa string na tela xD
Se procurarmos no HelpPC a função que imprime caracteres na tela, vamos encontrar a INT 21,9 (serviço 9 da interrupção 21).
Então, de repente, você olha pra tela e ve que o DSX tem que apontar para uma string terminada em $ (MEU DEUSSS!!! O QUE É ISSO??). Bom...vamos ignorar o tal do DSX por enquanto, nao se assuste, eu avisei que ia complicar um pouquinho...mas nada que um cérebro não resolva...
Bom...o DX precisa conter o endereço da string que vamos exibir(precisa ser um ponteiro para a string).
Bom...como vocês sabem...esse programa é um programa DOS, roda no "console" do windows...entao temos algumas coisas que devemos lembrar:
Todo programa DOS é carregado no endereço 0x100. O NASM precisa de um endereço para calcular o valor dos ponteiros no programa, entao temos que dizer ao NASM que o nosso endereço base (endereço onde o programa será carregado) é o 0x100, como? usando o comando ORG (derivado de ORiGem), assim:
Código:
[ORG 0x100]
Então agora MÃO NA MASSA...
Código:
MOV AH, 9 ; AH deve ser 9 para a int 21 imprimir uma string MOV DX, msg ; DX=Ponteiro para msg INT 0x21 MOV AX, 0x4C00 ; Termina o programa INT 0x21 msg DB 'HelloWorld!$' ; Insere a mensagem que é uma string terminada em "$"
Uffa...acabamos variáveis =)
Tá começando a ficar mais interessante né?
Então vamos complicar mais, para ficar mais interessante...
>>>Entrada do teclado<<<
Abrimos o HelpPC felizes e contentes, procuramos por uma funçao que pegue as teclas digitadas...e então encontramos a função DOS 21,A. Oba! vamos entrar nela...PELAS BARBAS DO PROFETA!!!(expressão de véio) O QUE SÃO ESSES "BAGULHOS" ESCRITOS AQUI???
Haha...contenha-se...eu sei seu sei...parece complicado, mas você é um hacker, você vai entender...
Bom...olhando para a tela de informações sobre a função, DSX deve ser um ponteiro para um buffer com 3 partes:
-MAX : o numero maximo de caracteres que devem ser lidos
-COUNT : o número de caracteres retornados
-BUFFER : a parte aonde os dados serão guardados
Dessa vez vai ser diferente, primeiro o código para ajudar-nos:
Código:
[ORG 0x100] MOV AH, 9 ; Imprime "Escreva aqui: " MOV DX, msgentrada ; INT 0x21 ; MOV AH, 0xA ; Guarda as teclas digitadas MOV DX, buff ; e armazena no buffer INT 0x21 ; MOV AX, 0x4C00 ; Termina o programa INT 0x21 ; msgentrada DB 'Escreva aqui: $' ; E finalmente o buffer buff: max DB 20 count DB 0 data DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Na área max definimos o número máximo de teclas que o buffer pode guardar, que no nosso caso é 20. E como colocamos 20 como tamanho máximo, colocamos também 20 zeros na área data. Para o programa parar, aperte Enter.
Ok...agora vamos ver mais uma coisa bem importante =)
>>> Os Jumps (Saltos) <<<
Os jumps, como o próprio nome diz, servem para fazer saltos de uma parte do código para outra (esse processo é conhecido como "devio da linha de execução"). Geralmente são condicionais, ou seja, funcionam mais ou menos como se fossem um comando "If", utilizado para checar se certas condições são verdadeiras ou falsas, mas também há o chamado salto incondicional, que não depende de nenhuma condição para ser tomado.
Os mais utilizados:
Código:
JE - Salta se for igual (Equal) JNE - Salta se for diferente (Not Equal) JZ - Salta se for zero JNZ - Salta se não for zero JA - Salta se acima (Above) JAE - Salta se acima ou igual (Above or Equal) JB - Salta se abaixo (Below) JBE - Salta se abaixo ou igual (Below or Equal) JNA - Salta se não acima (Not Above) JNB - Salta se não abaixo (Not Below) JMP - Salto Incondicional
Código:
[ORG 0x100] MOV CX, 0 ; CX é o contador, colocamos 0 nele Escreve: ; marcador chamado Escreve, para o programa saber para onde direcionar o salto MOV AH, 9 ; Escrever mensagem MOV DX, msg INT 0x21 INC CX ; CX é o contador e a instrução INC vem de Increase, Incrementar (CX = CX +1) CMP CX, 5 ; Compara o valor de CX com 5 JNE Escreve ; Caso o contador seja diferente de 5, salta para Escreve e repete tudo até chegar no salto novamente MOV AX, 0x4C00 ; Termina o programa INT 0x21 msg DB 'BELEZA!',13,10,'$' ; A string contém o código 13,10, que faz com que o console passe para a próxima linha
Primeiramente o programa escreve a string msg na tela, em seguida adiciona 1 no valor do contador CX e então, compara o valor de CX com 5, caso não seja 5, escreve a string de novo e adiciona mais 1 ao contador CX...E assim vai até que isso tenha sido feito 5 vezes, daí o contador terá valor 5 e o salto não será executado, o programa continuará executando o código normalmente, que no caso, fecha o programa.
Legal né? Então agora vamos conversar um pouco sobre as Funções =)
>>> As Funções <<<
No exemplo acima, definimos uma parte do código chamada "Escreve", isso se chama marcador. As funções em Assembly são parecidas com marcadores, porém, têm instrução de retorno e não se utiliza jumps mas, sim, a chamamos no código. Uma função pode exigir parâmetros e é para isso que serve a famosa Pilha e também os Registradores, para passarmos os parâmetros exijidos na função. Funções são comumente usadas em tarefas repetitivas.
Aqui um breve exemplo de como usar este recurso:
Código:
[ORG 0x100] CALL funcao ; Chama a função MOV AX, 0x4C00 ; Termina o programa INT 0x21 ; funcao: ; Começo da função ; aqui colocamos o código que será executado RETN ; Essa instrução indica o fim de uma função, dizendo ao programa para sair do código da função e retornar ao código principal do programa
=)
Ei!! Está pensando o mesmo que eu?? Podemos melhorar aquele nosso programa que grava as teclas digitadas, utilizando jumps e comparações...
Vamos lá...vou colocar o código comentado aqui, analise-o com calma e entenda o que cada instrução faz, não será preciso eu ficar explicando pra que serve cada instrução...você já as conhece...
Código:
[ORG 0x100] comeco: MOV AH, 9 ; Serviço 9 (de impressão) da INT 21 MOV DX, msg1 ; Coloca o DX como ponteiro para a mensagem "Digite Algo: " INT 0x21 ; Chama a interrupção e escreve na tela teclas: MOV AH, 0x1 ; Serviço 1 da função 21, para rastrear uma tecla INT 0x21 CMP AL,13 ; o código da tecla é gravada em AL. Se for 13(enter)... JE final ; Salte para o marcador "final" CMP AL, 27 ; Se for Esc JE fechar ; termina o programa MOV [data+bx], AL ; põe caracter da tecla no buffer MOV BL, [count] ; põe número já digitado em BL INC BX ; incrementa número de caracteres MOV [count], BL; atualiza o count CMP BL, 20 ; se o número de caracteres for 20.... JE final ; Pular para o marcador "final" JMP teclas ; Case nenhum jump acima seja tomado, salta para o marcador "teclas" e espera uma nova tecla final: MOV AL, '$' ; Põe o caractere terminador '$' em AL MOV BL, [count] ; Põe número de teclas digitadas em BL MOV BH, 0 ; Zera o byte alto de BX MOV [data+bx], AL ; Adiciona o '$' no final do buffer MOV AH, 9 ; Serviço 9 da interrupção 21 MOV DX, msg2 ; DX = ponteiro para msg2 INT 0x21 ; Escreve msg2 ("Texto digitado: ") na tela MOV AH, 9 ; Serviço 9 da função 21 MOV DX, data ; DX = ponteiro para os dados do buffer INT 0x21 XOR BX,BX ; Zera o registrador BX (mesma coisa que MOV BX,0) MOV BL, 20 ; Máximo de caracteres (20) XOR AX,AX ; Zera o registrador AX MOV [count], AL ; Zera o contador limpar: MOV [data+bx], AL ; Põe 0 na posição início do buffer + BL DEC BL ; Decrementa BL CMP BL, 0 ; Compara BL com 0 JA limpar ; Se for maior que 0 continua limpando o buffer JMP comeco ; Se BL for 0, volta para "comeco" e reescreve "Digite Algo: " fechar: MOV AX, 0x4c00 ; terminar programa INT 0x21 ; Os caracteres 13 e 10 forçam uma nova linha msg1 DB 13,10,13,10,'Digite Algo: $' msg2 DB 13,10,'Texto Digitado: $' ; Este é o buffer de entrada com espaço para 20 bytes para caracteres ; e 1 byte para '$' buf: count DB 0 data DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
>>> A Pilha <<<
A pilha é um lugar na memória do processador, que serve para guardar dados. Usamos a instrução PUSH para colocar dados na pilha e POP para retirar. Lembra daquela famosa frase: "Os últimos serão os primeiros" ?
Pois é...essa é a regra quando se usa a Pilha...os dados são retirados na ordem inversa que foram colocados.
Imagine que você tem 4 pratos, um laranja, um azul, um verde e um amarelo.
Você vai fazer uma pilha com esses pratos...então primeiro você coloca o amarelo, depois o verde, depois o azul e em cima de todos, o laranja. Agora você vai retirar de 1 em 1, quando você começar a retirar, o primeiro a ser retirado é o que está em cima, o laranja (o último que foi colocado). Assim funciona a pilha...
Lembra da instrução CALL? Aquela que chama uma função? Espero que sim...
Lembra também que toda função, no seu final, possui a instrução RETN?
Então...um exemplo de uso da pilha é o seguinte: quando você utiliza a função CALL, ela guarda na pilha o endereço de retorno, sendo assim, quando a instrução RETN for atingida, o programa saberá pra qual endereço deve voltar...
Como sempre, vamos a um exemplo básico:
Código:
MOV AX, 456 MOV BX, 789 PUSH AX ; Coloca o valor de AX na pilha, que é 456 PUSH BX ; Coloca o valor de BX na pilha, que é 789 POP AX ; AX = Valor que está no topo da pilha = 789 POP BX ; BX = Valor que está no topo da pilha = 456
Super simples né? xD
Vamos continuar complicando mais as coisas então...só faltam mais 2 assuntos para acabarmos...
>>> Os Segmentos <<<
Bom...como sabemos, os pentes de memória de hoje em dia conseguem armazenar até milhares de megabytes, um número estupendamente maior do que o que é possível com 16 bits...
Em 16 bits, temos 2 bytes e , cada byte é um conjunto de 8 números binários(8 bits), logo, o número máximo que conseguimos com 2 bytes em binário é: 1111 1111 1111 1111, este número é o 65535 em decimal, que corresponde a 64 Kb, muitas vezes inferior ao número de megabytes que as memórias de hoje possuem...
A memória tem suas posições, mas desde que as memórias melhoraram e ficaram capazes de armazenar muitos MB, surgiu um problema...como fazer com as posições que ficam acima dos 64 Kb, isto é, o que fazer com as posições acima da posição 65535?
A solução foi dividir a memória em "lotes" (segmentos) de 65536 bytes (de 0 a 65535).
Primeiramente os segmentos foram colocados um após o outro: o Segmento 1 começa na posição 0 e vai até a 65535, o Segmento 1 começa em 65536 e vai até a 131071...e assim por diante...
Quando quisessemos acessar uma posição, utilizariamos 2 números, um para indicarmos o segmento que iriamos acessar e o outro para indicarmos a posição dentro desse segmento...
Só que houve um segundo problema...ao colocarmos os segmentos um após o outro, haviam programas que quando utilizavam a memória, não utilizavam todo o segmento...então a memória ficava cheia de "buracos" sem dados gravados...
Foi aí que surgiu uma nova idéia...colocar os segmenos um em cima do outro, dando um espaço de 16 bits entre o começo de um e o começo do outro...
Então agora o Segmento 0 começa na posição 0 e vai até 65535, O Segmento 1 começa na posição 16 e vai até 65551...
O Segmento x começa na posição x*16 e vai até a posição x*16 + 65535
Pronto...agora você sabe(ou não IUaHSIUAHS) como a memória do seu pc é dividida...não é fantástico? ;P
Finalmente...chegamos ao último assunto...
>>> Deslocamento <<<
O deslocamento, chamado de OffSet é um endereço que muda de acordo com o segmento e serve para indicar um byte dentro das 65536 posições disponíveis (lembre-se que as posições vão de 0 a 65535 e temos de incluir o 0, o que resulta em 65536 posições possíveis).
Mesmo depois da mudança na divisão da memória, ainda usamos dois endereços para definir uma posição na memória...
Vamos a um exemplo de como indicar posições na memória...
Endereço 2345:6789 --> Segmento 2345 , Deslocamento 6789
Ou seja, é a posição 6789 dentro do segmento 2345
O segmento começa na posição 37520 (2345*16) da memória e o nosso deslocamento dentro do segmento é de 6789, ou seja, 6789 posições à frente do começo do segmento. Se o segmento começa em 37520, a posição que queremos (6789 posições à frente) fica no endereço 44309 da memória... xDD
Bom...o seu processador, muito esperto, possui registradores especiais para lidar com os segmentos, o principais são: CS(Code Segment) , DS(Data Segment) , ES(Extra Segment)...O CS gerencia aonde está o código do programa, o DS gerencia aonde estão os dados do programa e o ES, como o nome diz, é um adicional.
Também há um registrador para gerenciar o deslocamento...o SI(Segment Index)
Quando queremos acessar um dado do programa, utilizamos a dupla DS:SI, onde DS diz o segmento que está o dado e o SI diz o deslocamento dentro do segmento em questão....
Não há instruções que alterem diretamente esses registradores...se um programador distraído bagunçar esses registradores ou se um programador inexperiente resolver "xeretar" eles e alterar de forma indevida, os resultados serão EXTREMAMENTE DANOSOS...entao é justamente por isso que, caso o programador queira altera-los, terá de fazer de forma indireta, oq eu certamente vai exigir mais atenção e evitar erros acidentais...
Vamos ao exemplo de como alterar os valores... (lembre-se: nunca brinque com assembly por conta própria, principalmente quando se trata de operações com memória)
Código:
MOV AX, 1000 ; AX = 1000 MOV DS, AX ; Altera o número do DS (segmento de dados) para 1000 MOV AX, [ds:0] ; Põe word do endereço 1000:0000 em AX - endereço 16000 MOV [1234], AX ; Põe valor de AX no segmento atual no deslocamento 1234 INC AX ; Incrementa AX MOV [ds:32], AX ; Põe valor de AX no endereço 1000:0032 - endereço 16032
FINALMENTE!!! Chegamos ao fim do nosso artigo...
Gostaria de deixar claro que este artigo é de exclusiva autoria MINHA para o Guia do Hacker, tudo o que está aí saiu da minha cabeça...portanto não tenho créditos a colocar.
Se você for copiar, coloque os créditos... =)
Você vai encontrar outros artigos e referências que seguem a mesma sequencia que este, assim como utiliza exemplos bem parecidos. A razão disso é clara, existe uma sequencia mais didática para se fazer um artigo qualquer...por exemplo, eu não poderia falar sobre a instrução INT antes de lhe falar o que e pra que serve um registrador...quanto aos exemplos...é claro que alguem que nem conheço em algum outro artigo deve ter feito exemplos parecidos...isso é normal...nao tem muito o que variar nesses exemplos básicos...
Bom...acho que depois de ter tido paciência (e muita!) e lido esse artigo, você agora viu que Assembly não é algo de outro mundo...obviamente é uma linguagem difícil, mas não é impossível de se aprender.
Aqui coloquei as coisas básicas de Assembly, o necessário para você começar...a partir de agora a responsabilidade de aprender mais é sua, tente fazer programas com outras funções, para isso use SEMPRE o HelpPC...
Depois que você dominar BEM esses conceitos básicos, passe para uma parte mais avançada da linguagem, com intruções e interrupções mais complicadas e até tente aprender a programar ShellCodes...Mas tenha calma, o segredo de um bom programador é fazer as coisas em passos para alcançar seus objetivos...não se atropele, siga o seu ritmo e chegue aonde você quer!
Espero que gostem do meu artigo, deu um trabalho legal para escrever ele, mas espero que seja de grande utilidade para vocês.
Abraço a todos...
Flws
Comment