Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Forsaga

Það gerðist svo að þjónninn varð fyrir árás lausnarhugbúnaðarvíruss, sem fyrir „heppni“ skildi .ibd skrárnar (hráar gagnaskrár innodb töflur) að hluta eftir ósnertar, en dulkóðaði á sama tíma .fpm skrárnar algjörlega ( uppbyggingarskrár). Í þessu tilviki gæti .idb verið skipt í:

  • háð endurreisn með stöðluðum verkfærum og leiðbeiningum. Fyrir slík tilvik, það er frábært verða;
  • dulkóðaðar töflur að hluta. Aðallega eru þetta stórar töflur, sem (eins og ég skil) höfðu árásarmennirnir ekki nóg vinnsluminni fyrir fulla dulkóðun;
  • Jæja, fullkomlega dulkóðaðar töflur sem ekki er hægt að endurheimta.

Það var hægt að ákvarða hvaða valmöguleika töflurnar tilheyra með því einfaldlega að opna hana í hvaða textaritli sem er undir viðeigandi kóðun (í mínu tilfelli er það UTF8) og einfaldlega skoða skrána fyrir tilvist textareita, til dæmis:

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Einnig, í upphafi skráarinnar geturðu fylgst með fjölda 0 bæta, og vírusar sem nota blokkdulkóðunaralgrímið (algengasta) hafa venjulega áhrif á þá líka.
Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Í mínu tilviki skildu árásarmennirnir eftir 4-bæta streng (1, 0, 0, 0) í lok hverrar dulkóðaðrar skráar, sem einfaldaði verkefnið. Til að leita að ósýktum skrám var handritið nóg:

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)

Þannig reyndist það finna skrár sem tilheyra fyrstu gerðinni. Annað felur í sér mikla handavinnu, en það sem fannst var nú þegar nóg. Allt væri í lagi, en þú þarft að vita það algjörlega nákvæm uppbygging og (auðvitað) kom upp mál að ég þurfti að vinna með borð sem skiptist oft. Enginn mundi hvort reitgerðinni var breytt eða nýjum dálki bætt við.

Wilds City gat því miður ekki hjálpað til við slíkt mál, þess vegna er þessi grein skrifuð.

Komdu að málinu

Það er uppbygging á töflu frá 3 mánuðum síðan sem er ekki í samræmi við núverandi (hugsanlega einn reitur, og hugsanlega fleiri). Uppbygging borðs:

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)
); 

í þessu tilfelli þarftu að draga út:

  • id_point int (11);
  • id_user int (11);
  • date_start DATETIME;
  • date_finish DATETIME.

Til að endurheimta er bæti-fyrir-bæta greining á .ibd skránni notuð, fylgt eftir með því að breyta þeim í læsilegra form. Þar sem til að finna það sem við þurfum þurfum við aðeins að greina gagnagerðir eins og int og datatime, greinin mun aðeins lýsa þeim, en stundum munum við einnig vísa til annarra gagnategunda, sem geta hjálpað í öðrum svipuðum atvikum.

Dæmi 1: reitir með tegundunum DATETIME og TEXT höfðu NULL gildi og þeim er einfaldlega sleppt í skránni, vegna þessa var ekki hægt að ákvarða uppbygginguna sem á að endurheimta í mínu tilfelli. Í nýju dálkunum var sjálfgefið gildi núll og hluti af viðskiptunum gæti tapast vegna stillingarinnar innodb_flush_log_at_trx_commit = 0, þannig að auka tíma þyrfti að eyða til að ákvarða uppbygginguna.

Dæmi 2: það ætti að taka með í reikninginn að línur sem eytt er með DELETE verða allar í ibd skránni, en með ALTER TABLE verður uppbygging þeirra ekki uppfærð. Þar af leiðandi getur uppbygging gagna verið breytileg frá upphafi skráar til enda hennar. Ef þú notar oft OPTIMIZE TABLE, þá er ólíklegt að þú lendir í slíku vandamáli.

