解剖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的数据文件。在这个数据库里面,我已经创建了一个单独的存储过程就像这样:

  1. SET ANSI_NULLS ON
  2. GO
  3. SET QUOTED_IDENTIFIER ON
  4. GO
  5. -- =============================================
  6. -- Author:
  7. -- Create date:
  8. -- Description:
  9. -- =============================================
  10. CREATE PROCEDURE XYZ
  11. AS
  12. BEGIN
  13. -- SET NOCOUNT ON added to prevent extra result sets from
  14. -- interfering with SELECT statements.
  15. SET NOCOUNT ON;
  16.  
  17. -- Insert statements for procedure here
  18. SELECT 'AABBCC' AS Output
  19. END

现在,当我select * from sys.procedures的时候,我们可以看到存储过程的object ID 是2105058535

  1. select * from sys.procedures

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

  1. select * from sys.sql_modules where object_id = 2105058535

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

  1. select object_definition(object_id('sys.sql_modules'))
  1. SELECT
  2. object_id = o.id,
  3. definition = Object_definition(o.id),
  4. uses_ansi_nulls = Sysconv(bit, o.status & 0x40000), -- OBJMOD_ANSINULLS
  5. uses_quoted_identifier = sysconv(bit, o.status & 0x80000), -- OBJMOD_QUOTEDIDENT
  6. is_schema_bound = sysconv(bit, o.status & 0x20000), -- OBJMOD_SCHEMABOUND
  7. uses_database_collation = sysconv(bit, o.status & 0x100000), -- OBJMOD_USESDBCOLL
  8. is_recompiled = sysconv(bit, o.status & 0x400000), -- OBJMOD_NOCACHE
  9. null_on_null_input = sysconv(bit, o.status & 0x200000), -- OBJMOD_NULLONNULL
  10. execute_as_principal_id = x.indepid
  11. FROM
  12. sys.sysschobjs o
  13. LEFT JOIN
  14. sys.syssingleobjrefs x ON x.depid = o.id AND x.class = 22 AND x.depsubid = 0 -- SRC_OBJEXECASOWNER
  15. WHERE
  16. o.pclass <> 100 AND
  17. (
  18. (o.type = 'TR' AND has_access('TR', o.id, o.pid, o.nsclass) = 1) OR
  19. (type IN ('P','V','FN','IF','TF','RF','IS') AND has_access('CO', o.id) = 1) OR
  20. (type IN ('R','D') AND o.pid = 0)
  21. )

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

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

  1. select object_definition(object_id('object_definition'))

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

  1. select object_definition(object_id('sys.syscomments'))
  1. SELECT
  2. o.id AS id,
  3. convert(smallint, case when o.type in ('P', 'RF') then 1 else 0 end) AS number,
  4. s.colid,
  5. s.status,
  6. convert(varbinary(8000), s.text) AS ctext,
  7. convert(smallint, 2 + 4 * (s.status & 1)) AS texttype,
  8. convert(smallint, 0) AS language,
  9. sysconv(bit, s.status & 1) AS encrypted,
  10. sysconv(bit, 0) AS compressed,
  11. s.text
  12. FROM
  13. sys.sysschobjs o
  14. CROSS APPLY
  15. OpenRowset(TABLE SQLSRC, o.id, 0) s
  16. WHERE
  17. o.nsclass = 0 AND
  18. o.pclass = 1 AND
  19. o.type IN ('C','D','P','R','V','X','FN','IF','TF','RF','IS','TR') AND
  20. has_access('CO', o.id) = 1
  21.  
  22. UNION ALL
  23.  
  24. SELECT
  25. c.object_id AS id,
  26. convert(smallint, c.column_id) AS number,
  27. s.colid,
  28. s.status,
  29. convert(varbinary(8000), s.text) AS ctext,
  30. convert(smallint, 2 + 4 * (s.status & 1)) AS texttype,
  31. convert(smallint, 0) AS language,
  32. sysconv(bit, s.status & 1) AS encrypted,
  33. sysconv(bit, 0) AS compressed,
  34. s.text
  35. FROM
  36. sys.computed_columns c
  37. CROSS APPLY
  38. OpenRowset(TABLE SQLSRC, c.object_id, c.column_id) s
  39.  
  40. UNION ALL
  41.  
  42. SELECT
  43. p.object_id AS id,
  44. convert(smallint, p.procedure_number) AS number,
  45. s.colid,
  46. s.status,
  47. convert(varbinary(8000), s.text) AS ctext,
  48. convert(smallint, 2 + 4 * (s.status & 1)) AS texttype,
  49. convert(smallint, 0) AS language,
  50. sysconv(bit, s.status & 1) AS encrypted,
  51. sysconv(bit, 0) AS compressed,
  52. s.text
  53. FROM
  54. sys.numbered_procedures p
  55. CROSS APPLY
  56. OpenRowset(TABLE SQLSRC, p.object_id, p.procedure_number) s
  57.  
  58. UNION ALL
  59.  
  60. SELECT
  61. o.id AS id,
  62. convert(smallint, case when o.type in ('P', 'RF') then 1 else 0 end) AS number,
  63. s.colid,
  64. s.status,
  65. convert(varbinary(8000), s.text) AS ctext,
  66. convert(smallint, 2) AS texttype,
  67. convert(smallint, 0) AS language,
  68. sysconv(bit, 0) AS encrypted,
  69. sysconv(bit, 0) AS compressed,
  70. s.text
  71. FROM
  72. sys.sysobjrdb o
  73. CROSS APPLY
  74. OpenRowset(TABLE SQLSRC, o.id, 0) s
  75. WHERE
  76. db_id() = 1 AND
  77. 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,我们可以计算代码所在的页面编号

01055472 / 8192 = 128

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

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

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

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

  1. select * from sys.sysobjects where id = 60

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

  1. select * from sys.sysobjvalues

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

  1. select * from sys.sysobjvalues where objid = 2105058535

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

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

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

  1. select
  2. p.name,
  3. cast(v.imageval as varchar(MAX))
  4. from
  5. sys.procedures p
  6. inner join
  7. sys.sysobjvalues v on p.object_id = v.objid

第十五篇完

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

  1. 解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)

    解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译) http://improve.dk/orcamdf-rawdatabase-a-swiss-a ...

  2. 解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译)

    解剖SQLSERVER 第十二篇   OrcaMDF 行压缩支持(译) http://improve.dk/orcamdf-row-compression-support/ 在这两个月的断断续续的开发 ...

  3. 解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译)

    解剖SQLSERVER 第十四篇    Vardecimals 存储格式揭秘(译) http://improve.dk/how-are-vardecimals-stored/ 在这篇文章,我将深入研究 ...

  4. Python之路【第十五篇】:Web框架

    Python之路[第十五篇]:Web框架   Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1 2 3 4 5 6 ...

  5. 第十五篇 Integration Services:SSIS参数

    本篇文章是Integration Services系列的第十五篇,详细内容请参考原文. 简介在前一篇,我们使用SSDT-BI将第一个SSIS项目My_First_SSIS_Project升级/转换到S ...

  6. 【译】第十五篇 Integration Services:SSIS参数

    本篇文章是Integration Services系列的第十五篇,详细内容请参考原文. 简介在前一篇,我们使用SSDT-BI将第一个SSIS项目My_First_SSIS_Project升级/转换到S ...

  7. 跟我学SpringCloud | 第十五篇:微服务利剑之APM平台(一)Skywalking

    目录 SpringCloud系列教程 | 第十五篇:微服务利剑之APM平台(一)Skywalking 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要 ...

  8. Egret入门学习日记 --- 第十五篇(书中 6.1~6.9节 内容)

    第十五篇(书中 6.1~6.9节 内容) 好的,昨天完成了第五章. 今天来看第六章. 总结重点: 1.如何对组件进行分组? 跟着做: 重点1:如何对组件进行分组? 首先,选中你想要组合的组件. 然后点 ...

  9. 淘宝(阿里百川)手机客户端开发日记第十五篇 JSON解析(四)

    解析一个从淘宝传递的JSON (大家如有兴趣可以测试下):{ "tae_item_detail_get_response": { "data": { " ...

随机推荐

  1. Python:进程

    由于GIL的存在,python一个进程同时只能执行一个线程.因此在python开发时,计算密集型的程序常用多进程,IO密集型的使用多线程 1.多进程创建: #创建方法1:将要执行的方法作为参数传给Pr ...

  2. 转载:align

    1. 原理    int a;    int size = 8;        <----> 1000(bin)计算a以size为倍数的下界数:    就让这个数(要计算的这个数)表示成二 ...

  3. Java Reflection

    Java语言的反射机制 1. Java反射的含义:获取应用中正在运行的Java对象. 2. Java反射机制: 在运行的程序中,对于任意的类,都可以知道这个类的属性.方法以及构造函数,对于任意对象都可 ...

  4. python 生成 xml文件 属性的顺序问题

    需求很奇葩. 文档示例 <ITEM key="username" eng="User Name" chn="用户名" val=&quo ...

  5. Git 在团队中的最佳实践--如何正确使用Git Flow[转]

    原文地址:http://www.cnblogs.com/cnblogsfans/p/5075073.html Git的优点 Git的优点很多,但是这里只列出我认为非常突出的几点. 由于是分布式,所有本 ...

  6. MVc Forms Membership rolemanage 角色权限验证管理

    Forms  登录验证Membership 权限验证rolemanage 角色管理 以往的项目中只有单纯的Forms 验证今天想把这三个结合到mvc 中发现要导入aspnet_ 相关表,但是有个问题验 ...

  7. JavaScript中关于时间的知识点

    1.计算时间差,天数,小时数,余数 var begintime_ms = Date.parse(new Date(begintime.replace(/-/g, "/"))); / ...

  8. JavaScript事件——冒泡、捕获

    本节要点:1.干预系统的事件处理机制 (一)DOM事件流 (二)停止事件冒泡 (三)阻止事件的默认行为 1.干预系统的事件处理机制 (一)DOM事件流 DOM模型是一个树形结构,在DOM模型中,HTM ...

  9. 论人品 | | noip1015模拟考

    第一题:火车进站... 由于有了老师给的助攻,第一题的时间为半小时,主要在读题了.... jzoj1146 第二题:car 难在正方形的计算? 第二题时间:1.5hour 第三题:sort排序?

  10. SGA(System Global Area)

    系统激活时在内存内规划的一个固定的区域,用于存储每位使用者所需存取的数据和必备的系统信息.这个区域成为系统全局区. 数据块缓存区:存放读取数据文件的数据块副本,或者曾经处理过的数据.有效减少读取数据时 ...