文章出處

解剖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的時候,我們可以看到存儲過程的object ID 是2105058535

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,我們可以計算代碼所在的頁面編號

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

 

 

第十五篇完


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()