Skip to content

Instantly share code, notes, and snippets.

@StarlitSkies
Last active May 12, 2024 06:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StarlitSkies/f417c1ded9de8fceffdb532f92823377 to your computer and use it in GitHub Desktop.
Save StarlitSkies/f417c1ded9de8fceffdb532f92823377 to your computer and use it in GitHub Desktop.
3DS firmware integrity checker
# A script for checking all of a 3DS system's important files and their integrity.
# original script by FrozenFire, overhaul & fixes by StarlitSkies
# last modified: NaN
@cleanup
set FULL "0"
set NAND "0"
set SDCARD "0"
set MISC "0"
set NANDSECTORS_LOG ""
set CTRNAND_LOG ""
set TWLNAND_LOG ""
set FIRM_LOG ""
set SD_LOG ""
set RAM_LOG ""
set LOG "disabled"
@menu
labelsel -o -s "Select what system files to check.\n Console is a $[RDTYPE] $[ONTYPE] booted from $[HAX].\n Log status: $[LOG]" check_*
goto menu
@check_Full
set FULL "1"
goto NAND_Sectors
@check_NAND_Only
set NAND "1"
goto NAND_Sectors
@check_SD_Only
set SDCARD "1"
goto SD_Files
@check_Miscellaneous_Only
set MISC "1"
goto MISC_Files
@NAND_Sectors
# this checks whether the NAND header signature is the retail sighax signature, based on their hashes
# sighax will always be read as valid by boot9 even without cfw, so it's just a helpful indicator of whether custom partitions are supposed to be there
set SIGHAX "0"
if sha S:/nand.bin@0:100 A4AE99B93412E4643E4686987B6CFD59701D5C655CA2FF671CE680B4DDCF0948
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: The NAND header has a sighax signature.\n"
set SIGHAX "1"
end
# hash-based check of NAND header partition table against retail partition tables
if sha S:/nand.bin@100:60 dfd434b883874d8b585a102f3cf3ae4cef06767801db515fdf694a7e7cd98bc2
if chk $[ONTYPE] "N3DS"
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: The NAND header is stock. (N3DS)\n"
else
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: Your O3DS has an N3DS NAND header.\n"
end
elif sha S:/nand.bin@100:60 ae9b6645105f3aec22c2e3ee247715ab302874fca283343c731ca43ea1baa25d
if chk $[ONTYPE] "O3DS"
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: The NAND header is stock. (O3DS)\n"
else
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: Your N3DS has an O3DS NAND header.\n"
end
else
fget S:/nand.bin@100:4 NCSD
#check for the NCSD magic, if it's not present header is guaranteed invalid
if chk $[NCSD] "4E435344"
if chk $[SIGHAX] "1"
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Warning: NAND partition table is modified, but there is sighax in the NAND header.\n"
else
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Error: NAND partition table is modified, and there is no sighax in the NAND header.\n"
end
# this next one should only ever be possible if booted from ntrboot, but i'm including it anyway
else
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Error: NAND header is invalid.\n"
end
end
#Secret Sector, which is doubly important for N3DSes
if chk $[ONTYPE] "N3DS"
if not sha S:/sector0x96.bin 82F2730D2C2DA3F30165F987FDCCAC5CBAB24B4E5F65C981CD7BE6F438E6D9D3
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Warning: The Secret Sector is invalid. a9lh might be installed.\n"
end
else
fget S:/nand.bin@12C00:2 SBYTE
if chk -u $[SBYTE] "0000"
if chk -u $[SBYTE] "FFFF"
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Warning: There may be a9lh leftovers in the secret sector.\n"
end
end
end
#verify twl MBR is retail (there is no good reason this should ever be modified)
if not sha S:/twlmbr.bin 77a98e31f1ff7ec4ef2bfacca5a114a49a70dcf8f1dcd23e7a486973cfd06617
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: TWL MBR is invalid, meaning DS mode is unusuable.\n"
end
# get first byte in stage2 location
fget S:/nand.bin@B800000:1 ABYTE
# instead of checking the full sector against multiple stage2s, this just checks if the sector is "clean"
# (if first byte is not "clean" assume stage2 is there, can be done in a better way)
# if stage2 was replaced with trash it would trigger this warning tho
if chk -u $[ABYTE] "00"
if chk -u $[ABYTE] "FF"
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Warning: There are likely leftovers from a9lh's stage2 payload.\n"
end
end
if chk $[FULL] "1"
goto CTRNAND
elif chk $[NAND] "1"
goto CTRNAND
else
goto Results
end
@CTRNAND
# check if CTRNAND can be accessed
if isdir 1:
#find movable.sed, check if it's valid, and get its LFCS portion hash
if find 1:/private/movable.sed movable
fget $[movable]@0:4 SEED
fget $[movable]@5:1 CMAC
shaget $[movable]@8:110 LFCSM
#check if movable.sed at least has the SEED magic
if chk -u $[SEED] "53454544"
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: movable.sed is invalid.\n"
elif chk $[CMAC] "01"
if not fget $[movable]@120:1 NULL
if ask "movable.sed is misconfigured. Fix this issue?"
fset [$movable]@5 00
set CTRNAND_LOG "$[CTRNAND_LOG]Information: movable.sed is 288 bytes but had the CMAC flag. The flag has been disabled.\n"
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: movable.sed is 288 bytes but has the CMAC flag.\n"
end
end
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: movable.sed not found.\n"
end
# account for both potential filenames of LocalFriendCodeSeed
if find 1:/rw/sys/LocalFriendCodeSeed_B LFCS
elif find -s 1:/rw/sys/LocalFriendCodeSeed_A LFCS
end
if chk -u $[LFCS] ""
shaget $[LFCS]@0:110 check
#Compare the LFCS in movable.sed to the raw LFCS file
if chk -u $[LFCSM] $[check]
set CTRNAND_LOG "$[CTRNAND_LOG]Information: Your LFCS doesn't match the one inside movable.sed.\n"
end
if find M:/otp_dec.mem OTP
fget $[LFCS]@108:8 LFCSEED
fget $[OTP]@8:8 OTPSEED
if chk -u $[LFCSEED] $[OTPSEED]
set CTRNAND_LOG "$[CTRNAND_LOG]Warning: Your console is using a donor LFCS.\n"
end
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: LFCS not found.\n"
end
# find secureinfo_(a/b) and check if it matches the console region, then check for region change (presence of secureinfo_c) and do checks against that file
if find 1:/rw/sys/SecureInfo_A SEC
elif find -s 1:/rw/sys/SecureInfo_B SEC
end
if find -s 1:/rw/sys/SecureInfo_C ALTSEC
else
set ALTSEC ""
end
if chk -u $[ALTSEC] ""
set REGCHG "1"
fget $[ALTSEC]@100:1 ALTREGSEC
if chk $[ALTREGSEC] "00"
set ALTREG "JPN"
elif chk $[ALTREGSEC] "01"
set ALTREG "USA"
elif chk $[ALTREGSEC] "02"
set ALTREG "EUR"
end
else
set REGCHG "0"
end
if chk -u $[SEC] ""
fget $[SEC]@100:1 REGSEC
if chk $[REGSEC] "00"
set REG "JPN"
elif chk $[REGSEC] "01"
set REG "USA"
elif chk $[REGSEC] "02"
set REG "EUR"
end
if chk -u $[REG] $[REGION]
set CTRNAND_LOG "$[CTRNAND_LOG]Warning: Your SecureInfo doesn't match your console's region.\n"
end
if chk $[REGCHG] "1"
if chk -u $[ALTREG] $[REGION]
set CTRNAND_LOG "$[CTRNAND_LOG]Information: Your console is region changed from $[REG] to $[ALTREG].\n"
else
set CTRNAND_LOG "$[CTRNAND_LOG]Information: Your console is region changed to the same region...?\n"
end
end
elif chk $[REGCHG] "1"
set CTRNAND_LOG "$[CTRNAND_LOG]Warning: Your console's original SecureInfo is missing. Only SecureInfo_C is present.\n"
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: SecureInfo not found.\n"
end
# check whether system can boot without SD card
if find 1:/boot.firm NULL
if find 1:/rw/luma/payloads/GodMode9.firm NULL
set CTRNAND_LOG "$[CTRNAND_LOG]Information: GodMode9 and Luma3DS are in the NAND.\n"
else
set CTRNAND_LOG "$[CTRNAND_LOG]Information: Luma3DS is in the NAND, but GodMode9 isn't.\n"
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Warning: Luma3DS is not in the NAND.\n"
end
# check whether nand title database exists
if find 1:/dbs/title.db NANDTITLEDB
fget $[NANDTITLEDB]@100:4 DIFF
# check whether nand title.db has the DIFF magic
if chk -u $[DIFF] "44494646"
set CTRNAND_LOG "$[CTRNAND_LOG]Warning: CTRNAND title.db is invalid.\n"
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: CTRNAND title.db not found.\n"
end
# check whether first HWCAL exists
if find 1:/ro/sys/HWCAL0.dat HWCAL0
# check if it has the CCAL magic
fget $[HWCAL0]@0:4 CCAL0
if chk -u $[CCAL0] "4343414C"
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: HWCAL0 is invalid.\n"
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: HWCAL0 not found.\n"
end
# check whether second HWCAL exists
if find 1:/ro/sys/HWCAL1.dat HWCAL1
# check if it has the CCAL magic
fget $[HWCAL1]@0:4 CCAL1
if chk -u $[CCAL1] "4343414C"
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: HWCAL1 is invalid.\n"
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: HWCAL1 not found.\n"
end
else
set CTRNAND_LOG "$[CTRNAND_LOG]Error: CTRNAND not found.\n"
end
if chk $[FULL] "1"
goto TWLNAND
elif chk $[NAND] "1"
goto TWLNAND
else
goto Results
end
@TWLNAND
#check if TWLNAND is accessible
if isdir 2:
if not isdir 2:/shared2
# the 3DS won't boot if this folder doesn't exist.
# if it isn't there, remaking it is harmless
if ask "An important folder in the TWLNAND is missing. Fix this issue?"
mkdir 2:/shared2
set TWLNAND_LOG "$[TWLNAND_LOG]Information: The shared2 folder did not exist, but was recreated automatically.\n"
else
set TWLNAND_LOG "$[TWLNAND_LOG]Critical: There is no shared2 folder in the TWLNAND.\n"
end
end
else
set TWLNAND_LOG "$[TWLNAND_LOG]Error: TWLNAND not found.\n"
end
if not isdir 3:
set TWLNAND_LOG "$[TWLNAND_LOG]Warning: TWLP not found.\n"
end
if chk $[FULL] "1"
goto FIRM_Data
elif chk $[NAND] "1"
goto FIRM_Data
else
goto Results
end
@FIRM_Data
# compare firm slots against the hashes of all payloads we could reasonably expect
# todo: add hashes for new b9s/fb3ds versions as they're released
for S: firm*.bin
strsplit FIRM $[FORPATH] "/"
strsplit -b FIRM $[FIRM] "."
# immediately start with b9s checks so you can skip the other checks and speed up the script if it is
if sha $[FORPATH]@0:3E00 72002296D1B8B1D4E462EA340124D836BA7F8D6BF16617F369ED90983C42BB98
set FIRM_LOG "$[FIRM_LOG]Information: b9s v1.4 installed to $[FIRM].\n"
elif sha $[FORPATH]@0:7800 79C68585B4BA1D7C4A91B858861553E768C6576B92E303810B33219736B3598B
set FIRM_LOG "$[FIRM_LOG]Warning: b9s v1.3 installed to $[FIRM].\n"
elif sha $[FORPATH]@0:10C00 A765E44844BD5667CC1D7A9A89AD45EC674F8392367F4418CCB08152581D7B3A
set FIRM_LOG "$[FIRM_LOG]Warning: b9s v1.2 installed to $[FIRM].\n"
elif sha $[FORPATH]@0:10C00 D77BEE742E7E7D528BAA20E6ADA7AC822598DDCACDFC81B1F13E32C94F4EBC50
set FIRM_LOG "$[FIRM_LOG]Warning: b9s v1.1 installed to $[FIRM].\n"
elif sha $[FORPATH]@0:20800 43978C226D3164047051B1B534D6589608F1FA04E0B1766E1FDBEB3BC41707B6
set FIRM_LOG "$[FIRM_LOG]Warning: b9s v1.0 installed to $[FIRM].\n"
elif verify $[FORPATH]
# check for the sighax signatures that fastboot3DS uses
# sciresm is used when fb3DS is installed by outside sources, derrek is used if fb3DS updated itself
# sciresm will be the far more common one, as a result
if sha $[FORPATH]@100:100 078CC0CFD850A27093DDA2630C3603CA0C96969BD1F26DA48AC7B1BAE5DD5219
set FIRMSIG "SciresM"
elif sha $[FORPATH]@100:100 ADB73ABC35708EF1DFE9EF9CA5FAC8BFC2DF916BB2E38101858482409F0D450A
set FIRMSIG "derrek"
else
set FIRMSIG "none"
end
# minfirm is a special case, so get it out of the way before signatures get involved
if sha $[FORPATH]@0:100 93EE0A3799072EFB368DAD3174D8DE2EC9735BC13AC78C087DA80
set FIRM_LOG "$[FIRM_LOG]Critical: minfirm installed to $[FIRM].\n"
elif chk -u $[FIRMSIG] "none"
if sha $[FORPATH]@0:100 D36E802EEA55B92110438D0A3B09DFCEEEC71AEB7BF05073A2E0E857827F3903
set FIRM_LOG "$[FIRM_LOG]Information: fb3DS v1.2 ($[FIRMSIG] sig) installed to $[FIRM].\n"
elif sha $[FORPATH]@0:100 9C8D28272421C78AC796EB9023A6D1373F31176CB693CE1B04B1B78112E25226
set FIRM_LOG "$[FIRM_LOG]Warning: fb3DS v1.1 ($[FIRMSIG] sig) installed to $[FIRM].\n"
elif sha $[FORPATH]@0:100 5E58A159C057D0762E6BFC53FE5A5CDAECA338544B252B85524DFBBB1D546DCB
set FIRM_LOG "$[FIRM_LOG]Warning: fb3DS v1.1-beta ($[FIRMSIG] sig) installed to $[FIRM].\n"
elif sha $[FORPATH]@0:100 12EBA2DDB6B5203E66CBE82A963B56AECF540814F15F8539D0CE65DAE818BBB7
set FIRM_LOG "$[FIRM_LOG]Warning: fb3DS v1.0 ($[FIRMSIG] sig) installed to $[FIRM].\n"
else
set FIRM_LOG "$[FIRM_LOG]Warning: Valid unknown firm with $[FIRMSIG] sig installed to $[FIRM].\n"
end
else
set FIRM_LOG "$[FIRM_LOG]Information: Valid stock/unknown firm installed to $[FIRM].\n"
end
else
set FIRM_LOG "$[FIRM_LOG]Error: Invalid firm installed to $[FIRM].\n"
end
next
if chk $[FULL] "1"
goto SD_Files
else
goto Results
end
@SD_Files
if isdir "0:/Nintendo 3DS"
if isdir A:
if find A:/dbs/title.db TITLEDB
fget $[TITLEDB]@100:4 DIFF
#check whether SD title.db has the DIFF magic
if chk -u $[DIFF] "44494646"
if ask "The SD title.db is invalid. Fix this issue?"
rm $[TITLEDB]
fdummy $[TITLEDB] 400
set SD_LOG "$[SD_LOG]Warning: The SD title.db needs to be reset. Reboot and go into System Settings -> Data Management -> Nintendo 3DS -> Software to do this.\n"
else
set SD_LOG "$[SD_LOG]Critical: SD title.db is invalid.\n"
end
end
else
if ask "The SD title.db does not exist. Fix this issue?"
if not isdir A:/dbs
mkdir A:/dbs
set TITLEDB A:/dbs/title.db
fdummy $[TITLEDB] 400
set SD_LOG "$[SD_LOG]Warning: The SD title.db needs to be reset. Reboot and go into System Settings -> Data Management -> Nintendo 3DS -> Software to do this.\n"
else
set SD_LOG "$[SD_LOG]Critical: SD title.db not found.\n"
end
end
else
set SD_LOG "$[SD_LOG]Warning: Nintendo 3DS folder's data is inaccessible.\n"
end
if not find 0:/boot.3dsx NULL
set SD_LOG "$[SD_LOG]Warning: There is no boot.3dsx in the SD card root.\n"
end
if not find 0:/boot.firm NULL
set SD_LOG "$[SD_LOG]Warning: There is no boot.firm in the SD card root.\n"
end
else
set SD_LOG "$[SD_LOG]Warning: Nintendo 3DS folder not found."
end
if chk $[FULL] "1"
goto MISC_Files
else
goto Results
end
@MISC_Files
# check whether the NVRAM SPI flash works, because it's incredibly bad news if it doesn't
if not shaget M:/nvram.mem@0:400 NULL
set RAM_LOG "$[RAM_LOG]Critical: NVRAM is inaccessible.\n"
end
@Results
dumptxt 0:/gm9/ctrcheck_latest.txt "---$[DATESTAMP] $[TIMESTAMP]---\n"
if chk -u $[NANDSECTORS_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[NANDSECTORS_LOG]
end
if chk -u $[CTRNAND_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[CTRNAND_LOG]
end
if chk -u $[TWLNAND_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[TWLNAND_LOG]
end
if chk -u $[FIRM_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[FIRM_LOG]
end
if chk -u $[SD_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[SD_LOG]
end
if chk -u $[RAM_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[RAM_LOG]
end
textview 0:/gm9/ctrcheck_latest.txt
if chk $[LOG] "activated"
dumptxt -p 0:/gm9/ctrcheck_log.txt "\n\n---$[DATESTAMP] $[TIMESTAMP]---\n"
if chk -u $[NANDSECTORS_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_log.txt $[NANDSECTORS_LOG]
end
if chk -u $[CTRNAND_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_log.txt $[CTRNAND_LOG]
end
if chk -u $[TWLNAND_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_log.txt $[TWLNAND_LOG]
end
if chk -u $[FIRM_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_log.txt $[FIRM_LOG]
end
if chk -u $[SD_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[SD_LOG]
end
if chk -u $[RAM_LOG] ""
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[RAM_LOG]
end
echo "These results have been appended to \nthe log in the gm9 folder."
end
echo "Thanks for using this script!"
goto cleanup
@check_Toggle_log
if chk $[LOG] "disabled"
set LOG "activated"
else
set LOG "disabled"
end
goto menu
@check_Exit
@ItsCrocoSwine
Copy link

script broken, line #333 should be textview 0:/gm9/ctrcheck_latest.txt

@StarlitSkies
Copy link
Author

wow, that's bizarre. i noticed and fixed that already, no reason it should have made it into the actual release
thanks tho

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment