Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Virgeschicht

Et ass geschitt, datt de Server vun engem Ransomware Virus attackéiert gouf, deen duerch e "glécklechen Accident" deelweis d'.ibd Dateien (Rohdatendateien vun innodb Dëscher) onberéiert gelooss huet, awer gläichzäiteg d'.fpm Dateien komplett verschlësselt huet ( Strukturdateien). An dësem Fall kéint .idb opgedeelt ginn:

  • ënnerleien zu Restauratioun duerch Standard Tools a Guiden. Fir esou Fäll gëtt et eng excellent ginn;
  • deelweis verschlësselte Dëscher. Meeschtens sinn dës grouss Dëscher, fir déi (wéi ech verstinn) d'Ugräifer net genuch RAM fir voll Verschlësselung hunn;
  • Gutt, voll verschlësselte Dëscher déi net restauréiert kënne ginn.

Et war méiglech ze bestëmmen wéi eng Optioun d'Dëscher gehéieren, andeems se se einfach an all Texteditor ënner der gewënschter Kodéierung opmaachen (a mengem Fall ass et UTF8) an einfach d'Datei fir d'Präsenz vun Textfelder ze gesinn, zum Beispill:

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Och, am Ufank vun der Datei kënnt Dir eng grouss Zuel vun 0 Bytes beobachten, a Viren déi de Blockverschlësselungsalgorithmus benotzen (déi heefegst) beaflossen se normalerweis och.
Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

A mengem Fall hunn d'Ugräifer e 4-Byte String (1, 0, 0, 0) um Enn vun all verschlësselte Fichier verlooss, wat d'Aufgab vereinfacht huet. Fir no oninfizéierte Dateien ze sichen, war de Skript genuch:

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)

Sou huet et erausgestallt fir Dateien ze fannen déi zum éischten Typ gehéieren. Déi zweet ëmfaasst vill manuell Aarbecht, mee wat fonnt gouf war schonn duer. Alles wier gutt, awer Dir musst wëssen absolut präzis Struktur an (natierlech) e Fall entstanen, datt ech mat engem oft änneren Dësch ze schaffen haten. Keen huet sech drun erënnert ob d'Feldtyp geännert gouf oder eng nei Kolonn bäigefüügt gouf.

Wilds City konnt leider net mat esou engem Fall hëllefen, dofir ass dësen Artikel geschriwwen.

Méi no un de Punkt

Et gëtt eng Struktur vun enger Tabell vu virun 3 Méint, déi net mat der aktueller (eventuell e Feld, an eventuell méi) entsprécht. Dësch Struktur:

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

an dësem Fall musst Dir extrahieren:

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

Fir d'Erhuelung gëtt eng Byte-by-Byte-Analyse vun der .ibd-Datei benotzt, gefollegt andeems se se an eng méi liesbar Form konvertéieren. Well fir ze fannen wat mir brauchen, brauche mir nëmmen Datentypen wéi Int an Datatime ze analyséieren, den Artikel beschreift nëmmen se, awer heiansdo wäerte mir och op aner Datentypen bezéien, déi an aneren ähnlechen Tëschefäll hëllefe kënnen.

Problem 1: Felder mat Typen DATETIME an TEXT haten NULL-Wäerter, a si ginn einfach an der Datei iwwersprangen, dofir war et a mengem Fall net méiglech, d'Struktur ze bestëmmen fir ze restauréieren. An den neie Sailen war de Standardwäert null, an en Deel vun der Transaktioun konnt verluer ginn duerch d'Astellung innodb_flush_log_at_trx_commit = 0, sou datt zousätzlech Zäit muss verbruecht ginn fir d'Struktur ze bestëmmen.

Problem 2: Et sollt berécksiichtegt ginn datt d'Zeilen, déi iwwer DELETE geläscht ginn, all an der ibd Datei sinn, awer mat ALTER TABLE gëtt hir Struktur net aktualiséiert. Als Resultat kann d'Datestruktur vum Ufank vun der Datei bis zum Enn variéieren. Wann Dir dacks OPTIMIZE TABLE benotzt, da sidd Dir onwahrscheinlech sou e Problem ze stoussen.

Opgepasst, d'DBMS Versioun beaflosst d'Art a Weis wéi d'Date gespäichert ginn, an dëst Beispill funktionnéiert vläicht net fir aner grouss Versiounen. A mengem Fall gouf d'Windows Versioun vum mariadb 10.1.24 benotzt. Och, obwuel a mariadb Dir mat InnoDB Dëscher schafft, tatsächlech sinn se XtraDB, déi d'Uwendbarkeet vun der Method mat InnoDB mysql ausgeschloss.

Datei Analyse

