zoukankan      html  css  js  c++  java
  • 解剖SQLSERVER 第十五篇 SQLSERVER存储过程的源文本存放在哪里?(译)

    解剖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

     

    第十五篇完

  • 相关阅读:
    Admin注册和路由分发详解
    自定义Xadmin
    苑昊老师的博客
    pip 国内源 配置
    Django模型层(2)
    Django 中间件
    Django form表单
    整理的最全 python常见面试题(基本必考)
    AJAX
    Cookie、Session和自定义分页
  • 原文地址:https://www.cnblogs.com/lyhabc/p/4025919.html
Copyright © 2011-2022 走看看