gered

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

分类:

技术点:

相关文章: