Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

prasejarah

Kebetulan pelayan telah diserang oleh virus ransomware, yang, oleh "kemalangan bertuah", sebahagiannya meninggalkan fail .ibd (fail data mentah jadual innodb) tidak disentuh, tetapi pada masa yang sama menyulitkan sepenuhnya fail .fpm ( fail struktur). Dalam kes ini, .idb boleh dibahagikan kepada:

  • tertakluk kepada pemulihan melalui alat dan panduan standard. Untuk kes sedemikian, terdapat yang sangat baik menjadi;
  • jadual yang disulitkan sebahagiannya. Kebanyakannya ini adalah jadual besar, yang mana (seperti yang saya faham) penyerang tidak mempunyai RAM yang mencukupi untuk penyulitan penuh;
  • Nah, jadual yang disulitkan sepenuhnya yang tidak boleh dipulihkan.

Adalah mungkin untuk menentukan pilihan mana yang dimiliki oleh jadual dengan hanya membukanya dalam mana-mana editor teks di bawah pengekodan yang dikehendaki (dalam kes saya ia adalah UTF8) dan hanya melihat fail untuk kehadiran medan teks, sebagai contoh:

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Selain itu, pada permulaan fail anda boleh memerhatikan sejumlah besar 0 bait, dan virus yang menggunakan algoritma penyulitan blok (paling biasa) biasanya mempengaruhinya juga.
Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Dalam kes saya, penyerang meninggalkan rentetan 4-bait (1, 0, 0, 0) pada akhir setiap fail yang disulitkan, yang memudahkan tugas. Untuk mencari fail yang tidak dijangkiti, skrip sudah mencukupi:

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)

Oleh itu, ternyata untuk mencari fail kepunyaan jenis pertama. Yang kedua melibatkan banyak kerja manual, tetapi apa yang ditemui sudah mencukupi. Semuanya akan baik-baik saja, tetapi anda perlu tahu struktur yang sangat tepat dan (sudah tentu) timbul satu kes bahawa saya terpaksa bekerja dengan jadual yang kerap berubah. Tiada siapa yang ingat sama ada jenis medan telah ditukar atau lajur baharu telah ditambahkan.

Wilds City, malangnya, tidak dapat membantu dengan kes sedemikian, itulah sebabnya artikel ini ditulis.

Sampai ke intinya

Terdapat struktur jadual dari 3 bulan lalu yang tidak bertepatan dengan yang semasa (mungkin satu medan, dan mungkin lebih). Struktur jadual:

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

dalam kes ini, anda perlu mengekstrak:

  • id_point int(11);
  • id_user int(11);
  • date_start MASA TARIKH;
  • date_finish MASA TARIKH.

Untuk pemulihan, analisis bait demi bait bagi fail .ibd digunakan, diikuti dengan menukarnya kepada bentuk yang lebih mudah dibaca. Memandangkan untuk mencari apa yang kami perlukan, kami hanya perlu menganalisis jenis data seperti int dan masa data, artikel itu akan menerangkannya sahaja, tetapi kadangkala kami juga akan merujuk kepada jenis data lain, yang boleh membantu dalam insiden serupa yang lain.

Masalah 1: medan dengan jenis DATETIME dan TEXT mempunyai nilai NULL, dan ia hanya dilangkau dalam fail, kerana ini, tidak mungkin untuk menentukan struktur untuk dipulihkan dalam kes saya. Dalam lajur baharu, nilai lalai adalah batal dan sebahagian daripada urus niaga boleh hilang disebabkan oleh tetapan innodb_flush_log_at_trx_commit = 0, jadi masa tambahan perlu dibelanjakan untuk menentukan struktur.

Masalah 2: perlu diambil kira bahawa baris yang dipadam melalui DELETE semuanya akan berada dalam fail ibd, tetapi dengan ALTER TABLE strukturnya tidak akan dikemas kini. Akibatnya, struktur data boleh berbeza dari permulaan fail hingga penghujungnya. Jika anda sering menggunakan OPTIMIZE TABLE, maka anda tidak mungkin menghadapi masalah sebegitu.

Perhatikan, versi DBMS mempengaruhi cara data disimpan dan contoh ini mungkin tidak berfungsi untuk versi utama yang lain. Dalam kes saya, versi windows mariadb 10.1.24 telah digunakan. Juga, walaupun dalam mariadb anda bekerja dengan jadual InnoDB, sebenarnya ia adalah XtraDB, yang tidak termasuk kebolehgunaan kaedah dengan InnoDB mysql.

