Skip to content

Commit

Permalink
- Added encryption to support.zip for extra privacy / security.
Browse files Browse the repository at this point in the history
- Regression bug fix: When a Magisk module was disabled in Magisk, it was not reflected in PixelFlasher.
- Added `Refresh` button to refresh Magisk module list at will.
- When `Apply Custom ROM` checkbox is checked, make sure that a ROM file is also selected, otherwise ignore the selection.
- After rebooting to bootloader, if the device is not detected, sleep 5 seconds and try again.
- Update `Readme` to include a link to proper USB driver setup on Linux systems.
  • Loading branch information
badabing2005 committed May 9, 2024
1 parent 911f913 commit 0ea3b64
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 66 deletions.
2 changes: 1 addition & 1 deletion build-on-mac.spec
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ exe = EXE(pyz,
icon='images/icon-dark-256.icns')
app = BUNDLE(exe,
name='PixelFlasher.app',
version='6.9.3.1',
version='6.9.4.0',
icon='./images/icon-dark-256.icns',
bundle_identifier='com.badabing.pixelflasher')
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
rm -rf build dist
VERSION=6.9.3.1
VERSION=6.9.4.0
NAME="PixelFlasher"
DIST_NAME="PixelFlasher"

Expand Down
2 changes: 1 addition & 1 deletion constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

APPNAME = 'PixelFlasher'
CONFIG_FILE_NAME = 'PixelFlasher.json'
VERSION = '6.9.3.1'
VERSION = '6.9.4.0'
SDKVERSION = '33.0.3'
MAIN_WIDTH = 1400
MAIN_HEIGHT = 1040
Expand Down
14 changes: 14 additions & 0 deletions magisk_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ def __init__(self, *args, parent=None, config=None, **kwargs):
self.disable_denylist_button = wx.Button(self, wx.ID_ANY, u"Disable Denylist", wx.DefaultPosition, wx.DefaultSize, 0)
self.disable_denylist_button.SetToolTip(u"Disable Magisk denylist")

# Refresh
self.refresh_button = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, 0)
self.refresh_button.SetToolTip(u"Refresh Magisk modules list.")

# static line
self.staticline1 = wx.StaticLine(parent=self, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LI_HORIZONTAL)

Expand All @@ -154,6 +158,7 @@ def __init__(self, *args, parent=None, config=None, **kwargs):
self.disable_zygisk_button.SetMinSize((button_width, -1))
self.enable_denylist_button.SetMinSize((button_width, -1))
self.disable_denylist_button.SetMinSize((button_width, -1))
self.refresh_button.SetMinSize((button_width, -1))

# Label for managing denylist and SU Permissions
management_label = wx.StaticText(parent=self, id=wx.ID_ANY, label=u"To manage denylist or to manage SU permissions, use PixelFlasher's App Manager feature.")
Expand Down Expand Up @@ -189,6 +194,7 @@ def __init__(self, *args, parent=None, config=None, **kwargs):
v_buttons_sizer.Add(self.disable_zygisk_button, 0, wx.TOP | wx.RIGHT, 5)
v_buttons_sizer.Add(self.enable_denylist_button, 0, wx.TOP | wx.RIGHT, 5)
v_buttons_sizer.Add(self.disable_denylist_button, 0, wx.TOP | wx.RIGHT, 5)
v_buttons_sizer.Add(self.refresh_button, 0, wx.TOP | wx.RIGHT, 5)
v_buttons_sizer.Add(self.staticline1, 0, wx.TOP | wx.RIGHT, 5)
v_buttons_sizer.AddStretchSpacer()

Expand Down Expand Up @@ -224,6 +230,7 @@ def __init__(self, *args, parent=None, config=None, **kwargs):
self.disable_zygisk_button.Bind(wx.EVT_BUTTON, self.onDisableZygisk)
self.enable_denylist_button.Bind(wx.EVT_BUTTON, self.onEnableDenylist)
self.disable_denylist_button.Bind(wx.EVT_BUTTON, self.onDisableDenylist)
self.refresh_button.Bind(wx.EVT_BUTTON, self.onRefresh)
self.cancel_button.Bind(wx.EVT_BUTTON, self.onCancel)
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected, self.list)
self.html.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
Expand Down Expand Up @@ -302,6 +309,7 @@ def PopulateList(self, refresh=False):
self.systemless_hosts_button.Enable(False)
self.enable_denylist_button.Enable(False)
self.disable_denylist_button.Enable(False)
self.refresh_button.Enable(False)

