Last active
May 12, 2024 06:22
-
-
Save StarlitSkies/f417c1ded9de8fceffdb532f92823377 to your computer and use it in GitHub Desktop.
3DS firmware integrity checker
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
# 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 |
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
script broken, line #333 should be
textview 0:/gm9/ctrcheck_latest.txt