ibd فائل کے بائٹ بائی بائٹ تجزیہ کا استعمال کرتے ہوئے بغیر کسی اسٹرکچر فائل کے XtraDB ٹیبلز سے ڈیٹا کی بازیافت

ibd فائل کے بائٹ بائی بائٹ تجزیہ کا استعمال کرتے ہوئے بغیر کسی اسٹرکچر فائل کے XtraDB ٹیبلز سے ڈیٹا کی بازیافت

پس منظر

ایسا ہوا کہ سرور پر ایک ransomware وائرس کا حملہ ہوا، جس نے ایک "خوش قسمت حادثے" کے ذریعے .ibd فائلوں کو (innodb ٹیبلز کی خام ڈیٹا فائلوں) کو جزوی طور پر چھوڑ دیا، لیکن ساتھ ہی ساتھ .fpm فائلوں کو مکمل طور پر خفیہ کر دیا۔ ساخت کی فائلیں)۔ اس صورت میں، .idb کو اس میں تقسیم کیا جا سکتا ہے:

  • معیاری ٹولز اور گائیڈز کے ذریعے بحالی سے مشروط۔ اس طرح کے معاملات کے لئے، ایک بہترین ہے بن;
  • جزوی طور پر خفیہ کردہ میزیں۔ زیادہ تر یہ بڑی میزیں ہیں، جن کے لیے (جیسا کہ میں سمجھتا ہوں) حملہ آوروں کے پاس مکمل انکرپشن کے لیے کافی RAM نہیں تھی۔
  • ٹھیک ہے، مکمل طور پر خفیہ کردہ میزیں جو بحال نہیں ہوسکتی ہیں.

مطلوبہ انکوڈنگ (میرے معاملے میں یہ 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 کے ساتھ ان کی ساخت کو اپ ڈیٹ نہیں کیا جائے گا۔ نتیجے کے طور پر، ڈیٹا کا ڈھانچہ فائل کے آغاز سے اس کے اختتام تک مختلف ہو سکتا ہے۔ اگر آپ اکثر OPTIMIZE TABLE استعمال کرتے ہیں، تو آپ کو ایسی پریشانی کا سامنا کرنے کا امکان نہیں ہے۔

توجہ دینا، DBMS ورژن ڈیٹا کو ذخیرہ کرنے کے طریقے کو متاثر کرتا ہے، اور یہ مثال دوسرے بڑے ورژن کے لیے کام نہیں کر سکتی۔ میرے معاملے میں، mariadb 10.1.24 کا ونڈوز ورژن استعمال کیا گیا تھا۔ اس کے علاوہ، اگرچہ mariadb میں آپ InnoDB ٹیبلز کے ساتھ کام کرتے ہیں، حقیقت میں وہ ہیں۔ XtraDB۔، جو InnoDB mysql کے ساتھ طریقہ کار کے قابل اطلاق کو خارج کرتا ہے۔

فائل کا تجزیہ

ازگر میں، ڈیٹا کی قسم بائٹس() نمبروں کے باقاعدہ سیٹ کی جگہ یونیکوڈ ڈیٹا دکھاتا ہے۔ اگرچہ آپ فائل کو اس فارم میں دیکھ سکتے ہیں، لیکن سہولت کے لیے آپ بائٹس کو عددی شکل میں تبدیل کر کے بائٹ ارے کو باقاعدہ صف میں تبدیل کر سکتے ہیں (list(example_byte_array))۔ کسی بھی صورت میں، دونوں طریقے تجزیہ کے لیے موزوں ہیں۔

کئی ibd فائلوں کو دیکھنے کے بعد، آپ کو درج ذیل مل سکتے ہیں:

ibd فائل کے بائٹ بائی بائٹ تجزیہ کا استعمال کرتے ہوئے بغیر کسی اسٹرکچر فائل کے XtraDB ٹیبلز سے ڈیٹا کی بازیافت

مزید یہ کہ، اگر آپ فائل کو ان مطلوبہ الفاظ سے تقسیم کرتے ہیں، تو آپ کو زیادہ تر ڈیٹا کے بلاکس بھی ملیں گے۔ ہم infimum کو تقسیم کار کے طور پر استعمال کریں گے۔

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

ایک دلچسپ مشاہدہ: اعداد و شمار کی ایک چھوٹی سی مقدار کے ساتھ جدولوں کے لیے، انفیمم اور سپریمم کے درمیان بلاک میں قطاروں کی تعداد کا اشارہ ہوتا ہے۔

ibd فائل کے بائٹ بائی بائٹ تجزیہ کا استعمال کرتے ہوئے بغیر کسی اسٹرکچر فائل کے XtraDB ٹیبلز سے ڈیٹا کی بازیافت - پہلی قطار کے ساتھ ٹیسٹ ٹیبل

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

مجھے یقین ہے کہ اگر آپ کافی وقت صرف کریں تو اس غلط فہمی کو دور کیا جا سکتا ہے۔
اگلا، ایک فنکشن جو سٹرنگ سے ڈیٹ ٹائم آبجیکٹ لوٹاتا ہے۔ سکرپٹ:

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.###
میں سمجھتا ہوں کہ یہ طریقہ ہر کسی کے لیے موزوں نہیں ہے، لیکن مضمون کا بنیادی مقصد آپ کے تمام مسائل کو حل کرنے کے بجائے فوری کارروائی کرنا ہے۔ میرے خیال میں سب سے درست حل یہ ہو گا کہ سورس کوڈ کا خود مطالعہ شروع کر دیں۔ ماریڈب، لیکن محدود وقت کی وجہ سے، موجودہ طریقہ سب سے تیز لگتا تھا۔

کچھ معاملات میں، فائل کا تجزیہ کرنے کے بعد، آپ اوپر دیے گئے لنکس میں سے کسی ایک معیاری طریقہ کا استعمال کرتے ہوئے تخمینی ساخت کا تعین کرنے اور اسے بحال کرنے کے قابل ہو جائیں گے۔ یہ بہت زیادہ درست ہوگا اور کم مسائل پیدا کرے گا۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں