37

第五站 使用winHex利器加深理解数据页

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654078461&%3Bidx=3&%3Bsn=89d48c7b4f8d81cec622c91584a9a047
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

这篇我来介绍一个winhex利器,这个工具网上有介绍,用途大着呢,可以用来玩数据修复,恢复删除文件等等。。。。它能够将一个file解析成hex形式,这样你就可以对hex进行修改,然后你就可以看到修复后的结果,为什么要在sqlserver系列中说这个呢???很简单呀,sqlserver的DB本质上也是一个mdf文件,对吧,既然是文件,我就可以利用winhex对它进行随意的修改,然后你也知道sqlserver的数据都是以数据页的形式封装的,那我就可以修改它的数据页,对不对,这样我就可以随便改变记录的顺序,包括槽位,记录,页头等等。。。说干就干吧!!!

一:准备数据

我计划在数据库中插入三条测试数据,如图:

DROP TABLE dbo.Person

CREATE TABLE Person(ID INT IDENTITY,NAME VARCHAR(5),Age INT)

INSERT dbo.Person VALUES('amy',20)
INSERT dbo.Person VALUES('anna',25)
INSERT dbo.Person VALUES('smart',28)

SELECT * FROM dbo.Person

接下来通过DBCC命令,查看下三条记录的数据页情况,如下图:


DBCC TRACEON(3604)
DBCC IND(Ctrip,Person,-1)
DBCC PAGE(Ctrip,1,78,2)

DATA:


Memory Dump @0x00000000100EA000

00000000100EA000: 01010400 00800001 00000000 00000c00 †................
00000000100EA010: 00000000 00000300 3f000000 551fa500 †........?...U...
00000000100EA020: 4e000000 01000000 8e000000 66000000 †N...........f...
00000000100EA030: 03000000 00000000 00000000 00000000 †................
00000000100EA040: 01000000 00000000 00000000 00000000 †................
00000000100EA050: 00000000 00000000 00000000 00000000 †................
00000000100EA060: 30000c00 01000000 14000000 03000001 †0...............
00000000100EA070: 00160061 6d793000 0c000200 00001900 †...amy0.........
00000000100EA080: 00000300 00010017 00616e6e 6130000c †.........anna0..
00000000100EA090: 00030000 001c0000 00030000 01001800 †................
00000000100EA0A0: 736d6172 74000000 00000000 00000000 †smart...........
00000000100EA0B0: 00000000 00000000 00000000 00000000 †................

....

00000000100EBFC0: 20202020 20202020 20202020 20202020 †
00000000100EBFD0: 20202020 20200000 00000000 00000000 † ..........
00000000100EBFE0: 00000000 00000000 00000000 00000000 †................
00000000100EBFF0: 00000000 00000000 1f0b8d00 76006000 †............v.`.

OFFSET TABLE:

Row - Offset
2 (0x2) - 141 (0x8d)
1 (0x1) - 118 (0x76)
0 (0x0) - 96 (0x60)

我想大家现在都清楚了,数据页中的一条条存储记录都是通过页尾的槽位指向的,具体可以参见前几篇对数据页的介绍,比如你看到页尾的:8d0076006000了吗?要注意,这些都是按照字节逆序来的。

  1. 6000 这个就是slot0,也就是 (0x0) - 96 (0x60)

  1. 0x76 这个就是slot1,也就是(0x1) - 118 (0x76)

  1. 0x8d 这个就是slot2,也就是(0x2) - 141 (0x8d)

是不是有点意思,如果你一定要看到slot具体指向的内容,你可以继续用DBCC命令,一清二楚。



DBCC PAGE(Ctrip,1,78,1)

PAGE: (1:78)


BUFFER:


BUF @0x0000000083FD8E00

bpage = 0x0000000083ADC000 bhash = 0x0000000000000000 bpageno = (1:78)
bdbid = 8 breferences = 0 bUse1 = 2495
bstat = 0x1c0000b blog = 0xbbbbbbbb bnext = 0x0000000000000000

PAGE HEADER:


Page @0x0000000083ADC000

m_pageId = (1:78) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 63 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594042056704
Metadata: PartitionId = 72057594041204736 Metadata: IndexId = 0
Metadata: ObjectId = 341576255 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 12 m_slotCnt = 3 m_freeCnt = 8021
m_freeData = 165 m_reservedCnt = 0 m_lsn = (142:102:3)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 0

Allocation Status

GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:1) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED

DATA:


Slot 0, Offset 0x60, Length 22, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 22
Memory Dump @0x000000000F7FC060

0000000000000000: 30000c00 01000000 14000000 03000001 †0...............
0000000000000010: 001600
61 6d79††††††††††††††††††††††††...amy

Slot 1, Offset 0x76, Length 23, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23
Memory Dump @0x000000000F7FC076

0000000000000000: 30000c00 02000000 19000000 03000001 †0...............
0000000000000010: 001700
61 6e6e61††††††††††††††††††††††...anna

Slot 2, Offset 0x8d, Length 24, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 24
Memory Dump @0x000000000F7FC08D

0000000000000000: 30000c00 03000000 1c000000 03000001 †0...............
0000000000000010: 001800
73 6d617274 †††††††††††††††††††...smart

OFFSET TABLE:

Row - Offset
2 (0x2) - 141 (0x8d)
1 (0x1) - 118 (0x76)
0 (0x0) - 96 (0x60)


DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

仔细观察下上面的红色字体,有没有总结出各个slot槽位对应的记录内容,比如:

  • slot0槽位指向的记录内容:amy => 616d79。

  • slot1槽位指向的记录内容:anna => 616e6e61。

  • slot2槽位指向的记录内容:smart => 736d617274。

这里你要知道,winhex中都是16进制表示的,所以2个16进制对应一个字节。

二:使用WinHex修改数据

我们大家都知道,sqlserver引擎会通过扫描slot槽位来呈现数据,就像上面的记录那样,依次扫描slot0...slot1....slot2...,如下图:

AfiQniq.png!web

上面这个截图没什么稀奇的地方,大家也觉得见怪不怪了,那下面就有一个想法来了,如果我通过winHex来交换slot0和slot1的顺序,那效果会是怎样???按照常理说,这时候引擎还是按照slot槽位依次扫描,这时候应该会将ID=2的记录先喷出来,然后再喷出ID=1,ID=3。。。事实是不是这样子呢?好奇吧,我们来看看。。。

三:相关步骤

  1. 我们知道Ctrip数据库是联机的,要修改它必须先脱机,然后再关掉数据页的一致性校验( 这个也是数据库的保护机制,防止第三方恶意的去篡改数据 ),这个应该大家都明白,如下图:

UfIv2eu.png!web

  1. 从网上下载一个破解版的winhex,然后打开本地的Ctrip.mdf文件,调整winhex的编辑模式为默认的可读写,如图:

fyuYfmn.png!web

  1. 我们知道一个数据页的大小是8KB=8192B,那么第78号数据页的起始位置的偏移量应该就是:78*8192=638976,然后通过快捷键Alt+G打开偏移量列表,键入638976,如下图:

26bYbuI.png!web

找到记录的内容之后,我们再来找槽位,槽位的开始位置在78号数据页的末尾,那怎么算呢?这个算法也很简单,offset=79*8192-1=647167。说干就干。

qeaQNvF.png!web

当你真的找到了偏移量,是不是很兴奋呢?下面要做的就是把60和76交换一下,也就是将slot0和slot1交换,看看怎么样????

  1. 交换完毕后,ctrl+s保存,然后让Ctrip数据库联机,并使用Sql语句查看下现在的效果???

RnaeqqR.png!web

当你看到这张图的时候,是不是已经疯了。。。。这样我就非常肯定的论证了,引擎真的就是通过依次扫描slot的槽位来指向记录的,如果你大概理解了上面的操作,现在你可以修改任意数据页的数据了,只要你找得到数据页的偏移量,然后任由你发挥啦!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK