Home > Database > Mysql Tutorial > 解剖SQLSERVER 第二篇 对数据页面头进行逆向(译)

解剖SQLSERVER 第二篇 对数据页面头进行逆向(译)

WBOY
Release: 2016-06-07 15:19:47
Original
1103 people have browsed it

解剖 SQLSERVER 第二 篇 对 数据 页面 头 进行 逆向 (译) http://improve.dk/reverse-engineering-sql-server-page-headers/ 在开发OrcaMDF 的时候第一个挑战就是解析 数据 页面 头部,我们知道 数据 页面 分两部分,96字节的 页面 头部和8096字节的 数据

解剖SQLSERVER 第二篇  对数据页面进行逆向(译)

http://improve.dk/reverse-engineering-sql-server-page-headers/

在开发OrcaMDF 的时候第一个挑战就是解析数据页面头部,我们知道数据页面分两部分,96字节的页面头部和8096字节的数据

大神 Paul Randal 写了一篇文章很好的描述了页头结构,然而,即使文章描述得很详细,但是我还是找不出任何关于页头存储的格式

每一个字段的数据类型和他们的顺序

 

我们可以使用DBCC PAGE命令,我填充一些随机数据进去数据页面,然后把页面dump出来

页面号是(1:101):

<span>DBCC</span> TRACEON (<span>3604</span><span>)
</span><span>DBCC</span> PAGE (TextTest, <span>1</span>, <span>101</span>, <span>2</span>)
Copy after login
Copy after login

结果分两部分,首先,我们获得DBCC PAGE已经格式化好的页面内容,dump出来的内容的第二部分是96字节的页面

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

开始动手了,我们需要找出页面头部的这些数据值对应的数据类型是什么

为了简单,我们需要关注一些唯一值以便我们不会获取到某些存在含糊的数值

我们从m_freeCnt这个字段开始,我们看到m_freeCnt的值是4066,而数据行大小是8060,所以很明显,m_freeCnt的数据类型不可能是tinyint

m_freeCnt不太可能使用int类型,一种有依据的猜测是m_freeCnt有可能使用smallint类型,这让数据行足够容纳 0-8060 字节空间的数据

smallint:从-2^15(-32,768)到2^15-1(32,767)的整数数据 存储大小为 2 个字节,本人也觉得m_freeCnt这个字段的值不可能太大

现在,4066这个十进制数换成十六进制是0x0FE2. 字节交换,变成0xE20F,现在我们知道,我们已经匹配到m_freeCnt的数据类型了

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

另外,我们已经知道页头里面第一个字段的数据类型和位置

<span>/*</span><span>
    Bytes    Content
    -----    -------
    00-27    ?
    28-29    FreeCnt (smallint)
    30-95    ?
</span><span>*/</span>
Copy after login

继续我们的查找,我们看到m_freeData =3895,换算成十六进制是0x0F37 字节交换后0x370F 

我们发现m_freeCnt这个字段存储在m_freeCnt的后面

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

使用这个技巧,我们能匹配存储在页头的并且没有含糊的唯一数据

不过 ,对于m_level这个字段,他跟m_xactReserved字段,m_reservedCnt字段,m_ghostRecCnt字段的值是一样的

我们怎麽知道0这个值哪个才属于m_level字段? 并且我们怎麽找出他的数据类型呢?这有可能是tinyint 到bigint类型

 

我们请出Visual Studio大神,然后shutdown SQLSERVER

把mdf文件拖入VS,VS会打开hex编辑器,我们根据页面偏移算出页面位置

101 * 8192 = 827,392 

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

 

看着红色框给我们标出的字节内容,他已经标识出我们的页面头内容,并且确定了我们已经跳转到正确的位置

 解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

现在我们会填一些数值进去mdf文件里面然后保存文件,请不要胡乱在生产数据库上进行测试

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

现在我们启动SQLSERVER,然后再次运行DBCC PAGE命令

<span>DBCC</span> TRACEON (<span>3604</span><span>)
</span><span>DBCC</span> PAGE (TextTest, <span>1</span>, <span>101</span>, <span>2</span>)
Copy after login
Copy after login

可以注意到,现在页面头变成了这样

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

有几个数值变了,m_xactReserved 字段先前的数值是0,现在变成了30806,将这个数字转换成十六进制并进行字节交换得到0x5678

看一下页面头,现在我们已经识别出另外一个字段的值和数据类型(smallint)

解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

我们更新一下我们页头表格

<span>/*</span><span>
    Bytes    Content
    -----    -------
    00-27    ?
    28-29    FreeCnt (smallint)
    30-49    ?
    50-51    XactReserved (smallint)
    30-95    ?
</span><span>*/</span>
Copy after login

沿着这种方法继续,把页头进行混乱修改,将修改后的页头和DBCC PAGE的输出进行关联,有可能找出这些字段的数据类型

如果你看到下面的消息,你就知道已经把页面头部搞混乱了

 解剖SQLSERVER 第二篇  对数据页面头进行逆向(译)

你应该觉得自豪的,没有人能修好你胡乱修改出来的错误

