热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

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

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

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

DBCC TRACEON (3604)
DBCC PAGE (TextTest, 1, 101, 2)

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

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

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

我们从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的数据类型了

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

/*
    Bytes    Content
    -----    -------
    00-27    ?
    28-29    FreeCnt (smallint)
    30-95    ?
*/

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

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

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

不过 ,对于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

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

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

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

DBCC TRACEON (3604)
DBCC PAGE (TextTest, 1, 101, 2)

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

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

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

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

/*
    Bytes    Content
    -----    -------
    00-27    ?
    28-29    FreeCnt (smallint)
    30-49    ?
    50-51    XactReserved (smallint)
    30-95    ?
*/

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

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

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

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

/*
    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    ?
*/

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

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

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

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

using System;
using System.Text;
namespace OrcaMDF.Core.Engine.Pages
{
public class PageHeader
{
public short FreeCnt { get; private set; }
public short FreeData { get; private set; }
public short FlagBits { get; private set; }
public string Lsn { get; private set; }
public int ObjectID { get; private set; }
public PageType Type { get; private set; }
public short Pminlen { get; private set; }
public short IndexID { get; private set; }
public byte TypeFlagBits { get; private set; }
public short SlotCnt { get; private set; }
public string XdesID { get; private set; }
public short XactReserved { get; private set; }
public short ReservedCnt { get; private set; }
public byte Level { get; private set; }
public byte HeaderVersion { get; private set; }
public short GhostRecCnt { get; private set; }
public PagePointer NextPage { get; private set; }
public PagePointer PreviousPage { get; private set; }
public PagePointer Pointer { get; private set; }
public PageHeader(byte[] header)
{
if (header.Length != 96)
throw new ArgumentException("Header length must be 96.");
/*
                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    ?
            */
HeaderVersion = header[0];
Type = (PageType)header[1];
TypeFlagBits = header[2];
Level = header[3];
FlagBits = BitConverter.ToInt16(header, 4);
IndexID = BitConverter.ToInt16(header, 6);
PreviousPage = new PagePointer(BitConverter.ToInt16(header, 12), BitConverter.ToInt32(header, 8));
Pminlen = BitConverter.ToInt16(header, 14);
NextPage = new PagePointer(BitConverter.ToInt16(header, 20), BitConverter.ToInt32(header, 16));
SlotCnt = BitConverter.ToInt16(header, 22);
ObjectID = BitConverter.ToInt32(header, 24);
FreeCnt = BitConverter.ToInt16(header, 28);
FreeData = BitConverter.ToInt16(header, 30);
Pointer = new PagePointer(BitConverter.ToInt16(header, 36), BitConverter.ToInt32(header, 32));
ReservedCnt = BitConverter.ToInt16(header, 38);
Lsn = "(" + BitConverter.ToInt32(header, 40) + ":" + BitConverter.ToInt32(header, 44) + ":" + BitConverter.ToInt16(header, 48) + ")";
XactReserved = BitConverter.ToInt16(header, 50);
XdesID = "(" + BitConverter.ToInt16(header, 56) + ":" + BitConverter.ToInt32(header, 52) + ")";
GhostRecCnt = BitConverter.ToInt16(header, 58);
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine("m_freeCnt:\t" + FreeCnt);
sb.AppendLine("m_freeData:\t" + FreeData);
sb.AppendLine("m_flagBits:\t0x" + FlagBits.ToString("x"));
sb.AppendLine("m_lsn:\t\t" + Lsn);
sb.AppendLine("m_objId:\t" + ObjectID);
sb.AppendLine("m_pageId:\t(" + Pointer.FileID + ":" + Pointer.PageID + ")");
sb.AppendLine("m_type:\t\t" + Type);
sb.AppendLine("m_typeFlagBits:\t" + "0x" + TypeFlagBits.ToString("x"));
sb.AppendLine("pminlen:\t" + Pminlen);
sb.AppendLine("m_indexId:\t" + IndexID);
sb.AppendLine("m_slotCnt:\t" + SlotCnt);
sb.AppendLine("m_nextPage:\t" + NextPage);
sb.AppendLine("m_prevPage:\t" + PreviousPage);
sb.AppendLine("m_xactReserved:\t" + XactReserved);
sb.AppendLine("m_xdesId:\t" + XdesID);
sb.AppendLine("m_reservedCnt:\t" + ReservedCnt);
sb.AppendLine("m_ghostRecCnt:\t" + GhostRecCnt);
return sb.ToString();
}
}
}

第二篇完

推荐阅读
  • 在安装 SQL Server 时,选择混合验证模式可以提供更高的灵活性和管理便利性。如果您已经安装了 SQL Server 并使用单一的 Windows 身份验证模式,可以通过以下步骤将其更改为混合验证模式。 ... [详细]
  • 本文介绍了在 SQL Server 2012 客户端中格式化 SQL 查询语句的多种方法,包括内置功能和第三方工具,帮助用户提高代码可读性和维护性。 ... [详细]
  • 本文介绍了一个基于 Java SpringMVC 和 SSM 框架的综合系统,涵盖了操作日志记录、文件管理、头像编辑、权限控制、以及多种技术集成如 Shiro、Redis 等,旨在提供一个高效且功能丰富的开发平台。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • Python处理Word文档的高效技巧
    本文详细介绍了如何使用Python处理Word文档,涵盖从基础操作到高级功能的各种技巧。我们将探讨如何生成文档、定义样式、提取表格数据以及处理超链接和图片等内容。 ... [详细]
  • 编写了几个500行左右代码的程序,但基本上解决问题还是面向过程的思维,如何从问题中抽象出类,形成类的划分和设计,从而用面向对象的思维解决问题?有这方面的入门好书吗?最好是结合几个具体的案例分析的 ... [详细]
  • Qt QTableView 内嵌控件的实现方法
    本文详细介绍了在 Qt QTableView 中嵌入控件的多种方法,包括使用 QItemDelegate、setIndexWidget 和 setIndexWidget 结合布局管理器。每种方法都有其适用场景和优缺点。 ... [详细]
  • Ulysses Mac v29:革新文本编辑与写作体验
    探索Ulysses Mac v29,这款先进的纯文本编辑器为Mac用户带来了全新的写作和编辑环境。它不仅具备简洁直观的界面,还融合了Markdown等标记语言的最佳特性,支持多种格式导出,并提供强大的组织和同步功能。 ... [详细]
  • 本文详细介绍了Vim编辑器的三种主要模式及其常用命令,帮助用户更好地掌握这一强大的文本编辑工具。 ... [详细]
  • 解决Windows 10开机频繁自检问题的实用方法
    许多用户在使用Windows 10系统时,经常会遇到开机时自动进行磁盘检查的情况。这不仅影响了开机速度,还可能带来不必要的麻烦。本文将详细介绍如何通过简单的注册表修改来避免每次开机时的磁盘自检,提升系统启动效率。 ... [详细]
  • 本文详细介绍了流编辑器sed中的G、H、g、h命令,探讨了它们的工作原理及应用场景。通过实例解析和图解分析,帮助读者掌握这些高级命令的使用方法。 ... [详细]
  • HTML基础入门指南
    本文将深入浅出地介绍HTML的基础知识,包括其定义、开发工具、制定机构、特性、基本标签及更多实用内容。 ... [详细]
  • 本文介绍了解决Oracle 10G数据库中ORA-12541 TNS: no listener错误的详细步骤。该错误通常发生在监听器服务未正确启动或配置不当的情况下,文章将指导您通过检查服务状态、配置注册表和启动监听器来解决问题。 ... [详细]
  • Python自动化测试入门:Selenium环境搭建
    本文详细介绍如何在Python环境中安装和配置Selenium,包括开发工具PyCharm的安装、Python环境的设置以及Selenium包的安装方法。此外,还提供了编写和运行第一个自动化测试脚本的步骤。 ... [详细]
  • java文本编辑器,java文本编辑器设计思路
    java文本编辑器,java文本编辑器设计思路 ... [详细]
author-avatar
Amyjionydp
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有