zoukankan      html  css  js  c++  java
  • SQL存储过程解密研究

    从网上搜索SQL存储过程解密,可以看到一大堆的资料,其内容都基本上都一致,这是先放上一篇:
    解密存储过程
    本文将以此为基础进行研究,虽能解密成功,但其中解密那一段究其原理是到底是什么,一直也弄不明白,望了解内情的朋友告知。
    先谈问题:
    网上得来的存储过和经试验存在几个问题:
    1、并没有删除原存储重建,仅仅在控制台做了一个输出,拷贝出来很不方便。
    2、对于长度大点的加密数据会解密失败。
    带着这两个问题来改造此存储过程。

    先做准备工作,首先需要知道DAC这么个东西, 指的是数据库专用管理员连接,为管理员提供的一种特殊的诊断连接。
    知道了后得先打开它,以SQL2008为例:
    右击“对象浏览器”,找到“Facets”,点击,如图:
     
    找到“Sruface Area Configuration”,选择“RemoteDacEnabled”,设为True:


    然后进行DAC登录,CMD模式下敲如下命令,不清楚原理的可以自行研究:
    sqlcmd -A -S 192.168.1.101 -U sa -P 123456 
    命令提示行下打开需处理的数据库:
    >USE TEST
    >GO

    准备就绪,复制搜索得到的存储过程(这里有些语法问题,做些小调整即可),生成解密存储程,然后我们准备两个加密后的存储过程,其中一个长度较大(可自行循环创建一个500行的存储程,本文不再提供),验证得出结论,短小的存储过程很快即解密成功,并输出,但长度较大的却解密失败。
    接下来看看其如何解密的:
    先看这句
    select @maxColID = max(subobjid),@intEncrypted = imageval FROM sys.sysobjvalues WHERE objid = object_id(@procedure)
    指的是加密后的数据存放在“sys.sysobjvalues”表中,其内容存放于“imageval”字段。

    知道了加密后的数据,就得进行解密,它定义了4个关键字段:
    DECLARE @real_01 nvarchar(max)
    DECLARE @fake_01 nvarchar(max)
    DECLARE @fake_encrypt_01 nvarchar(max)
    DECLARE @real_decrypt_01 nvarchar(max)
    分别指的原始加密数据内容、原始加密存储过程的CREATE语句、自己构造的假的存储过程加密后的数据、最终解密后的存储过程。
    其方法是按位将@real_01、@fake_encrypt_01、@real_decrypt_01进行异或运算,此处为何如此处理,原理不明!!!
    WHILE @intProcSpace<=(datalength(@real_01)/2)
    BEGIN
    --xor real & fake & fake encrypted
    SET @real_decrypt_01 = stuff(@real_decrypt_01, @intProcSpace, 1, NCHAR(UNICODE(substring(@real_01, @intProcSpace, 1)) ^ (UNICODE(substring(@fake_01, @intProcSpace, 1)) ^ UNICODE(substring(@fake_encrypt_01, @intProcSpace, 1)))))
    SET @intProcSpace=@intProcSpace+1
    END
    实际上到此为止,加密后的存储过程已解密出来了。

    其下面还有一大段语句没有仔细研究,但基本上是利用sp_helptext将内容输出,方法比较繁索,而且没有达到我们要的效果,我们将换一种方法进行输出。
    基本上就这么简单,除了原理不清楚外,基本上已达到要求,接下来要解决开始提出的两个问题。
    首先是长度问题,为什么长度一大就解密失败,来看看@real_decrypt_01的定义并进行初始化@real_decrypt_01的语句:
    DECLARE @real_decrypt_01 nvarchar(max)
    SET @real_decrypt_01 = replicate(N'A', (datalength(@real_01) /2 ))
    乍一看没什么问题,但我们使用LEN(@real_decrypt_01)输出看看,最大输出长度为4000,可能问题就出现NVARCHAR的长度上了,理论上NVARCHAR(MAX)支持2G的大小。为什么会出现这种情况没有研究过,但有人给出了解决方法,进行显示转换:
    SET @real_decrypt_01 = replicate(CONVERT(NVARCHAR(MAX), N'A'), (datalength(@real_01) /2 ))
    其它的几还有几处也是该原因,改正后进行重新运行,问题解决,长度较大的存储过程也解密成功。

    第一个问题解决了,如何能方便的输出呢,试验了删除重建,但未成功,那么就用最简单的方法吧,利用xp_cmdshell将内容输出到文本。
    先建立一个物理表,用于存储解密后的数据:
    CREATE TABLE [dbo].[SQL_DECODE](
    [ID] [
    int] IDENTITY(1,1) NOT NULL,
    [SQLTEXT] [nvarchar](max) NOT NULL,
    CONSTRAINT [ID] PRIMARY KEY CLUSTERED
    (
    [ID] ASC
    )
    ) ON [PRIMARY]

    GO

    然后建立一个存储过程,遍历所有加密过的存储过程,调用解密存储过程进行解密,解密后输出:
    CREATE PROCEDURE [dbo].[DECODE_DATABASE]
    AS
    SET NOCOUNT ON
    BEGIN

    DECLARE @PROC_NAME VARCHAR(
    256)
    SET @PROC_NAME
    = ''
    DECLARE @ROWS INT
    DECLARE @TEMP TABLE(
    NAME VARCHAR(
    256)
    )
    INSERT INTO @TEMP SELECT NAME FROM sysobjects WHERE TYPE
    = 'P'
    AND NAME NOT IN (
    'DECODE_DATABASE', 'DECODE_PROC'
    )
    SET @ROWS
    = @@ROWCOUNT
    WHILE @ROWS
    > 0
    BEGIN
    SELECT @PROC_NAME
    = NAME FROM (
    SELECT ROW_NUMBER() OVER (ORDER by NAME) AS ROW, NAME FROM @TEMP
    ) T
    WHERE ROW
    = @ROWS
    EXEC [DECODE_PROC] @PROC_NAME
    PRINT @PROC_NAME
    SET @ROWS
    = @ROWS - 1
    END

    RETURN

    EXEC master..xp_cmdshell
    'bcp "SELECT [SQLTEXT] FROM TEST.dbo.[SQL_DECODE]" queryout C:\decode.txt -c -T -S PC2011043012JUJ'
    END


    GO

    最后是改造后的存储过程:
    CREATE PROCEDURE [dbo].[DECODE_PROC](
    @PROC_NAME SYSNAME
    = NULL
    )
    AS
    SET NOCOUNT ON

    DECLARE @PROC_NAME_LEN INT
    --存储过程名长度
    DECLARE @MAX_COL_ID SMALLINT
    --最大列ID
    SELECT @MAX_COL_ID
    = MAX(subobjid) FROM sys.sysobjvalues WHERE objid = OBJECT_ID(@PROC_NAME) GROUP BY imageval

    SELECT @PROC_NAME_LEN
    = DATALENGTH(@PROC_NAME) + 29
    DECLARE @REAL_01 NVARCHAR(MAX)
    --真实加密存储过程数据
    DECLARE @FACK_01 NVARCHAR(MAX)
    --修改为假的存储过程,长度(40003 - 存在过程名长度),原理不明?
    DECLARE @FACK_ENCRYPT_01 NVARCHAR(MAX)
    --伪加密存储过街程数据
    DECLARE @REAL_DECRYPT_01 NVARCHAR(MAX)
    --最终解密后的数据,初始化为原始加密长度的一半的“A”,原理不明?

    SET @REAL_01
    = (
    SELECT imageval FROM sys.sysobjvalues WHERE objid
    = object_id(@PROC_NAME) AND valclass = 1 AND subobjid = 1
    )

    DECLARE @REAL_DATA_LEN BIGINT
    SET @REAL_DATA_LEN
    = DATALENGTH(@REAL_01)
    --PRINT @REAL_DATA_LEN

    DECLARE @FACK_LEN BIGINT
    SET @FACK_LEN
    = @REAL_DATA_LEN * 10 --改造:假的长度在原真实数据长度上放大10倍

    --此处需将NVARCHAR显示转换成NVARCHAR(MAX),不然将只能产生4K长度
    SET @FACK_01
    = 'ALTER PROCEDURE ' + @PROC_NAME + ' WITH ENCRYPTION AS ' + REPLICATE(CONVERT(NVARCHAR(MAX), '-'), @FACK_LEN - @PROC_NAME_LEN)
    --PRINT '@FACK_01 = ' + STR(LEN(@FACK_01))
    EXECUTE (@FACK_01)
    SET @FACK_ENCRYPT_01
    = (
    SELECT imageval FROM sys.sysobjvalues WHERE objid
    = object_id(@PROC_NAME) AND valclass = 1 AND subobjid = 1
    )

    SET @FACK_01
    = 'CREATE PROCEDURE ' + @PROC_NAME + ' WITH ENCRYPTION AS ' + REPLICATE(CONVERT(VARCHAR(MAX), '-'), @FACK_LEN - @PROC_NAME_LEN)
    SET @REAL_DECRYPT_01
    = REPLICATE(CONVERT(NVARCHAR(MAX), N'A'), (DATALENGTH(@REAL_01) /2))
    --PRINT 'LEN(@REAL_DECRYPT_01) = ' + STR(LEN(@REAL_DECRYPT_01))


    --按位对 @REAL_01、 @FACK_01、 @REAL_DECRYPT_01 进行异或操作。
    DECLARE @INT_PROC_SPACE BIGINT
    SET @INT_PROC_SPACE
    = 1
    WHILE @INT_PROC_SPACE
    <= (DATALENGTH(@REAL_01) /2 )
    BEGIN
    SET @REAL_DECRYPT_01
    = STUFF(
    @REAL_DECRYPT_01,
    @INT_PROC_SPACE,
    1,
    NCHAR(UNICODE(SUBSTRING(@REAL_01, @INT_PROC_SPACE,
    1)) ^ (UNICODE(SUBSTRING(@FACK_01, @INT_PROC_SPACE, 1)) ^ UNICODE(SUBSTRING(@FACK_ENCRYPT_01, @INT_PROC_SPACE, 1))))
    )
    SET @INT_PROC_SPACE
    = @INT_PROC_SPACE + 1
    END

    --移除WITH ENCRYPTION
    SET @REAL_DECRYPT_01 = REPLACE(@REAL_DECRYPT_01, 'WITH ENCRYPTION', '')
    INSERT INTO [SQL_DECODE] VALUES (@REAL_DECRYPT_01)

    --PRINT '@REAL_DECRYPT_01 = ' + @REAL_DECRYPT_01
    --PRINT 'LEN(@REAL_DECRYPT_01) = ' + STR(LEN(@REAL_DECRYPT_01))

    --删除原存储过程
    SET @FACK_01
    = 'DROP PROCEDURE ' + @PROC_NAME
    EXEC(@FACK_01)

    GO

    至此,解密全过程大功告成,命令行模式下运行:
    >EXEC [DECODE_DATABASE]
    >GO
    在C盘根目录下,解密后的存储过程文本生成成功。
    运行前别忘记打开xp_cmdshell使用权限,同打开DAC一样:


    或者命令行模式下敲如下命令:
    >sp_configure 'show advanced options',1
    >reconfigure
    >go
    >sp_configure 'xp_cmdshell',1
    >reconfigure
    >go
  • 相关阅读:
    用汇编的眼光看C++(之算术符重载) 四
    用汇编的眼光看C++(之嵌入汇编) 四
    用汇编的眼光看C++(之const属性) 四
    用汇编的眼光看C++(之虚函数) 四
    用汇编的眼光看C++(之类继承) 四
    用汇编的眼光看C++(之模板类) 四
    用汇编的眼光看C++(之class构造、析构) 四
    用汇编的眼光看C++(之类静态变量、静态函数) 四
    用汇编的眼光看C++(之特殊函数) 四
    多线程基础知识
  • 原文地址:https://www.cnblogs.com/FlySoul/p/FM.html
Copyright © 2011-2022 走看看