mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-02-05 21:29:30 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b40e6f10 | ||
|
|
69a07cde85 | ||
|
|
b9b4d6876c | ||
|
|
bed965c6dd | ||
|
|
f01c04d796 | ||
|
|
5de970965e | ||
|
|
f3e4aa0b7e | ||
|
|
e2110e80c0 | ||
|
|
b0f2c6658a | ||
|
|
baf0b9045e | ||
|
|
b82eb54aab | ||
|
|
7d541b6c4b | ||
|
|
9d72a25cc2 | ||
|
|
79e8c1b6bf | ||
|
|
8f6df04e2c | ||
|
|
466144f8c9 | ||
|
|
629f2cc11e | ||
|
|
1ffd1a3bff | ||
|
|
d4bcd62941 | ||
|
|
702c31413b | ||
|
|
d4a6e902cf | ||
|
|
c00e72506e | ||
|
|
7ccc160e90 | ||
|
|
068b83da8e | ||
|
|
d429ea3e64 | ||
|
|
5bf1d85e05 | ||
|
|
32b655b1f9 | ||
|
|
e3a64ece96 | ||
|
|
74986afc38 | ||
|
|
3cc2bcf022 | ||
|
|
ebd13e3728 | ||
|
|
2d7a54ddb7 | ||
|
|
60d989d899 | ||
|
|
4de3cfe6fa | ||
|
|
50034975c3 | ||
|
|
ab4f076846 | ||
|
|
08840055a4 | ||
|
|
02eafc688a | ||
|
|
db7c7ee273 | ||
|
|
383e6bdfce | ||
|
|
169bd48762 | ||
|
|
a2ab3c25c9 | ||
|
|
abf0843d22 | ||
|
|
ca64a2575e | ||
|
|
035760b5f5 | ||
|
|
66e6a8cd4a | ||
|
|
d85e7656c4 | ||
|
|
942151b6d7 | ||
|
|
d7a51c7798 | ||
|
|
bd86b95494 | ||
|
|
5562b403a4 | ||
|
|
ead70df624 | ||
|
|
33e62e046b | ||
|
|
bb0b5a3d05 |
40
.github/workflows/build_and_test.yml
vendored
Normal file
40
.github/workflows/build_and_test.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Run publish script
|
||||
run: ./publish-nix.sh -d
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: "*.nupkg,*.snupkg,*.zip"
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
55
.github/workflows/build_program.yml
vendored
55
.github/workflows/build_program.yml
vendored
@@ -1,55 +0,0 @@
|
||||
name: Build Program
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [NDecrypt]
|
||||
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net9.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0, net9.0]
|
||||
conf: [Debug] #[Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8') || startsWith(matrix.framework, 'net9')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: |
|
||||
cd ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
zip -r ${{ github.workspace }}/${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ./
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
5
.github/workflows/check_pr.yml
vendored
5
.github/workflows/check_pr.yml
vendored
@@ -11,7 +11,10 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2024 Matt Nadareski
|
||||
Copyright (c) 2018-2025 Matt Nadareski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -1,912 +0,0 @@
|
||||
// using System;
|
||||
// using System.IO;
|
||||
// using SabreTools.IO.Extensions;
|
||||
// using SabreTools.Models.N3DS;
|
||||
// using SabreTools.Serialization.Wrappers;
|
||||
// using static NDecrypt.Core.CommonOperations;
|
||||
|
||||
// namespace NDecrypt.Core
|
||||
// {
|
||||
// // https://www.3dbrew.org/wiki/CIA
|
||||
// public class CIATool : ITool
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// Decryption args to use while processing
|
||||
// /// </summary>
|
||||
// private readonly DecryptArgs _decryptArgs;
|
||||
|
||||
// /// <summary>
|
||||
// /// Indicates if development images are expected
|
||||
// /// </summary>
|
||||
// private readonly bool _development;
|
||||
|
||||
// /// <summary>
|
||||
// /// Set of all partition keys
|
||||
// /// </summary>
|
||||
// private readonly PartitionKeys[] KeysMap = new PartitionKeys[8];
|
||||
|
||||
// public CIATool(bool development, DecryptArgs decryptArgs)
|
||||
// {
|
||||
// _development = development;
|
||||
// _decryptArgs = decryptArgs;
|
||||
// }
|
||||
|
||||
// /// <inheritdoc/>
|
||||
// public bool EncryptFile(string filename, bool force)
|
||||
// {
|
||||
// // Ensure the constants are all set
|
||||
// if (_decryptArgs.IsReady != true)
|
||||
// {
|
||||
// Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// // Open the read and write on the same file for inplace processing
|
||||
// using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
// using var output = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// // Deserialize the CIA information
|
||||
// var cia = ReadCIA(input);
|
||||
// if (cia == null)
|
||||
// {
|
||||
// Console.WriteLine("Error: Not a 3DS CIA!");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Encrypt all 8 NCCH partitions
|
||||
// EncryptAllPartitions(cia, force, input, output);
|
||||
// return false;
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
|
||||
// Console.WriteLine("Please check that the file was a valid 3DS CIA file and try again.");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <inheritdoc/>
|
||||
// public bool DecryptFile(string filename, bool force)
|
||||
// {
|
||||
// // Ensure the constants are all set
|
||||
// if (_decryptArgs.IsReady != true)
|
||||
// {
|
||||
// Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// // Open the read and write on the same file for inplace processing
|
||||
// using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
// using var output = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// // Deserialize the CIA information
|
||||
// var cia = ReadCIA(input);
|
||||
// if (cia == null)
|
||||
// {
|
||||
// Console.WriteLine("Error: Not a 3DS CIA!");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Decrypt all 8 NCCH partitions
|
||||
// DecryptAllPartitions(cia, force, input, output);
|
||||
// return false;
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
|
||||
// Console.WriteLine("Please check that the file was a valid 3DS CIA file and try again.");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// #region Decrypt
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt all partitions in the content file data of a CIA header
|
||||
// /// </summary>
|
||||
// /// <param name="cia">CIA representing the 3DS CIA file</param>
|
||||
// /// <param name="force">Indicates if the operation should be forced</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void DecryptAllPartitions(SabreTools.Serialization.Wrappers.CIA cia, bool force, Stream input, Stream output)
|
||||
// {
|
||||
// // Check the partitions table
|
||||
// if (cia.Model.Partitions == null)
|
||||
// {
|
||||
// Console.WriteLine("Invalid partitions table!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Iterate over all 8 NCCH partitions
|
||||
// for (int p = 0; p < cia.Model.Partitions.Length; p++)
|
||||
// {
|
||||
// // Check the partition exists
|
||||
// var header = cia.Model.Partitions[0];
|
||||
// if (header == null)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {p} Not found... Skipping...");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Decrypt the partition, if possible
|
||||
// if (ShouldDecryptPartition(cia, p, force))
|
||||
// DecryptPartition(header, p, input, output);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Determine if the current partition should be decrypted
|
||||
// /// </summary>
|
||||
// private static bool ShouldDecryptPartition(SabreTools.Serialization.Wrappers.CIA cia, int index, bool force)
|
||||
// {
|
||||
// // If we're forcing the operation, tell the user
|
||||
// if (force)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} is not verified due to force flag being set.");
|
||||
// return true;
|
||||
// }
|
||||
// // If we're not forcing the operation, check if the 'NoCrypto' bit is set
|
||||
// else if (cia.Model.Partitions![index]!.Flags!.PossblyDecrypted())
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index}: Already Decrypted?...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // By default, it passes
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt a single partition
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void DecryptPartition(NCCHHeader header, int index, Stream input, Stream output)
|
||||
// {
|
||||
// // Get the table entry -- TODO: Fix this to get the real entry
|
||||
// var tableEntry = new PartitionTableEntry();
|
||||
|
||||
// // Determine the keys needed for this partition
|
||||
// SetDecryptionKeys(header, index);
|
||||
|
||||
// // Decrypt the parts of the partition
|
||||
// DecryptExtendedHeader(header, index, tableEntry, input, output);
|
||||
// DecryptExeFS(header, index, tableEntry, input, output);
|
||||
// DecryptRomFS(header, index, tableEntry, input, output);
|
||||
|
||||
// // Update the flags
|
||||
// UpdateDecryptCryptoAndMasks(header, tableEntry, output);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Determine the set of keys to be used for decryption
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// private void SetDecryptionKeys(NCCHHeader header, int index)
|
||||
// {
|
||||
// // Get partition-specific values
|
||||
// byte[]? rsaSignature = header.RSA2048Signature;
|
||||
|
||||
// // Set the header to use based on mode
|
||||
// BitMasks masks = header.Flags!.BitMasks;
|
||||
// CryptoMethod method = header.Flags.CryptoMethod;
|
||||
|
||||
// // Get the partition keys
|
||||
// KeysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt the extended header, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool DecryptExtendedHeader(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get required offsets
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint partitionOffset = GetPartitionOffset(tableEntry, mediaUnitSize);
|
||||
// if (partitionOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint extHeaderSize = GetExtendedHeaderSize(header);
|
||||
// if (extHeaderSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Seek to the extended header
|
||||
// input.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
|
||||
// output.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
|
||||
|
||||
// Console.WriteLine($"Partition {index} ExeFS: Decrypting: ExHeader");
|
||||
|
||||
// // Create the Plain AES cipher for this partition
|
||||
// var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C!, header.PlainIV());
|
||||
|
||||
// // Process the extended header
|
||||
// PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null);
|
||||
|
||||
// #if NET6_0_OR_GREATER
|
||||
// // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer
|
||||
// input.Seek(0, SeekOrigin.Begin);
|
||||
// #endif
|
||||
// output.Flush();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt the ExeFS, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool DecryptExeFS(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Validate the ExeFS
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize) - mediaUnitSize;
|
||||
// if (exeFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint exeFsSize = GetExeFSSize(header, mediaUnitSize);
|
||||
// if (exeFsSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Decrypt the filename table
|
||||
// DecryptExeFSFilenameTable(header, index, tableEntry, input, output);
|
||||
|
||||
// // For all but the original crypto method, process each of the files in the table
|
||||
// if (header.Flags!.CryptoMethod != CryptoMethod.Original)
|
||||
// DecryptExeFSFileEntries(header, index, tableEntry, input, output);
|
||||
|
||||
// // Seek to the ExeFS
|
||||
// input.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Create the ExeFS AES cipher for this partition
|
||||
// int ctroffsetE = (int)(mediaUnitSize / 0x10);
|
||||
// byte[] exefsIVWithOffset = Add(header.ExeFSIV(), ctroffsetE);
|
||||
// var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C!, exefsIVWithOffset);
|
||||
|
||||
// // Setup and perform the decryption
|
||||
// PerformAESOperation(exeFsSize - mediaUnitSize,
|
||||
// cipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} ExeFS: Decrypting: {s}"));
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt the ExeFS Filename Table
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void DecryptExeFSFilenameTable(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get ExeFS offset
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (exeFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Seek to the ExeFS header
|
||||
// input.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// Console.WriteLine($"Partition {index} ExeFS: Decrypting: ExeFS Filename Table");
|
||||
|
||||
// // Create the ExeFS AES cipher for this partition
|
||||
// var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C!, header.ExeFSIV());
|
||||
|
||||
// // Process the filename table
|
||||
// PerformAESOperation(mediaUnitSize, cipher, input, output, null);
|
||||
|
||||
// #if NET6_0_OR_GREATER
|
||||
// // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer
|
||||
// input.Seek(0, SeekOrigin.Begin);
|
||||
// #endif
|
||||
// output.Flush();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt the ExeFS file entries
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void DecryptExeFSFileEntries(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get ExeFS offset
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsHeaderOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (exeFsHeaderOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Get to the start of the files
|
||||
// uint exeFsFilesOffset = exeFsHeaderOffset + mediaUnitSize;
|
||||
// input.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
|
||||
// var exefsHeader = SabreTools.Serialization.Deserializers.N3DS.ParseExeFSHeader(input);
|
||||
|
||||
// // If the header failed to read, log and return
|
||||
// if (exefsHeader == null)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS header could not be read. Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// foreach (var fileHeader in exefsHeader.FileHeaders!)
|
||||
// {
|
||||
// // Only decrypt a file if it's a code binary
|
||||
// if (fileHeader == null || !fileHeader.IsCodeBinary())
|
||||
// continue;
|
||||
|
||||
// // Create the ExeFS AES ciphers for this partition
|
||||
// uint ctroffset = (fileHeader.FileOffset + mediaUnitSize) / 0x10;
|
||||
// byte[] exefsIVWithOffsetForHeader = Add(header.ExeFSIV(), (int)ctroffset);
|
||||
// var firstCipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey!, exefsIVWithOffsetForHeader);
|
||||
// var secondCipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C!, exefsIVWithOffsetForHeader);
|
||||
|
||||
// // Seek to the file entry
|
||||
// input.Seek(exeFsFilesOffset + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsFilesOffset + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Setup and perform the encryption
|
||||
// uint exeFsSize = GetExeFSSize(header, mediaUnitSize);
|
||||
// PerformAESOperation(exeFsSize,
|
||||
// firstCipher,
|
||||
// secondCipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} ExeFS: Decrypting: {fileHeader.FileName}...{s}"));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Decrypt the RomFS, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool DecryptRomFS(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Validate the RomFS
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint romFsOffset = GetRomFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (romFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint romFsSize = GetRomFSSize(header, mediaUnitSize);
|
||||
// if (romFsSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Seek to the RomFS
|
||||
// input.Seek(romFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(romFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Create the RomFS AES cipher for this partition
|
||||
// var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey!, header.RomFSIV());
|
||||
|
||||
// // Setup and perform the decryption
|
||||
// PerformAESOperation(romFsSize,
|
||||
// cipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} RomFS: Decrypting: {s}"));
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Update the CryptoMethod and BitMasks for the decrypted partition
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void UpdateDecryptCryptoAndMasks(NCCHHeader header,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream output)
|
||||
// {
|
||||
// // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
|
||||
// uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;
|
||||
|
||||
// // Write the new CryptoMethod
|
||||
// output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
// output.Write((byte)CryptoMethod.Original);
|
||||
// output.Flush();
|
||||
|
||||
// // Write the new BitMasks flag
|
||||
// output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
// BitMasks flag = header.Flags!.BitMasks;
|
||||
// flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
|
||||
// flag |= BitMasks.NoCrypto;
|
||||
// output.Write((byte)flag);
|
||||
// output.Flush();
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Encrypt
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt all partitions in the content file data of a CIA header
|
||||
// /// </summary>
|
||||
// /// <param name="cia">CIA representing the 3DS CIA file</param>
|
||||
// /// <param name="force">Indicates if the operation should be forced</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void EncryptAllPartitions(SabreTools.Serialization.Wrappers.CIA cia, bool force, Stream input, Stream output)
|
||||
// {
|
||||
// // Check the partitions table
|
||||
// if (cia.Model.Partitions == null)
|
||||
// {
|
||||
// Console.WriteLine("Invalid partitions table!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Iterate over all 8 NCCH partitions
|
||||
// for (int p = 0; p < cia.Model.Partitions.Length; p++)
|
||||
// {
|
||||
// // Check the partition exists
|
||||
// var header = cia.Model.Partitions[0];
|
||||
// if (header == null)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {p} Not found... Skipping...");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Encrypt the partition, if possible
|
||||
// if (ShouldEncryptPartition(cia, p, force))
|
||||
// EncryptPartition(header, p, input, output);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Determine if the current partition should be encrypted
|
||||
// /// </summary>
|
||||
// private static bool ShouldEncryptPartition(SabreTools.Serialization.Wrappers.CIA cia, int index, bool force)
|
||||
// {
|
||||
// // If we're forcing the operation, tell the user
|
||||
// if (force)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} is not verified due to force flag being set.");
|
||||
// return true;
|
||||
// }
|
||||
// // If we're not forcing the operation, check if the 'NoCrypto' bit is set
|
||||
// else if (!cia.Model.Partitions![index]!.Flags!.PossblyDecrypted())
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index}: Already Encrypted?...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // By default, it passes
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt a single partition
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void EncryptPartition(NCCHHeader header, int index, Stream input, Stream output)
|
||||
// {
|
||||
// // Get the table entry -- TODO: Fix this to get the real entry
|
||||
// var tableEntry = new PartitionTableEntry();
|
||||
|
||||
// // Determine the keys needed for this partition
|
||||
// SetEncryptionKeys(header, index);
|
||||
|
||||
// // Encrypt the parts of the partition
|
||||
// EncryptExtendedHeader(header, index, tableEntry, input, output);
|
||||
// EncryptExeFS(header, index, tableEntry, input, output);
|
||||
// EncryptRomFS(header, index, tableEntry, input, output);
|
||||
|
||||
// // Update the flags
|
||||
// UpdateEncryptCryptoAndMasks(header, index, tableEntry, output);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Determine the set of keys to be used for encryption
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// private void SetEncryptionKeys(NCCHHeader header, int index)
|
||||
// {
|
||||
// // Get partition-specific values
|
||||
// byte[]? rsaSignature = header.RSA2048Signature;
|
||||
|
||||
// // TODO: Figure out what sane defaults for these values are
|
||||
// // TODO: Can we actually re-encrypt a CIA?
|
||||
|
||||
// // Set the header to use based on mode
|
||||
// BitMasks masks = BitMasks.NoCrypto; // ciaHeader.BackupHeader.Flags.BitMasks;
|
||||
// CryptoMethod method = CryptoMethod.Original; // ciaHeader.BackupHeader.Flags.CryptoMethod;
|
||||
|
||||
// // Get the partition keys
|
||||
// KeysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt the extended header, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool EncryptExtendedHeader(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get required offsets
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint partitionOffset = GetPartitionOffset(tableEntry, mediaUnitSize);
|
||||
// if (partitionOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint extHeaderSize = GetExtendedHeaderSize(header);
|
||||
// if (extHeaderSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Seek to the extended header
|
||||
// input.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
|
||||
// output.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
|
||||
|
||||
// Console.WriteLine($"Partition {index} ExeFS: Encrypting: ExHeader");
|
||||
|
||||
// // Create the Plain AES cipher for this partition
|
||||
// var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C!, header.PlainIV());
|
||||
|
||||
// // Process the extended header
|
||||
// PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null);
|
||||
|
||||
// #if NET6_0_OR_GREATER
|
||||
// // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer
|
||||
// input.Seek(0, SeekOrigin.Begin);
|
||||
// #endif
|
||||
// output.Flush();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt the ExeFS, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool EncryptExeFS(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Validate the ExeFS
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize) - mediaUnitSize;
|
||||
// if (exeFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint exeFsSize = GetExeFSSize(header, mediaUnitSize);
|
||||
// if (exeFsSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // TODO: Determine how to figure out the original crypto method, if possible
|
||||
// // For all but the original crypto method, process each of the files in the table
|
||||
// //if (ciaHeader.BackupHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
// // EncryptExeFSFileEntries(header, index, tableEntry, reader, writer);
|
||||
|
||||
// // Encrypt the filename table
|
||||
// EncryptExeFSFilenameTable(header, index, tableEntry, input, output);
|
||||
|
||||
// // Seek to the ExeFS
|
||||
// input.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Create the ExeFS AES cipher for this partition
|
||||
// int ctroffsetE = (int)(mediaUnitSize / 0x10);
|
||||
// byte[] exefsIVWithOffset = Add(header.ExeFSIV(), ctroffsetE);
|
||||
// var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C!, exefsIVWithOffset);
|
||||
|
||||
// // Setup and perform the decryption
|
||||
// PerformAESOperation(exeFsSize - mediaUnitSize,
|
||||
// cipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} ExeFS: Encrypting: {s}"));
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt the ExeFS Filename Table
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void EncryptExeFSFilenameTable(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get ExeFS offset
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (exeFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Seek to the ExeFS header
|
||||
// input.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// Console.WriteLine($"Partition {index} ExeFS: Encrypting: ExeFS Filename Table");
|
||||
|
||||
// // Create the ExeFS AES cipher for this partition
|
||||
// var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C!, header.ExeFSIV());
|
||||
|
||||
// // Process the filename table
|
||||
// PerformAESOperation(mediaUnitSize, cipher, input, output, null);
|
||||
|
||||
// #if NET6_0_OR_GREATER
|
||||
// // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer
|
||||
// input.Seek(0, SeekOrigin.Begin);
|
||||
// #endif
|
||||
// output.Flush();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt the ExeFS file entries
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void EncryptExeFSFileEntries(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Get ExeFS offset
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint exeFsHeaderOffset = GetExeFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (exeFsHeaderOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Get to the start of the files
|
||||
// uint exeFsFilesOffset = exeFsHeaderOffset + mediaUnitSize;
|
||||
// input.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
|
||||
// var exefsHeader = SabreTools.Serialization.Deserializers.N3DS.ParseExeFSHeader(input);
|
||||
|
||||
// // If the header failed to read, log and return
|
||||
// if (exefsHeader == null)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} ExeFS header could not be read. Skipping...");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// foreach (var fileHeader in exefsHeader.FileHeaders!)
|
||||
// {
|
||||
// // Only decrypt a file if it's a code binary
|
||||
// if (fileHeader == null || !fileHeader.IsCodeBinary())
|
||||
// continue;
|
||||
|
||||
// // Create the ExeFS AES ciphers for this partition
|
||||
// uint ctroffset = (fileHeader.FileOffset + mediaUnitSize) / 0x10;
|
||||
// byte[] exefsIVWithOffsetForHeader = Add(header.ExeFSIV(), (int)ctroffset);
|
||||
// var firstCipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey!, exefsIVWithOffsetForHeader);
|
||||
// var secondCipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C!, exefsIVWithOffsetForHeader);
|
||||
|
||||
// // Seek to the file entry
|
||||
// input.Seek(exeFsFilesOffset + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
// output.Seek(exeFsFilesOffset + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Setup and perform the encryption
|
||||
// uint exeFsSize = GetExeFSSize(header, mediaUnitSize);
|
||||
// PerformAESOperation(exeFsSize,
|
||||
// firstCipher,
|
||||
// secondCipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} ExeFS: Encrypting: {fileHeader.FileName}...{s}"));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Encrypt the RomFS, if it exists
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private bool EncryptRomFS(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream input,
|
||||
// Stream output)
|
||||
// {
|
||||
// // Validate the RomFS
|
||||
// uint mediaUnitSize = 0x200;
|
||||
// uint romFsOffset = GetRomFSOffset(header, tableEntry, mediaUnitSize);
|
||||
// if (romFsOffset == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// uint romFsSize = GetRomFSSize(header, mediaUnitSize);
|
||||
// if (romFsSize == 0)
|
||||
// {
|
||||
// Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Seek to the RomFS
|
||||
// input.Seek(romFsOffset, SeekOrigin.Begin);
|
||||
// output.Seek(romFsOffset, SeekOrigin.Begin);
|
||||
|
||||
// // Force setting encryption keys for partitions 1 and above
|
||||
// if (index > 0)
|
||||
// {
|
||||
// //var backupHeader = ciaHeader.BackupHeader;
|
||||
// KeysMap[index].SetRomFSValues((BitMasks)0x00);
|
||||
// }
|
||||
|
||||
// // Create the RomFS AES cipher for this partition
|
||||
// var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey!, header.RomFSIV());
|
||||
|
||||
// // Setup and perform the decryption
|
||||
// PerformAESOperation(romFsSize,
|
||||
// cipher,
|
||||
// input,
|
||||
// output,
|
||||
// (string s) => Console.WriteLine($"\rPartition {index} RomFS: Encrypting: {s}"));
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Update the CryptoMethod and BitMasks for the encrypted partition
|
||||
// /// </summary>
|
||||
// /// <param name="header">NCCH header representing the partition</param>
|
||||
// /// <param name="index">Index of the partition</param>
|
||||
// /// <param name="tableEntry">PartitionTableEntry header representing the partition</param>
|
||||
// /// <param name="output">Stream representing the output</param>
|
||||
// private void UpdateEncryptCryptoAndMasks(NCCHHeader header,
|
||||
// int index,
|
||||
// PartitionTableEntry tableEntry,
|
||||
// Stream output)
|
||||
// {
|
||||
// // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
|
||||
// uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;
|
||||
|
||||
// // Write the new CryptoMethod
|
||||
// output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
|
||||
// // For partitions 1 and up, set crypto-method to 0x00
|
||||
// if (index > 0)
|
||||
// output.Write((byte)CryptoMethod.Original);
|
||||
|
||||
// // TODO: Determine how to figure out the original crypto method, if possible
|
||||
// // If partition 0, restore crypto-method from backup flags
|
||||
// //else
|
||||
// // writer.Write((byte)ciaHeader.BackupHeader.Flags.CryptoMethod);
|
||||
|
||||
// output.Flush();
|
||||
|
||||
// // Write the new BitMasks flag
|
||||
// output.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
// BitMasks flag = header.Flags!.BitMasks;
|
||||
// flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;
|
||||
|
||||
// // TODO: Determine how to figure out the original crypto method, if possible
|
||||
// //flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ciaHeader.BackupHeader.Flags.BitMasks;
|
||||
// output.Write((byte)flag);
|
||||
// output.Flush();
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Serialization
|
||||
|
||||
// /// <summary>
|
||||
// /// Read from a stream and get a CIA header, if possible
|
||||
// /// </summary>
|
||||
// /// <param name="input">Stream representing the input</param>
|
||||
// /// <returns>CIA header object, null on error</returns>
|
||||
// private static SabreTools.Serialization.Wrappers.CIA? ReadCIA(Stream input)
|
||||
// => SabreTools.Serialization.Wrappers.CIA.Create(input);
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
// }
|
||||
89
NDecrypt.Core/Configuration.cs
Normal file
89
NDecrypt.Core/Configuration.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
internal class Configuration
|
||||
{
|
||||
#region DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// Encryption data taken from woodsec
|
||||
/// </summary>
|
||||
public string? NitroEncryptionData { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// AES Hardware Constant
|
||||
/// </summary>
|
||||
/// <remarks>generator</remarks>
|
||||
public string? AESHardwareConstant { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
/// <remarks>slot0x18KeyX</remarks>
|
||||
public string? KeyX0x18 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
public string? DevKeyX0x18 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x1B (New 3DS 9.6)
|
||||
/// </summary>
|
||||
/// <remarks>slot0x1BKeyX</remarks>
|
||||
public string? KeyX0x1B { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x1B New 3DS 9.6)
|
||||
/// </summary>
|
||||
public string? DevKeyX0x1B { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
/// <remarks>slot0x25KeyX</remarks>
|
||||
public string? KeyX0x25 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public string? DevKeyX0x25 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
/// <remarks>slot0x2CKeyX</remarks>
|
||||
public string? KeyX0x2C { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
public string? DevKeyX0x2C { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public static Configuration? Create(string path)
|
||||
{
|
||||
// Ensure the file exists
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
// Parse the configuration directly
|
||||
try
|
||||
{
|
||||
string contents = File.ReadAllText(path);
|
||||
return JsonConvert.DeserializeObject<Configuration?>(contents);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.Nitro;
|
||||
using NitroDeserializer = SabreTools.Serialization.Deserializers.Nitro;
|
||||
using SabreTools.Serialization.Deserializers;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
public class DSTool : ITool
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly byte[] NitroEncryptionData =
|
||||
[
|
||||
0x99,0xD5,0x20,0x5F,0x57,0x44,0xF5,0xB9,0x6E,0x19,0xA4,0xD9,0x9E,0x6A,0x5A,0x94,
|
||||
0xD8,0xAE,0xF1,0xEB,0x41,0x75,0xE2,0x3A,0x93,0x82,0xD0,0x32,0x33,0xEE,0x31,0xD5,
|
||||
0xCC,0x57,0x61,0x9A,0x37,0x06,0xA2,0x1B,0x79,0x39,0x72,0xF5,0x55,0xAE,0xF6,0xBE,
|
||||
0x5F,0x1B,0x69,0xFB,0xE5,0x9D,0xF1,0xE9,0xCE,0x2C,0xD9,0xA1,0x5E,0x32,0x05,0xE6,
|
||||
0xFE,0xD3,0xFE,0xCF,0xD4,0x62,0x04,0x0D,0x8B,0xF5,0xEC,0xB7,0x2B,0x60,0x79,0xBB,
|
||||
0x12,0x95,0x31,0x0D,0x6E,0x3F,0xDA,0x2B,0x88,0x84,0xF0,0xF1,0x3D,0x12,0x7E,0x25,
|
||||
0x45,0x22,0xF1,0xBB,0x24,0x06,0x1A,0x06,0x11,0xAD,0xDF,0x28,0x8B,0x64,0x81,0x34,
|
||||
0x2B,0xEB,0x33,0x29,0x99,0xAA,0xF2,0xBD,0x9C,0x14,0x95,0x9D,0x9F,0xF7,0xF5,0x8C,
|
||||
0x72,0x97,0xA1,0x29,0x9D,0xD1,0x5F,0xCF,0x66,0x4D,0x07,0x1A,0xDE,0xD3,0x4A,0x4B,
|
||||
0x85,0xC9,0xA7,0xA3,0x17,0x95,0x05,0x3A,0x3D,0x49,0x0A,0xBF,0x0A,0x89,0x8B,0xA2,
|
||||
0x4A,0x82,0x49,0xDD,0x27,0x90,0xF1,0x0B,0xE9,0xEB,0x1C,0x6A,0x83,0x76,0x45,0x05,
|
||||
0xBA,0x81,0x70,0x61,0x17,0x3F,0x4B,0xDE,0xAE,0xCF,0xAB,0x39,0x57,0xF2,0x3A,0x56,
|
||||
0x48,0x11,0xAD,0x8A,0x40,0xE1,0x45,0x3F,0xFA,0x9B,0x02,0x54,0xCA,0xA6,0x93,0xFB,
|
||||
0xEF,0x4D,0xFE,0x6F,0xA3,0xD8,0x87,0x9C,0x08,0xBA,0xD5,0x48,0x6A,0x8D,0x2D,0xFD,
|
||||
0x6E,0x15,0xF8,0x74,0xBD,0xBE,0x52,0x8B,0x18,0x22,0x8A,0x9E,0xFB,0x74,0x37,0x07,
|
||||
0x1B,0x36,0x6C,0x4A,0x19,0xBA,0x42,0x62,0xB9,0x79,0x91,0x10,0x7B,0x67,0x65,0x96,
|
||||
0xFE,0x02,0x23,0xE8,0xEE,0x99,0x8C,0x77,0x3E,0x5C,0x86,0x64,0x4D,0x6D,0x78,0x86,
|
||||
0xA5,0x4F,0x65,0xE2,0x1E,0xB2,0xDF,0x5A,0x0A,0xD0,0x7E,0x08,0x14,0xB0,0x71,0xAC,
|
||||
0xBD,0xDB,0x83,0x1C,0xB9,0xD7,0xA1,0x62,0xCD,0xC6,0x63,0x7C,0x52,0x69,0xC3,0xE6,
|
||||
0xBF,0x75,0xCE,0x12,0x44,0x5D,0x21,0x04,0xFA,0xFB,0xD3,0x3C,0x38,0x11,0x63,0xD4,
|
||||
0x95,0x85,0x41,0x49,0x46,0x09,0xF2,0x08,0x43,0x11,0xDC,0x1F,0x76,0xC0,0x15,0x6D,
|
||||
0x1F,0x3C,0x63,0x70,0xEA,0x87,0x80,0x6C,0xC3,0xBD,0x63,0x8B,0xC2,0x37,0x21,0x37,
|
||||
0xDC,0xEE,0x09,0x23,0x2E,0x37,0x6A,0x4D,0x73,0x90,0xF7,0x50,0x30,0xAC,0x1C,0x92,
|
||||
0x04,0x10,0x23,0x91,0x4F,0xD2,0x07,0xAA,0x68,0x3E,0x4F,0x9A,0xC9,0x64,0x60,0x6A,
|
||||
0xC8,0x14,0x21,0xF3,0xD6,0x22,0x41,0x12,0x44,0x24,0xCF,0xE6,0x8A,0x56,0xDD,0x0D,
|
||||
0x53,0x4D,0xE1,0x85,0x1E,0x8C,0x52,0x5A,0x9C,0x19,0x84,0xC2,0x03,0x57,0xF1,0x6F,
|
||||
0xE3,0x00,0xBE,0x58,0xF6,0x4C,0xED,0xD5,0x21,0x64,0x9C,0x1F,0xBE,0x55,0x03,0x3C,
|
||||
0x4A,0xDC,0xFF,0xAA,0xC9,0xDA,0xE0,0x5D,0x5E,0xBF,0xE6,0xDE,0xF5,0xD8,0xB1,0xF8,
|
||||
0xFF,0x36,0xB3,0xB9,0x62,0x67,0x95,0xDB,0x31,0x5F,0x37,0xED,0x4C,0x70,0x67,0x99,
|
||||
0x90,0xB5,0x18,0x31,0x6C,0x3D,0x99,0x99,0xE4,0x42,0xDA,0xD3,0x25,0x42,0x13,0xA0,
|
||||
0xAE,0xD7,0x70,0x6C,0xB1,0x55,0xCF,0xC7,0xD7,0x46,0xD5,0x43,0x61,0x17,0x3D,0x44,
|
||||
0x28,0xE9,0x33,0x85,0xD5,0xD0,0xA2,0x93,0xAA,0x25,0x12,0x1F,0xFB,0xC5,0x0B,0x46,
|
||||
0xF5,0x97,0x76,0x56,0x45,0xA6,0xBE,0x87,0xB1,0x94,0x6B,0xE8,0xB1,0xFE,0x33,0x99,
|
||||
0xAE,0x1F,0x3E,0x6C,0x39,0x71,0x1D,0x09,0x00,0x90,0x37,0xE4,0x10,0x3E,0x75,0x74,
|
||||
0xFF,0x8C,0x83,0x3B,0xB0,0xF1,0xB0,0xF9,0x01,0x05,0x47,0x42,0x95,0xF1,0xD6,0xAC,
|
||||
0x7E,0x38,0xE6,0x9E,0x95,0x74,0x26,0x3F,0xB4,0x68,0x50,0x18,0xD0,0x43,0x30,0xB4,
|
||||
0x4C,0x4B,0xE3,0x68,0xBF,0xE5,0x4D,0xB6,0x95,0x8B,0x0A,0xA0,0x74,0x25,0x32,0x77,
|
||||
0xCF,0xA1,0xF7,0x2C,0xD8,0x71,0x13,0x5A,0xAB,0xEA,0xC9,0x51,0xE8,0x0D,0xEE,0xEF,
|
||||
0xE9,0x93,0x7E,0x19,0xA7,0x1E,0x43,0x38,0x81,0x16,0x2C,0xA1,0x48,0xE3,0x73,0xCC,
|
||||
0x29,0x21,0x6C,0xD3,0x5D,0xCE,0xA0,0xD9,0x61,0x71,0x43,0xA0,0x15,0x13,0xB5,0x64,
|
||||
0x92,0xCF,0x2A,0x19,0xDC,0xAD,0xB7,0xA5,0x9F,0x86,0x65,0xF8,0x1A,0x9F,0xE7,0xFB,
|
||||
0xF7,0xFD,0xB8,0x13,0x6C,0x27,0xDB,0x6F,0xDF,0x35,0x1C,0xF7,0x8D,0x2C,0x5B,0x9B,
|
||||
0x12,0xAB,0x38,0x64,0x06,0xCC,0xDE,0x31,0xE8,0x4E,0x75,0x11,0x64,0xE3,0xFA,0xEA,
|
||||
0xEB,0x34,0x54,0xC2,0xAD,0x3F,0x34,0xEB,0x93,0x2C,0x7D,0x26,0x36,0x9D,0x56,0xF3,
|
||||
0x5A,0xE1,0xF6,0xB3,0x98,0x63,0x4A,0x9E,0x32,0x83,0xE4,0x9A,0x84,0x60,0x7D,0x90,
|
||||
0x2E,0x13,0x0E,0xEE,0x93,0x4B,0x36,0xA2,0x85,0xEC,0x16,0x38,0xE8,0x88,0x06,0x02,
|
||||
0xBF,0xF0,0xA0,0x3A,0xED,0xD7,0x6A,0x9A,0x73,0xE1,0x57,0xCF,0xF8,0x44,0xB8,0xDC,
|
||||
0x2E,0x23,0x59,0xD1,0xDF,0x95,0x52,0x71,0x99,0x61,0xA0,0x4B,0xD5,0x7F,0x6E,0x78,
|
||||
0xBA,0xA9,0xC5,0x30,0xD3,0x40,0x86,0x32,0x9D,0x32,0x0C,0x9C,0x37,0xB7,0x02,0x2F,
|
||||
0xBA,0x54,0x98,0xA9,0xC4,0x13,0x04,0xC9,0x8D,0xBE,0xC8,0xE7,0x5D,0x97,0x50,0x2E,
|
||||
0x93,0xD6,0x22,0x59,0x0C,0x27,0xBC,0x22,0x92,0xE0,0xA7,0x20,0x0F,0x93,0x6F,0x7F,
|
||||
0x4C,0x9F,0xD3,0xB5,0xA6,0x2A,0x0B,0x74,0x67,0x49,0x7D,0x10,0x26,0xCB,0xD1,0xC5,
|
||||
0x86,0x71,0xE7,0x8C,0xA0,0x9C,0xE9,0x5B,0xB2,0x1A,0xF6,0x01,0xEE,0x8C,0x9E,0x5E,
|
||||
0x83,0xF2,0x1A,0xDB,0xE6,0xE5,0xEA,0x84,0x59,0x76,0xD2,0x7C,0xF6,0x8D,0xA5,0x49,
|
||||
0x36,0x48,0xC2,0x16,0x52,0xBB,0x83,0xA3,0x74,0xB9,0x07,0x0C,0x3B,0xFF,0x61,0x28,
|
||||
0xE1,0x61,0xE9,0xE4,0xEF,0x6E,0x15,0xAA,0x4E,0xBA,0xE8,0x5D,0x05,0x96,0xBB,0x32,
|
||||
0x56,0xB0,0xFB,0x72,0x52,0x0F,0x0E,0xC8,0x42,0x25,0x65,0x76,0x89,0xAF,0xF2,0xDE,
|
||||
0x10,0x27,0xF0,0x01,0x4B,0x74,0xA7,0x97,0x07,0xD5,0x26,0x54,0x54,0x09,0x1F,0x82,
|
||||
0x0A,0x86,0x7D,0x30,0x39,0x0E,0xB3,0x26,0x9B,0x0B,0x57,0xBB,0x36,0x06,0x31,0xAF,
|
||||
0xFD,0x79,0xFC,0xD9,0x30,0x10,0x2B,0x0C,0xB3,0xE1,0x9B,0xD7,0x7B,0xDC,0x5F,0xEF,
|
||||
0xD2,0xF8,0x13,0x45,0x4D,0x47,0x75,0xBD,0x46,0x96,0x3C,0x7E,0x75,0xF3,0x3E,0xB5,
|
||||
0x67,0xC5,0x9A,0x3B,0xB0,0x5B,0x29,0x6B,0xDE,0x80,0x5B,0xC8,0x15,0x05,0xB1,0x31,
|
||||
0xB6,0xCE,0x49,0xDD,0xAD,0x84,0xB5,0xAE,0x60,0xDC,0x67,0x31,0x34,0x30,0xFE,0x4E,
|
||||
0xBD,0x80,0x2F,0xA6,0xBF,0x63,0x39,0x21,0x86,0xD9,0x35,0x7F,0x16,0x68,0x22,0x05,
|
||||
0x54,0xE9,0x90,0x26,0x8C,0x07,0x6C,0x51,0xA4,0x31,0x55,0xD7,0x09,0x07,0xA8,0x3E,
|
||||
0x2E,0x53,0x66,0xC1,0xF8,0xF2,0x7B,0xC4,0xF2,0x58,0xCF,0xF1,0x87,0xC5,0xA2,0xE7,
|
||||
0x27,0x8F,0x30,0x87,0x58,0xA0,0x64,0x62,0x23,0x18,0xB9,0x88,0x7C,0xFA,0xCE,0xC4,
|
||||
0x98,0xAE,0xAD,0x17,0xCC,0x4A,0x5B,0xF3,0xE9,0x48,0xD5,0x56,0xD3,0x0D,0xF2,0xC8,
|
||||
0x92,0x73,0x8C,0xDB,0xD7,0x2F,0x56,0xAC,0x81,0xF9,0x92,0x69,0x4D,0xC6,0x32,0xF6,
|
||||
0xE6,0xC0,0x8D,0x21,0xE2,0x76,0x80,0x61,0x11,0xBC,0xDC,0x6C,0x93,0xAF,0x19,0x69,
|
||||
0x9B,0xD0,0xBF,0xB9,0x31,0x9F,0x02,0x67,0xA3,0x51,0xEE,0x83,0x06,0x22,0x7B,0x0C,
|
||||
0xAB,0x49,0x42,0x40,0xB8,0xD5,0x01,0x7D,0xCE,0x5E,0xF7,0x55,0x53,0x39,0xC5,0x99,
|
||||
0x46,0xD8,0x87,0x9F,0xBA,0xF7,0x64,0xB4,0xE3,0x9A,0xFA,0xA1,0x6D,0x90,0x68,0x10,
|
||||
0x30,0xCA,0x8A,0x54,0xA7,0x9F,0x60,0xC3,0x19,0xF5,0x6B,0x0D,0x7A,0x51,0x98,0xE6,
|
||||
0x98,0x43,0x51,0xB4,0xD6,0x35,0xE9,0x4F,0xC3,0xDF,0x0F,0x7B,0xD6,0x2F,0x5C,0xBD,
|
||||
0x3A,0x15,0x61,0x19,0xF1,0x4B,0xCB,0xAA,0xDC,0x6D,0x64,0xC9,0xD3,0xC6,0x1E,0x56,
|
||||
0xEF,0x38,0x4C,0x50,0x71,0x86,0x75,0xCC,0x0D,0x0D,0x4E,0xE9,0x28,0xF6,0x06,0x5D,
|
||||
0x70,0x1B,0xAA,0xD3,0x45,0xCF,0xA8,0x39,0xAC,0x95,0xA6,0x2E,0xB4,0xE4,0x22,0xD4,
|
||||
0x74,0xA8,0x37,0x5F,0x48,0x7A,0x04,0xCC,0xA5,0x4C,0x40,0xD8,0x28,0xB4,0x28,0x08,
|
||||
0x0D,0x1C,0x72,0x52,0x41,0xF0,0x7D,0x47,0x19,0x3A,0x53,0x4E,0x58,0x84,0x62,0x6B,
|
||||
0x93,0xB5,0x8A,0x81,0x21,0x4E,0x0D,0xDC,0xB4,0x3F,0xA2,0xC6,0xFC,0xC9,0x2B,0x40,
|
||||
0xDA,0x38,0x04,0xE9,0x5E,0x5A,0x86,0x6B,0x0C,0x22,0x25,0x85,0x68,0x11,0x8D,0x7C,
|
||||
0x92,0x1D,0x95,0x55,0x4D,0xAB,0x8E,0xBB,0xDA,0xA6,0xE6,0xB7,0x51,0xB6,0x32,0x5A,
|
||||
0x05,0x41,0xDD,0x05,0x2A,0x0A,0x56,0x50,0x91,0x17,0x47,0xCC,0xC9,0xE6,0x7E,0xB5,
|
||||
0x61,0x4A,0xDB,0x73,0x67,0x51,0xC8,0x33,0xF5,0xDA,0x6E,0x74,0x2E,0x54,0xC3,0x37,
|
||||
0x0D,0x6D,0xAF,0x08,0xE8,0x15,0x8A,0x5F,0xE2,0x59,0x21,0xCD,0xA8,0xDE,0x0C,0x06,
|
||||
0x5A,0x77,0x6B,0x5F,0xDB,0x18,0x65,0x3E,0xC8,0x50,0xDE,0x78,0xE0,0xB8,0x82,0xB3,
|
||||
0x5D,0x4E,0x72,0x32,0x07,0x4F,0xC1,0x34,0x23,0xBA,0x96,0xB7,0x67,0x4E,0xA4,0x28,
|
||||
0x1E,0x34,0x62,0xEB,0x2D,0x6A,0x70,0xE9,0x2F,0x42,0xC4,0x70,0x4E,0x5A,0x31,0x9C,
|
||||
0xF9,0x5B,0x47,0x28,0xAA,0xDA,0x71,0x6F,0x38,0x1F,0xB3,0x78,0xC4,0x92,0x6B,0x1C,
|
||||
0x9E,0xF6,0x35,0x9A,0xB7,0x4D,0x0E,0xBF,0xCC,0x18,0x29,0x41,0x03,0x48,0x35,0x5D,
|
||||
0x55,0xD0,0x2B,0xC6,0x29,0xAF,0x5C,0x60,0x74,0x69,0x8E,0x5E,0x9B,0x7C,0xD4,0xBD,
|
||||
0x7B,0x44,0x64,0x7D,0x3F,0x92,0x5D,0x69,0xB6,0x1F,0x00,0x4B,0xD4,0x83,0x35,0xCF,
|
||||
0x7E,0x64,0x4E,0x17,0xAE,0x8D,0xD5,0x2E,0x9A,0x28,0x12,0x4E,0x2E,0x2B,0x49,0x08,
|
||||
0x5C,0xAE,0xC6,0x46,0x85,0xAE,0x41,0x61,0x1E,0x6F,0x82,0xD2,0x51,0x37,0x16,0x1F,
|
||||
0x0B,0xF6,0x59,0xA4,0x9A,0xCA,0x5A,0xAF,0x0D,0xD4,0x33,0x8B,0x20,0x63,0xF1,0x84,
|
||||
0x80,0x5C,0xCB,0xCF,0x08,0xB4,0xB9,0xD3,0x16,0x05,0xBD,0x62,0x83,0x31,0x9B,0x56,
|
||||
0x51,0x98,0x9F,0xBA,0xB2,0x5B,0xAA,0xB2,0x22,0x6B,0x2C,0xB5,0xD4,0x48,0xFA,0x63,
|
||||
0x2B,0x5F,0x58,0xFA,0x61,0xFA,0x64,0x09,0xBB,0x38,0xE0,0xB8,0x9D,0x92,0x60,0xA8,
|
||||
0x0D,0x67,0x6F,0x0E,0x37,0xF5,0x0D,0x01,0x9F,0xC2,0x77,0xD4,0xFE,0xEC,0xF1,0x73,
|
||||
0x30,0x39,0xE0,0x7D,0xF5,0x61,0x98,0xE4,0x2C,0x28,0x55,0x04,0x56,0x55,0xDB,0x2F,
|
||||
0x6B,0xEC,0xE5,0x58,0x06,0xB6,0x64,0x80,0x6A,0x2A,0x1A,0x4E,0x5B,0x0F,0xD8,0xC4,
|
||||
0x0A,0x2E,0x52,0x19,0xD9,0x62,0xF5,0x30,0x48,0xBE,0x8C,0x7B,0x4F,0x38,0x9B,0xA2,
|
||||
0xC3,0xAF,0xC9,0xD3,0xC7,0xC1,0x62,0x41,0x86,0xB9,0x61,0x21,0x57,0x6F,0x99,0x4F,
|
||||
0xC1,0xBA,0xCE,0x7B,0xB5,0x3B,0x4D,0x5E,0x8A,0x8B,0x44,0x57,0x5F,0x13,0x5F,0x70,
|
||||
0x6D,0x5B,0x29,0x47,0xDC,0x38,0xE2,0xEC,0x04,0x55,0x65,0x12,0x2A,0xE8,0x17,0x43,
|
||||
0xE1,0x8E,0xDD,0x2A,0xB3,0xE2,0x94,0xF7,0x09,0x6E,0x5C,0xE6,0xEB,0x8A,0xF8,0x6D,
|
||||
0x89,0x49,0x54,0x48,0xF5,0x2F,0xAD,0xBF,0xEA,0x94,0x4B,0xCA,0xFC,0x39,0x87,0x82,
|
||||
0x5F,0x8A,0x01,0xF2,0x75,0xF2,0xE6,0x71,0xD6,0xD8,0x42,0xDE,0xF1,0x2D,0x1D,0x28,
|
||||
0xA6,0x88,0x7E,0xA3,0xA0,0x47,0x1D,0x30,0xD9,0xA3,0x71,0xDF,0x49,0x1C,0xCB,0x01,
|
||||
0xF8,0x36,0xB1,0xF2,0xF0,0x22,0x58,0x5D,0x45,0x6B,0xBD,0xA0,0xBB,0xB2,0x88,0x42,
|
||||
0xC7,0x8C,0x28,0xCE,0x93,0xE8,0x90,0x63,0x08,0x90,0x7C,0x89,0x3C,0xF5,0x7D,0xB7,
|
||||
0x04,0x2D,0x4F,0x55,0x51,0x16,0xFD,0x7E,0x79,0xE8,0xBE,0xC1,0xF2,0x12,0xD4,0xF8,
|
||||
0xB4,0x84,0x05,0x23,0xA0,0xCC,0xD2,0x2B,0xFD,0xE1,0xAB,0xAD,0x0D,0xD1,0x55,0x6C,
|
||||
0x23,0x41,0x94,0x4D,0x77,0x37,0x4F,0x05,0x28,0x0C,0xBF,0x17,0xB3,0x12,0x67,0x6C,
|
||||
0x8C,0xC3,0x5A,0xF7,0x41,0x84,0x2A,0x6D,0xD0,0x94,0x12,0x27,0x2C,0xB4,0xED,0x9C,
|
||||
0x4D,0xEC,0x47,0x82,0x97,0xD5,0x67,0xB9,0x1B,0x9D,0xC0,0x55,0x07,0x7E,0xE5,0x8E,
|
||||
0xE2,0xA8,0xE7,0x3E,0x12,0xE4,0x0E,0x3A,0x2A,0x45,0x55,0x34,0xA2,0xF9,0x2D,0x5A,
|
||||
0x1B,0xAB,0x52,0x7C,0x83,0x10,0x5F,0x55,0xD2,0xF1,0x5A,0x43,0x2B,0xC6,0xA7,0xA4,
|
||||
0x89,0x15,0x95,0xE8,0xB4,0x4B,0x9D,0xF8,0x75,0xE3,0x9F,0x60,0x78,0x5B,0xD6,0xE6,
|
||||
0x0D,0x44,0xE6,0x21,0x06,0xBD,0x47,0x22,0x53,0xA4,0x00,0xAD,0x8D,0x43,0x13,0x85,
|
||||
0x39,0xF7,0xAA,0xFC,0x38,0xAF,0x7B,0xED,0xFC,0xE4,0x2B,0x54,0x50,0x98,0x4C,0xFC,
|
||||
0x85,0x80,0xF7,0xDF,0x3C,0x80,0x22,0xE1,0x94,0xDA,0xDE,0x24,0xC6,0xB0,0x7A,0x39,
|
||||
0x38,0xDC,0x0F,0xA1,0xA7,0xF4,0xF9,0x6F,0x63,0x18,0x57,0x8B,0x84,0x41,0x2A,0x2E,
|
||||
0xD4,0x53,0xF2,0xD9,0x00,0x0F,0xD0,0xDD,0x99,0x6E,0x19,0xA6,0x0A,0xD0,0xEC,0x5B,
|
||||
0x58,0x24,0xAB,0xC0,0xCB,0x06,0x65,0xEC,0x1A,0x13,0x38,0x94,0x0A,0x67,0x03,0x2F,
|
||||
0x3F,0xF7,0xE3,0x77,0x44,0x77,0x33,0xC6,0x14,0x39,0xD0,0xE3,0xC0,0xA2,0x08,0x79,
|
||||
0xBB,0x40,0x99,0x57,0x41,0x0B,0x01,0x90,0xCD,0xE1,0xCC,0x48,0x67,0xDB,0xB3,0xAF,
|
||||
0x88,0x74,0xF3,0x4C,0x82,0x8F,0x72,0xB1,0xB5,0x23,0x29,0xC4,0x12,0x6C,0x19,0xFC,
|
||||
0x8E,0x46,0xA4,0x9C,0xC4,0x25,0x65,0x87,0xD3,0x6D,0xBE,0x8A,0x93,0x11,0x03,0x38,
|
||||
0xED,0x83,0x2B,0xF3,0x46,0xA4,0x93,0xEA,0x3B,0x53,0x85,0x1D,0xCE,0xD4,0xF1,0x08,
|
||||
0x83,0x27,0xED,0xFC,0x9B,0x1A,0x18,0xBC,0xF9,0x8B,0xAE,0xDC,0x24,0xAB,0x50,0x38,
|
||||
0xE9,0x72,0x4B,0x10,0x22,0x17,0x7B,0x46,0x5D,0xAB,0x59,0x64,0xF3,0x40,0xAE,0xF8,
|
||||
0xBB,0xE5,0xC8,0xF9,0x26,0x03,0x4E,0x55,0x7D,0xEB,0xEB,0xFE,0xF7,0x39,0xE6,0xE0,
|
||||
0x0A,0x11,0xBE,0x2E,0x28,0xFF,0x98,0xED,0xC0,0xC9,0x42,0x56,0x42,0xC3,0xFD,0x00,
|
||||
0xF6,0xAF,0x87,0xA2,0x5B,0x01,0x3F,0x32,0x92,0x47,0x95,0x9A,0x72,0xA5,0x32,0x3D,
|
||||
0xAE,0x6B,0xD0,0x9B,0x07,0xD2,0x49,0x92,0xE3,0x78,0x4A,0xFA,0xA1,0x06,0x7D,0xF2,
|
||||
0x41,0xCF,0x77,0x74,0x04,0x14,0xB2,0x0C,0x86,0x84,0x64,0x16,0xD5,0xBB,0x51,0xA1,
|
||||
0xE5,0x6F,0xF1,0xD1,0xF2,0xE2,0xF7,0x5F,0x58,0x20,0x4D,0xB8,0x57,0xC7,0xCF,0xDD,
|
||||
0xC5,0xD8,0xBE,0x76,0x3D,0xF6,0x5F,0x7E,0xE7,0x2A,0x8B,0x88,0x24,0x1B,0x38,0x3F,
|
||||
0x0E,0x41,0x23,0x77,0xF5,0xF0,0x4B,0xD4,0x0C,0x1F,0xFA,0xA4,0x0B,0x80,0x5F,0xCF,
|
||||
0x45,0xF6,0xE0,0xDA,0x2F,0x34,0x59,0x53,0xFB,0x20,0x3C,0x52,0x62,0x5E,0x35,0xB5,
|
||||
0x62,0xFE,0x8B,0x60,0x63,0xE3,0x86,0x5A,0x15,0x1A,0x6E,0xD1,0x47,0x45,0xBC,0x32,
|
||||
0xB4,0xEB,0x67,0x38,0xAB,0xE4,0x6E,0x33,0x3A,0xB5,0xED,0xA3,0xAD,0x67,0xE0,0x4E,
|
||||
0x41,0x95,0xEE,0x62,0x62,0x71,0x26,0x1D,0x31,0xEF,0x62,0x30,0xAF,0xD7,0x82,0xAC,
|
||||
0xC2,0xDC,0x05,0x04,0xF5,0x97,0x07,0xBF,0x11,0x59,0x23,0x07,0xC0,0x64,0x02,0xE8,
|
||||
0x97,0xE5,0x3E,0xAF,0x18,0xAC,0x59,0xA6,0x8B,0x4A,0x33,0x90,0x1C,0x6E,0x7C,0x9C,
|
||||
0x20,0x7E,0x4C,0x3C,0x3E,0x61,0x64,0xBB,0xC5,0x6B,0x7C,0x7E,0x3E,0x9F,0xC5,0x4C,
|
||||
0x9F,0xEA,0x73,0xF5,0xD7,0x89,0xC0,0x4C,0xF4,0xFB,0xF4,0x2D,0xEC,0x14,0x1B,0x51,
|
||||
0xD5,0xC1,0x12,0xC8,0x10,0xDF,0x0B,0x4A,0x8B,0x9C,0xBC,0x93,0x45,0x6A,0x3E,0x3E,
|
||||
0x7D,0xC1,0xA9,0xBA,0xCD,0xC1,0xB4,0x07,0xE4,0xE1,0x68,0x86,0x43,0xB2,0x6D,0x38,
|
||||
0xF3,0xFB,0x0C,0x5C,0x66,0x37,0x71,0xDE,0x56,0xEF,0x6E,0xA0,0x10,0x40,0x65,0xA7,
|
||||
0x98,0xF7,0xD0,0xBE,0x0E,0xC8,0x37,0x36,0xEC,0x10,0xCA,0x7C,0x9C,0xAB,0x84,0x1E,
|
||||
0x05,0x17,0x76,0x02,0x1C,0x4F,0x52,0xAA,0x5F,0xC1,0xC6,0xA0,0x56,0xB9,0xD8,0x04,
|
||||
0x84,0x44,0x4D,0xA7,0x59,0xD8,0xDE,0x60,0xE6,0x38,0x0E,0x05,0x8F,0x03,0xE1,0x3B,
|
||||
0x6D,0x81,0x04,0x33,0x6F,0x30,0x0B,0xCE,0x69,0x05,0x21,0x33,0xFB,0x26,0xBB,0x89,
|
||||
0x7D,0xB6,0xAE,0x87,0x7E,0x51,0x07,0xE0,0xAC,0xF7,0x96,0x0A,0x6B,0xF9,0xC4,0x5C,
|
||||
0x1D,0xE4,0x44,0x47,0xB8,0x5E,0xFA,0xE3,0x78,0x84,0x55,0x42,0x4B,0x48,0x5E,0xF7,
|
||||
0x7D,0x47,0x35,0x86,0x1D,0x2B,0x43,0x05,0x03,0xEC,0x8A,0xB8,0x1E,0x06,0x3C,0x76,
|
||||
0x0C,0x48,0x1A,0x43,0xA7,0xB7,0x8A,0xED,0x1E,0x13,0xC6,0x43,0xEE,0x10,0xEF,0xDB,
|
||||
0xEC,0xFB,0x3C,0x83,0xB2,0x95,0x44,0xEF,0xD8,0x54,0x51,0x4E,0x2D,0x11,0x44,0x1D,
|
||||
0xFB,0x36,0x59,0x1E,0x7A,0x34,0xC1,0xC3,0xCA,0x57,0x00,0x61,0xEA,0x67,0xA5,0x16,
|
||||
0x9B,0x55,0xD0,0x55,0xE1,0x7F,0xD9,0x36,0xD2,0x40,0x76,0xAE,0xDC,0x01,0xCE,0xB0,
|
||||
0x7A,0x83,0xD5,0xCB,0x20,0x98,0xEC,0x6B,0xC1,0x72,0x92,0x34,0xF3,0x82,0x57,0x37,
|
||||
0x62,0x8A,0x32,0x36,0x0C,0x90,0x43,0xAE,0xAE,0x5C,0x9B,0x78,0x8E,0x13,0x65,0x02,
|
||||
0xFD,0x68,0x71,0xC1,0xFE,0xB0,0x31,0xA0,0x24,0x82,0xB0,0xC3,0xB1,0x79,0x69,0xA7,
|
||||
0xF5,0xD2,0xEB,0xD0,0x82,0xC0,0x32,0xDC,0x9E,0xC7,0x26,0x3C,0x6D,0x8D,0x98,0xC1,
|
||||
0xBB,0x22,0xD4,0xD0,0x0F,0x33,0xEC,0x3E,0xB9,0xCC,0xE1,0xDC,0x6A,0x4C,0x77,0x36,
|
||||
0x14,0x1C,0xF9,0xBF,0x81,0x9F,0x28,0x5F,0x71,0x85,0x32,0x29,0x90,0x75,0x48,0xC4,
|
||||
0xB3,0x4A,0xCE,0xD8,0x44,0x8F,0x14,0x2F,0xFD,0x40,0x57,0xEF,0xAA,0x08,0x75,0xD9,
|
||||
0x46,0xD1,0xD6,0x6E,0x32,0x55,0x1F,0xC3,0x18,0xFE,0x84,0x1F,0xFC,0x84,0xD5,0xFF,
|
||||
0x71,0x5E,0x1B,0x48,0xC3,0x86,0x95,0x0E,0x28,0x08,0x27,0xD3,0x38,0x83,0x71,0x7B,
|
||||
0x4C,0x80,0x63,0x54,0x9A,0x56,0xB0,0xAC,0xCF,0x80,0xCA,0x31,0x09,0xEF,0xFE,0xF3,
|
||||
0xBE,0xAF,0x24,0x7E,0xA6,0xFE,0x53,0x3F,0xC2,0x8D,0x4A,0x33,0x68,0xD1,0x22,0xA6,
|
||||
0x66,0xAD,0x7B,0xEA,0xDE,0xB6,0x43,0xB0,0xA1,0x25,0x95,0x00,0xA3,0x3F,0x75,0x46,
|
||||
0x14,0x11,0x44,0xEC,0xD7,0x95,0xBC,0x92,0xF0,0x4F,0xA9,0x16,0x53,0x62,0x97,0x60,
|
||||
0x2A,0x0F,0x41,0xF1,0x71,0x24,0xBE,0xEE,0x94,0x7F,0x08,0xCD,0x60,0x93,0xB3,0x85,
|
||||
0x5B,0x07,0x00,0x3F,0xD8,0x0F,0x28,0x83,0x9A,0xD1,0x69,0x9F,0xD1,0xDA,0x2E,0xC3,
|
||||
0x90,0x01,0xA2,0xB9,0x6B,0x4E,0x2A,0x66,0x9D,0xDA,0xAE,0xA6,0xEA,0x2A,0xD3,0x68,
|
||||
0x2F,0x0C,0x0C,0x9C,0xD2,0x8C,0x4A,0xED,0xE2,0x9E,0x57,0x65,0x9D,0x09,0x87,0xA3,
|
||||
0xB4,0xC4,0x32,0x5D,0xC9,0xD4,0x32,0x2B,0xB1,0xE0,0x71,0x1E,0x64,0x4D,0xE6,0x90,
|
||||
0x71,0xE3,0x1E,0x40,0xED,0x7D,0xF3,0x84,0x0E,0xED,0xC8,0x78,0x76,0xAE,0xC0,0x71,
|
||||
0x27,0x72,0xBB,0x05,0xEA,0x02,0x64,0xFB,0xF3,0x48,0x6B,0xB5,0x42,0x93,0x3F,0xED,
|
||||
0x9F,0x13,0x53,0xD2,0xF7,0xFE,0x2A,0xEC,0x1D,0x47,0x25,0xDB,0x3C,0x91,0x86,0xC6,
|
||||
0x8E,0xF0,0x11,0xFD,0x23,0x74,0x36,0xF7,0xA4,0xF5,0x9E,0x7A,0x7E,0x53,0x50,0x44,
|
||||
0xD4,0x47,0xCA,0xD3,0xEB,0x38,0x6D,0xE6,0xD9,0x71,0x94,0x7F,0x4A,0xC6,0x69,0x4B,
|
||||
0x11,0xF4,0x52,0xEA,0x22,0xFE,0x8A,0xB0,0x36,0x67,0x8B,0x59,0xE8,0xE6,0x80,0x2A,
|
||||
0xEB,0x65,0x04,0x13,0xEE,0xEC,0xDC,0x9E,0x5F,0xB1,0xEC,0x05,0x6A,0x59,0xE6,0x9F,
|
||||
0x5E,0x59,0x6B,0x89,0xBF,0xF7,0x1A,0xCA,0x44,0xF9,0x5B,0x6A,0x71,0x85,0x03,0xE4,
|
||||
0x29,0x62,0xE0,0x70,0x6F,0x41,0xC4,0xCF,0xB2,0xB1,0xCC,0xE3,0x7E,0xA6,0x07,0xA8,
|
||||
0x87,0xE7,0x7F,0x84,0x93,0xDB,0x52,0x4B,0x6C,0xEC,0x7E,0xDD,0xD4,0x24,0x48,0x10,
|
||||
0x69,0x9F,0x04,0x60,0x74,0xE6,0x48,0x18,0xF3,0xE4,0x2C,0xB9,0x4F,0x2E,0x50,0x7A,
|
||||
0xDF,0xD4,0x54,0x69,0x2B,0x8B,0xA7,0xF3,0xCE,0xFF,0x1F,0xF3,0x3E,0x26,0x01,0x39,
|
||||
0x17,0x95,0x84,0x89,0xB0,0xF0,0x4C,0x4B,0x82,0x91,0x9F,0xC4,0x4B,0xAC,0x9D,0xA5,
|
||||
0x74,0xAF,0x17,0x25,0xC9,0xCA,0x32,0xD3,0xBC,0x89,0x8A,0x84,0x89,0xCC,0x0D,0xAE,
|
||||
0x7C,0xA2,0xDB,0x9C,0x6A,0x78,0x91,0xEE,0xEA,0x76,0x5D,0x4E,0x87,0x60,0xF5,0x69,
|
||||
0x15,0x67,0xD4,0x02,0xCF,0xAF,0x48,0x36,0x07,0xEA,0xBF,0x6F,0x66,0x2D,0x06,0x8F,
|
||||
0xC4,0x9A,0xFE,0xF9,0xF6,0x90,0x87,0x75,0xB8,0xF7,0xAD,0x0F,0x76,0x10,0x5A,0x3D,
|
||||
0x59,0xB0,0x2E,0xB3,0xC7,0x35,0x2C,0xCC,0x70,0x56,0x2B,0xCB,0xE3,0x37,0x96,0xC5,
|
||||
0x2F,0x46,0x1B,0x8A,0x22,0x46,0xC7,0x88,0xA7,0x26,0x32,0x98,0x61,0xDF,0x86,0x22,
|
||||
0x8A,0xF4,0x1C,0x2F,0x87,0xA1,0x09,0xAA,0xCC,0xA9,0xAE,0xD3,0xBD,0x00,0x45,0x1C,
|
||||
0x9A,0x54,0x87,0x86,0x52,0x87,0xEF,0xFF,0x1E,0x8F,0xA1,0x8F,0xC1,0x89,0x5C,0x35,
|
||||
0x1B,0xDA,0x2D,0x3A,0x2C,0x16,0xB2,0xC2,0xF1,0x56,0xE2,0x78,0xC1,0x6B,0x63,0x97,
|
||||
0xC5,0x56,0x8F,0xC9,0x32,0x7F,0x2C,0xAA,0xAF,0xA6,0xA8,0xAC,0x20,0x91,0x22,0x88,
|
||||
0xDE,0xE4,0x60,0x8B,0xF9,0x4B,0x42,0x25,0x1A,0xE3,0x7F,0x9C,0x2C,0x19,0x89,0x3A,
|
||||
0x7E,0x05,0xD4,0x36,0xCC,0x69,0x58,0xC2,0xC1,0x32,0x8B,0x2F,0x90,0x85,0xEB,0x7A,
|
||||
0x39,0x50,0xA5,0xA1,0x27,0x92,0xC5,0x66,0xB0,0x20,0x4F,0x58,0x7E,0x55,0x83,0x43,
|
||||
0x2B,0x45,0xE2,0x9C,0xE4,0xD8,0x12,0x90,0x2C,0x16,0x83,0x56,0x16,0x79,0x03,0xB3,
|
||||
0xAD,0x2D,0x61,0x18,0x1A,0x13,0x1F,0x37,0xE2,0xE1,0x9C,0x73,0x7B,0x80,0xD5,0xFD,
|
||||
0x2D,0x51,0x87,0xFC,0x7B,0xAA,0xD7,0x1F,0x2C,0x7A,0x8E,0xAF,0xF4,0x8D,0xBB,0xCD,
|
||||
0x95,0x11,0x7C,0x72,0x0B,0xEE,0x6F,0xE2,0xB9,0xAF,0xDE,0x37,0x83,0xDE,0x8C,0x8D,
|
||||
0x62,0x05,0x67,0xB7,0x96,0xC6,0x8D,0x56,0xB6,0x0D,0xD7,0x62,0xBA,0xD6,0x46,0x36,
|
||||
0xBD,0x8E,0xC8,0xE6,0xEA,0x2A,0x6C,0x10,0x14,0xFF,0x6B,0x5B,0xFA,0x82,0x3C,0x46,
|
||||
0xB1,0x30,0x43,0x46,0x51,0x8A,0x7D,0x9B,0x92,0x3E,0x83,0x79,0x5B,0x55,0x5D,0xB2,
|
||||
0x6C,0x5E,0xCE,0x90,0x62,0x8E,0x53,0x98,0xC9,0x0D,0x6D,0xE5,0x2D,0x57,0xCD,0xC5,
|
||||
0x81,0x57,0xBA,0xE1,0xE8,0xB8,0x8F,0x72,0xE5,0x4F,0x13,0xDC,0xEA,0x9D,0x71,0x15,
|
||||
0x10,0xB2,0x11,0x88,0xD5,0x09,0xD4,0x7F,0x5B,0x65,0x7F,0x2C,0x3B,0x38,0x4C,0x11,
|
||||
0x68,0x50,0x8D,0xFB,0x9E,0xB0,0x59,0xBF,0x94,0x80,0x89,0x4A,0xC5,0x1A,0x18,0x12,
|
||||
0x89,0x53,0xD1,0x4A,0x10,0x29,0xE8,0x8C,0x1C,0xEC,0xB6,0xEA,0x46,0xC7,0x17,0x8B,
|
||||
0x25,0x15,0x31,0xA8,0xA2,0x6B,0x43,0xB1,0x9D,0xE2,0xDB,0x0B,0x87,0x9B,0xB0,0x11,
|
||||
0x04,0x0E,0x71,0xD2,0x29,0x77,0x89,0x82,0x0A,0x66,0x41,0x7F,0x1D,0x0B,0x48,0xFF,
|
||||
0x72,0xBB,0x24,0xFD,0xC2,0x48,0xA1,0x9B,0xFE,0x7B,0x7F,0xCE,0x88,0xDB,0x86,0xD9,
|
||||
0x85,0x3B,0x1C,0xB0,0xDC,0xA8,0x33,0x07,0xBF,0x51,0x2E,0xE3,0x0E,0x9A,0x00,0x97,
|
||||
0x1E,0x06,0xC0,0x97,0x43,0x9D,0xD8,0xB6,0x45,0xC4,0x86,0x67,0x5F,0x00,0xF8,0x88,
|
||||
0x9A,0xA4,0x52,0x9E,0xC7,0xAA,0x8A,0x83,0x75,0xEC,0xC5,0x18,0xAE,0xCE,0xC3,0x2F,
|
||||
0x1A,0x2B,0xF9,0x18,0xFF,0xAE,0x1A,0xF5,0x53,0x0B,0xB5,0x33,0x51,0xA7,0xFD,0xE8,
|
||||
0xA8,0xE1,0xA2,0x64,0xB6,0x22,0x17,0x43,0x80,0xCC,0x0A,0xD8,0xAE,0x3B,0xBA,0x40,
|
||||
0xD7,0xD9,0x92,0x4A,0x89,0xDF,0x04,0x10,0xEE,0x9B,0x18,0x2B,0x6A,0x77,0x69,0x8A,
|
||||
0x68,0xF4,0xF9,0xB9,0xA2,0x21,0x15,0x6E,0xE6,0x1E,0x3B,0x03,0x62,0x30,0x9B,0x60,
|
||||
0x41,0x7E,0x25,0x9B,0x9E,0x8F,0xC5,0x52,0x10,0x08,0xF8,0xC2,0x69,0xA1,0x21,0x11,
|
||||
0x88,0x37,0x5E,0x79,0x35,0x66,0xFF,0x10,0x42,0x18,0x6E,0xED,0x97,0xB6,0x6B,0x1C,
|
||||
0x4E,0x36,0xE5,0x6D,0x7D,0xB4,0xE4,0xBF,0x20,0xB9,0xE0,0x05,0x3A,0x69,0xD5,0xB8,
|
||||
0xE3,0xD5,0xDC,0xE0,0xB9,0xAC,0x53,0x3E,0x07,0xA4,0x57,0xAD,0x77,0xFF,0x48,0x18,
|
||||
0x76,0x2A,0xAC,0x49,0x2A,0x8E,0x47,0x75,0x6D,0x9F,0x67,0x63,0x30,0x35,0x8C,0x39,
|
||||
0x05,0x39,0xD5,0x6F,0x64,0x3A,0x5B,0xAD,0xCA,0x0B,0xBB,0x82,0x52,0x99,0x45,0xB1,
|
||||
0x93,0x36,0x36,0x99,0xAF,0x13,0x20,0x44,0x36,0xD8,0x02,0x44,0x09,0x39,0x92,0x85,
|
||||
0xFF,0x4A,0x4A,0x97,0x87,0xA6,0x63,0xD7,0xC7,0xB5,0xB5,0x24,0xED,0x0F,0xB4,0x6F,
|
||||
0x0C,0x58,0x52,0x14,0xD9,0xA6,0x7B,0xD3,0x79,0xBC,0x38,0x58,0xA1,0xBD,0x3B,0x84,
|
||||
0x06,0xD8,0x1A,0x06,0xFD,0x6B,0xA8,0xEA,0x4B,0x69,0x28,0x04,0x37,0xAD,0x82,0x99,
|
||||
0xFB,0x0E,0x1B,0x85,0xBD,0xA8,0x5D,0x73,0xCD,0xDC,0x58,0x75,0x0A,0xBE,0x63,0x6C,
|
||||
0x48,0xE7,0x4C,0xE4,0x30,0x2B,0x04,0x60,0xB9,0x15,0xD8,0xDA,0x86,0x81,0x75,0x8F,
|
||||
0x96,0xD4,0x8D,0x1C,0x5D,0x70,0x85,0x7C,0x1C,0x67,0x7B,0xD5,0x08,0x67,0xA6,0xCE,
|
||||
0x4B,0x0A,0x66,0x70,0xB7,0xE5,0x63,0xD4,0x5B,0x8A,0x82,0xEA,0x10,0x67,0xCA,0xE2,
|
||||
0xF4,0xEF,0x17,0x85,0x2F,0x2A,0x5F,0x8A,0x97,0x82,0xF8,0x6A,0xD6,0x34,0x10,0xEA,
|
||||
0xEB,0xC9,0x5C,0x3C,0xE1,0x49,0xF8,0x46,0xEB,0xDE,0xBD,0xF6,0xA9,0x92,0xF1,0xAA,
|
||||
0xA6,0xA0,0x18,0xB0,0x3A,0xD3,0x0F,0x1F,0xF3,0x6F,0xFF,0x31,0x45,0x43,0x44,0xD3,
|
||||
0x50,0x9A,0xF7,0x88,0x09,0x96,0xC1,0xCE,0x76,0xCC,0xF2,0x2C,0x2C,0xBA,0xAD,0x82,
|
||||
0x77,0x8F,0x18,0x84,0xC0,0xD2,0x07,0x9C,0x36,0x90,0x83,0x4E,0x0B,0xA5,0x4F,0x43,
|
||||
0x3E,0x04,0xAB,0x78,0x4F,0xD6,0xFB,0x09,0x01,0x24,0x90,0xDA,0x6F,0x3C,0x3A,0x61,
|
||||
0x0D,0x7F,0x69,0x4A,0xEB,0x2B,0x30,0x02,0xB4,0xDB,0xE0,0x84,0xA9,0xEC,0xD7,0x35,
|
||||
0xBF,0x37,0x7D,0x85,0x58,0xCE,0xA9,0x4E,0xE4,0x80,0xC7,0xA8,0xD3,0x30,0x67,0x48,
|
||||
0xEB,0x29,0xAF,0x2F,0x74,0x6A,0xB4,0xA7,0x3F,0x0F,0x3F,0x92,0xAF,0xF3,0xCA,0xAC,
|
||||
0xAF,0x4B,0xD9,0x94,0xC0,0x43,0xCA,0x81,0x0D,0x2F,0x48,0xA1,0xB0,0x27,0xD5,0xD2,
|
||||
0xEF,0x4B,0x05,0x85,0xA3,0xDE,0x4D,0x93,0x30,0x3C,0xF0,0xBB,0x4A,0x8F,0x30,0x27,
|
||||
0x4C,0xEB,0xE3,0x3E,0x64,0xED,0x9A,0x2F,0x3B,0xF1,0x82,0xF0,0xBA,0xF4,0xCF,0x7F,
|
||||
0x40,0xCB,0xB0,0xE1,0x7F,0xBC,0xAA,0x57,0xD3,0xC9,0x74,0xF2,0xFA,0x43,0x0D,0x22,
|
||||
0xD0,0xF4,0x77,0x4E,0x93,0xD7,0x85,0x70,0x1F,0x99,0xBF,0xB6,0xDE,0x35,0xF1,0x30,
|
||||
0xA7,0x5E,0x71,0xF0,0x6B,0x01,0x2D,0x7B,0x64,0xF0,0x33,0x53,0x0A,0x39,0x88,0xF3,
|
||||
0x6B,0x3A,0xA6,0x6B,0x35,0xD2,0x2F,0x43,0xCD,0x02,0xFD,0xB5,0xE9,0xBC,0x5B,0xAA,
|
||||
0xD8,0xA4,0x19,0x7E,0x0E,0x5D,0x94,0x81,0x9E,0x6F,0x77,0xAD,0xD6,0x0E,0x74,0x93,
|
||||
0x96,0xE7,0xC4,0x18,0x5F,0xAD,0xF5,0x19,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Encryption process variables
|
||||
|
||||
private uint[] _cardHash = new uint[0x412];
|
||||
@@ -284,6 +16,16 @@ namespace NDecrypt.Core
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decryption args to use while processing
|
||||
/// </summary>
|
||||
private readonly DecryptArgs _decryptArgs;
|
||||
|
||||
public DSTool(DecryptArgs decryptArgs)
|
||||
{
|
||||
_decryptArgs = decryptArgs;
|
||||
}
|
||||
|
||||
#region Encrypt
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -296,8 +38,8 @@ namespace NDecrypt.Core
|
||||
using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// Deserialize the cart information
|
||||
var cart = NitroDeserializer.DeserializeStream(reader);
|
||||
if (cart == null)
|
||||
var commonHeader = Nitro.ParseCommonHeader(reader);
|
||||
if (commonHeader == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a DS or DSi Rom!");
|
||||
return false;
|
||||
@@ -308,7 +50,7 @@ namespace NDecrypt.Core
|
||||
_arg2 = new uint[3];
|
||||
|
||||
// Encrypt the secure area
|
||||
EncryptSecureArea(cart, force, reader, writer);
|
||||
EncryptSecureArea(commonHeader, force, reader, writer);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@@ -322,11 +64,11 @@ namespace NDecrypt.Core
|
||||
/// <summary>
|
||||
/// Encrypt secure area in the DS/DSi file
|
||||
/// </summary>s
|
||||
/// <param name="cart">Cart representing the DS file</param>
|
||||
/// <param name="commonHeader">CommonHeader representing the DS file header</param>
|
||||
/// <param name="force">Indicates if the operation should be forced</param>
|
||||
/// <param name="reader">Stream representing the input</param>
|
||||
/// <param name="writer">Stream representing the output</param>
|
||||
private void EncryptSecureArea(Cart cart, bool force, Stream reader, Stream writer)
|
||||
private void EncryptSecureArea(CommonHeader commonHeader, bool force, Stream reader, Stream writer)
|
||||
{
|
||||
// If we're forcing the operation, tell the user
|
||||
if (force)
|
||||
@@ -349,7 +91,7 @@ namespace NDecrypt.Core
|
||||
}
|
||||
}
|
||||
|
||||
EncryptARM9(cart.CommonHeader!, reader, writer);
|
||||
EncryptARM9(commonHeader, reader, writer);
|
||||
Console.WriteLine("File has been encrypted");
|
||||
}
|
||||
|
||||
@@ -450,8 +192,8 @@ namespace NDecrypt.Core
|
||||
using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// Deserialize the cart information
|
||||
var cart = NitroDeserializer.DeserializeStream(reader);
|
||||
if (cart == null)
|
||||
var commonHeader = Nitro.ParseCommonHeader(reader);
|
||||
if (commonHeader == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a DS or DSi Rom!");
|
||||
return false;
|
||||
@@ -462,7 +204,7 @@ namespace NDecrypt.Core
|
||||
_arg2 = new uint[3];
|
||||
|
||||
// Decrypt the secure area
|
||||
DecryptSecureArea(cart, force, reader, writer);
|
||||
DecryptSecureArea(commonHeader, force, reader, writer);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -477,11 +219,11 @@ namespace NDecrypt.Core
|
||||
/// <summary>
|
||||
/// Decrypt secure area in the DS/DSi file
|
||||
/// </summary>s
|
||||
/// <param name="cart">Cart representing the DS file</param>
|
||||
/// <param name="commonHeader">CommonHeader representing the DS file header</param>
|
||||
/// <param name="force">Indicates if the operation should be forced</param>
|
||||
/// <param name="reader">Stream representing the input</param>
|
||||
/// <param name="writer">Stream representing the output</param>
|
||||
private void DecryptSecureArea(Cart cart, bool force, Stream reader, Stream writer)
|
||||
private void DecryptSecureArea(CommonHeader commonHeader, bool force, Stream reader, Stream writer)
|
||||
{
|
||||
// If we're forcing the operation, tell the user
|
||||
if (force)
|
||||
@@ -504,7 +246,7 @@ namespace NDecrypt.Core
|
||||
}
|
||||
}
|
||||
|
||||
DecryptARM9(cart.CommonHeader!, reader, writer);
|
||||
DecryptARM9(commonHeader, reader, writer);
|
||||
Console.WriteLine("File has been decrypted");
|
||||
}
|
||||
|
||||
@@ -585,6 +327,42 @@ namespace NDecrypt.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Info
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? GetInformation(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open the file for reading
|
||||
using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Get a string builder for the status
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("\tSecure Area: ");
|
||||
|
||||
// Get the encryption status
|
||||
bool? decrypted = CheckIfDecrypted(input);
|
||||
if (decrypted == null)
|
||||
sb.Append("Empty");
|
||||
else if (decrypted == true)
|
||||
sb.Append("Decrypted");
|
||||
else
|
||||
sb.Append("Encrypted");
|
||||
|
||||
// Return the status for the secure area
|
||||
sb.Append(Environment.NewLine);
|
||||
return sb.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common
|
||||
|
||||
/// <summary>
|
||||
@@ -642,6 +420,38 @@ namespace NDecrypt.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strange, unlicenced values that can't determine decryption state
|
||||
else if ((firstValue == 0xE1D830D8 && secondValue == 0xE3530000) // Aquela Ball (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xDC002A02 && secondValue == 0x2900E612) // Bahlz (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE1A03BA3 && secondValue == 0xE2011CFF) // Battle Ship (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE3A01001 && secondValue == 0xE1A02001) // Breakout!! DS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE793200C && secondValue == 0xE4812004) // Bubble Fusion (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE583C0DC && secondValue == 0x0A00000B) // Carre Rouge (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x0202453C && secondValue == 0x02060164) // ChainReaction (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xEBFFF218 && secondValue == 0xE31000FF) // Collection (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x4A6CD003 && secondValue == 0x425B2301) // DiggerDS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE3A00001 && secondValue == 0xEBFFFF8C) // Double Skill (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x21043701 && secondValue == 0x45BA448C) // DSChess (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE59D0010 && secondValue == 0xE0833000) // Hexa-Virus (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE5C3A006 && secondValue == 0xE5C39007) // Invasion (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE1D920F4 && secondValue == 0xE06A3000) // JoggleDS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE59F32EC && secondValue == 0xE5DD7011) // London Underground (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE08A3503 && secondValue == 0xE1D3C4B8) // NumberMinds (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE1A0C001 && secondValue == 0xE0031001) // Paddle Battle (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE1A03005 && secondValue == 0xE88D0180) // Pop the Balls (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE8BD4030 && secondValue == 0xE12FFF1E) // Solitaire DS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE0A88006 && secondValue == 0xE1A00003) // Squash DS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE51F3478 && secondValue == 0xEB004A02) // Super Snake DS (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x1C200052 && secondValue == 0xFD12F013) // Tales of Dagur (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x601F491E && secondValue == 0x041B880B) // Tetris & Touch (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE1A03843 && secondValue == 0xE0000293) // Tic Tac Toe (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0xE3530000 && secondValue == 0x13A03003) // Warrior Training (World) (Unl) (Datel Games n' Music)
|
||||
|| (firstValue == 0x02054A80 && secondValue == 0x02054B80)) // Zi (World) (Unl) (Datel Games n' Music)
|
||||
{
|
||||
Console.WriteLine("Unlicensed invalid value found. Unknown if encrypted or decrypted.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Standard decryption values
|
||||
return firstValue == 0xE7FFDEFF && secondValue == 0xE7FFDEFF;
|
||||
}
|
||||
@@ -652,7 +462,7 @@ namespace NDecrypt.Core
|
||||
/// <param name="commonHeader">CommonHeader representing the DS file</param>
|
||||
private void Init1(CommonHeader commonHeader)
|
||||
{
|
||||
Buffer.BlockCopy(NitroEncryptionData, 0, _cardHash, 0, 4 * (1024 + 18));
|
||||
Buffer.BlockCopy(_decryptArgs.NitroEncryptionData, 0, _cardHash, 0, 4 * (1024 + 18));
|
||||
_arg2 = [commonHeader.GameCode, commonHeader.GameCode >> 1, commonHeader.GameCode << 1];
|
||||
Init2();
|
||||
Init2();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.IO.Readers;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace NDecrypt.Core
|
||||
{
|
||||
@@ -16,6 +17,282 @@ namespace NDecrypt.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// Blowfish Table
|
||||
/// </summary>
|
||||
public byte[] NitroEncryptionData { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Encryption data taken from woodsec
|
||||
/// </summary>
|
||||
private static readonly byte[] _nitroEncryptionData = [
|
||||
0x99,0xD5,0x20,0x5F,0x57,0x44,0xF5,0xB9,0x6E,0x19,0xA4,0xD9,0x9E,0x6A,0x5A,0x94,
|
||||
0xD8,0xAE,0xF1,0xEB,0x41,0x75,0xE2,0x3A,0x93,0x82,0xD0,0x32,0x33,0xEE,0x31,0xD5,
|
||||
0xCC,0x57,0x61,0x9A,0x37,0x06,0xA2,0x1B,0x79,0x39,0x72,0xF5,0x55,0xAE,0xF6,0xBE,
|
||||
0x5F,0x1B,0x69,0xFB,0xE5,0x9D,0xF1,0xE9,0xCE,0x2C,0xD9,0xA1,0x5E,0x32,0x05,0xE6,
|
||||
0xFE,0xD3,0xFE,0xCF,0xD4,0x62,0x04,0x0D,0x8B,0xF5,0xEC,0xB7,0x2B,0x60,0x79,0xBB,
|
||||
0x12,0x95,0x31,0x0D,0x6E,0x3F,0xDA,0x2B,0x88,0x84,0xF0,0xF1,0x3D,0x12,0x7E,0x25,
|
||||
0x45,0x22,0xF1,0xBB,0x24,0x06,0x1A,0x06,0x11,0xAD,0xDF,0x28,0x8B,0x64,0x81,0x34,
|
||||
0x2B,0xEB,0x33,0x29,0x99,0xAA,0xF2,0xBD,0x9C,0x14,0x95,0x9D,0x9F,0xF7,0xF5,0x8C,
|
||||
0x72,0x97,0xA1,0x29,0x9D,0xD1,0x5F,0xCF,0x66,0x4D,0x07,0x1A,0xDE,0xD3,0x4A,0x4B,
|
||||
0x85,0xC9,0xA7,0xA3,0x17,0x95,0x05,0x3A,0x3D,0x49,0x0A,0xBF,0x0A,0x89,0x8B,0xA2,
|
||||
0x4A,0x82,0x49,0xDD,0x27,0x90,0xF1,0x0B,0xE9,0xEB,0x1C,0x6A,0x83,0x76,0x45,0x05,
|
||||
0xBA,0x81,0x70,0x61,0x17,0x3F,0x4B,0xDE,0xAE,0xCF,0xAB,0x39,0x57,0xF2,0x3A,0x56,
|
||||
0x48,0x11,0xAD,0x8A,0x40,0xE1,0x45,0x3F,0xFA,0x9B,0x02,0x54,0xCA,0xA6,0x93,0xFB,
|
||||
0xEF,0x4D,0xFE,0x6F,0xA3,0xD8,0x87,0x9C,0x08,0xBA,0xD5,0x48,0x6A,0x8D,0x2D,0xFD,
|
||||
0x6E,0x15,0xF8,0x74,0xBD,0xBE,0x52,0x8B,0x18,0x22,0x8A,0x9E,0xFB,0x74,0x37,0x07,
|
||||
0x1B,0x36,0x6C,0x4A,0x19,0xBA,0x42,0x62,0xB9,0x79,0x91,0x10,0x7B,0x67,0x65,0x96,
|
||||
0xFE,0x02,0x23,0xE8,0xEE,0x99,0x8C,0x77,0x3E,0x5C,0x86,0x64,0x4D,0x6D,0x78,0x86,
|
||||
0xA5,0x4F,0x65,0xE2,0x1E,0xB2,0xDF,0x5A,0x0A,0xD0,0x7E,0x08,0x14,0xB0,0x71,0xAC,
|
||||
0xBD,0xDB,0x83,0x1C,0xB9,0xD7,0xA1,0x62,0xCD,0xC6,0x63,0x7C,0x52,0x69,0xC3,0xE6,
|
||||
0xBF,0x75,0xCE,0x12,0x44,0x5D,0x21,0x04,0xFA,0xFB,0xD3,0x3C,0x38,0x11,0x63,0xD4,
|
||||
0x95,0x85,0x41,0x49,0x46,0x09,0xF2,0x08,0x43,0x11,0xDC,0x1F,0x76,0xC0,0x15,0x6D,
|
||||
0x1F,0x3C,0x63,0x70,0xEA,0x87,0x80,0x6C,0xC3,0xBD,0x63,0x8B,0xC2,0x37,0x21,0x37,
|
||||
0xDC,0xEE,0x09,0x23,0x2E,0x37,0x6A,0x4D,0x73,0x90,0xF7,0x50,0x30,0xAC,0x1C,0x92,
|
||||
0x04,0x10,0x23,0x91,0x4F,0xD2,0x07,0xAA,0x68,0x3E,0x4F,0x9A,0xC9,0x64,0x60,0x6A,
|
||||
0xC8,0x14,0x21,0xF3,0xD6,0x22,0x41,0x12,0x44,0x24,0xCF,0xE6,0x8A,0x56,0xDD,0x0D,
|
||||
0x53,0x4D,0xE1,0x85,0x1E,0x8C,0x52,0x5A,0x9C,0x19,0x84,0xC2,0x03,0x57,0xF1,0x6F,
|
||||
0xE3,0x00,0xBE,0x58,0xF6,0x4C,0xED,0xD5,0x21,0x64,0x9C,0x1F,0xBE,0x55,0x03,0x3C,
|
||||
0x4A,0xDC,0xFF,0xAA,0xC9,0xDA,0xE0,0x5D,0x5E,0xBF,0xE6,0xDE,0xF5,0xD8,0xB1,0xF8,
|
||||
0xFF,0x36,0xB3,0xB9,0x62,0x67,0x95,0xDB,0x31,0x5F,0x37,0xED,0x4C,0x70,0x67,0x99,
|
||||
0x90,0xB5,0x18,0x31,0x6C,0x3D,0x99,0x99,0xE4,0x42,0xDA,0xD3,0x25,0x42,0x13,0xA0,
|
||||
0xAE,0xD7,0x70,0x6C,0xB1,0x55,0xCF,0xC7,0xD7,0x46,0xD5,0x43,0x61,0x17,0x3D,0x44,
|
||||
0x28,0xE9,0x33,0x85,0xD5,0xD0,0xA2,0x93,0xAA,0x25,0x12,0x1F,0xFB,0xC5,0x0B,0x46,
|
||||
0xF5,0x97,0x76,0x56,0x45,0xA6,0xBE,0x87,0xB1,0x94,0x6B,0xE8,0xB1,0xFE,0x33,0x99,
|
||||
0xAE,0x1F,0x3E,0x6C,0x39,0x71,0x1D,0x09,0x00,0x90,0x37,0xE4,0x10,0x3E,0x75,0x74,
|
||||
0xFF,0x8C,0x83,0x3B,0xB0,0xF1,0xB0,0xF9,0x01,0x05,0x47,0x42,0x95,0xF1,0xD6,0xAC,
|
||||
0x7E,0x38,0xE6,0x9E,0x95,0x74,0x26,0x3F,0xB4,0x68,0x50,0x18,0xD0,0x43,0x30,0xB4,
|
||||
0x4C,0x4B,0xE3,0x68,0xBF,0xE5,0x4D,0xB6,0x95,0x8B,0x0A,0xA0,0x74,0x25,0x32,0x77,
|
||||
0xCF,0xA1,0xF7,0x2C,0xD8,0x71,0x13,0x5A,0xAB,0xEA,0xC9,0x51,0xE8,0x0D,0xEE,0xEF,
|
||||
0xE9,0x93,0x7E,0x19,0xA7,0x1E,0x43,0x38,0x81,0x16,0x2C,0xA1,0x48,0xE3,0x73,0xCC,
|
||||
0x29,0x21,0x6C,0xD3,0x5D,0xCE,0xA0,0xD9,0x61,0x71,0x43,0xA0,0x15,0x13,0xB5,0x64,
|
||||
0x92,0xCF,0x2A,0x19,0xDC,0xAD,0xB7,0xA5,0x9F,0x86,0x65,0xF8,0x1A,0x9F,0xE7,0xFB,
|
||||
0xF7,0xFD,0xB8,0x13,0x6C,0x27,0xDB,0x6F,0xDF,0x35,0x1C,0xF7,0x8D,0x2C,0x5B,0x9B,
|
||||
0x12,0xAB,0x38,0x64,0x06,0xCC,0xDE,0x31,0xE8,0x4E,0x75,0x11,0x64,0xE3,0xFA,0xEA,
|
||||
0xEB,0x34,0x54,0xC2,0xAD,0x3F,0x34,0xEB,0x93,0x2C,0x7D,0x26,0x36,0x9D,0x56,0xF3,
|
||||
0x5A,0xE1,0xF6,0xB3,0x98,0x63,0x4A,0x9E,0x32,0x83,0xE4,0x9A,0x84,0x60,0x7D,0x90,
|
||||
0x2E,0x13,0x0E,0xEE,0x93,0x4B,0x36,0xA2,0x85,0xEC,0x16,0x38,0xE8,0x88,0x06,0x02,
|
||||
0xBF,0xF0,0xA0,0x3A,0xED,0xD7,0x6A,0x9A,0x73,0xE1,0x57,0xCF,0xF8,0x44,0xB8,0xDC,
|
||||
0x2E,0x23,0x59,0xD1,0xDF,0x95,0x52,0x71,0x99,0x61,0xA0,0x4B,0xD5,0x7F,0x6E,0x78,
|
||||
0xBA,0xA9,0xC5,0x30,0xD3,0x40,0x86,0x32,0x9D,0x32,0x0C,0x9C,0x37,0xB7,0x02,0x2F,
|
||||
0xBA,0x54,0x98,0xA9,0xC4,0x13,0x04,0xC9,0x8D,0xBE,0xC8,0xE7,0x5D,0x97,0x50,0x2E,
|
||||
0x93,0xD6,0x22,0x59,0x0C,0x27,0xBC,0x22,0x92,0xE0,0xA7,0x20,0x0F,0x93,0x6F,0x7F,
|
||||
0x4C,0x9F,0xD3,0xB5,0xA6,0x2A,0x0B,0x74,0x67,0x49,0x7D,0x10,0x26,0xCB,0xD1,0xC5,
|
||||
0x86,0x71,0xE7,0x8C,0xA0,0x9C,0xE9,0x5B,0xB2,0x1A,0xF6,0x01,0xEE,0x8C,0x9E,0x5E,
|
||||
0x83,0xF2,0x1A,0xDB,0xE6,0xE5,0xEA,0x84,0x59,0x76,0xD2,0x7C,0xF6,0x8D,0xA5,0x49,
|
||||
0x36,0x48,0xC2,0x16,0x52,0xBB,0x83,0xA3,0x74,0xB9,0x07,0x0C,0x3B,0xFF,0x61,0x28,
|
||||
0xE1,0x61,0xE9,0xE4,0xEF,0x6E,0x15,0xAA,0x4E,0xBA,0xE8,0x5D,0x05,0x96,0xBB,0x32,
|
||||
0x56,0xB0,0xFB,0x72,0x52,0x0F,0x0E,0xC8,0x42,0x25,0x65,0x76,0x89,0xAF,0xF2,0xDE,
|
||||
0x10,0x27,0xF0,0x01,0x4B,0x74,0xA7,0x97,0x07,0xD5,0x26,0x54,0x54,0x09,0x1F,0x82,
|
||||
0x0A,0x86,0x7D,0x30,0x39,0x0E,0xB3,0x26,0x9B,0x0B,0x57,0xBB,0x36,0x06,0x31,0xAF,
|
||||
0xFD,0x79,0xFC,0xD9,0x30,0x10,0x2B,0x0C,0xB3,0xE1,0x9B,0xD7,0x7B,0xDC,0x5F,0xEF,
|
||||
0xD2,0xF8,0x13,0x45,0x4D,0x47,0x75,0xBD,0x46,0x96,0x3C,0x7E,0x75,0xF3,0x3E,0xB5,
|
||||
0x67,0xC5,0x9A,0x3B,0xB0,0x5B,0x29,0x6B,0xDE,0x80,0x5B,0xC8,0x15,0x05,0xB1,0x31,
|
||||
0xB6,0xCE,0x49,0xDD,0xAD,0x84,0xB5,0xAE,0x60,0xDC,0x67,0x31,0x34,0x30,0xFE,0x4E,
|
||||
0xBD,0x80,0x2F,0xA6,0xBF,0x63,0x39,0x21,0x86,0xD9,0x35,0x7F,0x16,0x68,0x22,0x05,
|
||||
0x54,0xE9,0x90,0x26,0x8C,0x07,0x6C,0x51,0xA4,0x31,0x55,0xD7,0x09,0x07,0xA8,0x3E,
|
||||
0x2E,0x53,0x66,0xC1,0xF8,0xF2,0x7B,0xC4,0xF2,0x58,0xCF,0xF1,0x87,0xC5,0xA2,0xE7,
|
||||
0x27,0x8F,0x30,0x87,0x58,0xA0,0x64,0x62,0x23,0x18,0xB9,0x88,0x7C,0xFA,0xCE,0xC4,
|
||||
0x98,0xAE,0xAD,0x17,0xCC,0x4A,0x5B,0xF3,0xE9,0x48,0xD5,0x56,0xD3,0x0D,0xF2,0xC8,
|
||||
0x92,0x73,0x8C,0xDB,0xD7,0x2F,0x56,0xAC,0x81,0xF9,0x92,0x69,0x4D,0xC6,0x32,0xF6,
|
||||
0xE6,0xC0,0x8D,0x21,0xE2,0x76,0x80,0x61,0x11,0xBC,0xDC,0x6C,0x93,0xAF,0x19,0x69,
|
||||
0x9B,0xD0,0xBF,0xB9,0x31,0x9F,0x02,0x67,0xA3,0x51,0xEE,0x83,0x06,0x22,0x7B,0x0C,
|
||||
0xAB,0x49,0x42,0x40,0xB8,0xD5,0x01,0x7D,0xCE,0x5E,0xF7,0x55,0x53,0x39,0xC5,0x99,
|
||||
0x46,0xD8,0x87,0x9F,0xBA,0xF7,0x64,0xB4,0xE3,0x9A,0xFA,0xA1,0x6D,0x90,0x68,0x10,
|
||||
0x30,0xCA,0x8A,0x54,0xA7,0x9F,0x60,0xC3,0x19,0xF5,0x6B,0x0D,0x7A,0x51,0x98,0xE6,
|
||||
0x98,0x43,0x51,0xB4,0xD6,0x35,0xE9,0x4F,0xC3,0xDF,0x0F,0x7B,0xD6,0x2F,0x5C,0xBD,
|
||||
0x3A,0x15,0x61,0x19,0xF1,0x4B,0xCB,0xAA,0xDC,0x6D,0x64,0xC9,0xD3,0xC6,0x1E,0x56,
|
||||
0xEF,0x38,0x4C,0x50,0x71,0x86,0x75,0xCC,0x0D,0x0D,0x4E,0xE9,0x28,0xF6,0x06,0x5D,
|
||||
0x70,0x1B,0xAA,0xD3,0x45,0xCF,0xA8,0x39,0xAC,0x95,0xA6,0x2E,0xB4,0xE4,0x22,0xD4,
|
||||
0x74,0xA8,0x37,0x5F,0x48,0x7A,0x04,0xCC,0xA5,0x4C,0x40,0xD8,0x28,0xB4,0x28,0x08,
|
||||
0x0D,0x1C,0x72,0x52,0x41,0xF0,0x7D,0x47,0x19,0x3A,0x53,0x4E,0x58,0x84,0x62,0x6B,
|
||||
0x93,0xB5,0x8A,0x81,0x21,0x4E,0x0D,0xDC,0xB4,0x3F,0xA2,0xC6,0xFC,0xC9,0x2B,0x40,
|
||||
0xDA,0x38,0x04,0xE9,0x5E,0x5A,0x86,0x6B,0x0C,0x22,0x25,0x85,0x68,0x11,0x8D,0x7C,
|
||||
0x92,0x1D,0x95,0x55,0x4D,0xAB,0x8E,0xBB,0xDA,0xA6,0xE6,0xB7,0x51,0xB6,0x32,0x5A,
|
||||
0x05,0x41,0xDD,0x05,0x2A,0x0A,0x56,0x50,0x91,0x17,0x47,0xCC,0xC9,0xE6,0x7E,0xB5,
|
||||
0x61,0x4A,0xDB,0x73,0x67,0x51,0xC8,0x33,0xF5,0xDA,0x6E,0x74,0x2E,0x54,0xC3,0x37,
|
||||
0x0D,0x6D,0xAF,0x08,0xE8,0x15,0x8A,0x5F,0xE2,0x59,0x21,0xCD,0xA8,0xDE,0x0C,0x06,
|
||||
0x5A,0x77,0x6B,0x5F,0xDB,0x18,0x65,0x3E,0xC8,0x50,0xDE,0x78,0xE0,0xB8,0x82,0xB3,
|
||||
0x5D,0x4E,0x72,0x32,0x07,0x4F,0xC1,0x34,0x23,0xBA,0x96,0xB7,0x67,0x4E,0xA4,0x28,
|
||||
0x1E,0x34,0x62,0xEB,0x2D,0x6A,0x70,0xE9,0x2F,0x42,0xC4,0x70,0x4E,0x5A,0x31,0x9C,
|
||||
0xF9,0x5B,0x47,0x28,0xAA,0xDA,0x71,0x6F,0x38,0x1F,0xB3,0x78,0xC4,0x92,0x6B,0x1C,
|
||||
0x9E,0xF6,0x35,0x9A,0xB7,0x4D,0x0E,0xBF,0xCC,0x18,0x29,0x41,0x03,0x48,0x35,0x5D,
|
||||
0x55,0xD0,0x2B,0xC6,0x29,0xAF,0x5C,0x60,0x74,0x69,0x8E,0x5E,0x9B,0x7C,0xD4,0xBD,
|
||||
0x7B,0x44,0x64,0x7D,0x3F,0x92,0x5D,0x69,0xB6,0x1F,0x00,0x4B,0xD4,0x83,0x35,0xCF,
|
||||
0x7E,0x64,0x4E,0x17,0xAE,0x8D,0xD5,0x2E,0x9A,0x28,0x12,0x4E,0x2E,0x2B,0x49,0x08,
|
||||
0x5C,0xAE,0xC6,0x46,0x85,0xAE,0x41,0x61,0x1E,0x6F,0x82,0xD2,0x51,0x37,0x16,0x1F,
|
||||
0x0B,0xF6,0x59,0xA4,0x9A,0xCA,0x5A,0xAF,0x0D,0xD4,0x33,0x8B,0x20,0x63,0xF1,0x84,
|
||||
0x80,0x5C,0xCB,0xCF,0x08,0xB4,0xB9,0xD3,0x16,0x05,0xBD,0x62,0x83,0x31,0x9B,0x56,
|
||||
0x51,0x98,0x9F,0xBA,0xB2,0x5B,0xAA,0xB2,0x22,0x6B,0x2C,0xB5,0xD4,0x48,0xFA,0x63,
|
||||
0x2B,0x5F,0x58,0xFA,0x61,0xFA,0x64,0x09,0xBB,0x38,0xE0,0xB8,0x9D,0x92,0x60,0xA8,
|
||||
0x0D,0x67,0x6F,0x0E,0x37,0xF5,0x0D,0x01,0x9F,0xC2,0x77,0xD4,0xFE,0xEC,0xF1,0x73,
|
||||
0x30,0x39,0xE0,0x7D,0xF5,0x61,0x98,0xE4,0x2C,0x28,0x55,0x04,0x56,0x55,0xDB,0x2F,
|
||||
0x6B,0xEC,0xE5,0x58,0x06,0xB6,0x64,0x80,0x6A,0x2A,0x1A,0x4E,0x5B,0x0F,0xD8,0xC4,
|
||||
0x0A,0x2E,0x52,0x19,0xD9,0x62,0xF5,0x30,0x48,0xBE,0x8C,0x7B,0x4F,0x38,0x9B,0xA2,
|
||||
0xC3,0xAF,0xC9,0xD3,0xC7,0xC1,0x62,0x41,0x86,0xB9,0x61,0x21,0x57,0x6F,0x99,0x4F,
|
||||
0xC1,0xBA,0xCE,0x7B,0xB5,0x3B,0x4D,0x5E,0x8A,0x8B,0x44,0x57,0x5F,0x13,0x5F,0x70,
|
||||
0x6D,0x5B,0x29,0x47,0xDC,0x38,0xE2,0xEC,0x04,0x55,0x65,0x12,0x2A,0xE8,0x17,0x43,
|
||||
0xE1,0x8E,0xDD,0x2A,0xB3,0xE2,0x94,0xF7,0x09,0x6E,0x5C,0xE6,0xEB,0x8A,0xF8,0x6D,
|
||||
0x89,0x49,0x54,0x48,0xF5,0x2F,0xAD,0xBF,0xEA,0x94,0x4B,0xCA,0xFC,0x39,0x87,0x82,
|
||||
0x5F,0x8A,0x01,0xF2,0x75,0xF2,0xE6,0x71,0xD6,0xD8,0x42,0xDE,0xF1,0x2D,0x1D,0x28,
|
||||
0xA6,0x88,0x7E,0xA3,0xA0,0x47,0x1D,0x30,0xD9,0xA3,0x71,0xDF,0x49,0x1C,0xCB,0x01,
|
||||
0xF8,0x36,0xB1,0xF2,0xF0,0x22,0x58,0x5D,0x45,0x6B,0xBD,0xA0,0xBB,0xB2,0x88,0x42,
|
||||
0xC7,0x8C,0x28,0xCE,0x93,0xE8,0x90,0x63,0x08,0x90,0x7C,0x89,0x3C,0xF5,0x7D,0xB7,
|
||||
0x04,0x2D,0x4F,0x55,0x51,0x16,0xFD,0x7E,0x79,0xE8,0xBE,0xC1,0xF2,0x12,0xD4,0xF8,
|
||||
0xB4,0x84,0x05,0x23,0xA0,0xCC,0xD2,0x2B,0xFD,0xE1,0xAB,0xAD,0x0D,0xD1,0x55,0x6C,
|
||||
0x23,0x41,0x94,0x4D,0x77,0x37,0x4F,0x05,0x28,0x0C,0xBF,0x17,0xB3,0x12,0x67,0x6C,
|
||||
0x8C,0xC3,0x5A,0xF7,0x41,0x84,0x2A,0x6D,0xD0,0x94,0x12,0x27,0x2C,0xB4,0xED,0x9C,
|
||||
0x4D,0xEC,0x47,0x82,0x97,0xD5,0x67,0xB9,0x1B,0x9D,0xC0,0x55,0x07,0x7E,0xE5,0x8E,
|
||||
0xE2,0xA8,0xE7,0x3E,0x12,0xE4,0x0E,0x3A,0x2A,0x45,0x55,0x34,0xA2,0xF9,0x2D,0x5A,
|
||||
0x1B,0xAB,0x52,0x7C,0x83,0x10,0x5F,0x55,0xD2,0xF1,0x5A,0x43,0x2B,0xC6,0xA7,0xA4,
|
||||
0x89,0x15,0x95,0xE8,0xB4,0x4B,0x9D,0xF8,0x75,0xE3,0x9F,0x60,0x78,0x5B,0xD6,0xE6,
|
||||
0x0D,0x44,0xE6,0x21,0x06,0xBD,0x47,0x22,0x53,0xA4,0x00,0xAD,0x8D,0x43,0x13,0x85,
|
||||
0x39,0xF7,0xAA,0xFC,0x38,0xAF,0x7B,0xED,0xFC,0xE4,0x2B,0x54,0x50,0x98,0x4C,0xFC,
|
||||
0x85,0x80,0xF7,0xDF,0x3C,0x80,0x22,0xE1,0x94,0xDA,0xDE,0x24,0xC6,0xB0,0x7A,0x39,
|
||||
0x38,0xDC,0x0F,0xA1,0xA7,0xF4,0xF9,0x6F,0x63,0x18,0x57,0x8B,0x84,0x41,0x2A,0x2E,
|
||||
0xD4,0x53,0xF2,0xD9,0x00,0x0F,0xD0,0xDD,0x99,0x6E,0x19,0xA6,0x0A,0xD0,0xEC,0x5B,
|
||||
0x58,0x24,0xAB,0xC0,0xCB,0x06,0x65,0xEC,0x1A,0x13,0x38,0x94,0x0A,0x67,0x03,0x2F,
|
||||
0x3F,0xF7,0xE3,0x77,0x44,0x77,0x33,0xC6,0x14,0x39,0xD0,0xE3,0xC0,0xA2,0x08,0x79,
|
||||
0xBB,0x40,0x99,0x57,0x41,0x0B,0x01,0x90,0xCD,0xE1,0xCC,0x48,0x67,0xDB,0xB3,0xAF,
|
||||
0x88,0x74,0xF3,0x4C,0x82,0x8F,0x72,0xB1,0xB5,0x23,0x29,0xC4,0x12,0x6C,0x19,0xFC,
|
||||
0x8E,0x46,0xA4,0x9C,0xC4,0x25,0x65,0x87,0xD3,0x6D,0xBE,0x8A,0x93,0x11,0x03,0x38,
|
||||
0xED,0x83,0x2B,0xF3,0x46,0xA4,0x93,0xEA,0x3B,0x53,0x85,0x1D,0xCE,0xD4,0xF1,0x08,
|
||||
0x83,0x27,0xED,0xFC,0x9B,0x1A,0x18,0xBC,0xF9,0x8B,0xAE,0xDC,0x24,0xAB,0x50,0x38,
|
||||
0xE9,0x72,0x4B,0x10,0x22,0x17,0x7B,0x46,0x5D,0xAB,0x59,0x64,0xF3,0x40,0xAE,0xF8,
|
||||
0xBB,0xE5,0xC8,0xF9,0x26,0x03,0x4E,0x55,0x7D,0xEB,0xEB,0xFE,0xF7,0x39,0xE6,0xE0,
|
||||
0x0A,0x11,0xBE,0x2E,0x28,0xFF,0x98,0xED,0xC0,0xC9,0x42,0x56,0x42,0xC3,0xFD,0x00,
|
||||
0xF6,0xAF,0x87,0xA2,0x5B,0x01,0x3F,0x32,0x92,0x47,0x95,0x9A,0x72,0xA5,0x32,0x3D,
|
||||
0xAE,0x6B,0xD0,0x9B,0x07,0xD2,0x49,0x92,0xE3,0x78,0x4A,0xFA,0xA1,0x06,0x7D,0xF2,
|
||||
0x41,0xCF,0x77,0x74,0x04,0x14,0xB2,0x0C,0x86,0x84,0x64,0x16,0xD5,0xBB,0x51,0xA1,
|
||||
0xE5,0x6F,0xF1,0xD1,0xF2,0xE2,0xF7,0x5F,0x58,0x20,0x4D,0xB8,0x57,0xC7,0xCF,0xDD,
|
||||
0xC5,0xD8,0xBE,0x76,0x3D,0xF6,0x5F,0x7E,0xE7,0x2A,0x8B,0x88,0x24,0x1B,0x38,0x3F,
|
||||
0x0E,0x41,0x23,0x77,0xF5,0xF0,0x4B,0xD4,0x0C,0x1F,0xFA,0xA4,0x0B,0x80,0x5F,0xCF,
|
||||
0x45,0xF6,0xE0,0xDA,0x2F,0x34,0x59,0x53,0xFB,0x20,0x3C,0x52,0x62,0x5E,0x35,0xB5,
|
||||
0x62,0xFE,0x8B,0x60,0x63,0xE3,0x86,0x5A,0x15,0x1A,0x6E,0xD1,0x47,0x45,0xBC,0x32,
|
||||
0xB4,0xEB,0x67,0x38,0xAB,0xE4,0x6E,0x33,0x3A,0xB5,0xED,0xA3,0xAD,0x67,0xE0,0x4E,
|
||||
0x41,0x95,0xEE,0x62,0x62,0x71,0x26,0x1D,0x31,0xEF,0x62,0x30,0xAF,0xD7,0x82,0xAC,
|
||||
0xC2,0xDC,0x05,0x04,0xF5,0x97,0x07,0xBF,0x11,0x59,0x23,0x07,0xC0,0x64,0x02,0xE8,
|
||||
0x97,0xE5,0x3E,0xAF,0x18,0xAC,0x59,0xA6,0x8B,0x4A,0x33,0x90,0x1C,0x6E,0x7C,0x9C,
|
||||
0x20,0x7E,0x4C,0x3C,0x3E,0x61,0x64,0xBB,0xC5,0x6B,0x7C,0x7E,0x3E,0x9F,0xC5,0x4C,
|
||||
0x9F,0xEA,0x73,0xF5,0xD7,0x89,0xC0,0x4C,0xF4,0xFB,0xF4,0x2D,0xEC,0x14,0x1B,0x51,
|
||||
0xD5,0xC1,0x12,0xC8,0x10,0xDF,0x0B,0x4A,0x8B,0x9C,0xBC,0x93,0x45,0x6A,0x3E,0x3E,
|
||||
0x7D,0xC1,0xA9,0xBA,0xCD,0xC1,0xB4,0x07,0xE4,0xE1,0x68,0x86,0x43,0xB2,0x6D,0x38,
|
||||
0xF3,0xFB,0x0C,0x5C,0x66,0x37,0x71,0xDE,0x56,0xEF,0x6E,0xA0,0x10,0x40,0x65,0xA7,
|
||||
0x98,0xF7,0xD0,0xBE,0x0E,0xC8,0x37,0x36,0xEC,0x10,0xCA,0x7C,0x9C,0xAB,0x84,0x1E,
|
||||
0x05,0x17,0x76,0x02,0x1C,0x4F,0x52,0xAA,0x5F,0xC1,0xC6,0xA0,0x56,0xB9,0xD8,0x04,
|
||||
0x84,0x44,0x4D,0xA7,0x59,0xD8,0xDE,0x60,0xE6,0x38,0x0E,0x05,0x8F,0x03,0xE1,0x3B,
|
||||
0x6D,0x81,0x04,0x33,0x6F,0x30,0x0B,0xCE,0x69,0x05,0x21,0x33,0xFB,0x26,0xBB,0x89,
|
||||
0x7D,0xB6,0xAE,0x87,0x7E,0x51,0x07,0xE0,0xAC,0xF7,0x96,0x0A,0x6B,0xF9,0xC4,0x5C,
|
||||
0x1D,0xE4,0x44,0x47,0xB8,0x5E,0xFA,0xE3,0x78,0x84,0x55,0x42,0x4B,0x48,0x5E,0xF7,
|
||||
0x7D,0x47,0x35,0x86,0x1D,0x2B,0x43,0x05,0x03,0xEC,0x8A,0xB8,0x1E,0x06,0x3C,0x76,
|
||||
0x0C,0x48,0x1A,0x43,0xA7,0xB7,0x8A,0xED,0x1E,0x13,0xC6,0x43,0xEE,0x10,0xEF,0xDB,
|
||||
0xEC,0xFB,0x3C,0x83,0xB2,0x95,0x44,0xEF,0xD8,0x54,0x51,0x4E,0x2D,0x11,0x44,0x1D,
|
||||
0xFB,0x36,0x59,0x1E,0x7A,0x34,0xC1,0xC3,0xCA,0x57,0x00,0x61,0xEA,0x67,0xA5,0x16,
|
||||
0x9B,0x55,0xD0,0x55,0xE1,0x7F,0xD9,0x36,0xD2,0x40,0x76,0xAE,0xDC,0x01,0xCE,0xB0,
|
||||
0x7A,0x83,0xD5,0xCB,0x20,0x98,0xEC,0x6B,0xC1,0x72,0x92,0x34,0xF3,0x82,0x57,0x37,
|
||||
0x62,0x8A,0x32,0x36,0x0C,0x90,0x43,0xAE,0xAE,0x5C,0x9B,0x78,0x8E,0x13,0x65,0x02,
|
||||
0xFD,0x68,0x71,0xC1,0xFE,0xB0,0x31,0xA0,0x24,0x82,0xB0,0xC3,0xB1,0x79,0x69,0xA7,
|
||||
0xF5,0xD2,0xEB,0xD0,0x82,0xC0,0x32,0xDC,0x9E,0xC7,0x26,0x3C,0x6D,0x8D,0x98,0xC1,
|
||||
0xBB,0x22,0xD4,0xD0,0x0F,0x33,0xEC,0x3E,0xB9,0xCC,0xE1,0xDC,0x6A,0x4C,0x77,0x36,
|
||||
0x14,0x1C,0xF9,0xBF,0x81,0x9F,0x28,0x5F,0x71,0x85,0x32,0x29,0x90,0x75,0x48,0xC4,
|
||||
0xB3,0x4A,0xCE,0xD8,0x44,0x8F,0x14,0x2F,0xFD,0x40,0x57,0xEF,0xAA,0x08,0x75,0xD9,
|
||||
0x46,0xD1,0xD6,0x6E,0x32,0x55,0x1F,0xC3,0x18,0xFE,0x84,0x1F,0xFC,0x84,0xD5,0xFF,
|
||||
0x71,0x5E,0x1B,0x48,0xC3,0x86,0x95,0x0E,0x28,0x08,0x27,0xD3,0x38,0x83,0x71,0x7B,
|
||||
0x4C,0x80,0x63,0x54,0x9A,0x56,0xB0,0xAC,0xCF,0x80,0xCA,0x31,0x09,0xEF,0xFE,0xF3,
|
||||
0xBE,0xAF,0x24,0x7E,0xA6,0xFE,0x53,0x3F,0xC2,0x8D,0x4A,0x33,0x68,0xD1,0x22,0xA6,
|
||||
0x66,0xAD,0x7B,0xEA,0xDE,0xB6,0x43,0xB0,0xA1,0x25,0x95,0x00,0xA3,0x3F,0x75,0x46,
|
||||
0x14,0x11,0x44,0xEC,0xD7,0x95,0xBC,0x92,0xF0,0x4F,0xA9,0x16,0x53,0x62,0x97,0x60,
|
||||
0x2A,0x0F,0x41,0xF1,0x71,0x24,0xBE,0xEE,0x94,0x7F,0x08,0xCD,0x60,0x93,0xB3,0x85,
|
||||
0x5B,0x07,0x00,0x3F,0xD8,0x0F,0x28,0x83,0x9A,0xD1,0x69,0x9F,0xD1,0xDA,0x2E,0xC3,
|
||||
0x90,0x01,0xA2,0xB9,0x6B,0x4E,0x2A,0x66,0x9D,0xDA,0xAE,0xA6,0xEA,0x2A,0xD3,0x68,
|
||||
0x2F,0x0C,0x0C,0x9C,0xD2,0x8C,0x4A,0xED,0xE2,0x9E,0x57,0x65,0x9D,0x09,0x87,0xA3,
|
||||
0xB4,0xC4,0x32,0x5D,0xC9,0xD4,0x32,0x2B,0xB1,0xE0,0x71,0x1E,0x64,0x4D,0xE6,0x90,
|
||||
0x71,0xE3,0x1E,0x40,0xED,0x7D,0xF3,0x84,0x0E,0xED,0xC8,0x78,0x76,0xAE,0xC0,0x71,
|
||||
0x27,0x72,0xBB,0x05,0xEA,0x02,0x64,0xFB,0xF3,0x48,0x6B,0xB5,0x42,0x93,0x3F,0xED,
|
||||
0x9F,0x13,0x53,0xD2,0xF7,0xFE,0x2A,0xEC,0x1D,0x47,0x25,0xDB,0x3C,0x91,0x86,0xC6,
|
||||
0x8E,0xF0,0x11,0xFD,0x23,0x74,0x36,0xF7,0xA4,0xF5,0x9E,0x7A,0x7E,0x53,0x50,0x44,
|
||||
0xD4,0x47,0xCA,0xD3,0xEB,0x38,0x6D,0xE6,0xD9,0x71,0x94,0x7F,0x4A,0xC6,0x69,0x4B,
|
||||
0x11,0xF4,0x52,0xEA,0x22,0xFE,0x8A,0xB0,0x36,0x67,0x8B,0x59,0xE8,0xE6,0x80,0x2A,
|
||||
0xEB,0x65,0x04,0x13,0xEE,0xEC,0xDC,0x9E,0x5F,0xB1,0xEC,0x05,0x6A,0x59,0xE6,0x9F,
|
||||
0x5E,0x59,0x6B,0x89,0xBF,0xF7,0x1A,0xCA,0x44,0xF9,0x5B,0x6A,0x71,0x85,0x03,0xE4,
|
||||
0x29,0x62,0xE0,0x70,0x6F,0x41,0xC4,0xCF,0xB2,0xB1,0xCC,0xE3,0x7E,0xA6,0x07,0xA8,
|
||||
0x87,0xE7,0x7F,0x84,0x93,0xDB,0x52,0x4B,0x6C,0xEC,0x7E,0xDD,0xD4,0x24,0x48,0x10,
|
||||
0x69,0x9F,0x04,0x60,0x74,0xE6,0x48,0x18,0xF3,0xE4,0x2C,0xB9,0x4F,0x2E,0x50,0x7A,
|
||||
0xDF,0xD4,0x54,0x69,0x2B,0x8B,0xA7,0xF3,0xCE,0xFF,0x1F,0xF3,0x3E,0x26,0x01,0x39,
|
||||
0x17,0x95,0x84,0x89,0xB0,0xF0,0x4C,0x4B,0x82,0x91,0x9F,0xC4,0x4B,0xAC,0x9D,0xA5,
|
||||
0x74,0xAF,0x17,0x25,0xC9,0xCA,0x32,0xD3,0xBC,0x89,0x8A,0x84,0x89,0xCC,0x0D,0xAE,
|
||||
0x7C,0xA2,0xDB,0x9C,0x6A,0x78,0x91,0xEE,0xEA,0x76,0x5D,0x4E,0x87,0x60,0xF5,0x69,
|
||||
0x15,0x67,0xD4,0x02,0xCF,0xAF,0x48,0x36,0x07,0xEA,0xBF,0x6F,0x66,0x2D,0x06,0x8F,
|
||||
0xC4,0x9A,0xFE,0xF9,0xF6,0x90,0x87,0x75,0xB8,0xF7,0xAD,0x0F,0x76,0x10,0x5A,0x3D,
|
||||
0x59,0xB0,0x2E,0xB3,0xC7,0x35,0x2C,0xCC,0x70,0x56,0x2B,0xCB,0xE3,0x37,0x96,0xC5,
|
||||
0x2F,0x46,0x1B,0x8A,0x22,0x46,0xC7,0x88,0xA7,0x26,0x32,0x98,0x61,0xDF,0x86,0x22,
|
||||
0x8A,0xF4,0x1C,0x2F,0x87,0xA1,0x09,0xAA,0xCC,0xA9,0xAE,0xD3,0xBD,0x00,0x45,0x1C,
|
||||
0x9A,0x54,0x87,0x86,0x52,0x87,0xEF,0xFF,0x1E,0x8F,0xA1,0x8F,0xC1,0x89,0x5C,0x35,
|
||||
0x1B,0xDA,0x2D,0x3A,0x2C,0x16,0xB2,0xC2,0xF1,0x56,0xE2,0x78,0xC1,0x6B,0x63,0x97,
|
||||
0xC5,0x56,0x8F,0xC9,0x32,0x7F,0x2C,0xAA,0xAF,0xA6,0xA8,0xAC,0x20,0x91,0x22,0x88,
|
||||
0xDE,0xE4,0x60,0x8B,0xF9,0x4B,0x42,0x25,0x1A,0xE3,0x7F,0x9C,0x2C,0x19,0x89,0x3A,
|
||||
0x7E,0x05,0xD4,0x36,0xCC,0x69,0x58,0xC2,0xC1,0x32,0x8B,0x2F,0x90,0x85,0xEB,0x7A,
|
||||
0x39,0x50,0xA5,0xA1,0x27,0x92,0xC5,0x66,0xB0,0x20,0x4F,0x58,0x7E,0x55,0x83,0x43,
|
||||
0x2B,0x45,0xE2,0x9C,0xE4,0xD8,0x12,0x90,0x2C,0x16,0x83,0x56,0x16,0x79,0x03,0xB3,
|
||||
0xAD,0x2D,0x61,0x18,0x1A,0x13,0x1F,0x37,0xE2,0xE1,0x9C,0x73,0x7B,0x80,0xD5,0xFD,
|
||||
0x2D,0x51,0x87,0xFC,0x7B,0xAA,0xD7,0x1F,0x2C,0x7A,0x8E,0xAF,0xF4,0x8D,0xBB,0xCD,
|
||||
0x95,0x11,0x7C,0x72,0x0B,0xEE,0x6F,0xE2,0xB9,0xAF,0xDE,0x37,0x83,0xDE,0x8C,0x8D,
|
||||
0x62,0x05,0x67,0xB7,0x96,0xC6,0x8D,0x56,0xB6,0x0D,0xD7,0x62,0xBA,0xD6,0x46,0x36,
|
||||
0xBD,0x8E,0xC8,0xE6,0xEA,0x2A,0x6C,0x10,0x14,0xFF,0x6B,0x5B,0xFA,0x82,0x3C,0x46,
|
||||
0xB1,0x30,0x43,0x46,0x51,0x8A,0x7D,0x9B,0x92,0x3E,0x83,0x79,0x5B,0x55,0x5D,0xB2,
|
||||
0x6C,0x5E,0xCE,0x90,0x62,0x8E,0x53,0x98,0xC9,0x0D,0x6D,0xE5,0x2D,0x57,0xCD,0xC5,
|
||||
0x81,0x57,0xBA,0xE1,0xE8,0xB8,0x8F,0x72,0xE5,0x4F,0x13,0xDC,0xEA,0x9D,0x71,0x15,
|
||||
0x10,0xB2,0x11,0x88,0xD5,0x09,0xD4,0x7F,0x5B,0x65,0x7F,0x2C,0x3B,0x38,0x4C,0x11,
|
||||
0x68,0x50,0x8D,0xFB,0x9E,0xB0,0x59,0xBF,0x94,0x80,0x89,0x4A,0xC5,0x1A,0x18,0x12,
|
||||
0x89,0x53,0xD1,0x4A,0x10,0x29,0xE8,0x8C,0x1C,0xEC,0xB6,0xEA,0x46,0xC7,0x17,0x8B,
|
||||
0x25,0x15,0x31,0xA8,0xA2,0x6B,0x43,0xB1,0x9D,0xE2,0xDB,0x0B,0x87,0x9B,0xB0,0x11,
|
||||
0x04,0x0E,0x71,0xD2,0x29,0x77,0x89,0x82,0x0A,0x66,0x41,0x7F,0x1D,0x0B,0x48,0xFF,
|
||||
0x72,0xBB,0x24,0xFD,0xC2,0x48,0xA1,0x9B,0xFE,0x7B,0x7F,0xCE,0x88,0xDB,0x86,0xD9,
|
||||
0x85,0x3B,0x1C,0xB0,0xDC,0xA8,0x33,0x07,0xBF,0x51,0x2E,0xE3,0x0E,0x9A,0x00,0x97,
|
||||
0x1E,0x06,0xC0,0x97,0x43,0x9D,0xD8,0xB6,0x45,0xC4,0x86,0x67,0x5F,0x00,0xF8,0x88,
|
||||
0x9A,0xA4,0x52,0x9E,0xC7,0xAA,0x8A,0x83,0x75,0xEC,0xC5,0x18,0xAE,0xCE,0xC3,0x2F,
|
||||
0x1A,0x2B,0xF9,0x18,0xFF,0xAE,0x1A,0xF5,0x53,0x0B,0xB5,0x33,0x51,0xA7,0xFD,0xE8,
|
||||
0xA8,0xE1,0xA2,0x64,0xB6,0x22,0x17,0x43,0x80,0xCC,0x0A,0xD8,0xAE,0x3B,0xBA,0x40,
|
||||
0xD7,0xD9,0x92,0x4A,0x89,0xDF,0x04,0x10,0xEE,0x9B,0x18,0x2B,0x6A,0x77,0x69,0x8A,
|
||||
0x68,0xF4,0xF9,0xB9,0xA2,0x21,0x15,0x6E,0xE6,0x1E,0x3B,0x03,0x62,0x30,0x9B,0x60,
|
||||
0x41,0x7E,0x25,0x9B,0x9E,0x8F,0xC5,0x52,0x10,0x08,0xF8,0xC2,0x69,0xA1,0x21,0x11,
|
||||
0x88,0x37,0x5E,0x79,0x35,0x66,0xFF,0x10,0x42,0x18,0x6E,0xED,0x97,0xB6,0x6B,0x1C,
|
||||
0x4E,0x36,0xE5,0x6D,0x7D,0xB4,0xE4,0xBF,0x20,0xB9,0xE0,0x05,0x3A,0x69,0xD5,0xB8,
|
||||
0xE3,0xD5,0xDC,0xE0,0xB9,0xAC,0x53,0x3E,0x07,0xA4,0x57,0xAD,0x77,0xFF,0x48,0x18,
|
||||
0x76,0x2A,0xAC,0x49,0x2A,0x8E,0x47,0x75,0x6D,0x9F,0x67,0x63,0x30,0x35,0x8C,0x39,
|
||||
0x05,0x39,0xD5,0x6F,0x64,0x3A,0x5B,0xAD,0xCA,0x0B,0xBB,0x82,0x52,0x99,0x45,0xB1,
|
||||
0x93,0x36,0x36,0x99,0xAF,0x13,0x20,0x44,0x36,0xD8,0x02,0x44,0x09,0x39,0x92,0x85,
|
||||
0xFF,0x4A,0x4A,0x97,0x87,0xA6,0x63,0xD7,0xC7,0xB5,0xB5,0x24,0xED,0x0F,0xB4,0x6F,
|
||||
0x0C,0x58,0x52,0x14,0xD9,0xA6,0x7B,0xD3,0x79,0xBC,0x38,0x58,0xA1,0xBD,0x3B,0x84,
|
||||
0x06,0xD8,0x1A,0x06,0xFD,0x6B,0xA8,0xEA,0x4B,0x69,0x28,0x04,0x37,0xAD,0x82,0x99,
|
||||
0xFB,0x0E,0x1B,0x85,0xBD,0xA8,0x5D,0x73,0xCD,0xDC,0x58,0x75,0x0A,0xBE,0x63,0x6C,
|
||||
0x48,0xE7,0x4C,0xE4,0x30,0x2B,0x04,0x60,0xB9,0x15,0xD8,0xDA,0x86,0x81,0x75,0x8F,
|
||||
0x96,0xD4,0x8D,0x1C,0x5D,0x70,0x85,0x7C,0x1C,0x67,0x7B,0xD5,0x08,0x67,0xA6,0xCE,
|
||||
0x4B,0x0A,0x66,0x70,0xB7,0xE5,0x63,0xD4,0x5B,0x8A,0x82,0xEA,0x10,0x67,0xCA,0xE2,
|
||||
0xF4,0xEF,0x17,0x85,0x2F,0x2A,0x5F,0x8A,0x97,0x82,0xF8,0x6A,0xD6,0x34,0x10,0xEA,
|
||||
0xEB,0xC9,0x5C,0x3C,0xE1,0x49,0xF8,0x46,0xEB,0xDE,0xBD,0xF6,0xA9,0x92,0xF1,0xAA,
|
||||
0xA6,0xA0,0x18,0xB0,0x3A,0xD3,0x0F,0x1F,0xF3,0x6F,0xFF,0x31,0x45,0x43,0x44,0xD3,
|
||||
0x50,0x9A,0xF7,0x88,0x09,0x96,0xC1,0xCE,0x76,0xCC,0xF2,0x2C,0x2C,0xBA,0xAD,0x82,
|
||||
0x77,0x8F,0x18,0x84,0xC0,0xD2,0x07,0x9C,0x36,0x90,0x83,0x4E,0x0B,0xA5,0x4F,0x43,
|
||||
0x3E,0x04,0xAB,0x78,0x4F,0xD6,0xFB,0x09,0x01,0x24,0x90,0xDA,0x6F,0x3C,0x3A,0x61,
|
||||
0x0D,0x7F,0x69,0x4A,0xEB,0x2B,0x30,0x02,0xB4,0xDB,0xE0,0x84,0xA9,0xEC,0xD7,0x35,
|
||||
0xBF,0x37,0x7D,0x85,0x58,0xCE,0xA9,0x4E,0xE4,0x80,0xC7,0xA8,0xD3,0x30,0x67,0x48,
|
||||
0xEB,0x29,0xAF,0x2F,0x74,0x6A,0xB4,0xA7,0x3F,0x0F,0x3F,0x92,0xAF,0xF3,0xCA,0xAC,
|
||||
0xAF,0x4B,0xD9,0x94,0xC0,0x43,0xCA,0x81,0x0D,0x2F,0x48,0xA1,0xB0,0x27,0xD5,0xD2,
|
||||
0xEF,0x4B,0x05,0x85,0xA3,0xDE,0x4D,0x93,0x30,0x3C,0xF0,0xBB,0x4A,0x8F,0x30,0x27,
|
||||
0x4C,0xEB,0xE3,0x3E,0x64,0xED,0x9A,0x2F,0x3B,0xF1,0x82,0xF0,0xBA,0xF4,0xCF,0x7F,
|
||||
0x40,0xCB,0xB0,0xE1,0x7F,0xBC,0xAA,0x57,0xD3,0xC9,0x74,0xF2,0xFA,0x43,0x0D,0x22,
|
||||
0xD0,0xF4,0x77,0x4E,0x93,0xD7,0x85,0x70,0x1F,0x99,0xBF,0xB6,0xDE,0x35,0xF1,0x30,
|
||||
0xA7,0x5E,0x71,0xF0,0x6B,0x01,0x2D,0x7B,0x64,0xF0,0x33,0x53,0x0A,0x39,0x88,0xF3,
|
||||
0x6B,0x3A,0xA6,0x6B,0x35,0xD2,0x2F,0x43,0xCD,0x02,0xFD,0xB5,0xE9,0xBC,0x5B,0xAA,
|
||||
0xD8,0xA4,0x19,0x7E,0x0E,0x5D,0x94,0x81,0x9E,0x6F,0x77,0xAD,0xD6,0x0E,0x74,0x93,
|
||||
0x96,0xE7,0xC4,0x18,0x5F,0xAD,0xF5,0x19,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3DS-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
@@ -23,47 +300,41 @@ namespace NDecrypt.Core
|
||||
/// </summary>
|
||||
public byte[] AESHardwareConstant { get; private set; } = [];
|
||||
|
||||
#region Retail Keys
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x18 { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x1B (New 3DS 9.6)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x1B { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x25 { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x2C { get; private set; } = [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Development Keys
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x18 (New 3DS 9.3)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x18 { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x1B (New 3DS 9.6)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x1B { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x1B New 3DS 9.6)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x1B { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x25 { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x25 (> 7.x)
|
||||
/// </summary>
|
||||
public byte[] DevKeyX0x25 { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
public byte[] KeyX0x2C { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Dev KeyX 0x2C (< 6.x)
|
||||
/// </summary>
|
||||
@@ -71,8 +342,179 @@ namespace NDecrypt.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Test Values
|
||||
|
||||
/// <summary>
|
||||
/// Expected hash for NitroEncryptionData
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedNitroSha512Hash =
|
||||
[
|
||||
0x1A, 0xD6, 0x40, 0x21, 0xFC, 0x3D, 0x1A, 0x9A,
|
||||
0x9B, 0xC0, 0x88, 0x8E, 0x2E, 0x68, 0xDE, 0x4E,
|
||||
0x8A, 0x60, 0x6B, 0x86, 0x63, 0x22, 0xD2, 0xC7,
|
||||
0xC6, 0xD7, 0xD6, 0xCE, 0x65, 0xF5, 0xBA, 0xA7,
|
||||
0xEA, 0x69, 0x63, 0x7E, 0xC9, 0xE4, 0x57, 0x7B,
|
||||
0x01, 0xFD, 0xCE, 0xC2, 0x26, 0x3B, 0xD9, 0x0D,
|
||||
0x84, 0x57, 0xC2, 0x00, 0xB8, 0x56, 0x9F, 0xE5,
|
||||
0x56, 0xDA, 0x8D, 0xDE, 0x84, 0xB8, 0x8E, 0xE4,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Initial value for key validation tests
|
||||
/// </summary>
|
||||
private static readonly byte[] TestIV =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Pattern to use for key validation tests
|
||||
/// </summary>
|
||||
private static readonly byte[] TestPattern =
|
||||
[
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for KeyX0x18
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedKeyX0x18 =
|
||||
[
|
||||
0x06, 0xF1, 0xB2, 0x3B, 0x12, 0xAD, 0x80, 0xC1,
|
||||
0x13, 0xC6, 0x18, 0x3D, 0x27, 0xB8, 0xB9, 0x95,
|
||||
0x49, 0x73, 0x59, 0x82, 0xEF, 0xFE, 0x16, 0x48,
|
||||
0x91, 0x2A, 0x89, 0x55, 0x9A, 0xDC, 0x3C, 0xA0,
|
||||
0x84, 0x46, 0x14, 0xE0, 0x16, 0x59, 0x8E, 0x4F,
|
||||
0xC2, 0x6C, 0x52, 0xA4, 0x7D, 0xAD, 0x4F, 0x23,
|
||||
0xF1, 0xC6, 0x99, 0x44, 0x39, 0xB7, 0x42, 0xF0,
|
||||
0x1F, 0xBB, 0x02, 0xF6, 0x0A, 0x8A, 0xC2, 0x9A,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for DevKeyX0x18
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedDevKeyX0x18 =
|
||||
[
|
||||
0x99, 0x6E, 0x3C, 0x54, 0x97, 0x3C, 0xEA, 0xE8,
|
||||
0xBA, 0xAE, 0x18, 0x5C, 0x93, 0x27, 0x65, 0x50,
|
||||
0xF6, 0x6D, 0x67, 0xD7, 0xEF, 0xBD, 0x7C, 0xCB,
|
||||
0x8A, 0xC1, 0x1A, 0x54, 0xFC, 0x3B, 0x8B, 0x3A,
|
||||
0x0E, 0xE5, 0xEF, 0x27, 0x4A, 0x73, 0x7E, 0x0A,
|
||||
0x2E, 0x2E, 0x9D, 0xAF, 0x6C, 0x03, 0xF2, 0x91,
|
||||
0xC4, 0xFA, 0x73, 0xFD, 0x6B, 0xA0, 0x07, 0xD4,
|
||||
0x75, 0x5B, 0x6F, 0x2E, 0x8B, 0x68, 0x4C, 0xD1,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for KeyX0x1B
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedKeyX0x1B =
|
||||
[
|
||||
0x0A, 0xE4, 0x79, 0x02, 0x1B, 0xFA, 0x25, 0x4B,
|
||||
0x2D, 0x92, 0x4F, 0xA8, 0x41, 0x59, 0xCE, 0x10,
|
||||
0x09, 0xE6, 0x08, 0x61, 0x23, 0xC7, 0xD2, 0x30,
|
||||
0x84, 0x37, 0xD5, 0x49, 0x42, 0x94, 0xB2, 0x70,
|
||||
0x6A, 0xF3, 0x75, 0xB0, 0x1F, 0x4F, 0xA1, 0xCE,
|
||||
0x03, 0xA2, 0x6A, 0x19, 0x5D, 0x32, 0x0D, 0xB5,
|
||||
0x79, 0xCD, 0xFD, 0xF0, 0xDE, 0x49, 0x26, 0x2D,
|
||||
0x29, 0x36, 0x30, 0x69, 0x8B, 0x45, 0xE1, 0xFC,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for DevKeyX0x1B
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedDevKeyX0x1B =
|
||||
[
|
||||
0x16, 0x4F, 0xD9, 0x58, 0xC9, 0x20, 0xB3, 0xED,
|
||||
0xC4, 0xEB, 0x57, 0x39, 0x10, 0xEF, 0xA8, 0xCC,
|
||||
0xE5, 0x49, 0xBF, 0x52, 0x10, 0xA9, 0xCC, 0xE1,
|
||||
0x65, 0x3B, 0x2D, 0x51, 0x45, 0xFB, 0x60, 0x52,
|
||||
0x3E, 0x29, 0xEB, 0xEB, 0x3F, 0xF2, 0x76, 0x08,
|
||||
0x00, 0x05, 0x7F, 0x64, 0x29, 0x4A, 0x17, 0x22,
|
||||
0x56, 0x7F, 0x49, 0x94, 0x1A, 0x8C, 0x56, 0x35,
|
||||
0x38, 0xBE, 0xA4, 0x2E, 0x58, 0xD3, 0x81, 0x8C,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for KeyX0x25
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedKeyX0x25 =
|
||||
[
|
||||
0x37, 0xBC, 0x73, 0xD6, 0xEE, 0x73, 0xE0, 0x94,
|
||||
0x42, 0x84, 0x74, 0xE5, 0xD8, 0xFB, 0x5F, 0x65,
|
||||
0xF4, 0xCF, 0x2E, 0xC1, 0x43, 0x48, 0x6C, 0xAA,
|
||||
0xC8, 0xF9, 0x96, 0xE6, 0x33, 0xDD, 0xE7, 0xBF,
|
||||
0xD2, 0x21, 0x89, 0x39, 0x13, 0xD1, 0xEC, 0xCA,
|
||||
0x1D, 0x5D, 0x1F, 0x77, 0x95, 0xD2, 0x8B, 0x27,
|
||||
0x92, 0x79, 0xC5, 0x1D, 0x72, 0xA7, 0x28, 0x57,
|
||||
0x41, 0x0E, 0x46, 0xB8, 0x80, 0x7B, 0x7C, 0x0D,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for DevKeyX0x25
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedDevKeyX0x25 =
|
||||
[
|
||||
0x71, 0x65, 0x30, 0xF2, 0x68, 0xEC, 0x65, 0x0A,
|
||||
0x8C, 0x9E, 0xC5, 0x5A, 0xFA, 0x37, 0x8E, 0xDA,
|
||||
0x7B, 0x58, 0x3B, 0x66, 0x7C, 0x9D, 0x16, 0xD9,
|
||||
0x2D, 0x8F, 0xCF, 0x04, 0x66, 0x7F, 0x27, 0x41,
|
||||
0xBF, 0x5F, 0x1E, 0x11, 0x4C, 0xD6, 0xB9, 0x0A,
|
||||
0xC5, 0x42, 0xCF, 0x2B, 0x87, 0x6B, 0xD4, 0x72,
|
||||
0x4D, 0x9C, 0x29, 0x2E, 0xF8, 0xB0, 0x6F, 0x22,
|
||||
0x35, 0x5B, 0x96, 0x83, 0xD1, 0xE4, 0x5E, 0xDB,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for KeyX0x2C
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedKeyX0x2C =
|
||||
[
|
||||
0xAE, 0x44, 0x20, 0xDB, 0xA5, 0x96, 0xDC, 0xF3,
|
||||
0xD8, 0x23, 0x9E, 0x3C, 0x44, 0x73, 0x3D, 0xCD,
|
||||
0x07, 0xD5, 0xF8, 0xD0, 0xC6, 0xB3, 0x5A, 0x80,
|
||||
0xB5, 0x5A, 0x55, 0x30, 0x5D, 0x4A, 0xBE, 0x61,
|
||||
0xBF, 0xEF, 0x64, 0x17, 0x28, 0xD6, 0x26, 0x52,
|
||||
0x42, 0x4D, 0x8F, 0x1C, 0xBC, 0x63, 0xD3, 0x91,
|
||||
0x7D, 0xA6, 0x4F, 0xAF, 0x26, 0x38, 0x60, 0xEE,
|
||||
0x79, 0x92, 0x2F, 0xD8, 0xCA, 0x4E, 0xE7, 0xEC,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Expected output value for DevKeyX0x2C
|
||||
/// </summary>
|
||||
private static readonly byte[] ExpectedDevKeyX0x2C =
|
||||
[
|
||||
0x5F, 0x73, 0xD5, 0x9A, 0x67, 0xFF, 0x8C, 0x12,
|
||||
0x31, 0x58, 0x0B, 0x58, 0x46, 0xFE, 0x05, 0x16,
|
||||
0x92, 0xE4, 0x84, 0x06, 0x18, 0x9B, 0x58, 0x91,
|
||||
0xE7, 0xF8, 0xCD, 0xA9, 0x95, 0xAC, 0x07, 0xCD,
|
||||
0x43, 0x20, 0x7A, 0x8C, 0xCC, 0xAB, 0x48, 0x50,
|
||||
0x29, 0x2F, 0x96, 0x73, 0xB0, 0xD9, 0xE5, 0xCB,
|
||||
0xE6, 0x9A, 0x0D, 0xF7, 0xD0, 0x1E, 0xC2, 0xEC,
|
||||
0xC1, 0xE2, 0x8E, 0xEE, 0x89, 0xB9, 0xB1, 0x97,
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants
|
||||
/// </summary>
|
||||
/// <param name="keyfile">Path to the keyfile</param>
|
||||
public DecryptArgs(string? config)
|
||||
{
|
||||
InitConfigJson(config);
|
||||
ValidateKeys();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants
|
||||
/// </summary>
|
||||
@@ -80,11 +522,17 @@ namespace NDecrypt.Core
|
||||
/// <param name="useAesKeysTxt">Indicates if the keyfile format is aeskeys.txt</param>
|
||||
public DecryptArgs(string? keyfile, bool useAesKeysTxt)
|
||||
{
|
||||
// Set the default DS values
|
||||
NitroEncryptionData = _nitroEncryptionData;
|
||||
|
||||
// Read the proper keyfile format
|
||||
if (useAesKeysTxt)
|
||||
InitAesKeysTxt(keyfile);
|
||||
else
|
||||
InitKeysBin(keyfile);
|
||||
|
||||
// Perform validation tests
|
||||
ValidateKeys();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,7 +555,7 @@ namespace NDecrypt.Core
|
||||
// Ignore comments in the file
|
||||
if (reader.RowType == IniRowType.Comment)
|
||||
continue;
|
||||
if (reader.KeyValuePair == null || string.IsNullOrWhiteSpace(reader.KeyValuePair?.Key))
|
||||
if (reader.KeyValuePair == null || string.IsNullOrEmpty(reader.KeyValuePair?.Key))
|
||||
break;
|
||||
|
||||
var kvp = reader.KeyValuePair!.Value;
|
||||
@@ -200,6 +648,41 @@ namespace NDecrypt.Core
|
||||
IsReady = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants from config.json
|
||||
/// </summary>
|
||||
/// <param name="config">Path to config.json</param>
|
||||
private void InitConfigJson(string? config)
|
||||
{
|
||||
if (config == null || !File.Exists(config))
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to read the configuration file
|
||||
var configObj = Configuration.Create(config);
|
||||
if (configObj == null)
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the fields from the configuration
|
||||
NitroEncryptionData = StringToByteArray(configObj.NitroEncryptionData);
|
||||
AESHardwareConstant = StringToByteArray(configObj.AESHardwareConstant);
|
||||
KeyX0x18 = StringToByteArray(configObj.KeyX0x18);
|
||||
KeyX0x1B = StringToByteArray(configObj.KeyX0x1B);
|
||||
KeyX0x25 = StringToByteArray(configObj.KeyX0x25);
|
||||
KeyX0x2C = StringToByteArray(configObj.KeyX0x2C);
|
||||
DevKeyX0x18 = StringToByteArray(configObj.DevKeyX0x18);
|
||||
DevKeyX0x1B = StringToByteArray(configObj.DevKeyX0x1B);
|
||||
DevKeyX0x25 = StringToByteArray(configObj.DevKeyX0x25);
|
||||
DevKeyX0x2C = StringToByteArray(configObj.DevKeyX0x2C);
|
||||
|
||||
IsReady = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants from keys.bin
|
||||
/// </summary>
|
||||
@@ -250,9 +733,128 @@ namespace NDecrypt.Core
|
||||
IsReady = true;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa
|
||||
private static byte[] StringToByteArray(string hex)
|
||||
/// <summary>
|
||||
/// Validate that all keys provided are going to be valid
|
||||
/// </summary>
|
||||
/// <remarks>Does not know what the keys are, just the result</remarks>
|
||||
private void ValidateKeys()
|
||||
{
|
||||
// NitroEncryptionData
|
||||
if (NitroEncryptionData.Length > 0)
|
||||
{
|
||||
using var hasher = System.Security.Cryptography.SHA512.Create();
|
||||
byte[] actual = hasher.ComputeHash(NitroEncryptionData);
|
||||
if (!Extensions.EqualsExactly(ExpectedNitroSha512Hash, actual))
|
||||
{
|
||||
Console.WriteLine($"NitroEncryptionData invalid value, disabling...");
|
||||
NitroEncryptionData = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x18
|
||||
if (KeyX0x18.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(KeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedKeyX0x18, actual))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x18 invalid value, disabling...");
|
||||
KeyX0x18 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x18
|
||||
if (DevKeyX0x18.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(DevKeyX0x18, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedDevKeyX0x18, actual))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x18 invalid value, disabling...");
|
||||
DevKeyX0x18 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x1B
|
||||
if (KeyX0x1B.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(KeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedKeyX0x1B, actual))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x1B invalid value, disabling...");
|
||||
KeyX0x1B = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x1B
|
||||
if (DevKeyX0x1B.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(DevKeyX0x1B, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedDevKeyX0x1B, actual))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x1B invalid value, disabling...");
|
||||
DevKeyX0x1B = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x25
|
||||
if (KeyX0x25.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(KeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedKeyX0x25, actual))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x25 invalid value, disabling...");
|
||||
KeyX0x25 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x25
|
||||
if (DevKeyX0x25.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(DevKeyX0x25, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedDevKeyX0x25, actual))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x25 invalid value, disabling...");
|
||||
DevKeyX0x25 = [];
|
||||
}
|
||||
}
|
||||
|
||||
// KeyX0x2C
|
||||
if (KeyX0x2C.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(KeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedKeyX0x2C, actual))
|
||||
{
|
||||
Console.WriteLine($"KeyX0x2C invalid value, disabling...");
|
||||
KeyX0x2C = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DevKeyX0x2C
|
||||
if (DevKeyX0x2C.Length > 0)
|
||||
{
|
||||
var cipher = CommonOperations.CreateAESEncryptionCipher(DevKeyX0x2C, TestIV);
|
||||
byte[] actual = cipher.ProcessBytes(TestPattern);
|
||||
if (!Extensions.EqualsExactly(ExpectedDevKeyX0x2C, actual))
|
||||
{
|
||||
Console.WriteLine($"DevKeyX0x2C invalid value, disabling...");
|
||||
DevKeyX0x2C = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa
|
||||
private static byte[] StringToByteArray(string? hex)
|
||||
{
|
||||
// Handle null values
|
||||
if (hex == null)
|
||||
return [];
|
||||
|
||||
int NumberChars = hex.Length;
|
||||
byte[] bytes = new byte[NumberChars / 2];
|
||||
for (int i = 0; i < NumberChars; i += 2)
|
||||
|
||||
@@ -17,5 +17,12 @@
|
||||
/// <param name="force">Indicates if the operation should be forced</param>
|
||||
/// <returns>True if the file could be decrypted, false otherwise</returns>
|
||||
bool DecryptFile(string filename, bool force);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get information on an input file
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file get information on</param>
|
||||
/// <returns>String representing the info, null on error</returns>
|
||||
string? GetInformation(string filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,21 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.3.1</VersionPrefix>
|
||||
<VersionPrefix>0.3.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Common code for all NDecrypt processors</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
@@ -35,19 +37,13 @@
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="2.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.7.4" />
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.9.0" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))" />
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="2.2.1" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.2" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.6.2" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.8" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.8.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -68,7 +68,11 @@ namespace NDecrypt.Core
|
||||
NormalKey2C = RotateLeft(NormalKey2C, 87);
|
||||
|
||||
// Special case for zero-key
|
||||
#if NET20 || NET35
|
||||
if ((masks & BitMasks.FixedCryptoKey) > 0)
|
||||
#else
|
||||
if (masks.HasFlag(BitMasks.FixedCryptoKey))
|
||||
#endif
|
||||
{
|
||||
Console.WriteLine("Encryption Method: Zero Key");
|
||||
NormalKey = new byte[16];
|
||||
@@ -113,7 +117,11 @@ namespace NDecrypt.Core
|
||||
public void SetRomFSValues(BitMasks masks)
|
||||
{
|
||||
// NormalKey has a constant value for zero-key
|
||||
#if NET20 || NET35
|
||||
if ((masks & BitMasks.FixedCryptoKey) > 0)
|
||||
#else
|
||||
if (masks.HasFlag(BitMasks.FixedCryptoKey))
|
||||
#endif
|
||||
{
|
||||
NormalKey = new byte[16];
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.N3DS;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
@@ -31,41 +32,7 @@ namespace NDecrypt.Core
|
||||
_decryptArgs = decryptArgs;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool EncryptFile(string filename, bool force)
|
||||
{
|
||||
// Ensure the constants are all set
|
||||
if (_decryptArgs.IsReady != true)
|
||||
{
|
||||
Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Open the read and write on the same file for inplace processing
|
||||
using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var output = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// Deserialize the cart information
|
||||
var cart = N3DS.Create(input);
|
||||
if (cart?.Model == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS cart image!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encrypt all 8 NCCH partitions
|
||||
EncryptAllPartitions(cart, force, input, output);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
|
||||
Console.WriteLine("Please check that the file was a valid 3DS or New 3DS cart image and try again.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#region Decrypt
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DecryptFile(string filename, bool force)
|
||||
@@ -103,8 +70,6 @@ namespace NDecrypt.Core
|
||||
}
|
||||
}
|
||||
|
||||
#region Decrypt
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt all partitions in the partition table of an NCSD header
|
||||
/// </summary>
|
||||
@@ -360,6 +325,11 @@ namespace NDecrypt.Core
|
||||
return;
|
||||
}
|
||||
|
||||
// Reread the decrypted ExeFS header
|
||||
uint exeFsHeaderOffset = cart.GetExeFSOffset(index);
|
||||
input.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
|
||||
cart.ExeFSHeaders[index] = SabreTools.Serialization.Deserializers.N3DS.ParseExeFSHeader(input);
|
||||
|
||||
// Get the ExeFS header
|
||||
var exeFsHeader = cart.ExeFSHeaders[index];
|
||||
if (exeFsHeader?.FileHeaders == null)
|
||||
@@ -368,8 +338,7 @@ namespace NDecrypt.Core
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the ExeFS offset
|
||||
uint exeFsHeaderOffset = cart.GetExeFSOffset(index);
|
||||
// Get the ExeFS files offset
|
||||
uint exeFsFilesOffset = exeFsHeaderOffset + cart.MediaUnitSize;
|
||||
|
||||
// Loop through and process all headers
|
||||
@@ -478,6 +447,42 @@ namespace NDecrypt.Core
|
||||
|
||||
#region Encrypt
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool EncryptFile(string filename, bool force)
|
||||
{
|
||||
// Ensure the constants are all set
|
||||
if (_decryptArgs.IsReady != true)
|
||||
{
|
||||
Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Open the read and write on the same file for inplace processing
|
||||
using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var output = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
// Deserialize the cart information
|
||||
var cart = N3DS.Create(input);
|
||||
if (cart?.Model == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS cart image!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encrypt all 8 NCCH partitions
|
||||
EncryptAllPartitions(cart, force, input, output);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
|
||||
Console.WriteLine("Please check that the file was a valid 3DS or New 3DS cart image and try again.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt all partitions in the partition table of an NCSD header
|
||||
/// </summary>
|
||||
@@ -871,5 +876,42 @@ namespace NDecrypt.Core
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Info
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? GetInformation(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open the file for reading
|
||||
using var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Deserialize the cart information
|
||||
var cart = N3DS.Create(input);
|
||||
if (cart?.Model == null)
|
||||
return "Error: Not a 3DS cart image!";
|
||||
|
||||
// Get a string builder for the status
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// Iterate over all 8 NCCH partitions
|
||||
for (int p = 0; p < 8; p++)
|
||||
{
|
||||
bool decrypted = cart.PossiblyDecrypted(p);
|
||||
sb.AppendLine($"\tPartition {p}: {(decrypted ? "Decrypted" : "Encrypted")}");
|
||||
}
|
||||
|
||||
// Return the status for all partitions
|
||||
return sb.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
namespace NDecrypt
|
||||
{
|
||||
/// <summary>
|
||||
/// Functionality to use from the program
|
||||
/// </summary>
|
||||
internal enum Feature
|
||||
{
|
||||
NULL,
|
||||
Decrypt,
|
||||
Encrypt,
|
||||
Info,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of the detected file
|
||||
/// </summary>
|
||||
@@ -11,6 +22,5 @@ namespace NDecrypt
|
||||
iQueDS,
|
||||
N3DS,
|
||||
iQue3DS,
|
||||
N3DSCIA,
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,22 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>0.3.1</VersionPrefix>
|
||||
<VersionPrefix>0.3.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>NDecrypt</Title>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>DS/3DS Encryption Tool</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2018-2024</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2018-2025</Copyright>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/NDecrypt</RepositoryUrl>
|
||||
@@ -41,4 +42,8 @@
|
||||
<ProjectReference Include="..\NDecrypt.Core\NDecrypt.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using NDecrypt.Core;
|
||||
|
||||
namespace NDecrypt
|
||||
@@ -20,14 +23,18 @@ namespace NDecrypt
|
||||
return;
|
||||
}
|
||||
|
||||
bool encrypt;
|
||||
Feature feature;
|
||||
if (args[0] == "decrypt" || args[0] == "d")
|
||||
{
|
||||
encrypt = false;
|
||||
feature = Feature.Decrypt;
|
||||
}
|
||||
else if (args[0] == "encrypt" || args[0] == "e")
|
||||
{
|
||||
encrypt = true;
|
||||
feature = Feature.Encrypt;
|
||||
}
|
||||
else if (args[0] == "info" || args[0] == "i")
|
||||
{
|
||||
feature = Feature.Info;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -39,16 +46,16 @@ namespace NDecrypt
|
||||
force = false,
|
||||
outputHashes = false,
|
||||
useAesKeysTxt = false;
|
||||
string? config = null;
|
||||
string? keyfile = null;
|
||||
int start = 1;
|
||||
for (; start < args.Length; start++)
|
||||
{
|
||||
if (args[start] == "-a" || args[start] == "--aes-keys"
|
||||
|| args[start] == "-c" || args[start] == "--citra")
|
||||
if (args[start] == "-a" || args[start] == "--aes-keys")
|
||||
{
|
||||
useAesKeysTxt = true;
|
||||
}
|
||||
else if (args[start] == "-dev" || args[start] == "--development")
|
||||
else if (args[start] == "-d" || args[start] == "--development")
|
||||
{
|
||||
development = true;
|
||||
}
|
||||
@@ -67,7 +74,7 @@ namespace NDecrypt
|
||||
|
||||
start++;
|
||||
string tempPath = args[start];
|
||||
if (string.IsNullOrWhiteSpace(tempPath))
|
||||
if (string.IsNullOrEmpty(tempPath))
|
||||
Console.WriteLine($"Invalid keyfile path: null or empty path found!");
|
||||
|
||||
tempPath = Path.GetFullPath(tempPath);
|
||||
@@ -76,12 +83,31 @@ namespace NDecrypt
|
||||
else
|
||||
keyfile = tempPath;
|
||||
}
|
||||
else if (args[start] == "-c" || args[start] == "--config")
|
||||
{
|
||||
if (start == args.Length - 1)
|
||||
Console.WriteLine("Invalid config path: no additional arguments found!");
|
||||
|
||||
start++;
|
||||
string tempPath = args[start];
|
||||
if (string.IsNullOrEmpty(tempPath))
|
||||
Console.WriteLine($"Invalid config path: null or empty path found!");
|
||||
|
||||
tempPath = Path.GetFullPath(tempPath);
|
||||
if (!File.Exists(tempPath))
|
||||
Console.WriteLine($"Invalid config path: file {tempPath} not found!");
|
||||
else
|
||||
config = tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Derive the config path based on the runtime folder if not already set
|
||||
config = DeriveConfigFile(config);
|
||||
|
||||
// Derive the keyfile path based on the runtime folder if not already set
|
||||
keyfile = DeriveKeyFile(keyfile, useAesKeysTxt);
|
||||
|
||||
@@ -93,24 +119,27 @@ namespace NDecrypt
|
||||
}
|
||||
|
||||
// Initialize the decrypt args, if possible
|
||||
var decryptArgs = new DecryptArgs(keyfile, useAesKeysTxt);
|
||||
DecryptArgs decryptArgs;
|
||||
if (config != null)
|
||||
decryptArgs = new DecryptArgs(config);
|
||||
else
|
||||
decryptArgs = new DecryptArgs(keyfile, useAesKeysTxt);
|
||||
|
||||
// Create reusable tools
|
||||
_tools[FileType.NDS] = new DSTool();
|
||||
_tools[FileType.NDS] = new DSTool(decryptArgs);
|
||||
_tools[FileType.N3DS] = new ThreeDSTool(development, decryptArgs);
|
||||
//_tools[FileType.N3DSCIA] = new CIATool(development, decryptArgs);
|
||||
|
||||
for (int i = start; i < args.Length; i++)
|
||||
{
|
||||
if (File.Exists(args[i]))
|
||||
{
|
||||
ProcessPath(args[i], encrypt, force, outputHashes);
|
||||
ProcessPath(args[i], feature, force, outputHashes);
|
||||
}
|
||||
else if (Directory.Exists(args[i]))
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(args[i], "*", SearchOption.AllDirectories))
|
||||
foreach (string file in Directory.GetFiles(args[i], "*", SearchOption.AllDirectories))
|
||||
{
|
||||
ProcessPath(file, encrypt, force, outputHashes);
|
||||
ProcessPath(file, feature, force, outputHashes);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -124,10 +153,10 @@ namespace NDecrypt
|
||||
/// Process a single file path
|
||||
/// </summary>
|
||||
/// <param name="path">File path to process</param>
|
||||
/// <param name="encrypt">Indicates if the file should be encrypted or decrypted</param>
|
||||
/// <param name="feature">Indicates what should be done to the file</param>
|
||||
/// <param name="force">Indicates if the operation should be forced</param>
|
||||
/// <param name="outputHashes">Indicates if hashes should be output after a successful operation</param>
|
||||
private static void ProcessPath(string path, bool encrypt, bool force, bool outputHashes)
|
||||
private static void ProcessPath(string path, Feature feature, bool force, bool outputHashes)
|
||||
{
|
||||
// Attempt to derive the tool for the path
|
||||
var tool = DeriveTool(path);
|
||||
@@ -137,16 +166,23 @@ namespace NDecrypt
|
||||
Console.WriteLine($"Processing {path}");
|
||||
|
||||
// Encrypt or decrypt the file as requested
|
||||
if (encrypt && !tool.EncryptFile(path, force))
|
||||
if (feature == Feature.Encrypt && !tool.EncryptFile(path, force))
|
||||
{
|
||||
Console.WriteLine("Encryption failed!");
|
||||
return;
|
||||
}
|
||||
else if (!encrypt && !tool.DecryptFile(path, force))
|
||||
else if (feature == Feature.Decrypt && !tool.DecryptFile(path, force))
|
||||
{
|
||||
Console.WriteLine("Decryption failed!");
|
||||
return;
|
||||
}
|
||||
else if (feature == Feature.Info)
|
||||
{
|
||||
string? infoString = tool.GetInformation(path);
|
||||
infoString ??= "There was a problem getting file information!";
|
||||
|
||||
Console.WriteLine(infoString);
|
||||
}
|
||||
|
||||
// Output the file hashes, if expected
|
||||
if (outputHashes)
|
||||
@@ -159,7 +195,7 @@ namespace NDecrypt
|
||||
/// <param name="err">Additional error text to display, can be null to ignore</param>
|
||||
private static void DisplayHelp(string? err = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(err))
|
||||
if (!string.IsNullOrEmpty(err))
|
||||
Console.WriteLine($"Error: {err}");
|
||||
|
||||
Console.WriteLine(@"Usage: NDecrypt <operation> [flags] <path> ...
|
||||
@@ -167,18 +203,37 @@ namespace NDecrypt
|
||||
Possible values for <operation>:
|
||||
e, encrypt - Encrypt the input files
|
||||
d, decrypt - Decrypt the input files
|
||||
i, info - Output file information
|
||||
|
||||
Possible values for [flags] (one or more can be used):
|
||||
-c, --config <path> Path to config.json
|
||||
-a, --aes-keys Enable using aes_keys.txt instead of keys.bin
|
||||
-dev, --development Enable using development keys, if available
|
||||
-k, --keyfile <path> Path to keys.bin or aes_keys.txt
|
||||
-d, --development Enable using development keys, if available
|
||||
-f, --force Force operation by avoiding sanity checks
|
||||
-h, --hash Output size and hashes to a companion file
|
||||
-k, --keyfile <path> Path to keys.bin or aes_keys.txt
|
||||
|
||||
<path> can be any file or folder that contains uncompressed items.
|
||||
More than one path can be specified at a time.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the full path to the config file, if possible
|
||||
/// </summary>
|
||||
private static string? DeriveConfigFile(string? config)
|
||||
{
|
||||
// If a path is passed in
|
||||
if (!string.IsNullOrEmpty(config))
|
||||
{
|
||||
config = Path.GetFullPath(config);
|
||||
if (File.Exists(config))
|
||||
return config;
|
||||
}
|
||||
|
||||
// Derive the keyfile path, if possible
|
||||
return GetFileLocation("config.json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the full path to the keyfile, if possible
|
||||
/// </summary>
|
||||
@@ -192,18 +247,8 @@ More than one path can be specified at a time.");
|
||||
return keyfile;
|
||||
}
|
||||
|
||||
// Derive the keyfile path based on the runtime folder if not already set
|
||||
using var processModule = System.Diagnostics.Process.GetCurrentProcess().MainModule;
|
||||
string applicationDirectory = Path.GetDirectoryName(processModule?.FileName) ?? string.Empty;
|
||||
|
||||
// Use the proper default name for the type
|
||||
if (useAesKeysTxt)
|
||||
keyfile = Path.Combine(applicationDirectory, "aes_keys.txt");
|
||||
else
|
||||
keyfile = Path.Combine(applicationDirectory, "keys.bin");
|
||||
|
||||
// Only return the path if the file exists
|
||||
return File.Exists(keyfile) ? keyfile : null;
|
||||
// Derive the keyfile path, if possible
|
||||
return GetFileLocation(useAesKeysTxt ? "aes_keys.txt" : "keys.bin");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,7 +270,6 @@ More than one path can be specified at a time.");
|
||||
FileType.NDSi => _tools[FileType.NDS],
|
||||
FileType.iQueDS => _tools[FileType.NDS],
|
||||
FileType.N3DS => _tools[FileType.N3DS],
|
||||
//FileType.N3DSCIA => _tools[FileType.N3DSCIA],
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -257,21 +301,60 @@ More than one path can be specified at a time.");
|
||||
}
|
||||
else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase) // Standard carts
|
||||
|| filename.EndsWith(".3ds.dec", StringComparison.OrdinalIgnoreCase) // Decrypted carts/images
|
||||
|| filename.EndsWith(".3ds.enc", StringComparison.OrdinalIgnoreCase)) // Encrypted carts/images
|
||||
|| filename.EndsWith(".3ds.enc", StringComparison.OrdinalIgnoreCase) // Encrypted carts/images
|
||||
|| filename.EndsWith(".cci", StringComparison.OrdinalIgnoreCase)) // Development carts/images
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo 3DS");
|
||||
return FileType.N3DS;
|
||||
}
|
||||
// else if (filename.EndsWith(".cia", StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// Console.WriteLine("File recognized as Nintendo 3DS CIA [CAUTION: NOT WORKING CURRENTLY]");
|
||||
// return FileType.N3DSCIA;
|
||||
// }
|
||||
|
||||
Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds");
|
||||
Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds, *.cci");
|
||||
return FileType.NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for a file in local and config directories
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to check in local and config directories</param>
|
||||
/// <returns>The full path to the file if found, null otherwise</returns>
|
||||
/// <remarks>
|
||||
/// This method looks in the following locations:
|
||||
/// - %HOME%/.config/ndecrypt
|
||||
/// - Assembly location directory
|
||||
/// - Process runtime directory
|
||||
/// </remarks>
|
||||
private static string? GetFileLocation(string filename)
|
||||
{
|
||||
// User home directory
|
||||
#if NET20 || NET35
|
||||
string homeDir = Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
|
||||
homeDir = Path.Combine(Path.Combine(homeDir, ".config"), "ndecrypt");
|
||||
#else
|
||||
string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
homeDir = Path.Combine(homeDir, ".config", "ndecrypt");
|
||||
#endif
|
||||
if (File.Exists(Path.Combine(homeDir, filename)))
|
||||
return Path.Combine(homeDir, filename);
|
||||
|
||||
// Local directory
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
string runtimeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
#else
|
||||
string runtimeDir = AppContext.BaseDirectory;
|
||||
#endif
|
||||
if (File.Exists(Path.Combine(runtimeDir, filename)))
|
||||
return Path.Combine(runtimeDir, filename);
|
||||
|
||||
// Process directory
|
||||
using var processModule = System.Diagnostics.Process.GetCurrentProcess().MainModule;
|
||||
string applicationDirectory = Path.GetDirectoryName(processModule?.FileName) ?? string.Empty;
|
||||
if (File.Exists(Path.Combine(applicationDirectory, filename)))
|
||||
return Path.Combine(applicationDirectory, filename);
|
||||
|
||||
// No file was found
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out the hashes of a file to a named file
|
||||
/// </summary>
|
||||
|
||||
79
README.md
79
README.md
@@ -1,7 +1,6 @@
|
||||
# NDecrypt
|
||||
|
||||
[](https://ci.appveyor.com/project/mnadareski/ndecrypt)
|
||||
[](https://github.com/SabreTools/NDecrypt/actions/workflows/build_program.yml)
|
||||
[](https://github.com/SabreTools/NDecrypt/actions/workflows/build_and_test.yml)
|
||||
|
||||
A simple tool for simple people.
|
||||
|
||||
@@ -15,7 +14,7 @@ This is a code port of 3 different programs:
|
||||
|
||||
## No really, what is this?
|
||||
|
||||
This tool allows you to encrypt and decrypt your personally dumped NDS and N3DS roms with minimal hassle. The only caveat right now is that you need a `keys.bin` or `aes_keys.txt` file for your personally obtained encryption keys.
|
||||
This tool allows you to encrypt and decrypt your personally dumped Nintendo DS, 3DS, and New 3DS cart images with minimal hassle.
|
||||
|
||||
## Where do I find it?
|
||||
|
||||
@@ -25,45 +24,73 @@ For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/N
|
||||
|
||||
## So how do I use this?
|
||||
|
||||
NDecrypt.exe <operation> [flags] <path> ...
|
||||
Usage: NDecrypt <operation> [flags] <path> ...
|
||||
|
||||
Possible values for <operation>:
|
||||
e, encrypt - Encrypt the input files
|
||||
d, decrypt - Decrypt the input files
|
||||
i, info - Output file information
|
||||
|
||||
Possible values for [flags] (one or more can be used):
|
||||
-c, --citra - Enable using aes_keys.txt instead of keys.bin
|
||||
-dev, --development - Enable using development keys, if available
|
||||
-f, --force - Force operation by avoiding sanity checks
|
||||
-h, --hash - Output size and hashes to a companion file
|
||||
-k, --keyfile <path> - Path to keys.bin or aes_keys.txt
|
||||
-c, --config <path> Path to config.json
|
||||
-a, --aes-keys Enable using aes_keys.txt instead of keys.bin
|
||||
-k, --keyfile <path> Path to keys.bin or aes_keys.txt
|
||||
-d, --development Enable using development keys, if available
|
||||
-f, --force Force operation by avoiding sanity checks
|
||||
-h, --hash Output size and hashes to a companion file
|
||||
|
||||
<path> can be any file or folder that contains uncompressed items.
|
||||
More than one path can be specified at a time.
|
||||
|
||||
**Note:** This overwrites the input files, so make backups if you're working on your original, personal dumps.
|
||||
### Additional Notes
|
||||
|
||||
**Note:** Mixed folders or inputs are also accepted, you can decrypt or encrypt multiple files, regardless of their type. This being said, you can only do encrypt OR decrypt at one time.
|
||||
- Input files are overwritten, even if they are only partially processed. You should make backups of the files you're working on if you're worried about this.
|
||||
- Mixed folders or inputs are also accepted, you can decrypt or encrypt multiple files, regardless of their type. This being said, you can only do encrypt _OR_ decrypt at one time.
|
||||
- Required files will automatically be searched for in the application runtime directory as well as `%HOME%/.config/ndecrypt`, also known as `%USERPROFILE%\.config\ndecrypt` on Windows.
|
||||
- If found, `config.json` will take priority over both `keys.bin` and `aes_keys.txt`, even if `-a` and/or `-k` are defined. You've been warned.
|
||||
|
||||
## I feel like something is missing...
|
||||
## I feel like something is missing
|
||||
|
||||
You (possibly*) are! In fact, you may be asking, "Hey, what was that `keys.bin` you mentioned??". I'm glad you asked. It's used only for Nintendo 3DS and New 3DS files. Since some people don't like reading code, you need the 9 16-bit keys in little endian format (most common extraction methods produce big endian, so keep that in mind). It's recommended that you fill with 0x00 if you don't have access to a particular value so it doesn't mess up the read. They need to be in the following order:
|
||||
There are 3 major files that you can use to give NDecrypt that extra _oomph_ of functionality that it really needs. That is, you can't do any encryption or decryption without at least one of these present. I can't give you the files and I can't generate them for you on the fly with the correct values. Keys are a thorny thing and I just do not want to deal with them. Values are validated, at least, but you'll only get yelled at on run if one of them is wrong. Don't worry, they're just disabled, not removed.
|
||||
|
||||
| Name | `aes_keys.txt` Entry |
|
||||
| --- | --- |
|
||||
| Hardware constant | `generator` |
|
||||
| KeyX0x18 | `slot0x18KeyX` |
|
||||
| KeyX0x1B | `slot0x1BKeyX` |
|
||||
| KeyX0x25 | `slot0x25KeyX` |
|
||||
| KeyX0x2C | `slot0x2CKeyX` |
|
||||
| DevKeyX0x18 | **UNMAPPED** |
|
||||
| DevKeyX0x1B | **UNMAPPED** |
|
||||
| DevKeyX0x25 | **UNMAPPED** |
|
||||
| DevKeyX0x2C | **UNMAPPED** |
|
||||
This convenient table gives an overview of the 3 supported types, the keys that they provide, as well as an even more convenient map to a well-known external tool's configuration format.
|
||||
|
||||
The last 4 are only required if you use the `-dev` flag. Once again, don't ask for these, please. If you're missing a required key, then things won't work. Don't blame me, blame society. Or something. And yes, I'll fix this being required across the board at some point.
|
||||
| `config.json` | `keys.bin` order | `aes_keys.txt` | rom-properties `keys.conf` |
|
||||
| --- | --- | --- | --- |
|
||||
| `NitroEncryptionData` | **N/A** | **UNMAPPED** | **UNMAPPED** |
|
||||
| `AESHardwareConstant` | 1 | `generator` | `ctr-scrambler` |
|
||||
| `KeyX0x18` | 2 | `slot0x18KeyX` | `ctr-Slot0x18KeyX` |
|
||||
| `KeyX0x1B` | 3 | `slot0x1BKeyX` | `ctr-Slot0x1BKeyX` |
|
||||
| `KeyX0x25` | 4 | `slot0x25KeyX` | `ctr-Slot0x25KeyX` |
|
||||
| `KeyX0x2C` | 5 | `slot0x2CKeyX` | `ctr-Slot0x2CKeyX` |
|
||||
| `DevKeyX0x18` | 6 | **UNMAPPED** | `ctr-dev-Slot0x18KeyX` |
|
||||
| `DevKeyX0x1B` | 7 | **UNMAPPED** | `ctr-dev-Slot0x1BKeyX` |
|
||||
| `DevKeyX0x25` | 8 | **UNMAPPED** | `ctr-dev-Slot0x25KeyX` |
|
||||
| `DevKeyX0x2C` | 9 | **UNMAPPED** | `ctr-dev-Slot0x2CKeyX` |
|
||||
|
||||
*If you choose to use the `-c` option, you can instead provide your personally filled out `aes_keys.txt` file in the same folder as NDecrypt and that can be used instead. Please note that if you choose to use this file, you will not be able to use the `-dev` flag. If you forget and happen to use them together, NDecrypt will disable that flag for you. You're welcome.
|
||||
**Note:** `Dev*` keys are not required for the vast majority of normal operations. They're only used if the `-d` option is included. Working with your own retail carts will pretty much never require these, so don't drive yourself silly dealing with them.
|
||||
|
||||
**Note:** The `NitroEncryptionData` field is also known as the "Blowfish table" for Nintendo DS carts. It's stored in the same hex string format as the other keys. There's some complicated stuff about how it's used and where it's stored, but all you need to know is that it wasn't required for `keys.bin` and `aes_keys.txt` but will be for `config.json`.
|
||||
|
||||
**Community Note:** If you would like to try out the new `config.json` format below and already have either `keys.bin` or `aes_keys.txt`, consider using [this helpful community-made script](https://gist.github.com/Dimensional/82f212a0b35bcf9caaa2bc9a70b3a92a) to make your life a bit easier.
|
||||
|
||||
### `config.json`
|
||||
|
||||
The up-and-coming, shiny, new, exciting, JSON-based format for storing the encryption keys that you need for Nintendo DS, 3DS, and New 3DS. This JSON file is not generated by anything, but maps pretty much one-to-one with the code inside of NDecrypt, making it super convenient to use. Keys provided need to be hex strings (e.g. `"AABBCCDD"`). Any keys that are left with `null` or `""` as the value will be ignored. See [the sample config](https://github.com/SabreTools/NDecrypt/blob/master/config-default.json) that I've nicely generated for you. You're welcome.
|
||||
|
||||
This is used if it's found, even if you have a `keys.bin` file or if you're using the `-a` flag. It's intentionally very bullish about being used because this will be the singular format for keys in the future. I know I mentioned this above as well, but I also know users don't like reading.
|
||||
|
||||
In the future, this file will be automatically generated on first run along with some cutesy little message telling you to fill it out when you get a chance. It's not doing it right now because I don't want to confuse users. Including those reading this. How meta.
|
||||
|
||||
### `keys.bin`
|
||||
|
||||
This is the OG of NDecrypt key file formats. It's a weird, binary blob of a format that is composed of little-endian values (most common extraction methods produce big endian, so keep that in mind). It's only compatible wtih Nintendo 3DS and New 3DS keys and is incredibly inflexible in its layout. The little-endianness of it is a relic of how keys were handled in-code previously and I really can't fix it now. If you don't have a key, it needs to be filled with `0x00` bytes so it doesn't mess up the read. Yeah.
|
||||
|
||||
Oddly, this gets confused with some similar format that GodMode9 works with, but it has nothing to do with it. If you try to use one of those files in place of this one, something will probably break. It wasn't intentional, I just didn't look ahead of time. See the table in the main part of this section for the order the keys need to be stored in.
|
||||
|
||||
### `aes_keys.txt`
|
||||
|
||||
This is an INI-based format that was super popular among 3DS emulators and probably still is. To use this over `keys.bin`, the `-a` flag has to be included or else it won't be found. Yes, even if `keys.bin` isn't even in the folder. Weird thing, I know, but just roll with it please. The one major downside to this is that development keys can't be defined in this format. If you forget this and use `-d` anyway, NDecrypt will disable that flag for you. You're welcome.
|
||||
|
||||
## But does it work?
|
||||
|
||||
|
||||
26
appveyor.yml
26
appveyor.yml
@@ -1,26 +0,0 @@
|
||||
# version format
|
||||
version: 0.2.5-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
# vm template
|
||||
image: Visual Studio 2022
|
||||
|
||||
# install dependencies
|
||||
install:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- git submodule update --init --recursive
|
||||
|
||||
# build step
|
||||
build_script:
|
||||
- dotnet build
|
||||
|
||||
# success/failure tracking
|
||||
on_success:
|
||||
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
|
||||
- ps: ./send.ps1 success $env:WEBHOOK_URL
|
||||
on_failure:
|
||||
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
|
||||
- ps: ./send.ps1 failure $env:WEBHOOK_URL
|
||||
12
config-default.json
Normal file
12
config-default.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"NitroEncryptionData": null,
|
||||
"AESHardwareConstant": null,
|
||||
"KeyX0x18": null,
|
||||
"KeyX0x1B": null,
|
||||
"KeyX0x25": null,
|
||||
"KeyX0x2C": null,
|
||||
"DevKeyX0x18": null,
|
||||
"DevKeyX0x1B": null,
|
||||
"DevKeyX0x25": null,
|
||||
"DevKeyX0x2C": null
|
||||
}
|
||||
24
publish-nix.sh
Normal file → Executable file
24
publish-nix.sh
Normal file → Executable file
@@ -10,13 +10,17 @@
|
||||
|
||||
# Optional parameters
|
||||
USE_ALL=false
|
||||
INCLUDE_DEBUG=false
|
||||
NO_BUILD=false
|
||||
NO_ARCHIVE=false
|
||||
while getopts "uba" OPTION; do
|
||||
while getopts "udba" OPTION; do
|
||||
case $OPTION in
|
||||
u)
|
||||
USE_ALL=true
|
||||
;;
|
||||
d)
|
||||
INCLUDE_DEBUG=true
|
||||
;;
|
||||
b)
|
||||
NO_BUILD=true
|
||||
;;
|
||||
@@ -39,6 +43,7 @@ COMMIT=$(git log --pretty=%H -1)
|
||||
# Output the selected options
|
||||
echo "Selected Options:"
|
||||
echo " Use all frameworks (-u) $USE_ALL"
|
||||
echo " Include debug builds (-d) $INCLUDE_DEBUG"
|
||||
echo " No build (-b) $NO_BUILD"
|
||||
echo " No archive (-a) $NO_ARCHIVE"
|
||||
echo " "
|
||||
@@ -49,7 +54,7 @@ RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64" "o
|
||||
|
||||
# Use expanded lists, if requested
|
||||
if [ $USE_ALL = true ]; then
|
||||
FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0")
|
||||
FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0")
|
||||
fi
|
||||
|
||||
# Create the filter arrays
|
||||
@@ -64,6 +69,9 @@ if [ $NO_BUILD = false ]; then
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Packages
|
||||
dotnet pack NDecrypt.Core/NDecrypt.Core.csproj --output $BUILD_FOLDER
|
||||
|
||||
# Build Program
|
||||
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${RUNTIMES[@]}"; do
|
||||
@@ -88,14 +96,14 @@ if [ $NO_BUILD = false ]; then
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish NDecrypt/NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
@@ -128,8 +136,8 @@ if [ $NO_ARCHIVE = false ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
# Only include Debug if set
|
||||
if [ $INCLUDE_DEBUG = true ]; then
|
||||
cd $BUILD_FOLDER/NDecrypt/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/NDecrypt_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
fi
|
||||
|
||||
@@ -12,6 +12,10 @@ param(
|
||||
[Alias("UseAll")]
|
||||
[switch]$USE_ALL,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("IncludeDebug")]
|
||||
[switch]$INCLUDE_DEBUG,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoBuild")]
|
||||
[switch]$NO_BUILD,
|
||||
@@ -30,6 +34,7 @@ $COMMIT = git log --pretty=format:"%H" -1
|
||||
# Output the selected options
|
||||
Write-Host "Selected Options:"
|
||||
Write-Host " Use all frameworks (-UseAll) $USE_ALL"
|
||||
Write-Host " Include debug builds (-IncludeDebug) $INCLUDE_DEBUG"
|
||||
Write-Host " No build (-NoBuild) $NO_BUILD"
|
||||
Write-Host " No archive (-NoArchive) $NO_ARCHIVE"
|
||||
Write-Host " "
|
||||
@@ -40,7 +45,7 @@ $RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'os
|
||||
|
||||
# Use expanded lists, if requested
|
||||
if ($USE_ALL.IsPresent) {
|
||||
$FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0')
|
||||
$FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0')
|
||||
}
|
||||
|
||||
# Create the filter arrays
|
||||
@@ -55,6 +60,9 @@ if (!$NO_BUILD.IsPresent) {
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Package
|
||||
dotnet pack NDecrypt.Core\NDecrypt.Core.csproj --output $BUILD_FOLDER
|
||||
|
||||
# Build Program
|
||||
foreach ($FRAMEWORK in $FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $RUNTIMES) {
|
||||
@@ -75,15 +83,15 @@ if (!$NO_BUILD.IsPresent) {
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
else {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish NDecrypt\NDecrypt.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
@@ -112,8 +120,8 @@ if (!$NO_ARCHIVE.IsPresent) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
# Only include Debug if set
|
||||
if ($INCLUDE_DEBUG.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\NDecrypt\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\NDecrypt_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user