XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Sana oldin

Shunday qilib, serverga ransomware virusi hujum qildi, u "baxtli voqea" tufayli .ibd fayllarini (innodb jadvallarining xom ma'lumotlar fayllari) qisman daxlsiz qoldirdi, lekin ayni paytda .fpm fayllarini to'liq shifrladi ( struktura fayllari). Bunday holda, .idb ni quyidagilarga bo'lish mumkin:

  • standart asboblar va qo'llanmalar orqali qayta tiklanishi kerak. Bunday holatlar uchun ajoyib narsa bor bo'lish;
  • qisman shifrlangan jadvallar. Ko'pincha bu katta jadvallar bo'lib, ular uchun (men tushunganimdek) tajovuzkorlarda to'liq shifrlash uchun etarli RAM yo'q edi;
  • Xo'sh, qayta tiklanmaydigan to'liq shifrlangan jadvallar.

Jadvallar qaysi variantga tegishli ekanligini aniqlash uchun uni istalgan matn muharririda kerakli kodlash ostida (mening holimda bu UTF8) ochish va matn maydonlarining mavjudligi uchun faylni oddiygina ko'rish orqali aniqlash mumkin edi, masalan:

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Bundan tashqari, faylning boshida siz ko'p sonli 0 baytni kuzatishingiz mumkin va blokli shifrlash algoritmidan foydalanadigan viruslar (eng keng tarqalgan) odatda ularga ham ta'sir qiladi.
XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Mening holimda, tajovuzkorlar har bir shifrlangan faylning oxirida 4 baytlik satrni (1, 0, 0, 0) qoldirishdi, bu esa vazifani soddalashtirdi. Infektsiyalanmagan fayllarni qidirish uchun skript etarli edi:

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)

Shunday qilib, birinchi turga tegishli fayllar topildi. Ikkinchisi juda ko'p qo'l mehnatini o'z ichiga oladi, ammo topilgan narsa allaqachon etarli edi. Hammasi yaxshi bo'lardi, lekin siz bilishingiz kerak mutlaqo aniq tuzilma va (albatta) tez-tez o'zgarib turadigan stol bilan ishlashim kerak bo'lgan holat paydo bo'ldi. Maydon turi o'zgartirilganmi yoki yangi ustun qo'shilganmi, hech kim eslolmadi.

Wilds City, afsuski, bunday holatda yordam bera olmadi, shuning uchun bu maqola yozilmoqda.

Gapga keling

3 oy oldingi jadval tuzilmasi mavjud bo'lib, u hozirgisiga to'g'ri kelmaydi (ehtimol bitta maydon va ehtimol undan ko'p). Jadval tuzilishi:

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

bu holda siz quyidagilarni olishingiz kerak:

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

Qayta tiklash uchun .ibd faylining bayt-bayt tahlili qo'llaniladi, so'ngra ularni yanada o'qilishi mumkin bo'lgan shaklga aylantiradi. Bizga kerak bo'lgan narsani topish uchun biz faqat int va datatime kabi ma'lumotlar turlarini tahlil qilishimiz kerak, maqolada faqat ular tasvirlanadi, lekin ba'zida biz boshqa shunga o'xshash hodisalarda yordam beradigan boshqa ma'lumotlar turlariga ham murojaat qilamiz.

Muammo 1: DATETIME va TEXT turlari bo'lgan maydonlar NULL qiymatlarga ega edi va ular shunchaki faylda o'tkazib yuborilgan, shuning uchun mening ishimda qayta tiklanadigan tuzilmani aniqlab bo'lmadi. Yangi ustunlarda standart qiymat null edi va innodb_flush_log_at_trx_commit = 0 sozlamasi tufayli tranzaksiyaning bir qismi yo'qolishi mumkin edi, shuning uchun tuzilmani aniqlash uchun qo'shimcha vaqt sarflash kerak bo'ladi.

Muammo 2: shuni hisobga olish kerakki, DELETE orqali oʻchirilgan qatorlar hammasi ibd faylida boʻladi, lekin ALTER TABLE bilan ularning tuzilishi yangilanmaydi. Natijada, ma'lumotlar strukturasi faylning boshidan oxirigacha o'zgarishi mumkin. Agar siz OPTIMIZE TABLE-dan tez-tez foydalansangiz, unda bunday muammoga duch kelishingiz dargumon.

E'tibor bering, DBMS versiyasi ma'lumotlarni saqlash usuliga ta'sir qiladi va bu misol boshqa asosiy versiyalar uchun ishlamasligi mumkin. Mening holimda mariadb 10.1.24 ning windows versiyasi ishlatilgan. Bundan tashqari, mariadb-da siz InnoDB jadvallari bilan ishlasangiz ham, aslida ular shunday XtraDB, bu usulning InnoDB MySQL bilan qo'llanilishini istisno qiladi.

Fayl tahlili

Pythonda ma'lumotlar turi bayt() oddiy raqamlar to'plami o'rniga Unicode ma'lumotlarini ko'rsatadi. Faylni ushbu shaklda ko'rishingiz mumkin bo'lsa-da, qulaylik uchun bayt massivini oddiy massivga (ro'yxat(misol_bayt_massivi)) aylantirish orqali baytlarni raqamli shaklga aylantirishingiz mumkin. Har holda, ikkala usul ham tahlil qilish uchun javob beradi.

Bir nechta ibd fayllarini ko'rib chiqqandan so'ng, siz quyidagilarni topishingiz mumkin:

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Bundan tashqari, agar siz faylni ushbu kalit so'zlar bilan ajratsangiz, siz asosan hatto ma'lumotlar bloklarini olasiz. Biz bo'luvchi sifatida infimumdan foydalanamiz.

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

Qiziqarli kuzatish: kichik hajmdagi ma'lumotlarga ega jadvallar uchun infimum va supremum o'rtasida blokdagi qatorlar soniga ko'rsatgich mavjud.

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash — 1-qator bilan sinov stoli

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash - 2 qatorli test jadvali

Qator massiv jadvalini[0] o'tkazib yuborish mumkin. Uni ko'rib chiqqach, men hali ham xom jadval ma'lumotlarini topa olmadim. Ehtimol, bu blok indekslar va kalitlarni saqlash uchun ishlatiladi.
Jadval [1] dan boshlab va uni raqamli massivga tarjima qilganda, siz allaqachon ba'zi naqshlarni ko'rishingiz mumkin, xususan:

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Bu satrda saqlanadigan int qiymatlari. Birinchi bayt raqamning ijobiy yoki salbiy ekanligini ko'rsatadi. Mening holatimda barcha raqamlar ijobiydir. Qolgan 3 baytdan siz quyidagi funksiya yordamida raqamni aniqlashingiz mumkin. 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

Misol uchun, 128, 0, 0, 1 = 1yoki 128, 0, 75, 108 = 19308.
Jadvalda avtomatik o'sish xususiyatiga ega asosiy kalit bor edi va uni shu yerda ham topish mumkin

XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash

Sinov jadvallari ma'lumotlarini taqqoslab, DATETIME ob'ekti 5 baytdan iborat bo'lishi va 153 dan boshlanganligi aniqlandi (ehtimol yillik intervallarni ko'rsatadi). DATTIME diapazoni "1000-01-01" dan "9999-12-31" gacha bo'lganligi sababli, baytlar soni o'zgarishi mumkin deb o'ylayman, lekin mening holatimda ma'lumotlar 2016 yildan 2019 yilgacha bo'lgan davrga to'g'ri keladi, shuning uchun biz taxmin qilamiz bu 5 bayt etarli.

Vaqtni soniyalarsiz aniqlash uchun quyidagi funksiyalar yozildi. 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}

Yil va oy uchun funktsional funktsiyani yozish mumkin emas edi, shuning uchun men uni buzishga majbur bo'ldim. 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

Ishonchim komilki, agar siz n ko'p vaqt sarflasangiz, bu tushunmovchilikni tuzatish mumkin.
Keyinchalik, satrdan datetime obyektini qaytaradigan funksiya. 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)

int, int, datetime, datetime dan tez-tez takrorlanadigan qiymatlarni aniqlashga muvaffaq bo'ldi XtraDB jadvallaridan ibd faylini bayt-bayt tahlili yordamida struktura faylisiz ma'lumotlarni qayta tiklash, bu sizga kerak bo'lgan narsaga o'xshaydi. Bundan tashqari, bunday ketma-ketlik har bir satrda ikki marta takrorlanmaydi.

Oddiy iboradan foydalanib, biz kerakli ma'lumotlarni topamiz:

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)

Shuni esda tutingki, ushbu iboradan foydalanib qidirishda kerakli maydonlarda NULL qiymatlarni aniqlab bo'lmaydi, ammo mening holatimda bu muhim emas. Keyin biz halqada topilgan narsalarni ko'rib chiqamiz. 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)

Aslida, bu hammasi, natija massividagi ma'lumotlar bizga kerak bo'lgan ma'lumotlardir. ###PS.###
Men bu usul hamma uchun mos emasligini tushunaman, lekin maqolaning asosiy maqsadi sizning barcha muammolaringizni hal qilishdan ko'ra tezkor harakat qilishdir. Menimcha, eng to'g'ri yechim manba kodini o'zingiz o'rganishni boshlashdir mariadb, lekin cheklangan vaqt tufayli joriy usul eng tezkor bo'lib tuyuldi.

Ba'zi hollarda, faylni tahlil qilgandan so'ng, siz taxminiy tuzilmani aniqlashingiz va yuqoridagi havolalardan standart usullardan biri yordamida uni tiklashingiz mumkin. Bu ancha to'g'ri bo'ladi va kamroq muammolarni keltirib chiqaradi.

Manba: www.habr.com

a Izoh qo'shish