Maison  >  Article  >  base de données  >  解剖SQLSERVER 第二篇 对数据页面头进行逆向(译)

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

WBOY
WBOYoriginal
2016-06-07 15:19:471060parcourir

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

结果分两部分,首先,我们获得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>

继续我们的查找,我们看到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>)

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

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

沿着这种方法继续,把页头进行混乱修改,将修改后的页头和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>

我不确定页头的其他字节跟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>);

 

大家可以看一下我写的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>

 

 

第二篇完

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn