Corrompendo seu banco de dados no SQL Server

Hoje de manhã eu recebi uma pergunta de um amigo que queria saber como corromper um banco de dados, para fazer alguns testes. O mecanismo que ele estava utilizando não era efetivo, então eu mencionei que ele poderia usar um editor hexadecimal para fazer o trabalho, mas como não temos muitas referências em português, resolvi gastar alguns minutinhos para machucar um banco de dados. Vamos?

Criando o banco de dados

Execute o script abaixo para criarmos um coitado que será corrompido…

USE master
go

-- drop database BDProblema

CREATE DATABASE BDProblema
go

USE BDProblema
go

ALTER DATABASE BDProblema
SET PAGE_VERIFY NONE
go

CREATE TABLE Dados (
IdDado INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Nome VARCHAR(200) NOT NULL)
go

INSERT INTO Dados VALUES ('Luciano Caixeta Moreira')
go

Note que eu configurei o banco de dados para não trabalhar com nenhum modelo de verificação de página, pois quero mostrar para vocês uma edição do arquivo sem recebermos nenhuma mensagem de erro.

Analisando a tabela criada com DBCC PAGE

Agora vamos dar uma olhada na estrutura da página. Primeiro descobrimos onde ela está e depois usando o DBCC PAGE para vermos sua estrutura.

SELECT *
FROM sys.sysindexes as s
where s.id = OBJECT_ID('Dados')
/*
First: 0x900000000100
Arquivo = 1
Página = 144 (0x90)
*/

DBCC TRACEON(3604)
DBCC PAGE (BDProblema, 1, 144, 2)

/*
Vamos guardar o início da página, pois é o que importa!

Memory Dump @0x5C19C000

5C19C000: 01010400 00c00001 00000000 00000800 †.....À..........
5C19C010: 00000000 00000100 1b000000 781f8600 †............x.†.
5C19C020: 90000000 01000000 14000000 40000000 †............@...
5C19C030: 13000000 00000000 00000000 00000000 †................
5C19C040: 01000000 00000000 00000000 00000000 †................
5C19C050: 00000000 00000000 00000000 00000000 †................
5C19C060: 30000800 01000000 02000001 0026004c †0............&.L
5C19C070: 75636961 6e6f2043 61697865 7461204d †uciano Caixeta M
5C19C080: 6f726569 72610000 21212121 21212121 †oreira..!!!!!!!!        

*/

Agora vamos executar uma pequena alteração para identificarmos se o 0x4c em negrito é realmente o “L” do meu nome.

UPDATE Dados
SET Nome = 'Muciano Caixeta Moreira'

DBCC TRACEON(3604)
DBCC PAGE (BDProblema, 1, 144, 2)

/*

5C19C000: 01010400 00c00001 00000000 00000800 †.....À..........
5C19C010: 00000000 00000100 1b000000 781f8600 †............x.†.
5C19C020: 90000000 01000000 14000000 5b000000 †............[...
5C19C030: 02000000 00000000 00000000 00000000 †................
5C19C040: 01000000 00000000 00000000 00000000 †................
5C19C050: 00000000 00000000 00000000 00000000 †................
5C19C060: 30000800 01000000 02000001 0026004d †0............&.M
5C19C070: 75636961 6e6f2043 61697865 7461204d †uciano Caixeta M
5C19C080: 6f726569 72610000 21212121 21212121 †oreira..!!!!!!!!

*/

Aqui vemos que o caractere na posição 0x5C19C06F alterou de 0x4c (L) para 0x4d (M).
Como estamos na página 144, sabemos que o offset dessa página no arquivo em bytes é: 144 * 8192 = 1179648 (em hexadecimal –> 0x120000). Vamos para o editor hexadecimal?

Editando o arquivo BDProblema.mdf

Pare a instância do SQL Server em questão e utilizando o editor hexadecimal de sua preferência (baixei o Free Hex Editor Neo da HDD Software) abra o arquivo BDProblema.mdf.

Navegue até o offset 120000 e a partir daí vá até a posição 6F e encontre o caractere 4d, alterando-o para 4c.

(Antes)
image

(Depois)
image

Agora salve o arquivo, feche o editor, reinicie a instância do SQL Server e execute um SELECT na tabela Dados. Qual será o resultado??? O “Muciano” voltou a ser “Luciano”. Até aqui não vemos nenhum erro, pois o SQL Server não estava com nenhuma verificação específica para o banco de dados, o que está para mudar…

Habilitando o page checksum e corrompendo o banco

Primeiramente, execute o script abaixo:

ALTER DATABASE BDProblema
SET PAGE_VERIFY CHECKSUM
go

UPDATE Dados
SET Nome = 'Muciano Caixeta Moreira'
go

DBCC CHECKDB()
GO

DBCC results for 'Dados'.
There are 1 rows in 1 pages for object "Dados".
CHECKDB found 0 allocation errors and 0 consistency errors in database 'BDProblema'.

Como resultado, teremos um banco de dados com CHECKSUM habilitado e a página com seu checksum calculado, pois foi executado um update na tabela. O CHECKDB vai executar com sucesso e nenhum erro será reportado.

Repita do procedimento listado no tópico “Editando o arquivo BDProblema.mdf”, onde você vai alterar o nome “Muciano” para “Luciano” através do editor hexadecimal. Reinicie o serviço do SQL Server e tente executar a consulta abaixo:

SELECT *
FROM Dados
/*
IdDado Nome
----------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Msg 824, Level 24, State 2, Line 1
SQL Server detected a logical consistency-based I/O error: incorrect checksum (expected: 0xd73d977b; actual: 0xd73d97fb). It occurred during a read of page (1:144) in database ID 7 at offset 0x00000000120000 in file 'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\BDProblema.mdf'. Additional messages in the SQL Server error log or system event log may provide more detail. This is a severe error condition that threatens database integrity and must be corrected immediately. Complete a full database consistency check (DBCC CHECKDB). This error can be caused by many factors; for more information, see SQL Server Books Online.
*/

Se tentar executar um CHECKDB, vai ver algo parecido com:

DBCC results for 'Dados'.
Msg 8928, Level 16, State 1, Line 1
Object ID 2105058535, index ID 1, partition ID 72057594038779904, alloc unit ID 72057594039697408 (type In-row data): Page (1:144) could not be processed. See other errors for details.
Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 2105058535, index ID 1, partition ID 72057594038779904, alloc unit ID 72057594039697408 (type In-row data), page (1:144). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12716041 and -4.
There are 0 rows in 0 pages for object "Dados".
CHECKDB found 0 allocation errors and 2 consistency errors in table 'Dados' (object ID 2105058535).
CHECKDB found 0 allocation errors and 2 consistency errors in database 'BDProblema'.
repair_allow_data_loss is the minimum repair level for the errors found by DBCC CHECKDB (BDProblema).
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Pronto! Você está com o seu banco de dados corrompido.
Como é um problema na página de dados, não podemos fazer um rebuild de um índice (método utilizado quando um índice não-cluster está corrompido) e temos que partir para outros métodos de disaster recovery, como recuperar um backup.

Espero que você tenha gostado e te ajudado a entender como pode simular falhas no seu banco de dados, para testar seu planejamento de disaster recovery, antes que seu ambiente de produção não esteja ok e você não sabia o que fazer. Além disso, claro, veja como é importante manter o checksum habilitado!

Até a próxima!

[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================

CorromperBD.sql