Blog bem humorado, na Lingua Portuguesa, destinado a assuntos de Segurança de Informações com ênfase em Resposta a Incidentes e Forense/Investigação Computacional.
domingo, 10 de janeiro de 2010
Forense em SQL Server
Um dos assuntos que recebem grande importância atualmente é a Forense de Banco de Dados. Qualquer sistema de informação atual usa um banco de dados para armazenar e tratar suas informações, e está em todos os esquemas atuais de arquitetura de SI. Como parte fundamental dessa arquitetura, nada mais natural que estudos apareçam para desvendar os vestígios das ações realizadas em um banco de dados. Não é incomum, por exemplo, que tenhamos esse componente em investigações de invasão e roubo de informações, pois em última instância, é lá que as informações ficam. Poucos são os que já militam nessa área, mas já despontam algumas pesquisas interessantes.
No final de novembro de 2009, li um artigo excelente sobre artefatos e vestígios em bancos de dados SQL Server. Na verdade, inicialmente eu li uma apresentação em PPT feita na Black Hat, e depois acabei descobrindo que o autor, Kevvie Fowler, adaptou a apresentação de um paper que ele mesmo produziu como resultado do trabalho da certificação GCFA Gold, da SANS.
O artigo detalha muito bem como é possível, apenas de posse do Transaction Log do Banco e sem nenhum outro log ativado, descrever as operações CRUD que foram realizadas nas bases de dados. Na verdade, existem vários produtos no mercado que permitem logar todas as operações em um banco de dados. Porém, o mais comum é chegarmos em um caso e não termos essa facilidade. No entanto, o Transaction Log estará sempre lá, pois faz parte da arquitetura do banco e por isso nem precisa de algum tipo de iniciativa por parte de nenhuma equipe. Logicamente, nem tudo estará lá. As operações de Select, por exemplo, não fazem parte do Transaction Log.
Como não estamos na País das Maravilhas, tinha de existir um "entretanto". Neste caso, o entretanto é que o Transaction Log não é publicamente mapeado. Há alguns softwares que fazem recuperação de dados através do transaction, e o que dizem é que essas empresas licenciaram as informações diretamente da Microsoft.
O artigo não aborda todas as opções possíveis de serem encontradas em um Transaction, mas dá um bom pontapé para um estudo no sentido de automatizar as operações de levantamento dos vestígios. Com isso em mente, iniciei uma linha de pesquisa para criar um script que pudesse automatizar os passos indicados no artigo do Kevvie. Troquei alguns emails com ele, e por fim o script está no que posso chamar de versão alfa. Ainda há algumas coisas a implementar e vários testes, já que são tantas possibilidades que não há como tratar tudo de uma vez só. É aí que entra a comunidade de Computação Forense, pois há poucos dias eu postei algo sobre esse assunto em uma lista de discussões internacional e vários profissionais se colocaram a disposição para ajudar. Em breve vou iniciar a discussão dos detalhes do grupo e ver como faremos para desenvolver novas funcionalidades e testar o script. Provavelmente, o script ficará instalado no SourceForge, mas ainda discutiremos isso com o grupo de trabalho.
Você gostaria de participar ? O perfil é de alguém que conheça SQL Server, para participar dos testes. Se conhecer Perl e SQL Server, poderá participar programando também. O resultado do trabalho será sempre distribuído como open source.
Entre em contato, aqui pelo blog ou pelo meu email, indicando que deseja trabalhar no projeto de Forense em SQL Server. Quanto mais interessados, melhor.
Até o próximo post !
domingo, 9 de setembro de 2007
Nosso Amigo Hash - Parte VI - Complemento
Usando Banco de Dados e hash parcial
Este é um complemento do artigo anterior, onde estudamos o uso do utilitário ssdeep, que calcula hashes parciais dos arquivos, para fazer a classificação dos arquivos entre conhecidos e desconhecidos, ou ainda para localizar na imagem forense possíveis arquivos maliciosos ou criminosos, mas que receberam algum tipo de alteração para serem descaracterizados.
A técnica, inicialmente, seria substituir o uso da base NSRL (que só contém hashes MD5 e SHA-1) por uma criada por nós, onde o hash dos arquivos seriam computados pelo ssdeep. Essa base tem a tendência de crcescer cada vez mais (e quanto maior melhor, certo ?), porém o utilitário teria problemas de performance e de memória para realizar a operação. Esses problemas já são conhecidos nossos e falamos deles em um artigo passado (Parte V).
A idéia agora é aplicar o mesmo princípio, mas com algumas dificuldades a mais, porque enquanto a comparação com hashes MD5 ou SHA-1 é imediata (se são ou não iguais), a comparação dos hashes parciais precisa de um cálculo entre ambos, indicando o score de proximidade entre os arquivos. Por conta disso, estudei os fontes do ssdeep e converti as partes correlacionadas à nossa necessidade do C para o Transact SQL.
Vamos ao procedimento.
1) Montando uma base de hashes ssdeep:
A base pode ser obtida executando o utilitário ssdeep sobre os arquivos que quisermos catalogar. O comando é:
C:\catalogar>ssdeep -l * > base.txt
Esse comando deve ser usado na primeira vez que estiver carregando a base, e em todas as vezes que for necessário adicionar hashes a base.
2) Importando a base de hashes parciais:
O procedimento de importação é o seguinte (Antes de iniciar, remova a tabela Aux, se existir):
1) Clique com o botão direito sobre Tables e em seguida, clique na opção Import Tables.
2) Indique que o Data Source é um arquivo texto, e indique o arquivo base.txt
3) Deixe as opções default do File Format, mas marque First Row Has Column Names, pois a prmeira linha do arquivo base.txt é um cabeçalho.
4) Clique em Next, e marque Comma como delimitador.
5) Clique em Next e em Next novamente. Troque o nome da tabela destino para Aux.
6) Clique em Next e finalize o processo, importando os dados.
A tabela Aux será criada com os registros importados. Logo em seguida, faça o seguinte:
a) Se essa for a primeira importação da base, execute as seguintes queries no Query Analyser:
select
block=left(ssdeep,charindex(':',ssdeep)-1),
outro=substring(ssdeep, charindex(':',ssdeep)+1, 255),
nome=[1.0--blocksize:hash:hash]
into
#x
from
aux
select
block,
hash1=left(outro,charindex(':',outro)-1),
hash2=substring(outro, charindex(':',outro)+1, 255),
nome
into
sshash
from
#x
Após ser executado, o código acima cria a tabela sshash com a primeira importação dos hashes parciais.
b) Se essa NÃO for a primeira importação da base, execute as seguintes queries no Query Analyser:
select
block=left(ssdeep,charindex(':',ssdeep)-1),
outro=substring(ssdeep, charindex(':',ssdeep)+1, 255),
nome=[1.0--blocksize:hash:hash]
into
#x
from
aux
Insert
sshash
select
block,
hash1=left(outro,charindex(':',outro)-1),
hash2=substring(outro, charindex(':',outro)+1, 255),
nome
from
#x
Após ser executado, o código acima adiciona à tabela sshash os hashes parciais recém importados.
3) Calculando os hashes ssdeep de uma imagem:
Após a imagem montada, deve-se ir para o diretório raiz da mesma e executar o seguinte comando:
/media/mnt>ssdeep -r * > /reports/sshashes.txt
Esse comando calculará os hashes de todos os arquivos em todos os diretórios da imagem.
4) Importando o arquivo de hashes
Esse processo é idêntico ao procedimento 2 em relação a Importação do arquivo para a tabela Aux.
As queries a serem aplicadas logo após são:
a) Caso seja a primeira vez que esse processo estiver sendo usado:
select
block=left(ssdeep,charindex(':',ssdeep)-1),
outro=substring(ssdeep, charindex(':',ssdeep)+1, 255),
nome=[1.0--blocksize:hash:hash]
into
#x
from
aux
select
[case] = 'Codigo-do-caso',
block,
hash1=left(outro,charindex(':',outro)-1),
hash2=substring(outro, charindex(':',outro)+1, 255),
nome
into
sshashComp
from
#x
b) Se essa NÃO for o primeiro caso a ser verificado, execute as seguintes queries no Query Analyser:
select
block=left(ssdeep,charindex(':',ssdeep)-1),
outro=substring(ssdeep, charindex(':',ssdeep)+1, 255),
nome=[1.0--blocksize:hash:hash]
into
#x
from
aux
Insert
sshashComp
select
[Case] = 'Codigo-do-caso'
block,
hash1=left(outro,charindex(':',outro)-1),
hash2=substring(outro, charindex(':',outro)+1, 255),
nome
from
#x
5) Comparando os arquivos da imagem:
Execute no Query Analyser a rotina sscompara. O primeiro parâmetro é o código do caso e o segundo é o score mínimo que desejamos acusar como arquivos próximos.
Ex:
exec sscompara '2007-001', 80
O comando acima vai separar os arquivos do caso 2007-001 que tenham no mínimo 80 de score de proximidade com os arquivos cadastrados, indicando qual é o arquivo na imagem e qual é o arquivo cadastrado.
Ilustrando ...
Imagine que você trabalhe para algum órgão de investigação contra a pedofilia e tenha um diretório cheio de imagens e vídeos com esse conteúdo, capturados e catalogados. Seguindo os passos acima, você poderá criar e manter uma base de hashes parciais, e ao analisar uma imagem forense de um hard disk suspeito, verificar se há algum arquivo na imagem que se assemelhe aos existentes em sua base. Note que se o suspeito usou algum tipo de descaracterização dos vídeos e imagens (troca de bits, crop, etc), ainda assim o método descrito irá indicar a semelhança.
Sugestões de melhoria
- Criar índices nas tabelas
- Criar stored procedures para os comandos após a carga, listados acima.
- Adaptar a rotina sscompara para dar como saída os arquivos da imagem que não tem proximidade nenhuma com a base. Essa alteração é bem simples e vai permitir anular a técnica de Anti-Forense comentada no artigo Parte VI.
Um estudo interessante seria apurar a média do score obtido entre uma imagem original e imagens alteradas a partir dessa, para situações onde apenas um bit é modificado, uso de crops diversos, modificações na resolução, tamanho, ou até mesmo inserção de informações por esteganografia. Alguém se habilita ?
Até o próximo post !
Fontes dos SQLs e Stored Procedures:
CREATE Procedure dbo.eliminate_sequences @seq varchar(255) OUTPUT
as
Declare @ret varchar(255),
@i int,
@j int,
@len int
Set @ret = substring(@seq,1,3)
IF (@ret ='')
return
Set @len = Len(@seq)
Set @i = 4
Set @j = 4
While (@i <= @len) Begin
IF (substring(@seq,@i,1) <> substring(@seq,@i-1,1)) Or
(substring(@seq,@i,1) <> substring(@seq,@i-2,1)) Or
(substring(@seq,@i,1) <> substring(@seq,@i-3,1)) Begin
Set @ret = @ret + substring(@seq, @i,1)
Set @j = @j + 1
End
Set @i = @i + 1
End
Set @seq = @ret
Return
GO
----------------------------------------------------------------------------
Create Procedure dbo.has_common_substring @s1 varchar(255), @s2 varchar(255)
as
If (Len(@s1) <>
Return 0
If (Len(@s2) <>
Return 0
Declare
@i int
Set @i = 1
While (@i <= (Len(@s1)-6)) Begin
If (charindex(substring(@s1,@i,7), @s2) > 0)
Return 1
Set @i = @i + 1
End
Return 0
go
-------------------------------------------------------------------------------
CREATE PROCEDURE dbo.edit_distn @from varchar(255), @from_len int,
@to varchar(255), @to_len int
as
Declare
@row int,
@col int,
@indice int,
@radix int,
@low int,
@swp_int int,
@swp_char varchar(255),
@menor int,
@valor int,
@prim int,
@seg int,
@ter int
Declare @buf table (indice int, valor int)
/* Handle trivial cases when one string is empty */
If ((@from = '') Or (@from_len = 0)) Begin
If ((@to = '') Or (@to_len = 0))
Return 0
Else
Return @to_len
End
Else
If ((@to = '') Or (@to_len = 0))
Return @from_len
Set @radix = 2 * @from_len + 3
If ((@from_len > @to_len) And (@from_len > 2000)) Begin
Set @swp_int = @to_len
Set @to_len = @from_len
Set @from_len = @swp_int
Set @swp_char = @to
Set @to = @from
Set @from = @swp_char
End
Set @indice = 0
If (substring(@from,1,1) = substring(@to,1,1))
Set @menor = 0
Else
Set @menor = 2
Insert @buf values (@indice, @menor)
Set @indice = @indice + 1
Set @low = @menor
Set @col = 1
While (@col < @from_len) Begin
If (substring(@from,@col+1,1) = substring(@to,1,1))
Set @menor = @col
Else
Set @menor = @col + 3
If (@menor > (@col+2))
Set @menor = @col+2
Select @valor = valor from @buf where indice = (@indice-1)
If (@menor > (@valor+1))
Set @menor = @valor + 1
Insert @buf values (@indice, @menor)
If (@menor < @low)
Set @low = @menor
Set @indice = @indice + 1
Set @col = @col + 1
End
/* Now handle the rest of the matrix */
Set @row = 1
While (@row < @to_len) Begin
Set @col = 0
While (@col < @from_len) Begin
If (@row = 0)
Set @prim = @col
Else
If (@col = 0)
Set @prim = @row
Else
Select @prim = valor from @buf where indice = ((@indice + @from_len + 2) % @radix)
If (substring(@from,@col+1,1) != substring(@to, @row+1,1))
Set @prim = @prim+3
If (@row = 0)
Set @seg = @col+1
Else
Select @seg = valor from @buf where indice = ((@indice + @from_len + 3) % @radix)
Set @seg = @seg+1
If (@col = 0)
Set @ter = @row+1
Else
Select @ter = valor from @buf where indice = ((@indice + @radix - 1) % @radix)
Set @ter = @ter+1
Set @menor = @prim
If (@seg < @menor)
Set @menor = @seg
If (@ter < @menor)
Set @menor = @ter
Insert @buf values (@indice, @menor)
If ((substring(@from,@col+1,1) = substring(@to,@row,1)) And
(@col > 0) And
(substring(@from, @col, 1) = substring(@to, @row+1,1))) Begin
If ((@row-1) = 0)
Set @prim = @col-1
Else
If ((@col-1) = 0)
Set @prim = @row-1
Else
Select @prim = valor from @buf where indice = ((@indice + 1) % @radix)
Set @prim = @prim+5
If (@menor > @prim)
Set @menor = @prim
Insert @buf values (@indice, @menor)
End
Select @valor = valor from @buf where indice = @indice
If ((@valor < @low) Or (@col = 0))
Set @low = @valor
Set @indice = (@indice+1) % @radix
Set @col = @col + 1
End
If (@low > 100)
Break
Set @row = @row+1
End
Select @row = valor from @buf where indice = ((@indice+@radix-1) % @radix)
Return @row
Go
---------------------------------------------------------------------------------
CREATE PROCEDURE dbo.score_strings @s1 varchar(255), @s2 varchar(255), @bloco int
as
Declare
@score int,
@len1 int,
@len2 int,
@menor int,
@ret int
Set @len1 = len(@s1)
Set @len2 = len(@s2)
if (@len1 > 64) Or (@len2 > 64)
Return 0
exec @ret = has_common_substring @s1, @s2
If (@ret = 0)
Return 0
exec @score = edit_distn @s1, @len1, @s2, @len2
Set @score = (@score * 64)/(@len1+@len2)
Set @score = (100 * @score) / 64
If (@score >= 100)
return 0
Set @score = 100 - @score
Set @menor = @len1
If (@len2 < @len1)
Set @menor = @len2
if (@score > (@bloco/3 * @menor))
Set @score = (@bloco/3 * @menor)
Return @score
go
----------------------------------------------------------------
CREATE PROCEDURE dbo.spamsum @bloco1 int, @hash11 varchar(255), @hash12 varchar(255), @bloco2 int, @hash21 varchar(255), @hash22 varchar(255)
as
Declare @score int,
@score1 int,
@score2 int
Set @score = 0
IF ((@bloco1 <> @bloco2) AND
(@bloco1 <> (@bloco2*2)) AND
(@bloco2 <> (@bloco1*2)))
return 0
exec eliminate_sequences @hash11 OUTPUT
exec eliminate_sequences @hash12 OUTPUT
exec eliminate_sequences @hash21 OUTPUT
exec eliminate_sequences @hash22 OUTPUT
IF (@hash12 = '') OR (@hash22 = '')
Return 0
If (@bloco1 = @bloco2) BEGIN
exec @score1 = score_strings @hash11, @hash21, @bloco1
exec @score2 = score_strings @hash12, @hash22, @bloco2
Set @score = @score1
IF (@score2 > @score)
Set @score = @score2
END
ELSE
IF (@bloco1 = @bloco2*2)
exec @score = score_strings @hash11, @hash22, @bloco1
ELSE
exec @score = score_strings @hash12, @hash21, @bloco2
return @score
go
------------------------------------------------------------------------------------
-------------------------------------------------------------------
Create Procedure dbo.sscompara @caso varchar(255), @minscore int
as
-- Separa quem tem algum match, obedecendo o score mínimo
Declare @tabCaso Cursor,
@tabBase Cursor,
@score int,
@lbloco1 int,
@lhash11 varchar(255),
@lhash12 varchar(255),
@filename varchar(255),
@lbloco2 int,
@lhash21 varchar(255),
@lhash22 varchar(255),
@nome varchar(255)
Declare @saida table (score int, arq1 varchar(255), base varchar(255))
Set @tabBase = Cursor Fast_Forward
For
Select block, hash1, hash2, [nome] from sshash
Set @tabCaso = Cursor Scroll Read_only
For
Select block, hash1, hash2, nome from sshashComp where [case] = @caso
Open @tabBase
Open @tabCaso
Fetch Next from @tabBaseinto @lbloco1, @lhash11, @lhash12, @filename
While (@@Fetch_Status = 0) Begin
Fetch First from @tabCaso
into @lbloco2, @lhash21, @lhash22, @nome
While (@@Fetch_Status = 0) Begin
exec @score = spamsum @lbloco1, @lhash11, @lhash12, @lbloco2, @lhash21,
@lhash22
if (@score >= @minscore)
Insert @saida values (@score, @nome, @filename)
Fetch Next from @tabCaso
into @lbloco2, @lhash21, @lhash22, @nome
End
Fetch Next from @tabBase
into @lbloco1, @lhash11, @lhash12, @filename
End
Close @tabBase
Close @tabCaso
Deallocate @tabBase
Deallocate @tabCaso
Select * from @saida
go
quinta-feira, 23 de agosto de 2007
Nosso Amigo Hash - Parte V
Classificação de arquivos usando banco de dados
Enfim, cheguei ao tão falado post. Minha cara de pau já estava além da conta, mas realmente foi impossível escrever antes ...
Um ponto importante: Esse artigo é uma adaptação do artigo escrito pelo Mark McKinnon em seu blog.
Apenas relembrando o final do último artigo sobre hash, nosso objetivo maior é classificar cada arquivo em nossa imagem a ser analisada, de forma a indicar se o arquivo é conhecido ou não. Com essa classificação, conseguimos reduzir a ação de análise forense e focar em arquivos que podem realmente ter relevância para o processo. Quem nos apoiará na classificação é a base de hashes (conhecida também por hashset) do NSRL.
Vamos melhorar isso acrescentando mais uma ferramenta importante à nossa equação: Um banco de dados. A estratégia é dividir o problema em três fases:
1) Vamos criar uma base de dados e popular ela com os hashes da NSRL
2) Vamos passar o MD5Deep na imagem (ela precisará estar montada) e carregar os hashes obtidos em uma outra tabela da base.
3) A classificação dos arquivos poderá ser facilmente obtida com queries SQL a partir daí.
Note que:
- Há um notável ganho com essa solução, principalmente se levarmos em conta que o tempo levado para obter o resultado por uma segunda vez (uma repetição da classificação, por exemplo) é muito pequeno, compreendendo apenas a fase 3.
- A base de hashes da NSRL não é normalizada e repete arquivos com frequência, porque ela registra os arquivos conforme a aplicação que os arquivos pertencem, e é muito comum um arquivo pertencer a mais de uma aplicação ou versão.
- Na verdade, não nos importa saber de qual aplicação, versão ou SO específico é o arquivo em questão. Queremos apenas dizer se o arquivo é conhecido ou não. Isso vai nos facilitar bastante o tratamento, e a base NSRL ficará bastante reduzida.
- Vamos nos ater ao MD5, mas o SHA-1 poderá ser usado, a gosto ;)
Fase 1
Vamos criar a base de dados e carregar a base NSRL. Estou considerando que os quatro discos com a base NSRL já se encontram descompactados e disponíveis. Usarei como servidor de banco de dados o SQL Server, mas o processo pode ser facilmente adaptado para outros SGBDs.
1) Usando a ferramenta Enterprise Manager (ou o Query Analyser, para quem prefere usar DDLs SQL diretamente), crie uma base de dados. Escolha um nome para a base e use as opções default.
2) Abra a base, clique em tables com o botão direito, depois All Tasks e em seguida em Import Table.
3) Na primeira tela do Wizard, escolha para Data Source o text file. Informe, na caixa de texto Filename, o nome do arquivo NSRLFiles.txt. Clique em Next.
4) Deixe as opções default dessa tela, e marque também a check box First Row has Column Name. Clique em Next.
5) Nessa tela, novamente ficaremos com as opções default (delimiter = comma). Clique Next.
6) Na tela Destination, mantenha as opções default. Clique Next.
7) Na tela Select Source Table and Views, permaneça com o nome sugerido para a nova tabela (nsrlfile, o mesmo nome do arquivo a ser carregado). Clique Next.
8) Essa é a ultima tela. Clique Next.
O arquivo será carregado e gerará a tabela nsrlfile. Esse nome já aparecerá na relação de tabelas do banco.
Esse processo deverá ser repetido para o arquivo nsrlfile.txt de cada um dos quatro diretórios (ou discos) da base NSRL. A cada carga, o arquivo será importado e seus dados serão adicionados a tabela nsrlfile criada.
No final do processo, essa tabela estará com milhares de registros, muitos deles duplicados. Tenha em mente que é necessário ter bastante espaço em disco para essa carga inicial. Ela também consome bastante tempo.
Como nosso interesse é apenas indicar se o arquivo é conhecido ou não, podemos retirar todas as duplicações, e ainda por cima podemos retirar também alguns campos da tabela.
Entre no utilitário Query Analyser, conecte-se ao banco recem criado e digite a seguinte query:
Select distinct [md5], [sha-1], FileName into NSRL from nsrlfile
A query acima vai criar uma tabela nova somente com os campos de hash md5 e sha-1, mais o nome do arquivo. Cada registro só aparecerá uma única vez.
Para melhorar ainda mais o acesso no futuro, é interessante criar índices para os campos md5 e SHA-1. Esses índices podem ser criados diretamente com DDL SQL ou na interface do Entreprise Manager. Se optar por essa forma, você deve:
- Clicar com o botão direito sobre o nome da tabela NSRL e clicar no menu Design Table.
- Em seguida, clique no segundo botão da esquerda para direita (Table and Index Properties).
- Ao abrir a janela de propriedades, clique na aba Indexes/Keys e no botão NEW para criar um novo índice.
- Indique o campo MD5 na lista Column Name. Aceite o nome sugerido para o indice.
- Clique novamente no botão NEW, indique o campo SHA-1 na Column Name. O nome sugerido para o índice deve ser mantido.
- Clique em Close e logo em seguida no primeiro botão da esquerda para direita, para salvar as alterações.
Lembre-se que essa operação pode demorar bastante, já que a tabela é bem grande. Aqui, nós estamos criando dois índices distintos para facilitar as consultas posteriores.
Ao final dessa fase, temos a base NSRL completamente carregada, sem duplicações, e indexada na tabela NSRL. Vamos para a próxima fase, mas antes exclua a tabela nsrlfile.
Fase 2
O objetivo dessa fase é obter um conjunto de hashes da nossa imagem, e em seguida carregá-los para nosso banco. Na fase 3 faremos a classificação propriamente dita.
O conjunto de hashes é obtido através do uso do MD5Deep (ou o SHA1Deep). Vamos manter nosso foco para o MD5, mas os passos para o SHA-1 são análogos.
- Inicialmente, monte a imagem sendo analisada. Provavelmente, você estará com um Live CD de análise de forense computacional, como o Helix ou o FCCU.
- Vá para a raiz da imagem montada e use o comando MD5Deep -r * > resultado.txt
Esse comando faz com que todos os arquivos tenham seu hash calculado, desde a raiz, e de forma recursiva. Toda a saída do comando é jogada para o arquivo resultado.txt. Lembre-se que essa operação pode ser demorada, de acordo com o tamanho da imagem e do número de arquivos nela.
O próximo passo é carregar esse arquivo para uma tabela na nossa base. Não vou entrar em detalhes novamente, pois o processo é o mesmo de quando carregamos os arquivos nsrlfile.txt. Apenas leve em conta que, nesse caso, nao há nomes de campos na primeira linha e como separador de campo indique dois espaços. Observe que o próprio programa de carga já mostra as primeiras linhas carregadas, com nomes de colunas col001 e col002. Confira se está tudo certinho e indique aux como nome da tabela a ser criada com esse resultado. Novamente, essa operação será longa, dependendo do tamanho do arquivo a ser carregado.
Ao final dessa operação, teremos uma nova tabela chamada aux com duas colunas (Col001 e Col002). Vamos ao Query Analyser rodar um comando SQL que vai criar uma nova tabela a partir dessa, acrescentando um campo para o código do caso. Com esse código, sempre poderemos guardar as informações relativas ao caso e consultar quantas vezes quisermos.
Select [Caso] = '2007-001', [md5] = Col001, Arq = Col002 into Hashes from Aux
Confira se o conteúdo da tabela Hashes está igual ao da tabela Aux. Exclua a tabela Aux (vamos salvar espaço, certo ?) e crie índice para o campo MD5 da tabela Hashes.
Ao final dessa fase, nós temos uma base de dados com duas tabelas: a NSRL, com toda a base de hashes da nsrl, sem duplicações e campos desnecessários, e a tabela hashes, já carregada com os hashes da imagem do nosso primeiro caso. Todas devidamente indexadas.
Vamos para a próxima fase ...
Fase 3
Essa é a hora da verdade ... Vamos executar duas queries. Uma imprimirá os arquivos conhecidos e a outra os não conhecidos. Usando o Query Analyser:
Select CONHECIDOS=Arq from hashes h left outer join nsrl n on h.md5 = n.md5
where
caso = 'codigo do caso'
and
n.md5 is not null
Select DESCONHECIDOS=Arq from hashes h left outer join nsrl n on h.md5 = n.md5
where
caso = 'codigo do caso'
and
n.md5 is null
A saída de cada um pode ser usada em análises mais minuciosas. Ela equivale à saída do comando MD5Deep com as opções comentadas no último post sobre hashes.
Comentários e melhorias
- As duas queries acima podem ser gravadas como stored procedures, passando o código do caso por parâmetro. Ficará mais fácil para re-executar as classificações
- O tamanho da base ficará enorme, mas pouco utilizado. Um comando de Shrink Database é interessante para recuperar o espaço.
- Backup é fundamental. Faça um backup logo após o término de cada fase, para garantir.
- Uma abordagem alternativa é fazer o Shrink Database logo após o término da Fase 1. Faça um backup em seguida, e assim ganhará mais tempo e gastará menos espaço no backup.
- A fase 3 será executada todas as vezes que você quiser reclassificar os arquivos. Melhor do que fazer isso é gravar as duas saídas ...
- Se quiser adicionar os hashes de um novo caso/imagem, a operação é bem simples. Obtenha o arquivo com os hashes (como no caso do resultado.txt). Carregue-o para uma tabela aux, como no processo da fase 2. Logo em seguida, no Query Analyser, execute a seguinte query:
Insert hashes select Caso = 'codigo do novo caso', [md5] = Col001, Arq = Col002 from aux
Esse comando SQL vai inserir a tabela recem carregada para a tabela de hashes definitiva. Exclua a tabela aux, ao final. A partir daqui, execute as queries da fase 3 para classificar os arquivos.
- O processo explicado acima pode ser feito em uma passada só, diretamente na carga. Se você conhece o SQL Server, vá em frente !
- A NSRL libera versões novas 4 vezes por ano. A fase 1 precisará ser refeita todas as vezes, se você quiser se manter atualizado com as versões.
- Uma boa opção é montar uma base própria de hashes contendo figuras, musicas e outros itens. Recomendo colocar esses em uma tabela a parte, carregando-a da mesma forma que os arquivos anteriores. As queries da fase 3 precisarão de uma adaptação, pois a classificação será feita a partir de uma union da tabela NSRL com a tabela criada por você.
- É possível inserir mais um nível de classificação: Quebrar os arquivos conhecidos em bons e maus. Basta criar mais um campo na tabela NSRL com o default de ser bom. Na tabela acima, com seus próprios hashes como base, sempre que um arquivo for reconhecido como mau, o tal campo deve ser setado de acordo.
Finalmente ...
Demorou, mas chegamos ao fim dessa parte do artigo. Vou parar por aqui comentando o seguinte:
1) Você aprendeu que a classificação de arquivos é muito importante, pois com ela diminuímos os arquivos a serem analisados, tornando a investigação viável e mais focada.
2) Você aprendeu também que se modificamos apenas um mero bit em um arquivo, o resultado da computação do hash dele vai ser completamente diferente do original. Veja bem ... Estamos falando de mudar um mísero bit !
De posse das duas informações acima, pense junto comigo o que alguém poderia fazer para piorar a vida de um Investigador Computacional...
Certo, você percebeu ! Se um usuário mal intencionado trocar apenas um bit, ou byte, de todos os arquivos no HD, a classificação desse HD diria que todos os arquivos são desconhecidos. Isso inviabilizaria (ou tornaria muito difícil) a investigação. Essa técnica é conhecida como Anti-Forensics (é uma das técnicas usadas, na verdade), e pode atrapalhar um bocado a nossa investigação.
Mas nem tudo está perdido !
No próximo artigo sobre hash, vamos falar de uma solução bastante nova para esse problema.
Até o próximo post !