You've already forked scripts
mirror of
https://github.com/nh-server/scripts.git
synced 2026-04-30 11:25:45 -07:00
792 lines
30 KiB
Plaintext
792 lines
30 KiB
Plaintext
# A script for checking all of a 3DS console's important files and their integrity.
|
|
# original script by FrozenFire
|
|
# overhaul, fixes, and current maintenance by StarlitSkies
|
|
# last modified: 2024-09-30
|
|
# if you didn't get this file from https://github.com/nh-server/scripts, go there and make sure this copy wasn't tampered with
|
|
|
|
@cleanup
|
|
set VERSION "4"
|
|
set FULL "0"
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]"
|
|
|
|
set NANDSECTORS_LOG ""
|
|
set ESSENTIALS_LOG ""
|
|
set CTRNAND_LOG ""
|
|
set TWLNAND_LOG ""
|
|
set FIRM_LOG ""
|
|
set SD_LOG ""
|
|
set MISC_LOG ""
|
|
set LOG "disabled"
|
|
|
|
@menu
|
|
labelsel -o -s "Select which parts of the system to check.\nConsole is a $[REGION] $[RDTYPE] $[ONTYPE] using $[HAX].\nCurrent id0 is $[SYSID0].\n\nPermanent logging: $[LOG]" check_*
|
|
goto menu
|
|
|
|
@check_All
|
|
set FULL "1"
|
|
goto NAND_Header
|
|
|
|
@check_NAND_Only
|
|
goto NAND_Header
|
|
|
|
@check_SD_Only
|
|
goto SD_Files
|
|
|
|
@NAND_Header
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: ~~~\nNAND Sectors: ---\nCTRNAND: ---\nTWLNAND: ---\nTWLP: ---\nFIRM Partitions: ---"
|
|
|
|
# check whether NAND header signature is sighax, based on hashes
|
|
# sighax will always be read as valid by boot9 even without cfw, so it's just used to check whether custom partitions should be possible
|
|
set SIGHAX "0"
|
|
if find S:/nand_hdr.bin NULL
|
|
if not shaget S:/nand_hdr.bin@0:200 NULL
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Error: NAND header is an invalid size.\n"
|
|
goto NAND_Sectors
|
|
end
|
|
else
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Error: NAND header not found.\n"
|
|
goto NAND_Sectors
|
|
end
|
|
|
|
if sha S:/nand_hdr.bin@0:100 A4AE99B93412E4643E4686987B6CFD59701D5C655CA2FF671CE680B4DDCF0948
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: NAND header's signature is sighax.\n"
|
|
set SIGHAX "1"
|
|
end
|
|
|
|
# hash-based check of NAND header partition table against retail partition tables
|
|
if sha S:/nand_hdr.bin@100:60 dfd434b883874d8b585a102f3cf3ae4cef06767801db515fdf694a7e7cd98bc2
|
|
if chk $[ONTYPE] "N3DS"
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: NAND header is stock. (n3DS)\n"
|
|
else
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: o3DS has an n3DS NAND header.\n"
|
|
end
|
|
elif sha S:/nand_hdr.bin@100:60 ae9b6645105f3aec22c2e3ee247715ab302874fca283343c731ca43ea1baa25d
|
|
if chk $[ONTYPE] "O3DS"
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: NAND header is stock. (o3DS)\n"
|
|
else
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: n3DS has an o3DS NAND header.\n"
|
|
end
|
|
else
|
|
fget S:/nand_hdr.bin@100:4 NCSD
|
|
#check for the NCSD magic header, if it's not present then there's definitely something wrong
|
|
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
|
|
else
|
|
# your NAND has a minor case of serious brain damage
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Error: NAND header data is invalid. You've met with a terrible fate, haven't you?\n"
|
|
end
|
|
end
|
|
|
|
@NAND_Sectors
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: ~~~\nCTRNAND: ---\nTWLNAND: ---\nTWLP: ---\nTWLP: ---\nFIRM Partitions: ---"
|
|
|
|
# verify 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: Secret Sector data is invalid. a9lh might be installed.\n"
|
|
end
|
|
else
|
|
if 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
|
|
else
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: NAND is unreadable at offset 0x12C00...?\n"
|
|
end
|
|
end
|
|
|
|
# verify the TWL MBR exists and is retail (there is no good reason this should ever be modified)
|
|
if find S:/twlmbr.bin NULL
|
|
if not sha S:/twlmbr.bin 77a98e31f1ff7ec4ef2bfacca5a114a49a70dcf8f1dcd23e7a486973cfd06617
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: TWL MBR data is invalid.\n"
|
|
end
|
|
else
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: TWL MBR not found.\n"
|
|
end
|
|
|
|
# get first byte in stage2 location
|
|
if not fget S:/nand.bin@B800000:1 ABYTE
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Critical: NAND is unreadable at offset 0xB800000...?\n"
|
|
end
|
|
# 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
|
|
|
|
# check for presence of bonus drive, just because it might come in handy to know sometimes
|
|
if isdir 8:
|
|
set NANDSECTORS_LOG "$[NANDSECTORS_LOG]Information: Bonus drive is enabled.\n"
|
|
end
|
|
|
|
|
|
@CTRNAND
|
|
# check if CTRNAND can be accessed, skip all CTRNAND checks if it isn't
|
|
if not isdir 1:
|
|
set CTRNAND_LOG "$[CTRNAND_LOG]Error: CTRNAND not found.\n"
|
|
goto TWLNAND
|
|
end
|
|
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: DONE\nCTRNAND: ~~~\nTWLNAND: ---\nTWLP: ---\nFIRM Partitions: ---"
|
|
|
|
# find movable.sed, check if it's valid, and check whether CMAC is set correctly based on size (288 bytes vs 320 bytes)
|
|
set MOVABLEVALID "0"
|
|
set MLFCSHASH ""
|
|
if not find 1:/private/movable.sed movable
|
|
goto LFCS
|
|
end
|
|
if shaget $[movable]@0:100 NULL
|
|
fget $[movable]@8:10 ISVALID
|
|
if chk $[ISVALID] "00000000000000000000000000000000"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Critical: movable.sed's copy of LFCS is blank.\n"
|
|
end
|
|
else
|
|
goto LFCS
|
|
end
|
|
fget $[movable]@0:4 HEADER
|
|
if chk -u $[HEADER] "53454544"
|
|
goto LFCS
|
|
end
|
|
set MOVABLEVALID "1"
|
|
fget $[movable]@5:1 CMAC
|
|
shaget $[movable]@8:110 MLFCSHASH
|
|
#check if movable.sed at least has the SEED magic header, then check whether CMAC is set correctly based on size (320 vs 288 bytes)
|
|
if chk $[CMAC] "01"
|
|
if not fget $[movable]@120:1 NULL
|
|
if ask "movable.sed is misconfigured.\nPress (A) to reconfigure it to normal values."
|
|
fset $[movable]@5 00
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Information: movable.sed has been fixed by removing the CMAC flag.\n"
|
|
else
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Critical: movable.sed is 288 bytes but has the CMAC flag.\n"
|
|
end
|
|
end
|
|
end
|
|
|
|
@LFCS
|
|
# find LFCS, check if it's valid, check if it's the same as movable.sed's copy of LFCS, and check against OTP seed that's used to make part of LFCS
|
|
# account for both potential filenames of LocalFriendCodeSeed
|
|
set LFCSVALID "0"
|
|
if find 1:/rw/sys/LocalFriendCodeSeed_B LFCS
|
|
elif find 1:/rw/sys/LocalFriendCodeSeed_A LFCS
|
|
else
|
|
goto SecureInfo
|
|
end
|
|
if shaget $[LFCS]@0:100 NULL
|
|
fget $[LFCS]@0:10 ISVALID
|
|
if chk $[ISVALID] "00000000000000000000000000000000"
|
|
goto SecureInfo
|
|
end
|
|
else
|
|
goto SecureInfo
|
|
end
|
|
set LFCSVALID "1"
|
|
shaget $[LFCS]@0:110 LFCSHASH
|
|
# compare the LFCS in movable.sed to the raw LFCS file, if movable.sed exists
|
|
if chk -u $[MLFCSHASH] ""
|
|
if chk -u $[MLFCSHASH] $[LFCSHASH]
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Information: LFCS doesn't match movable.sed.\n"
|
|
end
|
|
end
|
|
if find M:/otp_dec.mem OTP
|
|
fget $[LFCS]@108:8 LFCSEED
|
|
fget $[OTP]@8:8 OTPSEED
|
|
if chk -u $[LFCSEED] $[OTPSEED]
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Warning: Console is using a donor LFCS.\n"
|
|
end
|
|
end
|
|
|
|
@SecureInfo
|
|
# find secureinfo_(a/b) and check if it's valid
|
|
set SECVALID "0"
|
|
if find 1:/rw/sys/SecureInfo_A SEC
|
|
elif find 1:/rw/sys/SecureInfo_B SEC
|
|
else
|
|
goto HWCAL0
|
|
end
|
|
if not find -s 1:/rw/sys/SecureInfo_C ALTSEC
|
|
set ALTSEC ""
|
|
end
|
|
if not shaget $[SEC]@0:110 NULL
|
|
goto HWCAL0
|
|
end
|
|
# the checks here are done against the end of the file since that part contains the serial; the RSA signature isn't important for troubleshooting purposes
|
|
fget $[SEC]@100:10 ISVALID
|
|
if chk $[ISVALID] "00000000000000000000000000000000"
|
|
goto HWCAL0
|
|
else
|
|
set SECVALID "1"
|
|
fget $[SEC]@10C:1 SECSERIALCHECK
|
|
if chk $[SECSERIALCHECK] "00"
|
|
fget $[SEC]@102:0A SECSERIAL
|
|
else
|
|
fget $[SEC]@102:0B SECSERIAL
|
|
end
|
|
end
|
|
|
|
# then check if it matches the console region, check for region change (presence of secureinfo_c), and use that for both data comparison and to see what region was changed to
|
|
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"
|
|
elif chk $[ALTREGSEC] "04"
|
|
set ALTREG "CHN"
|
|
elif chk $[ALTREGSEC] "05"
|
|
set ALTREG "KOR"
|
|
elif chk $[ALTREGSEC] "06"
|
|
set ALTREG "TWN"
|
|
end
|
|
else
|
|
set REGCHG "0"
|
|
end
|
|
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"
|
|
elif chk $[REGSEC] "04"
|
|
set REG "CHN"
|
|
elif chk $[REGSEC] "05"
|
|
set REG "KOR"
|
|
elif chk $[REGSEC] "06"
|
|
set REG "TWN"
|
|
end
|
|
if chk -u $[REG] $[REGION]
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Warning: SecureInfo doesn't match the console's region.\n"
|
|
end
|
|
if chk $[REGCHG] "1"
|
|
if chk -u $[ALTREG] $[REGION]
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Information: Console's region is changed from $[REG] to $[ALTREG].\n"
|
|
else
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Information: SecureInfo_C exists, but console's region is unchanged.\n"
|
|
end
|
|
end
|
|
|
|
@HWCAL0
|
|
# check whether first HWCAL exists
|
|
set HWCAL0VALID "0"
|
|
if find 1:/ro/sys/HWCAL0.dat HWCAL0
|
|
if not shaget $[HWCAL0]@0:900 NULL
|
|
goto HWCAL1
|
|
end
|
|
else
|
|
goto HWCAL1
|
|
end
|
|
# if it's valid, check if it has the CCAL magic header
|
|
fget $[HWCAL0]@0:4 HEADER
|
|
if chk $[HEADER] "4343414C"
|
|
set HWCAL0VALID "1"
|
|
end
|
|
|
|
@HWCAL1
|
|
# check whether second HWCAL exists
|
|
set HWCAL1VALID "0"
|
|
if find 1:/ro/sys/HWCAL1.dat HWCAL1
|
|
if not shaget $[HWCAL1]@0:900 NULL
|
|
goto Misc_CTRNAND
|
|
end
|
|
else
|
|
goto Misc_CTRNAND
|
|
end
|
|
# if it's valid, check if it has the CCAL magic header
|
|
fget $[HWCAL1]@0:4 HEADER
|
|
if chk $[HEADER] "4343414C"
|
|
set HWCAL1VALID "1"
|
|
end
|
|
|
|
@Misc_CTRNAND
|
|
# 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. (This console cannot boot without an SD card.)\n"
|
|
end
|
|
|
|
# check whether nand title database exists
|
|
if find 1:/dbs/title.db NANDTITLEDB
|
|
if shaget $[NANDTITLEDB]@0:400 NULL
|
|
fget $[NANDTITLEDB]@100:4 HEADER
|
|
# check whether nand title.db has the DIFF magic header
|
|
if chk -u $[HEADER] "44494646"
|
|
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: CTRNAND title.db data is invalid.\n"
|
|
end
|
|
else
|
|
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: CTRNAND title.db is an invalid size.\n"
|
|
end
|
|
else
|
|
set CTRNAND_LOG "$[CTRNAND_LOG]Critical: CTRNAND title.db not found.\n"
|
|
end
|
|
|
|
set RECOVERYMODE "0"
|
|
if chk $[MOVABLEVALID] "0"
|
|
set RECOVERYMODE "1"
|
|
end
|
|
if chk $[LFCSVALID] "0"
|
|
set RECOVERYMODE "1"
|
|
end
|
|
if chk $[SECVALID] "0"
|
|
set RECOVERYMODE "1"
|
|
end
|
|
if chk $[HWCAL0VALID] "0"
|
|
set RECOVERYMODE "1"
|
|
end
|
|
if chk $[HWCAL1VALID] "0"
|
|
set RECOVERYMODE "1"
|
|
end
|
|
|
|
if chk $[RECOVERYMODE] "1"
|
|
goto CTRNAND_Recovery
|
|
else
|
|
goto TWLNAND
|
|
end
|
|
|
|
@CTRNAND_Recovery
|
|
# if the recovery flag exists, then they've already tried this. if any part of it failed before, it'll fail again now - no point in redoing it
|
|
if find 9:/RECOVERYFLAG NULL
|
|
if chk $[MOVABLEVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Bruh Moment: movable.sed is still invalid after a recovery attempt.\n"
|
|
end
|
|
if chk $[LFCSVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Bruh Moment: LFCS is still invalid after a recovery attempt.\n"
|
|
end
|
|
if chk $[SECVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Bruh Moment: SecureInfo is still invalid after a recovery attempt.\n"
|
|
end
|
|
if chk $[HWCAL0VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Bruh Moment: HWCAL0 is still invalid after a recovery attempt.\n"
|
|
end
|
|
if chk $[HWCAL1VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Bruh Moment: HWCAL1 is still invalid after a recovery attempt.\n"
|
|
end
|
|
goto TWLNAND
|
|
end
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: DONE\nCTRNAND: !!!\nTWLNAND: ---\nTWLP: ---\nFIRM Partitions: ---"
|
|
set TRYRECOVERY "0"
|
|
if find S:/essential.exefs NULL
|
|
if ask "Critical files in the CTRNAND are invalid,\nbut a backup was found.\nPress (A) to enter data recovery mode.\n \n(If you have already tried this recently,\nyou may safely skip this part.)"
|
|
allow -a 1:
|
|
set TRYRECOVERY "1"
|
|
else
|
|
if chk $[MOVABLEVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: movable.sed is invalid, and data recovery was denied.\n"
|
|
end
|
|
if chk $[LFCSVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: LFCS is invalid, and data recovery was denied.\n"
|
|
end
|
|
if chk $[SECVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: SecureInfo is invalid, and data recovery was denied.\n"
|
|
end
|
|
if chk $[HWCAL0VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: HWCAL0 is invalid, and data recovery was denied.\n"
|
|
end
|
|
if chk $[HWCAL1VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: HWCAL1 is invalid, and data recovery was denied.\n"
|
|
end
|
|
end
|
|
else
|
|
if chk $[MOVABLEVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: movable.sed is invalid, and essential.exefs does not exist.\n"
|
|
end
|
|
if chk $[LFCSVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: LFCS is invalid, and essential.exefs does not exist.\n"
|
|
end
|
|
if chk $[SECVALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: SecureInfo is invalid, and essential.exefs does not exist.\n"
|
|
end
|
|
if chk $[HWCAL0VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: HWCAL0 is invalid, and essential.exefs does not exist.\n"
|
|
end
|
|
if chk $[HWCAL1VALID] "0"
|
|
set ESSENTIALS_LOG "$[ESSENTIALS_LOG]Error: HWCAL1 is invalid, and essential.exefs does not exist.\n"
|
|
end
|
|
end
|
|
|
|
if chk $[TRYRECOVERY] "0"
|
|
goto TWLNAND
|
|
end
|
|
|
|
imgmount S:/essential.exefs
|
|
|
|
# check backup movable.sed integrity, fix CMAC if needed, then copy it into NAND if it's valid
|
|
set VALIDBACKUP "1"
|
|
if shaget G:/movable@0:100 NULL
|
|
fget G:/movable@0:4 HEADER
|
|
fget G:/movable@5:1 CMAC
|
|
if chk -u $[HEADER] "53454544"
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[CMAC] "01"
|
|
if not fget G:/movable@120:1 NULL
|
|
set CMACFIX "1"
|
|
else
|
|
set CMACFIX "0"
|
|
end
|
|
end
|
|
else
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[VALIDBACKUP] "1"
|
|
if chk $[CMACFIX] "0"
|
|
cp -n -w G:/movable 1:/private/movable.sed
|
|
else
|
|
cp -n -w G:/movable 9:/movable
|
|
fset 9:/movable@5 00
|
|
cp -n -w 9:/movable 1:/private/movable.sed
|
|
rm -o -s 9:/movable
|
|
end
|
|
end
|
|
|
|
# check backup LFCS integrity, then copy it into NAND if it's valid
|
|
set VALIDBACKUP "1"
|
|
if shaget G:/frndseed@0:100 NULL
|
|
fget G:/frndseed@0:10 ISVALID
|
|
if chk $[ISVALID] "00000000000000000000000000000000"
|
|
set VALIDBACKUP "0"
|
|
end
|
|
else
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[VALIDBACKUP] "1"
|
|
cp -n -w G:/frndseed 1:/rw/sys/LocalFriendCodeSeed_B
|
|
end
|
|
|
|
# check backup SecureInfo integrity, then copy it into NAND if it's valid
|
|
set VALIDBACKUP "1"
|
|
if shaget G:/secinfo@0:100 NULL
|
|
fget G:/secinfo@0:10 ISVALID
|
|
if chk $[ISVALID] "00000000000000000000000000000000"
|
|
set VALIDBACKUP "0"
|
|
end
|
|
else
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[VALIDBACKUP] "1"
|
|
cp -n -w G:/secinfo 1:/rw/sys/SecureInfo_A
|
|
end
|
|
|
|
# check backup HWCAL0 integrity, then copy it into NAND if it's valid
|
|
if shaget G:/hwcal0@0:900 NULL
|
|
fget G:/hwcal0@0:4 HEADER
|
|
if chk -u $[HEADER] "4343414C"
|
|
set VALIDBACKUP "0"
|
|
end
|
|
else
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[VALIDBACKUP] "1"
|
|
cp -n -w G:/hwcal0 1:/ro/sys/HWCAL0.dat
|
|
end
|
|
|
|
# check backup HWCAL1 integrity, then copy it into NAND if it's valid
|
|
if shaget G:/hwcal1@0:900 NULL
|
|
fget G:/hwcal1@0:4 HEADER
|
|
if chk -u $[HEADER] "4343414C"
|
|
set VALIDBACKUP "0"
|
|
end
|
|
else
|
|
set VALIDBACKUP "0"
|
|
end
|
|
if chk $[VALIDBACKUP] "1"
|
|
cp -n -w G:/hwcal1 1:/ro/sys/HWCAL1.dat
|
|
end
|
|
|
|
# the flag is made in ramdrive since people should do the requested rerun immediately, and i'm shoving it down their throat to do that as hard as i can. if they ignore it and reboot anyway, first off what the hell
|
|
fdummy 9:/RECOVERYFLAG 400
|
|
echo "Recovery attempt finished.\nctrcheck will now restart automatically.\n \Run another check to get the true results.\n"
|
|
imgumount
|
|
goto cleanup
|
|
|
|
@TWLNAND
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: DONE\nCTRNAND: DONE\nTWLNAND: ~~~\nTWLP: ---\nFIRM Partitions: ---"
|
|
#check if TWLNAND can be accessed
|
|
if not isdir 2:
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Error: TWLNAND not found.\n"
|
|
goto TWLP
|
|
end
|
|
# the console won't boot if shared2 doesn't exist, but remaking it is easy and harmless
|
|
if not isdir 2:/shared2
|
|
if ask "A folder required to boot is missing. Press (A) to fix this issue."
|
|
mkdir 2:/shared2
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Information: shared2 has been recreated.\n"
|
|
else
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Critical: shared2 not found, and was not recreated when asked.\n"
|
|
end
|
|
end
|
|
# check for inspect.log, and if it exists, compare its copy of the serial number against SecureInfo
|
|
if isdir 2:/sys/log
|
|
if find 2:/sys/log/inspect.log INSPECTLOG
|
|
if chk $[SECVALID] "1"
|
|
fget $[INSPECTLOG]@77:1 TWLNSERIALCHECK
|
|
if chk $[TWLNSERIALCHECK] "0a"
|
|
fget $[INSPECTLOG]@6D:0A TWLNSERIAL
|
|
else
|
|
fget $[INSPECTLOG]@6D:0B TWLNSERIAL
|
|
end
|
|
if chk -u $[SECSERIAL] $[TWLNSERIAL]
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Warning: inspect.log serial does not match SecureInfo.\n"
|
|
end
|
|
end
|
|
else
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Warning: inspect.log not found.\n"
|
|
end
|
|
else
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Warning: 2:/sys/log folder not found, meaning inspect.log also not found.\n"
|
|
end
|
|
|
|
@TWLP
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: DONE\nCTRNAND: DONE\nTWLNAND: DONE\nTWLP: ~~~\nFIRM Partitions: ---"
|
|
# you don't need TWLP to boot (or for a lot of things, really), but it's still good to check everything's in place
|
|
if not isdir 3:
|
|
set TWLNAND_LOG "$[TWLNAND_LOG]Warning: TWLP not found.\n"
|
|
end
|
|
|
|
@FIRM_Data
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: NAND. Progress:\n \nNAND Header: DONE\nNAND Sectors: DONE\nCTRNAND: DONE\nTWLNAND: DONE\nTWLP: DONE\nFIRM Partitions: ~~~"
|
|
# basic checks for how many FIRM partitions exist and which ones they are
|
|
set FIRMEXIST "0"
|
|
set ALTFIRMEXIST "0"
|
|
set BONUSFIRMS "0"
|
|
set TOOMANYFIRMS "0"
|
|
if find S:/firm0.bin NULL
|
|
set FIRMEXIST "1"
|
|
end
|
|
if find S:/firm1.bin NULL
|
|
set ALTFIRMEXIST "1"
|
|
end
|
|
if find S:/firm2.bin NULL
|
|
set BONUSFIRMS "1"
|
|
end
|
|
if find S:/firm5.bin NULL
|
|
set TOOMANYFIRMS "1"
|
|
end
|
|
if chk $[FIRMEXIST] "0"
|
|
if chk $[ALTFIRMEXIST] "0"
|
|
set FIRM_LOG "$[FIRM_LOG]Error: FIRM0 and FIRM1 both not found. Unless you have access to ntrboot, do NOT power off the console until you've fixed this.\n"
|
|
goto NAND_End
|
|
else
|
|
set FIRM_LOG "$[FIRM_LOG]Bruh Moment: FIRM0 not found, but FIRM1 exists. This shouldn't be possible, the console's probably having a stroke.\n"
|
|
end
|
|
else
|
|
if chk $[ALTFIRMEXIST] "0"
|
|
set FIRM_LOG "$[FIRM_LOG]Critical: FIRM1 not found, but FIRM0 exists. The console isn't dead yet, but fix this ASAP.\n"
|
|
end
|
|
end
|
|
if chk $[BONUSFIRMS] "1"
|
|
if chk $[TOOMANYFIRMS] "1"
|
|
set FIRM_LOG "$[FIRM_LOG]Bruh Moment: ...Why are there more than 5 FIRM partitions? I hope you only sacrificed the AGBSAVE.\n"
|
|
else
|
|
set FIRM_LOG "$[FIRM_LOG]Information: Extra FIRM partitions detected. But hey, rules were meant to be broken, right?\n"
|
|
end
|
|
end
|
|
|
|
# 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 since they're the most common, it speeds up the script if they get hit first
|
|
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
|
|
# and that means you'll basically never see derrek sig in practice. be wary if you do see it
|
|
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, since it's only used by b9stool and shouldn't stick around for long after cfw setup. best to get it out of the way before fb3DS
|
|
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
|
|
|
|
@NAND_End
|
|
if chk $[FULL] "1"
|
|
goto SD_Files
|
|
else
|
|
goto Misc_Files
|
|
end
|
|
|
|
|
|
@SD_Files
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: SD. Progress:\n \nSD Structure: ~~~"
|
|
# this script doesn't NEED an SD to run, necessarily, but you'd have to copy it to your NAND on purpose
|
|
if not isdir 0:
|
|
set SD_LOG "$[SD_LOG]Bruh Moment: No SD card is inserted. You went out of your way to find this, didn't you?\n"
|
|
goto SD_End
|
|
end
|
|
if isdir "0:/Nintendo 3DS"
|
|
if isdir A:
|
|
if find A:/dbs/title.db TITLEDB
|
|
if shaget $[TITLEDB]@0:400 NULL
|
|
fget $[TITLEDB]@100:4 HEADER
|
|
#check whether SD title.db has the DIFF magic header
|
|
if chk -u $[HEADER] "44494646"
|
|
if ask "The SD title.db is invalid.\nPress (A) to reset it.\n \n(This will delete all of your games and apps,\nbut they were likely also corrupted\nby whatever caused this.)"
|
|
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 data is invalid.\n"
|
|
end
|
|
end
|
|
else
|
|
if ask "The SD title.db is invalid.\nPress (A) to reset it.\n \n(This will delete all of your games and apps,\nbut they were likely also corrupted\nby whatever caused this.)"
|
|
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 an invalid size.\n"
|
|
end
|
|
end
|
|
else
|
|
set TITLEDB A:/dbs/title.db
|
|
if ask "The SD title.db does not exist.\nPress (A) to create a blank one.\n \n(A title database is necessary\nto install games and apps.)"
|
|
if not isdir A:/dbs
|
|
mkdir A:/dbs
|
|
end
|
|
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
|
|
if isdir "0:/Nintendo 3DS/$[SYSID0]"
|
|
set SD_LOG "$[SD_LOG]Warning: Nintendo 3DS folder has valid data, but the data is inaccessible.\n"
|
|
else
|
|
set SD_LOG "$[SD_LOG]Information: Nintendo 3DS folder exists, but has no data.\n"
|
|
end
|
|
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.\n"
|
|
end
|
|
|
|
@Misc_Files
|
|
set PREVIEW_MODE "ctrcheck v$[VERSION]\nCurrently processing: Other. Progress:\nNVRAM: ~~~\OTP: ---\n"
|
|
# check whether the NVRAM SPI flash works, because it's bad news if it doesn't
|
|
if not shaget M:/nvram.mem@0:400 NULL
|
|
set MISC_LOG "$[MISC_LOG]Critical: NVRAM is inaccessible.\n"
|
|
end
|
|
# boot9strap decrypts the OTP and makes it available to GodMode9, most other bootloaders don't
|
|
if not find M:/otp_dec.mem NULL
|
|
set MISC_LOG "$[MISC_LOG]Warning: Decrypted OTP not found. (If you aren't using b9s, you can ignore this.)\n"
|
|
end
|
|
|
|
@Results
|
|
dumptxt 0:/gm9/ctrcheck_latest.txt "Date and Time: $[DATESTAMP] $[TIMESTAMP]\n---"
|
|
if chk -u $[NANDSECTORS_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[NANDSECTORS_LOG]
|
|
end
|
|
if chk -u $[ESSENTIALS_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[ESSENTIALS_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 $[MISC_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_latest.txt $[MISC_LOG]
|
|
end
|
|
textview 0:/gm9/ctrcheck_latest.txt
|
|
if chk $[LOG] "activated"
|
|
dumptxt -p 0:/gm9/ctrcheck_log.txt "Date and Time: $[DATESTAMP] $[TIMESTAMP]\n---"
|
|
if chk -u $[NANDSECTORS_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_log.txt $[NANDSECTORS_LOG]
|
|
end
|
|
if chk -u $[ESSENTIALS_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_log.txt $[ESSENTIALS_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_log.txt $[SD_LOG]
|
|
end
|
|
if chk -u $[MISC_LOG] ""
|
|
dumptxt -p 0:/gm9/ctrcheck_log.txt $[MISC_LOG]
|
|
end
|
|
echo "Check complete.\n \nThese results are also in 0:/gm9/ctrcheck_latest.txt,\nbut the file will be overwritten if you rerun this.\n \nHowever, they have also been appended to\nthe permanent log in 0:/gm9/ctrcheck_log.txt."
|
|
else
|
|
echo "Check complete.\n \nThese results are also in 0:/gm9/ctrcheck_latest.txt,\nbut the file will be overwritten if you rerun this."
|
|
end
|
|
goto cleanup
|
|
|
|
@check_Toggle_permanent_log
|
|
if chk $[LOG] "disabled"
|
|
set LOG "activated"
|
|
else
|
|
set LOG "disabled"
|
|
end
|
|
goto menu
|
|
|
|
@check_Exit
|