5596.9 ARQUITETURA DE COMPUTADORES PROGRAMAÇÃO ASSEMBLY NA ARQUITETURA X86 Aécio Fróes / CP05241-67 / [email protected]. Bolsista CNPq/PIBIT LACMO: Aprendizado Computacional e Métodos de Otimização Visão geral 2 Motivação Aplicações reais Introdução Arquitetura x86 Ferramentas de programação Processo de programação Diretivas do Turbo Assembler Instruções da arquitetura x86 API do MS-DOS e BIOS PC Exemplos de código-fonte Referências Perguntas e Discussões Motivação 3 Sistemas operacionais Segurança de aplicativos e sistemas Identificação e correção de erros (patches) através de depuração e engenharia reversa, análise de vírus para construção de vacinas através de assinatura (virus signature) Construção de ferramentas de programação Rotinas de carga (bootloaders), partes do núcleo do sistema (kernel), sistemas de tempo real (realtime systems), sistemas embarcados (embedded systems), drivers de dispositivos (device drivers) Compiladores, Montadores (Assemblers), Linkers Rotinas de alto desempenho em aplicações Sistemas gráficos, jogos de computador, criptografia Aplicações reais 4 Initial Program Loader do QNX RTOS (http://www.qnx.com) bootsect.s do Linux (http://tldp.org/HOWTO/Linux-i386-BootCode-HOWTO/bootsect.html) ZSNES: emulador de jogos do Super Nintendo SoftICE Debugger, da Nu-Mega Technologies Vírus Chernobyl (CIH), por Chen Ing-Hau em 1998 UPX Packer: compactador de executáveis Maioria dos firmwares de IBM-PC (e.g. AWARD, AMI) Hotfix para o Windows Metafile, por Ilfak Guilfanov (http://www.hexblog.com/2005/12/wmf_vuln.html) IDA Pro Disassembler, por Ilfak Guilfanov FASM: Assembler escrito em Assembly (http://flatassembler.net) Introdução 5 Assembly utiliza mnemônicos curtos para representar as operações fundamentais que a UCP (Unidade Central de Processamento) pode executar Nesse contexto, um mnemônico é um código, usualmente entre 1 e 5 letras, que representa um opcode O código Assembly é de baixo nível e específico de um circuito, máquina ou arquitetura Um Assembler tradicional efetua um mapeamento do tipo um-paraum dos mnemônicos para as instruções e dados correspondentes Assemblers modernos incluem suporte a macros e recursos de alto nível, como controle de fluxo, funções, tipos abstratos de dados e até suporte a programação orientada a objetos Existem diversas sintaxes de Assembly, para a arquitetura x86 destacam-se a sintaxe Intel e a sintaxe AT&T. Não é case sensitive Arquitetura x86 6 Intel 4004: primeiro microprocessador em um único chip Surgiu em 1978 (Intel 8086/8088) Arquitetura de 16, 32 e/ou 64 bits x86-16 (16 bits) x86-32/IA-32 (32 bits) x86-64/EM64T, Intel64, AMD64 (64 bits) Inicialmente CISC (Complex Instruction Set Computer), mas atualmente os processadores são híbridos de RISC e CISC Tamanho de instrução variável Palavras são armazenadas na memória em ordenação little-endian Código objeto criado para os processadores de 1978 ainda executam nos últimos processadores da arquitetura x86 Arquitetura x86 7 Diagrama de blocos do 8086 Arquitetura x86 8 Modos de operação Modo real Modo protegido e “modo” virtual-8086 Modelos de memória No modelo flat, a memória é vista como um simples e contínuo espaço de endereçamento. Código, dados e pilha estão todos contidos nesse espaço No modelo segmentado, a memória é, no contexto de um programa, um grupo de espaços de endereçamento independentes chamados de segmentos. Para endereçar um byte em um segmento, o programa utiliza um endereço lógico composto de um seletor de segmento e um deslocamento (offset) nesse segmento O modelo de memória utilizado no modo real, que é o modelo do 8086, é uma implementação específica do modelo segmentado onde cada segmento tem o mesmo tamanho, com tamanho máximo de até 64KB Modos de operação vs. Modelo de memória Arquitetura x86 9 Registradores do 8086 AX: acumulador para operandos e resultados BX: ponteiro para dados no segmento DS CX: contador para operações com string e loops DX: ponteiro para E/S SI: ponteiro para origem em operações com strings DI: ponteiro para destino em operações com strings SP: ponteiro de pilha, no segmento SS BP: ponteiro base de dados na pilha, no segmento SS IP: ponteiro de instrução no segmento CS CS: segmento de código DS: segmento de dados ES: segmento extra SS: segmento de pilha Arquitetura x86 10 Registrador de flags Arquitetura x86 11 Modos de endereçamento do 8086 Os modos de endereçamento para os processadores de 16 bits da arquitetura x86 podem ser resumidos pela seguinte fórmula: Arquitetura x86 12 Tipos de dados fundamentais Arquitetura x86 13 Organização dos dados na memória Arquitetura x86 14 Tipos de dados numéricos Ferramentas de programação 15 Editor de Texto MS-DOS Editor, vi, emacs, Microsoft Bloco de Notas, Notepad++ Assembler TASM: Borland Turbo Assembler MASM: Microsoft Macro Assembler NASM: The Netwide Assembler Linker TLINK: Borland Turbo Link LINK: Microsoft Overlay Linker ld: The GNU linker Debugger TD: Borland Turbo Debugger DEBUG: Debug do Microsoft DOS/Windows Processo de programação 16 Passo 1: criação de um ou mais arquivos de texto contendo mnemônicos e diretivas (arquivo geralmente com a extensão .asm, ou .inc em caso de include) Passo 2: montagem através de um assembler, que produzirá, em caso de sucesso, um ou mais arquivos objeto (extensão .obj) edit relogio.asm tasm relogio[.asm] Passo 3: ligação (linkedição) dos vários arquivos objeto com eventuais bibliotecas (extensão .lib) que produzirá o executável final (arquivo .exe ou .com) tlink relogio[.obj] Diretivas do Turbo Assembler 17 .8086P Habilita montagem das instruções do processador 8086 e do coprocessador numérico 8087. Este é o modo de instruções padrão do Turbo Assembler. END [expressão] Delimita o fim de um módulo. Todo o conteúdo após essa diretiva é ignorado. O argumento expressão, se presente, indica o ponto de entrada do programa: o endereço em que o programa inicia sua execução. Um programa pode conter vários módulos, mas apenas um deve definir um ponto de entrada (e este módulo é chamado de módulo principal). Diretivas do Turbo Assembler 18 .MODEL modelo Configura o modelo de memória para diretivas de segmentação simplificadas. Os modelos de memória definem os limites de código e dados do seu programa. Eles determinam quando o Assembler deve considerar referências para dados e códigos como NEAR ou FAR. Você deve utilizar a diretiva MODEL antes de utilizar as diretivas de segmento simplificadas como .CODE e .DATA. Modelos clássicos: TINY (dados e código combinados em um único grupo, utilizado para binários .COM); SMALL (código contido em um único segmento, modelo mais comum de binários do tipo .EXE) Diretivas do Turbo Assembler 19 .CODE .DATA Inicia ou continua o segmento de código do módulo, quando utilizada com a diretiva .MODEL Define o início do segmento de dados inicializado do módulo .STACK [tamanho] Inicia ou continua um segmento de pilha. O parâmetro opcional tamanho indica a quantidade de pilha para reservar, em bytes. O valor padrão é 400h (1024 bytes). Diretivas do Turbo Assembler 20 ORG expressão Define o contador de posição do Assembler como sendo o valor de expressão. É possível utilizar a diretiva ORG para associar um LABEL com um endereço absoluto específico. INCLUDE arquivo Faz com que o Assembler processe código-fonte de um arquivo externo. Pode ser utilizado para incluir uma biblioteca de MACROS ou PROCEDURES. Diretivas do Turbo Assembler 21 nome EQU valor Permite associar um valor numérico a um nome nome DB expressão [,expressão] ... nome DW expressão [,expressão] ... nome DD expressão [,expressão] ... Aloca um Byte/Word/Doubleword e associa um nome que pode ser utilizado posteriormente para referenciar o dado. Em todos os casos, expressão pode ser ? (uma interrogação, que inicializa o dado com valores indeterminados), DUP (uma expressão duplicada, que repete a operação de alocação através de um contador), uma constante com faixa de valores de acordo com o tamanho, uma expressão relativa. Exemplo: mensagem DB 'Pressione qualquer tecla para continuar. . . $' Diretivas do Turbo Assembler 22 PROC nome ENDP [nome] Define uma macro que será expandida depois quando o seu nome é encontrado ENDM [nome] Marca o fim de uma procedure (PROC) MACRO nome Define o início de uma procedure e associa um nome Termina uma definição de macro @data Segmento que DS deve assumir quando é utilizada a diretiva .DATA Instruções da arquitetura x86 23 AAA: ASCII Adjust After Addition Sintaxe: AAA Função: ajusta a soma de dois valores BCD não-compactados para criar um BCD não-compactado em AX. Exemplo: MOV AH,00H ;AH=0 MOV AL,33H ;AL = '3' MOV DL,38H ;DL = '8' ADD AL,DL ; AL = 6B AAA ; AH = 1, AL = 1 AAD: ASCII Adjust AX Before Division AAM: ASCII Adjust AX After Multiply AAS: ASCII Adjust AL After Subtraction Instruções da arquitetura x86 24 ADC: adição com carry flag Sintaxe: ADC memória|registrador, memória|registrador|imediato Função: adiciona o primeiro operando, o segundo e o carry flag armazenando o resultado no primeiro operando. Exemplo: MOV AH,30H ; AH = 30H STC ; CF = 1 ADC AH,00H ; AH = 31H ADD: adição Sintaxe: ADD memória|registrador, memória|registrador|imediato Função: adiciona o primeiro operando e o segundo, armazenando o resultado no primeiro operando. Exemplo: ADD AX,AX ; AX = AX+AX Instruções da arquitetura x86 25 AND: AND lógico Sintaxe: AND memória|registrador, memória|registrador|imediato Função: efetua um AND lógico bit-a-bit e armazena o resultado no primeiro operando. Exemplo: MOV DL,39H ; DL = '9' AND DL, 0FH ; DL = 9 CALL: chama função ou subrotina Sintaxe: CALL memória|registrador|imediato Função: chamada de função ou subrotina. Armazena [CS]:IP na pilha para o retorno através da instrução RET. CBW: converte byte para word Sintaxe: CBW Função: converte o byte em AL em uma word em AX, considerando o bit de sinal em AL Instruções da arquitetura x86 26 CLI: limpa flag de interrupção Sintaxe: CLI Função: zera o flag de interrupção (IF = 0, "desativa" interrupções) CMP: compara dois operandos Sintaxe: CMP memória|registrador, memória|registrador|imediato Função: efetua a subtração operando1-operando2, descartando o resultado porém ativando os flags. Exemplo: CMP DL,39H ; DL - 39H JZ fim ; JUMP se o flag de zero estiver ativado (i.e. se DL == 39H) CWD Sintaxe: CWD Função: converte a word em AX em uma doubleword em DX:AX, considerando o bit de sinal em AX Instruções da arquitetura x86 27 DEC: decremento de 1 Sintaxe: DEC registrador|memória Função: efetua um decremento de uma unidade do operando e salva o resultado no operando. Exemplo: MOV DL,0AH ; DL = 0AH DEC DL ; DL = 09H DIV: divisão sem sinal Sintaxe: DIV registrador|memória Função: se o operando é de 8 bits, AL = AX / operando e AH = AX % operando, e se é de 16 bits, AX = DX:AX / operando e DX = DX:AX % operando. Com resultados sem tratar o bit MSB como sendo de sinal IDIV: divisão com sinal Sintaxe e função iguais as da instrução DIV, porém considera o bit MSB como sendo de sinal Instruções da arquitetura x86 28 IMUL: multiplicação com sinal Sintaxe e função iguais as da instrução MUL, porém considera o bit MSB como sendo de sinal IN: entrada de dados de uma porta Sintaxe: IN AL|AX, DX|imediato de 8 bits Função: efetua a leitura de um byte (resultado em AL) ou word (resultado em AX) de uma porta INC: incremento de 1 Sintaxe: INC registrador|memória Função: efetua um incremento de uma unidade do operando e salva o resultado no operando. Exemplo: MOV DL,0FFH ; DL = FFH INC DL ; DL = 00H Instruções da arquitetura x86 29 INT: gera uma chamada de interrupção Sintaxe: INT imediato de 8 bits Função: PUSHF, PUSH CS, PUSH IP IRET: retorno de uma rotina de tratamento de interrupção Sintaxe: IRET Função: efetua POP IP, POP CS e POPF nesta ordem JXX|JNXX: deslocamento condicional Sintaxe: JXX|JNXX deslocamento imediato de 8 bits Função: efetua um deslocamento (salto) para execução de uma instrução em outra posição de memória O próprio assembler irá calcular o deslocamento, quando necessário, baseado na posição atual $. Como o operando é de 8 bits, o deslocamento está limitado entre -127 e 128 bytes. Instruções da arquitetura x86 30 JXX|JNXX: deslocamento condicional JA: jump se acima (operador > sem sinal) JAE: jump se maior ou igual (operador >= sem sinal) JB: jump se abaixo (operador < sem sinal) JBE: jump se abaixo ou igual (operador <= sem sinal) JE: jump se igual (operador ==) JG: jump se maior (operador > com sinal) JGE: jump se maior ou igual (operador >= com sinal) JL: jump se menor (operador < com sinal) JLE: jump se menor ou igual (operador <= com sinal) Os deslocamentos também podem ser a negação dos acima vistos (i.e. JNA, JNAE, JNB, JNBE, JNE, JNG, JNGE, JNL, JNLE Instruções da arquitetura x86 31 JMP: deslocamento incondicional Sintaxe: JMP deslocamento imediato|memória 16 bits|segmento:offset Função: efetua o deslocamento (salto) para execução de uma instrução em outra posição de memória LEA: Load Effective Address Sintaxe: LEA registrador 16 bits, referência na memória Função: armazena no registrador o offset da referência na memória. Exemplo: MOV BX,4040H ; BX = 4040H LEA SI,[BX+2] ; SI = 4042H MOV: mover dados Sintaxe: MOV registrador|memória, registrador|memória|imediato Função: copiar o segundo operando para o primeiro operando. Exemplo: MOV [BP+10H], CL Instruções da arquitetura x86 32 MUL: Multiplicação sem sinal Sintaxe: MUL registrador|memória Função: se o operando é de 8 bits, AL = AX / operando e AH = AX % operando, e se é de 16 bits, AX = DX:AX / operando e DX = DX:AX % operando. Com resultados sem tratar o bit MSB como sendo de sinal NEG: negação pelo complemento de 2 Sintaxe: NEG registrador|memória Função: utilizada para tornar o operando negativo (i.e. operando = zero – operando). Exemplo: MOV AH,02H ; AH = 02H NEG AH ; AH = 0FEH NOP: código para não-operação Sintaxe: NOP Função: utilizada para não executar nenhuma operação Instruções da arquitetura x86 33 NOT: negação pelo complemento de 1 Sintaxe: NOT registrador|memória Função: utilizada para inverter os bits do operando (i.e. negação lógica). Exemplo: MOV AL, 0FH ; AL = 0FH NOT AL ; AL = 0F0H OR: operação de OR lógico Sintaxe: OR registrador|memória, registrador|memória|imediato Função: efetua um OR lógico bit-a-bit entre o primeiro operando e o segundo, armazenando o resultado no primeiro operando. Exemplo: MOV CL, 02H ; CL = 02H OR CL, 01H ; CL = 03H Instruções da arquitetura x86 34 OUT: escreve um valor em uma porta de E/S Sintaxe: OUT DX|imediato, AX|AL Função: escreve um byte (segundo operando AL) ou uma word (segundo operando AX) em uma porta de E/S PUSH: salva uma word na pilha Sintaxe: PUSH registrador|memória Função: salva o operando de 16 bits na pilha (SP = SP-2, SS:[SP] = operando) POP: restaura uma word da pilha Sintaxe: POP registrador|memória Função: restaura para o operando o valor de 16 bits do topo da pilha (operando = SS:[SP], SP = SP+2) PUSHF: salva o registrador de flags na pilha POPF: restaura o registrador de flags da pilha Instruções da arquitetura x86 35 RET: retorno de uma subrotina ou função Sintaxe: RET Função: copia a word do topo da pilha para o registrador IP, geralmente utilizada para retornar de uma subrotina chamada através de CALL SHL: efetua o deslocamento dos bits para esquerda Sintaxe: SHL registrador|memória, 1|CL Função: efetua os deslocamentos de bits para a esquerda indicados no segundo operando. Exemplo: MOV AH,01H ; AH = 01H MOV CL,02H ; CL = 02H SHL AH,CL ; AH = 04H SHR: efetua o deslocamento dos bits para direita Sintaxe e função idênticas a SHL, alterando apenas o sentido do deslocamento Instruções da arquitetura x86 36 SBB: subtração com empréstimo (carry) Sintaxe: SBB registrador|memória, registrador|memória|imediato Função: efetua subtração entre o primeiro operando e o segundo operando mais o carry, armazenando o resultado no primeiro operando STI: ativa o flag de interrupção (IF=1) Sintaxe: STI Função: ativa o flag de interrupção, ativando assim as interrupções SUB: subtração Sintaxe: SUB registrador|memória, registrador|memória|imediato Função: efetua subtração entre o primeiro operando e o segundo, armazenando o resultado no primeiro operando. Exemplo: MOV AX,0002H ; AX = 02H SUB AX,0002H ; AX = 00H, ZF = 1 Instruções da arquitetura x86 37 TEST: comparação lógica Sintaxe: TEST registrador|memória,registrador|memória|imediato Função: efetua um AND lógico bit-a-bit entre o primeiro operando e o segundo, ativando os devidos flags mas descartando o resultado. Exemplo: MOV AX,0000H ; AX = 0H, ZF = 0 TEST AX,AX ; AX = 0H, ZF = 1 XOR: efetua o XOR lógico Sintaxe: XOR registrador|memória,registrador|memória|imediato Função: efetua um XOR lógico bit-a-bit entre o primeiro operando e o segundo, armazenando o resultado no primeiro operando. Exemplo: XOR AX,AX API do MS-DOS e BIOS IBM PC 38 INT 10H: funções da BIOS para o vídeo Função 00H: altera o modo de vídeo Função 0FH: obtém o modo de vídeo atual Função 01H: altera o tamanho do cursor Função 02H: altera a posição do cursor Função 03H: altera a posição e o tamanho do cursor Função 05H: altera a página do vídeo ativa Função 08H: obtém caractere e atributo na posição do cursor API do MS-DOS e BIOS IBM PC 39 INT 16H: interrupção do teclado Função 00: lê um caractere do teclado Chamada: AH = 00H Retorna: AH = scan code, AL = caractere ASCII Função 01: obtém o status do teclado Chamada: AH = 01H Retorna: ZF = 1 se não tem um caractere disponível para leitura Retorna: ZF = 0, AH = scan code, AL = caractere ASCII caso contrário Função 02: obtém os flags do teclado Chamada: AH = 02H Retorna: AL = flags (armazenados em 0000:0417H) API do MS-DOS e BIOS IBM PC 40 INT 20H: termina processo Chamada: CS = endereço de segmento do PSP Retorna: nada Outros métodos para terminar processo no MS-DOS: INT 21H, função 00H INT 21H, função 31H INT 21H, função 4CH INT 27H As funções 31H e 4CH da INT 21H são os métodos mais utilizados, pois permitem que um código de retorno seja passado para o processo pai API do MS-DOS e BIOS IBM PC 41 INT 21H: API do MS-DOS Função 01H: entrada de caractere com echo Chamada: AH = 01H Retorna: AL = código ASCII do caractere Função 02H: saída de caractere Chamada: AH = 02H, DL = código ASCII do caractere Função 07H: entrada de caractere sem echo Chamada: AH = 07H Retorna: AL = código ASCII do caractere Função 08H: entrada de caractere sem echo (CTRL+C) Chamada: AH = 08H Retorna: AL = código ASCII do caractere API do MS-DOS e BIOS IBM PC 42 INT 21H: API do MS-DOS Função 09H: exibe string Chamada: AH = 09H, DS:DX = segmento:offset da string Retorna: nada Função 0AH: entrada bufferizada do teclado Chamada: AH = 0AH, DS:DX = segmento:offset do buffer Retorna: dados armazenados no buffer Obs: o primeiro byte do buffer deve conter seu tamanho máximo Função 4CH: termina processo com código de retorno Chamada: AH = 4CH, AL = código de retorno Retorna: nada (memória é liberada, arquivos são fechados, etc.) Exemplos de código-fonte 43 Exemplo clássico de entrada e saída Relogio residente Calculadora em ponto flutuante Dump de uma área da memória Tabela de dados na memória Referências 44 STALLINGS, W. Arquitetura e Organização de Computadores, 8ed. Pearson Education, 2009. DUNCAN, R. Advanced MS-DOS Programming. Microsoft Press, 1986. IA-32 Intel Architecture Software Developer’s Manual. Intel Press, 2005. HYDE, R. Art of Assembly Language Programming and HLA. No Starch Press, 2003. http://en.wikipedia.org/wiki/X86_assembly_language Turbo Assembler Help. Borland, 1996. Perguntas e Discussão 45 Dúvidas mailto:[email protected]