ibd файлынын байт-байт анализин колдонуу менен структуралык файлсыз XtraDB таблицаларынан маалыматтарды калыбына келтирүү

ibd файлынын байт-байт анализин колдонуу менен структуралык файлсыз XtraDB таблицаларынан маалыматтарды калыбына келтирүү

prehistory

Серверге ransomware вирусу кол салган, ал "бактылуу кырсык" менен .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 ДАТА УБАКЫТ;
  • date_finish ДАТА УБАКЫТ.

Калыбына келтирүү үчүн .ibd файлынын байт-байт анализи колдонулат, андан кийин аларды окула турган формага айландыруу керек. Бизге керектүү нерсени табуу үчүн, биз int жана datatime сыяктуу маалымат түрлөрүн гана талдап алышыбыз керек, макалада алар гана сүрөттөлөт, бирок кээде биз башка ушул сыяктуу окуяларда жардам бере турган башка маалымат түрлөрүнө да кайрылабыз.

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

Маселе 2: DELETE аркылуу жок кылынган саптардын баары ibd файлында болоорун эске алуу керек, бирок ALTER TABLE менен алардын структурасы жаңыланбайт. Натыйжада, маалымат структурасы файлдын башынан аягына чейин өзгөрүшү мүмкүн. Эгерде сиз оптимизациялоо таблицасын көп колдонсоңуз, анда мындай көйгөйгө туш болушуңуз күмөн.

көңүл буруңуздар, DBMS версиясы маалыматтарды сактоо ыкмасына таасир этет жана бул мисал башка негизги версиялар үчүн иштебеши мүмкүн. Менин учурда, mariadb 10.1.24 Windows версиясы колдонулган. Ошондой эле, mariadbде сиз InnoDB таблицалары менен иштесеңиз да, чындыгында алар XtraDB, бул ыкманын InnoDB mysql менен колдонулушун жокко чыгарат.

Файлды талдоо

Питондо, маалымат түрү байт() сандардын кадимки топтомунун ордуна Юникод маалыматтарын көрсөтөт. Файлды бул формада көрө алсаңыз да, ыңгайлуулук үчүн байт массивдерин кадимки массивге (тизме(мисалы_байт_массив)) айландыруу аркылуу байттарды сандык формага айландырсаңыз болот. Кандай болбосун, эки ыкма тең талдоо үчүн ылайыктуу.

Бир нече 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, бирок убакыттын чектелүүсүнөн улам, учурдагы ыкма эң ылдам болуп көрүндү.

Кээ бир учурларда, файлды талдоодон кийин, сиз болжолдуу түзүмүн аныктап, аны жогорудагы шилтемелерден стандарттык ыкмалардын бирин колдонуп калыбына келтире аласыз. Бул алда канча туура жана азыраак көйгөйлөрдү жаратат.

Source: www.habr.com

Комментарий кошуу