Hola, chicos,
¿Estás bien?
En esta publicación me gustaría demostrarles cómo recuperar el código fuente de un objeto cifrado (CON CIFRADO) en SQL Server. ¿Cuántas veces he visto a programadores cifrar objetos en SQL Server con la falsa esperanza de que este código realmente esté protegido de cambios y vistas de otros usuarios?
Introducción
Para aquellos que han trabajado en TI durante algún tiempo, creo que es unánime entre ellos que no existe una protección del código fuente realmente efectiva. Si programa en .NET, tiene la JetBrains dotPeck. Si cifra sus objetos en la base de datos Oracle, existe el sitio web ¡Desenvuélvelo! lo que rompe este cifrado ONLINE. En PHP, ya he usado Zend Guard para cifrar archivos fuente y existen herramientas en línea que también rompen el cifrado.
En SQL Server esto tampoco es diferente. Incluso hay herramientas (Redgate SQL Prompt, ApexSQL Complete y varias otras) que hacen esto automáticamente, mostrando el código fuente de los objetos cifrados de una manera tan natural y transparente que ni siquiera notarás que el objeto estaba cifrado.
Aunque es posible romper el cifrado de objetos en SQL Server, esto no significa directamente una falla de seguridad. Para poder romper este cifrado, necesitarás usar una conexión DAC, que ya expliqué cómo habilitar y usar en la publicación. Habilitación y uso de una conexión de administrador remoto (DAC) dedicada en SQL Server.
La conexión DAC es una conexión dedicada para administradores de sistemas (lo que limita en gran medida los usuarios que pueden realizar este procedimiento) que necesitan conectarse a instancias que están experimentando problemas que impiden nuevas conexiones, como que se alcance el límite de usuarios simultáneos de la instancia.
Como el método de conexión es diferente (incluso utiliza un puerto dedicado), esta conexión “especial” siempre está disponible, incluso en esta situación. Cabe mencionar que esta conexión solo permite conectar 1 usuario a la vez, por lo que si alguien ya está conectado, otra persona no podrá utilizarlo.
Todo este requisito para utilizar la conexión DAC es simplemente para que puedas leer los datos de la tabla sys.sysobjvalues. Incluso si es administrador de sistemas, solo podrá acceder a los datos de esta tabla mediante la conexión DAC.
Ejemplo SIN usar la conexión DAC

Mensaje 208, Nivel 16, Estado 1, Línea 16
Nombre de objeto no válido "sys.sysobjvalues".
Ejemplo de uso de la conexión DAC

SQL Server utiliza el algoritmo de cifrado de flujo RC4™ para cifrar objetos y almacena estos datos en la columna imageval de la tabla sys.sysobjvalues. En esta publicación, demostraré un algoritmo que le permite recuperar esta información y devolver el código fuente original del objeto cifrado.
Cómo enumerar los objetos cifrados en la base de datos
Para identificar qué objetos en su base de datos están cifrados, separé 4 consultas para lograr este objetivo:
Usando la vista sys.sql_modules:
SELECT
B.[name],
B.[type_desc]
FROM
sys.sql_modules A
JOIN sys.objects B ON A.[object_id] = B.[object_id]
WHERE
A.[definition] IS NULL
Usando la función OBJECTPROPERTY:
SELECT
[name],
[type_desc]
FROM
sys.objects
WHERE
OBJECTPROPERTY([object_id], 'IsEncrypted') = 1
Usando la vista sys.syscomments:
SELECT
A.[name],
A.[type_desc]
FROM
sys.objects A
JOIN sys.syscomments B ON A.[object_id] = B.id
WHERE
B.[encrypted] = 1
Usando la base de datos INFORMACIÓN_SCHEMA:
SELECT
ROUTINE_NAME,
ROUTINE_TYPE
FROM
INFORMATION_SCHEMA.ROUTINES
WHERE
ROUTINE_DEFINITION IS NULL
Cómo crear un SP cifrado
Para esta publicación, crearé un SP muy simple y cifrado y demostraré cómo obtener su código fuente.
USE [dirceuresende]
GO
CREATE PROCEDURE dbo.stpTeste_Decrypt
WITH ENCRYPTION
AS
BEGIN
----------------------------- Comentário da SP -----------------------------
PRINT 'dirceuresende'
SELECT 'dirceuresende'
END
Como puede ver a continuación, no es posible ver el código fuente de un objeto cifrado usando la vista sys.sql_modules, incluso si es miembro de la función sysadmin.

Ni siquiera utilizando el procedimiento del sistema sp_helptext es posible obtener el código fuente de este objeto, devolviendo el mensaje de alerta “El texto del objeto ‘dbo.stpTeste_Decrypt’ está cifrado”, como podemos ver a continuación:

Rompiendo el cifrado de objetos por SSMS
Para romper el cifrado de objetos a través de SQL Server Management Studio (SSMS), inicie una conexión DAC a la instancia deseada. Para hacer esto, simplemente agregue el prefijo “ADMIN:” antes del nombre de su instancia, por lo tanto: “ADMIN:servidor\instancia”, como se muestra en la imagen a continuación:

Ahora use el código a continuación y podrá ver el código fuente de su objeto cifrado.
código fuente
USE [dirceuresende]
GO
SET NOCOUNT ON
-- Aqui você tem que alterar para o objeto que você quer descriptografar
DECLARE
@ObjectOwnerOrSchema NVARCHAR(128) = 'dbo',
@ObjectName NVARCHAR(128) = 'stpTeste_Decrypt'
DECLARE
@i INT,
@ObjectDataLength INT,
@ContentOfEncryptedObject NVARCHAR(MAX),
@ContentOfDecryptedObject NVARCHAR(MAX),
@ContentOfFakeObject NVARCHAR(MAX),
@ContentOfFakeEncryptedObject NVARCHAR(MAX),
@ObjectType NVARCHAR(128),
@ObjectID INT
SET @ObjectID = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
IF @ObjectID IS NULL
BEGIN
RAISERROR('The object name or schema provided does not exist in the database', 16, 1)
RETURN
END
IF NOT EXISTS(SELECT TOP 1 * FROM sys.syscomments WHERE id = @ObjectID AND encrypted = 1)
BEGIN
RAISERROR('The object provided exists however it is not encrypted. Aborting.', 16, 1)
RETURN
END
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'PROCEDURE') IS NOT NULL
SET @ObjectType = 'PROCEDURE'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'TRIGGER') IS NOT NULL
SET @ObjectType = 'TRIGGER'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'VIEW') IS NOT NULL
SET @ObjectType = 'VIEW'
ELSE
SET @ObjectType = 'FUNCTION'
SELECT TOP 1 @ContentOfEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE [objid] = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
SET @ObjectDataLength = DATALENGTH(@ContentOfEncryptedObject)/2
SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
WHILE DATALENGTH(@ContentOfFakeObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2))
END
SET XACT_ABORT OFF
BEGIN TRAN
EXEC(@ContentOfFakeObject)
IF @@ERROR <> 0
ROLLBACK TRAN
SELECT TOP 1 @ContentOfFakeEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE [objid] = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
IF @@TRANCOUNT > 0
ROLLBACK TRAN
SET @ContentOfFakeObject = N'CREATE ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
WHILE DATALENGTH(@ContentOfFakeObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2))
END
SET @i = 1
SET @ContentOfDecryptedObject = N''
WHILE DATALENGTH(@ContentOfDecryptedObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfDecryptedObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', 4000)
ELSE
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', @ObjectDataLength - (DATALENGTH(@ContentOfDecryptedObject)/2))
END
WHILE (@i <= @ObjectDataLength)
BEGIN
SET @ContentOfDecryptedObject = STUFF(@ContentOfDecryptedObject, @i, 1,
NCHAR(
UNICODE(SUBSTRING(@ContentOfEncryptedObject, @i, 1)) ^
(
UNICODE(SUBSTRING(@ContentOfFakeObject, @i, 1)) ^
UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @i, 1))
)))
SET @i = @i + 1
END
SET @i = 0
WHILE DATALENGTH(@ContentOfDecryptedObject)/2 > (@i + 1)*2000
BEGIN
PRINT(SUBSTRING(@ContentOfDecryptedObject, 1 + 2000*@i, 2000*(@i + 1)))
SET @i = @i + 1
END
PRINT(SUBSTRING(@ContentOfDecryptedObject, 1 + 2000*@i, 2000*(@i + 1)))
Rompiendo el cifrado de objetos usando CLR (C#)
Ahora que he explicado cómo romper los objetos, les mostraré una solución más práctica y funcional en la vida cotidiana, que puede permitir que incluso los usuarios que no son administradores de sistemas vean el origen de los objetos cifrados y sin necesidad de abrir una nueva conexión DAC, que incluso podría ser desde otro servidor o instancia.
Para hacer esto, usaré el CLR para realizar la conexión a través de DAC, usando un usuario administrador de sistemas. Por lo tanto, incluso si la persona no es un administrador de sistemas, siempre que tenga permiso en esta función, podrá ver el origen de los objetos no cifrados. Para desglosar el código, usaré el mismo código que se muestra arriba, simplemente ejecutándolo a través de CLR (C#) en una conexión DAC, transparente para el usuario que usa la función.
código fuente
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read
)]
public static SqlString fncDescriptografa_Objeto(SqlString Ds_Servidor, SqlString Ds_Database, SqlString Ds_Schema, SqlString Ds_Objeto)
{
var conexaoLocal => "data source=LOCALHOST;initial catalog=CLR;Application Name=SQLCLR;persist security info=False;Enlist=False;packet size=4096;user id='usuario';password='senha'";
var servidor = conexaoLocal.Replace("LOCALHOST", $"ADMIN:{Ds_Servidor.Value}");
using (var con = new SqlConnection(servidor))
{
con.Open();
using (var cmd = new SqlCommand($@"
USE [{Ds_Database.Value}];
DECLARE
@ObjectOwnerOrSchema NVARCHAR(128) = '{Ds_Schema}',
@ObjectName NVARCHAR(128) = '{Ds_Objeto}'
DECLARE
@i INT,
@ObjectDataLength INT,
@ContentOfEncryptedObject NVARCHAR(MAX),
@ContentOfDecryptedObject NVARCHAR(MAX),
@ContentOfFakeObject NVARCHAR(MAX),
@ContentOfFakeEncryptedObject NVARCHAR(MAX),
@ObjectType NVARCHAR(128),
@ObjectID INT
SET NOCOUNT ON
SET @ObjectID = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
IF (@ObjectID IS NULL)
BEGIN
RAISERROR('The object name or schema provided does not exist in the database', 16, 1)
RETURN
END
IF NOT EXISTS(SELECT TOP 1 * FROM syscomments WHERE id = @ObjectID AND encrypted = 1)
BEGIN
RAISERROR('The object provided exists however it is not encrypted. Aborting.', 16, 1)
RETURN
END
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'PROCEDURE') IS NOT NULL
SET @ObjectType = 'PROCEDURE'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'TRIGGER') IS NOT NULL
SET @ObjectType = 'TRIGGER'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'VIEW') IS NOT NULL
SET @ObjectType = 'VIEW'
ELSE
SET @ObjectType = 'FUNCTION'
SELECT TOP 1 @ContentOfEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
SET @ObjectDataLength = DATALENGTH(@ContentOfEncryptedObject)/2
SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
WHILE DATALENGTH(@ContentOfFakeObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2))
END
SET XACT_ABORT OFF
BEGIN TRAN
EXEC(@ContentOfFakeObject)
IF @@ERROR <> 0
ROLLBACK TRAN
SELECT TOP 1 @ContentOfFakeEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
IF @@TRANCOUNT > 0
ROLLBACK TRAN
SET @ContentOfFakeObject = N'CREATE ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
WHILE DATALENGTH(@ContentOfFakeObject) / 2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject) / 2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject) / 2))
END
SET @i = 1
SET @ContentOfDecryptedObject = N''
WHILE DATALENGTH(@ContentOfDecryptedObject) / 2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfDecryptedObject) / 2 + 4000 < @ObjectDataLength
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', 4000)
ELSE
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', @ObjectDataLength - (DATALENGTH(@ContentOfDecryptedObject) / 2))
END
WHILE (@i <= @ObjectDataLength)
BEGIN
SET @ContentOfDecryptedObject = STUFF(@ContentOfDecryptedObject, @i, 1,
NCHAR(
UNICODE(SUBSTRING(@ContentOfEncryptedObject, @i, 1)) ^
(
UNICODE(SUBSTRING(@ContentOfFakeObject, @i, 1)) ^ UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @i, 1))
))
)
SET @i = @i + 1
END
SELECT @ContentOfDecryptedObject", con) { CommandType = CommandType.Text })
{
var resultado = (cmd.ExecuteScalar() != null) ? (string) cmd.ExecuteScalar() : "";
return resultado;
}
}
}
}
Después de crear la función en su proyecto CLR y publicar el cambio, usémosla:

Vale la pena recordar que en esta solución, el usuario que utiliza la función no necesita (y no debería) utilizar una conexión DAC.
Referencias:
https://sqlrendimiento.com/2016/05/sql-rendimiento/the-internals-of-with-encryption
https://www.codeproject.com/Articles/5068/RC-Encryption-Algorithm-C-Version
https://sqljunkieshare.com/2012/03/07/decrypting-encrypted-stored-procedures-views-functions-in-sql-server-20052008-r2/
https://pt.linkedin.com/pulse/uma-procedure-cryptada-e-agora-f%C3%A1bio-oliveira
¡Eso es todo, amigos!
Espero que hayas disfrutado de esta publicación y nos vemos la próxima.
SQL Server cómo recuperar la vista, obtener descifrar el código fuente del procedimiento almacenado, objetos cifrados CON CIFRADO
SQL Server cómo recuperar la vista, obtener descifrar el código fuente del procedimiento almacenado, objetos cifrados CON CIFRADO

Comentários (0)
Carregando comentários…