New generation style!

* More dynamic: Includes version in the index.html
* Still no external dependencies.
* Added lockpick and TegraExplorer
This commit is contained in:
noirscape 2021-05-12 23:42:11 +02:00
parent 6eabfb88b7
commit bb4a6362dd
8 changed files with 253 additions and 159 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
hekate.bin.js
lockpick.bin.js
tegraexplorer.bin.js
index.html

15
Makefile Normal file
View File

@ -0,0 +1,15 @@
generate_files:
@python gen_contents.py
clean:
@rm hekate.bin.js
@rm lockpick.bin.js
@rm tegraexplorer.bin.js
@rm index.html
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := help

View File

@ -1,85 +0,0 @@
import requests
import io
import shutil
import zipfile
API_URL = "https://api.github.com/repos/CTCaer/hekate/releases/latest"
BIN_NAME = "hekate_ctcaer_"
SERIALIZED_FILENAME = "hekate.bin.js"
### BASIC FILE STUFF, DO NOT EDIT
DEFAULT_SERIALIZED_CONTENTS = """
const hekate = new Uint8Array([
{}
]);
"""
## Script that downloads the latest bin from hekate and serializes it as a .bin.js file.
def download_file_to_bytes_io(url) -> io.BytesIO:
r = requests.get(url)
filedata = io.BytesIO()
if r.status_code == 200:
filedata.write(r.content)
filedata.seek(0)
return filedata
def extract_specific_file_from_zip(zip_file) -> io.BytesIO:
filename = None
filedata = io.BytesIO()
with zipfile.ZipFile(zip_file) as z:
for zinfo in z.infolist():
if zinfo.filename.startswith(BIN_NAME):
filename = zinfo.filename
break
if filename is None:
raise Exception("Zipfile does not contain required match.")
with z.open(filename) as zfile:
shutil.copyfileobj(zfile, filedata)
filedata.seek(0)
return filedata
def fetch_hekate_zip() -> io.BytesIO:
r = requests.get(API_URL)
jdata = r.json()
return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith("hekate_ctcaer")][0]["browser_download_url"])
def get_hekate_payload(hekate_zip: io.BytesIO) -> io.BytesIO:
return extract_specific_file_from_zip(hekate_zip)
def serialize_to_js(hekate_payload: io.BytesIO, filename: str):
hekate_payload = hekate_payload.read()
# List so we can loop byte for byte
serialized = [bytes([b]) for b in hekate_payload]
final_str = []
# Formatting
max_ctr = 16
str_on_line = 0
# Special check to ensure that a comma isn't placed on the end
last_byte = len(serialized) - 1
for idx, byte in enumerate(serialized):
if idx != last_byte:
final_str.append(f"0x{byte.hex()}, ")
else:
final_str.append(f"0x{byte.hex()}")
# More readable
str_on_line += 1
if str_on_line >= max_ctr:
str_on_line = 0
final_str.append("\n ")
final_str = "".join(final_str)
out_string = DEFAULT_SERIALIZED_CONTENTS.format(final_str)
with open(filename, "w") as outfile:
outfile.write(out_string)
if __name__ == "__main__":
api_response = fetch_hekate_zip()
payload = get_hekate_payload(api_response)
serialize_to_js(payload, SERIALIZED_FILENAME)

148
gen_contents.py Normal file
View File

