Decrypt SQL 2000 Stored Procedures, Functions, Views, and Triggers

Note: This method has to be handled with utmost care only in critical situations, and my experienced database administrators to provide critical support services in case of a SQL emergency.

In order to decrypt the objects in SQL Server 2000, execute the code given below:

/*=========================================================
 
NAME: Decrypt SQL 2000 stored procedures, functions, views, and triggers
 
DESCRIPTION: The script below accepts an object (schema name + object name)
that were created using the WITH ENCRYPTION option and returns
the decrypted script that creates the object. This script
is useful to decrypt stored procedures, views, functions,
and triggers that were created WITH ENCRYPTION.
 
The algorithm used below is the following:
1. Check that the object exists and that it is encrypted.
2. In order to decrypt the object, the script ALTER (!!!) it
and later restores the object to its original one. This is
required as part of the decryption process: The object
is altered to contain dummy text (the ALTER uses WITH ENCRYPTION)
and then compared to the CREATE statement of the same dummy
content.
 
Note: The object is altered in a transaction, which is rolled
back immediately after the object is changed to restore
all previous settings.
 
3. A XOR operation between the original binary stream of the
enrypted object with the binary representation of the dummy
object and the binary version of the object in clear-text
is used to decrypt the original object.
 
USER PARAMETERS: @ObjectOwnerOrSchema
@ObjectName
 
RESULTSET: NA
 
RESULTSET SORT: NA
 
USING TABLES/VIEWS: syscomments
 
===========================================================*/
 
DECLARE @ObjectOwnerOrSchema NVARCHAR(128)
DECLARE @ObjectName NVARCHAR(128)
 
SET @ObjectOwnerOrSchema = 'dbo'
SET @ObjectName = 'myproc2'
 
DECLARE @i INT
DECLARE @j INT
DECLARE @k INT
DECLARE @ObjectDataLength INT
DECLARE @ContentOfEncryptedObject NVARCHAR(4000)
DECLARE @ContentOfDecryptedObject NVARCHAR(4000)
DECLARE @ContentOfFakeObject NVARCHAR(4000)
DECLARE @ContentOfFakeEncryptedObject NVARCHAR(4000)
DECLARE @ObjectType NVARCHAR(128)
DECLARE @CurrColID INT
DECLARE @CurrDataLength INT
DECLARE @CurrPtr BINARY(16)
DECLARE @CurrOffset INT
DECLARE @tmpStrForExec VARCHAR(8000)
DECLARE @ObjectID INT
 
SET NOCOUNT ON
 
SET @ObjectID = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName
                          + ']')
 
-- Check that the provided object exists in the database.
IF @ObjectID IS NULL 
    BEGIN
        RAISERROR ( 'The object name or schema provided does not exist in the database',
            16, 1 )
        RETURN
    END
 
-- Check that the provided object is encrypted.
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
 
