Assembly (x86)
Assembly vs Assembler
Antes de começarmos vamos esclarecer um ponto, a diferença entre Assembly e Assembler.
O Assembly é uma linguagem de programação que irá ser escrito de uma forma que seja entendível por humanos e, então, será convertido para linguagem de máquina através do Assembler. Esses arquivos desenvolvidos em Assembly irá conter a extensão .asm
.
Já o Assembler tem um papel diferente, pois este converte código desenvolvido em linguagem Assembly para linguagem de máquina. Após utilizamos o Assembler, irá ser gerado um Object File
, que é uma representação binária do programa. Feito, será preciso utilizar um linker
para combinar (juntar) os arquivos necessários para gerar o executável final (isso é necesário quando temos chamadas externas, por exemplo, de arquivos .dll
situadas na system32
).
Compilando em Assembly
Primeiro precisamos escrever um código asm
(Assembly), transformar em arquivo obj
(linguagem de máquina) e por último fazer um linker
(arquivo exe), para associar o código com as bibliotecas, como por exemplo, o arquivo User32.dll, presente na system32.
Caso utilize algum recurso, como por exemplo o sleep
(que utiliza Kernel32.dll) iremos ter que adicionar a algumas informações a mais no comando.
Registradores
Os processadores têm seu próprio conjunto de variáveis especiais chamadas Registradores
. A maioria das instruções usam esses Registros para ler ou gravar dados, portanto é essencial entender os Registradores de um processador para então entender as Instruções
(mais detalhes logo abaixo).
REGISTRADOR
DESCRIÇÃO
EAX
Utilizado em operações aritméticas e guardar resultados. É o registrador acumulador
EBX
É um Registrador secundário, usado como "base" em operações de vetores
ECX
Contador utilizado em Loops
EDX
Guarda endereço de dados
ESP
Este fica sempre no topo da Stack e é conhecido como Stack Pointer
, é chamado Ponteiro porque armazenam endereços de 32 bits, que apontam essencialmente para esse local na memória. Esse registro é bastante importante para a execução de programas e gerenciamento de memória
EBP
Conhecido como Base Pointer
e assim como o ESP, este também é chamado Ponteiro porque armazenam endereços de 32 bits
ESI
Conhecido como Source Index
, tecnicamente também é um Ponteiro que é comumente utilizado para apontar para a origem e o destino quando os dados precisam ser lidos ou gravados
EDI
Conhecido como Destination Index
, tecnicamente também é um Ponteiro, pois trabalha de forma semelhante ao ESI.
EIP
Instruction Pointer
(Program Counter), indica em qual instrução do código a execução se encontra no momento
EFLAGS
Guarda resultado de operações, por exemplo, condições feita em um if
, onde o resultado dessa condição não é armazenada em nenhuma variável
Ao realizarmos um Disassembler, podemos ver os Registradores divididos em partes, ou seja, utilizando somente uma parte dele. Isso é feito por questões de performance do software. Podemos utilizar como exemplo, o Registrador rax
(64 bits), que pode tem uma parte chamada eax
(32 bits), que tem uma parte chamada ax
(16 bits), que tem duas partes chamadas de ah
(8 bits - memória alta) e al
(8 bits - memória baixa).
Diferenças Entre Intel e AT&T
Ordem dos Parâmetros
Quando utilizamos o mov
, para mover algo, temos uma pequena e importante diferença nessas sintaxes. No exemplo abaixo, ambos os casos ebp
irá receber o valor de esp
, porém note que eles estão escritos em ordem diferente:
Tamanho dos Parâmetros
Na Intel, o Assembler identifica o tamanho do registrador que foi utilizado, já na AT&T podemos ver sufixos que indicam o tamanho dos operandos (note que é um pushl
e não push
).
Valores Imediatos
Estes, são valores que não estão armazenadas em variáveis. Podemos usar um exemplo de Valor Imediato no seguinte trecho, onde a string está sendo definida sem passar por uma variável:
Na Intel, o Assembler identiica o tipo automaticamente, enquanto na AT&T possui um $
como prefixo, enquanto os registradores possuem um %
.
Instruções
Movimentos
Antes de vermos as Instruções de fato, vamos entender quais são essas movimentação de dados.
MOVIMENTO
DESCRIÇÃO
Imediato para Registrador
Acontece quando setamos o valor de uma variável, exemplo: x = 10;
Registrador para Registrador
Quando passamos o valor de eax
para ebx
Imediato para Memória
Quando defimos que vamos escrever em determinada posição da Memória RAM
Registrador para Memória / Memória para Regitrador
Quando movemos um valor da Registrador para a Memória RAM
Tipos de Instruções
Agora podemos ver e entender melhor sobre as instruções.
INSTRUÇÃO
EXEMPLO
DESCRIÇÃO
NOP
NOP
No Operation (\x90), o processador não faz nada. Útil pra utilizar antes da chamada de shellcode
INT3
Interrupção (Breakpoint)
MOV
mov ecx,eax
mov eax, [esp]
Define o valor de ecx
com o mesmo valor de eax
. No segundo exemplo, joga valor (e não o endereço de memória) de ESP para EAX
ADD
add eax,0x10
Incrementa um valor (imediato) hexadecimal de 10 em eax
SUB
sub eax,0x10
Subtrai 0x10 de eax
e armazena o valor em eax
XOR
xor eax,ebx
xor eax,eax
Faz o xor
entre eax
e ebx
e joga o valor em eax
. No segundo exemplo, teremos eax
com o valor zerado
AND
and eax,ebx
Faz um and
entre eax
e ebx
e joga o valor em eax
CMP
cmp eax,ebx
Compara eax
e ebx
e seta flags
JE
je 0x40084
Jump Equal = Pula para o endereço informado se a eflag
estiver setado como igual na instrução cmp
JNE
jne 0x40084
Jump Not Equal = Semelhante ao JE
, porém este irá mudar de endereço caso a instrução cmp
não seja igual
JMP
jmp 0x40084
Jump = Pula para o endereço informado, independente de condições em CMP
CALL
call 0x40084
Pula para o endereco definido e salva a sua localização atual
RET
ret
Retorna para o endereço salvo com a instrução call
LEA
lea eax,[esp+0x1c]
Semelhante ao mov
, porém ao invés de mover o valor, esse move o endereço de um registrador para outro
PUSH
PUSH 41424344
Coloca no topo da Stack
POP
POP EBX
Remove do topo da stack e joga o valor para EBX
Compilando
Primeiro precisamos escrever um código asm
(Assembly), transformar em arquivo obj
(linguagem de máquina) e por último fazer um linker
para gerar o arquivo exe e associar o código as devidas bibliotecas, como por exemplo, o arquivo User32.dll
, presente no system32
.
No exemplo acima não utilizamos nenhuma biblioteca externa. No exemplo abaixo, estamos compilando um código que utiliza o Sleep, presente em Kernel32.dll, então o comando ficaria da seguinte forma.
Permissões
Antes de começarmos, é sempre bom verificar as permissões que o binário possui
Disassemble
Objdump
Por padrão, será utilizado o AT&T.
Outra maneira
arwin
Busca pelo endereço de memória que está sendo utilizado para chamar determinada DLL.
Sites
Last updated