ProHoster > Blog > podávání > Obnova dat z tabulek XtraDB bez souboru struktury pomocí analýzy souboru ibd byte po byte
Obnova dat z tabulek XtraDB bez souboru struktury pomocí analýzy souboru ibd byte po byte
pravěk
Stalo se, že server byl napaden virem ransomwaru, který „šťastnou náhodou“ částečně ponechal soubory .ibd (soubory nezpracovaných dat tabulek innodb) nedotčené, ale zároveň soubory .fpm zcela zašifroval ( strukturní soubory). V tomto případě lze .idb rozdělit na:
podléhají restaurování pomocí standardních nástrojů a vodítek. Pro takové případy existuje vynikající stát se;
částečně šifrované tabulky. Většinou se jedná o velké tabulky, u kterých (jak jsem pochopil) útočníci neměli dostatek RAM pro plné šifrování;
No, plně zašifrované tabulky, které nelze obnovit.
Bylo možné určit, ke které možnosti tabulky patří, pouhým otevřením v libovolném textovém editoru pod požadovaným kódováním (v mém případě je to UTF8) a jednoduchým zobrazením souboru pro přítomnost textových polí, například:
Na začátku souboru můžete také pozorovat velké množství 0 bajtů a viry, které používají algoritmus blokového šifrování (nejběžnější), je obvykle také ovlivňují.
V mém případě útočníci nechali na konci každého zašifrovaného souboru 4bajtový řetězec (1, 0, 0, 0), což zjednodušilo úlohu. K vyhledání neinfikovaných souborů stačil skript:
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)
Ukázalo se tedy, že byly nalezeny soubory patřící do prvního typu. Druhá obnáší hodně ruční práce, ale toho, co se našlo, už bylo dost. Všechno by bylo v pořádku, ale musíte to vědět naprosto precizní konstrukce a (samozřejmě) vznikl případ, že jsem musel pracovat s často přebalovacím pultem. Nikdo si nepamatoval, zda byl změněn typ pole nebo byl přidán nový sloupec.
Wilds City bohužel s takovým případem nedokázalo pomoci, a proto vzniká tento článek.
Dostaňte se k věci
Existuje struktura tabulky před 3 měsíci, která se neshoduje se současnou (možná jedno pole a možná více). Struktura tabulky:
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)
);
v tomto případě musíte extrahovat:
id_point int(11);
id_user int(11);
date_start ČAS SCHŮZKY;
date_finish ČAS SCHŮZKY.
Pro obnovu se používá byte byte analýza souboru .ibd s následným převedením do čitelnější podoby. Protože k nalezení toho, co potřebujeme, potřebujeme pouze analyzovat datové typy jako int a datatime, článek popíše pouze je, ale někdy se odkážeme i na jiné datové typy, které mohou pomoci při dalších podobných incidentech.
Problém 1: pole s typy DATETIME a TEXT měla hodnoty NULL a v souboru jsou jednoduše přeskočena, z tohoto důvodu nebylo možné v mém případě určit strukturu k obnovení. V nových sloupcích byla výchozí hodnota null a část transakce mohla být ztracena kvůli nastavení innodb_flush_log_at_trx_commit = 0, takže určení struktury by bylo nutné věnovat více času.
Problém 2: je třeba vzít v úvahu, že řádky smazané pomocí DELETE budou všechny v souboru ibd, ale s ALTER TABLE nebude jejich struktura aktualizována. V důsledku toho se datová struktura může lišit od začátku souboru do jeho konce. Pokud často používáte OPTIMIZE TABLE, pak je nepravděpodobné, že se s takovým problémem setkáte.
Poznámka, verze DBMS ovlivňuje způsob ukládání dat a tento příklad nemusí fungovat pro jiné hlavní verze. V mém případě byla použita windows verze mariadb 10.1.24. Také, ačkoli v mariadb pracujete s tabulkami InnoDB, ve skutečnosti jsou XtraDB, což vylučuje použitelnost metody s InnoDB mysql.
Analýza souborů
V pythonu datový typ bytes() zobrazí data Unicode místo běžné sady čísel. I když můžete soubor zobrazit v této podobě, pro usnadnění můžete převést bajty do číselné podoby převedením bajtového pole na běžné pole (seznam(example_byte_array)). V každém případě jsou pro analýzu vhodné obě metody.
Po prohlédnutí několika souborů ibd můžete najít následující:
Navíc, pokud soubor rozdělíte těmito klíčovými slovy, získáte většinou sudé bloky dat. Jako dělitel použijeme infimum.
table = table.split("infimum".encode())
Zajímavý postřeh: u tabulek s malým množstvím dat je mezi infimum a supremum ukazatel na počet řádků v bloku.
— zkušební tabulka s 1. řádkem
- testovací stůl se 2 řádky
Tabulku pole řádků[0] lze přeskočit. Po prozkoumání jsem stále nemohl najít nezpracovaná data tabulky. S největší pravděpodobností se tento blok používá k ukládání indexů a klíčů.
Počínaje tabulkou[1] a převést ji do numerického pole si již můžete všimnout některých vzorů, konkrétně:
Toto jsou hodnoty int uložené v řetězci. První bajt označuje, zda je číslo kladné nebo záporné. V mém případě jsou všechna čísla kladná. Ze zbývajících 3 bajtů můžete určit číslo pomocí následující funkce. 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
Například, 128, 0, 0, 1 = 1Nebo 128, 0, 75, 108 = 19308.
Tabulka měla primární klíč s automatickým přírůstkem a lze jej také nalézt zde
Po porovnání dat z testovacích tabulek bylo zjištěno, že objekt DATETIME se skládá z 5 bajtů a začíná 153 (s největší pravděpodobností označující roční intervaly). Vzhledem k tomu, že rozsah DATTIME je '1000-01-01' až '9999-12-31', myslím, že počet bajtů se může lišit, ale v mém případě data spadají do období 2016 až 2019, takže budeme předpokládat že 5 bajtů stačí.
Pro určení času bez sekund byly napsány následující funkce. Skript:
Jsem si jist, že pokud strávíte n množství času, lze toto nedorozumění napravit.
Dále funkce, která vrací objekt datetime z řetězce. 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)
Podařilo se detekovat často se opakující hodnoty z int, int, datetime, datetime , vypadá to, že to je to, co potřebujete. Navíc se taková sekvence neopakuje dvakrát na řádek.
Upozorňujeme, že při vyhledávání pomocí tohoto výrazu nebude možné určit hodnoty NULL v požadovaných polích, ale v mém případě to není kritické. Pak procházíme to, co jsme našli, ve smyčce. 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)
Ve skutečnosti to je vše, data z pole výsledků jsou data, která potřebujeme. ###PS.###
Chápu, že tato metoda není vhodná pro každého, ale hlavním cílem článku je spíše vyvolat akci, než vyřešit všechny vaše problémy. Myslím, že nejsprávnějším řešením by bylo začít studovat zdrojový kód sám mariadb, ale vzhledem k omezenému času se současná metoda zdála být nejrychlejší.
V některých případech budete po analýze souboru schopni určit přibližnou strukturu a obnovit ji pomocí jedné ze standardních metod z výše uvedených odkazů. Bude to mnohem správnější a způsobí to méně problémů.