视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
sql存储过程的解决方法
2020-11-09 10:01:07 责编:小采
文档
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行的存储程,本文不再提供),验证得出结论,短小的存储过程很快即解密成功,并输出,但长度较大的却解密失败。
接下来看看其如何解密的:
先看这句
@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

下载本文
显示全文
专题