self.list.SetItemColumnImage(i, 0, -1)
with contextlib.suppress(Exception):
Expand Down Expand Up @@ -436,6 +444,12 @@ def onEnableDenylist(self, e):
device.magisk_enable_denylist(True)
self._on_spin('stop')

# -----------------------------------------------
# onRefresh
# -----------------------------------------------
def onRefresh(self, e):
self.refresh_modules()

# -----------------------------------------------
# onDisableDenylist
# -----------------------------------------------
Expand Down
14 changes: 10 additions & 4 deletions modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ def get_flash_settings(self):
message += f"Android SDK Version: {get_sdk_version()}\n"
message += f"Device: {self.config.device} {device.hardware} {device.build}\n"
message += f"Factory Image: {self.config.firmware_path}\n"
if p_custom_rom :
if p_custom_rom and p_custom_rom_path:
message += f"Custom Rom: {str(p_custom_rom)}\n"
message += f"Custom Rom File: {p_custom_rom_path}\n"
rom_file = ntpath.basename(p_custom_rom_path)
Expand Down Expand Up @@ -4105,9 +4105,15 @@ def flash_phone(self):
self.refresh_device(device_id)
device = get_phone()
if device is None:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Unable to detect the device.")
print("Aborting ...\n")
return -1
# sleep 5 seconds and try again for good measure
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Unable to detect the device.\n Retrying in 5 seconds ...")
time.sleep(5)
self.refresh_device(device_id)
device = get_phone()
if device is None:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Still unable to detect the device.")
print("Aborting ...\n")
return -1
print("Checking if the bootloader is unlocked ...")
if not (device.unlocked or (self.config.advanced_options and self.config.flash_mode == 'customFlash' and image_mode == 'SIDELOAD') or self.config.flash_mode == 'OTA'):
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Bootloader is locked, can't flash.")
Expand Down
2 changes: 1 addition & 1 deletion phone.py
Original file line number Diff line number Diff line change
Expand Up @@ -2145,7 +2145,7 @@ def get_magisk_detailed_modules(self, refresh=False):
try:
if self.mode == 'adb' and self.rooted:
if sys.platform == "win32":
theCmd = f"\"{get_adb()}\" -s {self.id} shell \"su -c \'for FILE in /data/adb/modules/*; do echo $FILE; if test -f \"$FILE/remove\"; then echo \"state=remove\"; elif test -f \"$FILE/disabled\"; then echo \"state=disabled\"; else echo \"state=enabled\"; fi; cat \"$FILE/module.prop\"; echo; echo -----pf;done\'\""
theCmd = f"\"{get_adb()}\" -s {self.id} shell \"su -c \'for FILE in /data/adb/modules/*; do echo $FILE; if test -f \"$FILE/remove\"; then echo \"state=remove\"; elif test -f \"$FILE/disable\"; then echo \"state=disabled\"; else echo \"state=enabled\"; fi; cat \"$FILE/module.prop\"; echo; echo -----pf;done\'\""
res = run_shell(theCmd, encoding='utf-8')
if res.returncode == 0:
modules = []
Expand Down
187 changes: 134 additions & 53 deletions runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup
from cryptography import x509
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

import lz4.frame
import requests
Expand Down Expand Up @@ -1835,6 +1840,7 @@ def create_support_zip():
tmp_dir_full = os.path.join(config_path, 'tmp')
support_dir_full = os.path.join(config_path, 'support')
support_zip = os.path.join(tmp_dir_full, 'support.zip')
temp_dir = tempfile.gettempdir()

# if a previous support dir exist delete it along with support.zip
if os.path.exists(support_dir_full):
Expand Down Expand Up @@ -1925,9 +1931,41 @@ def create_support_zip():
if os.path.exists(file_path):
sanitize_db(file_path)

# create symmetric key
session_key = Fernet.generate_key()

# zip support folder
debug(f"Zipping {support_dir_full} ...")
shutil.make_archive(support_dir_full, 'zip', support_dir_full)
zip_file_path = shutil.make_archive(support_dir_full, 'zip', support_dir_full)

# delete support folder
delete_all(support_dir_full)

