Bem-vindo à segunda parte da nossa série de artigos sobre Engenharia Reversa Básica! Link para o cabeçalho

██   ██  █████   ██████  
 ██ ██  ██   ██ ██       
  ███    █████  ███████  
 ██ ██  ██   ██ ██    ██ 
██   ██  █████   ██████  
                         

Agora que já exploramos os conceitos fundamentais de CPU no primeiro artigo, chegou a hora de entender como os dados são representados e manipulados internamente pelos computadores. Vamos falar sobre sistemas de numeração, com foco em hexadecimal, que é crucial para quem trabalha com engenharia reversa em baixo nível.

No nosso primeiro artigo, eu disse — Não se prenda a uma sequência infinita de “porquês!?”. E digo novamente: Não se preocupe em entender todos os detalhes de uma vez. O objetivo aqui é entender a essência dos conceitos e como cada um contribui para o funcionamento do sistema computacional. A ideia não é se perder em explicações técnicas, mas sim entender como tudo se encaixa, e isso irá ficar muito claro na prática futuramente.

Sistema Binário Link para o cabeçalho

O sistema binário é a linguagem dos computadores. é um sistema de numeração que utiliza apenas dois símbolos, como estados de interruptor: 0 -> desligado e 1 -> ligado. Ele é a base da computação, e razão para isso é que: os computadores, em seu nível mais profundo, funcionam com circuitos elétricos que podem estar em dois estados distintos: ligado ou desligado. O 0 e o 1 são a maneira de representar esses estados binários. Por debaixo dos panos, o sistema é isso:

0001010101011010101011100101010010101010...

Mas, obviamente, nós não temos contato direto com isso e nem vemos esse binário. Isso é processado pelas camadas de abstração, como: circuitos, CPU, sistema operacional e diversas outras partes que formam o computador, que tratam dos binários para nós.

Para a formação de caracteres e números, os bits se repetem, assim como em qualquer outro sistema numérico.

  • 1 bit pode representar 2 valores: 0 ou 1.
  • 2 bits podem representar 4 valores: 00, 01, 10 e 11.
  • 3 bits podem representar 8 valores: 000, 001, 010, 011, 100, 101, 110 e 111. E assim por diante…

No sistema operacional, tudo é representado por binários. Cada sequência binária pode representar algo específico: pode ser uma tecla de escape, uma tecla de espaço, uma entrada, uma saída, um número, uma letra, e por aí vai. Tudo tem um significado codificado em binário.

Você pode ver na tabela ASCII como as saídas de texto são representadas em binário.

  • Aqui está um exemplo dessa tabela.

Características do Sistema Binário Link para o cabeçalho

  • Base 2: O sistema binário é composto por dois símbolos: 0 e 1.
    • Assim como qualquer outro sistema numérico, como o decimal (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), o sistema binário usa dois dígitos. Com apenas esses dois símbolos, podemos representar números infinitos, basta reorganizá-los de maneiras diferentes.
  • Posicional: Cada dígito tem um valor que depende da sua posição na sequência. Isso ocorre porque o sistema é posicional e de base 2, ou seja, a posição de cada bit define seu valor.
  • Armazenamento de Dados: Discos rígidos, SSDs, memórias RAM e outros dispositivos de armazenamento utilizam o sistema binário para armazenar informações. Cada bit é uma cadeia, que representa dados, e esses dados são interpretados pelo sistema operacional.
  • Representação de Dados: Todos os dados e instruções em um computador são representados em binário. Isso inclui texto, imagens, vídeos, programas e qualquer outro tipo de informação. Até as instruções que a CPU executa são representadas em binário, e são interpretadas conforme o contexto.
  • Representação: No sistema operacional, ele é representado por 0b, ou seja: 0b1100110.

Conclusão Link para o cabeçalho

Embora o sistema binário seja fundamental para a computação, você provavelmente não precisará ter contato diretamente no seu dia a dia. No entanto, entender como ele funciona é importante, pois, no futuro, você pode precisar de algum conhecimento básico sobre binários para compreender outros conceitos relacionados à engenharia reversa.

Recursos Auxiliares Link para o cabeçalho

Hexadecimal Link para o cabeçalho

Bom, agora iremos falar sobre o Hexadecimal. Acredito que seja o sistema numérico mais utilizado pra quem faz engenharia reversa, ao lidarmos com endereços de memória e manipulação de dados de baixo nível, o sistema hexadecimal é muito mais compacto e legível do que o sistema binário.

O sistema hexadecimal é de base 16, o que significa que ele usa 16 símbolos diferentes para representar valores. Esses símbolos são:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

Números de 10 a 15 são representados por letras:

  • A = 10
  • B = 11
  • C = 12
  • D = 13
  • E = 14
  • F = 15
... 8, 9, A, B, C, D, E, F, 10, 11, 12...

10 equivale a 16 e assim por diante…

Como funciona a Numeração Hexadecimal Link para o cabeçalho

