◇ OpenCyber Academy
Engenharia · Capítulo 4 · Português

Capítulo 4 — Programação I

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Capítulo 4 — Programação I

Mapa da aula

Aula 04 publicada. Mapa, exemplo, EX010.

flowchart TD
  N0["4.1 Por que programar"]
  N1["4.2 Lendo em blocos"]
  N2["4.3 hashlib"]
  N3["Laboratório — hash em blocos e verificação"]
  N4["4.4 Lendo o resultado"]
  N5["4.5 Bons hábitos"]
  N6["4.6 Testando seu script"]
  N7["Escolhendo o tamanho do bloco"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX010"]

Desenho lógico

flowchart LR
  N0["4.1 Por que programar"]
  N1["4.2 Lendo em blocos"]
  N2["4.3 hashlib"]
  N3["Laboratório — hash em blocos e verificação"]
  N4["4.4 Lendo o resultado"]
  N5["4.5 Bons hábitos"]
  N6["4.6 Testando seu script"]
  N7["Escolhendo o tamanho do bloco"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX010"]
[4.1 Por que programar]
    |
    v
[4.2 Lendo em blocos]
    |
    v
[4.3 hashlib]
    |
    v
[Laboratório — hash em blocos e verificação]
    |
    v
[4.4 Lendo o resultado]
    |
    v
[4.5 Bons hábitos]
    |
    v
[4.6 Testando seu script]
    |
    v
[Escolhendo o tamanho do bloco]
    |
    v
[Exemplo resolvido] --> [Exercício EX010]

Objetivos de aprendizagem

Você vai (1) escrever Python simples com a biblioteca padrão; (2) calcular hashes de dados em blocos; (3) verificar uma cópia contra um hash de aquisição; (4) mostrar que a mudança de um único bit é detectada.

4.1 Por que programar

Os peritos usam ferramentas validadas, mas os engenheiros constroem e testam os fluxos em torno delas. A primeira tarefa que Hugo dá a Priya: escrever um script que verifique uma cópia de evidência sem carregá-la inteira na memória. Carregar uma imagem de 500 GB na memória é impossível numa estação de trabalho comum, e copiá-la antes dobra tanto o armazenamento quanto o tempo. Por isso, a tarefa ensina o processamento em fluxo: ler um pedaço, processá-lo, descartá-lo. O mesmo padrão está por trás da indexação, do carving e da comparação com conjuntos de hashes, de modo que o primeiro script é um modelo para boa parte do trabalho posterior do curso.

4.2 Lendo em blocos

Imagens grandes não cabem na memória. Ler blocos de 4 KiB ou maiores e alimentar cada um num objeto de hash dá o mesmo resumo que calcular o hash do arquivo inteiro de uma vez. As funções de hash são projetadas para que alimentar os dados em pedaços dê exatamente o mesmo resultado que alimentá-los de uma vez, desde que os pedaços cheguem em ordem e nenhum seja pulado. O laboratório abaixo alimenta seu arquivo de 95.000 bytes em blocos de 4.096 bytes: 23 blocos completos e um bloco final de apenas 792 bytes. Um laço que descartasse esse último pedaço curto ainda imprimiria um resumo de aparência plausível, e é por isso que o laboratório compara o resultado em blocos com um hash do arquivo inteiro.

4.3 hashlib

O módulo hashlib do Python oferece o SHA-256. O método update() absorve bytes; hexdigest() devolve o resultado como texto hexadecimal. O objeto devolvido por hashlib.sha256() mantém estado interno entre as chamadas de update(), por isso um objeto por arquivo é essencial; reutilizá-lo misturaria dois arquivos num só resumo. Priya também imprime o nome do algoritmo, a partir do atributo name do objeto, ao lado de cada resumo, para que um relatório nunca mostre uma sequência hexadecimal solta cujo algoritmo o leitor precise adivinhar. Chamar hexdigest() não reinicia o objeto, por isso ele é chamado uma vez, depois do último bloco.

Laboratório — hash em blocos e verificação

"""Hash a synthetic evidence file in chunks and verify it (stdlib only)."""
import hashlib
data = b"SYNTHETIC-EVIDENCE-" * 5000
h = hashlib.sha256()
for i in range(0, len(data), 4096):
    h.update(data[i:i+4096])
acq = h.hexdigest()
print("bytes:", len(data))
print("acquisition sha256:", acq[:16] + "...")
print("verify copy:", "MATCH" if hashlib.sha256(data).hexdigest() == acq else "DIFFER")
bad = bytearray(data); bad[1000] ^= 1
print("verify altered copy:", "MATCH" if hashlib.sha256(bad).hexdigest() == acq else "DIFFER")

Execute com python3 lab_hash.py. Saída verificada:

bytes: 95000
acquisition sha256: d260c00b9c859434...
verify copy: MATCH
verify altered copy: DIFFER

4.4 Lendo o resultado

O arquivo sintético de 95.000 bytes tem seu hash calculado em blocos na aquisição. Calcular o hash da cópia inteira dá o mesmo resumo: MATCH. Inverter um único bit no deslocamento 1000 produz um resumo completamente diferente: DIFFER. Essa sensibilidade é a razão pela qual valores de hash são usados para demonstrar integridade, como a ISO/IEC 27037 descreve para a preservação de evidências digitais. Vale repetir o teste do bit invertido em deslocamentos diferentes: o primeiro byte, o último byte e um byte situado na fronteira entre dois blocos. Um defeito na divisão em blocos apareceria mais provavelmente numa fronteira, de modo que um teste que só muda o deslocamento 1000 poderia não detectá-lo. Priya acrescenta os três casos ao seu arquivo de testes e guarda as entradas sintéticas junto com o script.

4.5 Bons hábitos

Use nomes claros, imprima o que foi comparado, nunca sobrescreva arquivos de entrada e mantenha os scripts sob controle de versão, para que o código exato usado possa ser apresentado mais tarde. Imprimir o que foi comparado significa que a saída nomeia os dois arquivos, os dois resumos e o veredito, e não apenas "OK". Um revisor que leia a saída meses depois deve conseguir dizer qual cópia foi conferida contra qual original. O controle de versão permite que Priya marque com uma tag o commit usado em cada caso, de modo que o script que produziu um resultado possa ser recuperado exatamente, mesmo depois de melhorias posteriores.

4.6 Testando seu script

Antes de confiar no script com evidências, teste-o com entradas cujas respostas são conhecidas, incluindo um arquivo vazio. O arquivo vazio é um caso-limite clássico: seu SHA-256 é uma constante bem conhecida, e um script que trava ou não imprime nada para ele tem um defeito. Outros testes úteis são um arquivo com exatamente um bloco, um arquivo com um byte a mais e um arquivo que o script não tem permissão para ler. Cada teste tem um resultado esperado anotado antes de ser executado.

Escolhendo o tamanho do bloco

O tamanho do bloco afeta a velocidade, não o resumo. Blocos muito pequenos desperdiçam tempo com chamadas de função; blocos muito grandes consomem memória. Priya mede alguns tamanhos em arquivos sintéticos e escolhe 1 MiB, documentando por quê. Um revisor pode refazer a medição e chegar à mesma escolha.

Exemplo resolvido

A primeira versão de Priya abria o arquivo em modo texto, o que pode alterar as quebras de linha em alguns sistemas e estragar os hashes. Hugo aponta o problema; ela passa para o modo binário e acrescenta um teste que compara seu resultado com o de uma segunda ferramenta.

Caso sintético. Os dados são gerados pelo script do laboratório.

Erros e limites

Um hash coincidente mostra que a cópia é igual ao que teve seu hash calculado na aquisição; não mostra que a própria aquisição foi completa.

Exercícios

Síntese

O hash em blocos no modo binário, testado com entradas conhecidas, é a ferramenta básica da integridade das evidências.

Continue estudando — capítulo 04 de 50

← Capítulo 03 (rascunho) · Índice de capítulos do curso · Capítulo 05 (rascunho) →