@ -0,0 +1,148 @@
import requests
import io
import shutil
import zipfile
bin_files = {
"hekate": {
"API_URL": "https://api.github.com/repos/CTCaer/hekate/releases/latest", # API version url
"BIN_NAME": "hekate_ctcaer", # BIN_NAME -> used to determine what file to extract from zip or what to download from API
"SERIALIZED_FILENAME": "hekate.bin.js", # File to store serialized contents in.
"SHORT_NAME": "hekate",
"zipped": True # Do we need to extract a zip or is it just a raw payload.
},
"tegraexplorer": {
"API_URL": "https://api.github.com/repos/suchmememanyskill/TegraExplorer/releases/latest",
"BIN_NAME": "TegraExplorer",
"SERIALIZED_FILENAME": "tegraexplorer.bin.js",
"SHORT_NAME": "tegraexplorer",
"zipped": False,
},
"lockpick": {
"API_URL": "https://api.github.com/repos/shchmue/Lockpick_RCM/releases/latest",
"BIN_NAME": "Lockpick_RCM",
"SERIALIZED_FILENAME": "lockpick.bin.js",
"SHORT_NAME": "lockpick",
"zipped": False,
},
}
### BASIC FILE STUFF, DO NOT EDIT
DEFAULT_SERIALIZED_CONTENTS = """
const {} = new Uint8Array([
{}
]);
"""
## Script that downloads the latest bin from hekate and serializes it as a .bin.js file.
def download_file_to_bytes_io(url) -> io.BytesIO:
r = requests.get(url)
filedata = io.BytesIO()
if r.status_code == 200:
filedata.write(r.content)
filedata.seek(0)
return filedata
def extract_specific_file_from_zip(zip_file, bin_name) -> io.BytesIO:
filename = None
filedata = io.BytesIO()
with zipfile.ZipFile(zip_file) as z:
for zinfo in z.infolist():
if zinfo.filename.startswith(bin_name):
filename = zinfo.filename
break
if filename is None:
raise Exception("Zipfile does not contain required match.")
with z.open(filename) as zfile:
shutil.copyfileobj(zfile, filedata)
filedata.seek(0)
return filedata
def fetch_hekate_zip(api_url) -> io.BytesIO:
r = requests.get(api_url)
jdata = r.json()
return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith("hekate_ctcaer")][0]["browser_download_url"])
def fetch_github_repo_file(api_url, filename) -> io.BytesIO:
r = requests.get(api_url)
jdata = r.json()
return download_file_to_bytes_io([d for d in jdata["assets"] if d["name"].startswith(filename)][0]["browser_download_url"])
def fetch_github_latest_tag(api_url) -> io.BytesIO:
r = requests.get(api_url)
jdata = r.json()
return jdata["tag_name"]
def serialize_to_js(short_name: str, payload: io.BytesIO, filename: str):
payload = payload.read()
# List so we can loop byte for byte
serialized = [bytes([b]) for b in payload]
final_str = []
# Formatting
max_ctr = 16
str_on_line = 0
# Special check to ensure that a comma isn't placed on the end
last_byte = len(serialized) - 1
for idx, byte in enumerate(serialized):
if idx != last_byte:
final_str.append(f"0x{byte.hex()}, ")
else:
final_str.append(f"0x{byte.hex()}")
# More readable
str_on_line += 1
if str_on_line >= max_ctr:
str_on_line = 0
final_str.append("\n ")
final_str = "".join(final_str)
out_string = DEFAULT_SERIALIZED_CONTENTS.format(short_name, final_str)
with open(filename, "w") as outfile:
outfile.write(out_string)
def generate_js(program: str):
api_response = fetch_github_repo_file(bin_files[program]["API_URL"], bin_files[program]["BIN_NAME"])
if bin_files[program]["zipped"]:
payload = extract_specific_file_from_zip(api_response, bin_files[program]["BIN_NAME"])
else:
payload = api_response
serialize_to_js(bin_files[program]["SHORT_NAME"], payload, bin_files[program]["SERIALIZED_FILENAME"])
def generate_html():
with open("html/preface.html", "r") as preface_file:
preface = preface_file.read()
with open("html/core.html", 'r') as template_file:
core = template_file.read()
with open("html/post.html", "r") as post_file:
post = post_file.read()
html_data_list = []
html_data_list.append(preface)
html_data_list.append(core.format(
fetch_github_latest_tag(bin_files["hekate"]["API_URL"]),
fetch_github_latest_tag(bin_files["tegraexplorer"]["API_URL"]),
fetch_github_latest_tag(bin_files["lockpick"]["API_URL"]),
))
html_data_list.append(post)
out_string = "".join(html_data_list)
with open("index.html", "w") as indexfile:
indexfile.write(out_string)
def main():
generate_js("hekate")
generate_js("tegraexplorer")
generate_js("lockpick")
generate_html()
if __name__ == "__main__":
main()

28
html/core.html Normal file
View File

