Engenharia reversa de formato binário usando arquivos .SNG da Korg como exemplo



Vivemos um tempo maravilhoso. Ao nosso redor existe uma abundância de tecnologia: telefones, computadores, relógios inteligentes e outros aparelhos. Todos os dias, os fabricantes estão lançando cada vez mais dispositivos no mercado. A maioria deles é destinada a uma vida curta e brilhante (ou não): uma poderosa empresa de marketing no momento do lançamento, 1-2 anos de suporte total do fabricante e depois esquece lentamente. Dispositivos simples podem funcionar por anos após o final do período de suporte oficial. Com dispositivos inteligentes, está ficando mais difícil. É bom que pelo menos o gadget continue funcionando depois de desconectar os servidores / serviços do fabricante. E é uma sorte se a próxima atualização do SO, drivers ou outro software não superar a compatibilidade.

Infelizmente, os eventos estão se desenvolvendo cada vez mais de acordo com um cenário pessimista. E 5 a 10 anos após a compra, temos dispositivos tecnicamente sólidos em nossas mãos, que, no entanto, não podem ser usados ​​devido à falta de suporte de software. Claro, um gadget quebrado é desagradável. Mas muito mais desagradável se houver dados do usuário em um formato incompatível com qualquer coisa. Esses dados podem ser considerados perdidos se o dispositivo parar de funcionar. No meu caso, o pior ainda não aconteceu, mas os alarmes já estão tocando.

Portanto, existe a notória empresa Korg, que produz equipamentos musicais de alta qualidade. Em 2010, comprei um sintetizador dessa empresa para praticar música como hobby. A microstação Korg é um modelo bastante avançado. Entre outras coisas, possui um seqüenciador a bordo para gravar suas faixas e pode gravar dados em um cartão de memória no formato SNG proprietário. É possível exportar para um formato midi comum, mas quase todos os metadados são perdidos: informações sobre efeitos e filtros sobrepostos, várias configurações de instrumentos virtuais, etc. O principal problema para mim, pessoalmente, é a velocidade da transição para a gravação de idéias musicais. A musa é uma criação extravagante, e na maioria das vezes me deparo com uma ideia interessante apenas improvisando ou tocando algo simples. Quanto mais rápido pressiono o botão de gravar sem andar pelo menu, maior a probabilidade de repetir e gravar um fragmento interessante, que no futuro poderá se tornar parte de um trabalho completo. Obviamente, essa abordagem é imperfeita, mas estamos falando de um hobby. De uma forma ou de outra, durante quase dez anos, acumulei cerca de mil esboços musicais no formato SNG.

A campainha tocou na forma de uma série de falhas do sintetizador, exigindo um piscar do dispositivo. E pensei em converter todos os meus dados acumulados para o formato Midi, ainda mais porque facilitará muito o armazenamento, a organização e a edição deles. A busca pelo conversor no Google não deu nada. Existem muitos pedidos em todos os tipos de fóruns, a história continua há 20 anos, se não mais. Tudo o que encontrei foi o antigo utilitário Windows de outra pessoa, naturalmente incompatível com meus arquivos.

E então eu decidi tentar ver o que esse formato SNG tem tudo a ver? Talvez em algum lugar lá dentro, existam dados MIDI normais que você possa extrair e salvar facilmente?

Uma tentativa de resolver o problema "testa"


Portanto, nas instruções do sintetizador, você pode descobrir que o formato SNG é um contêiner no qual as chamadas "músicas" são armazenadas. Cada música contém 16 faixas de sequenciador com dados de música, além de configurações para sons e efeitos. Ao exportar para o formato Midi pelo menu do sintetizador, cada “música” é exportada para um arquivo .MID separado, e todas as configurações de sons e efeitos são perdidas. Porque Eu jogo minhas idéias da forma mais simples e sem efeitos, o problema é precisamente um grande número de arquivos SNG e a inconveniência do processo de conversão manual. Vamos ver se esse processo pode ser acelerado ou automatizado.

Primeiro, vamos lembrar o que são dados MIDI. Simplificando, este é um fluxo de eventos musicais: pressionar e soltar uma tecla, pressionar e soltar um pedal de sustentação, alterar o andamento, o patch (instrumento virtual) e outros parâmetros. Cada evento contém um delta de tempo a partir do momento do evento anterior e os dados, por exemplo, intensidade da nota e afinação. O formato de arquivo midi é muito simples: além dos cabeçalhos e dos próprios dados, praticamente não há nada lá.

Pink é um evento Note On. Amarelo pálido é um delta do tempo. Azul - evento Nota desativada.

Vamos tentar procurar nossos dados midi no arquivo SNG. Para fazer isso, escreva uma sequência de várias notas no sintetizador e exporte-a para os dois formatos. Porque Como não sabemos exatamente onde estão os dados musicais nos arquivos binários, tentaremos repetir o processo com diferentes seqüências de notas.

A seguir, uso o editor hexadecimal Synalyze It! Suas capacidades no futuro serão muito úteis para nós. Enquanto isso, basta usar a função de comparação binária.

De fato, apenas o nome da "música" coincidiu. Comparando dois arquivos SNG com diferentes seqüências de notas, podemos adivinhar aproximadamente onde exatamente os dados musicais estão armazenados, mas, por enquanto, isso não nos ajudará de forma alguma - o formato dos dados é diferente. O arquivo em si é dez vezes maior que o arquivo Midi e parece conter muitas informações adicionais. Você pode ver a assinatura KORG nos primeiros quatro bytes e em algumas outras linhas, incluindo o nome da “música” e os nomes dos patches (tons) atribuídos às faixas.

Analisando a estrutura dos blocos de dados


Isso poderia ser concluído se, felizmente, não houvesse ferramentas que tornassem relativamente fácil analisar e entender a estrutura dos dados binários. O mesmo programa Synalaze It! Nos ajudará com isso, o que permite criar e aplicar uma “gramática” para analisar arquivos binários.

A gramática é uma estrutura descritiva hierárquica que permite representar dados binários de forma legível por humanos. O programa permite que você baixe gramática para alguns formatos. Por exemplo, para o mesmo midi:

Para o formato SNG, nenhuma gramática pronta era esperada. Bem, vamos ver o que podemos extrair do arquivo por conta própria.

Vamos começar com o título. Normalmente, esta parte contém a assinatura do arquivo, informações sobre a versão, tamanhos e compensações dos blocos de dados. Depois de comparar vários arquivos SNG diferentes, encontramos as partes invariáveis ​​e prestamos mais atenção às que mudam

Crie uma estrutura de título no editor de gramática. Os primeiros 4 bytes são obviamente a assinatura do arquivo. Suponha que os 4 bytes a seguir tenham versão. As próximas dezenas de bytes não mudam e não contêm nada de interessante - criaremos para eles dados binários do tamanho apropriado. Mas então a diversão começa. Você pode observar alguns padrões no comportamento dos bytes nas compensações 0x13 e 0x1b. O segundo parece corresponder ao número de "músicas" em nosso arquivo. E o primeiro também cresce com a quantidade de dados no cabeçalho - esse parece ser o tamanho, apenas a contagem regressiva não vem do início do arquivo, mas do próximo byte 0x14. Nesse estágio, só podemos adivinhar o tipo de dados numéricos. Suponha que o tamanho seja do tipo UInt32, ou seja, leva 4 bytes. Adicione-os à nossa estrutura. Agora podemos definir o tamanho da estrutura do cabeçalho (tamanho + 20).

Gramática com estrutura de cabeçalho de arquivo adicionada


Vamos ver o que vem a seguir. Se você observar atentamente, notará que as abreviações de três letras estão espalhadas por todo o arquivo: SNG1, SDK1, SGS1 e assim por diante. Esses caracteres são encontrados em todos os arquivos SNG, portanto, podemos assumir que são assinaturas de determinados blocos. Além disso, nosso título terminou com muito sucesso pouco antes de uma dessas assinaturas. Compare o comportamento dos 4 bytes a seguir em arquivos de tamanhos diferentes. Pode-se observar que os valores aumentam com o aumento da quantidade de dados.

Mais algumas experiências, análises e cálculos, e a seguinte imagem começa a surgir:



Visualização alternativa de gráfico


Assim, nosso arquivo consiste em uma hierarquia bastante simples de blocos. Existem blocos pai que podem conter vários blocos filhos. Existem blocos de folhas (na terminologia de árvores binárias) que não contêm outros blocos.

Então a mágica começa. Com apenas algumas estruturas gramaticais, podemos analisar completamente a estrutura do arquivo de bloco

Portanto, crie uma estrutura de modelo DataChunk com os seguintes campos (o tamanho é indicado entre colchetes):

id: String [4]
tamanho: Int [4]
hierarquia: Int [4]
dados: estrutura

Agora crie uma estrutura parentChunk que herda DataChunk. Na propriedade hierarquia, especifique Valor Fixo 0x400 - este é um sinal do bloco pai. Certifique-se de marcar a caixa de seleção Deve corresponder.

Da mesma forma, crie childChunk. A hierarquia nesse caso terá dois valores: 0x240100 e 0x100

Adicione referências às estruturas parentChunk e childChunk à estrutura parentChunk de dados - dessa maneira, criamos recursão.

Por fim, adicione uma referência à estrutura parentChunk no nó principal.

A ordem dos elementos na estrutura de dados do parentChunk deve ser Variável, também é necessário definir o número mínimo e máximo de filhos dessa estrutura: 0 e Ilimitado, respectivamente.

Vamos aplicar as alterações, e pronto - nosso arquivo é bem analisado nos principais blocos

Ainda não sabemos nada sobre os dados em si, mas agora podemos navegar com muito mais facilidade no arquivo e focar em encontrar as informações de que precisamos.

Analisando um bloco que contém um índice de um arquivo


Para o treinamento, vamos tentar analisar algum bloco simples, por exemplo, SDK1. Aparentemente, ele contém algo como um sumário - uma lista de músicas e provavelmente alguns desvios / tamanhos.

Crie uma estrutura sdk1Chunk que herda childChunk. Editaremos o campo ID, indicando a assinatura do nosso bloco no campo Fixed Values. Não se esqueça da caixa de seleção Deve corresponder. Nos dados do bloco, pode-se observar um padrão de repetição bastante óbvio: o nome da “música” e dados até agora desconhecidos. Observe que o tamanho dos fragmentos repetidos é de 64 bytes. Além disso, comparando as versões dos arquivos com um número diferente de "músicas", você pode determinar que o número seja armazenado nos quatro primeiros bytes. Usando cálculos simples e fazendo várias suposições, obtemos a seguinte versão da estrutura na gramática:

Aqui, criei uma estrutura filho songInfo de 64 bytes e indiquei a capacidade de repetir um número de vezes. Aqui está o resultado da aplicação da gramática:

Uma análise mais aprofundada dos arquivos continua sendo uma questão técnica. Mudei as configurações gerais da "música" e os parâmetros de faixas individuais no sintetizador. Comparando versões de arquivo com várias alterações, você pode melhorar e refinar a gramática. Após um número suficientemente grande dessas iterações, quase não há partes de dados não reconhecidas no arquivo. Fiquei um pouco empolgado com o processo e resolvi quase todas as seções do arquivo, embora isso não fosse necessário para a tarefa original.

Uma pequena parte da gramática de um arquivo SNG após 8 horas de análise


Sentirei falta dos detalhes desse processo - no futuro, focaremos na análise dos dados musicais diretamente.

Mas mais sobre isso na próxima parte. Lá encontraremos uma tarefa interessante de conversão de dados (é bastante adequada para entrevistas), tentaremos resolvê-lo com um pequeno script e ouviremos um resultado bastante incomum de conversão de teste.

Resultados Preliminares


A necessidade de arquivos binários de engenharia reversa pode ocorrer inesperadamente. Por exemplo, para analisar o firmware do dispositivo, converter de formatos de dados raros, analisar ameaças digitais ou até modificar trivialmente os jogos salvos. As ferramentas modernas permitem que você resolva esses problemas com rapidez e eficiência. Cerca de 10 anos atrás, eu estava pesquisando firmware de laptop e esse processo pode levar várias semanas. Em seguida, foi necessário escrever scripts manualmente para analisar blocos de dados e criar estruturas. Com uma nova abordagem parcialmente automatizada, criei uma gramática de arquivo quase completa em apenas alguns dias.

Você pode iniciar a análise do arquivo binário procurando por strings - elas podem fornecer as primeiras pistas e acelerar o processo de análise. Geralmente, os arquivos binários consistem em blocos de dados organizados em uma estrutura hierárquica ou linear. Se você lida com essa estrutura, uma análise mais aprofundada será muito mais fácil. O cabeçalho do arquivo pode dar dicas sobre as compensações / tamanhos dos blocos de dados. Nos primeiros estágios, faz sentido focar na descrição de estruturas e blocos óbvios. A tarefa de análise é bastante simplificada pela capacidade de criar novas versões de arquivos com diferentes configurações, parâmetros e dados. Existem várias dificuldades associadas a tipos de dados desconhecidos e ordem de bytes em sua representação binária (Endianness). Abordaremos essas questões na próxima parte.

Leitura Recomendada


Andreas Pehnack. Como abordar a análise de formato de arquivo binário

Source: https://habr.com/ru/post/pt442580/


All Articles