解剖SQLSERVER 第十五篇 SQLSERVER存儲過程的源文本存放在哪裏?(譯)

解剖SQLSERVER 第十五篇  SQLSERVER存儲過程的源文本存放在哪裏?(譯)

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

 

 

第十五篇完

相關文章
相關標籤/搜索