ProHoster > Blogi > Haldamine > Andmete taastamine XtraDB tabelitest ilma struktuurifailita, kasutades ibd-faili bait-baidi analüüsi
Andmete taastamine XtraDB tabelitest ilma struktuurifailita, kasutades ibd-faili bait-baidi analüüsi
eelajalugu
Juhtus nii, et serverit ründas lunavaraviirus, mis "õnneliku õnnetuse" tõttu jättis osaliselt .ibd-failid (innodb-tabelite töötlemata andmefailid) puutumata, kuid samal ajal krüpteeris täielikult .fpm-failid ( struktuurifailid). Sel juhul võib .idb jagada järgmisteks osadeks:
Taastatakse standardsete tööriistade ja juhendite abil. Sellistel juhtudel on suurepärane muutuda;
osaliselt krüptitud tabelid. Enamasti on need suured tabelid, mille jaoks (nagu ma aru saan) ei olnud ründajatel täielikuks krüptimiseks piisavalt RAM-i;
Noh, täiesti krüptitud tabelid, mida ei saa taastada.
Tabelite hulka kuuluvaid valikuid oli võimalik kindlaks teha, avades selle lihtsalt mis tahes tekstiredaktoris soovitud kodeeringu all (minu puhul on see UTF8) ja lihtsalt vaadates faili tekstiväljade olemasolu kohta, näiteks:
Samuti võib faili alguses jälgida suurt hulka 0 baiti ja plokkrüpteerimisalgoritmi kasutavad viirused (kõige levinumad) mõjutavad tavaliselt ka neid.
Minu puhul jätsid ründajad iga krüptitud faili lõppu 4-baidise stringi (1, 0, 0, 0), mis lihtsustas ülesannet. Nakatamata failide otsimiseks piisas skriptist:
def opened(path):
files = os.listdir(path)
for f in files:
if os.path.isfile(path + f):
yield path + f
for full_path in opened("C:somepath"):
file = open(full_path, "rb")
last_string = ""
for line in file:
last_string = line
file.close()
if (last_string[len(last_string) -4:len(last_string)]) != (1, 0, 0, 0):
print(full_path)
Seega selgus, et leiti esimesse tüüpi kuuluvad failid. Teisega kaasneb palju käsitsitööd, kuid leitu oli juba piisav. Kõik oleks hästi, aga sa pead teadma täiesti täpne struktuur ja (muidugi) tekkis juhus, et pidin töötama tihti muutuva lauaga. Keegi ei mäletanud, kas välja tüüpi muudeti või lisati uus veerg.
Kahjuks ei saanud Wilds City sellise juhtumi puhul aidata, mistõttu seda artiklit kirjutataksegi.
Tulge asja juurde
Seal on 3 kuu taguse tabeli struktuur, mis ei kattu praegusega (võib-olla üks väli ja võib-olla ka rohkem). Tabeli struktuur:
CREATE TABLE `table_1` (
`id` INT (11),
`date` DATETIME ,
`description` TEXT ,
`id_point` INT (11),
`id_user` INT (11),
`date_start` DATETIME ,
`date_finish` DATETIME ,
`photo` INT (1),
`id_client` INT (11),
`status` INT (1),
`lead__time` TIME ,
`sendstatus` TINYINT (4)
);
sel juhul peate välja võtma:
id_point int(11);
id_user int(11);
date_start KUUPÄEV KELLAAEG;
date_finish KUUPÄEV KELLAAEG.
Taastamiseks kasutatakse .ibd-faili bait-baidi analüüsi, millele järgneb nende teisendamine loetavamaks vormiks. Kuna vajaliku leidmiseks peame analüüsima ainult andmetüüpe, nagu int ja datatime, kirjeldatakse artiklis ainult neid, kuid mõnikord viitame ka teistele andmetüüpidele, mis võivad aidata muude sarnaste juhtumite korral.
1 probleem: väljadel tüüpidega DATETIME ja TEXT olid NULL väärtused ja need jäetakse failis lihtsalt vahele, seetõttu ei olnud minu puhul võimalik taastada struktuuri. Uutes veergudes oli vaikeväärtus null ja osa tehingust võis kaotsi minna sätte innodb_flush_log_at_trx_commit = 0 tõttu, mistõttu tuleks struktuuri määramiseks kulutada lisaaega.
2 probleem: tuleb arvestada, et DELETE kaudu kustutatud read on kõik ibd failis, kuid ALTER TABLE abil nende struktuuri ei uuendata. Selle tulemusena võib andmestruktuur faili algusest lõpuni varieeruda. Kui kasutate sageli OPTIMISE TABLE'i, ei teki tõenäoliselt sellist probleemi.
Обратите внимание, mõjutab DBMS-i versioon andmete salvestamise viisi ja see näide ei pruugi teiste suuremate versioonide puhul töötada. Minu puhul kasutati mariadb 10.1.24 windowsi versiooni. Kuigi mariadb-s töötate InnoDB tabelitega, on need tegelikult nii XtraDB, mis välistab meetodi rakendatavuse InnoDB mysql-iga.
Failianalüüs
Pythonis andmetüüp baiti () kuvab Unicode'i andmed tavalise numbrikomplekti asemel. Kuigi saate faili sellel kujul vaadata, saate mugavuse huvides teisendada baidid numbriliseks, teisendades baitimassiivi tavaliseks massiiviks (list(example_byte_array)). Igal juhul sobivad analüüsiks mõlemad meetodid.
Pärast mitme ibd-faili läbivaatamist leiate järgmist.
Veelgi enam, kui jagate faili nende märksõnadega, saate enamasti ühtlased andmeplokid. Jagajana kasutame infimumi.
table = table.split("infimum".encode())
Huvitav tähelepanek: väikese andmemahuga tabelite puhul on infimum ja supremum vahel osuti ploki ridade arvule.
— katsetabel 1. reaga
- 2-realine katsetabel
Rea massiivi tabeli[0] saab vahele jätta. Pärast selle läbivaatamist ei leidnud ma ikka veel tabeli töötlemata andmeid. Tõenäoliselt kasutatakse seda plokki indeksite ja võtmete salvestamiseks.
Alustades tabelist[1] ja tõlkides selle numbrimassiiviks, võite juba märgata mõningaid mustreid, nimelt:
Need on stringi salvestatud int väärtused. Esimene bait näitab, kas arv on positiivne või negatiivne. Minu puhul on kõik numbrid positiivsed. Ülejäänud 3 baidist saate arvu määrata järgmise funktsiooni abil. Skript:
def find_int(val: str): # example '128, 1, 2, 3'
val = [int(v) for v in val.split(", ")]
result_int = val[1]*256**2 + val[2]*256*1 + val[3]
return result_int
Näiteks 128, 0, 0, 1 = 1Või 128, 0, 75, 108 = 19308.
Tabelil oli primaarvõti koos automaatse suurendamisega ja selle leiate ka siit
Pärast testtabelite andmete võrdlemist selgus, et objekt DATETIME koosneb 5 baidist ja algas 153-ga (mis näitab tõenäoliselt aastaseid intervalle). Kuna DATTIME vahemik on '1000-01-01' kuni '9999-12-31', arvan, et baitide arv võib erineda, kuid minu puhul langevad andmed perioodi 2016-2019, seega eeldame et 5 baiti piisab.
Aja määramiseks ilma sekunditeta kirjutati järgmised funktsioonid. Skript:
Olen kindel, et kui kulutate n palju aega, saab selle arusaamatuse parandada.
Järgmiseks funktsioon, mis tagastab stringist datetime objekti. Skript:
def find_data_time(val:str):
val = [int(v) for v in val.split(", ")]
day = day_(val[2])
hour = hour_(val[2], val[3])
minutes = min_(val[3], val[4])
year, month = year_month(val[1], val[2])
return datetime(year, month, day, hour, minutes)
Suutis tuvastada sageli korduvad väärtused alates int, int, datetime, datetime , tundub, et see on see, mida vajate. Pealegi ei korrata sellist järjestust kaks korda rea kohta.
Pange tähele, et selle avaldise abil otsides ei ole võimalik vajalikel väljadel NULL-väärtusi määrata, kuid minu puhul pole see kriitiline. Seejärel vaatame leidu silmusena läbi. Skript:
result = []
for val in fined:
pre_result = []
bd_int = re.findall(r"128, d*, d*, d*", val)
bd_date= re.findall(r"(153, 1[6,5,4,3]d, d*, d*, d*)", val)
for it in bd_int:
pre_result.append(find_int(bd_int[it]))
for bd in bd_date:
pre_result.append(find_data_time(bd))
result.append(pre_result)
Tegelikult on see kõik, tulemuste massiivi andmed on andmed, mida me vajame. ###PS.###
Ma saan aru, et see meetod ei sobi kõigile, kuid artikli põhieesmärk on pigem ärgitada tegutsema, mitte lahendada kõiki teie probleeme. Arvan, et kõige õigem lahendus oleks hakata ise lähtekoodi uurima mariadb, kuid piiratud aja tõttu tundus praegune meetod kõige kiirem.
Mõnel juhul saate pärast faili analüüsimist määrata ligikaudse struktuuri ja taastada selle, kasutades ühte ülaltoodud linkide standardmeetoditest. See on palju õigem ja põhjustab vähem probleeme.