Am Python, Datentyp bytes() weist Unicode Daten amplaz vun engem normale Set vun Zuelen. Och wann Dir d'Datei an dësem Formulaire kuckt, kënnt Dir fir d'Bequemlechkeet d'Bytes an numeresch Form konvertéieren andeems Dir de Byte-Array an eng regulär Array konvertéiert (Lëscht (example_byte_array)). Op alle Fall si béid Methode gëeegent fir Analyse.

Nodeems Dir e puer ibd Dateien gekuckt hutt, fannt Dir déi folgend:

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Ausserdeem, wann Dir d'Datei mat dëse Schlësselwieder deelt, kritt Dir meeschtens souguer Datenblocken. Mir wäerten Infimum als Divisor benotzen.

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

Eng interessant Observatioun: fir Dëscher mat enger klenger Quantitéit vun Donnéeën, tëscht Infimum an Supremum gëtt et e Pointer op d'Zuel vun de Reihen am Block.

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei - Test Dësch mat 1. Zeil

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei - Test Dësch mat 2 Zeile

D'Zeilenarray-Tabelle[0] kann iwwersprangen ginn. Nodeems ech et duerchgekuckt hunn, konnt ech nach ëmmer déi rau Tabelldaten net fannen. Wahrscheinlech gëtt dëse Block benotzt fir Indexen a Schlësselen ze späicheren.
Vun der Tabell[1] unzefänken an et an eng numeresch Array ze iwwersetzen, kënnt Dir schonn e puer Mustere bemierken, nämlech:

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Dëst sinn Int Wäerter déi an enger String gespäichert sinn. Den éischte Byte weist ob d'Zuel positiv oder negativ ass. A mengem Fall sinn all Zuelen positiv. Vun de reschtlechen 3 Bytes kënnt Dir d'Zuel mat der folgender Funktioun bestëmmen. 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

Zum Beispill, 128, 0, 0, 1 = 1oder 128, 0, 75, 108 = 19308.
Den Dësch hat e primäre Schlëssel mat Auto-Inkrement, an et kann och hei fonnt ginn

Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei

Nodeems Dir d'Daten vun den Testtabellen verglach hutt, gouf opgedeckt datt den DATETIME Objet aus 5 Bytes besteet an ugefaang mat 153 (wahrscheinlech beweist alljährlechen Intervalle). Zënter dem DATTIME Beräich ass '1000-01-01' bis '9999-12-31', mengen ech, datt d'Zuel vun de Bytes variéiere kann, awer a mengem Fall falen d'Donnéeën an der Period vun 2016 bis 2019, also wäerte mir unhuelen datt 5 Bytes genuch.

Fir d'Zäit ouni Sekonnen ze bestëmmen, goufen déi folgend Funktiounen geschriwwen. Skript:

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}

Et war net méiglech eng funktionell Funktioun fir d'Joer a Mount ze schreiwen, also hunn ech et missen hacken. Skript:

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

Ech si sécher datt wann Dir n Zäit verbréngt, kann dëst Mëssverständnis korrigéiert ginn.
Als nächst eng Funktioun déi en Datetime-Objet vun enger String zréckkënnt. 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)

Mir konnten dacks widderholl Wäerter vun int, int, datetime, datetime entdecken Daten aus XtraDB Dëscher recuperéieren ouni Strukturdatei mat Byte-by-Byte Analyse vun der ibd Datei, et gesäit aus wéi wann dat ass wat Dir braucht. Ausserdeem gëtt sou eng Sequenz net zweemol pro Zeil widderholl.

Mat engem regulären Ausdrock fanne mir déi néideg Donnéeën:

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)

Notéiert w.e.g. datt wann Dir sicht mat dësem Ausdrock, et net méiglech ass NULL Wäerter an den erfuerderleche Felder ze bestëmmen, awer a mengem Fall ass dëst net kritesch. Da gi mir duerch dat wat mir an enger Loop fonnt hunn. 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)

Eigentlech, dat ass alles, d'Donnéeën aus der Resultatarray sinn d'Donnéeën déi mir brauchen. ###PS.###
Ech verstinn datt dës Method net fir jiddereen gëeegent ass, awer d'Haaptziel vum Artikel ass d'Aktioun ze froen anstatt all Är Probleemer ze léisen. Ech mengen déi richteg Léisung wier de Quellcode selwer ze studéieren mariadb, mee wéinst limitéierter Zäit schéngt déi aktuell Method déi séierst ze sinn.

An e puer Fäll, nodeems Dir d'Datei analyséiert hutt, kënnt Dir déi geschätzte Struktur bestëmmen an se mat enger vun de Standardmethoden aus de Linken hei uewen restauréieren. Dëst wäert vill méi korrekt sinn a manner Probleemer verursaachen.

Source: will.com

Setzt e Commentaire