(4.16)存储过程的加密与解密
(4.16)存储过程的加密与解密
存储过程加密概念:无法查看到过程的内容,也无法右键生成create等脚本
适用范围:从SQL2008到SQL2016,2017和2019还没试过,2005反正不行
【1】先决条件DAC
【1.1】远程DAC启用(如果本机就不用这一步)
--执行下面的SQL可以启用远程使用DAC: Use master GO /* 0 = Allow Local Connection, 1 = Allow Remote Connections*/ sp_configure 'remote admin connections', 1 GO RECONFIGURE GO
【1.2】SSMS启动DAC
注意,要以DAC的方式新建查询才能够使用~(详情参考:SQL Server DAC——专用管理员连接)
在主菜单的“文件”中,依次选“新建”、“数据库引擎查询”,在弹出的“连接到服务器”对话窗口的“服务器名称”框中,
输入“admin:<你的实例的名称>”。
【2】推荐加密解密
【2.1】SP加密
use master go alter procedure sp_test @num int with encryption as begin select 1 union all select 2 select * from test1 update test1 set str=10 end
加密后,有把锁,如下图。
加密的SP,获取相关创建脚本会报错。如下图
【2.2】查看加密SP
--列出当前数据库中所有加密的存储过程 select DB_NAME() dbname,SCHEMA_NAME(A.schema_id) schemaname,A.name spname,case when A.object_id not in ( select distinct object_id from sys.numbered_procedures) then 0 else 1 end is_numbered from sys.all_objects A inner join sys.sql_modules B on A.object_id=B.object_id where A.type='P' and A.is_ms_shipped=0 and B.definition is null and A.object_id>0 and A.name not like 'dbtwin_%' go
【2.3】解密
(1)解密SP
/****** Object: StoredProcedure [dbo].[dbtwin_sp_decrypt] Script Date: 01/01/2020 21:09:31 ******/ IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[dbtwin_sp_decrypt]') AND type in (N'P', N'PC')) DROP PROCEDURE [dbo].[dbtwin_sp_decrypt] GO /****** Object: StoredProcedure [dbo].[dbtwin_sp_decrypt] Script Date: 01/01/2020 21:09:31 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[dbtwin_sp_decrypt](@procname sysname = NULL) AS DECLARE @encrypted NVARCHAR(MAX) DECLARE @encryptedLength INT DECLARE @tempStr NVARCHAR(MAX) DECLARE @tempHead NVARCHAR(MAX) DECLARE @tempBody NVARCHAR(MAX) DECLARE @dummySp NVARCHAR(MAX) DECLARE @dummyEncrypted NVARCHAR(MAX) DECLARE @decryptedMessage NVARCHAR(MAX) DECLARE @cnt INT DECLARE @decryptedChar NCHAR(1) SET NOCOUNT ON SET @encrypted = (SELECT imageval FROM sys.sysobjvalues WHERE object_name(objid)=@procname) SET @encryptedLength=DATALENGTH(@encrypted) / 2 SET @tempStr = N'PROCEDURE ' + @procname + N' WITH ENCRYPTION AS' BEGIN TRAN SET @tempHead = N'ALTER ' + @tempStr SET @tempBody = REPLICATE(N' ',(@encryptedLength - LEN(@tempHead))) EXEC(@tempHead + @tempBody) SET @dummyEncrypted = (SELECT imageval FROM sys.sysobjvalues WHERE object_name(objid)=@procname) ROLLBACK TRAN SET @dummySp =N'CREATE ' + @tempStr + @tempBody SET @decryptedMessage ='' SET @cnt = 1 WHILE @cnt <> @encryptedLength BEGIN SET @decryptedChar =NCHAR(UNICODE(SUBSTRING(@encrypted, @cnt, 1)) ^ UNICODE(SUBSTRING(@dummySp, @cnt, 1)) ^ UNICODE(SUBSTRING(@dummyEncrypted, @cnt, 1))) SET @decryptedChar=ISNULL(@decryptedChar,N' ') SET @decryptedMessage = @decryptedMessage + @decryptedChar SET @cnt = @cnt + 1 END SELECT @decryptedMessage GO
解密演示
【2.4】解密优化版
/****** Object: StoredProcedure [dbo].[dbtwin_sp_decrypt] Script Date: 01/04/2020 19:27:12 ******/ IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[dbtwin_sp_decrypt]') AND type in (N'P', N'PC')) DROP PROCEDURE [dbo].[dbtwin_sp_decrypt] GO /****** Object: StoredProcedure [dbo].[dbtwin_sp_decrypt] Script Date: 01/04/2020 19:27:12 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[dbtwin_sp_decrypt](@procname sysname = NULL) AS DECLARE @encrypted NVARCHAR(MAX) DECLARE @encryptedLength INT DECLARE @tempStr NVARCHAR(MAX) DECLARE @tempHead NVARCHAR(MAX) DECLARE @tempBody NVARCHAR(MAX) DECLARE @dummySp NVARCHAR(MAX) DECLARE @dummyEncrypted NVARCHAR(MAX) DECLARE @decryptedMessage NVARCHAR(MAX) DECLARE @cnt INT DECLARE @decryptedChar NCHAR(1) DECLARE @x NVARCHAR(MAX) SET NOCOUNT ON SET @encrypted = (SELECT imageval FROM sys.sysobjvalues WHERE object_name(objid)=@procname) SET @encryptedLength=DATALENGTH(@encrypted) / 2 SET @tempStr = N'PROCEDURE ' + @procname + N' WITH ENCRYPTION AS' BEGIN TRAN SET @tempHead = N'ALTER ' + @tempStr SET @x=N'-' SET @tempBody = REPLICATE(@x,(@encryptedLength - LEN(@tempHead))) EXEC(@tempHead + @tempBody) SET @dummyEncrypted = (SELECT imageval FROM sys.sysobjvalues WHERE object_name(objid)=@procname) ROLLBACK TRAN SET @dummySp =N'CREATE ' + @tempStr + @tempBody SET @decryptedMessage ='' SET @cnt = 1 WHILE @cnt <> @encryptedLength BEGIN SET @decryptedChar =NCHAR(UNICODE(SUBSTRING(@encrypted, @cnt, 1)) ^ UNICODE(SUBSTRING(@dummySp, @cnt, 1)) ^ UNICODE(SUBSTRING(@dummyEncrypted, @cnt, 1))) SET @decryptedMessage = @decryptedMessage + ISNULL(@decryptedChar,N' ') SET @cnt = @cnt + 1 END DECLARE @TextLength int DECLARE @BasePos int DECLARE @CurrentPos int SET @BasePos =1 SET @CurrentPos=1 SET @TextLength = DATALENGTH(@decryptedMessage) / 2 WHILE @CurrentPos != 0 BEGIN --通过回车查找行的结束 SET @CurrentPos = CHARINDEX(nchar(13)+ nchar(10), @decryptedMessage,@BasePos) IF @CurrentPos != 0 BEGIN PRINT SUBSTRING(@decryptedMessage, @BasePos, @CurrentPos - @BasePos ) END ELSE BEGIN IF @BasePos <= @TextLength BEGIN PRINT SUBSTRING(@decryptedMessage, @BasePos, @TextLength - @BasePos) END END SET @BasePos = @CurrentPos + 2 END GO
【3】另外一种方法
【3.1】SP加密
use test go alter procedure sp_test @num int with encryption as begin select 1 union all select 2 select * from test..test1 update test..test1 set num=10,num1=10 where id = @num end
【3.2】解密
create procedure sp_DecryptObject ( @Object sysname, --要解密的对象名:函数,存储过程,视图或触发器 @MaxLength int=4000 --评估内容的长度 ) as set nocount on /* 1. 解密 */ if not exists(select 1 from sys.objects a where a.object_id=object_id(@Object) And a.type in('P','V','TR','FN','IF','TF')) begin --SQL Server 2008 raiserror ( N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。' ,16,1) --SQL Server 2012 --throw 50001, N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。',1 return end if exists(select 1 from sys.sql_modules a where a.object_id=object_id(@Object) and a.definition is not null) begin --SQL Server 2008 raiserror (N'对象没有加密!' ,16,1) --SQL Server 2012 --throw 50001, N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。',1 return end declare @sql nvarchar(max) --解密出来的SQL语句 ,@imageval nvarchar(max) --加密字符串 ,@tmpStr nvarchar(max) --临时SQL语句 ,@tmpStr_imageval nvarchar(max) --临时SQL语句(加密后) ,@type char(2) --对象类型('P','V','TR','FN','IF','TF') ,@objectID int --对象ID ,@i int --While循环使用 ,@Oject1 nvarchar(1000) set @objectID=object_id(@Object) set @type=(select a.type from sys.objects a where a.object_id=@objectID) declare @Space4000 nchar(4000) set @Space4000=replicate('-',4000) /* @tmpStr 会构造下面的SQL语句 ------------------------------------------------------------------------------- alter trigger Tr_Name on Table_Name with encryption for update as return /**/ alter proc Proc_Name with encryption as select 1 as col /**/ alter view View_Name with encryption as select 1 as col /**/ alter function Fn_Name() returns int with encryption as begin return(0) end/**/ */ set @Oject1=quotename(object_schema_name(@objectID))+'.'+quotename(@Object) set @tmpStr= case when @type ='P ' then N'Alter Procedure '+@Oject1+' with encryption as select 1 as column1 ' when @type ='V ' then N'Alter View '+@Oject1+' with encryption as select 1 as column1 ' when @type ='FN' then N'Alter Function '+@Oject1+'() returns int with encryption as begin return(0) end ' when @type ='IF' then N'Alter Function '+@Oject1+'() returns table with encryption as return(Select a.name from sys.types a) ' when @type ='TF' then N'Alter Function '+@Oject1+'() returns @t table(name nvarchar(50)) with encryption as begin return end ' else 'Alter Trigger '+@Oject1+'on '+quotename(object_schema_name(@objectID))+'.'+(select Top(1) quotename(object_name(parent_id)) from sys.triggers a where a.object_id=@objectID)+' with encryption for update as return ' end set @tmpStr=@tmpStr+'/*'+@Space4000 set @i=0 while @i < (ceiling(@MaxLength*1.0/4000)-1) begin set @tmpStr=@tmpStr+ @Space4000 Set @i=@i+1 end set @tmpStr=@tmpStr+'*/' ------------ set @imageval =(select top(1) a.imageval from sys.sysobjvalues a where a.objid=@objectID and a.valclass=1) begin tran exec(@tmpStr) set @tmpStr_imageval =(select top(1) a.imageval from sys.sysobjvalues a where a.objid=@objectID and a.valclass=1) rollback tran ------------- set @tmpStr=stuff(@tmpStr,1,5,'create') set @sql='' set @i=1 while @i<= (datalength(@imageval)/2) begin set @sql=@sql+isnull(nchar(unicode(substring(@tmpStr,@i,1)) ^ unicode(substring(@tmpStr_imageval,@i,1))^unicode(substring(@imageval,@i,1)) ),'') Set @i+=1 end /* 2. 列印 */ declare @patindex int while @sql>'' begin set @patindex=patindex('%'+char(13)+char(10)+'%',@sql) if @patindex >0 begin print substring(@sql,1,@patindex-1) set @sql=stuff(@sql,1,@patindex+1,'') end else begin set @patindex=patindex('%'+char(13)+'%',@sql) if @patindex >0 begin print substring(@sql,1,@patindex-1) set @sql=stuff(@sql,1,@patindex,'') end else begin set @patindex=patindex('%'+char(10)+'%',@sql) if @patindex >0 begin print substring(@sql,1,@patindex-1) set @sql=stuff(@sql,1,@patindex,'') end else begin print @sql set @sql='' end end end end
成功破解
【4】解密过程原理
SQL SERVER SP解密过程推导
在SQL SERVER里,已知微软在加密和解密存储过程时用的是RC4算法。下面是解密存储过程的解题思路,读者可以据此自己写个解密小程序。
想解密SP,其实就是解答下列一道证明题。
已知条件:
1. 已知需要解密的存储过程SP_A的密文为Ma。
2. 用一个已知明文的SP_B替换SP_A,这样得到SP_B的明文和密文分别为Tb和Mb。
求证:Ta=Ma⊕Tb⊕Mb,Ta即为需要解密的存储过程的明文。
证明过程:
1. 用DAC登录SQLSERVER,获取Ma和Mb: SELECT imageval FROM sys.sysobjvalues where …
2. 由于RC4算法的加密和解密使用的是相同的秘钥,假设秘钥为K,那么:
a. Mb=Tb⊕K
b. 根据异或运算公式,可以得到下列该存储过程加解秘时使用的秘钥为:
K=K⊕0=K⊕Tb⊕Tb=(K⊕Tb)⊕Tb=(Tb⊕K)⊕Tb=Mb⊕Tb
3. 因为Ma=Ta⊕K,则再次利用异或运算公式,得到下列推导结果:
Ta=Ta⊕0=Ta⊕(K⊕K)=(Ta⊕K)⊕K=Ma⊕K=Ma⊕Mb⊕Tb
4. 即Ta=Ma⊕Tb⊕Mb,Ta就是需要解密的存储过程的明文。
参考自:DBTWIN 无锡SQL集群服务商