XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

historiaurrea

Gertatu zen zerbitzaria ransomware birus batek eraso zuela, eta horrek, "zoriozko istripu baten ondorioz", partzialki .ibd fitxategiak (innodb tauletako datu gordinak) ukitu gabe utzi zituen, baina aldi berean .fpm fitxategiak guztiz enkriptatu zituen ( egitura fitxategiak). Kasu honetan, .idb honela banatu daiteke:

  • tresna eta gida estandarren bidez zaharberritzeko gai. Horrelako kasuetarako, bikaina dago bihurtu;
  • partzialki zifratutako taulak. Gehienetan mahai handiak dira, eta horretarako (ulertzen dudanez) erasotzaileek ez zuten nahikoa RAM zifratze osoa egiteko;
  • Beno, guztiz enkriptatuta dauden taulak berreskuratu ezin direnak.

Taulak zein aukerari dagozkion zehaztea posible zen, nahi den kodifikazioaren azpian edozein testu-editore batean irekiz (nire kasuan UTF8 da) eta fitxategia testu-eremuen presentzia ikusita, adibidez:

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

Era berean, fitxategiaren hasieran 0 byte kopuru handi bat beha dezakezu, eta blokeen enkriptazio algoritmoa (ohikoena) erabiltzen duten birusek ere eragiten diete normalean.
XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

Nire kasuan, erasotzaileek 4 byteko kate bat utzi zuten (1, 0, 0, 0) enkriptatutako fitxategi bakoitzaren amaieran, eta horrek zeregina erraztu zuen. Kutsatu gabeko fitxategiak bilatzeko, scripta nahikoa zen:

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)

Horrela, lehen motako fitxategiak aurkitu ditu. Bigarrenak eskuzko lan handia dakar, baina aurkitutakoa nahikoa zen jada. Dena ondo legoke, baina jakin behar duzu egitura guztiz zehatza eta (noski) maiz aldatzen den mahai batekin lan egin behar izan nuen kasu bat sortu zen. Inork ez zuen gogoratzen eremu mota aldatu zen edo zutabe berri bat gehitu zen.

Wilds Cityk, zoritxarrez, ezin izan zuen horrelako kasu batean lagundu, horregatik idazten ari da artikulu hau.

Lortu puntura

Orain dela 3 hilabeteko taula baten egitura dago, egungoarekin bat ez datorrena (baliteke eremu bat, eta agian gehiago). Taularen egitura:

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

kasu honetan, atera behar duzu:

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

Berreskuratzeko, .ibd fitxategiaren bytez byte analisia erabiltzen da, eta, ondoren, forma irakurgarriago batean bihurtuko da. Behar duguna aurkitzeko, int eta datatime bezalako datu motak bakarrik aztertu behar ditugunez, artikuluak horiek bakarrik deskribatuko ditu, baina batzuetan beste datu mota batzuetara ere aipatuko dugu, antzeko beste gorabehera batzuetan lagun dezaketenak.

1. arazoa: DATETIME eta TEXT motako eremuek NULL balioak zituzten, eta fitxategian besterik gabe saltatzen dira, horregatik, ezin izan zen nire kasuan leheneratu beharreko egitura zehaztu. Zutabe berrietan, balio lehenetsia nulua zen, eta transakzioaren zati bat gal zitekeen innodb_flush_log_at_trx_commit = 0 ezarpenaren ondorioz, beraz, denbora gehiago eman beharko litzateke egitura zehazteko.

2. arazoa: kontuan izan behar da DELETE bidez ezabatu diren errenkadak ibd fitxategian egongo direla, baina ALTER TABLE-rekin ez da haien egitura eguneratuko. Ondorioz, datuen egitura aldatu egin daiteke fitxategiaren hasieratik amaierara arte. OPTIMIZE TABLE maiz erabiltzen baduzu, orduan nekez aurkituko duzu arazo hori.

Arreta ezazu, DBMS bertsioak datuak biltegiratzeko moduan eragiten du, eta baliteke adibide honek ez funtzionatzea beste bertsio nagusietarako. Nire kasuan, mariadb 10.1.24 windows bertsioa erabili zen. Gainera, mariadb-n InnoDB taulekin lan egiten duzun arren, hain zuzen ere bai XtraDB, InnoDB mysql-ekin metodoaren aplikagarritasuna baztertzen duena.

Fitxategien azterketa

Python-en, datu mota byte () Unicode datuak bistaratzen ditu ohiko zenbaki-multzo baten ordez. Fitxategia formulario honetan ikus dezakezun arren, erosotasunerako byteak zenbakizko forman bihur ditzakezu byte-matrizea array arrunt batean bihurtuz (zerrenda(adibidea_byte_array)). Nolanahi ere, bi metodoak analisirako egokiak dira.

Hainbat ibd fitxategi aztertu ondoren, honako hauek aurki ditzakezu:

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

Gainera, fitxategia gako-hitz horien arabera zatitzen baduzu, gehienetan datu blokeak ere lortuko dituzu. Infimum zatitzaile gisa erabiliko dugu.

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

Behaketa interesgarri bat: datu kopuru txikia duten tauletan, infimum eta supremum artean blokeko errenkada kopuruaren erakuslea dago.

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz β€” proba-taula 1. ilararekin

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz - 2 errenkada dituen proba-taula

Errenkadako array-taula[0] saltatu daiteke. Begiratu ondoren, oraindik ezin izan ditut taulako datu gordinak aurkitu. Seguruenik, bloke hau indizeak eta gakoak gordetzeko erabiltzen da.
Taula[1] hasita eta zenbakizko matrize batera itzuliz, dagoeneko eredu batzuk nabari ditzakezu, hau da:

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

Hauek kate batean gordetako int balioak dira. Lehenengo byteak zenbakia positiboa ala negatiboa den adierazten du. Nire kasuan, zenbaki guztiak positiboak dira. Gainerako 3 byteetatik, zenbakia zehaztu dezakezu honako funtzio hau erabiliz. Gidoia:

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

Esate baterako, 128, 0, 0, 1 = 1Edo 128, 0, 75, 108 = 19308.
Taulak gehikuntza automatikoko gako nagusi bat zuen, eta hemen ere aurki daiteke

XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz

Proba-tauletako datuak alderatu ondoren, DATETIME objektua 5 bytez osatuta dagoela eta 153-rekin hasten zela agerian geratu zen (litekeena da urteko tarteak adieraziz). DATTIME tartea '1000-01-01' eta '9999-12-31' bitartekoa denez, byte kopurua alda daitekeela uste dut, baina nire kasuan, datuak 2016tik 2019ra bitarteko epean jaisten dira, beraz, suposatuko dugu. 5 byte nahikoa.

Segundorik gabeko denbora zehazteko, honako funtzio hauek idatzi dira. Gidoia:

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}

Ezin izan zen urterako eta hilabeterako funtzio funtzional bat idatzi, beraz, hackeatu behar izan nuen. Gidoia:

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

Ziur nago denbora n gastatzen baduzu, gaizki-ulertu hau zuzendu daitekeela.
Ondoren, kate batetik datetime objektu bat itzultzen duen funtzio bat. Gidoia:

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)

Maiz errepikatzen diren balioak detektatzeko kudeatzen da int, int, datetime, datetime XtraDB tauletako datuak berreskuratzea egitura fitxategirik gabe ibd fitxategiaren bytez byte azterketa erabiliz, badirudi hau dela behar duzuna. Gainera, sekuentzia hori ez da lerro bakoitzeko bi aldiz errepikatzen.

Adierazpen erregular bat erabiliz, beharrezko datuak aurkituko ditugu:

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)

Kontuan izan esamolde hau erabiliz bilatzean, ezin izango dela zehaztu NULL balioak beharrezko eremuetan, baina nire kasuan hori ez da kritikoa. Ondoren, begizta batean aurkitu duguna igaroko dugu. Gidoia:

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)

Egia esan, hori da guztia, emaitza-matrizeko datuak behar ditugun datuak dira. ###PS.###
Ulertzen dut metodo hau ez dela guztiontzat egokia, baina artikuluaren helburu nagusia zure arazo guztiak konpontzea baino ekintza bultzatzea da. Irtenbiderik zuzenena zuk zeuk iturburu kodea aztertzen hastea izango litzatekeela uste dut mariadb, baina denbora mugatua zela eta, egungo metodoa azkarrena zela zirudien.

Zenbait kasutan, fitxategia aztertu ondoren, gutxi gorabeherako egitura zehaztu eta goiko esteketako metodo estandarren bat erabiliz leheneratu ahal izango duzu. Hau askoz zuzenagoa izango da eta arazo gutxiago sortuko ditu.

Iturria: www.habr.com

Gehitu iruzkin berria