-
-
Save ross-spencer/752a622f748c3a2be52b4a8d2f8a0b5f to your computer and use it in GitHub Desktop.
Scans SQLite files for known features (UserVersion, AppId, Schema...)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Scans SQLite files for known features (UserVersion, AppId, Schema...) | |
# Ange Albertini 2024 | |
import argparse | |
import hashlib | |
import sqlite3 | |
import sys | |
user_versions = { | |
b"_MTN": "Monotone source repository", | |
} | |
application_ids = { | |
b"\x0F\x05\x51\x11": "Fossil repository", | |
b"\x0F\x05\x51\x12": "Fossil checkout", | |
b"\x0F\x05\x51\x13": "Fossil configuration", | |
b"Acrn": "FlyingMeat Acorn", | |
b"BeDb": "Bentley Systems BeSQLite", | |
b"BeLn": "Bentley Systems Localization", | |
b"Esri": "Esri Spatially-Enabled", | |
b"MPBX": "MBTiles tileset", | |
b"j\3WD": "TeXnicard card", | |
b"CEWE": "CEWE Photobuch", | |
b"TOIR": "Riot Games patcher", | |
b"TMPR": "TwinMotion", | |
b"V35\0": "Vector35 Binary Ninja", | |
b"\x5C\xDE\x09\xEF": "Maple Workbook", | |
b"AUDY": "Audacity3 Project file", | |
b"\x5E\x96\x73\x0E": "Corsair iCue", | |
b"CHEW": "Chewing IME", | |
# Bitcoin | |
# magic: https://en.bitcoin.it/wiki/Protocol_documentation#Message_structure | |
b"\xF9\xBE\xB4\xD9": "Bitcoin Wallet", # MainNet | |
b"\x0B\xBE\xB4\xD9": "Bitcoin Wallet", # Testnet3 | |
b"\xFA\xBF\xB5\xDA": "Bitcoin Wallet", # testnet/regtest | |
b"\x0A\x03\xCF\x40": "Bitcoin Wallet", # signet | |
b"\xF9\xBE\xB4\xFE": "Bitcoin Wallet", # namecoin | |
b"GPKG": "OGC GeoPackage", | |
b"GP10": "OGC GeoPackage", # v1.0 | |
} | |
schemas = { | |
'CREATE TABLE GlobalState (LastSuccessfulSync DATE, HTTPModifiedDate DATE, FileLastModifiedDate DATE, TestPopulation VARCHAR(30), TestSegment VARCHAR(30),' | |
' ProductName VARCHAR(30), ProductMajorVersion INTEGER, ProductMinorVersion INTEGER, LicenseState VARCHAR(15), Language VARCHAR(15), OEM VARCHAR(15), Channel VARCHAR(15) );\n' | |
'CREATE TABLE IPMMessage (ID INTEGER PRIMARY KEY, Title VARCHAR(50), Content VARCHAR(100), ExternalLink VARCHAR(60), AcceptString VARCHAR(20), RejectString VARCHAR(20),' | |
' StartDate DATE, ExpiryDate DATE, LastModifiedDate DATE, Priority INTEGER, DisplayLocation INTEGER, Context INTEGER, PairID INTEGER, MinProductVersion VARCHAR(15),' | |
' MaxProductVersion VARCHAR(15), CCID INTEGER, Status INTEGER, AlreadyDisplayed INTEGER, DisplayCount INTEGER, Version INTEGER, MessageFlag INTEGER );\n' | |
'CREATE TABLE MsgAssetMap (MessageID INTEGER NOT NULL, AssetContext VARCHAR(50) NOT NULL, AssetTitle VARCHAR(100), AssetAction VARCHAR(100), primaryURI VARCHAR(60),' | |
'secondaryURI VARCHAR(60),flags INTEGER DEFAULT 0, AssetType VARCHAR(10) , locationId VARCHAR(32) , LastSyncDate DATE , LastModDate DATE , PRIMARY KEY (MessageID, AssetContext ),' | |
' FOREIGN KEY (MessageID) REFERENCES IPMMessage(ID) ON DELETE CASCADE );\n' | |
'CREATE TABLE ControlMessages (CMDisplayLocation INTEGER, CMMessage VARCHAR(1000) NOT NULL, CMValue VARCHAR(1000), PRIMARY KEY (CMDisplayLocation, CMMessage ) );\n' | |
'CREATE TABLE MessageProperties (msgID INTEGER NOT NULL, messagePropKey VARCHAR(50) NOT NULL, messagePropVal VARCHAR(100), PRIMARY KEY (msgID, messagePropKey ),' | |
' FOREIGN KEY (msgID) REFERENCES IPMMessage(ID) ON DELETE CASCADE );\n': 'Adobe In-Product Messages', | |
'CREATE TABLE pref_events (event_id INTEGER NOT NULL PRIMARY KEY, event_time INTEGER NOT NULL,' | |
' instance_guid TEXT NOT NULL, section_name TEXT NOT NULL, pref_key TEXT, pref_value TEXT, client_nonce INTEGER NOT NULL, added INTEGER NOT NULL );\n' | |
'CREATE TABLE version_table ( version INTEGER NOT NULL PRIMARY KEY );\n': 'Adobe SharedDataEvents', | |
'CREATE TABLE files (\n' | |
' path_id integer primary key check(path_id != 0),\n' | |
' path text not null check(length(path) != 0),\n' | |
' size integer not null,\n' | |
' timestamp integer not null,\n' | |
' permissions integer not null,\n' | |
' symlink text not null,\n' | |
' chunking_version integer not null,\n' | |
' min_chunk_size integer not null,\n' | |
' chunk_size integer not null,\n' | |
' max_chunk_size integer not null,\n' | |
' type integer not null\n' | |
');\n' | |
'CREATE TABLE chunks (\n' | |
' path_id integer not null,\n' | |
' offset integer not null,\n' | |
' id integer not null check (id != 0),\n' | |
' size integer not null,\n' | |
' foreign key (path_id) references files(path_id),\n' | |
' primary key (path_id, offset)\n' | |
') without rowid;\n' | |
'CREATE TABLE sqlite_stat1(tbl,idx,stat);\n': "Riot Games patcher", | |
'CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL);\n': "Bitcoin wallet", | |
'CREATE TABLE sqlar(\n' | |
' name TEXT PRIMARY KEY,\n' | |
' mode INT,\n' | |
' mtime INT,\n' | |
' sz INT,\n' | |
' data BLOB\n' | |
');\n': 'SQLite Archive (sqlarfs)', | |
'CREATE TABLE sqlar(\n' | |
' name TEXT PRIMARY KEY, -- name of the file\n' | |
' mode INT, -- access permissions\n' | |
' mtime INT, -- last modification time\n' | |
' sz INT, -- original file size\n' | |
' data BLOB -- compressed content\n' | |
');\n': 'SQLite Archive', | |
'CREATE TABLE sqlar(\n' | |
' name TEXT PRIMARY KEY,\n' | |
' mode INT,\n' | |
' mtime INT,\n' | |
' sz INT,\n' | |
' data BLOB\n' | |
' );\n': 'SQLite Archive (rust)', | |
'CREATE TABLE s3item_pending_changes\n' | |
' (\n' | |
' sequence_number INTEGER PRIMARY KEY ASC,\n' | |
' op blob,\n' | |
' args blob,\n' | |
' time float,\n' | |
' done int default 0 \n' | |
' );\n' | |
'CREATE TABLE s3item_history\n' | |
' (\n' | |
' sequence_number INTEGER PRIMARY KEY ASC,\n' | |
' key blob,\n' | |
' op blob,\n' | |
' data blob,\n' | |
' time float\n' | |
' );\n' | |
'CREATE TABLE s3api_per_key_metadata\n' | |
' (\n' | |
' s3key blob,\n' | |
' headers blob,\n' | |
' live_date real,\n' | |
' old_version_of blob,\n' | |
' primary key (s3key),\n' | |
' unique (s3key)\n' | |
' );\n': 'Internet Archive metadata', | |
'CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);\n' | |
'CREATE TABLE cookies(creation_utc INTEGER NOT NULL,host_key TEXT NOT NULL,top_frame_site_key TEXT NOT NULL,name TEXT NOT NULL,' | |
'value TEXT NOT NULL,encrypted_value BLOB NOT NULL,path TEXT NOT NULL,expires_utc INTEGER NOT NULL,is_secure INTEGER NOT NULL,' | |
'is_httponly INTEGER NOT NULL,last_access_utc INTEGER NOT NULL,has_expires INTEGER NOT NULL,is_persistent INTEGER NOT NULL,' | |
'priority INTEGER NOT NULL,samesite INTEGER NOT NULL,source_scheme INTEGER NOT NULL,source_port INTEGER NOT NULL,is_same_party INTEGER NOT NULL,last_update_utc INTEGER NOT NULL);\n' | |
'CREATE UNIQUE INDEX cookies_unique_index ON cookies(host_key, top_frame_site_key, name, path);\n': "Cookies.db", | |
"CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY," | |
" originAttributes TEXT NOT NULL DEFAULT ''," | |
" name TEXT, value TEXT, host TEXT, path TEXT, expiry INTEGER, lastAccessed INTEGER," | |
" creationTime INTEGER, isSecure INTEGER, isHttpOnly INTEGER, inBrowserElement INTEGER DEFAULT 0," | |
" sameSite INTEGER DEFAULT 0, rawSameSite INTEGER DEFAULT 0, schemeMap INTEGER DEFAULT 0," | |
" isPartitionedAttributeSet INTEGER DEFAULT 0," | |
" CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes));\n": "Mozilla Cookies", | |
'CREATE TABLE moz_formhistory (\n' | |
' id INTEGER PRIMARY KEY, fieldname TEXT NOT NULL, value TEXT NOT NULL, timesUsed INTEGER, firstUsed INTEGER, lastUsed INTEGER, guid TEXT\n' | |
' \n' | |
' );\n' | |
'CREATE TABLE moz_deleted_formhistory (\n' | |
' id INTEGER PRIMARY KEY, timeDeleted INTEGER, guid TEXT\n' | |
' \n' | |
' );\n' | |
'CREATE TABLE moz_sources (\n' | |
' id INTEGER PRIMARY KEY, source TEXT NOT NULL\n' | |
' \n' | |
' );\n' | |
'CREATE TABLE moz_history_to_sources (\n' | |
' history_id INTEGER, source_id INTEGER\n' | |
' ,\n' | |
' PRIMARY KEY (history_id, source_id),\n' | |
' FOREIGN KEY (history_id) REFERENCES moz_formhistory(id) ON DELETE CASCADE,\n' | |
' FOREIGN KEY (source_id) REFERENCES moz_sources(id) ON DELETE CASCADE\n' | |
' \n' | |
' ) WITHOUT ROWID;\n' | |
'CREATE INDEX moz_formhistory_index ON moz_formhistory(fieldname);\n' | |
'CREATE INDEX moz_formhistory_lastused_index ON moz_formhistory(lastUsed);\n' | |
'CREATE INDEX moz_formhistory_guid_index ON moz_formhistory(guid);\n': 'Mozilla Form History', | |
} | |
schema_hashes = { | |
'e4634cc4f8e933a19105d0517bc52e358d58a4fa3cc052a1adeeed61fee5e982': 'Twin Motion', | |
'73255bd07ef841f4e83fd1b200a9159cf4f0526a797b3427b1733ca38e146bcd': 'Internet Archive metadata', | |
'a912b5ec3cc97373f00b4359bff0dcef772e22c8fcdac1906edba2edaa7ac328': 'Monotone source repository', | |
'c9d31186c845b56c59f02b6b53d508648c10b7f6cec0a993bbae8e97b012ec46': 'Firefox bookmark icons', | |
'519f988a92e0b7271a783b2043d5ee1c4289f8358b50244e78fb51fc2bc63c11': 'Cookies', | |
'25bef53e35d91470e5b9b910a427f6feb9c7c2fb7c3e0be6d9008560998b107c': 'Mozilla Cookies', | |
'43406bf1c991d7c56919f776eabbd861c326f31edde35d0eaf1911bc49cdf5d5': 'Mozilla Form History', | |
'20736fb77f994d6c55f3a3be1d1ad202764f82bade53e719232513bfc5110c2c': 'Microsoft stores', | |
'b1532ea493e5dba68c00d38ccd9e22873e6fcc2f00baaaa1864a9ab8d8ab6eff': 'CloudFlare Network Error Logging reports', | |
'805fafb0cd67a189ee818645149d10f28c4357a4e8e455b9bb66b89d2f6702d3': 'Bitcoin Wallet', | |
'199aa46e1d5c832d77a7019b78b6ef0bc9eef219dad1d347f0cc0fbc4312c585': 'Chewing IME', | |
"cbf1d291ccd67a6d669803d68bf73f848ed9c4f37b79aa47820da0840ed5a8e4": "sqlar", | |
"561fb39579a4b92b1e0fac372c930ec87d7b0321b99edc59808e48677e633895": "sqlar", | |
"3e83e986bf152bd51fd7f8619f19eaaba4c53d84392db25f28ec8622dcfb2526": "sqlar", | |
"b92816a2117ca795240b00f44bdaa43098ec31360cdd677ada807125eb990579": "Ccmdb", | |
"38c7d766819ab324103085c0a6891a0599e98e9a7160987653baa618e10ba4a1": "360safe.Summary.dat", | |
"fe4072e1e296e15e443a6d5b5b69b4bc29a15a5716cf3d59e91857ced650cc2a": "3870112724rsegmnoittet-es.sqlite", | |
"69beb90d7a2577df0ac6acd5fc2b0d5ddd87d3f3ff5864f0e8bad141d6bd42e0": "ActivitiesCache.db", | |
"b078d27aa611271d5ec2024d1766a1bd8c42ca914e88601c3593a0363d60250a": "AdobeNGLAppIDMap.db", | |
"44d211178031186065e0224e2af304cde276a33e5707d83f67124a7443b65145": "AssemblyRegister.db", | |
"3c758542ea2e4c24046b03e9566f5de10993630184541d61bb676f59cbf85e5c": "Browse.VC.db", | |
"613a27ef3dc08a0cc49e425406632bad8832f5f24da3b98b9a41a176fcf7dfac": "Browse.VC.db", | |
"1d8892cad25f37ec0834e94ac238919fabde479a245fd3948ce922faff1dd6e6": "Census.db", | |
"17cf7e999c5d802921a28572e50befd40281e5ac6f5acd094a4b84520ff42626": "DIPS", | |
"9d499a107dba5cd7d87ee9eb0af93c98dac5da7524f14b602f9d8217dd72e97b": "EventLog.db", | |
"5bdd528ded6cb02292f66ef1d198741f18f690f807297f9113c53dd604a3e64e": "EventStore.db", | |
"6f0d392603def8441c50393c016bab91a31a406b8b9c6f318f7fce57894da015": "GPLS.db", | |
"ace46d10f4b8bf1260a56474ccb62e7245343b4cb39f975bc46aec622518ffdd": "History", | |
"62126bd759757089a7fbc0f019040cb181c30a435f5d51dffd4fd40d32e429c7": "Install.db", | |
"bfc588e1a9445f890cfd7c400f0de1caa3885aa36762c5d83c66a3dd07213dc0": "LocalStorage.db", | |
"0a692cbfeed9c2285d00f11bdddd99e9946dcf2a08207cfd37562eedc00075cf": "Login Data", | |
"99f5185950e11829d88ade25bf8d7fa2fd303ac816860d15cc453309e65dee54": "MBG", | |
"95fae60fadc74d78add71470120c27d8f89d1331ea621cc2f0d108c2666985a8": "Media_db.db", | |
"cba30cd327ee3fe2845b825977087908456aa4b588fcc809f352d74b11baf0d2": "Network Action Predictor", | |
"5ef8da39ca87190b068b5c3d1bfc2ed7c84588a1177ef94e5e642870878d0186": "Network Action Predictor", | |
"b2e379f10b23b6b9c1dfe2b975d9c0df78cabe04f05065be3ab797ec5b89bdc3": "Quarantine.db", | |
"2e3a061cb62dceffdca4a66c71999985b4508bd07255c99a46e267a9a6b2da68": "StateRepository-Deployment.srd", | |
"c42a92e515028ca5e617af551017163d712f8576a67ebfb9bafad76258011393": "StateRepository-Machine.srd", | |
"cee901dfa7ee2d29fef4bff27bc364282b5cf8d13bcb3d9bf51df0accea7f01a": "Synchronizer", | |
"72712f0e4d06d89f550e54ad461ca96243a7840394b0d3c9fd5bb17d21a1ab50": "TDData-data.db", | |
"6a0e7ac0505cf62b6620da2abb61217c570f049e70cd2c93a6463bdd3216600e": "Web Data", | |
"d1a222bf23547148d08a124cf431108f25dd6f97035c3a23d63df5b103442377": "Web Data", | |
"64c3ee4941f5a535e76f1f3dcd186148a2767f13170d1e7bfbf7d1c69ef41591": "WebAssistDatabase", | |
"4e9f5fb453532fbae415552c9b2c6ad666c9ed24628584486139cfe82e04f546": "__leanplum.sqlite", | |
"33cf0d178cd6224ba78fa0eb99f4b268c221c88ed1e8c57587d9de544a19b01c": "anzu.db", | |
"ee6d84735a82d7db8a2d19a11288c6ad1b6a2fb9ed3be4d58c4562bd2649ba7a": "app_database.db", | |
"529b1addbabfd3306f6ac8f734b5b1734bf6623e517a73649b645d2c956c2ebb": "archive.db", | |
"a9f0625c7a0f991fc8975d9e0730adbbadc62e304b0edf4849995a8f49324f8f": "autopsy.db", | |
"aa8204611e828d737b3a9c4489dab5e4d17e267005b92d51bfade256107b70cc": "c2rclient.db", | |
"5da6ddf73033935d2a095c1a76c6181c2275fa5405e70cb875f4c19336147cb5": "cache.db", | |
"99a792e6047af56198c389e3cc85068a49459a802cde1336e3947d702fe3ae2f": "cert9.db", | |
"e96577026e7b071c2dcd0752b59541c48a7b5903f7a0f6298abecef4addb6461": "client.serverlist.1.db", | |
"edd318f30f9d0f04c6259e182213ec0522ad022b19fbb91da2b3684212b89ecd": "cty.dat.sqlite", | |
"c1c25fd09cee6681c27a006087279afd927b948cb72afc24da19f82dd2af5a0c": "data.db", | |
"1b06b0ebf9884001143dfec20a280f4e352030adf350468f400732325842c49a": "data.sqlite", | |
"60ecc3bb8821dcced901e81dc46e9b0fe1e8b32bd5ed1a377f323362d5695a86": "file__0.localstorage", | |
"b45b5fd2bb47a79b542d88fd8e59e48a0a6c6174874600193edfc49e50a90658": "first_party_sets.db", | |
"fb1ee772956b7021141370312d7c9931585909228d3bcc7cd635541b05d28a39": "folder_icon_link.db", | |
"ae4411fa7ceb8a722cbdb785bbf62d5e29cccd93ff9c9c18dfefb446590305ea": "ga.sqlite3", | |
"df697cd67c7c7d142c361fd32f1e02aa946b74c7da3138b1ec6ab1590203cb9b": "hdpim.db", | |
"4c6647fd0a4e3c68c7f2606e30f31d488624d03278ecc95d2c525e5bf22071fe": "hitomi_downloader_GUI.ini", | |
"f161614a81ae0f54f843635659ccedd1aca81e642c0d370f2a9d77141bd726f1": "hydra.db", | |
"d7d5c6da0c6168083aa41f4340499d318a4919fb8ad905fd3df2b050b73620ca": "installer.db", | |
"ba297b1936ce3563f92e616ec5eedc69fd2a1f27f0bbfcc54e1f356792cc68c8": "key4.db", | |
"8f01548a007263677255ba374784564b05d069d7f0353d935546ac8f712bbe93": "launcher-v2.sqlite", | |
"806f40fa990e9793e88d8227e11e8ad498a339e5bab9d39d24280c8bff9dca32": "main.db", | |
"8e1022e8f0f69d011ef83d2222f1a62f5f7a61ed60992da836d4d5f1b7f917d1": "metadata.db", | |
"6e67e9acffbddd7daaa8afb5683d888ce2b7c3aa34b167b6cb3badfdc69a95d8": "weird_6e67", | |
"8b7135a83720e7078e4cc96e27e03d7e26616f6ee3acd1ac080051a1f070bde4": "pal.db", | |
"6ae0e03f6d1c8d45bf91b7d80f7ca8eb1c088dda736341c5cf16dfe147ea9079": "pcd.db", | |
"e45d95d2aa4d6363095f5a9ea62752c798b31f73263e974aee493b351d1acf9a": "permissions.sqlite", | |
"28900b3652398ebd7c9d892fc476cdec8a0b0808d6267515cef36c7c6bac9500": "photothumb.db", | |
"0cde4d48c0fb79fc3672b3f16b90a0fb859a8e41de22e83b564ca9e87872d2fd": "pim.db", | |
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": "psid.db", | |
"48224a37a39dfd3cb94cac4d747f4ea1676df576672661fa5cb1672af623ae00": "quotamanager", | |
"3b2da09df7757eac41fd706df56b2befa1dbf62cb8acdf31c25ceec5ba00502b": "reportnew.db", | |
"1602399d818eb2ff0d858bc7000944b1cd28066e3d6e3bb2271f9c4fc76bdf53": "resume.dat", | |
"f69ffeee0558c1948758a0228a6de3d0e5eace6836268e4a9dd69a6a73c2e398": "settings.db", | |
"25b9c32e16fd94a9850829c16dc9874f2dcffc365fbabe8d7474c3d5d094c48d": "slnx.sqlite", | |
"c972ccbe9357e5f061e28843419def0f0d4bcbce3af5ab13cadffc5a66c8d3c3": "sponge.dat", | |
"c14043a957c79761fd67c118ab8d093aa769119730adfbabb56b61c84bd2b579": "store.bak", | |
"9b21a6a0ab430fc8a164a7dbc57aaf4cb24e45b6d28a17c9b4e1c4f8585efe15": "task", | |
"8dc99fbb154f3fa6a7ea3cdc81772168a6cab65cff6990520e12dd0f76fce78c": "trash_database", | |
"0dfa976a291cca097a5911cf6b0133427d9581307f79183fd6757e03bb51a00c": "uFiler.db", | |
"1dc000686fd94fb5ce6c167d3580b660c1942f34b4820dd998f3aecaa11a4028": "ukm_db", | |
"1a0420300d7432986582f9408cc18ce1710ef6026a3dc2e8ab1c1dcc6f93cbdc": "user_vod.db_magis", | |
"88f4525e69ce8d49a1e2b34ec3d37c6a44e46fd3ca09d86f40198a60f257cb66": "wdch.dat", | |
"b933b2a2c39db398dd2ec8540e113c560dd336fc30a97ed9a320243aa8a81836": "wpndatabase.db", | |
"2836469e240f01b8f08eba04b74201a8f3d167f191318c117e707d9a849acd33": "z.xyz", | |
} | |
queries = { | |
"Fossil repository": "SELECT login AS Login, pw as Password FROM user", | |
"Mozilla Cookies": "SELECT host, name, value FROM moz_cookies", | |
"Cookies": "SELECT host_key, name, value FROM cookies", | |
"sqlar": "SELECT name, sz AS Size, SUBSTR(data, 1, 10) AS Contents FROM sqlar", | |
"Web Data": "SELECT name_on_card FROM credit_cards", | |
"Fossil checkout": "SELECT pathname AS PathName FROM vfile", | |
"History": "SELECT top_level_url, frame_url FROM visited_links", | |
"Mozilla Form History": "SELECT value AS Url FROM moz_formhistory", | |
} | |
HEADER_LENGTH = 80 | |
def readSQLiteFile(fn): | |
try: | |
fh = open(fn, "rb") | |
except FileNotFoundError: | |
return False, "file not found" | |
data = fh.read(HEADER_LENGTH) | |
fh.close() | |
if len(data) < HEADER_LENGTH: | |
return False, "file too small" | |
if data[:16] != b"SQLite format 3\0": | |
fh.close() | |
return False, "invalid signature" | |
page_size_bytes = int.from_bytes(data[16:16+2], byteorder="little") | |
if page_size_bytes not in [2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100]: | |
return False, "invalid page_size_bytes: %X" % page_size_bytes | |
file_format_write_version = int.from_bytes( | |
data[18:18+1], byteorder="little") | |
if file_format_write_version not in [1, 2]: | |
return False, "invalid file_format_write_version: %X" % file_format_write_version | |
file_format_read_version = int.from_bytes( | |
data[19:19+1], byteorder="little") | |
if file_format_read_version not in [1, 2]: | |
return False, "invalid file_format_read_version: %X" % file_format_read_version | |
return True, data | |
def run_query(connection, query, data=(), map=lambda x: x[0]): | |
lines = [] | |
cursor = connection.cursor() | |
for row in cursor.execute(query, data): | |
if row is not None and row[0] is not None: | |
lines.append(map(row)) | |
cursor.close() | |
connection.commit() | |
return lines | |
parser = argparse.ArgumentParser( | |
prog='SQLbuddy', description='SQLite file type checker') | |
parser.add_argument('filename', help='database file') | |
parser.add_argument( | |
'--dump-schema', help='dump the database schema', action='store_true') | |
args = parser.parse_args() | |
fn = args.filename | |
result, info = readSQLiteFile(fn) | |
if result == False: | |
print("Error %s: %s" % (repr(fn), info)) | |
sys.exit() | |
# https://www.sqlite.org/pragma.html#pragma_user_version | |
user_version = info[60:60+4] | |
# https://www.sqlite.org/pragma.html#pragma_application_id | |
application_id = info[68:68+4] | |
print("Filename: %s" % (repr(fn))) | |
def b2h(b): | |
return b" ".join(b"%02X" % c for c in b).decode() | |
if user_version != b'\0\0\0\0': | |
print("user_version: %s (%s) [%s]" % ( | |
repr(user_version[1:]), b2h(user_version), | |
user_versions[user_version] if user_version in user_versions else "UNK")) | |
if application_id != b'\0\0\0\0': | |
print("application_id: %s (%s) [%s]" % ( | |
repr(application_id)[1:], b2h(application_id), | |
application_ids[application_id] if application_id in application_ids else "UNK")) | |
connection = sqlite3.connect(fn) | |
# adding ";\n" to each statement like '.schema' does | |
QUERY_END = ";\n" | |
GET_SQL = "SELECT sql FROM sqlite_schema;" # include index, triggers and views | |
try: | |
lines = run_query(connection, GET_SQL, data=(), | |
map=lambda row: row[0] + QUERY_END) | |
connection.close() | |
except sqlite3.OperationalError: | |
print("Error: Operational Error") | |
sys.exit() | |
schema = "".join(lines) | |
def printSchema(schema): | |
lines = schema.split("\n") | |
for line in lines[:-1]: | |
print(repr(line + "\n")) | |
if schema in schemas: | |
print("Schema: %s" % (schemas[schema] if schema in schemas else 'UNK')) | |
if args.dump_schema: | |
printSchema(schema) | |
schema_hash = hashlib.sha256(schema.encode()).hexdigest() | |
appName = schema_hashes[schema_hash] if schema_hash in schema_hashes else 'UNK' | |
print("Schema Hash: %s [%s]" % ( | |
schema_hash, appName)) | |
if appName in queries: | |
query = queries[appName] | |
connection = sqlite3.connect(fn) | |
try: | |
lines = run_query(connection, query, data=()) | |
connection.close() | |
except sqlite3.OperationalError: | |
print("Error: Operational Error") | |
sys.exit() | |
print("Contents: %s" % (" ".join(repr(line) for line in lines))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment