LP CHEATS
Gostaria de reagir a esta mensagem? Crie uma conta em poucos cliques ou inicie sessão para continuar.

Aprendend Assembly

Ir para baixo

Aprendend Assembly Empty Aprendend Assembly

Mensagem  B0b-3sp0nj4 Ter Jul 19, 2011 2:07 am

Porque Assembly?

Assembly é uma linguagem de baixo nível, ou seja, é uma linguagem próxima daquela que as máquinas "entendem". Para utilizá-la é necessário conhecer não só a própria linguagem como também as características de funcionamento da máquina. Isto é coisa de doido? Nem tanto. Vou fazer algumas comparações e depois você pode decidir se vale a pena assemblar.

Arquivos executáveis de alta performance
Se você tem uma rotina que acaba fritando a CPU de tanto processamento, então pense no Assembly. Os executáveis criados em Assembly apresentam duas grandes vantagens: velocidade de execução e tamanho reduzido. Além disto, como a linguagem não tem firulas e vai direto ao ponto (ou aos registradores ), a execução exige muito menos recursos porque qualquer assembler supera a capacidade dos melhores compiladores de linguagens de alto nível. O software de performance crítica é um alvo natural para os programas em assembly puro.

Bibliotecas de link dinâmico (DLLs)
O MASM (da micro$oft), NASM (da equipe liderada por Simon Tatham e Julian Hall), o TASM da Borland, o A386 de Eric Isaacson ou o GoAsm de Jeremy Gordon, enfim, praticamente todos os assemblers mais conhecidos são capazes de construir bibliotecas de link dinâmico de altíssima performance que podem ser utilizadas pelos próprios programas assembladores, pelo Visual C/C++, Delphi, Visual Basic, e outras linguagens capazes de chamar uma DLL. Usar um assembler significa obter arquivos de tamanho mínimo e alto rendimento que podem ser interfaceados com qualquer linguagem apta a chamar uma DLL. Algoritmos que, por exigirem processamento pesado, não podem ser implementados em linguagens de alto nível, tornam-se viáveis quando escritos em Assembly.

Módulos de biblioteca para programas Visual C/C++
O MASM produz um formato de módulo objeto idêntico aos compiladores do Visual C/C++. Isto permite construir módulos ou bibliotecas em MASM e linká-los diretamente a programas C/C++. Com isto, programadores C/C++ podem manipular áreas de código críticas de forma muito eficiente, ou seja, podem otimizar a manipulação de gráficos, dados de alta velocidade, criptografia, compressão de dados e qualquer outra forma de manipulação de informação que exija muito processamento.

Software Gratuito
Assembladores são softwares gratuitos, não podem ser comprados, vendidos ou incluídos em qualquer tipo de software comercial. Já que o MASM é uma das poucas coisas que a Microsoft disponibiliza sem cobrar um caminhão de US$... este é mais um motivo para começar a assemblar.
Para mim, estes quatro argumentos são mais do que suficientes. Aliás, sempre que posso e quando o peso do processamento é muito grande, opto pelo Assembly. É claro que não vou criar um aplicativo inteiro nesta linguagem, mesmo porque é para ganhar tempo na produção que existem os RAD da vida, mas enxertar código Assembly no Delphi ou no C++ são dois palitos. E o resultado é altamente compensador!

Na série de tutoriais desta seção vou tratar apenas de Assembly puro (nada de mistura com outras linguagens) porque se eu misturar assuntos o resultado acaba sendo uma salada. Uma vez dominado o arroz com feijão da linguagem, criar estas entidades híbridas não é nenhum bicho de sete cabeças. E aí, está afim de me acompanhar? Então vamos lá... siga a vovó.

Por Onde Começar?



Assemble vem do Inglês e significa construir, juntar partes; é daí que vem o nome de uma linguagem de programação, o ASSEMBLY. Assembler significa construtor e é o nome que se dá a programas que "juntam partes", que "constroem" executáveis. É por isto que não é possível escrever um programa em assembler (como se costuma ver por aí): o programa é escrito em Assembly e depois pode ser transformado num executável por um assembler

Por onde começar é aquela parte inicial, chata pra caramba, mas que não adianta ignorar porque vai fazer falta mais tarde. Neste texto você vai ter a oportunidade de conhecer um pouco do MASM e da estrutura de um programa.

Para se comunicar com um assembler são utilizadas algumas convenções que o programa exige para poder executar as tarefas desejadas. Cada assembler tem suas próprias convenções. Nestes tutoriais vamos utilizar o MASM32 como assembler, mas você pode usar qualquer outro da sua escolha. As diferenças são pequenas e é fácil adaptar o código.

