好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

解剖SQLSERVER第十五篇SQLSERVER存储过程的源文本存放在哪里

解剖SQLSERVER 第十五篇 SQLSERVER存储过程的源文本存放在哪里?(译) http://improve.dk/where-does-sql-server-store-the-source-for-stored-procedures/ 目前我正在扩展OrcaMDF Studio的功能 不单只支持系统表,DMVs 和用户表 而且也要支持存储过程。那

解剖SQLSERVER 第十五篇 SQLSERVER存储过程的源文本存放在哪里?(译)

http://improve.dk/where-does-sql-server-store-the-source-for-stored-procedures/

目前我正在扩展OrcaMDF Studio的功能 不单只支持系统表,DMVs 和用户表 而且也要支持存储过程。那很容易,我们只需要查询sys.procedures --或者查询sys.sysschobjs,

因为当SQLSERVER没有在运行的时候我们是不能查询sys.procedures 的

然而,我不想只是列出存储过程名称,我也需要显示存储过程里面的源代码。这带来了新的任务--检索源代码。源代码存储在哪里?

我在Google上找不到任何有用的资料,所以我们只能依靠自己观察了!

我已经创建了一个新的空数据库 这个数据库有一个3MB的数据文件。在这个数据库里面,我已经创建了一个单独的存储过程就像这样:

 SET  ANSI_NULLS  ON 
 GO 
 SET  QUOTED_IDENTIFIER  ON 
 GO 
 --   =============================================  
--   Author:          
--   Create date:   
--   Description:      
--   ============================================= 
 CREATE   PROCEDURE   XYZ
      AS 
 BEGIN 
     --   SET NOCOUNT ON added to prevent extra result sets from 
     --   interfering with SELECT statements. 
     SET  NOCOUNT  ON  ;

      --   Insert statements for procedure here 
     SELECT   '  AABBCC  '   AS   Output
  END  

 select   *   from  sys.procedures 


到目前为止一切顺利。然后我们可以检索存储过程的定义 使用查询sys.sql_modules 视图返回nvarchar(MAX)类型的定义文本

 select   *   from  sys.sql_modules  where   object_id   =   2105058535  

上面就是XYZ存储过程的源代码!等下,我可以从sys.sysschobjs表里获取存储过程的object ID,我不需要访问
sys.sql_modules ,sys.sql_modules 只是一个视图而不是系统表。我们看一下sys.sql_modules 视图是如何获取定义的:

 select  object_definition( object_id ( '  sys.sql_modules  ' )) 

 SELECT 
     object_id   =   o.id,
    definition   =   Object_definition(o.id),
    uses_ansi_nulls   =  Sysconv( bit , o.status  &   0x40000 ),  --   OBJMOD_ANSINULLS 
    uses_quoted_identifier  =  sysconv( bit , o.status  &   0x80000 ),    --   OBJMOD_QUOTEDIDENT 
    is_schema_bound  =  sysconv( bit , o.status  &   0x20000 ),     --   OBJMOD_SCHEMABOUND 
    uses_database_collation  =  sysconv( bit , o.status  &   0x100000 ),   --   OBJMOD_USESDBCOLL 
    is_recompiled  =  sysconv( bit , o.status  &   0x400000 ),      --   OBJMOD_NOCACHE 
    null_on_null_input  =  sysconv( bit , o.status  &   0x200000 ),    --   OBJMOD_NULLONNULL 
    execute_as_principal_id  =   x.indepid
  FROM  
    sys.sysschobjs o
  LEFT   JOIN  
    sys.syssingleobjrefs x   ON  x.depid  =  o.id  AND  x.class  =   22   AND  x.depsubid  =   0   --   SRC_OBJEXECASOWNER 
 WHERE  
    o.pclass       100   AND  
    (
        (o.type   =   '  TR  '   AND  has_access( '  TR  ' , o.id, o.pid, o.nsclass)  =   1 )  OR  
        (type   IN  ( '  P  ' , '  V  ' , '  FN  ' , '  IF  ' , '  TF  ' , '  RF  ' , '  IS  ' )  AND  has_access( '  CO  ' , o.id)  =   1 )  OR  
        (type   IN  ( '  R  ' , '  D  ' )  AND  o.pid  =   0  )
    )  

大家如果使用sqlprompt的话也可以直接显示定义而不需要执行object_definition函数

可以看到sys.sql_modules 视图也是使用系统函数object_definition 来获取代码
不幸的是,下面的代码无法工作

 select  object_definition( object_id ( '  object_definition  ' )) 

我碰巧记得有一个废弃的视图可以代替sys.sql_modules,sys.syscomments 视图
我们看一下获取到的代码

 select  object_definition( object_id ( '  sys.syscomments  ' )) 

 SELECT  
    o.id   AS   id,  
      convert ( smallint ,  case   when  o.type  in  ( '  P  ' ,  '  RF  ' )  then   1   else   0   end )  AS   number  ,  
    s.colid,
    s.status,  
      convert ( varbinary ( 8000 ), s. text )  AS   ctext,  
      convert ( smallint ,  2   +   4   *  (s.status  &   1 ))  AS   texttype,  
      convert ( smallint ,  0 )  AS   language,  
    sysconv(  bit , s.status  &   1 )  AS   encrypted,  
    sysconv(  bit ,  0 )  AS   compressed,  
    s.  text   
 FROM  
    sys.sysschobjs o
  CROSS   APPLY
      OpenRowset ( TABLE  SQLSRC, o.id,  0  ) s  
  WHERE  
    o.nsclass   =   0   AND  
    o.pclass   =   1   AND  
    o.type   IN  ( '  C  ' , '  D  ' , '  P  ' , '  R  ' , '  V  ' , '  X  ' , '  FN  ' , '  IF  ' , '  TF  ' , '  RF  ' , '  IS  ' , '  TR  ' )  AND  
    has_access(  '  CO  ' , o.id)  =   1   

 UNION   ALL   

 SELECT  
    c.  object_id   AS   id,  
      convert ( smallint , c.column_id)  AS   number  ,  
    s.colid,
    s.status,  
      convert ( varbinary ( 8000 ), s. text )  AS   ctext,  
      convert ( smallint ,  2   +   4   *  (s.status  &   1 ))  AS   texttype,  
      convert ( smallint ,  0 )  AS   language,  
    sysconv(  bit , s.status  &   1 )  AS   encrypted,  
    sysconv(  bit ,  0 )  AS   compressed,  
    s.  text   
 FROM  
    sys.computed_columns c
  CROSS   APPLY
      OpenRowset ( TABLE  SQLSRC, c. object_id  , c.column_id) s  

  UNION   ALL   

 SELECT  
    p.  object_id   AS   id,  
      convert ( smallint , p.procedure_number)  AS   number  ,  
    s.colid,
    s.status,  
      convert ( varbinary ( 8000 ), s. text )  AS   ctext,  
      convert ( smallint ,  2   +   4   *  (s.status  &   1 ))  AS   texttype,  
      convert ( smallint ,  0 )  AS   language,  
    sysconv(  bit , s.status  &   1 )  AS   encrypted,  
    sysconv(  bit ,  0 )  AS   compressed,  
    s.  text   
 FROM  
    sys.numbered_procedures p
  CROSS   APPLY
      OpenRowset ( TABLE  SQLSRC, p. object_id  , p.procedure_number) s  

  UNION   ALL   

 SELECT  
    o.id   AS   id,  
      convert ( smallint ,  case   when  o.type  in  ( '  P  ' ,  '  RF  ' )  then   1   else   0   end )  AS   number  ,  
    s.colid,
    s.status,  
      convert ( varbinary ( 8000 ), s. text )  AS   ctext,  
      convert ( smallint ,  2 )  AS   texttype,  
      convert ( smallint ,  0 )  AS   language,  
    sysconv(  bit ,  0 )  AS   encrypted,  
    sysconv(  bit ,  0 )  AS   compressed,  
    s.  text   
 FROM  
    sys.sysobjrdb o
  CROSS   APPLY
      OpenRowset ( TABLE  SQLSRC, o.id,  0  ) s  
  WHERE 
     db_id ()  =   1   AND   
    o.type   IN  ( '  P  ' , '  V  ' , '  X  ' , '  FN  ' , '  IF  ' , '  TF  ' ) 

很令人失望,他不使用object_definition, 而是使用另一个内部函数格式是OpenRowset(TABLE SQLSRC, o.id, 0)。我不会轻易放弃 --我对 OpenRowset(TABLE RSCPROP)函数进行逆向

让我们使用不同的方法去解决这个问题。在SQLSERVER里面任何东西的存储都使用8KB页面的固定格式。当存储过程不是加密的,他们一定以明文存储在数据库的某个地方--只是我们不知道在哪个地方。

我们分离数据库并使用hex编辑器进行破解(我推荐使用HxD这个hex编辑器)

HxD hex编辑器下载:

http://files.cnblogs.com/lyhabc/HxDhex%E7%BC%96%E8%BE%91%E5%99%A8.rar


我们为了要找到存储过程的位置,我在存储过程里故意使用[SELECT ‘AABBCC’ 这个字符串
以便于我们能够容易的找到存储过程的所在位置:

我们找到了:

好了,我们现在代码是存储在数据库里面。数据存储在偏移位置为0x00101AF0 的数据文件里。十进制值是01055472。我们知道数据页面是8KB,我们可以计算代码所在的页面编号

现在我们知道代码存储在页面号128页上 --我们重新附加数据库,使用DBCC PAGE看一下页面内容:

 --  只显示数据页面头 
 DBCC  TRACEON ( 3604  )
  GO 
 DBCC  PAGE(Test2,  1 ,  128 ,  0  )
  GO  

注意,对于DBCC PAGE 命令我使用了页面样式0作为执行。在这里我只想查看数据页面头--那里会有一些有趣的东西

正如所料,这是一个正常的数据页面,m_type 字段显示的值为1(type id为1表示这是数据库内部的数据页面)
更有趣的是,我们可以看到页面属于object ID 60!我们看一下object ID 60是什么对象:

 select   *   from  sys.sysobjects  where  id  =   60  


让我们看看sys.sysobjvalues的内容。注意,当你查询sys.sysobjvalues视图的时候,需要使用DAC连接,可以看到他实际上是一个内部的系统表:

 select   *   from  sys.sysobjvalues 

这里显示的很多内容我们都不需要关心,不过我们需要尝试过滤出我们的存储过程object ID为2105058535的信息:

 select   *   from  sys.sysobjvalues  where  objid  =   2105058535  

我想知道imageval 列包含了什么内容,如果我没有记错 0x2D2D 在ASCII里面应该是[-]
这提醒了我 XYZ这个存储过程刚开始的时候 ,我们尝试将这列的值转换为我们可读的形式

 select   convert ( varchar ( max ), imageval)  from  sys.sysobjvalues  where  objid  =   2105058535  

亲爱的读者,这就是XYZ存储过程的源代码,他存储在sys.sysobjvalues系统表中。
作为最后一个例子,下面是不依靠object_definition()函数和sys.sql_modules视图从而检索出用户存储过程的源代码列表

 select  
    p.name,
      cast (v.imageval  as   varchar ( MAX  ))
  from  
    sys.procedures p
  inner   join  
    sys.sysobjvalues v   on  p. object_id   =  v.objid 

第十五篇完

查看更多关于解剖SQLSERVER第十五篇SQLSERVER存储过程的源文本存放在哪里的详细内容...

  阅读:42次