Por dentro dos formatos de arquivo binário: cabeçalhos de PNG, ZIP e Mach-O explicados
Todo formato de arquivo que você usa no dia a dia, imagem, arquivo compactado, executável, tem uma estrutura definida até o byte, e quase nada dessa estrutura fica visível pelas ferramentas que você normalmente usa para abrir esses arquivos. A Pré-Visualização te mostra uma imagem. O Utilitário de Arquivo te mostra arquivos extraídos. Nenhum deles te mostra os bytes que tornaram esses resultados possíveis. Aqui está o que três formatos comuns de fato parecem por baixo, e como examiná-los você mesmo.
PNG: uma sequência de chunks rotulados
Todo arquivo PNG começa com os mesmos oito bytes: 89 50 4E 47 0D 0A 1A 0A. Isso não é dado de imagem, é uma assinatura, escolhida de propósito para incluir um byte que fica inutilizável se o arquivo for transferido por acidente como texto (0x0A e 0x0D são caracteres de nova linha, então uma transferência quebrada em modo texto os corrompe de um jeito fácil de detectar).
Depois da assinatura, tudo em um PNG é um chunk, e todo chunk segue o mesmo layout de quatro partes:
- Comprimento (4 bytes): quantos bytes de dados vêm a seguir, sem contar o tipo nem o CRC.
- Tipo (4 bytes, sempre ASCII): um código de quatro letras como
IHDR(cabeçalho da imagem, sempre primeiro),IDAT(dados de pixel comprimidos, pode haver vários) ouIEND(marca o fim do arquivo). - Dados: o que aquele tipo de chunk especificar. Os dados do
IHDRsão a largura, a altura, a profundidade de bits e o tipo de cor, sempre no mesmo layout fixo. - CRC (4 bytes): um checksum do tipo e dos dados, para que um decodificador possa detectar um chunk corrompido em vez de lê-lo errado silenciosamente.
Esse é o formato inteiro. Um decodificador de PNG só percorre chunk após chunk, lendo o comprimento para saber quanto pular, até chegar no IEND.
ZIP: cabeçalhos locais, depois um diretório no final
Um arquivo ZIP guarda cada arquivo com seu próprio cabeçalho de arquivo local, marcado pela assinatura 50 4B 03 04 (o PK no início é uma referência a Phil Katz, o criador do formato). Esse cabeçalho inclui o nome do arquivo, o método de compressão e tanto o tamanho comprimido quanto o descomprimido, seguidos imediatamente pelos dados comprimidos do arquivo.
A parte que confunde as pessoas: esses tamanhos e checksums por arquivo no cabeçalho local às vezes são zero, com os valores reais guardados mais adiante em um “descritor de dados”, porque o ZIP foi projetado para suportar escrita em streaming, em que o tamanho final não é conhecido até os dados já terem sido escritos. A lista definitiva do que de fato está no arquivo compactado fica em um diretório central no final do arquivo, o que também é a razão de alguns ZIPs corrompidos ou truncados parecerem ter conteúdo quando você varre a partir do início, mas falharem ao abrir, porque a ferramenta que você está usando lê o diretório central primeiro, e ele está faltando ou danificado.
Mach-O: número mágico, depois load commands
O Mach-O é o formato de executável que o macOS usa para binários compilados. Ele começa com um número mágico que diz a um carregador duas coisas de uma vez: se é 32 bits ou 64 bits, e qual ordem de bytes o resto do arquivo usa. 0xFEEDFACE é 32 bits, 0xFEEDFACF é 64 bits, e, se você vir os bytes invertidos (0xCEFAEDFE ou 0xCFFAEDFE), significa que o arquivo foi escrito na ordem de bytes oposta à da máquina que o está lendo.
Depois do número mágico vem um cabeçalho fixo (tipo de CPU, tipo de arquivo e uma contagem de load commands), seguido por essa quantidade de load commands em sequência. Os load commands são como um arquivo Mach-O descreve tudo de que o carregador precisa: quais segmentos mapear na memória, quais bibliotecas dinâmicas vincular, onde fica o ponto de entrada. É uma ideia parecida com os chunks do PNG: um cabeçalho fixo, depois uma sequência de registros autodescritivos, cada um dizendo ao leitor quanto pular para chegar ao próximo.
Examinando isso você mesmo
Nada disso exige escrever um parser. Abra qualquer arquivo PNG, ZIP ou Mach-O no Hexter e a aba Analisar reconhece o formato automaticamente, percorrendo a estrutura de chunks ou de load commands de verdade e mostrando-a como uma árvore ao lado do hex bruto, para que você possa clicar em um campo e ver exatamente de quais bytes ele veio. É somente leitura por padrão, então isso é genuinamente seguro de fazer em um arquivo real, um binário de app, uma captura de tela, um arquivo compactado que você baixou, sem risco algum de alterá-lo.
Se você quiser conferir o que está acima à mão: abra um PNG e verifique se os primeiros oito bytes batem com a assinatura, depois olhe os bytes 8 a 11 (o comprimento do primeiro chunk) e os bytes 12 a 15 (seu tipo, que deve dizer IHDR). É um exercício pequeno o bastante para fazer em alguns minutos, e é uma forma genuinamente útil de entender um formato de arquivo que você usou a vida inteira mas nunca de fato examinou.