makenand.sh/makenand.sh
2026-03-31 02:22:07 -04:00

271 lines
11 KiB
Bash
Executable File

# makenand.sh
# https://problemkaputt.de/gbatek.htm#dsisdmmcinternalnandlayout
# Overall eMMC Layout
#
# Offset Size Description
# 00000000h 200h PC-style MBR, encrypted with a per-console key
# 00000200h 200h Stage 2 Boot Info Block 1 (used)
# 00000400h 200h Stage 2 Boot Info Block 2 (unused, same as above)
# 00000600h 200h Stage 2 Boot Info Block 3 (unused, nonsense NAND offsets)
# 00000800h 26600h Stage 2 ARM9 Bootcode (encrypted with universal key)
# 00026E00h 27600h Stage 2 ARM7 Bootcode (encrypted with universal key)
# 0004E400h 400h Stage 2 Footer -- unknown format, but first 10 bytes
# are (unencrypted) build number of Stage 2 bootloader
# 0004E800h B1000h Unused (all 00h)
# 000FF800h 200h Unused (all 00h) (or No$gba Footer with CID & Console ID)
# 000FFA00h 400h Diagnostic area. (often contains build date of
# device in plaintext) Blank in never-before-booted
# DSi. Might be written to during firmware updates.
# 000FFE00h 200h Unused (all FFh)
# 00100000h EE00h Unused (all 00h)
# 0010EE00h CDF1200h 1st partition (205.9Mbyte) (main, encrypted, FAT16)
# 0CF00000h 9A00h Unused (all 00h)
# 0CF09A00h 20B6600h 2nd partition (32.7Mbyte) (photo, encrypted, FAT12)
# For 240.0MB chips (Samsung KMAPF0000M-S998 or KLM5617EFW-B301):
# 0EFC0000h BA00h Unused (all 00h)
# 0EFCBA00h 34600h 3rd partition (0.2Mbyte) (extra, unformatted)
# 0F000000h - End of 240MByte Address Space
# For 245.5MB chips (ST NAND02GAH0LZC5, both rev30 and rev31):
# 0EFC0000h B600h Unused (all 00h?) (smaller unused area as in 240MB chip)
# 0EFCB600h 5B4A00h 3rd partition (5.7Mbyte) (extra, unformatted)
# 0F580000h - End of 245.5MByte Address Space
#
# Making a new NAND is easy:
#
# - create blank TWL_MAIN
# - copy VBR
# - populate it with system files and firmware
# - create blank TWL_PHOTO
# - copy VBR
# - add the management file
# - create blank nand.bin
# - copy stage2
# - copy MBR
# - copy in TWL_MAIN and TWL_PHOTO
# - encrypt the NAND
#
# HIIIIII VIKRINOX ENJOY MY SLOPPY SCRIPT! IT'S TERRIBLE! I'M SORRY!
# mcopy doesn't make directories, this is my shortcut
makemdirs() {
local foldersonly=$(dirname $1)
local current=""
IFS='/' read -ra PARTS <<< "$foldersonly"
for p in "${PARTS[@]}"; do
#echo "$p"
current="$current/$p"
if ! mdir "$2/$current" >/dev/null 2>&1; then
mmd "$2/$current"
fi
done
}
rm -f twl_main.img
rm -f twl_photo.img
rm -f nand.bin
rm -r mount
CONSOLE_SIGNING="$1"
mkdir mount
# Start by making a blank partition!
# We'll work on each partition as is, then copy them into a completed NAND.
#
# The "seek" value is the full size of the partition.
echo ">>> Working on TWL_MAIN"
dd if=/dev/zero of=twl_main.img bs=1 count=0 seek=$((0xCDF1200)) status=progress
# Create TWL_MAIN VBR
# I just copied this from a random NAND. The VBR seems to be standard, and it seems good to keep things standard. I don't want to add the risk of errors from changing things.
dd if="./donor/vbr/TWL_MAIN.bin" of=twl_main.img bs=1 seek=0 count=$(stat -c%s "./donor/vbr/TWL_MAIN.bin") conv=notrunc status=progress
rm -f temp.bin
# Just "mounting" the drive for writing. Not really mounting but whatever.
cat > mtoolsrc <<EOF
drive d: file="./twl_main.img"
EOF
export MTOOLSRC=./mtoolsrc
# First thing to do inside of TWL_MAIN is checking HWInfo (if provided). This will give us a firmware region to target.
# The status variable will contain the region byte of the launcher TID.
#
# Probably not a bad idea to confirm that the TID is for an known region.
nogba_consoleid=$(echo $3 | tac -rs .. | echo "$(tr -d '\n')")
python hwinfo.py --consoleid $nogba_consoleid --hwinfo $2 verify
if [[ "$2" != "" ]]; then
if [[ "$CONSOLE_SIGNING" == "dev" ]]; then
HWINFO_STATUS=$(python hwinfo.py --consoleid $nogba_consoleid --hwinfo $2 --dev verify)
else
HWINFO_STATUS=$(python hwinfo.py --consoleid $nogba_consoleid --hwinfo $2 verify)
fi
if [[ "$HWINFO_STATUS" == "Signature is valid" ]] && [[ "$2" != "" ]]; then
REGION_CODE=$(xxd -p -s 160 -l 1 "$2")
echo ">>> Region is $REGION_CODE"
else
echo ">>> HWInfo is bad, so region was not found! Setting as ALL."
echo ">>> Factory firmware will be used."
REGION_CODE="ALL"
fi
else
echo ">>> HWInfo is bad, so region was not found! Setting as ALL."
echo ">>> Factory firmware will be used."
REGION_CODE="ALL"
fi
# Make system folders
echo ">>> Making dirs"
mmd d:/sys
mmd d:/sys/log
mmd d:/shared1
mmd d:/shared2
mmd d:/shared2/launcher
mmd d:/ticket
mmd d:/title
mmd d:/tmp
# Copy the font data
if [[ "$REGION_CODE" == "43" ]]; then
echo ">>> Copying CN FontTable"
mcopy ./donor/TWLFontTable_CN.dat "d:/sys/TWLFontTable.dat"
elif [[ "$MODEL" == "4b" ]]; then
echo ">>> Copying KR FontTable"
mcopy ./donor/TWLFontTable_KR.dat "d:/sys/TWLFontTable.dat"
else
echo ">>> Copying World FontTable"
mcopy ./donor/TWLFontTable.dat "d:/sys/TWLFontTable.dat"
fi
# Copy other important files
echo ">>> Writing cert"
mcopy ./donor/$CONSOLE_SIGNING/cert.sys "d:/sys/cert.sys"
# MelonDS WILL NOT BOOT without these configs:
echo ">>> Writing configs"
mcopy ./donor/shared1/TWLCFG1.dat "d:/shared1/TWLCFG1.dat"
mcopy ./donor/shared1/TWLCFG0.dat "d:/shared1/TWLCFG0.dat"
mcopy ./donor/wrap.bin "d:/shared2/launcher/wrap.bin"
# Not sure if unique, still copying just because.
echo ">>> Writing HWInfo"
mcopy ./donor/HWINFO_N.dat "d:/sys/HWINFO_N.dat"
# Now we'll install from a region specific title list.
# Each line contains a path like "title/000300xx/xxxxxxxx/file.ext"
while IFS= read -r FILE; do
echo ">>> Working on $FILE"
makemdirs "$FILE" "d:"
if [[ "$FILE" == *".tik" ]]; then
echo ">>> Writing ticket..."
./twltool syscrypt --consoleid $3 --in "./donor/$CONSOLE_SIGNING/software/$FILE" --out "./tmp.tik" --encrypt
mcopy -D o "./tmp.tik" "d:/$FILE"
else
echo ">>> Moving title datas..."
# If the item is a title TMD, we will intentionally break it to trigger the backup unlaunch.
# This is only needed for region specific firmware. Since ALL/HNAA has the backup unlaunch as a TMD, it will automatically trigger.
if [[ "$FILE" == *"00030017"* ]] && [[ "$FILE" == *"title.tmd" ]]; then
cp "./donor/$CONSOLE_SIGNING/software/$FILE" "./tmp.tmd"
printf "G" | dd of="./tmp.tmd" bs=1 seek=400 conv=notrunc status=none
mcopy -D o "./tmp.tmd" "d:/$FILE"
mattrib +r "d:/$FILE"
else
mcopy -D o "./donor/$CONSOLE_SIGNING/software/$FILE" "d:/$FILE"
fi
fi
done < "./donor/$CONSOLE_SIGNING/software/$REGION_CODE.txt"
# Now we'll install the general title list (yes this is redundant code, I DON"T CARE IT'S 6AM!!!!!!!!!!!!!!)
# This contains things like the wifi firmware, and full factory firmware.
while IFS= read -r FILE; do
echo ">>> Working on $FILE"
makemdirs "$FILE" "d:"
if [[ "$FILE" == *".tik" ]]; then
echo ">>> Writing ticket..."
# Encrypt ticket to console.
./twltool syscrypt --consoleid $3 --in "./donor/$CONSOLE_SIGNING/software/$FILE" --out "./tmp.tik" --encrypt
mcopy -D o "./tmp.tik" "d:/$FILE"
else
echo ">>> Moving title datas..."
mcopy -D o "./donor/$CONSOLE_SIGNING/software/$FILE" "d:/$FILE"
fi
done < "./donor/$CONSOLE_SIGNING/software/ALL.txt"
# Create backup unlaunch for safety
makemdirs "title/00030017/484e4141/content/title.tmd" "d:"
mcopy -D o "./donor/hb/HNAALaunch.tmd" "d:/title/00030017/484e4141/content/title.tmd"
mattrib +r "d:/title/00030017/484e4141/content/title.tmd"
if [[ "$REGION_CODE" == "ALL" ]]; then
echo ">>> HWInfo is bad! HNAA unlaunch will be needed."
else
echo ">>> HWInfo is good!"
mcopy "$2" "d:/sys/HWINFO_S.dat"
fi
mdir -/ d:/
# Make blank photo partition, same idea as before
echo ">>> Working on TWL_PHOTO"
dd if=/dev/zero of=twl_photo.img bs=1 count=0 seek=$((0x20B6600)) status=progress
# Create TWL_PHOTO VBR
dd if="./donor/vbr/TWL_PHOTO.bin" of=twl_photo.img bs=1 seek=0 count=$(stat -c%s "./donor/vbr/TWL_PHOTO.bin") conv=notrunc status=progress
rm -f temp.bin
cat > mtoolsrc <<EOF
drive d: file="./twl_photo.img"
EOF
export MTOOLSRC=./mtoolsrc
makemdirs "photo/private/ds/app/484E49$(xxd -p -s 160 -l 1 "$2")/pit.bin" "d:"
mcopy "./donor/pit.bin" "d:/photo/private/ds/app/484E49$(xxd -p -s 160 -l 1 "$2")/pit.bin"
mdir -/ d:/
echo ">>> Working on full NAND"
dd if=/dev/zero of=nand.bin bs=1 count=0 seek=$((0xF000040)) status=progress
echo ">>> Installing bootloader (stage2)"
dd if="./donor/$CONSOLE_SIGNING/stage2/Stage2_v2435-8325_${CONSOLE_SIGNING}.nand" of=nand.bin bs=1 count=$(stat -c%s "./donor/$CONSOLE_SIGNING/stage2/Stage2_v2435-8325_${CONSOLE_SIGNING}.nand") conv=notrunc status=progress
echo ">>> Writing NAND MBR"
NAND_BRAND=$(printf "%s" "$4" | xxd -r -p | xxd -p -s 14 -l 1)
if [[ "$NAND_BRAND" == "15" ]]; then
echo ">>> Looks like Samsung"
dd if="./donor/mbr/KMAPF0000M_MBR.bin" of=nand.bin bs=1 count=$(stat -c%s "./donor/mbr/KMAPF0000M_MBR.bin") skip=0 seek=0 conv=notrunc status=progress
elif [[ "$NAND_BRAND" == "fe" ]]; then
echo ">>> Looks like ST"
dd if="./donor/mbr/NAND02GAH0LZC5r30_MBR.bin" of=nand.bin bs=1 count=$(stat -c%s "./donor/mbr/NAND02GAH0LZC5r30_MBR.bin") skip=0 seek=0 conv=notrunc status=progress
fi
echo "$NAND_BRAND"
# Offsets for both partitions are in the "seek" parameter (just ignore the /256)
echo ">>> Copying partitions into NAND"
dd if=twl_main.img of=nand.bin bs=256 count=$(( $(stat -c%s twl_main.img) / 512 )) skip=0 seek=$((0x10EE00 / 256)) conv=notrunc status=progress
dd if=twl_photo.img of=nand.bin bs=256 count=$(( $(stat -c%s twl_main.img) / 512 )) skip=0 seek=$((0xCF09A00 / 256)) conv=notrunc status=progress
echo '>>> Writing NO$GBA footer'
echo "44536920654D4D43204349442F435055${4}${nogba_consoleid}000000000000000000000000000000000000000000000000" | xxd -r -p > temp.bin
dd if=temp.bin of=nand.bin bs=1 count=$(stat -c%s temp.bin) skip=0 seek=$((0xF000000)) conv=notrunc status=progress
echo ">>> Encrypting NAND"
./twltool nandcrypt --consoleid $3 --cid $4 --in nand.bin --out nand_final.bin
# Final notes:
#
# We need to stop negative compression.
#
# The work in progress NAND is zerofilled and decrypted. It will compress down to nothing. Great!
# However, when we encrypt it for "nand_final.bin", all those zerobytes will turn into encrypted data.
# The NAND will then compress to a crazy 239mb... not great.
#
# We'll turn those zerobytes into compressed data during processing.
# THEN we'll encrypt, turning everything back into compressable zeroes.
#
# This would look like:
# - Create zerofilled partition
# - Encrypt partition (keeping in mind the counter for AES-CTR when the partition is actually in the NAND)
# - Write VBRm and this time zerofill the file tables (encrypted data will otherwise cause errors)
# - Continue as normal