O Assembler

Antes de mais nada, faça o download do macroassembler da Microsoft, o MASM 32 versão 9, na seção de downloads da Aldeia. Procure na categoria Informática|Compiladores/Decompiladores. Descompacte o arquivo zipado num diretório (quem "fala" Assembly não usa pastas, usa diretórios!) da sua escolha e clique em /seuDiretório/QEDITOR.EXE. Esta é a interface de edição de programação - não é cheia de nove horas porque programadores assembly estão acostumados a escovar bits e a fazer a maior parte do trabalho na unha sem muita frescura.

Muito bem. O editor está à disposição e aí vem a pergunta: o que fazer agora? tô afim de começar! Hehehe... lembra da parte inicial, chata pra caramba? Pois é esta aqui. Tenha um pouco de paciência e, por enquanto, feche o editor e leia o resto do texto. Depois a gente conversa.

A sequência do trabalho


Quando se escreve um programa, na realidade se escreve um texto que contém um roteiro que precisa ser transformado num programa. Esta transformação é feita em duas etapas. Primeiro, o texto precisa ser transformado num novo roteiro que contenha instruções para a CPU. Esta primeira tradução produz os assim chamados arquivos objeto. O programa que produz arquivos objeto é chamado de assembler ou compilador. Os arquivos objeto, por sua vez, servem de fonte para outro tipo de programa: o linker. Os linkers adicionam referências de endereços aos arquivos objeto e os transformam em arquivos executáveis, a versão final do seu programa. Resumindo as etapas, tem-se o seguinte:

Código PHP:
Arquivo Texto (.ASM) ---> Arquivo Objeto (.OBJ) ---> Arquivo Executável (.EXE)
Editor de Texto Assembler (compilador) Linker
O MASM é um aplicativo que pode coordenar o seu trabalho: possui um editor de texto, um compilador e um linker. Através da janela do MASM é possível gerenciar todo o processo de produção de um programa.

Você pode escrever seu texto (ou script) em qualquer editor de texto, até com o bloco de notas do Windows. Como usaremos o MASM, podemos usar o editor de texto do próprio. Se escrevermos uma receita de bolo no editor de texto e pedirmos para o MASM (compilador+linker) transformá-lo num programa... hmmmm... o MASM vai ficar perdidinho da silva. É preciso criar um texto que o MASM entenda e, o que é mais importante, obedecendo uma determinada estrutura.

A Estrutura de um Programa


A primeira informação que o MASM precisa para poder trabalhar é o tipo de CPU para a qual o programa se destina. Isto é necessário para que o compilador possa escolher o conjunto de instruções compatíveis com a CPU. Como pretendemos produzir programas de 32 bits, indicamos o conjunto de instruções como sendo do tipo 80386 em diante (80386, 80486, Pentium, etc). Trabalhar com o conjunto de instruções do 386 costuma ser mais do que suficiente. Como é que passamos essa informação para o compilador? Usando uma diretiva apropriada:
Código PHP:
.386 ---> para processadores do tipo 80386

.486 ---> para processadores do tipo 80486
Puxa vida, por enquanto, nada de programa. Ainda precisamos indicar qual é o modelo de memória que deve ser usado. Um executável é carregado na memória de acordo com o modelo de memória definido durante a compilação. Na época dos computadores de 16 bits, o programa era carregado na memória em segmentos de tamanho predefinido. Era uma complicação danada gerenciar a execução do programa: para cada endereço era necessário indicar o segmento correspondente e saltar de um segmento para outro - parecia soluço e "comia" um monte de processamento. Com o advento dos 32 bits, os executáveis passaram a ser carregados na memória em endereços contíguos. Imagine um segmento único de memória contendo o executável, uma tripa enorme com a sequência de instruções. Este modelo de memória foi denominado FLAT, ou seja, modelo plano ou contínuo. Então, lá vai mais uma diretiva:

Código PHP:
.MODEL FLAT
Qualquer programa, com toda certeza (tem tanta certeza assim?) vai realizar algum trabalho, ou seja, vai ter funções. As funções geralmente precisam de dados para executar uma tarefa. Estes dados podem ser gerados dentro da própria função ou serem enviados para elas. Dados enviados a uma função são chamados de parâmetros. A forma de mandar estes parâmetros também precisa ser definida previamente: se houver mais de um parâmetro, podemos enviá-los de frente para trás ou de trás para frente, ou seja, da esquerda para a direita ou da direita para a esquerda. Veja um exemplo: suponha que uma função espere receber dois parâmetros (param1 e param2). Podemos enviá-los na sequência param1, param2 ou na sequência param2, param1. A primeira convenção de passagem de parâmetros é conhecida como PASCAL e a segunda como convenção C.