我已经编好了一个页头结构表

<span>/*</span><span>
    Bytes    Content
    -----    -------
    00    HeaderVersion (tinyint)
    01    Type (tinyint)
    02    TypeFlagBits (tinyint)
    03    Level (tinyint)
    04-05    FlagBits (smallint)
    06-07    IndexID (smallint)
    08-11    PreviousPageID (int)
    12-13    PreviousFileID (smallint)
    14-15    Pminlen (smallint)
    16-19    NextPageID (int)
    20-21    NextPageFileID (smallint)
    22-23    SlotCnt (smallint)
    24-27    ObjectID (int)
    28-29    FreeCnt (smallint)
    30-31    FreeData (smallint)
    32-35    PageID (int)
    36-37    FileID (smallint)
    38-39    ReservedCnt (smallint)
    40-43    Lsn1 (int)
    44-47    Lsn2 (int)
    48-49    Lsn3 (smallint)
    50-51    XactReserved (smallint)
    52-55    XdesIDPart2 (int)
    56-57    XdesIDPart1 (smallint)
    58-59    GhostRecCnt (smallint)
    60-95    ?
</span><span>*/</span>
Copy after login

我不确定页头的其他字节跟DBCC PAGE输出的字段对应关系,我测试过的所有页面这些字节似乎都存储为0

我认为这些应该都是为将来某种用途使用的保留字节。好了, 我们已经获得页头格式,读取每个字段就很简单了