@ -0,0 +1,28 @@
<h3>Payload:</h3>
<div>
<form id="mainForm">
<p>
<input type="radio" name="payload" id="fusee.bin" value="fusee.bin" checked>
<label for="fusee.bin">Example payload (fusee.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="hekate.bin" value="hekate.bin" checked>
<label for="fusee.bin">Hekate {} - (hekate_ctcaer.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="tegraexplorer.bin" value="tegraexplorer.bin" checked>
<label for="fusee.bin">TegraExplorer v{} - (tegraexplorer.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="lockpick.bin" value="lockpick.bin" checked>
<label for="fusee.bin">Lockpick_RCM {} - (lockpick.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="uploaded" value="uploaded">
<label for="uploaded">Upload payload:</label>
<input type="file" id="payloadUpload">
</p>
</form>
<button id="goButton">Do the thing!</button>
</div>

12
html/post.html Normal file
View File

@ -0,0 +1,12 @@
<h3>Result:</h3>
<textarea cols="80" rows="12" id="output"></textarea>
<script src="fusee.bin.js"></script>
<script src="hekate.bin.js"></script>
<script src="lockpick.bin.js"></script>
<script src="tegraexplorer.bin.js"></script>
<script src="main.js"></script>
</body>
</html>

View File

@ -1,74 +1,43 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebFG</title>
</head>
<body>
<h1>Web Fusée Launcher</h1>
<p>Fusee Launcher ported to JavaScript using WebUSB.</p>
<p>
Source can be found on <a href="https://github.com/atlas44/web-fusee-launcher">GitHub</a> (or by hitting view source, there is no backend!).
Ported from <a href="https://github.com/reswitched/fusee-launcher">fusee-launcher</a>.
Thanks to ktemkin and ReSwitched for Fusée Gelée and a ton of other things!
</p>
<p>
<h4>Instructions:</h4>
<ol>
<li>Put the Switch in RCM, and connect it to your device.</li>
<li>Select either the example payload, or upload one.</li>
<li>Press 'Do the thing!'</li>
<li>On the consent screen that appears, select 'APX' and hit confirm.</li>
<li>If all goes well, the payload will launch!</li>
</ol>
</p>
<p>
<h4>Random stuff:</h4>
<ul>
<li>This is pretty poorly tested. I just kind wrote it and whatever. I'm not responsible if anything goes wrong!</li>
<li>This does NOT work on Windows due to a limitation in the Chrome implementation of WebUSB (and probably other reasons!)</li>
<li>This does NOT currently work on any browser but Chrome, because they don't implement WebUSB.</li>
<li>On Linux, you might get an access denied error!
If you do, you can try creating a file at <code>/etc/udev/rules.d/50-switch.rules</code>
<div>With the following contents:</div>
<div><code>SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="plugdev"</code></div>
</li>
<li>This has been tested and appears to work on Linux, OSX, Android (unrooted) and Chromebooks. Your mileage may vary.</li>
</ul>
</p>
<h3>Payload:</h3>
<div>
<form id="mainForm">
<p>
<input type="radio" name="payload" id="fusee.bin" value="fusee.bin" checked>
<label for="fusee.bin">Example payload (fusee.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="hekate.bin" value="hekate.bin" checked>
<label for="fusee.bin">Latest hekate (hekate_ctcaer.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="uploaded" value="uploaded">
<label for="uploaded">Upload payload:</label>
<input type="file" id="payloadUpload">
</p>
</form>
<button id="goButton">Do the thing!</button>
</div>
<h3>Result:</h3>
<textarea cols="80" rows="12" id="output"></textarea>
<script src="fusee.bin.js"></script>
<script src="hekate.bin.js"></script>
<script src="main.js"></script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebFG</title>
</head>
<body>
<h1>Web Fusée Launcher</h1>
<p>Fusee Launcher ported to JavaScript using WebUSB.</p>
<p>
Source can be found on <a href="https://github.com/atlas44/web-fusee-launcher">GitHub</a> (or by hitting view source, there is no backend!).
Ported from <a href="https://github.com/reswitched/fusee-launcher">fusee-launcher</a>.
Thanks to ktemkin and ReSwitched for Fusée Gelée and a ton of other things!
</p>
<p>
<h4>Instructions:</h4>
<ol>
<li>Put the Switch in RCM, and connect it to your device.</li>
<li>Select either the example payload, or upload one.</li>
<li>Press 'Do the thing!'</li>
<li>On the consent screen that appears, select 'APX' and hit confirm.</li>
<li>If all goes well, the payload will launch!</li>
</ol>
</p>
<p>
<h4>Random stuff:</h4>
<ul>
<li>This is pretty poorly tested. I just kind wrote it and whatever. I'm not responsible if anything goes wrong!</li>
<li>This does NOT work on Windows due to a limitation in the Chrome implementation of WebUSB (and probably other reasons!)</li>
<li>This does NOT currently work on any browser but Chrome, because they don't implement WebUSB.</li>
<li>On Linux, you might get an access denied error!
If you do, you can try creating a file at <code>/etc/udev/rules.d/50-switch.rules</code>
<div>With the following contents:</div>
<div><code>SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="plugdev"</code></div>
</li>
<li>This has been tested and appears to work on Linux, OSX, Android (unrooted) and Chromebooks. Your mileage may vary.</li>
</ul>
</p>

View File

@ -117,6 +117,10 @@ document.getElementById("goButton").addEventListener("click", async () => {
payload = fusee;
} else if (payloadType === "hekate.bin") {
payload = hekate;
} else if (payloadType === "tegraexplorer.bin") {
payload = tegraexplorer;
} else if (payloadType === "lockpick.bin") {
payload = lockpick;
} else if (payloadType === "uploaded") {
const file = document.getElementById("payloadUpload").files[0];
if (!file) {