Last active
August 21, 2023 08:28
-
-
Save rvtr/3793a40dd0c7930f12b79ca1931a3296 to your computer and use it in GitHub Desktop.
Extract firmware from gigaleak 3DS SystemUpdaters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# extract-updater-rofs | |
# Extracts firmware CIAs from ROFS containers for some gigaleak SystemUpdaters. Will accept "Contents.cnt" and decrypted "romfs.bin" | |
# Usage: ./extract-updater-romfs <input file> (output directory) | |
################################################################################### | |
# Set input and output paths | |
################################################################################### | |
OUTPUT_DIR="extract-updater-romfs_output" | |
rm -r "extract-updater-romfs_output" | |
mkdir "extract-updater-romfs_output" | |
ERROR="0" | |
if [ "${1}" != "" ]; then | |
INPUT_FILE="${1}" | |
if [ "${2}" != "" ]; then | |
OUTPUT_DIR="${2}" | |
rm -r "${OUTPUT_DIR}" | |
rm -r "extract-updater-romfs_output" | |
mkdir "${OUTPUT_DIR}" | |
fi | |
else | |
echo "ERROR: No input files specified." | |
echo "" | |
echo "./extract-updater-rofs.sh <input> (output dir)" | |
echo "" | |
echo "Note that if the output directory already exists, IT WILL BE DELETED" | |
ERROR=1 | |
fi | |
################################################################################### | |
# CIA extraction | |
################################################################################### | |
if [ "$ERROR" == "0" ]; then | |
echo "Finding CIA headers in file..." | |
od -t x -A d "${INPUT_FILE}" | grep "00002020 00000000 00000a00 00000350" | sed 's/ .*//' | sed 's/^0*//' > romfs-dir.txt | |
# Get start address of every CIA header and store to file | |
echo "Found all headers!" | |
echo "=================================================" | |
declare -i x=0 | |
declare -i i=1 | |
declare -i z=1 | |
echo "Extracting odd CIAs..." | |
echo "=================================================" | |
sed 1d romfs-dir.txt | while IFS=, read -r START_HEADER; read NEXT_HEADER | |
do | |
echo " Check CIA length" | |
echo "CIA $i header at ${START_HEADER}" | |
echo "Next header at ${NEXT_HEADER}" | |
echo "Finding CIA $i end from CIA $((i + 1)) header... " | |
y="00" | |
x=0 | |
while [ "$y" = "00" ]; do | |
x+=1 | |
y=$(od -j $((NEXT_HEADER - x)) -N 1 -x -A n "${INPUT_FILE}" | sed 's|[ ,]||g' | sed 's/^..//'); | |
# Get bytes one backwards from next header | |
# printf '%x\n' $((NEXT_HEADER - x)) | |
# echo $y | |
# echo $x | |
done | |
echo "End found!" | |
echo "Non-zerobyte ($y) at $((NEXT_HEADER - x))" | |
echo "Padding from CIA $i to $((i + 1)) is $((x - 1)) bytes." | |
echo " Extract CIA" | |
CIA_LENGTH=$(((NEXT_HEADER - START_HEADER) - x + 1)) | |
dd skip=${START_HEADER} count=${CIA_LENGTH} if="${INPUT_FILE}" of="${OUTPUT_DIR}/$i.cia" bs=1 | |
echo "CIA $i saved as '$i.cia'" | |
echo "=================================================" | |
i+=2 | |
done < romfs-dir.txt | |
i=2 | |
echo "Extracting even CIAs..." | |
echo "=================================================" | |
sed 1d romfs-dir.txt | while IFS=, read -r START_HEADER; read NEXT_HEADER | |
do | |
echo " Check CIA length" | |
echo "CIA $i header at ${START_HEADER}" | |
echo "Next header at ${NEXT_HEADER}" | |
echo "Finding CIA $i end from CIA $((i + 1)) header... " | |
y="00" | |
x=0 | |
while [ "$y" = "00" ]; do | |
x+=1 | |
y=$(od -j $((NEXT_HEADER - x)) -N 1 -x -A n "${INPUT_FILE}" | sed 's|[ ,]||g' | sed 's/^..//'); | |
# Get bytes one backwards from next header | |
# printf '%x\n' $((NEXT_HEADER - x)) | |
# echo $y | |
# echo $x | |
done | |
echo "End found!" | |
echo "Non-zerobyte ($y) at $((NEXT_HEADER - x))" | |
echo "Padding from CIA $i to $((i + 1)) is $((x - 1)) bytes." | |
echo " Extract CIA" | |
CIA_LENGTH=$(((NEXT_HEADER - START_HEADER) - x + 1)) | |
dd skip=${START_HEADER} count=${CIA_LENGTH} if="${INPUT_FILE}" of="${OUTPUT_DIR}/$i.cia" bs=1 | |
echo "CIA $i output as '$i.cia', ${CIA_LENGTH} bytes." | |
CIA_LENGTH="" | |
echo "=================================================" | |
i+=2 | |
done | |
START_HEADER=$( tail -n 1 romfs-dir.txt ) | |
NEXT_HEADER=$((16#$(xxd "${INPUT_FILE}" | grep "226e 6f6e 6522" | sed 's/: .*//' | sed 's/^0*//'))); | |
# Find "226e 6f6e 6522" as it is the last predictable data to mark the end of Contents.cnt and the last CIA. | |
# Upsettingly I have to use xxd because od wouldn't turn up any results for this... I liked od's formatting more :despair: | |
if [ $NEXT_HEADER != "" ]; then | |
echo " Check CIA length" | |
echo "Final CIA header at ${START_HEADER}" | |
echo "Contents.cnt end at ${NEXT_HEADER}" | |
echo "Finding end of final CIA from Contents.cnt end..." | |
y="00" | |
x=16 | |
# Start x as 16 to skip changing data and go right to padding | |
while [ "$y" = "00" ]; do | |
x+=1 | |
y=$(od -j $((NEXT_HEADER - x)) -N 1 -x -A n "${INPUT_FILE}" | sed 's|[ ,]||g' | sed 's/^..//'); | |
# Get bytes one backwards from next header | |
# printf '%x\n' $((NEXT_HEADER - x)) | |
# echo $y | |
# echo $x | |
done | |
echo "End found!" | |
echo "Non-zerobyte ($y) at $((NEXT_HEADER - x))" | |
echo "Padding from final CIA to Content.cnt end is $((x - 1)) bytes." | |
echo " Extract CIA" | |
CIA_LENGTH=$(((NEXT_HEADER - START_HEADER) - x + 1)) | |
dd skip=${START_HEADER} count=${CIA_LENGTH} if="${INPUT_FILE}" of="${OUTPUT_DIR}/0.cia" bs=1 | |
echo "Final CIA output as '0.cia', ${CIA_LENGTH} bytes." | |
echo "=================================================" | |
echo "All CIAs extracted from RomFS!" | |
else | |
echo "Could not find end of last CIA! For extracting manually, start address is ${START_HEADER} (decimal)" | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
L34:
There's a leftover
Contents.cnt
in the gigaleaks at:ctr.7z/ctr/sources/firmware/CTR-Kernel/updater1st/UpdaterCardImage/Default/Contents_11_without_shareddate.cnt
This has CIAs with a different header, so you'll need to change grep to find the following:
"00002020 00000000 00000000 00000378"