mirror of
https://github.com/tuya-cloudcutter/tuya-cloudcutter.git
synced 2026-02-19 21:51:18 +01:00
* Initial RTL8720CF support * Fix RTL8720CF_OTA file validation. * be a bit more robust on chip string matching * Really rough refactor of haxomatic for RTL8720CF, not complete. * Update RTL8720CF 2.3.0 haxomatic hex match strings * Remove length validation from authkey/uuid so it can work with both Tuya and CloudCutter generated keys. * Fix bk7231 string detection Add second RTL8720CF 2.3.0 profile * Refactor haxomatic to be more modular and maintainable. * haxomatic - minor cleanup * profile-building - Pull PSK when pulling schema. * Haxomatic - Search all binaries for patch patterns. Update known RTL8720CF match pattern identifiers. * Change network to custom 10.204.0.1/24 network (204 = 0xCC) Send multiple DNS servers, which may help devices that hang after DHCP Spend less time sending wifi connect requests so AP can start listening sooner. * Update exploit for new offsets. * Haxomatic - Add 1.0.x SDK * Update haxomatic for newer found patterns. * Minor tweaks * Updates to profile-building * Add storage parsing to extract_rtl8720cf * Switch to bk7231tools to extract rtl8720cf storage to remove an unneeded dependency. * remove debug code * Add special case for sdk identification for single build missing standard string. * Find swv before device_class, as we may want to search directly after it. * Update comments, seek entire bin for storage. * Add missing new address in profile. Add ability to process inactive OTA app. * Update documentation. * fix typo. * Fix a type in beken extract. * Add haxomatic pattern for oddball BK7231N 2.3.1 SDK. * Haxomatic - Add RTL8720CF 2.3.1 SDK pattern. * Fix copy/paste typo * profile-building - proceess_app - add more device class match strings. * one more * profile-building - better log SDK data * Add a special thanks section. * fix typo * Clean up documentation. * documentation - use numbered lists. * process_app - add another device class identifier.
84 lines
3.9 KiB
Python
84 lines
3.9 KiB
Python
import json
|
|
import os.path
|
|
import sys
|
|
|
|
|
|
def write_file(key, value: str):
|
|
try:
|
|
with open(os.path.join(base_folder, base_name + "_" + key + ".txt"), "x") as file:
|
|
file.write(value)
|
|
except:
|
|
return
|
|
|
|
def dump(file, process_inactive_app: bool = False):
|
|
with open(file, "r") as storage_file:
|
|
storage = json.load(storage_file)
|
|
global base_name, base_folder
|
|
base_name = os.path.basename(file)[:-13]
|
|
base_folder = os.path.dirname(file)
|
|
factory_pin = None
|
|
firmware_key = None
|
|
product_key = None
|
|
if 'gw_bi' in storage and storage['gw_bi']:
|
|
print(f"[+] uuid: {storage['gw_bi']['uuid']}")
|
|
write_file("uuid", storage['gw_bi']['uuid'])
|
|
print(f"[+] auth_key: {storage['gw_bi']['auth_key']}")
|
|
write_file("auth_key", storage['gw_bi']['auth_key'])
|
|
print(f"[+] ap_ssid: {storage['gw_bi']['ap_ssid']}")
|
|
write_file("ap_ssid", storage['gw_bi']['ap_ssid'])
|
|
if 'fac_pin' in storage['gw_bi'] and storage['gw_bi']['fac_pin'] is not None:
|
|
factory_pin = storage['gw_bi']['fac_pin']
|
|
print(f"[+] factory pin: {factory_pin}")
|
|
write_file("factory_pin", factory_pin)
|
|
# Not all firmwares have version information in storage
|
|
if 'gw_di' in storage:
|
|
if 'swv' in storage['gw_di'] and not process_inactive_app:
|
|
print(f"[+] storage swv: {storage['gw_di']['swv']}")
|
|
write_file("swv", storage['gw_di']['swv'])
|
|
if 'dev_swv' in storage['gw_di'] and not process_inactive_app:
|
|
print(f"[+] storage dev_swv: {storage['gw_di']['dev_swv']}")
|
|
write_file("mcuswv", storage['gw_di']['dev_swv'])
|
|
if 'bv' in storage['gw_di'] and not process_inactive_app:
|
|
print(f"[+] storage bv: {storage['gw_di']['bv']}")
|
|
write_file("bv", storage['gw_di']['bv'])
|
|
if 'firmk' in storage['gw_di'] and storage['gw_di']['firmk'] is not None:
|
|
firmware_key = storage['gw_di']['firmk']
|
|
print(f"[+] firmware key: {firmware_key}")
|
|
write_file("firmware_key", firmware_key)
|
|
if 'pk' in storage['gw_di'] and storage['gw_di']['pk'] is not None:
|
|
product_key = storage['gw_di']['pk']
|
|
print(f"[+] product key: {product_key}")
|
|
write_file("product_key", product_key)
|
|
if 's_id' in storage['gw_di'] and storage['gw_di']['s_id'] is not None:
|
|
schema_id = storage['gw_di']['s_id']
|
|
if schema_id in storage:
|
|
if write_file("schema_id", schema_id):
|
|
print(f"[+] schema: {storage[schema_id]}")
|
|
if write_file("schema", json.dumps(storage[schema_id])):
|
|
print(f"[+] schema {schema_id}:")
|
|
if 'baud_cfg' in storage and 'baud' in storage['baud_cfg']:
|
|
print(f"[+] TuyaMCU baud: {storage['baud_cfg']['baud']}")
|
|
write_file("tuyamcu_baud", f"{storage['baud_cfg']['baud']}")
|
|
elif 'uart_adapt_params' in storage and 'uart_baud' in storage['uart_adapt_params']:
|
|
print(f"[+] TuyaMCU baud: {storage['uart_adapt_params']['uart_baud']}")
|
|
write_file("tuyamcu_baud", f"{storage['uart_adapt_params']['uart_baud']}")
|
|
if not firmware_key and not product_key and not factory_pin:
|
|
print("[!] No gw_di, No version or key stored, manual lookup required")
|
|
write_file("manually_process", "No version or key stored, manual lookup required")
|
|
|
|
|
|
def run(storage_file: str, process_inactive_app: bool = False):
|
|
if not storage_file:
|
|
print('Usage: python parse_storage.py <storage.json file>')
|
|
sys.exit(1)
|
|
|
|
if os.path.exists(storage_file):
|
|
dump(storage_file, process_inactive_app)
|
|
else:
|
|
print('[!] Storage file not found')
|
|
return
|
|
|
|
|
|
if __name__ == '__main__':
|
|
run(sys.argv[1], sys.argv[2])
|