Skip to content

Instantly share code, notes, and snippets.

@raphaelm
Created August 19, 2013 14:37
Show Gist options
  • Save raphaelm/6269842 to your computer and use it in GitHub Desktop.
Save raphaelm/6269842 to your computer and use it in GitHub Desktop.
EasyMoney (Android expense tracker) backup files are obfuscated. This script decrypts them and stores the data in an SQLite3 file (just like it is stored on the Android device).
#!/usr/bin/env python3
import sys
import re
import sqlite3
import os.path
try:
INFILE = sys.argv[1]
OUTFILE = sys.argv[2]
except:
print("./decrypt.py INFILE OUTFILE")
sys.exit(0)
if os.path.exists(OUTFILE):
sql = []
else:
sql = [
"create table account (_id integer primary key autoincrement, name text not null, description text not null, start_balance numeric not null, create_date integer not null, monthly_budget numeric null, currency numeric null, position numeric null, default_tran_status text null, exclude_from_total numeric null);",
"create table budget (_id integer primary key autoincrement, account_id integer null, category_id integer null, amount numeric not null);",
"create table category (_id integer primary key autoincrement, name text not null, description text null, color text not null, type text null, parent_id numeric null);",
"create table category_color (_id integer primary key autoincrement, category_id integer null, color_code text not null);",
"create table category_tag (_id integer primary key autoincrement, category_id integer not null, name text not null);",
"create table currency (_id integer primary key autoincrement, currency_code text null, currency_symbol text null, placement text null, is_default text null, decimal_places integer null, decimal_separator text null, group_separator text null);",
"create table currency_symbol (_id integer primary key autoincrement, currency_code text null, currency_symbol text null, is_default text null);",
"create table license (_id integer primary key autoincrement, eula_agreed text null, install_date integer null, license_key text null);",
"create table passcode (_id integer primary key autoincrement, passcode text null, enabled text null);",
"create table project (_id integer primary key autoincrement, name text not null, description text null);",
"create table reminder (_id integer primary key autoincrement, tran_id integer not null, title text not null, due_date integer not null, reminder_date integer null, reminder_days integer null, status text null, repeat_id integer null);",
"create table repeat (_id integer primary key autoincrement, tran_id integer null, reminder_id integer null, next_date integer null, repeat integer null, repeat_param integer null);",
"create table skin (_id integer primary key autoincrement, name text null, header_bg_start text null, header_bg_end text null, h1_left text null, h1_right text null, h2_left text null, h2_right text null, divider_background text null, divider_text text null, summary_background text null, summary_text text null, button_background text null);",
"create table system_settings (_id integer primary key autoincrement, version text null);",
"create table tran (_id integer primary key autoincrement, account_id integer null, title text not null, amount numeric not null, tran_date integer null, remarks text not null, category_id integer null, status text null, repeat_id integer null, photo_id integer null, split_id integer null, transfer_account_id numeric null)",
"create table user_settings (_id integer primary key autoincrement, default_reminder_days integer null, reminder_time text null, currency_symbol text null, currency_code text null, bills_reminder_currency text null, autobackup_time text null, autobackup_enabled text null, account_balance_display text null, forward_period text null, forward_period_bills text null, auto_delete_backup_enabled text null, auto_delete_backup_days integer null, default_reporting_period text null, default_reporting_chart_period text null, sound_fx_enabled text null);",
]
f = open(INFILE, "r")
lines = f.readlines()[1:]
f.close()
for line in lines:
line = line[::-1]
line = re.sub("[^ABCDEF0-9]", "", line)
line = line.strip()
plain = bytearray()
for i in range(0, len(line), 2):
byte = line[i:i + 2]
byte = "".join(byte)
plain.append(int(byte, 16))
sql.append(plain.decode("UTF-8", errors="replace"))
conn = sqlite3.connect(OUTFILE)
cur = conn.cursor()
for query in sql:
conn.execute(query)
conn.commit()
cur.close()
conn.close()
@vmkernel
Copy link

vmkernel commented Jul 9, 2018

Thanks a lot, man!
I thought that I lost my backup with all my data I've collected for last 5 years because of simple (') sign somewhere in description of one of expenses project. Thanks to your code I've managed to decode my backup, find the faulty line and replace it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment