http://improve.dk/where-does-sql-server-store-the-source-for-stored-procedures/html
目前我正在擴展OrcaMDF Studio的功能 不單隻支持系統表,DMVs 和用戶表 並且也要支持存儲過程。那很容易,咱們只須要查詢sys.procedures --或者查詢sys.sysschobjs,sql
由於當SQLSERVER沒有在運行的時候咱們是不能查詢sys.procedures 的數據庫
然而,我不想只是列出存儲過程名稱,我也須要顯示存儲過程裏面的源代碼。這帶來了新的任務--檢索源代碼。源代碼存儲在哪裏?編輯器
我在Google上找不到任何有用的資料,因此咱們只能依靠本身觀察了!ide
我已經建立了一個新的空數據庫 這個數據庫有一個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的時候,咱們能夠看到存儲過程的object ID 是2105058535加密
select * from sys.procedures
到目前爲止一切順利。而後咱們能夠檢索存儲過程的定義 使用查詢sys.sql_modules 視圖返回nvarchar(MAX)類型的定義文本spa
select * from sys.sql_modules where object_id = 2105058535
上面就是XYZ存儲過程的源代碼!等下,我能夠從sys.sysschobjs表裏獲取存儲過程的object ID,我不須要訪問
sys.sql_modules ,sys.sql_modules 只是一個視圖而不是系統表。咱們看一下sys.sql_modules 視圖是如何獲取定義的:3d
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函數code
能夠看到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,咱們能夠計算代碼所在的頁面編號
01055472 / 8192 = 128
如今咱們知道代碼存儲在頁面號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
第十五篇完