Borgaðu eftirtekt, DBMS útgáfan hefur áhrif á hvernig gögn eru geymd og þetta dæmi virkar kannski ekki fyrir aðrar helstu útgáfur. Í mínu tilviki var Windows útgáfan af mariadb 10.1.24 notuð. Einnig, þó að þú vinnur með InnoDB töflum í mariadb, þá eru þær það í raun XtraDB, sem útilokar nothæfi aðferðarinnar með InnoDB mysql.

Skráargreining

Í python, gagnategund bæti() sýnir Unicode gögn í stað venjulegs talnasetts. Þó að þú getir skoðað skrána á þessu formi geturðu til hægðarauka umbreytt bæti í tölulegt form með því að breyta bætafylki í venjulega fylki (listi(dæmi_bæta_fylki)). Í öllum tilvikum henta báðar aðferðirnar til greiningar.

Eftir að hafa skoðað nokkrar ibd skrár geturðu fundið eftirfarandi:

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Þar að auki, ef þú deilir skránni með þessum leitarorðum, færðu að mestu jöfn gagnablokk. Við munum nota infimum sem deili.

table = table.split("infimum".encode())

Áhugaverð athugun: fyrir töflur með lítið magn af gögnum, á milli infimum og supremum er bendi á fjölda raða í reitnum.

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni — prófunartafla með 1. röð

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni - prófunartafla með 2 línum

Hægt er að sleppa raðfylkistöflunni[0]. Eftir að hafa skoðað það gat ég samt ekki fundið hrá töflugögnin. Líklegast er þessi kubbur notaður til að geyma vísitölur og lykla.
Með því að byrja á töflu[1] og þýða hana yfir í tölulegt fylki geturðu nú þegar tekið eftir sumum mynstrum, þ.e.

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Þetta eru int gildi sem eru geymd í streng. Fyrsta bætið gefur til kynna hvort talan sé jákvæð eða neikvæð. Í mínu tilfelli eru allar tölur jákvæðar. Af þeim 3 bætum sem eftir eru geturðu ákvarðað fjöldann með því að nota eftirfarandi aðgerð. Handrit:

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

Til dæmis, 128, 0, 0, 1 = 1Eða 128, 0, 75, 108 = 19308.
Taflan var með aðallykil með sjálfvirkri aukningu og hann má einnig finna hér

Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni

Eftir að hafa borið saman gögnin úr prófunartöflunum kom í ljós að DATETIME hluturinn samanstendur af 5 bætum og byrjaði á 153 (sem gefur líklega til kynna árlegt millibil). Þar sem DATTIME sviðið er '1000-01-01' til '9999-12-31' held ég að fjöldi bæta geti verið mismunandi, en í mínu tilfelli falla gögnin á tímabilinu 2016 til 2019, svo við gerum ráð fyrir þessi 5 bæti nóg.

Til að ákvarða tímann án sekúndna voru eftirfarandi aðgerðir skrifaðar. Handrit:

day_ = lambda x: x % 64 // 2  # {x,x,X,x,x }

def hour_(x1, x2):  # {x,x,X1,X2,x}
    if x1 % 2 == 0:
        return x2 // 16
    elif x1 % 2 == 1:
        return x2 // 16 + 16
    else:
        raise ValueError