Os parâmetros recebidos são guardados temporariamente num registrador da CPU chamado de pilha (stack). Imagine a pilha como uma pilha de caixas de sabão em pó no supermercado. À medida que você colocar novas caixas na pilha, a pilha vai crescendo; à medida que tira, a pilha vai encolhendo; se você tirar uma caixa do meio da pilha, as caixas vão cair. Como o conteúdo da pilha é alterado quando uma função é chamada ou quando a própria função alterá-lo, é preciso fazer um ajuste de pilha em cada retorno de função... senão a pilha "cai" e o programa dá pau. Na convenção de passagem de parâmetros do tipo Pascal, a função chamada é a responsável pelo ajuste da pilha antes do retorno. Na convenção C, a rotina chamadora é a responsável.

Existe uma terceira convenção de passagem de parâmetros denominada STDCALL (abreviação de STanDard CALL - chamada padrão). Usando esta convenção, os parâmetros são enviados da direita para a esquerda (como na convenção C) mas a função chamada é a responsável pelo ajuste da pilha. A STDCALL é um híbrido das convenções Pascal e C.

Os sistemas win32 utilizam exclusivamente a convenção de passagem de parâmetros STDCALL. Podemos e devemos completar a diretiva acima com:
Código PHP:
.MODEL FLAT, STDCALL
Na maioria das vezes, um programa precisa de dados inicializados (com valores definidos) para poder começar a funcionar. São coisas do tipo nome do aplicativo, título da janela principal, etc. Para indicar ao MASM que vamos listar nomes de variáveis e seus respectivos valores, usamos a diretiva .DATA. Tudo que o compilador encontrar nas linhas subsequentes, até encontrar outra diretiva, ele vai considerar como variáveis (dados) inicializados.

Também podemos preparar variáveis não inicializadas, ou seja, fazemos com que o assembler ponha determinados nomes de variáveis na lista de variáveis, mas sem valores definidos. Estes dados não inicializados podem ser usados posteriormente pelo código. A diretiva .DATA? faz a indicação.

Variáveis, como o próprio nome indica, podem ter seus valores alterados. Num determinado projeto pode ser que sejam necessários dados de valores fixos, as assim chamadas constantes. Neste caso, utilizamos a diretiva .CONST.

Todos estes dados, indicados antes do programa propriamente dito, podem ser usados em qualquer ponto do código. Tanto faz se estamos no módulo principal ou em alguma subrotina (função) - estes dados estão sempre disponíveis e acessíveis. São os chamados dados globais (variáveis e constantes). Enquanto o programa estiver sendo executado, estes dados ficam preservados. Só são destruídos quando o programa termina.

Ufa! Finalmente chegamos no miolo do programa. É onde deve ficar o código que indica como nosso programa deve se comportar e o que deve realizar. A diretiva usada não poderia ser outra: .CODE indica o início do nosso código. Só que esta é a última diretiva da estrutura e o assembler não tem como saber onde ela termina. Estabelecemos então o limite com um rótulo seguido por dois pontos. O dá um nome à área de código e indicamos o fim da área com um "end ". Você pode escolher o nome que quiser, por exemplo:

Código PHP:
.CODE ---> Início do código
inicio: ---> Rótulo indicando o início da área de código

... (seu código)

end inicio ---> Fim da área de código
Resumindo, a estrutura que o MASM entende e aceita para assemblar e linkar é a seguinte:

Código PHP:
.386
.MODEL FLAT, STDCALL
.DATA
... (aqui vão os dados inicializados)
.DATA?
... (aqui vão os dados não inicializados)
.CONST
... (aqui ficam as constantes)
.CODE
inicio:
... (aqui está todo o código do programa)
end inicio
Criando um Programa


Veja quais são os os arquivos envolvidos na rotina de criação de um programa.

Para criar um programa escrito em Assembly e que rode no ambiente Windows e *nix, várias etapas precisam ser cumpridas. Inicialmente escreve-se o chamado código fonte ou script do programa e, se necessário, um arquivo de recursos. Estes arquivos, em texto ASCII puro, serão transformados em arquivos objeto os quais, por sua vez, serão utilizados para compor o executável. Observe que são três etapas distintas: elaboração do código fonte, compilação e linkedição.

