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
- EX010 — Resultado do laboratório. O que o laboratório relatou para a cópia e para a cópia alterada? Rubrica: 3 = MATCH, DIFFER, com a razão; 2 = só os resultados; 1 = errado; 0 = nada.
- EX011 — Defeito do modo texto. Por que abrir evidências em modo texto arrisca hashes errados? Rubrica: 3 = a tradução de quebras de linha/codificação altera bytes; 2 = parcial; 1 = mais lento; 0 = nada.
- EX012 — Limite do hash. O que um hash coincidente não prova? Rubrica: 3 = a completude da aquisição original; 2 = parcial; 1 = prova tudo; 0 = nada.
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) →