Instruções x86
Nas listas desta seção, as instruções marcadas com um asterisco (*) são particularmente importantes. Instruções não tão marcadas não são críticas.
No processador x86, as instruções são de tamanho variável, portanto, desmontar para trás é um exercício de correspondência de padrões. Para desmontar para trás de um endereço, você deve começar a desmontar em um ponto mais atrás do que realmente deseja ir e, em seguida, olhar para a frente até que as instruções comecem a fazer sentido. As primeiras instruções podem não fazer sentido porque você pode ter começado a desmontar no meio de uma instrução. Infelizmente, há a possibilidade de que a desmontagem nunca seja sincronizada com o fluxo de instruções e você terá que tentar desmontar em um ponto de partida diferente até encontrar um ponto de partida que funcione.
Para instruções de comutador bem empacotadas, o compilador emite dados diretamente no fluxo de código, portanto, desmontar por meio de uma instrução switch geralmente tropeçará em instruções que não fazem sentido (porque elas são realmente dados). Localize o final dos dados e continue desmontando lá.
Notação de instrução
A notação geral para instruções é colocar o registro de destino à esquerda e a origem à direita. No entanto, pode haver algumas exceções a essa regra.
Normalmente, as instruções aritméticas são de dois registros com os registros de origem e destino combinados. O resultado é armazenado no destino.
Algumas das instruções têm versões de 16 e 32 bits, mas apenas as versões de 32 bits estão listadas aqui. Não listados aqui estão instruções de ponto flutuante, instruções privilegiadas e instruções que são usadas apenas em modelos segmentados (que o Microsoft Win32 não usa).
Para economizar espaço, muitas das instruções são expressas em formato combinado, conforme mostrado no exemplo a seguir.
* |
MOV |
r1, r/m/#n |
r1 = r/m/#n |
significa que o primeiro parâmetro deve ser um registro, mas o segundo pode ser um registro, uma referência de memória ou um valor imediato.
Para economizar ainda mais espaço, as instruções também podem ser expressas conforme mostrado no seguinte.
* |
MOV |
r1/m, r/m/#n |
r1/m = r/m/#n |
o que significa que o primeiro parâmetro pode ser um registro ou uma referência de memória, e o segundo pode ser um registro, uma referência de memória ou um valor imediato.
A menos que indicado de outra forma, quando essa abreviação é usada, você não pode escolher memória para origem e destino.
Além disso, um sufixo de tamanho de bit (8, 16, 32) pode ser acrescentado à origem ou ao destino para indicar que o parâmetro deve ser desse tamanho. Por exemplo, r8 significa um registro de 8 bits.
Memória, Transferência de Dados e Conversão de Dados
As instruções de transferência de dados e memória não afetam os sinalizadores.
Endereço efetivo
* |
LEA |
r, m |
Carregar endereço efetivo. (r = endereço de m) |
Por exemplo, LEA eax, [esi+4] significa eax = esi + 4. Essa instrução geralmente é usada para executar aritmética.
Transferência de dados
MOV |
r1/m, r2/m/#n |
r1/m = r/m/#n |
|
MOVSX |
r1, r/m |
Mova-se com a extensão de sinal. |
|
* |
MOVZX |
r1, r/m |
Mover-se com extensão zero. |
MOVSX e MOVZX são versões especiais da instrução mov que executam a extensão de sinal ou a extensão zero da origem para o destino. Essa é a única instrução que permite que a origem e o destino sejam tamanhos diferentes. (E, na verdade, eles devem ter tamanhos diferentes.
Manipulação de pilha
A pilha é apontada pelo registro esp . O valor em esp é a parte superior da pilha (mais recentemente enviada por push, primeiro a ser exibida); Os elementos de pilha mais antigos residem em endereços mais altos.
PUSH |
r/m/#n |
Efetuar push do valor para a pilha. |
|
POP |
r/m |
Valor pop da pilha. |
|
PUSHFD |
Efetuar push de sinalizadores na pilha. |
||
POPFD |
Sinalizadores pop da pilha. |
||
PUSHAD |
Envie por push todos os registros inteiros. |
||
POPAD |
Pop de todos os registros inteiros. |
||
Enter |
#n, #n |
Criar quadro de pilha. |
|
* |
DEIXAR |
Derrubar o quadro de pilha |
O compilador C/C++ não usa a instrução enter . (A instrução enter é usada para implementar procedimentos aninhados em linguagens como Algol ou Pascal.)
A instrução de licença é equivalente a:
mov esp, ebp
pop ebp
Conversão de dados
CBW |
Converter byte (al) em word (ax). |
CWD |
Converter palavra (ax) em dword (dx:ax). |
CWDE |
Converter palavra (ax) em dword (eax). |
CDQ |
converter dword (eax) em qword (edx:eax). |
Todas as conversões executam a extensão de sinal.
Manipulação aritmética e de bits
Todas as instruções aritméticas e de manipulação de bits modificam sinalizadores.
Aritmética
ADD |
r1/m, r2/m/#n |
r1/m += r2/m/#n |
|
ADC |
r1/m, r2/m/#n |
r1/m += r2/m/#n + carry |
|
SUB |
r1/m, r2/m/#n |
r1/m -= r2/m/#n |
|
SBB |
r1/m, r2/m/#n |
r1/m -= r2/m/#n + carry |
|
NEG |
r1/m |
r1/m = -r1/m |
|
INC |
r/m |
r/m += 1 |
|
DEC |
r/m |
r/m -= 1 |
|
CMP |
r1/m, r2/m/#n |
Computação r1/m - r2/m/#n |
A instrução cmp calcula a subtração e define sinalizadores de acordo com o resultado, mas joga o resultado fora. Normalmente, ele é seguido por uma instrução de salto condicional que testa o resultado da subtração.
MUL |
r/m8 |
Machado = Al * r/m8 |
|
MUL |
r/m16 |
dx:ax = ax * r/m16 |
|
MUL |
r/m32 |
edx:eax = eax * r/m32 |
|
IMUL |
r/m8 |
Machado = Al * r/m8 |
|
IMUL |
r/m16 |
dx:ax = ax * r/m16 |
|
IMUL |
r/m32 |
edx:eax = eax * r/m32 |
|
IMUL |
r1, r2/m |
r1 *= r2/m |
|
IMUL |
r1, r2/m, #n |
r1 = r2/m * #n |
Multiplicação sem sinal e assinada. O estado dos sinalizadores após a multiplicação é indefinido.
DIV |
r/m8 |
(ah, al) = (ax % r/m8, ax / r/m8) |
|
DIV |
r/m16 |
(dx, ax) = dx:ax / r/m16 |
|
DIV |
r/m32 |
(edx, eax) = edx:eax / r/m32 |
|
IDIV |
r/m8 |
(ah, al) = ax / r/m8 |
|
IDIV |
r/m16 |
(dx, ax) = dx:ax / r/m16 |
|
IDIV |
r/m32 |
(edx, eax) = edx:eax / r/m32 |
Divisão sem sinal e assinada. O primeiro registro na explicação do pseudocódigo recebe o restante e o segundo recebe o quociente. Se o resultado estourar o destino, uma exceção de estouro de divisão será gerada.
O estado dos sinalizadores após a divisão é indefinido.
* |
SETcc |
r/m8 |
Definir r/m8 como 0 ou 1 |
Se a condição cc for verdadeira, o valor de 8 bits será definido como 1. Caso contrário, o valor de 8 bits será definido como zero.
Decimal codificado em binário
Você não verá essas instruções, a menos que esteja depurando o código escrito em COBOL.
DAA |
Ajuste decimal após a adição. |
|
DAS |
Ajuste decimal após a subtração. |
Essas instruções ajustam o registro al depois de executar uma operação decimal com código binário empacotado.
AAA |
Ajuste ASCII após a adição. |
AAS |
Ajuste ASCII após a subtração. |
Essas instruções ajustam o registro al depois de executar uma operação decimal codificada em binário descompactada.
AAM |
Ajuste asCII após a multiplicação. |
AAD |
Ajuste asCII após a divisão. |
Essas instruções ajustam os registros al e ah depois de executar uma operação decimal codificada em binário descompactada.
Bits
AND |
r1/m, r2/m/#n |
r1/m = r1/m e r2/m/#n |
|
OU |
r1/m, r2/m/#n |
r1/m = r1/m ou r2/m/#n |
|
XOR |
r1/m, r2/m/#n |
r1/m = r1/m xor r2/m/#n |
|
NOT |
r1/m |
r1/m = bit a bit não r1/m |
|
* |
TEST |
r1/m, r2/m/#n |
Computar r1/m e r2/m/#n |
A instrução de teste calcula o operador AND lógico e define sinalizadores de acordo com o resultado, mas joga o resultado fora. Normalmente, ele é seguido por uma instrução de salto condicional que testa o resultado do AND lógico.
SHL |
r1/m, cl/#n |
r1/m <<= cl/#n |
|
SHR |
r1/m, cl/#n |
r1/m >>= cl/#n preenchimento zero |
|
* |
SAR |
r1/m, cl/#n |
r1/m >>= cl/#n sign-fill |
O último bit deslocado é colocado no transporte.
SHLD |
r1, r2/m, cl/#n |
Shift double para a esquerda. |
Shift r1 à esquerda por cl/#n, preenchendo com os bits superiores de r2/m. O último bit deslocado é colocado no transporte.
SHRD |
r1, r2/m, cl/#n |
Shift double para a direita. |
Shift r1 para a direita por cl/#n, preenchendo com os bits inferiores de r2/m. O último bit deslocado é colocado no transporte.
ROL |
r1, cl/#n |
Gire r1 para a esquerda por cl/#n. |
ROR |
r1, cl/#n |
Gire r1 para a direita por cl/#n. |
RCL |
r1, cl/#n |
Gire r1/C à esquerda por cl/#n. |
RCR |
r1, cl/#n |
Gire r1/C para a direita por cl/#n. |
A rotação é como mudar, exceto que os bits deslocados para fora reaparecem como os bits de preenchimento de entrada. A versão da linguagem C das instruções de rotação incorpora o bit de transporte na rotação.
BT |
r1, r2/#n |
Copie o bit r2/#n de r1 para transporte. |
BTS |
r1, r2/#n |
Defina bit r2/#n de r1, copie o valor anterior para carry. |
BTC |
r1, r2/#n |
Limpe o bit r2/#n de r1, copie o valor anterior para carry. |
Fluxo de Controle
Jcc |
Dest |
Condicional de ramificação. |
|
JMP |
Dest |
Pule direto. |
|
JMP |
r/m |
Ir indireto. |
|
CALL |
Dest |
Chame direto. |
|
* |
CALL |
r/m |
Chamar indireto. |
A instrução de chamada envia o endereço de retorno para a pilha e, em seguida, salta para o destino.
* |
RET |
#n |
Retorno |
A instrução ret é exibida e salta para o endereço de retorno na pilha. Um #n diferente de zero na instrução RET indica que, depois de estourar o endereço de retorno, o valor #n deve ser adicionado ao ponteiro de pilha.
LOOP |
Decremente ecx e pule se o resultado for diferente de zero. |
LOOPZ |
Decremente ecx e jump se result for diferente de zero e zr foi definido. |
LOOPNZ |
Decremente ecx e jump se result for diferente de zero e zr estiver claro. |
JECXZ |
Pule se ecx for zero. |
Essas instruções são remanescentes do patrimônio cisc do x86 e, em processadores recentes, são, na verdade, mais lentas do que as instruções equivalentes escritas no longo caminho.
Manipulação de cadeia de caracteres
MOVST |
Mova T de esi para edi. |
|
CMPST |
Compare T de esi com edi. |
|
SCAST |
Examine T de edi para accT. |
|
LODST |
Carregue T de esi em accT. |
|
STOST |
Armazene T para edi do accT. |
Depois de executar a operação, o registro de origem e destino é incrementado ou decrementado por sizeof(T), de acordo com a configuração do sinalizador de direção (para cima ou para baixo).
A instrução pode ser prefixada pelo REP para repetir a operação o número de vezes especificado pelo registro ecx .
A instrução rep mov é usada para copiar blocos de memória.
A instrução rep stos é usada para preencher um bloco de memória com accT.
Sinalizadores
LAHF |
Carregue ah de sinalizadores. |
SAHF |
Armazene ah para sinalizadores. |
STC |
Defina carry. |
CLC |
Limpar transporte. |
CMC |
Complementar transporte. |
STD |
Defina a direção para baixo. |
CLD |
Defina a direção para cima. |
STI |
Habilitar interrupções. |
CLI |
Desabilitar interrupções. |
Instruções interligadas
XCHG |
r1, r/m |
Troque r1 e r/m. |
XADD |
r1, r/m |
Adicione r1 a r/m, coloque o valor original em r1. |
CMPXCHG |
r1, r/m |
Comparar e trocar condicionalmente. |
A instrução cmpxchg é a versão atômica do seguinte:
cmp accT, r/m
jz match
mov accT, r/m
jmp done
match:
mov r/m, r1
done:
Diversos
INT |
#n |
Interceptar para kernel. |
|
LIMITE |
r, m |
Interceptar se r não estiver no intervalo. |
|
* |
NOP |
Sem operação. |
|
XLATB |
al = [ebx + al] |
||
BSWAP |
r |
Trocar ordem de bytes no registro. |
Aqui está um caso especial da instrução int .
INT |
3 |
Interceptação de ponto de interrupção do depurador. |
O opcode para INT 3 é 0xCC. O opcode para NOP é 0x90.
Ao depurar o código, talvez seja necessário aplicar patch em algum código. Você pode fazer isso substituindo os bytes ofensivos por 0x90.
Idiomas
XOR |
r, r |
r = 0 |
|
TEST |
r, r |
Verifique se r = 0. |
|
* |
ADD |
r, r |
Shift r para a esquerda por 1. |