As Etapas

Código:
Elaboraçã Compilação Linkedição
Arquivos fonte Arquivos objeto Executável
.asm .obj .exe ou .dll
.** .res
Os arquivos de recursos (.**) só são necessários quando se quer utilizar recursos adicionais, como menus e bitmaps. Isto significa que podem ou não estar presentes no seu projeto. Vamos explorar cada uma das fases.

O código fonte


Os arquivos que contêm o código fonte deve ser gravados em texto ASCII puro, ou seja, não devem conter caracteres especiais de formatação. Podem ser produzidos em qualquer editor de texto que ofereça a possibilidade de salvar arquivos neste formato (geralmente chamado simplesmente de formato texto).

O bloco de notas do Windows é um bom editor para este trabalho, mas você pode usar o editor que mais lhe convier, inclusive o editor oficial da Aldeia, o SilicioPad, que nada mais é do que o bloco de notas modificado para mostrar o número das linha no rodapé da janela (como fazer esta mandrakaria será tema de um tutorial avançado). Se quiser experimentar, faça o download do SilicioPad em Downloads/Informática/Editores.

É hábito dos programadores indicar a linguagem usada na extensão do arquivo fonte. Por exemplo, .asm para código fonte em Assembly, .c para a linguagem C, .cpp para a C++ ou .pas para Pascal (Delphi). O programa assembler (ou compilador) aceita qualquer extensão, contanto que o código fonte seja o que ele espera.

Um arquivo .asm contém as instruções para o processador na forma de palavras e números. Estas instruções serão executadas pelo processador quando o programa rodar. Acontece que o processador não entende o código fonte: ele precisa ser transformado em "linguagem de máquina" e estar "arrumado" de acordo com um padrão que o sistema operacional consiga identificar. Esta transformação é feita em duas etapas: o compilador prepara o código fonte guardando o código fornecido no formato COFF (Common Object File Format) num arquivo objeto (.obj) e este, por sua vez, é transformado pelo linkeditor num arquivo executável (.exe) no formato PE (Portable Executable).

O arquivo objeto


Um arquivo objeto é criado pelo compilador ou assembler a partir de um arquivo .asm. O compilador pega as instruções do arquivo .asm, que estão em palavras e números, e as transforma no formato objeto COFF, que é o formato que o linker espera. O compilador concatena todo o código e os dados de instrução presentes no código fonte e os coloca em seções de código e de dados no arquivo .obj. A seção de código contém as instruções, os chamados códigos operacionais (opcodes) que o processador executa quando o programa é rodado. A seção de dados contém informações que serão mantidas na memória enquanto o programa estiver sendo executado.

Não é possível rodar um arquivo .obj como um programa, por que não tem a forma final de um executável. O formato de um executável esperado pelo Windows é o formato PE. Algumas vezes o arquivo .obj é chamado de arquivo "binário" ou simplesmente "bin", o que se justifica porque ele não contém mais os mnemônicos, apenas números.

O código fonte dos recursos

Como todo código fonte, este também é um arquivo texto produzido com um editor de texto. Deve conter palavras e números que o sistema operacional usa para montar os recursos do programa que será executado os quais, geralmente, são menus, diálogos e tabelas de strings. Também serve de fonte de referência para outros arquivos como ícones, bitmaps e cursores. Maiores detalhes em como criar um arquivo de recursos você encontra nos tutoriais.

O arquivo objeto dos recursos


O arquivo de recursos compilado é como se fosse um "arquivo objeto" dos recursos. Normalmente não se usa este termo, mas a comparação é perfeitamente válida. Este arquivo é produzido por um compilador de recursos, o qual formata as instruções contidas num arquivo .** (palavras e números), convertendo-o num arquivo .res pronto para ser inserido na seção de recursos de um executável final por um linker.

O executável


O arquivo executável é o arquivo final que pode ser executado pelo sistema operacional. Geralmente está no formato PE, reconhecido pelo Unix, Linux, Windows, etc. O programa é produzido por um linker, o qual usa um ou mais arquivos .obj e .res e os combina num executável final. O formato PE também exige que o executável tenha um cabeçalho com informações a respeito do arquivo .exe. O linker fornece estas informações.

Arquivos DLL


Este tipo de arquivo contém funções e dados que outros executáveis podem utilizar quando estiverem rodando. São úteis quando contêm funções que serão chamadas repetidas vezes por vários executáveis. Ao invés de repetir estas funções em cada um dos executáveis, estes utilizam uma fonte comum: as DLLs. O acrônimo DLL origina-se de Dinamic Link Library - Biblioteca de Vínculo Dinâmico.

Códigos Operacionais e Mnemônicos



A "linguagem" binária


Em nível de hardware só existe um "idioma": o binário. Isto significa que o processador (e todos os outros componentes de um computador) só entendem "binarês", ou seja, bits ligados ou desligados.

Digamos que eu queira enviar o valor 1.453.785.342 para um registrador da CPU. Para que o computador "entenda" o que estou querendo dizer, este valor precisa chegar no formato binário. Considerando que bits ligados sejam iguais a 1 e bits desligados sejam iguais a 0, a seguinte "tripa" de bits precisa ser enviada:

A Linguagem Assembly

Assembly é uma linguagem de programação e uma linguagem de programação serve para fazer programas. Os programas são escritos em forma de texto. Usando um editor de texto criamos o chamado código fonte. Este código fonte é transformado pelo compilador e pelo linker num programa executável.

Muitas vezes ouvimos "linguagem assembler". É um erro muito difundido. Na realidade, Assembly é o nome da linguagem e assembler é um programa capaz de compilar código fonte em arquivos objeto.

A linguagem Assembly é considerada de baixo nível. Isto não significa que seja menos importante ou eficiente que uma linguagem chamada de alto nível - são apenas modos diferentes de se programar e níveis diferentes de atuação. O que posso dizer é que, com uma linguagem de baixo nível como a Assembly, você pilota diretamente a CPU do seu computador - nada de intermediários.

Uma das características da Assembly é que cada linha do código fonte possui apenas uma instrução para o processador (CPU). Por exemplo, MOV EAX,EDX irá MOVer o conteúdo do registrador EDX para o registrador EAX. Neste caso, a instrução "MOV" é chamada de mnemônico. Os mnemônicos são os "apelidos" das instruções, mais fáceis de guardar na memória do que seu valor hexadecimal ou seu valor binário exigido pelo processador. De mnemônico em mnemônico podemos escrever nosso código fonte e fazer com que o processador faça exatamente o que queremos, sem firulas ou perda de tempo. O resultado é um programa enxuto, rápido e altamente eficiente. Tome coragem! Experimente programar em Assembly!

Os componentes da linguagem Assembly


Os componentes da linguagem Assembly são basicamente as instruções para o processador. Ignorando as instruções que não podem ser utilizadas pelo sistema operacional Windows, assembly condicional, macros, ponto flutuante, MMX e instruções de 64 bits, os componentes da linguagem Assembly podem ser divididos nas seguintes categorias:

Instruções de registradores
Instruções de pilha
Instruções de execução
Instruções de memória
Instruções de flag
Declarações de memória
Diretivas para o assembler
Comentários
Instruções para o sistema operacional (Windows)
Instruções de registradores

Estas instruções transferem dados ou realizam cálculos utilizando os registradores de 32 bits da CPU. Existem seis registradores de uso geral chamados de EAX, EBX, ECX, EDX, ESI e EDI. Exemplos deste tipo de instrução são:

Código PHP:
MOV ESI,EBX ;move o conteúdo do registrador EBX para o registrador ESI
ADD EAX,EDI ;soma o conteúdo do registrador EDI com o do registrador EAX
BT ECX,0 ;testa o bit 0 do registrador ECX
CMP EDX,450 ;compara o conteúdo de EDX com 450
DIV ECX ;divide EDX:EAX (inteiro longo) por ECX
MUL ECX ;multiplica EAX por ECX e põe o resultado em EDX:EAX
SHL EDX,4 ;desloca os bits de EDX para a esquerda em 4 bits (multiplica por 16)
TEST EAX,8 ;testa o bit 3 do registrador EAX
Instruções de pilha

A pilha é uma área de memória reservada pelo sistema operacional como área de arquivamento temporário para cada programa que estiver rodando. São exemplos deste tipo de instrução:

Código PHP:
PUSH EAX ;põe o conteúdo do registrador EAX na pilha
POP EDX ;retira o último valor colocado na pilha e põe em EDX
PUSH 1000h ;põe o valor hexadecimal 1000 na pilha
MOV EBP,<a class="mosinfopop" style="cursor:help;border-bottom:1px dotted #000000;" href="javascript:void(0)" onmouseover="return overlib('<p>Extended Stack Pointer<br>&amp;nbsp;<br />Ponteiro de Pilha Estendido - um registrador que guarda o endereço do topo da pilha.</p>&amp;nbsp;<br />', CAPTION, 'ESP',BELOW,RIGHT, WIDTH, 300, FGCOLOR, '#CCCCFF', BGCOLOR, '#333399', TEXTCOLOR, '#000000', CAPCOLOR, '#FFFFFF', OFFSETX, 10, OFFSETY, 10);" onmouseout="return nd();" > ESP</a> ;move o valor do ponteiro da pilha para o registrador EBP
SUB ESP,30h ;move o ponteiro da pilha para abrir uma área de armazenamento para dados locais
MOV D[EBP-20h],500h ;insere o valor 500 hexa na área de dados locais
Instruções de execução

Estas instruções desviam o processador para que execute código a partir de um ponto que não seja a próxima linha de execução. São exemplos:

Código PHP:
CALL MAKEWINDOW ;executa o código do procedimento e depois retorna
CALL EAX ;executa o código a partir do endereço presente em EAX e depois retorna
RET ;termina este procedimento retornando ao chamador
JZ 4 ;se o resultado for zero, continua a execução a partir do marcador 4:
JC >.fim ;se a flag estiver ativa, continua a execução a partir de .fim
JMP MAKEWINDOW ;continua a execução a partir do procedimento nominado
LOOP 2 ;decrementa ECX e salta para o marcador 2: a não ser que ECX=0
Instruções de memória

Estas instruções lêem ou escrevem em áreas de memória que não sejam da pilha. Normalmente estas áreas estão na seção de dados do próprio executável ou podem ser alocadas pelo sistema operacional em tempo de execução. São exemplos:

Código PHP:
ADD EAX,[ESI] ;adiciona a EAX o conteúdo de memória cujo ponteiro de endereço está no registrador ESI
MOV EAX,[MEUSDADOS] ;move para EAX o conteúdo de memória cujo marcador é MEUSDADOS
SUB D[MEUSDADOS+64],10h ;subtrai 10h do dword em MEUSDADOS mais 64 bytes
CMP B[MEUSDADOS+EDX*4],2 ;compara um byte com 2 numa parte do array MEUSDADOS
LODSB ;carrega o byte na memória apontada por ESI em al
STOSD ;carrega o conteúdo de EAX na memória apontada por EDI
Instruções de flag

As principais flags usadas são a Z (flag zero), C (flag carry), S (flag de sinal) e D (flag de direção). A maioria das instruções alteram as flags automaticamente para mostrarem o resultado da instrução. Existem determinadas instruções que podem ser usadas para alterar o valor das flags manualmente:

Código PHP:
STC ;ativa a flag de carry
CLC ;limpa a flag de carry
STD ;ativa a flag de direção para LODS, STOS, CMPS, SCAS, MOVS
CLD ;limpa a flag de direção
Declarações de memória

O sistema operacional reserva memória para o executável quando ele é executado. Declarações são feitas para reservar memória na seção de dados ou na seção de constantes se os dados devem ser inicializados, isto é, devem receber um valor. Se forem dados não inicializados, a área de dados pode ficar reservada na seção de dados não inicializados. Isto não toma espaço algum no arquivo executável, por que um espaço de memória é alocado para este tipo de dado quando o executável é iniciado pela primeira vez.

Seguem exemplos de como a memória é declarada, o que pode variar de acordo com o assembler utilizado:

Código PHP:
DB 4 ;declara um byte e lhe atribui o valor inicial 4
MEUDADO DB 4 ;um byte de valor inicial 4 com o marcador MEUDADO
MYSTRUCT DD 16 DUP 0 ;16 dwords, todos com valor zero, chamados MYSTRUCT
BUFFER DB 1024 DUP ? ;1024 bytes chamados BUFFER como dados não definidos
Diretivas para o assembler

São instruções que orientam onde o Assembler deve colocar o código fonte que as segue. O Assembler marca a seção de código como apenas para leitura e executável; as seções de dados definidos e indefinidos como leitura/escrita. Veja alguns exemplos (que podem variar de acordo com o assembler):

Código PHP:
CODE SECTION ;tudo o que se segue deve ser colocado numa seção
;marcada para apenas leitura e executável (código)
DATA SECTION ;tudo o que se segue deve ser colocado numa seção
;com atributos de leitura e escrita mas não de código
CONST SECTION ;tudo o que se segue deve ser colocado numa seção
;com atributo de apenas leitura
Comentários

Após ponto e vírgula, o texto é ignorado até a próxima quebra de linha. Desta forma é possível associar descrições e explicações ao código fonte, as quais serão ignoradas pelo assembler.

