ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

тарихын

Серверге төлемдік бағдарлама вирусы шабуыл жасады, ол «бақытты апат» нәтижесінде .ibd файлдарын (innodb кестелерінің өңделмеген деректер файлдары) жартылай қалдырды, бірақ сонымен бірге .fpm файлдарын толығымен шифрлады ( құрылымдық файлдар). Бұл жағдайда .idb келесіге бөлінеді:

  • стандартты құралдар мен бағыттаушылар арқылы қалпына келтіруге жатады. Мұндай жағдайлар үшін тамаша бар болу;
  • ішінара шифрланған кестелер. Көбінесе бұл үлкен кестелер, олар үшін (менің түсінуімше) шабуылдаушыларда толық шифрлау үшін жедел жады жеткіліксіз болды;
  • Қалпына келтіру мүмкін емес толық шифрланған кестелер.

Кестелердің қай опцияға жататынын анықтауға оны кез келген мәтіндік редакторда қалаған кодтаумен (менің жағдайда бұл UTF8) жай ашу және мәтіндік өрістердің болуы үшін файлды жай қарау арқылы анықтауға болады, мысалы:

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

Сондай-ақ, файлдың басында 0 байттың үлкен санын байқауға болады және блоктық шифрлау алгоритмін қолданатын вирустар (ең көп таралған) әдетте оларға да әсер етеді.
ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

Менің жағдайда, шабуылдаушылар әрбір шифрланған файлдың соңында 4 байттық жолды (1, 0, 0, 0) қалдырды, бұл тапсырманы жеңілдетеді. Зақымдалмаған файлдарды іздеу үшін сценарий жеткілікті болды:

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)

Осылайша, бірінші типке жататын файлдар табылды. Екіншісі көп қол жұмысын қамтиды, бірақ табылған нәрсе жеткілікті болды. Бәрі жақсы болар еді, бірақ білу керек абсолютті дәл құрылым және (әрине) мен жиі өзгеретін кестемен жұмыс істеуге тура келетін жағдай туындады. Өріс түрі өзгертілгенін немесе жаңа баған қосылғанын ешкім есіне алмады.

Wilds City, өкінішке орай, мұндай жағдайда көмектесе алмады, сондықтан бұл мақала жазылды.

Нүктеге жақын

3 ай бұрынғы кестенің қазіргімен сәйкес келмейтін құрылымы бар (мүмкін бір өріс, мүмкін одан да көп). Кесте құрылымы:

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

бұл жағдайда сіз мыналарды алуыңыз керек:

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

Қалпына келтіру үшін .ibd файлының байт-байт талдауы пайдаланылады, содан кейін оларды оқуға болатын пішінге түрлендіру. Бізге қажет нәрсені табу үшін бізге тек int және datatime сияқты деректер түрлерін талдау қажет болғандықтан, мақалада тек олар сипатталады, бірақ кейде біз басқа ұқсас оқиғаларда көмектесетін басқа деректер түрлеріне де сілтеме жасаймыз.

1 мәселесі: DATETIME және TEXT түрлері бар өрістерде NULL мәндері болды және олар жай ғана файлда өткізіп жіберілді, осыған байланысты менің жағдайда қалпына келтірілетін құрылымды анықтау мүмкін болмады. Жаңа бағандарда әдепкі мән бос болды және innodb_flush_log_at_trx_commit = 0 параметріне байланысты транзакцияның бір бөлігі жоғалуы мүмкін, сондықтан құрылымды анықтау үшін қосымша уақыт жұмсалуы керек еді.

2 мәселесі: DELETE арқылы жойылған жолдардың барлығы ibd файлында болатынын ескеру керек, бірақ ALTER TABLE көмегімен олардың құрылымы жаңартылмайды. Нәтижесінде деректер құрылымы файлдың басынан аяғына дейін өзгеруі мүмкін. OPTIMIZE TABLE қолданбасын жиі пайдаланатын болсаңыз, мұндай мәселеге кезігуіңіз екіталай.

назар аударыңыз, ДҚБЖ нұсқасы деректерді сақтау жолына әсер етеді және бұл мысал басқа негізгі нұсқалар үшін жұмыс істемеуі мүмкін. Менің жағдайда, mariadb 10.1.24 нұсқасының Windows нұсқасы пайдаланылды. Сондай-ақ, mariadb-де InnoDB кестелерімен жұмыс істегеніңізге қарамастан, олар шын мәнінде XtraDB, бұл әдістің InnoDB mysql көмегімен қолдану мүмкіндігін жоққа шығарады.

Файлды талдау

Python тілінде деректер түрі байт() кәдімгі сандар жиынының орнына Юникод деректерін көрсетеді. Файлды осы пішінде көруге болатынымен, ыңғайлы болу үшін байт массивін кәдімгі массивке түрлендіру арқылы байттарды сандық пішінге түрлендіруге болады (тізім(мысал_байт_жиымы)). Кез келген жағдайда талдау үшін екі әдіс те қолайлы.

Бірнеше ibd файлдарын қарап шыққаннан кейін келесіні таба аласыз:

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

Сонымен қатар, егер сіз файлды осы кілт сөздерге бөлсеңіз, сіз негізінен деректер блоктарын аласыз. Бөлгіш ретінде infimum қолданамыз.

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

Қызықты байқау: деректер көлемі аз кестелер үшін infimum және supremum арасында блоктағы жолдар санының көрсеткіші бар.

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру — 1-ші қатардағы сынақ үстелі

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру - 2 қатардан тұратын сынақ кестесі

[0] жол жиымы кестесін өткізіп жіберуге болады. Оны қарап шыққаннан кейін мен әлі де бастапқы кесте деректерін таба алмадым. Сірә, бұл блок индекстер мен кілттерді сақтау үшін пайдаланылады.
[1] кестеден бастап және оны сандық массивке аударғанда, сіз кейбір үлгілерді байқай аласыз, атап айтқанда:

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

Бұл жолда сақталған int мәндері. Бірінші байт санның оң немесе теріс екенін көрсетеді. Менің жағдайда барлық сандар оң. Қалған 3 байттан санды келесі функция арқылы анықтауға болады. Сценарий:

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

Мысалы, 128, 0, 0, 1 = 1, немесе 128, 0, 75, 108 = 19308.
Кестеде автоматты өсімі бар бастапқы кілт болды және оны осы жерден табуға болады

ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру

Сынақ кестелеріндегі деректерді салыстыра отырып, DATETIME нысаны 5 байттан тұратыны және 153-тен басталатыны анықталды (ең алдымен, жылдық интервалдарды көрсетеді). DATTIME диапазоны '1000-01-01' және '9999-12-31' болғандықтан, менің ойымша, байттардың саны әртүрлі болуы мүмкін, бірақ менің жағдайда деректер 2016-2019 жылдар аралығында болады, сондықтан біз болжаймыз бұл 5 байт жеткілікті.

Секундсыз уақытты анықтау үшін келесі функциялар жазылды. Сценарий:

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}

Жыл мен айдың функционалдық функциясын жазу мүмкін болмады, сондықтан мен оны бұзуға тура келді. Сценарий:

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

Егер сіз n көп уақыт жұмсасаңыз, бұл түсініспеушілікті түзетуге болатынына сенімдімін.
Әрі қарай, жолдан datetime нысанын қайтаратын функция. Сценарий:

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)

int, int, datetime, datetime сияқты жиі қайталанатын мәндерді анықтауға мүмкіндік берді ibd файлының байт-байт талдауы арқылы құрылымдық файлсыз XtraDB кестелерінен деректерді қалпына келтіру, бұл сізге керек сияқты. Оның үстіне мұндай реттілік бір жолға екі рет қайталанбайды.

Тұрақты өрнектің көмегімен біз қажетті деректерді табамыз:

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)

Осы өрнекті пайдаланып іздеу кезінде қажетті өрістерде NULL мәндерін анықтау мүмкін болмайтынын ескеріңіз, бірақ менің жағдайда бұл маңызды емес. Содан кейін біз циклде тапқанымызды өткіземіз. Сценарий:

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)

Шындығында, нәтиже массивіндегі деректер бізге қажет деректер болып табылады. ###PS.###
Мен бұл әдіс бәріне жарамайтынын түсінемін, бірақ мақаланың негізгі мақсаты - сіздің барлық мәселелеріңізді шешуден гөрі жедел әрекет ету. Менің ойымша, ең дұрыс шешім - бастапқы кодты өзіңіз зерттеуді бастау mariadb, бірақ уақыт шектеулі болғандықтан, қазіргі әдіс ең жылдам болып көрінді.

Кейбір жағдайларда, файлды талдағаннан кейін, сіз шамамен құрылымды анықтай аласыз және оны жоғарыдағы сілтемелердегі стандартты әдістердің бірін пайдаланып қалпына келтіре аласыз. Бұл әлдеқайда дұрыс болады және аз проблемалар туғызады.

Ақпарат көзі: www.habr.com

пікір қалдыру