HeaderVersion = header[<span>0</span><span>];
Type </span>= (PageType)header[<span>1</span><span>];
TypeFlagBits </span>= header[<span>2</span><span>];
Level </span>= header[<span>3</span><span>];
FlagBits </span>= BitConverter.ToInt16(header, <span>4</span><span>);
IndexID </span>= BitConverter.ToInt16(header, <span>6</span><span>);
PreviousPage </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>12</span>), BitConverter.ToInt32(header, <span>8</span><span>));
Pminlen </span>= BitConverter.ToInt16(header, <span>14</span><span>);
NextPage </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>20</span>), BitConverter.ToInt32(header, <span>16</span><span>));
SlotCnt </span>= BitConverter.ToInt16(header, <span>22</span><span>);
ObjectID </span>= BitConverter.ToInt32(header, <span>24</span><span>);
FreeCnt </span>= BitConverter.ToInt16(header, <span>28</span><span>);
FreeData </span>= BitConverter.ToInt16(header, <span>30</span><span>);
Pointer </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>36</span>), BitConverter.ToInt32(header, <span>32</span><span>));
ReservedCnt </span>= BitConverter.ToInt16(header, <span>38</span><span>);
Lsn </span>= <span>"</span><span>(</span><span>"</span> + BitConverter.ToInt32(header, <span>40</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt32(header, <span>44</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt16(header, <span>48</span>) + <span>"</span><span>)</span><span>"</span><span>;
XactReserved </span>= BitConverter.ToInt16(header, <span>50</span><span>);
XdesID </span>= <span>"</span><span>(</span><span>"</span> + BitConverter.ToInt16(header, <span>56</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt32(header, <span>52</span>) + <span>"</span><span>)</span><span>"</span><span>;
GhostRecCnt </span>= BitConverter.ToInt16(header, <span>58</span>);
Copy after login

 

大家可以看一下我写的pageheader类

<span>using</span><span> System;
</span><span>using</span><span> System.Text;
</span><span>namespace</span><span> OrcaMDF.Core.Engine.Pages
{
</span><span>public</span> <span>class</span><span> PageHeader
{
</span><span>public</span> <span>short</span> FreeCnt { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> FreeData { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> FlagBits { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> Lsn { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>int</span> ObjectID { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> PageType Type { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> Pminlen { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> IndexID { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>byte</span> TypeFlagBits { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> SlotCnt { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> XdesID { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> XactReserved { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> ReservedCnt { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>byte</span> Level { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>byte</span> HeaderVersion { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>short</span> GhostRecCnt { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> PagePointer NextPage { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> PagePointer PreviousPage { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> PagePointer Pointer { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> PageHeader(<span>byte</span><span>[] header)
{
</span><span>if</span> (header.Length != <span>96</span><span>)
</span><span>throw</span> <span>new</span> ArgumentException(<span>"</span><span>Header length must be 96.</span><span>"</span><span>);
</span><span>/*</span><span>
                Bytes    Content
                -----    -------
                00        HeaderVersion (tinyint)
                01        Type (tinyint)
                02        TypeFlagBits (tinyint)
                03        Level (tinyint)
                04-05    FlagBits (smallint)
                06-07    IndexID (smallint)
                08-11    PreviousPageID (int)
                12-13    PreviousFileID (smallint)
                14-15    Pminlen (smallint)
                16-19    NextPageID (int)
                20-21    NextPageFileID (smallint)
                22-23    SlotCnt (smallint)
                24-27    ObjectID (int)
                28-29    FreeCnt (smallint)
                30-31    FreeData (smallint)
                32-35    PageID (int)
                36-37    FileID (smallint)
                38-39    ReservedCnt (smallint)
                40-43    Lsn1 (int)
                44-47    Lsn2 (int)
                48-49    Lsn3 (smallint)
                50-51    XactReserved (smallint)
                52-55    XdesIDPart2 (int)
                56-57    XdesIDPart1 (smallint)
                58-59    GhostRecCnt (smallint)
                60-63    Checksum/Tornbits (int)
                64-95    ?
            </span><span>*/</span><span>
HeaderVersion </span>= header[<span>0</span><span>];
Type </span>= (PageType)header[<span>1</span><span>];
TypeFlagBits </span>= header[<span>2</span><span>];
Level </span>= header[<span>3</span><span>];
FlagBits </span>= BitConverter.ToInt16(header, <span>4</span><span>);
IndexID </span>= BitConverter.ToInt16(header, <span>6</span><span>);
PreviousPage </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>12</span>), BitConverter.ToInt32(header, <span>8</span><span>));
Pminlen </span>= BitConverter.ToInt16(header, <span>14</span><span>);
NextPage </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>20</span>), BitConverter.ToInt32(header, <span>16</span><span>));
SlotCnt </span>= BitConverter.ToInt16(header, <span>22</span><span>);
ObjectID </span>= BitConverter.ToInt32(header, <span>24</span><span>);
FreeCnt </span>= BitConverter.ToInt16(header, <span>28</span><span>);
FreeData </span>= BitConverter.ToInt16(header, <span>30</span><span>);
Pointer </span>= <span>new</span> PagePointer(BitConverter.ToInt16(header, <span>36</span>), BitConverter.ToInt32(header, <span>32</span><span>));
ReservedCnt </span>= BitConverter.ToInt16(header, <span>38</span><span>);
Lsn </span>= <span>"</span><span>(</span><span>"</span> + BitConverter.ToInt32(header, <span>40</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt32(header, <span>44</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt16(header, <span>48</span>) + <span>"</span><span>)</span><span>"</span><span>;
XactReserved </span>= BitConverter.ToInt16(header, <span>50</span><span>);
XdesID </span>= <span>"</span><span>(</span><span>"</span> + BitConverter.ToInt16(header, <span>56</span>) + <span>"</span><span>:</span><span>"</span> + BitConverter.ToInt32(header, <span>52</span>) + <span>"</span><span>)</span><span>"</span><span>;
GhostRecCnt </span>= BitConverter.ToInt16(header, <span>58</span><span>);
}
</span><span>public</span> <span>override</span> <span>string</span><span> ToString()
{
</span><span>var</span> sb = <span>new</span><span> StringBuilder();
sb.AppendLine(</span><span>"</span><span>m_freeCnt:\t</span><span>"</span> +<span> FreeCnt);
sb.AppendLine(</span><span>"</span><span>m_freeData:\t</span><span>"</span> +<span> FreeData);
sb.AppendLine(</span><span>"</span><span>m_flagBits:\t0x</span><span>"</span> + FlagBits.ToString(<span>"</span><span>x</span><span>"</span><span>));
sb.AppendLine(</span><span>"</span><span>m_lsn:\t\t</span><span>"</span> +<span> Lsn);
sb.AppendLine(</span><span>"</span><span>m_objId:\t</span><span>"</span> +<span> ObjectID);
sb.AppendLine(</span><span>"</span><span>m_pageId:\t(</span><span>"</span> + Pointer.FileID + <span>"</span><span>:</span><span>"</span> + Pointer.PageID + <span>"</span><span>)</span><span>"</span><span>);
sb.AppendLine(</span><span>"</span><span>m_type:\t\t</span><span>"</span> +<span> Type);
sb.AppendLine(</span><span>"</span><span>m_typeFlagBits:\t</span><span>"</span> + <span>"</span><span>0x</span><span>"</span> + TypeFlagBits.ToString(<span>"</span><span>x</span><span>"</span><span>));
sb.AppendLine(</span><span>"</span><span>pminlen:\t</span><span>"</span> +<span> Pminlen);
sb.AppendLine(</span><span>"</span><span>m_indexId:\t</span><span>"</span> +<span> IndexID);
sb.AppendLine(</span><span>"</span><span>m_slotCnt:\t</span><span>"</span> +<span> SlotCnt);
sb.AppendLine(</span><span>"</span><span>m_nextPage:\t</span><span>"</span> +<span> NextPage);
sb.AppendLine(</span><span>"</span><span>m_prevPage:\t</span><span>"</span> +<span> PreviousPage);
sb.AppendLine(</span><span>"</span><span>m_xactReserved:\t</span><span>"</span> +<span> XactReserved);
sb.AppendLine(</span><span>"</span><span>m_xdesId:\t</span><span>"</span> +<span> XdesID);
sb.AppendLine(</span><span>"</span><span>m_reservedCnt:\t</span><span>"</span> +<span> ReservedCnt);
sb.AppendLine(</span><span>"</span><span>m_ghostRecCnt:\t</span><span>"</span> +<span> GhostRecCnt);
</span><span>return</span><span> sb.ToString();
}
}
}</span>
Copy after login

 

 

第二篇完

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template