Восстановление Данных Из Таблиц Xtradb Без Файла Структуры С Помощью Побайтового Анализа Файла Ibd



Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd



Фон

Так получилось, что сервер был атакован вирусом-вымогателем, который по «счастливой случайности» частично оставил файлы .

ibd (файлы необработанных данных таблиц innodb) нетронутыми, но при этом полностью зашифровал файлы .

fpm ( структурные файлы).

В этом случае .

idb можно разделить на:

  • подлежит восстановлению стандартными инструментами и направляющими.

    Для таких случаев есть отличное статья ;

  • частично зашифрованные таблицы.

    В основном это большие таблицы, для которых (как я понимаю) у злоумышленников не хватило оперативной памяти для полноценного шифрования;

  • Ну и полностью зашифрованные таблицы, которые невозможно восстановить.

Определить, к какому варианту относятся таблицы, можно было, открыв их в любом текстовом редакторе с нужной кодировкой (в моем случае это UTF8) и просто просмотрев файл на наличие текстовых полей, например:

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

Также в начале файла можно наблюдать большое количество 0 байт, и вирусы, использующие блочный алгоритм шифрования (самый распространенный), обычно поражают и их.



Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

В моем случае злоумышленники оставили в конце каждого зашифрованного файла 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:\\some\\path"): 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 для Windows 10.1.24. Кроме того, хотя в mariadb вы работаете с таблицами InnoDB, на самом деле они ЭкстраДБ , что исключает применимость метода с InnoDB mysql.

Анализ файлов

В Python тип данных байты() отображает данные в Юникоде вместо обычного набора чисел.

Хотя просмотреть файл можно и в таком виде, для удобства можно преобразовать байты в числовую форму, преобразуя массив байтов в обычный массив (список(example_byte_array)).

В любом случае оба метода полезны для анализа.

Просмотрев несколько файлов ibd, вы можете найти следующее:

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

Более того, если разделить файл по этим ключевым словам, то в большинстве случаев вы получите ровные блоки данных.

В качестве делителя мы будем использовать минимальную шкалу.



table = table.split("infimum".

encode())

Интересное наблюдение: для таблиц с небольшим объемом данных между нижней и верхней границей находится указатель на количество строк в блоке.



Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

— тестовая таблица с 1-й строкой

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

- тестовая таблица с 2 строками Таблицу массива строк[0] можно пропустить.

Просмотрев его, я все еще не смог найти необработанные данные таблицы.

Скорее всего, этот блок используется для хранения индексов и ключей.

Начиная с table[1] и переводя ее в числовой массив, уже можно заметить некоторые закономерности, а именно:

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

Это целочисленные значения, хранящиеся в строке.

Первый байт указывает, является ли число положительным или отрицательным.

В моем случае все числа положительные.

Из оставшихся 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 .

У таблицы был первичный ключ с автоинкрементом, его также можно найти здесь

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

Сравнив данные тестовых таблиц, выяснилось, что объект 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

Восстановление данных из таблиц XtraDB без файла структуры с помощью побайтового анализа файла ibd

, похоже, это то, что вам нужно.

При этом такая последовательность не повторяется дважды в строке.

С помощью регулярного выражения находим необходимые данные:

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.### Я понимаю, что этот метод подходит не всем, но основная цель статьи – подтолкнуть к действию, а не решить все ваши проблемы.

Думаю, самым правильным решением было бы начать изучать исходный код самостоятельно.

Мариадб , но из-за ограниченности времени текущий метод показался самым быстрым.

В некоторых случаях, проанализировав файл, вы сможете определить примерную структуру и восстановить его одним из стандартных методов по ссылкам выше.

Это будет намного правильнее и вызовет меньше проблем.

Теги: #python #Администрирование базы данных #python3 #Восстановление данных #mariadb #база данных #восстановление #bytes

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.