Instruções para o sistema operacional

Proporcionam ao programador o acesso a uma grande variedade de funções. No caso do sistema operacional Windows, proporcionam acesso à API. Veja os exemplos abaixo:

Código PHP:
PUSH 12h ;põe valor hexa 12 na pilha para a chamada à API
CALL GetKeyState ;pede ao Windows para por o estado da tecla Alt em EAX
TEST EAX,80000000h ;testa se a tecla Alt está sendo pressionada (bit 31 ligado)
JZ >L22 ;não, saltar para L22
PUSH 24h ;valor hexa 24 = ponto de interrogação, botões yes e no
PUSH ESI,EDI ;endereço do título, endereço da mensagem
PUSH [hWnd] ;manipulador da janela proprietária
CALL MessageBoxA ;mostra a caixa de mensagem Windows pedindo yes/no
CMP AL,7 ;checa se "no" foi clicado pelo usuário
JNZ >L40 ;não, saltar para L40
PUSH 0
PUSH ADDR FILE_DONE ;dá endereço do arquivo FILE_DONE para receber o resultado
PUSH ECX,EDX ;ECX = bytes que devem ser escritos, EDX=fonte de dados
PUSH ESI ;ESI = manipulador do arquivo
CALL WriteFile ;escrever ECX bytes de EDX para ESI
PUSH 808h,5h ;808 = em baixo e meio preenchido, 5 = elevado
PUSH EBX,EDX ;ebx = RECT, EDX = contexto do dispositivo
CALL DrawEdge ;desenhar retângulo especial com bordas na tela
PUSH 4h,3000h,ESI,0 ;4h = fazer memória leitura/escrita, 3000h = reservar
CALL VirtualAlloc ;reservar e consignar ESI bytes de memória leitura/escrita
PUSH 0,[hInst],0,0 ;param, manipulador do módulo, menu e proprietário
PUSH 208,130,30,300 ;altura, largura, y, x
PUSH 80C80000h ;estilo (POPUP+CAPTION+SYSMENU)
PUSH EAX ;EAX = endereço da string terminada em zero com o título
PUSH 'LISTBOX' ;por ponteiro para 'LISTBOX' na pilha
PUSH 0 ;estilo extended (nenhum)
CALL CreateWindowExA ;criar a janela listbox

...... ou, se preferir, usar INVOKE ..
INVOKE CreateWindowExA, 0,'LISTBOX',EAX,80C80000h,300,30,130,208,0,0,[hInst],0

.............

INVOKE ShowWindow, [hWnd], 1
Código PHP:
1010110101001110000000011111110 <-- 1.453.785.342 em binário
Tá na cara que não vai dar para programar desta forma, mesmo por que ninguém é maluco o suficiente para querer transformar opcodes e valores nestas "tripas" ininteligíveis.

Valores hexadecimais


Ainda em nível de hardware, num processador Intel ou compatível, as instruções estão embutidas nos circuitos e são denominadas de "opcodes" - códigos operacionais. Cada código operacional é identificado por um valor binário. Para realizar uma transferência para o registrador eax, por exemplo, o opcode é 10100001.

Apesar da informação poder ser escrita em nível de bit, o tamanho mínimo dos dados e códigos normalmente utilizado é um BYTE (8 bits). Em nível de BYTE, o código pode ser escrito na notação hexadecimal, porém este método de escrever ainda é muito complexo e exige o conhecimento de um grande número de opcodes. Aliás, o opcode 10100001 para transferir um valor para eax é A1 em hexadecimal.

Além disto, existe uma dificuldade adicional: os processadores Intel têm a peculiaridade de armazenar os dados em ordem inversa. Por exemplo, se quisermos copiar o valor 56 A7 00 FE para o registrador eax, precisamos enviar este valor como FE 00 A7 56. Só para você saber, esta é a notação hexadecimal do mesmo valor decimal e binário citado acima.

Se você não tem idéia do que vem a ser a notação hexadecimal, dê uma chegada na Escolinha da Aldeia e conheça os principais sistemas de notação. Entre eles, você vai conhecer os sistemas binário e hexadecimal

Assim não dava para ficar


Para facilitar o trabalho de escrever código de baixo nível, há muitos anos atrás foi desenvolvido um sistema onde grupos de códigos operacionais semelhantes receberam nomes que lembram suas funções e que os tornaram muito mais práticos de serem usados. Estes nomes são denominados MNEMÔNICOS. Ficando nos exemplos citados, o opcode 10100001 (em binário), ou A1 (em hexadecimal), foi trocado pelo mnemônico MOV (de mover, transferir). Este é o sistema utilizado nos assemblers de 32 bits modernos.