# encrypt support.zip with session key
symmetric_cipher = Fernet(session_key)
with open(zip_file_path, 'rb') as f:
encrypted_data = symmetric_cipher.encrypt(f.read())
encrypted_zip_file_path = zip_file_path + '.pf'
with open(encrypted_zip_file_path, 'wb') as f:
f.write(encrypted_data)

# delete unencrypted support.zip
os.remove(zip_file_path)

# encrypt session key with RSA public key
encrypted_session_key_path = os.path.join(tmp_dir_full, 'pf.dat')
encrypt_sk(session_key=session_key, output_file_name=encrypted_session_key_path, public_key=None)

# zip encrypted support.zip and session key
final_zip_file_path = zip_file_path
with zipfile.ZipFile(final_zip_file_path, 'w') as final_zip:
final_zip.write(encrypted_zip_file_path, arcname='support.pf')
final_zip.write(encrypted_session_key_path, arcname='pf.dat')

# delete encrypted support.zip and session key
os.remove(encrypted_zip_file_path)
os.remove(encrypted_session_key_path)

except Exception as e:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Encountered an error while creating support.zip.")
traceback.print_exc()
Expand All @@ -1937,64 +1975,107 @@ def create_support_zip():
# Function sanitize_file
# ============================================================================
def sanitize_file(filename):
debug(f"Sanitizing {filename} ...")
with contextlib.suppress(Exception):
with open(filename, "rt", encoding='ISO-8859-1', errors="replace") as fin:
data = fin.read()
data = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', data, flags=re.IGNORECASE)
data = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', data, flags=re.IGNORECASE)
data = re.sub(r'(\"device\":\s+)(\"\w+?\")', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device\sid:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device\s+\')(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(\(usb\)\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(superkey:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(./boot_patch.sh\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Rebooting device\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Flashing device\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(waiting for\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Serial\sNumber\.+\:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(fastboot(.exe)?\"? -s\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(adb(.exe)?\"? -s\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(\S\ \((?:adb|f\.b|rec|sid)\) )(.+?)(\s+.*)', r'\1REDACTED\3', data, flags=re.IGNORECASE)
data = re.sub(r'(?<=List of devices attached\n)((?:\S+\s+device\n)+)', lambda m: re.sub(r'(\S+)(\s+device)', r'REDACTED\2', m.group(0)), data, flags=re.MULTILINE)
data = re.sub(r'(?<=debug: fastboot devices:\n)((?:\S+\s+fastboot\n)+)', lambda m: re.sub(r'(\S+)(\s+fastboot)', r'REDACTED\2', m.group(0)), data, flags=re.MULTILINE)
with open(filename, "wt", encoding='ISO-8859-1', errors="replace") as fin:
fin.write(data)
try:
debug(f"Sanitizing {filename} ...")
with contextlib.suppress(Exception):
with open(filename, "rt", encoding='ISO-8859-1', errors="replace") as fin:
data = fin.read()
data = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', data, flags=re.IGNORECASE)
data = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', data, flags=re.IGNORECASE)
data = re.sub(r'(\"device\":\s+)(\"\w+?\")', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device\sid:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(device\s+\')(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(\(usb\)\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(superkey:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(./boot_patch.sh\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Rebooting device\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Flashing device\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(waiting for\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(Serial\sNumber\.+\:\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(fastboot(.exe)?\"? -s\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(adb(.exe)?\"? -s\s+)(\w+)', r'\1REDACTED', data, flags=re.IGNORECASE)
data = re.sub(r'(\S\ \((?:adb|f\.b|rec|sid)\) )(.+?)(\s+.*)', r'\1REDACTED\3', data, flags=re.IGNORECASE)
data = re.sub(r'(?<=List of devices attached\n)((?:\S+\s+device\n)+)', lambda m: re.sub(r'(\S+)(\s+device)', r'REDACTED\2', m.group(0)), data, flags=re.MULTILINE)
data = re.sub(r'(?<=debug: fastboot devices:\n)((?:\S+\s+fastboot\n)+)', lambda m: re.sub(r'(\S+)(\s+fastboot)', r'REDACTED\2', m.group(0)), data, flags=re.MULTILINE)
with open(filename, "wt", encoding='ISO-8859-1', errors="replace") as fin:
fin.write(data)
except Exception as e:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Encountered an error while sanitizing {filename}")
traceback.print_exc()


# ============================================================================
# Function sanitize_db
# ============================================================================
def sanitize_db(filename):
debug(f"Sanitizing {filename} ...")
con = sl.connect(filename)
con.execute("PRAGMA secure_delete = ON;")
cursor = con.cursor()
with con:
data = con.execute("SELECT id, file_path FROM BOOT")
for row in data:
id = row[0]
file_path = row[1]
if sys.platform == "win32":
file_path_sanitized = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
else:
file_path_sanitized = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
cursor.execute("Update BOOT set file_path = ? where id = ?", (file_path_sanitized, id,))
con.commit()
with con:
data = con.execute("SELECT id, file_path FROM PACKAGE")
for row in data:
id = row[0]
file_path = row[1]
if sys.platform == "win32":
file_path_sanitized = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
else:
file_path_sanitized = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
cursor.execute("Update PACKAGE set file_path = ? where id = ?", (file_path_sanitized, id,))
con.commit()
# Wipe the Write-Ahead log data
con.execute("VACUUM;")
try:
debug(f"Sanitizing {filename} ...")
con = sl.connect(filename)
con.execute("PRAGMA secure_delete = ON;")
cursor = con.cursor()
with con:
data = con.execute("SELECT id, file_path FROM BOOT")
for row in data:
id = row[0]
file_path = row[1]
if sys.platform == "win32":
file_path_sanitized = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
else:
file_path_sanitized = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
cursor.execute("Update BOOT set file_path = ? where id = ?", (file_path_sanitized, id,))
con.commit()
with con:
data = con.execute("SELECT id, file_path FROM PACKAGE")
for row in data:
id = row[0]
file_path = row[1]
if sys.platform == "win32":
file_path_sanitized = re.sub(r'(\\Users\\+)(?:.*?)(\\+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
else:
file_path_sanitized = re.sub(r'(\/Users\/+)(?:.*?)(\/+)', r'\1REDACTED\2', file_path, flags=re.IGNORECASE)
cursor.execute("Update PACKAGE set file_path = ? where id = ?", (file_path_sanitized, id,))
con.commit()
# Wipe the Write-Ahead log data
con.execute("VACUUM;")
except Exception as e:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Encountered an error while sanitizing db {filename}.")
traceback.print_exc()


# ============================================================================
# Function encrypt_file
# ============================================================================
def encrypt_sk(session_key, output_file_name, public_key=None):
try:
if public_key is None:
public_key = serialization.load_pem_public_key(
b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj/OLsTAnmLVDR0tpTCEF
TrEi0touCGRCRPmScmZpyY0+b+Iv52gFJMmYaqC+HUbd3F9tdLWFwWmRFSYXFXaV
STb1F8DbG+dqWMTG6HtilVl8yfX/ihftlfl/Zj6mtMj3BmMNe475GohwZTdfXXkF
hPRxrx2WIVlzrZAozVdfLCj6o7iCq27Wbsuis7x5LtlM5ojraK7lYPMlCXigR+2N
VDsaAzCaYZAxn2YXNrtLRcmwsRxEH1YnJgQiH7CqJz8w10ArkOxvZ/vbLq3Yrokd
JPcPqPWn9Zu0Rb9q3U42ghuO7f5Laqt0ANf4nHaMK+Q3sWZvf/rVpOIlrLVCaa/H
swIDAQAB
-----END PUBLIC KEY-----""",
backend=default_backend()
)

encrypted_session_key = public_key.encrypt(
session_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)

with open(output_file_name, 'wb') as f:
f.write(encrypted_session_key)
except Exception as e:
print(f"\n{datetime.now():%Y-%m-%d %H:%M:%S} ERROR: Encountered an in function encrypt_sk")
traceback.print_exc()


# ============================================================================
Expand Down
2 changes: 1 addition & 1 deletion windows-metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# https://github.com/DudeNr33/pyinstaller-versionfile
# create-version-file windows-metadata.yaml --outfile windows-version-info.txt
Version: 6.9.3.1
Version: 6.9.4.0
FileDescription: PixelFlasher
InternalName: PixelFlasher
OriginalFilename: PixelFlasher.exe
Expand Down
Loading

0 comments on commit 0ea3b64

Please sign in to comment.