Analisis fail

Dalam python, jenis data bait() memaparkan data Unicode sebagai ganti set nombor biasa. Walaupun anda boleh melihat fail dalam borang ini, untuk kemudahan anda boleh menukar bait ke dalam bentuk angka dengan menukar tatasusunan bait kepada tatasusunan biasa (senarai(contoh_bait_tatasusunan)). Walau apa pun, kedua-dua kaedah sesuai untuk analisis.

Selepas melihat melalui beberapa fail ibd, anda boleh menemui perkara berikut:

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Lebih-lebih lagi, jika anda membahagikan fail dengan kata kunci ini, anda akan mendapat sebahagian besar blok data yang sekata. Kami akan menggunakan infimum sebagai pembahagi.

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

Pemerhatian yang menarik: untuk jadual dengan jumlah data yang kecil, antara infimum dan supremum terdapat penunjuk kepada bilangan baris dalam blok.

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd β€” jadual ujian dengan baris pertama

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd - jadual ujian dengan 2 baris

Jadual tatasusunan baris[0] boleh dilangkau. Selepas melihatnya, saya masih tidak dapat mencari data jadual mentah. Kemungkinan besar, blok ini digunakan untuk menyimpan indeks dan kunci.
Bermula dengan jadual[1] dan menterjemahkannya ke dalam tatasusunan angka, anda sudah dapat melihat beberapa corak, iaitu:

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Ini adalah nilai int yang disimpan dalam rentetan. Bait pertama menunjukkan sama ada nombor itu positif atau negatif. Dalam kes saya, semua nombor adalah positif. Daripada baki 3 bait, anda boleh menentukan nombor menggunakan fungsi berikut. Skrip:

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

Sebagai contoh, 128, 0, 0, 1 = 1Atau 128, 0, 75, 108 = 19308.
Jadual mempunyai kunci utama dengan autokenaikan, dan ia juga boleh didapati di sini

Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd

Setelah membandingkan data daripada jadual ujian, ia telah mendedahkan bahawa objek DATETIME terdiri daripada 5 bait dan bermula dengan 153 (kemungkinan besar menunjukkan selang tahunan). Memandangkan julat DATTIME ialah '1000-01-01' hingga '9999-12-31', saya rasa bilangan bait mungkin berbeza-beza, tetapi dalam kes saya, data jatuh dalam tempoh dari 2016 hingga 2019, jadi kami akan menganggap 5 bait itu cukup.

Untuk menentukan masa tanpa saat, fungsi berikut telah ditulis. Skrip:

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}

Tidak mungkin untuk menulis fungsi berfungsi untuk tahun dan bulan, jadi saya terpaksa menggodamnya. Skrip:

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

Saya pasti bahawa jika anda meluangkan n jumlah masa, salah faham ini boleh diperbetulkan.
Seterusnya, fungsi yang mengembalikan objek datetime daripada rentetan. Skrip:

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)

Berurusan untuk mengesan nilai yang kerap berulang daripada int, int, datetime, datetime Memulihkan data daripada jadual XtraDB tanpa fail struktur menggunakan analisis bait demi bait bagi fail ibd, nampaknya inilah yang anda perlukan. Selain itu, urutan sedemikian tidak diulang dua kali setiap baris.

Menggunakan ungkapan biasa, kami mencari data yang diperlukan:

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)

Sila ambil perhatian bahawa apabila mencari menggunakan ungkapan ini, tidak mungkin untuk menentukan nilai NULL dalam medan yang diperlukan, tetapi dalam kes saya ini tidak kritikal. Kemudian kita pergi melalui apa yang kita dapati dalam gelung. Skrip:

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)

Sebenarnya, itu sahaja, data daripada tatasusunan hasil adalah data yang kami perlukan. ###PS.###
Saya faham bahawa kaedah ini tidak sesuai untuk semua orang, tetapi matlamat utama artikel adalah untuk menggesa tindakan daripada menyelesaikan semua masalah anda. Saya fikir penyelesaian yang paling betul adalah dengan mula mengkaji sendiri kod sumber mariadb, tetapi disebabkan masa yang terhad, kaedah semasa nampaknya adalah yang terpantas.

Dalam sesetengah kes, selepas menganalisis fail, anda akan dapat menentukan struktur anggaran dan memulihkannya menggunakan salah satu kaedah standard daripada pautan di atas. Ini akan menjadi lebih betul dan menyebabkan lebih sedikit masalah.

Sumber: www.habr.com

Tambah komen