using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using TwlBackupBlock;
// TODO もっとアサートを入れましょう
namespace FalsifyTwlBackup
{
class Program
{
// ブロックのインデックス
private const int INDEX_BANNER = 0;
private const int INDEX_HEADER = 1;
private const int INDEX_SIGNATURE = 2;
private const int INDEX_TMD = 3;
private const int INDEX_CONTENT = 4;
private const int INDEX_PUBLIC_SAVE = 5;
private const int INDEX_SUB_BANNER = 6;
// データの挿入位置
private const int NONE = -1;
private const int FRONT_BANNER = 10; // ブロック内データ本体の前
private const int FRONT_HEADER = 11;
private const int FRONT_SIGNATURE = 12;
private const int FRONT_TMD = 13;
private const int FRONT_CONTENT = 14;
private const int FRONT_PUBLIC_SAVE = 15;
private const int FRONT_SUB_BANNER = 16;
private const int REAR_BANNER = 20; // ブロック内データ本体の後
private const int REAR_HEADER = 21;
private const int REAR_SIGNATURE = 22;
private const int REAR_TMD = 23;
private const int REAR_CONTENT = 24;
private const int REAR_PUBLIC_SAVE = 25;
private const int REAR_SUB_BANNER = 26;
private const int REAR_DATA = 99; // データ全体の後
// 改ざんパターンの引き数
private const int ALL = 0;
private const int CAT100 = 1;
private const int CAT200 = 2;
private const int CAT300 = 3;
private const int CAT400 = 4;
private const int CAT500 = 5;
// 各カテゴリでの最大番号
private const int MAX_OF_CAT100 = 118;
private const int MAX_OF_CAT200 = 214;
private const int MAX_OF_CAT300 = 303;
private const int MAX_OF_CAT400 = 415;
private const int MAX_OF_CAT500 = 515;
private static readonly int[,] NUM_OF_PATTERN = new int[,]
{{100, MAX_OF_CAT100},
{200, MAX_OF_CAT200},
{300, MAX_OF_CAT300},
{400, MAX_OF_CAT400},
{500, MAX_OF_CAT500}};
// MACとハッシュセット更新の有無
private const int NC_MAC_AND_HASHSET = 0;
private const int CHG_MAC_AND_HASHSET = 1;
private const int BLOCK_SIZE = 7; // バックアップデータのブロックサイズ
private const int IMPROPER_DATA_SIZE = 16 * 1024; // 不正なデータのサイズ(16 KB)
private const int KEY_SIZE = 16; // 鍵のサイズ(16 Byte)
private const int HASH_SIZE = 32; // ハッシュのサイズ(32 Byte)
private const int AES_BLOCK_SIZE = 16 * 8; // 暗号化のブロックサイズ(16Byte)
private const string EXTENSION = ".bin"; // TwlBackupファイルの拡張子
///
/// 使用方法を表示します。
///
static void Usage()
{
Console.WriteLine("Usage: ");
Console.WriteLine("./FalsifyingTwlBackup.exe BACKUPFILE_NAME KEYFILE_NAME MACKEYFALE_NAME MODE");
Console.WriteLine(" BACKUPFILE_NAME : *.bin");
Console.WriteLine(" KEYFILE_NAME : *.txt");
Console.WriteLine(" MACKEYFALE_NAME : *.txt\n");
Console.WriteLine(" MODE");
Console.WriteLine(" -all (default) : output all falsifying pattern");
Console.WriteLine(" -cat CAT_NUM : output all pattern of CAT_NUM category");
Console.WriteLine(" ex) -cat 100 -> falsifying 100,101,102....");
Console.WriteLine(" -each PAT_NUM : output PAT_NUM pattern ");
Console.WriteLine(" ex) -each 204 -> falsifying 204 only");
Environment.Exit(-1);
}
///
/// 拡張子の判別を行います。
///
/// 確認対象となるファイル名
/// 拡張子
static void CheckExtension(string args, string extension)
{
Debug.Assert(args.Length > extension.Length);
if (args.ToLower().EndsWith(extension) == false)
{
Console.WriteLine("{0} is not [\"{1}\"] file!", args, extension);
Usage();
}
return;
}
///
/// ファイルの有無を確認します。
///
/// 確認対象となるファイルのパス
static void CheckFileExists(string args)
{
Debug.Assert(args.Length != 0);
if (!File.Exists(args))
{
Console.WriteLine("\"{0}\" is not exists!\n", args);
Usage();
}
}
///
/// コマンドライン引数のオプションから処理を決定するためのフラグを立てる
///
/// コマンドライン引数の格納された文字列
/// 処理判別のためのフラグ
static int GetFalsifyingType(string[] args)
{
Debug.Assert(args.Length >= 3);
if (args.Length > 3)
{
// -allのとき
if (args[3].ToUpper().TrimStart('-') == "ALL")
{
return ALL;
}
// -catのとき
else if (args[3].ToUpper().TrimStart('-') == "CAT")
{
if (4 >= args.Length) // CATなのに次の引数が無かったら帰れ!
{
Usage();
}
switch (args[4])
{
case "100": return CAT100;
case "200": return CAT200;
case "300": return CAT300;
case "400": return CAT400;
case "500": return CAT500;
default: Usage(); break; // never reach
}
}
// -eachのとき
else if (args[3].ToUpper().TrimStart('-') == "EACH")
{
if (4 >= args.Length) // EACHなのに次の引数が無かったら帰れ!
{
Usage();
}
// TODO 存在しない処理番号をはじくように
else
{
return Convert.ToInt16(args[4]);
}
}
}
// それ以外
return ALL;
}
///
/// 指定されたbyteサイズで、0のみのbyte列を作成します。
///
/// 作成するデータのサイズ[単位:byte]
/// 作成したデータのbyte列
// TODO 不正データの作成方法を選べるようにしてもいいかも
static byte[] CreateImproperData(int dataSize)
{
byte[] data = new byte[dataSize];
for (int i = 0; i < data.Length; i++)
{
//data[i] = (byte)(i);
data[i] = 0;
}
return data;
}
///
/// データの差し替えを行います。
///
/// 上書き対象となるデータ本体
/// 上書きする不正データ
static void ReplaceImproperData(AbstractBody dataBlocks, byte[] impData)
{
for (int i = 0; i < impData.Length; i++)
{
if (i < dataBlocks.Length) // 上書き先のデータサイズを超えませんように
{
dataBlocks[i] = impData[i];
}
}
return;
}
///
/// ※未使用
/// 暗号化に使用するIVを作成します。
/// AES方式で、ブロックサイズはAES_BLOCK_SIZE[bit]と同じです。
///
/// 生成したivのbyte列
static byte[] GenerateNewIv()
{
// 暗号化用IV作成
/*
* [参考] C#(.NET Framework)のAESを使ってファイルを暗号化してみる
* http://hibara.sblo.jp/article/43505136.html
*/
System.Security.Cryptography.AesCryptoServiceProvider
aes = new System.Security.Cryptography.AesCryptoServiceProvider();
aes.BlockSize = AES_BLOCK_SIZE; // AESはブロックサイズ、キー長ともに16byte
aes.GenerateIV(); // IVの設定(ブロックサイズと同サイズ = 16byte)
byte[] byteIV = aes.IV; // IVをバイト列に
return byteIV;
}
///
/// 署名のハッシュセットを変更します。
///
/// 変更対象となる署名ブロックを有するBlocks
/// 新しいハッシュ値
/// 変更するブロックのインデックス
static void ChangeHashSet(Blocks dataBlocks, byte[] hash, int index)
{
Debug.Assert((index <= BLOCK_SIZE - 1) && (index >= 0));
SignatureBody signature;
signature = (SignatureBody)dataBlocks.signature.body;
switch (index)
{
case INDEX_BANNER:
hash.CopyTo(signature.digest.banner, 0);
break;
case INDEX_HEADER:
hash.CopyTo(signature.digest.header, 0);
break;
case INDEX_TMD:
case INDEX_CONTENT:
case INDEX_PUBLIC_SAVE:
case INDEX_SUB_BANNER:
hash.CopyTo(signature.digest.section[index - (INDEX_SIGNATURE + 1)], 0);
break;
default:
Debug.Assert(false);
break;
}
}
///
/// byte列を結合します。
///
/// 前半部分に来るbyte列
/// 後半部分に来るbyte列
/// 連結後のbyte列
static byte[] MergeByteArray(byte[] front, byte[] rear)
{
/* 私のコード(色々アレだよ;;)
byte[] merged = new byte[front.Length + rear.Length];
for (int i = 0; i < (front.Length + rear.Length); i++)
{
if (i < front.Length)
{
merged[i] = front[i];
}
else
{
merged[i] = rear[i - front.Length];
}
}
*/
/* 田中さん案1(中にifは使わないほうがいいよ)
for (int i = 0; i < front.Length; i++)
{
merged[i] = front[i];
}
for (int i = 0; i < rear.Length; i++)
{
merged[i + front.Length] = rear[i];
}
*/
/* 田中さん案2(ライブラリを使うとコピーしてるのも分かりやすいよ^^)
Array.Copy(front, 0, merged, 0, front.Length);
Array.Copy(rear, 0, merged, front.Length, rear.Length);
*/
// 田中さん案3(結局1行で出来ちゃったよ;;)
return front.Concat(rear).ToArray();
}
///
/// byte列をbinファイルに出力します。
///
/// ファイル名となる改ざんパターンの番号
/// 出力ディレクトリのパス
/// ファイルに出力するデータ
static void OutputFalsifiedData(int improperNo, string outFolderPath, byte[] data)
{
string outFalsifiedDataPath = Convert.ToString(improperNo) + EXTENSION; // ファイル名を改ざん番号から生成
outFalsifiedDataPath = Path.Combine(outFolderPath, outFalsifiedDataPath); // 出力ディレクトリとファイル名を結合
File.WriteAllBytes(outFalsifiedDataPath, data); // 出力
}
///
/// データ本体とMACとIVをひとつのブロックに結合します。
/// 必要に応じて不正データを挿入します。
///
/// 暗号化するBlockを持つBlocks
/// 暗号化するBlockのインデックス
/// バックアップデータに関するインスタンス
/// 挿入する不正データ
/// 挿入する位置
/// 格納するMAC
/// MACを算出するためのハッシュ値
/// データ本体、MAC、IVを順に結合したbyte列
static byte[] EncryptBlock(Blocks dataBlocks, int index, Properties prop, byte[] impData, int insertPos, byte[] byteMac, byte[] byteHash = null)
{
byte[] encryptedBlock = new byte[0];
// (データ本体の前に不正データ挿入)
if (insertPos >= FRONT_BANNER && insertPos <= FRONT_SUB_BANNER)
{
if (index == insertPos - 10)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
}
// データ本体を暗号化
encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[index].body.GetBytes()); // bodyを暗号化
// (データ本体の後に不正データ挿入)
if (insertPos >= REAR_BANNER && insertPos <= REAR_SUB_BANNER)
{
if (index == insertPos - 20)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
}
if (byteHash != null)
{
byte[] buff = Utility.GetSha256(encryptedBlock); // 平文からSHA256ハッシュ算出
buff.CopyTo(byteHash, 0);
byteMac = Utility.GetAesCmac(byteHash, prop.macKeyData); // SHA256ハッシュからMACを算出
}
encryptedBlock = Utility.EncryptBody(encryptedBlock, prop.keyData, dataBlocks[index].iv); // データ本体を暗号化
encryptedBlock = MergeByteArray(encryptedBlock, byteMac); // MACを結合
encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[index].iv); // IVを結合
// (データブロックの後に不正データ挿入)
if (index == INDEX_SUB_BANNER && insertPos == REAR_DATA)
{
encryptedBlock = MergeByteArray(encryptedBlock, impData);
}
return encryptedBlock;
}
///
/// 各ブロックを暗号化した後、1つのbute列に結合します。
///
/// 暗号化するデータ
/// バックアップデータに関するインスタンス
/// 不正データ
/// 不正データを挿入する位置
/// ハッシュセット更新の有無
/// 全てのBlockを暗号化し、連結したbyte列
static byte[] MergeEncryptBody(Blocks dataBlocks, Properties prop, byte[] impData, int insertPos, int chgMacHash)
{
byte[] outData = new byte[0];
byte[][] encryptedBlocks = new byte[BLOCK_SIZE][];
byte[][] byteHash = new byte[BLOCK_SIZE][];
byte[][] byteMac = new byte[BLOCK_SIZE][];
//----------------------
// 各ブロックを暗号化
// ( MAC & HASH SET更新する場合 )
if (chgMacHash == CHG_MAC_AND_HASHSET)
{
for (int i = 0; i < BLOCK_SIZE; i++)
{ // 署名部以外を暗号化&byte列に結合
byteHash[i] = new byte[HASH_SIZE];
if (i != INDEX_SIGNATURE)
{
encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, byteMac[i], byteHash[i]); // ブロックを暗号化&結合
ChangeHashSet(dataBlocks, byteHash[i], i); // ハッシュセットを更新
}
}
// 最後に署名部を暗号化&byte列に結合
byteHash[INDEX_SIGNATURE] = Utility.GetSha256(dataBlocks[INDEX_SIGNATURE].body.GetBytes()); // 平文からSHA256ハッシュ算出
byteMac[INDEX_SIGNATURE] = Utility.GetAesCmac(byteHash[INDEX_SIGNATURE], prop.macKeyData); // SHA256ハッシュからMACを算出
encryptedBlocks[INDEX_SIGNATURE] = EncryptBlock(dataBlocks, INDEX_SIGNATURE, prop, impData, insertPos, byteMac[INDEX_SIGNATURE]);
}
// ( MAC & HASH SET更新しない場合 )
else
{
for (int i = 0; i < BLOCK_SIZE; i++)
{
encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, dataBlocks[i].mac); // ブロックを暗号化&結合
}
}
//----------------------
// 暗号化した各ブロックを結合
for (int i = 0; i < BLOCK_SIZE; i++)
{
outData = MergeByteArray(outData, encryptedBlocks[i]);
}
return outData;
}
// static void FalsifyingData(int improperNo, Properties prop, byte[] improperData)
//------------------------------------------------------------
// パターン番号に応じた改ざんを実行
// [in] int improperNo : 改ざんパターン番号
// [in] Properties prop : バックアップデータに関するインスタンス
// [in] byte[] improperData : 改ざんに使用する不正データ
//------------------------------------------------------------
///
/// パターン番号に応じた改ざんを実行
///
///
///
///
static void FalsifyingData(int improperNo, Properties prop, byte[] improperData)
{
Blocks blocksForFalsifying = (Blocks)prop.decryptedBlocks.Clone();
HeaderBody header;
header = (HeaderBody)blocksForFalsifying.header.body;
SignatureBody signature;
signature = (SignatureBody)blocksForFalsifying.signature.body;
byte[] outData;
//[1xx] 不正なデータ挿入
//------------------------------------
//[テンプレート]
//case (番号):
// (差し替えの場合)ReplaceImproperData(blocksForFalsifying[(差替え場所のindex)].body, improperData);
// outData = MergeEncryptBody(blocksForFalsifying, prop.keyData, prop.macKeyData, improperData, (不正データ挿入箇所), NC_MAC_AND_HASHSET);
// OutputFalsifyingData(improperNo, prop.outFolderPath, outData);
// break;
switch (improperNo)
{
case 100:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 101:
ReplaceImproperData(blocksForFalsifying[INDEX_BANNER].body, improperData);
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 102:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 103:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_HEADER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 104:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_HEADER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 105:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_SIGNATURE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 106:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_SIGNATURE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 107:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_TMD, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 108:
ReplaceImproperData(blocksForFalsifying[INDEX_TMD].body, improperData);
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 109:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_TMD, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 110:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_CONTENT, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 111:
ReplaceImproperData(blocksForFalsifying[INDEX_CONTENT].body, improperData);
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 112:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_CONTENT, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 113:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 114:
ReplaceImproperData(blocksForFalsifying[INDEX_PUBLIC_SAVE].body, improperData);
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 115:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 116:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_SUB_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 117:
ReplaceImproperData(blocksForFalsifying[INDEX_SUB_BANNER].body, improperData);
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 118:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_SUB_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
//[2xx] ヘッダブロック改ざん
//------------------------------------
//[テンプレート]
//case (番号):
// header.(変更箇所)++;
// outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
// OutputFalsifyingData(improperNo, prop.outFolderPath, outData);
// break;
case 200:
header.signature++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 201:
header.companyCode++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 202:
header.version++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 203:
header.uniqueId[0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 204:
header.hardwareId[0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 205:
header.titleId++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 206:
header.requiredSize++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 207:
header.fileSizes[0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 208:
header.fileSizes[1]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 209:
header.fileSizes[2]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 210:
header.fileSizes[3]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 211:
header.contentId++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 212:
header.tmdReserved.publicSaveSize++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 213:
header.tmdReserved.privateSaveSize++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 214:
header.fileSizes[2]++;
header.tmdReserved.publicSaveSize++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
//[3xx] 署名ブロック改ざん
//------------------------------------
//[テンプレート]
//case (番号):
// signature.(変更箇所)++;
// outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
// OutputFalsifyingData(improperNo, prop.outFolderPath, outData);
// break;
case 300:
signature.digest.banner[0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 301:
signature.sign[0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 302:
signature.cert[0][0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 303:
signature.digest.banner[0]++;
signature.sign[0]++;
signature.cert[0][0]++;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
//[4xx] 不正データ挿入+サイズ整合対応
//------------------------------------
//[テンプレート]
//case (番号):
// header.(変更箇所) += IMPROPER_DATA_SIZE;
// outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, (不正データ挿入箇所), NC_MAC_AND_HASHSET);
// OutputFalsifyingData(improperNo, prop.outFolderPath, outData);
// break;
case 400:
header.requiredSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 401:
header.requiredSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_DATA, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 402:
header.fileSizes[0] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_TMD, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 403:
header.fileSizes[0] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_TMD, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 404:
header.fileSizes[1] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_CONTENT, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 405:
header.fileSizes[1] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_CONTENT, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 406:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 407:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 408:
header.fileSizes[3] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_SUB_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 409:
header.fileSizes[3] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_SUB_BANNER, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 410:
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 411:
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 412:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 413:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 414:
header.tmdReserved.privateSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 415:
header.tmdReserved.privateSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
// [5xx] 不正データ挿入+サイズ整合対応 (MAC、ハッシュセット対応)
//--------------------------------------------------------------------
//[テンプレート]
//case (番号):
// header.(変更箇所) += IMPROPER_DATA_SIZE;
// outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, (不正データ挿入箇所), CHG_MAC_AND_HASHSET);
// OutputFalsifyingData(improperNo, prop.outFolderPath, outData);
// break;
case 500:
header.requiredSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_BANNER, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 501:
header.requiredSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_DATA, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 502:
header.fileSizes[0] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_TMD, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 503:
header.fileSizes[0] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_TMD, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 504:
header.fileSizes[1] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_CONTENT, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 505:
header.fileSizes[1] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_CONTENT, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 506:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 507:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 508:
header.fileSizes[3] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_SUB_BANNER, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 509:
header.fileSizes[3] += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_SUB_BANNER, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 510:
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 511:
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 512:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 513:
header.fileSizes[2] += IMPROPER_DATA_SIZE;
header.tmdReserved.publicSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 514:
header.tmdReserved.privateSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, FRONT_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
case 515:
header.tmdReserved.privateSaveSize += IMPROPER_DATA_SIZE;
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, REAR_PUBLIC_SAVE, CHG_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
// 未加工のデータ
case 999:
outData = MergeEncryptBody(blocksForFalsifying, prop, improperData, NONE, NC_MAC_AND_HASHSET);
OutputFalsifiedData(improperNo, prop.outFolderPath, outData);
break;
}
}
// static void SwitchFalsifyingPattern(int falsifyingType, Properties prop)
//------------------------------------------------------------
// 作成する改ざんデータのパターンを決定。
// [in] int falsifyingType : 出力する改ざんデータのパターン
// [in] Properties prop : バックアップデータに関するインスタンス
//------------------------------------------------------------
///
///
///
///
///
static void SwitchFalsifyingPattern(int falsifyingType, Properties prop)
{
// 改ざんに使用する不正なデータ作成
byte[] improperData = CreateImproperData(IMPROPER_DATA_SIZE);
switch (falsifyingType)
{
case ALL: // 全パターン
Console.WriteLine(" Falsifying \"all pattern\".");
for (int i = 0; i < NUM_OF_PATTERN.GetLength(0); i++)
{
for (int j = NUM_OF_PATTERN[i, 0]; j <= NUM_OF_PATTERN[i, 1]; j++)
{
FalsifyingData(j, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", j);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[i, 0]);
}
break;
case CAT100: // 1xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT100 - 1, 0]);
for (int i = 100; i <= MAX_OF_CAT100; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT100 - 1, 0]);
break;
case CAT200: // 2xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT200 - 1, 0]);
for (int i = 200; i <= MAX_OF_CAT200; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT200 - 1, 0]);
break;
case CAT300: // 3xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT300 - 1, 0]);
for (int i = 300; i <= MAX_OF_CAT300; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT300 - 1, 0]);
break;
case CAT400: // 4xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT400 - 1, 0]);
for (int i = 400; i <= MAX_OF_CAT400; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT400 - 1, 0]);
break;
case CAT500: // 5xx系列全て
Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[CAT500 - 1, 0]);
for (int i = 500; i <= MAX_OF_CAT500; i++)
{
FalsifyingData(i, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.", i);
}
Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[CAT500 - 1, 0]);
break;
default: // 1パターンのみを個別指定
Console.WriteLine(" Falsifying pattern No.{0}.", falsifyingType);
FalsifyingData(falsifyingType, prop, improperData);
Console.WriteLine(" Complete falsifying No.{0}.\n", falsifyingType);
break;
}
}
//==================================================
// メイン関数
//==================================================
static void Main(string[] args)
{
// コマンドライン引数の数チェック
if (args.Length < 3) // データ、ブロック鍵、MAC鍵
{
Usage();
return;
}
else
{
// 拡張子の判定
CheckExtension(args[0], ".bin"); // 入力ファイル名
CheckExtension(args[1], ".txt"); // ブロック鍵ファイル名
CheckExtension(args[2], ".txt"); // MAC鍵ファイル名
// ファイルが存在するかを確認
for (int i = 0; i < 3; i++)
{
CheckFileExists(args[i]);
}
}
string twlBackupName = args[0];
string keyName = args[1];
string macKeyData = args[2];
int falsifyingType = GetFalsifyingType(args); // オプションがあれば、改ざんのパターン決定
Properties prop = new Properties(); // インスタンスの作成
byte[] twlBackupData = File.ReadAllBytes(twlBackupName); // バックアップデータの読み込み
prop.SetOutputFolderPath(twlBackupName); // 出力フォルダを作成
// 鍵データの読み込み
string[] keyString = File.ReadAllLines(keyName);
prop.keyData = new byte[KEY_SIZE];
for (int i = 0; i < prop.keyData.Length; i++)
{
prop.keyData[i] = Convert.ToByte(keyString[i], 16);
}
// MAC鍵データの読み込み
string[] macKeyString = File.ReadAllLines(macKeyData);
prop.macKeyData = new byte[KEY_SIZE];
for (int i = 0; i < prop.keyData.Length; i++)
{
prop.macKeyData[i] = Convert.ToByte(macKeyString[i], 16);
}
// ファイル名の出力 + 鍵データの dump
Console.WriteLine("Twl Backup File : {0}", twlBackupName);
Console.Write("Block Key File : {0}, ", keyName);
dumpArray("", prop.keyData);
Console.Write("MAC Key Data : {0}, ", macKeyData);
dumpArray("", prop.macKeyData);
// バックアップデータを復号化
prop.decryptedBlocks = Utility.DecryptBackupData(twlBackupData, prop.keyData);
//==========================================
// 改ざん&ファイル出力
Console.WriteLine("Start ------------");
SwitchFalsifyingPattern(falsifyingType, prop);
Console.WriteLine("Complete --------");
Console.WriteLine("Output Dir: {0}", prop.outFolderPath);
}
// byte 配列の 16 進表示
static void dumpArray(String msg, byte[] array)
{
Console.Write(msg);
foreach (byte b in array)
{
Console.Write(String.Format("{0,0:X2} ", b));
}
Console.WriteLine();
}
}
}