min_ = lambda x1, x2: (x1 % 16) * 4 + (x2 // 64)  # {x,x,x,X1,X2}

Það var ekki hægt að skrifa virka fall fyrir árið og mánuðinn, svo ég varð að hakka hana. Handrit:

ym_list = {'2016, 1': '153, 152, 64', '2016, 2': '153, 152, 128', 
           '2016, 3': '153, 152, 192', '2016, 4': '153, 153, 0',
           '2016, 5': '153, 153, 64', '2016, 6': '153, 153, 128', 
           '2016, 7': '153, 153, 192', '2016, 8': '153, 154, 0', 
           '2016, 9': '153, 154, 64', '2016, 10': '153, 154, 128', 
           '2016, 11': '153, 154, 192', '2016, 12': '153, 155, 0',
           '2017, 1': '153, 155, 128', '2017, 2': '153, 155, 192', 
           '2017, 3': '153, 156, 0', '2017, 4': '153, 156, 64',
           '2017, 5': '153, 156, 128', '2017, 6': '153, 156, 192',
           '2017, 7': '153, 157, 0', '2017, 8': '153, 157, 64',
           '2017, 9': '153, 157, 128', '2017, 10': '153, 157, 192', 
           '2017, 11': '153, 158, 0', '2017, 12': '153, 158, 64', 
           '2018, 1': '153, 158, 192', '2018, 2': '153, 159, 0',
           '2018, 3': '153, 159, 64', '2018, 4': '153, 159, 128', 
           '2018, 5': '153, 159, 192', '2018, 6': '153, 160, 0',
           '2018, 7': '153, 160, 64', '2018, 8': '153, 160, 128',
           '2018, 9': '153, 160, 192', '2018, 10': '153, 161, 0', 
           '2018, 11': '153, 161, 64', '2018, 12': '153, 161, 128',
           '2019, 1': '153, 162, 0', '2019, 2': '153, 162, 64', 
           '2019, 3': '153, 162, 128', '2019, 4': '153, 162, 192', 
           '2019, 5': '153, 163, 0', '2019, 6': '153, 163, 64',
           '2019, 7': '153, 163, 128', '2019, 8': '153, 163, 192',
           '2019, 9': '153, 164, 0', '2019, 10': '153, 164, 64', 
           '2019, 11': '153, 164, 128', '2019, 12': '153, 164, 192',
           '2020, 1': '153, 165, 64', '2020, 2': '153, 165, 128',
           '2020, 3': '153, 165, 192','2020, 4': '153, 166, 0', 
           '2020, 5': '153, 166, 64', '2020, 6': '153, 1, 128',
           '2020, 7': '153, 166, 192', '2020, 8': '153, 167, 0', 
           '2020, 9': '153, 167, 64','2020, 10': '153, 167, 128',
           '2020, 11': '153, 167, 192', '2020, 12': '153, 168, 0'}

def year_month(x1, x2):  # {x,X,X,x,x }

    for key, value in ym_list.items():
        key = [int(k) for k in key.replace("'", "").split(", ")]
        value = [int(v) for v in value.split(", ")]
        if x1 == value[1] and x2 // 64 == value[2] // 64:
            return key
    return 0, 0

Ég er viss um að ef þú eyðir miklum tíma er hægt að leiðrétta þennan misskilning.
Næst, fall sem skilar datetime hlut úr streng. Handrit:

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)

Tókst að greina oft endurtekin gildi frá int, int, datetime, datetime Að endurheimta gögn úr XtraDB töflum án uppbyggingarskráar með því að nota bæti fyrir bæti greiningu á ibd skránni, það lítur út fyrir að þetta sé það sem þú þarft. Þar að auki er slík röð ekki endurtekin tvisvar í hverri línu.

Með því að nota reglulega tjáningu finnum við nauðsynleg gögn:

fined = re.findall(r'128, d*, d*, d*, 128, d*, d*, d*, 153, 1[6,5,4,3]d, d*, d*, d*, 153, 1[6,5,4,3]d, d*, d*, d*', int_array)

Vinsamlegast athugaðu að þegar leitað er með þessari tjáningu er ekki hægt að ákvarða NULL gildi í nauðsynlegum reitum, en í mínu tilfelli er þetta ekki mikilvægt. Síðan förum við í gegnum það sem við fundum í lykkju. Handrit:

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)

Reyndar, það er allt, gögnin frá niðurstöðufylkingunni eru gögnin sem við þurfum. ###PS.###
Mér skilst að þessi aðferð henti ekki öllum, en meginmarkmið greinarinnar er að hvetja til aðgerða frekar en að leysa öll vandamál þín. Ég held að réttasta lausnin væri að byrja sjálfur að rannsaka frumkóðann mariadb, en vegna takmarkaðs tíma virtist núverandi aðferð vera fljótvirkust.

Í sumum tilfellum, eftir að hafa greint skrána, muntu geta ákvarðað áætlaða uppbyggingu og endurheimt hana með einni af stöðluðu aðferðunum frá tenglum hér að ofan. Þetta verður miklu réttara og veldur færri vandamálum.

Heimild: www.habr.com

Bæta við athugasemd