-- Determine the type of the object
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  @ObjectDataLength = SUM(DATALENGTH(ctext) / 2)
FROM    syscomments
WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema, '''', '''''')
                       + '].[' + REPLACE(@ObjectName, '''', '''''') + ']')
 
IF @ObjectDataLength < 4000 
    BEGIN
        SELECT TOP 1
                @ContentOfEncryptedObject = ctext
        FROM    syscomments
        WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema, '''',
                                             '''''') + '].['
                               + REPLACE(@ObjectName, '''', '''''') + ']')
 
-- We need to alter the existing object in order to decrypt its content.
-- This is done in a transaction (which we later rollback) in order to make sure
-- that the underlying object is not impacted.
 
        SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' ['
            + @ObjectOwnerOrSchema + N'].[' + @ObjectName
            + N'] WITH ENCRYPTION AS'
        SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - ( DATALENGTH(@ContentOfFakeObject) / 2 ))
 
-- Since we need to alter the object in order to decrypt it, this is done
-- in a transaction
        BEGIN TRAN
 
        EXEC ( @ContentOfFakeObject
            )
 
-- Get the encrypted content of the new "fake" object.
        SELECT TOP 1
                @ContentOfFakeEncryptedObject = ctext
        FROM    syscomments
        WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema, '''',
                                             '''''') + '].['
                               + REPLACE(@ObjectName, '''', '''''') + ']')
 
        IF @@TRANCOUNT > 0 
            ROLLBACK TRAN
 
        SET @ContentOfFakeObject = N'CREATE ' + @ObjectType + N' ['
            + @ObjectOwnerOrSchema + N'].[' + @ObjectName
            + N'] WITH ENCRYPTION AS'
        SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - ( DATALENGTH(@ContentOfFakeObject) / 2 ))
 
        SET @i = 1
 
--Fill the variable that holds the decrypted data with a filler character
        SET @ContentOfDecryptedObject = N''
        SET @ContentOfDecryptedObject = @ContentOfDecryptedObject
            + REPLICATE(N'A',
                        @ObjectDataLength
                        - ( DATALENGTH(@ContentOfDecryptedObject) / 2 ))
 
-- Decrypt one character at a time
        WHILE @i <= @ObjectDataLength
            BEGIN
--xor real & fake & fake encrypted
                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
 
/* debug */
        SELECT  @ContentOfDecryptedObject
    END -- IF @ObjectDataLength < 4000
ELSE 
    BEGIN
-- create this table to hold the encrypted and decrypted data
        IF OBJECT_ID('tempdb..#tblTemp') IS NOT NULL 
            DROP TABLE #tblTemp
 
        CREATE TABLE #tblTemp
            (
              Idx INT IDENTITY(1, 1)
                      PRIMARY KEY CLUSTERED,
              ContentOfEncryptedObject NTEXT,
              ContentOfDecryptedObject NTEXT,
              ContentOfFakeObject NTEXT,
              ContentOfFakeEncryptedObject NTEXT
            )
 
-- Generate the first line
        INSERT  INTO #tblTemp
                (
                  ContentOfEncryptedObject
                )
        VALUES  ( N'' )
 
-- Get ContentOfEncryptedObject
        SELECT  @CurrColID = MIN(colid)
        FROM    syscomments
        WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema, '''',
                                             '''''') + '].['
                               + REPLACE(@ObjectName, '''', '''''') + ']')
 
        WHILE @CurrColID IS NOT NULL
            BEGIN
                SET @ContentOfEncryptedObject = NULL
 
                SELECT  @ContentOfEncryptedObject = ctext
                FROM    syscomments
                WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema,
                                                     '''', '''''') + '].['
                                       + REPLACE(@ObjectName, '''', '''''')
                                       + ']')
                        AND colid = @CurrColID
 
                SELECT  @CurrPtr = TEXTPTR(ContentOfEncryptedObject),
                        @CurrDataLength = ISNULL(DATALENGTH(ContentOfEncryptedObject),
                                                 0)
                FROM    #tblTemp
                WHERE   Idx = 1
 
                IF @CurrDataLength = 0 
                    BEGIN
                        UPDATE  #tblTemp
                        SET     ContentOfEncryptedObject = @ContentOfEncryptedObject
                        WHERE   Idx = 1
                    END
                ELSE 
                    BEGIN
                        SET @CurrOffset = NULL
                        UPDATETEXT #tblTemp.ContentOfEncryptedObject @CurrPtr
                            @CurrOffset 0 @ContentOfEncryptedObject
                    END
 
                SELECT  @CurrColID = MIN(colid)
                FROM    syscomments
                WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema,
                                                     '''', '''''') + '].['
                                       + REPLACE(@ObjectName, '''', '''''')
                                       + ']')
                        AND colid > @CurrColID
            END
 
-- We need to alter the existing object in order to decrypt its content.
 
        SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' ['
            + @ObjectOwnerOrSchema + N'].[' + @ObjectName
            + N'] WITH ENCRYPTION AS'
-- SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' ' + @ObjectName + N' WITH ENCRYPTION AS'
 
        UPDATE  #tblTemp
        SET     ContentOfFakeObject = @ContentOfFakeObject
        WHERE   Idx = 1
 
        SELECT  @CurrPtr = TEXTPTR(ContentOfFakeObject),
                @CurrDataLength = ISNULL(DATALENGTH(ContentOfFakeObject) / 2,
                                         0)
        FROM    #tblTemp
        WHERE   Idx = 1
 
        WHILE @CurrDataLength < @ObjectDataLength
            BEGIN
                SET @CurrOffset = NULL
 
                IF @CurrDataLength + 4000 < @ObjectDataLength 
                    SET @ContentOfFakeObject = REPLICATE(N'-', 4000)
                ELSE 
                    SET @ContentOfFakeObject = REPLICATE(N'-',
                                                         @ObjectDataLength
                                                         - @CurrDataLength)
 
                UPDATETEXT #tblTemp.ContentOfFakeObject @CurrPtr @CurrOffset 0
                    @ContentOfFakeObject
 
                SELECT  @CurrPtr = TEXTPTR(ContentOfFakeObject),
                        @CurrDataLength = ISNULL(DATALENGTH(ContentOfFakeObject)
                                                 / 2, 0)
                FROM    #tblTemp
                WHERE   Idx = 1
            END
 
-- Now we need to alter the object in order to decrypt it.
-- This is done in a transaction.
 
        SET @tmpStrForExec = ''
 
        SELECT  @j = CEILING(CAST(DATALENGTH(ContentOfFakeObject) AS DECIMAL(18, 0))
                             / ( 2 * 2000 ))
        FROM    #tblTemp
        WHERE   Idx = 1
 
        SET @i = 1
 
        WHILE @i <= @j
            BEGIN
                SET @tmpStrForExec = @tmpStrForExec
                    + 'DECLARE @ContentOfDecryptedObject'
                    + CAST(@i - 1 AS VARCHAR(32)) + ' NVARCHAR(4000) '
                    + CHAR(10) + CHAR(13) + 'SELECT @ContentOfDecryptedObject'
                    + CAST(@i - 1 AS VARCHAR(32))
                    + ' = SUBSTRING(ContentOfFakeObject, 1 + ('
                    + CAST(@i - 1 AS VARCHAR(32))
                    + '*2000), 2000) FROM #tblTemp WHERE Idx = 1' + CHAR(10)
                    + CHAR(13) + 'SET @ContentOfDecryptedObject'
                    + CAST(@i - 1 AS VARCHAR(32))
                    + ' = REPLACE(@ContentOfDecryptedObject'
                    + CAST(@i - 1 AS VARCHAR(32))
                    + ', '''''''', '''''''''''') ' + CHAR(10) + CHAR(13)
 
                SET @i = @i + 1
            END
 
        SET @tmpStrForExec = @tmpStrForExec + 'EXEC('
        SET @i = 1
 
        WHILE @i <= @j
            BEGIN
                IF @i < @j 
                    SET @tmpStrForExec = @tmpStrForExec
                        + '@ContentOfDecryptedObject'
                        + CAST(@i - 1 AS VARCHAR(32)) + ' + '
                ELSE 
                    SET @tmpStrForExec = @tmpStrForExec
                        + '@ContentOfDecryptedObject'
                        + CAST(@i - 1 AS VARCHAR(32)) + ')'
 
                SET @i = @i + 1
            END
 
        SET XACT_ABORT OFF
        BEGIN TRAN
 
/* debug */
-- PRINT(@tmpStrForExec)
 
        EXEC ( @tmpStrForExec
            )
 
        IF @@ERROR <> 0 
            ROLLBACK TRAN
 
-- Get ContentOfFakeEncryptedObject
        SELECT  @CurrColID = MIN(colid)
        FROM    syscomments
        WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema, '''',
                                             '''''') + '].['
                               + REPLACE(@ObjectName, '''', '''''') + ']')
 
        WHILE @CurrColID IS NOT NULL
            BEGIN
                SET @ContentOfFakeEncryptedObject = NULL
                SET @CurrOffset = NULL
 
                SELECT  @ContentOfFakeEncryptedObject = ctext
                FROM    syscomments
                WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema,
                                                     '''', '''''') + '].['
                                       + REPLACE(@ObjectName, '''', '''''')
                                       + ']')
                        AND colid = @CurrColID
 
                SELECT  @CurrPtr = TEXTPTR(ContentOfFakeEncryptedObject),
                        @CurrDataLength = ISNULL(DATALENGTH(ContentOfFakeEncryptedObject),
                                                 0)
                FROM    #tblTemp
                WHERE   Idx = 1
 
                IF @CurrDataLength = 0 
                    BEGIN
                        UPDATE  #tblTemp
                        SET     ContentOfFakeEncryptedObject = @ContentOfFakeEncryptedObject
                        WHERE   Idx = 1
                    END
                ELSE 
                    BEGIN
                        SET @CurrOffset = NULL
                        UPDATETEXT #tblTemp.ContentOfFakeEncryptedObject
                            @CurrPtr @CurrOffset 0
                            @ContentOfFakeEncryptedObject
                    END
 
                SELECT  @CurrColID = MIN(colid)
                FROM    syscomments
                WHERE   id = OBJECT_ID('[' + REPLACE(@ObjectOwnerOrSchema,
                                                     '''', '''''') + '].['
                                       + REPLACE(@ObjectName, '''', '''''')
                                       + ']')
                        AND colid > @CurrColID
            END
 
-- Get ContentOfFakeObject - the CREATE version
        SET @CurrOffset = 0
 
        SELECT  @CurrPtr = TEXTPTR(ContentOfFakeObject)
        FROM    #tblTemp
        WHERE   Idx = 1
 
        UPDATETEXT #tblTemp.ContentOfFakeObject @CurrPtr @CurrOffset 6
            'CREATE '
 
-- Prepopulate @ContentOfDecryptedObject
        SET @ContentOfDecryptedObject = REPLICATE(N'A', 4000)
 
-- Decrypt the object
        SET @i = 1
        SET @j = 1
 
        SELECT  @ContentOfEncryptedObject = SUBSTRING(ContentOfEncryptedObject,
                                                      ( @j - 1 ) * 4000 + 1,
                                                      4000),
                @ContentOfFakeObject = SUBSTRING(ContentOfFakeObject,
                                                 ( @j - 1 ) * 4000 + 1, 4000),
                @ContentOfFakeEncryptedObject = SUBSTRING(ContentOfFakeEncryptedObject,
                                                          ( @j - 1 ) * 4000
                                                          + 1, 4000)
        FROM    #tblTemp
        WHERE   Idx = 1
 
-- Decrypt one character at a time
        WHILE @i <= @ObjectDataLength
            BEGIN
 
-- We need @k since the index in STUFF is between 0 to 4K
                SET @k = @i - ( ( @j - 1 ) * 4000 )
 
--xor real & fake & fake encrypted
                SET @ContentOfDecryptedObject = STUFF(@ContentOfDecryptedObject,
                                                      @k, 1,
                                                      NCHAR(UNICODE(SUBSTRING(@ContentOfEncryptedObject, @k, 1))
                                                            ^ ( UNICODE(SUBSTRING(@ContentOfFakeObject, @k, 1)) ^ UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @k, 1)) )))
 
-- Here we write to the temp table. We need to write in one of two cases:
-- 1. We reached 4K chars in @ContentOfDecryptedObject.
-- 2. We're about to exit the loop and condition #1 is not met.
                IF ( @i = ( @j * 4000 ) )
                    OR ( ( @i <> ( @j * 4000 ) )
                         AND @i = @ObjectDataLength
                       ) 
                    BEGIN
                        SELECT  @CurrPtr = TEXTPTR(ContentOfDecryptedObject),
                                @CurrDataLength = ISNULL(( DATALENGTH(ContentOfDecryptedObject)
                                                           / 2 ), 0)
                        FROM    #tblTemp
                        WHERE   Idx = 1
 
                        SET @CurrOffset = NULL
 
                        IF @CurrDataLength = 0 
                            UPDATE  #tblTemp
                            SET     ContentOfDecryptedObject = @ContentOfDecryptedObject
                            WHERE   Idx = 1
                        ELSE 
                            UPDATETEXT #tblTemp.ContentOfDecryptedObject
                                @CurrPtr @CurrOffset 0
                                @ContentOfDecryptedObject
 
-- This is the data length after the last write-operation.
                        SET @CurrDataLength = @CurrDataLength
                            + ( DATALENGTH(@ContentOfDecryptedObject) / 2 )
 
                        IF @CurrDataLength + 4000 < @ObjectDataLength 
                            SET @ContentOfDecryptedObject = REPLICATE(N'-', 4000)
                        ELSE 
                            SET @ContentOfDecryptedObject = REPLICATE(N'-', @ObjectDataLength - @CurrDataLength)
 
                        SET @j = @j + 1
 
                        SELECT  @ContentOfEncryptedObject = SUBSTRING(ContentOfEncryptedObject, ( @j - 1 ) * 4000 + 1, 4000),
                                @ContentOfFakeObject = SUBSTRING(ContentOfFakeObject, ( @j - 1 ) * 4000 + 1, 4000),
                                @ContentOfFakeEncryptedObject = SUBSTRING(ContentOfFakeEncryptedObject, ( @j - 1 ) * 4000 + 1, 4000)
                        FROM    #tblTemp
                        WHERE   Idx = 1
                    END
 
                SET @i = @i + 1
            END
 
/* debug */
-- SELECT * FROM #tblTemp
 
-- Before rolling back the transaction, we need to store the content of @ContentOfDecryptedObject
-- in NVARCHAR(4K) variables, then rollback, and then update another temp table with the content
-- of these variables.
 
        DECLARE @tblBypassTransaction TABLE
            (
              Idx INT IDENTITY(1, 1),
              ContentOfDecryptedObject NVARCHAR(4000)
            )
 
        SELECT  @j = CEILING(CAST(DATALENGTH(ContentOfDecryptedObject) AS DECIMAL(18, 0))
                             / ( 2 * 4000 ))
        FROM    #tblTemp
        WHERE   Idx = 1
 
        SET @i = 1
 
        WHILE @i <= @j
            BEGIN
                INSERT  INTO @tblBypassTransaction
                        (
                          ContentOfDecryptedObject
                        )
                        SELECT  SUBSTRING(ContentOfDecryptedObject,
                                          1 + ( @i - 1 ) * 4000, 4000)
                        FROM    #tblTemp
                        WHERE   Idx = 1
 
                SET @i = @i + 1
            END
 
        IF @@TRANCOUNT > 0 
            ROLLBACK TRAN
 
-- This is the content of the descrypted object.
-- We use a table variable and not temp table since temp tables
-- would be rolled back in the last ROLLBACK TRAN statement.
        SELECT  *
        FROM    @tblBypassTransaction
    END -- IF @ObjectDataLength > 4000
VN:F [1.9.13_1145]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.13_1145]
Rating: 0 (from 0 votes)

December 24, 2008 В· veeyeskay В· No Comments
Posted in: SQL Server, SQL Server 2005 Total Views: 2,096