(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集群服务商

posted @ 2018-12-03 17:53  郭大侠1  阅读(1143)  评论(0编辑  收藏  举报