diff --git a/app.py b/app.py index 70a8bfa..e733325 100644 --- a/app.py +++ b/app.py @@ -7,9 +7,11 @@ import random import sqlite3 import time import html +import zipfile from argparse import ArgumentParser from flask import Flask, request, render_template, send_file from datetime import datetime +from io import BytesIO # stuff with open(f"{os.getcwd()}/config.json", "r") as f: @@ -17,6 +19,7 @@ with open(f"{os.getcwd()}/config.json", "r") as f: parser = ArgumentParser(prog="syscheck_receiver", description="Server that saves SysCheck report uploads.") parser.add_argument("-d", "--debug", dest="debug", action='store_true', help="Runs with Flask debug server instead of Waitress. DO NOT USE IN PRODUCTION!!!!") +parser.add_argument("-r", "--replace-url", dest="replace_url", help="Allows to replace the default sysCheck url with your own (mostly meant for the pre-provided docker images).") args = parser.parse_args() def id_generator(size=6, chars=string.ascii_uppercase + string.digits): @@ -36,11 +39,6 @@ def return_error(message: string, code: int): resp.headers["Content-Type"] = "application/json" return resp -def no_cf_chunking(txt): - resp = flask.Response(txt) - resp.headers["X-Content-Length"] = len(txt.encode("utf-8")) # Cloudflares Proxy strips "Content-Length" when chunking, so we just re-add it manually. - return resp - # docker if config["docker"]: report_dir = "/data/reports" @@ -49,6 +47,11 @@ else: report_dir = f"{os.getcwd()}/reports" db_dir = f"{os.getcwd()}/reports.db" +if args.replace_url is None: + replace_url = config["replace_str"] +else: + replace_url = args.replace_url + # sever code app = Flask('syscheck_receiver') @@ -69,28 +72,31 @@ def index(): return render_template("index.html", uploadIndex=uploadIndex, report_count=report_count[0][0], svr_ver=config["version"]), 200 -@app.route("/syscheck_receiver.php", methods=["POST"]) +@app.route("/syscheck_up.php", methods=["POST"]) # SysCheckME-dev +@app.route("/syscheck_receiver.php", methods=["POST"]) # literally anything else (DNS?) def syscheck_report(): form_data = request.form.to_dict(flat=False) report_txt = form_data["syscheck"][0] - console_id = get_console_id(report_txt)[:-4] - - # check if console id: is present + console_id = get_console_id(report_txt) if console_id == "0": - return no_cf_chunking("ERROR: Not a valid sysCheck!"), 200 - # check if syscheck isn't too small or too large - if len(report_txt.encode("utf-8")) > 6144: - return no_cf_chunking("ERROR: Report is too large! Max is 6KB."), 200 - elif len(report_txt.encode("utf-8")) < 1330: - return no_cf_chunking("ERROR: Report is too small! Min is 1.3KB."), 200 - + return "ERROR: Not a valid sysCheck!", 200 + # figure difference between syscheck2 and syscheckhde and newer + if report_txt.split("\n")[0].startswith("sysCheck v2.1.0b19"): + console_id_censor = console_id[:-4]+"****" + old_syscheck = True + else: + old_syscheck = False + console_id = console_id[:-4] timestamp = int(time.time()) report_id = id_generator(6, 'AaBbCcDdFfeEgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWXxYyZz1234567890') - if form_data["password"][0] == config["upload_password"]: + if form_data["password"][0] in config["upload_passwords"]: try: with open(f"{report_dir}/{report_id}.csv", "a+") as report: - report.write(report_txt) + if old_syscheck: + report.write(report_txt.replace(console_id, console_id_censor)) + else: + report.write(report_txt) db = sqlite3.connect(db_dir) cursor = db.cursor() @@ -98,26 +104,50 @@ def syscheck_report(): db.commit() db.close() - return no_cf_chunking(f"Success! Report ID: {report_id}"), 200 + return f"Success! Report ID: {report_id}", 200 except Exception as ex: print(ex) - return no_cf_chunking("ERROR: Failed to save SysCheck report!"), 200 + return "ERROR: Failed to save SysCheck report!", 200 + else: - return no_cf_chunking("ERROR: Unauthorized"), 200 + return "ERROR: Unauthorized", 200 -@app.route("/download_csv", methods=["GET"], defaults={'_route': 'direct'}) -@app.route("/view_report", methods=["GET"], defaults={'_route': 'template'}) -def view_report(_route): +@app.route("/view_report", methods=["GET"]) +def view_report(): report_id = request.args.get("id") if os.path.isfile(f"{report_dir}/{report_id}.csv"): - if _route == "template": - with open(f"{report_dir}/{report_id}.csv", "r") as report: - return render_template("view_report.html", report_id=report_id, report_content=html.escape(report.read()), svr_ver=config["version"]), 200 - else: - return send_file(f"{report_dir}/{report_id}.csv", as_attachment=True, download_name="report.csv") + with open(f"{report_dir}/{report_id}.csv", "r") as report: + return render_template("view_report.html", report_id=report_id, report_content=html.escape(report.read()), svr_ver=config["version"]), 200 else: return "Report does not exist.", 404 +@app.route("/syscheck_dl", methods=["GET"]) +def syscheck(): + if len("http://syscheck.rc24.xyz/syscheck_receiver.php") < len(replace_url): + return "Replacement host has to be exactly 46 characters; Specified URL is too long!", 400 + elif len("http://syscheck.rc24.xyz/syscheck_receiver.php") > len(replace_url): + return "Replacement host has to be exactly 46 characters; Specified URL is too short!", 400 + + dol = BytesIO() + zip = BytesIO() + + # hex edit boot.dol + dol2 = open(f"{os.getcwd()}/static/syscheck/boot.dol", "rb") + dol.write(dol2.read().replace("http://syscheck.rc24.xyz/syscheck_receiver.php".encode("utf-8"), replace_url.encode("utf-8"))) + dol.seek(0) + dol2.close() + + zf = zipfile.ZipFile(zip, "w", zipfile.ZIP_DEFLATED, False) + zf.writestr("apps/SysCheckME/boot.dol", dol.read()) + dol.close() + zf.write(f"{os.getcwd()}/static/syscheck/icon.png", "apps/SysCheckME/icon.png") + zf.write(f"{os.getcwd()}/static/syscheck/meta.xml", "apps/SysCheckME/meta.xml") + zf.close() + zip.seek(0) + + # send zipfile + return send_file(zip, mimetype="application/zip", as_attachment=True, download_name="SysCheckME.zip"), 200 + # handle errors @app.errorhandler(400) @app.errorhandler(404) diff --git a/config.json b/config.json index b7695db..8b5728b 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,11 @@ { "ip": "0.0.0.0", "port": "6969", - "version": "1.1.2", - "upload_password": "B277eNGp789a", + "version": "1.0.10", + "upload_passwords": [ + "d2wRuTEObSAN", + "B277eNGp789a" + ], + "replace_str": "http://syscheck.crafterpika.cc/syscheck_up.php", "docker": false } diff --git a/static/syscheck/boot.dol b/static/syscheck/boot.dol new file mode 100644 index 0000000..710ca3f Binary files /dev/null and b/static/syscheck/boot.dol differ diff --git a/static/syscheck/icon.png b/static/syscheck/icon.png new file mode 100644 index 0000000..5f682e7 Binary files /dev/null and b/static/syscheck/icon.png differ diff --git a/static/syscheck/meta.xml b/static/syscheck/meta.xml new file mode 100644 index 0000000..d60ccac --- /dev/null +++ b/static/syscheck/meta.xml @@ -0,0 +1,29 @@ + + + SysCheck ModMii Edition + blackb0x, JoostinOnline, Double_A, R2-D2199, Nano + 2.5.0 + 20230309000000 + System Checker ModMii Edition + Homebrew application which does several checks on installed IOS and custom IOS: + - Base IOS Detection + - vIOS Detection + - Beer Ticket + - IOS Stub + - Fake Signature (aka Trucha Bug) + - ES_DiVerify (aka ES_Identify) + - Flash Access + - NAND Access + - Boot2 Access + - USB 2.0 + +SysCheck generates a report on the root of your storage device (e.g. usb:/SysCheck.csv). + +The author can not be held responsible for any damage SysCheck might cause! + + --debug=false + --forceNoAHBPROT=false + --skipIOS=0 + + + diff --git a/templates/index.html b/templates/index.html index 046e2a2..30ffefc 100644 --- a/templates/index.html +++ b/templates/index.html @@ -9,8 +9,7 @@
- Download SysCheckME - How to use + Download SysCheckME

Latest Uploads: