using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Diagnostics; using TwlBackupBlock; namespace FalsifyTwlBackup { partial class Program { /// /// 指定された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 section, BkpType type) { Debug.Assert((section <= BLOCK_NUM - 1) && (section >= 0)); SignatureBody signature; signature = (SignatureBody)dataBlocks.signature.body; if (SECTION_CONTENT <= section && section < SECTION_CONTENT + MAX_CONTENTS) { hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.CONTENT + section - SECTION_CONTENT], 0); } else { switch (section) { case SECTION_BANNER: hash.CopyTo(signature.digest.banner, 0); break; case SECTION_HEADER: hash.CopyTo(signature.digest.header, 0); break; case SECTION_TMD: hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.TMD], 0); break; case SECTION_PUBLIC_SAVE: if (type != BkpType.LEGACY) { hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.PUBLIC_SAVE], 0); } else { hash.CopyTo(signature.digest.section[(int)Utility.SectionIndexLegacy.PUBLIC_SAVE], 0); } break; case SECTION_SUB_BANNER: if (type != BkpType.LEGACY) { hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.SUB_BANNER], 0); } else { hash.CopyTo(signature.digest.section[(int)Utility.SectionIndexLegacy.SUB_BANNER], 0); } break; case SECTION_PRIVATE_SAVE: hash.CopyTo(signature.digest.section[(int)Utility.SectionIndex.PRIVATE_SAVE], 0); break; default: Debug.Assert(false); break; } } } /// /// byte列を結合します。 /// /// 前半部分に来るbyte列 /// 後半部分に来るbyte列 /// 連結後のbyte列 static byte[] MergeByteArray(byte[] front, byte[] rear) { 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 section, Properties prop, byte[] impData, int insertPos, byte[] byteMac, byte[] byteHash = null) { byte[] encryptedBlock = new byte[0]; // (データ本体の前に不正データ挿入) if (insertPos >= FRONT_BANNER && insertPos <= FRONT_PRIVATE_SAVE) { if (section == insertPos - FRONT_BANNER) { encryptedBlock = MergeByteArray(encryptedBlock, impData); } } // データ本体を暗号化 encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[section].body.GetBytes()); // bodyを暗号化 // (データ本体の後に不正データ挿入) if (insertPos >= REAR_BANNER && insertPos <= REAR_PRIVATE_SAVE) { if (section == insertPos - REAR_BANNER) { 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[section].iv); // データ本体を暗号化 encryptedBlock = MergeByteArray(encryptedBlock, byteMac); // MACを結合 encryptedBlock = MergeByteArray(encryptedBlock, dataBlocks[section].iv); // IVを結合 // (データブロックの後に不正データ挿入) if (prop.bkpType != BkpType.WITH_PRIVATE_SAVE) { if (section == SECTION_SUB_BANNER && insertPos == REAR_DATA) { encryptedBlock = MergeByteArray(encryptedBlock, impData); } } else { if (section == SECTION_PRIVATE_SAVE && insertPos == REAR_DATA) { encryptedBlock = MergeByteArray(encryptedBlock, impData); } } return encryptedBlock; } /// /// 各ブロックを暗号化した後、1つの byte 列に結合します。 /// /// 暗号化するデータ /// バックアップデータに関するインスタンス /// 不正データ /// 不正データを挿入する位置 /// ハッシュセット更新の有無 /// 全ての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_NUM][]; byte[][] byteHash = new byte[BLOCK_NUM][]; byte[][] byteMac = new byte[BLOCK_NUM][]; //---------------------- // 各ブロックを暗号化 // ( MAC & HASH SET更新する場合 ) if (chgMacHash == CHG_MAC_AND_HASHSET) { for (int i = 0; i < BLOCK_NUM; i++) // 署名部以外を暗号化&byte列に結合 { byteHash[i] = new byte[HASH_SIZE]; if (i != SECTION_SIGNATURE) { if (dataBlocks[i].body != null) { encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, byteMac[i], byteHash[i]); // ブロックを暗号化&結合 ChangeHashSet(dataBlocks, byteHash[i], i, prop.bkpType); // ハッシュセットを更新 } else { encryptedBlocks[i] = null; } } } // 最後に署名部を暗号化&byte列に結合 byteHash[SECTION_SIGNATURE] = Utility.GetSha256(dataBlocks[SECTION_SIGNATURE].body.GetBytes()); // 平文からSHA256ハッシュ算出 byteMac[SECTION_SIGNATURE] = Utility.GetAesCmac(byteHash[SECTION_SIGNATURE], prop.macKeyData); // SHA256ハッシュからMACを算出 encryptedBlocks[SECTION_SIGNATURE] = EncryptBlock(dataBlocks, SECTION_SIGNATURE, prop, impData, insertPos, byteMac[SECTION_SIGNATURE]); } // ( MAC & HASH SET更新しない場合 ) else { for (int i = 0; i < BLOCK_NUM; i++) { if (dataBlocks[i].body != null) { encryptedBlocks[i] = EncryptBlock(dataBlocks, i, prop, impData, insertPos, dataBlocks[i].mac); // ブロックを暗号化&結合 } else { encryptedBlocks[i] = null; } } } //---------------------- // 暗号化した各ブロックを結合 for (int i = 0; i < BLOCK_NUM; i++) { if (encryptedBlocks[i] != null) { 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 bool FalsifyingData(int improperNo, Properties prop, byte[] improperData) { if (MIN_OF_CAT100 <= improperNo && improperNo <= MAX_OF_CAT100) { return FalsifyingDataCategory1xx(improperNo, prop, improperData); } else if (MIN_OF_CAT200 <= improperNo && improperNo <= MAX_OF_CAT200) { return FalsifyingDataCategory2xx(improperNo, prop, improperData); } else if (MIN_OF_CAT300 <= improperNo && improperNo <= MAX_OF_CAT300) { return FalsifyingDataCategory3xx(improperNo, prop, improperData); } else if (MIN_OF_CAT400 <= improperNo && improperNo <= MAX_OF_CAT400) { return FalsifyingDataCategory4xx(improperNo, prop, improperData); } else if (MIN_OF_CAT500 <= improperNo && improperNo <= MAX_OF_CAT500) { return FalsifyingDataCategory5xx(improperNo, prop, improperData); } else { return FalsifyingDataCategory9xx(improperNo, prop, improperData); } } // static void SwitchFalsifyingPattern(int falsifyingType, Properties prop) //------------------------------------------------------------ // 作成する改ざんデータのパターンを決定。 // [in] int falsifyingMode : 出力する改ざんデータパターン // [in] Properties prop : バックアップデータに関するインスタンス //------------------------------------------------------------ /// /// /// /// /// static void SwitchFalsifyingPattern(int falsifyingMode, Properties prop) { // 改ざんに使用する不正なデータ作成 byte[] improperData = CreateImproperData(IMPROPER_DATA_SIZE); switch (falsifyingMode) { case MODE_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++) { if (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 MODE_CAT100: // 1xx系列全て Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[MODE_CAT100 - 1, 0]); for (int i = 100; i <= MAX_OF_CAT100; i++) { if (FalsifyingData(i, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.", i); } } Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[MODE_CAT100 - 1, 0]); break; case MODE_CAT200: // 2xx系列全て Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[MODE_CAT200 - 1, 0]); for (int i = 200; i <= MAX_OF_CAT200; i++) { if (FalsifyingData(i, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.", i); } } Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[MODE_CAT200 - 1, 0]); break; case MODE_CAT300: // 3xx系列全て Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[MODE_CAT300 - 1, 0]); for (int i = 300; i <= MAX_OF_CAT300; i++) { if (FalsifyingData(i, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.", i); } } Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[MODE_CAT300 - 1, 0]); break; case MODE_CAT400: // 4xx系列全て Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[MODE_CAT400 - 1, 0]); for (int i = 400; i <= MAX_OF_CAT400; i++) { if (FalsifyingData(i, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.", i); } } Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[MODE_CAT400 - 1, 0]); break; case MODE_CAT500: // 5xx系列全て Console.WriteLine(" Falsifying category No.{0}.", NUM_OF_PATTERN[MODE_CAT500 - 1, 0]); for (int i = 500; i <= MAX_OF_CAT500; i++) { if (FalsifyingData(i, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.", i); } } Console.WriteLine(" Complete falsifying Cat.{0}.\n", NUM_OF_PATTERN[MODE_CAT500 - 1, 0]); break; default: // 1パターンのみを個別指定 Console.WriteLine(" Falsifying pattern No.{0}.", falsifyingMode); if (FalsifyingData(falsifyingMode, prop, improperData)) { Console.WriteLine(" Complete falsifying No.{0}.\n", falsifyingMode); } else { Console.WriteLine(" No.{0} isn't support by specified type.\n", falsifyingMode); } break; } } } }