Um mnemônico é um nome reservado de uma família de códigos operacionais que realizam tarefas semelhantes no processador. Os códigos operacionais atuais diferem quanto ao tamanho e ao tipo de operandos que são utilizados. Por exemplo, se quisermos usar o mnemônico MOV, podemos escrever

Código PHP:
mov eax, var1 ; move a variável var1 para o registrador eax
; o valor hexadecimal deste MOV é A1

mov var1, eax ; move o valor de eax para a variável var1
; o valor hexadecimal deste MOV é A3
E viva os mnemônicos!

No MASM32, assim como em outros macroassemblers, usamos apenas mnemônicos... graças a Deus! Conversamos usando mnemônicos como MOV, MUL (multiplicar) ou JP (jump=saltar) e deixamos a tarefa da tradução para o "binarês" para o assembler

Bit e Binário



Bit e Binário, veja o que é possível fazer com um simples bit.

Um bit é um elemento elétrico dentro do computador que pode estar "ligado" ou "desligado". Em termos físicos, é um semi-condutor capaz de conduzir pequenas quantidades de eletricidade quando está "ligado", coisa que não consegue quando está "desligado". Quando está "ligado" pode ser considerado como tendo o valor 1 (um). Em linguagem de computador diz-se que o bit está "setado" (do inglês - set). Quando está "desligado", pode ser considerado como tendo o valor zero. Em linguagem de computador o bit está "zerado". Bits só podem estar setados ou zerados - não possuem qualquer outro estado. Portanto, um bit só pode ter um valor binário de 0 ou 1.

Dois ou mais bits podem ser associados para criar números maiores. Quando bits são associados, o bit da direita é chamado de menos significativo e, se estiver setado, representa o valor um. O próximo bit, à esquerda, é chamado de mais significativo e possui o fator dois. Quando este bit estiver setado, ele representa o valor dois. Agora imagine um número formado por dois bits. O número em binário pode ser 00 ou 01 ou 10 ou 11 e seus equivalentes no sistema decimal são 0, 1, 2 e 3.

Byte, word e dword

Bytes, words e dwords são blocos de dados básicos usados em programação. O processador irá trabalhar com o tamanho de dados adequados para executar corretamente as instruções que receber.

Um byte possui 8 bits, um word possui 16 bits. Partindo desta premissa, um word possui 2 bytes (16 bits) e um duplo word (dword) possui 4 bytes ou 32 bits.

Um byte pode representar valores decimais de 0 a 255. O valor decimal 255 é o total dos valores de todos os 8 bits de um byte, ou seja, decimal 128, 64, 32, 16, 8, 4, 2 e 1. Some estes valores e você vai chegar no valor 255. Isto quer dizer que, quando todos os oito bits do byte estão setados, o valor do byte é 255.

Um word pode representar os valores decimais de 0 a 65.535 (o que é o mesmo que dizer 64 Kilobytes). 64 Kb é o total dos valores de todos os 16 bits setados, assim como na explicação anterior.

Um dword (literalmente um "double word" ou "palavra dupla") pode representar os valores decimais de 0 a 4.294.967.295 (4 Gigabytes). 4 Gb ou 4 giga é o total dos valores de todos os 32 bits.

Existem blocos de dados maiores que o processador consegue manipular - um qword (literalmente "quad word" ou "palavra quádrupla") são 64 bits de dados (4 words ou 8 bytes), um tword possui 80 bits de dados (10 bytes) e algumas instruções podem usar até 128 bits (16 bytes). Dá para perceber que estes números são bastante grandes.

Agora vamos para valores menores. Um nibble é a metade de um byte, ou seja, possui quatro bits de dados. Num byte existem dois nibbles e cada um deles só consegue representar valores decimais de 0 a 128. Este valor pode ser representado como um único número hexadecimal. Tome o número hexadecimal 148C como exemplo. Ele é constituído por dois bytes. O primeiro byte contém o valor 14h e o segundo 8Ch. Neste caso, os nibbles contêm os valores 1, 4, 8 e C (em hexadecimal).

B0b-3sp0nj4
Membro
Membro

Mensagens : 16
Data de inscrição : 18/07/2011

Ir para o topo Ir para baixo

Ir para o topo

- Tópicos semelhantes

 
Permissões neste sub-fórum
Não podes responder a tópicos