Assim como no sistema decimal, onde cada posição representa uma potência de 10, em hexadecimal a potência corresponde à posição de cada número no sistema hexadecimal.

Potências de 16 de 0 até 5: Link para o cabeçalho
(16⁰)
(16¹) (16²) (16³) (16⁴) (16⁵)
1 16 256 4.096 65.536 1.048.576

Vamos ao exemplo:
No número 2F (hexadecimal):

  • 2 ocupa segunda posição à esquerda:
    • 2 x 16¹ = 2 x 16 = 32
  • F ocupa a primeira posição à esquerda:
    • F representa 15 em decimal, então:
    • 15 x 16⁰ = 15 x 1 = 15

Portanto, 2F (hexadecimal) é igual a 32 + 15 = 47 em decimal.

Vamos a outro exemplo, o número: 0x543210

  • 5(16⁵) x 1.048.576 = 5.242.880
  • 4(16⁴) x 65.536 = 262.144
  • 3(16³) x 4.096 = 12.288
  • 2(16²) x 256 = 512
  • 1(16¹) x 16 = 16
  • 0(16⁰) x 1 = 0

A potência de cada número indica quantas vezes ele deve ser multiplicado pelo valor correspondente da posição.

Resultado Link para o cabeçalho

Agora, vamos somar esses valores para obter o número decimal de 0x543210:

5.242.880+262.144+12.288+512+16+0 = 5.517.840

Então, 0x543210 é igual a 5.517.840 em decimal.

Inserindo letras Link para o cabeçalho

podemos também inserir letras nos números hexadecimais. aprendemos acima que as letras A a F representam os valores de 10 a 15, portanto vamos usar o número 0xAB2345 como exemplo, e converter de hexadecimal para decimal:

Não se esqueça que: A =10 B = 11

  • A(16⁵) x 1.048.576 = 10.485.760
  • B(16⁴) x 65.536 = 720.896
  • 2(16³) x 4.096 = 8.192
  • 3(16²) x 256 = 768
  • 4(16¹) x 16 = 64
  • 5(16⁰)x 1 = 5
(AB2345)₁₆ = (10 × 16⁵) + (11 × 16⁴) + (2 × 16³) + (3 × 16²) + (4 × 16¹) + (5 × 16⁰) = (11215685)₁₀

somamos o resultado, e obtemos: 11.215.685. Portanto, 0xAB2345 é 11.215.685 em decimal. Legal, né?

Recursos Adicionais: Link para o cabeçalho

Hexadecimal nos Sistemas Operacionais Modernos Link para o cabeçalho

Bom, essa matemática toda por trás dos hexadecimais não será necessária, pois quem lida com isso é o sistema operacional. Mas há algumas coisas que você deve saber/lembrar quando for programar, fazer engenharia reversa ou coisas do tipo. Aqui vão elas:

  • Em um sistema operacional moderno, 1 número em hexadecimal equivale a um número binário de 4 dígitos (4 bits). Por exemplo:
1101 = D
0001 = 1
0110 = 6
0011 = 3
  • Nos sistemas modernos como windows e linux, usamos o prefixo 0x, por exemplo 0xD163

  • Endereços dinâmicos de memória são representados em hexadecimal.

intel-gen
  • Endereços estáticos/offsets de memória são representados por hexadecimal
intel-gen
  • Dados são representados em hexadecimal.
intel-gen
  • Quando formos aplicar engenharia reversa, iremos lidar com números hexadecimais.
intel-gen

Resumindo, o hexadecimal é muito importante, pois está presente em tudo.

x86 Byte, Word, Double Word Link para o cabeçalho

Bom, essas são as nomenclaturas para a quantidade de dados que um determinado segmento armazena.

Medida Tamanho (Intel) Nomenclatura Intel
Byte 8 bits BYTE
Word 16 bits WORD
Double Word 32 bits DWORD
Quad Word 64 bits QWORD

Quando formos programar ou analisar um source code de um malware, iremos lidar com essas nomenclaturas, principalmente ao lidarmos com a WINAPI. É importante, pois, por exemplo, se você declara uma variável do tamanho DWORD, você irá armazenar um valor de 32 bits:

DWORD procid = NULL;

Os registradores equivalem a esses tamanhos também. Veja:

  • AX = 16 bits (WORD)
  • EAX = 32 bits (DWORD)
  • RAX = 64 bits (QWORD)

Bom, a partir daqui é isso. O restante a gente aprende com a prática. Portanto pratique ao máximo, e esses conceitos irão entrar na sua mente.
Obrigado por ter lido até aqui!

 ▌ ▐·      ▪  ▪  ·▄▄▄▄  
▪█·█▌▪     ██ ██ ██▪ ██ 
▐█▐█• ▄█▀▄ ▐█·▐█·▐█· ▐█▌
 ███ ▐█▌.▐▌▐█▌▐█▌██. ██ 
. ▀   ▀█▄▀▪▀▀▀▀▀▀▀▀▀▀▀•