第五站 使用winHex利器加深理解数据页
這篇我來(lái)介紹一個(gè)winhex利器,這個(gè)工具網(wǎng)上有介紹,用途大著呢,可以用來(lái)玩數(shù)據(jù)修復(fù),恢復(fù)刪除文件等等。。。。它能夠?qū)⒁粋€(gè)file解析成hex形式,這樣你就可以對(duì)hex進(jìn)行修改,然后你就可以看到修復(fù)后的結(jié)果,為什么要在sqlserver系列中說(shuō)這個(gè)呢???很簡(jiǎn)單呀,sqlserver的DB本質(zhì)上也是一個(gè)mdf文件,對(duì)吧,既然是文件,我就可以利用winhex對(duì)它進(jìn)行隨意的修改,然后你也知道sqlserver的數(shù)據(jù)都是以數(shù)據(jù)頁(yè)的形式封裝的,那我就可以修改它的數(shù)據(jù)頁(yè),對(duì)不對(duì),這樣我就可以隨便改變記錄的順序,包括槽位,記錄,頁(yè)頭等等。。。說(shuō)干就干吧!!!
一:準(zhǔn)備數(shù)據(jù)
我計(jì)劃在數(shù)據(jù)庫(kù)中插入三條測(cè)試數(shù)據(jù),如圖:
DROP TABLE dbo.PersonCREATE 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接下來(lái)通過(guò)DBCC命令,查看下三條記錄的數(shù)據(jù)頁(yè)情況,如下圖:
DBCC TRACEON(3604) DBCC IND(Ctrip,Person,-1) DBCC PAGE(Ctrip,1,78,2)DATA:Memory Dump @0x00000000100EA00000000000100EA000: 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)我想大家現(xiàn)在都清楚了,數(shù)據(jù)頁(yè)中的一條條存儲(chǔ)記錄都是通過(guò)頁(yè)尾的槽位指向的,具體可以參見(jiàn)前幾篇對(duì)數(shù)據(jù)頁(yè)的介紹,比如你看到頁(yè)尾的:8d0076006000了嗎?要注意,這些都是按照字節(jié)逆序來(lái)的。
6000 這個(gè)就是slot0,也就是 (0x0) - 96 (0x60)
0x76 這個(gè)就是slot1,也就是(0x1) - 118 (0x76)
0x8d 這個(gè)就是slot2,也就是(0x2) - 141 (0x8d)
是不是有點(diǎn)意思,如果你一定要看到slot具體指向的內(nèi)容,你可以繼續(xù)用DBCC命令,一清二楚。
DBCC PAGE(Ctrip,1,78,1)PAGE: (1:78)BUFFER:BUF @0x0000000083FD8E00bpage = 0x0000000083ADC000 bhash = 0x0000000000000000 bpageno = (1:78) bdbid = 8 breferences = 0 bUse1 = 2495 bstat = 0x1c0000b blog = 0xbbbbbbbb bnext = 0x0000000000000000PAGE HEADER:Page @0x0000000083ADC000m_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 = 0Allocation StatusGAM (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_LOGGEDDATA:Slot 0, Offset 0x60, Length 22, DumpStyle BYTERecord Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Record Size = 22 Memory Dump @0x000000000F7FC0600000000000000000: 30000c00 01000000 14000000 03000001 ?0............... 0000000000000010: 00160061 6d79????????????????????????...amySlot 1, Offset 0x76, Length 23, DumpStyle BYTERecord Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Record Size = 23 Memory Dump @0x000000000F7FC0760000000000000000: 30000c00 02000000 19000000 03000001 ?0............... 0000000000000010: 00170061 6e6e61??????????????????????...annaSlot 2, Offset 0x8d, Length 24, DumpStyle BYTERecord Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Record Size = 24 Memory Dump @0x000000000F7FC08D0000000000000000: 30000c00 03000000 1c000000 03000001 ?0............... 0000000000000010: 00180073 6d617274 ???????????????????...smartOFFSET TABLE:Row - Offset 2 (0x2) - 141 (0x8d) 1 (0x1) - 118 (0x76) 0 (0x0) - 96 (0x60)DBCC 執(zhí)行完畢。如果 DBCC 輸出了錯(cuò)誤信息,請(qǐng)與系統(tǒng)管理員聯(lián)系。仔細(xì)觀(guān)察下上面的紅色字體,有沒(méi)有總結(jié)出各個(gè)slot槽位對(duì)應(yīng)的記錄內(nèi)容,比如:
- slot0槽位指向的記錄內(nèi)容:amy => 616d79。 
- slot1槽位指向的記錄內(nèi)容:anna => 616e6e61。 
- slot2槽位指向的記錄內(nèi)容:smart => 736d617274。 
這里你要知道,winhex中都是16進(jìn)制表示的,所以2個(gè)16進(jìn)制對(duì)應(yīng)一個(gè)字節(jié)。
二:使用WinHex修改數(shù)據(jù)
我們大家都知道,sqlserver引擎會(huì)通過(guò)掃描slot槽位來(lái)呈現(xiàn)數(shù)據(jù),就像上面的記錄那樣,依次掃描slot0...slot1....slot2...,如下圖:
上面這個(gè)截圖沒(méi)什么稀奇的地方,大家也覺(jué)得見(jiàn)怪不怪了,那下面就有一個(gè)想法來(lái)了,如果我通過(guò)winHex來(lái)交換slot0和slot1的順序,那效果會(huì)是怎樣???按照常理說(shuō),這時(shí)候引擎還是按照slot槽位依次掃描,這時(shí)候應(yīng)該會(huì)將ID=2的記錄先噴出來(lái),然后再?lài)姵鯥D=1,ID=3。。。事實(shí)是不是這樣子呢?好奇吧,我們來(lái)看看。。。
三:相關(guān)步驟
我們知道Ctrip數(shù)據(jù)庫(kù)是聯(lián)機(jī)的,要修改它必須先脫機(jī),然后再關(guān)掉數(shù)據(jù)頁(yè)的一致性校驗(yàn)(這個(gè)也是數(shù)據(jù)庫(kù)的保護(hù)機(jī)制,防止第三方惡意的去篡改數(shù)據(jù)),這個(gè)應(yīng)該大家都明白,如下圖:
從網(wǎng)上下載一個(gè)破解版的winhex,然后打開(kāi)本地的Ctrip.mdf文件,調(diào)整winhex的編輯模式為默認(rèn)的可讀寫(xiě),如圖:
我們知道一個(gè)數(shù)據(jù)頁(yè)的大小是8KB=8192B,那么第78號(hào)數(shù)據(jù)頁(yè)的起始位置的偏移量應(yīng)該就是:78*8192=638976,然后通過(guò)快捷鍵Alt+G打開(kāi)偏移量列表,鍵入638976,如下圖:
找到記錄的內(nèi)容之后,我們?cè)賮?lái)找槽位,槽位的開(kāi)始位置在78號(hào)數(shù)據(jù)頁(yè)的末尾,那怎么算呢?這個(gè)算法也很簡(jiǎn)單,offset=79*8192-1=647167。說(shuō)干就干。
當(dāng)你真的找到了偏移量,是不是很興奮呢?下面要做的就是把60和76交換一下,也就是將slot0和slot1交換,看看怎么樣????
交換完畢后,ctrl+s保存,然后讓Ctrip數(shù)據(jù)庫(kù)聯(lián)機(jī),并使用Sql語(yǔ)句查看下現(xiàn)在的效果???
當(dāng)你看到這張圖的時(shí)候,是不是已經(jīng)瘋了。。。。這樣我就非常肯定的論證了,引擎真的就是通過(guò)依次掃描slot的槽位來(lái)指向記錄的,如果你大概理解了上面的操作,現(xiàn)在你可以修改任意數(shù)據(jù)頁(yè)的數(shù)據(jù)了,只要你找得到數(shù)據(jù)頁(yè)的偏移量,然后任由你發(fā)揮啦!
總結(jié)
以上是生活随笔為你收集整理的第五站 使用winHex利器加深理解数据页的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: Redis背后的故事
- 下一篇: .NET Core + Kubernet
