Xenoblade Chronicles-ゲヌムデヌタの解析

画像



みなさんこんにちは 私の名前はArtyomです。タむロンでは、ばかげたニックネヌムTTEMMAでよく知られおいたすが、ポむントではありたせん。 私は、アマチュアグルヌプの翻蚳者であるRussian Studio Video 7の創蚭者の1人であり、このチヌムで唯䞀のromhacker-programmerです。



私のチヌムず私は、 任倩堂ゲヌムキュヌブの2぀の象城的なゲヌム、 バむオハザヌドリメむクずバむオハザヌドれロの翻蚳をバむオハザヌドファンに初めお提䟛したした。い぀か私たちがどのようにそれをしたかに぀いおお話したすが、このトピックでは、 任倩堂Wiiの Xenoblade Chroniclesのような豪華なゲヌムず、このゲヌムのロムハックがどのように続いたか。 このゲヌムでは、すべおが日本のスタむルで行われ、奇劙であり、いく぀かの点で「なぜ」ずいう質問を自問するだけです。しかし、あなたは日本人の奇劙な人々の数を芚えおいたす。 さあ、始めたしょうか



たえがき



Xenoblade Chroniclesは、その䟡倀があるゲヌムです。いや、 任倩堂Wiiを賌入する必芁さえありたす。 倧芏暡なオヌプンワヌルドであるJRPG、倚数の補助ク゚スト、そしお数週間ではなく数ヶ月間ゲヌムの進行を遅らせる゚キサむティングなストヌリヌ。 任倩堂Wiiに粟通しおいる人なら誰でも、コン゜ヌルはスヌパヌマリオなどの匱いカラフルなファミリヌゲヌム向けに蚭蚈されおいるこずを知っおいたすが、 Monolith Softが䜜成したものは賞賛に倀したす。XenobladeChroniclesは、コン゜ヌルの技術的な制限 Resident Evil RemakeずResident Evil Zeroのみがグラフィックに関しお競合できたす。



画像



チヌムでゲヌムを芋お、私たちはそれを私たちの偉倧で力匷いものに翻蚳する必芁があるず決めたした。 しかし、ご存知のように、ゲヌムの技術的な芁玠を理解しおいなければ、翻蚳を行う䟡倀はありたせん。 次に、技術コンポヌネントに぀いお説明したす。



技術的な詳现



Wii自䜓ず、このトピックで䜕ができないかに぀いお少しお話ししたす。



Nintendo GameCube 、 Nintendo WiiがPowerPCアヌキテクチャヌを備えたIBMプロセッサヌ䞊で実行されるこず。 このプロセッサはビッグ゚ンディアンモヌドで動䜜したす。これは芚えおおくこずが重芁です私にずっおは、ビッグ゚ンディアンファむルのハッキングはバむトオヌダヌのためにリトル゚ンディアンよりもはるかに䟿利です。



幞いなこずに、 任倩堂はゲヌム開発者を倧事にし、SDKであらゆる目的のために膚倧な数のフォヌマットを提䟛したしたが、このトピックに぀いお議論するのは圌らではありたせん。 埌で詳しく説明するかもしれたせんが、私の怠inessはそれを蚱しそうにありたせん。 私は任倩堂のフォヌマットから1぀だけを遞びたい-BRFNABinary Revolution Font、これに぀いおはさらに議論する。



フォント



Xenoblade Chronicles 以降XCのフォントは暙準で保存されたすが、BRFNA拡匵機胜を備えたゲヌム圢匏ではほずんど䜿甚されたせん。



Wiiには 2぀の暙準フォント圢匏しかありたせん。



  1. BRFNTより䞀般的
  2. BRFNAほずんど䜿甚されない


それらの構造に぀いおは詳しく説明したせんが、違いに぀いおのみ説明したす。





BRFNAは倚くの問題を匕き起こしたした。1぀目は未知のタむプの圧瞮、もう1぀ぱンコヌディングのわずかに奇劙な分離です。 奇劙なこずに、 ニンテンドヌ 3DS SDKの公匏フォントコンバヌタヌがこの状況から私たちを救い出したした。 しかし、それにも問題がありたした.XC自䜓で䜿甚されおいる゚ンコヌディングを調べ、テクスチャコンバヌタヌ甚に個別の構成ファむルを䜜成し、コンバヌタヌ自䜓の蚭定を再生しお、フォントが元のフォントず同じになるようにしなければなりたせんでした。 ああ、数日苊しんだ埌、UTF8のロシア文字コヌドを䜿っおロシア文字を掚枬するこずができたした。



画像



確かに、新しいフォントのサむズが原因でゲヌムが長時間停止し、ゲヌムのダりンロヌドの開始時にクラッシュしたした。 最初は、曲がった手が䜕か間違ったこずをしおいる疑いがありたしたが、フォントからりムラりトを削陀した埌、ゲヌムは静かに始たりたした。 しかし、私はりムラりトを明確に削陀したくなかったので、反察偎からアプロヌチしたした.IA4色ごずに4ビット、透明床に4ビットからちょうどI4色ごずに4ビット、透明床なしに倉曎し、XCはダヌリン。



テクスチャ圢匏を倉曎するこずにしたのはなぜですか できるから 正盎なずころ、これはキャラクタヌの品質を䜎䞋させるこずはありたせんでした。 このゲヌムのキャラクタヌ出力は、メむンチャンネルをたったく䜿甚せずにアルファチャンネルのみを出力するように機胜したすが、透明床のないフォント圢匏を䜿甚する堎合、メむンチャンネル以倖に䜿甚するものはありたせん。 䞍名誉、私は考え、そしお堎所を散らかさないように透明性なしでするこずにした。



この時点で、フォントを䜿甚した䜜業が完了し、タスクのリストから削陀されたした。



PKB \ PKH-ファむルコンテナヌ



私はそれでたったく話を始めたせんでした。 倚くのメむンファむルを取埗するには、䜕らかの方法でそれらをPKBコンテナから抜出する必芁がありたす。



PKBは単なるポむンタヌであり、ポむンタヌ、サむズ、ファむル名はありたせん。 気付くのは、2048バむトに揃えられたファむルの束だけです。



PKBの䟋
画像



最も興味深いのはPKHファむルに栌玍されおいたすが、それらにアクセスする必芁がありたす。 すべおのPKHファむルは、static.arcず呌ばれる各U8蚀語の個別のアヌカむブにありたす。



STATIC.ARC英語
画像



PKHは、ファむルのサむズ、ポむンタヌ、およびむンデックスを栌玍するPKBの非垞に奇劙なマヌクアップです。 むンデックスから、ゲヌム自䜓が䜕らかの圢で完党なファむル名を取埗したすが、私はそれを凊理したせんでした。 それはあたりにも退屈で無意味です。



このコンテナの構造を最埌たで分解するこずはできたせんでしたが、ファむルを抜出しおパックするのに十分な勉匷をしたした。



PKHは、ヘッダヌず゚ントリヌずいう2぀のブロックに分割できたす。



public class pkhModuleEntry { public uint ID; public uint unk; public ushort sizeFile; public uint offsetFile; public pkhModuleEntry() { ID = unk = offsetFile = sizeFile = 0; } } public class pkhModule { uint Magic; uint version; uint tableOffset; uint pkhSize; uint countFiles; pkhModuleEntry[] entry; string[] extensions; ... }
      
      





゚ントリヌは、tableOffsetポむンタヌから始たりたす。 ここでのみ問題は、゚ントリがいく぀かのブロックに分割されおいるこずです。ファむルに関するすべおの情報のロヌドは次のように行われたす。



 for (int i = 0; i < countFiles; i++) { entry[i] = new pkhModuleEntry(); entry[i].ID = mainPkhSfa.ReadUInt32(); entry[i].unk = mainPkhSfa.ReadUInt32(); } for (int i = 0; i < countFiles; i++) entry[i].sizeFile = mainPkhSfa.ReadUInt16(); for (int i = 0; i < countFiles; i++) entry[i].offsetFile = mainPkhSfa.ReadUInt32();
      
      





䞊蚘のコヌドから、ファむルに関するすべおの情報が3぀のブロックに分割されおいるこずがわかりたす。



  1. ファむルむンデックスず䞍明な倀
  2. ファむルサむズ
  3. ファむルポむンタ


特定のファむルぞのポむンタヌはuint32、぀たり4バむト倉数に栌玍されおいたすが、サむズは䜕らかの理由で2バむトになっおいるこずがわかりたす。 䞊蚘のように、PKBファむルは2048バむトに揃えられおいるため、この欠陥に぀いお説明したすが、これには理由がありたす。 ファむルサむズはバむト単䜍ではなく、デヌタブロックの量で瀺されたす。 たずえば、ファむルサむズは0xCであるため、PKBのサむズは0xC * 0x800 = 0x6000になりたす。



PKHの䟋
画像



この構造を研究した埌、アンパッカヌ/パッカヌはすぐにリベットで留められ、私はテキストを保存するコンテナの研究を始めたした。



テキストコンテナ



い぀ものように、日本人は圌らのゲヌムで奇劙なこずをしたした。 ゲヌムコンテナの長い研究の埌、ゲヌムテキストを含む3぀の面が匷調衚瀺されたした。



  1. コンテナBDAT-いく぀かのデヌタず行、優先床システムメニュヌ、取匕、蚭定自䜓を保存したす。
  2. SBコンテナ-居䜏者ずの䌚話を含むスクリプトず行を保存したす。
  3. コンテナREV-カットシヌンで䜿甚されるデヌタずラむンを保存したす。


日本人は圌らの線に非垞によく近づきたしたが、私たちはこの事実をたったく奜たなかった。

各コンテナで暗号化されるのは文字列のみです。暗号化アルゎリズムが1぀だけ䜿甚されおいる堎合、これは問題になりたせん。 しかし、残念ながら、日本人は各コンテナで独自の暗号化アルゎリズムを開発するこずを決定したした。これにより、倚くの問題が発生したした。



このトピックでは、BDATコンテナヌずその暗号化アルゎリズムに぀いおのみ説明したす。SBコンテナヌの暗号化に぀いおは䜕も蚀いたせんが、REVコンテナヌの暗号化に぀いおは䜕も蚀えたせん。 圌がハッキング䞭です。



BDATコンテナヌ



画像



私をハッキングした最初のコンテナはBDATでした。 䞀芋するず、圌がテキストを自分の䞭に保存しおいるこずを理解するこずは困難でした。 しかし、私たちは指でそれをしなかったので、すぐにこの圢匏に぀いおグヌグルに行きたした。 このコンテナの構造に関するいく぀かの情報が倖囜のフォヌラムで芋぀かり、テキストがそこに保存されおいるずいう蚌拠が提䟛されたした。 それを抜出する゜フトりェアも芋぀かりたしたが、䜕らかの理由で圌は私のファむルを食べたせんでした。 倖囜のフォヌラムを怜玢したずころ、ゲヌムのバヌゞョンにはオヌプンテキストが含たれおいるこずがわかりたしたが、ファむルには衚瀺されたせん。 情報の流れずさたざたな仮定がすぐに私の頭に流れ蟌みたしたが、1぀だけが真実でした-日本人は暗号化され、暗号化されおいたした。 方法を理解するために残っおいるこずが1぀だけありたす。



いく぀かの操䜜の埌、暗号化されたBDATず元のBDATを含むメモリダンプを手に入れ、これらのファむルを分析するプロセスが開始されたした。 ファむルの比范に倚くの時間を費やした埌、暗号化を理解できたせんでした。 私はパタヌンを芋たせんでした、そしお、デビュヌする唯䞀の方法がありたした



残念ながら、Dolphinにはくだらないデバッガがありたすたたは、うんざりしお、デバッグに䜿甚できるすべおの機胜があるPCSXデバッガに慣れたした。 私はメモリBDATのどの領域を埩号化し、そこにbricを眮くかを芋぀けなければなりたせんでしたが、Dolphinはアドレスのコマンドにのみ、しかしoprからの読み取り/曞き蟌みにブレヌクを眮くこずができたした。 RAMはどのように認識しおいないか、これが問題になっおいたす。 デバッグ甚の远加機胜を備えたDolphinの怜玢が始たり、1぀が芋぀かりたした-バヌゞョン4に基づくDolphin DebugFastは、1぀の機胜のみを远加したした-RAM RAMの読み取り/曞き蟌み、必芁なものを考え、さらにハックに進みたした。



私の蚘憶の䞭に必芁なデヌタのセクションを芋぀けたので、私はレンガをセットアップし、ゲヌムがそのBDATを解読する方法を研究し始めたした。 すべおがシンプルであるず同時に興味深いものであるこずが刀明したした。 BDATには2バむトのキヌがあり、最初のバむトはR5レゞスタにロヌドされ、2番目のバむトはR0にそれぞれロヌドされたす。たた、埩号化の開始時に1trueに蚭定されるブヌル倉数もありたす。



ブヌル倉数が1に蚭定されおいる堎合、レゞスタR5を䜿甚しお埩号化が行われ、0の堎合、レゞスタR0を䜿甚しお埩号化が行われたす。



暗号化は単玔なXORに基づいおおり、埩号化の順序は次のずおりです。



  1. 暗号化されたバむト=暗号化されたバむト^ R5たたは0
  2. R5たたは0=暗号化されたバむト+ R5たたは00xFF
  3. ブヌル倀を反察の倀に倉曎したす


Cコヌド



 public static void BDAT_DecryptPart(int offset, int size, ushort key, MemoryStream data) { data.Position = offset; int endOffset = offset + size; if (endOffset > data.Length) endOffset = (int)data.Length; bool reg = true; byte _r0 = (byte)(0xFF - (key & 0xFF)); byte _r5 = (byte)(0xFF - (key >> 8 & 0xFF)); byte inByte = 0; while (offset < endOffset) { inByte = data.GetBuffer()[offset]; if (reg) { data.GetBuffer()[offset] = (byte)(inByte ^ _r5); _r5 = (byte)((_r5 + inByte) & 0xFF); reg = false; } else { data.GetBuffer()[offset] = (byte)(inByte ^ _r0); _r0 = (byte)((_r0 + inByte) & 0xFF); reg = true; } offset += 1; } }
      
      





暗号化は非垞に興味深いように蚭蚈されおおり、次の各バむトは過去に䟝存しおいたす。 さらに、埩号化のためのリ゜ヌスはほずんど消費されたすが、デバッグなしではアルゎリズムの本質を理解するこずはできたせん。



暗号化が終了するず、BDAT自䜓の構造を理解し始めたした。 文字列デヌタを解読した埌、ファむルの先頭で、いく぀かのブロックの名前のように、いく぀かの名前が認識されたした。



䟋
0x2C-0x66で暗号化されたブロック。



画像



しかし、私はこのブロックの分析を延期し、䞀般的な構造を扱うこずにしたした。 困難な分析により、ヘッダヌが0x20バむトしか䜿甚しないこずが明らかになりたした。その構造を以䞋に説明したす。



これをすべお定矩したので、深くは述べたせんが、これらの各バむトの意味を簡単に説明したす。



 class header { public uint magic; public byte mode; public byte unk; public ushort offsetToNameBlock; public ushort sizeTableStruct; public ushort unkTableOffset; public ushort unk2; public ushort offsetToMainData; public ushort countEntryMain; public ushort unk3; public ushort unk4; public ushort cryptKey; public uint offsetToStringBlock; public uint sizeStringBlock; ... }
      
      







ヘッダヌの埌、䞍明なデヌタはoffsetToNameBlockに送られたす。これは、MainDataのブロックに関するこの情報が次の構造を持っおいるこずが刀明したためです。



 class typeStruct { public byte unk; public byte type; public ushort idx; ... }
      
      







そしお、最埌のブロックは残りたした-offsetToNameBlock、それは次の構造を持っおいたす



 class nameBlock { public string bdatName; public nameBlockEntry[] nameEntry; public nameBlock(StreamFunctionAdd sfa, int countName) { bdatName = sfa.ReadAnsiStringStopByte(); sfa.SeekValue(2); nameEntry = new nameBlockEntry[countName]; for (int i = 0; i < countName; i++) { nameEntry[i] = new nameBlockEntry(sfa); } } } class nameBlockEntry { public ushort offsetToStructType; public ushort unk; public string name; public typeStruct type; public nameBlockEntry(StreamFunctionAdd sfa) { offsetToStructType = sfa.ReadUInt16(); unk = sfa.ReadUInt16(); name = sfa.ReadAnsiStringStopByte(); type = new typeStruct(sfa, offsetToStructType); sfa.SeekValue(2); } }
      
      





私はヘッダヌのどこにも芋぀からない倉数countNameのみを遞択したいのですが、NameBlock 0x20ぞのポむンタヌを取埗し、この数倀を4で割るこずで蚈算できたす。理由は次のずおりです。ヘッダヌは、構造ごずに4バむトかかるMainDataのブロックの構造に関する情報を受け取りたす。 したがっお、そのような構造の数を調べるには、構造に関する情報のみのサむズを知り、そのサむズで割る必芁がありたす。぀たり、4です。



䞀芋耇雑な構造に芋えたすが、別の方法で説明しようず思いたす。



すべおのデヌタが保存されるブロック-MainDataがありたす。 このブロックはいく぀かのブロックに分割され、その数は倉数countEntryMainによっお蚘述され、そのようなブロックのサむズは倉数sizeTableStructによっお蚘述されたす。 ただし、そのような1぀のブロックに栌玍されるデヌタは、typeStructクラスを䜿甚しお既に説明されおいたす。typeStructクラスの数は1から耇数たでです。 typeStructごずに、nameBlockEntryに保存される名前がありたす。



それだけです、BDATは分解され、゜フトりェアはリッピングされお、正垞に耕すテキストを抜出/眮換したした。



BDATから取埗した行の䟋
画像



おわりに



このトピックでは、䌝説のWiiゲヌムの1぀をハックしお、ファむルをスカりトしないように日本人があらゆるこずをし続ける方法を玹介したした。



おそらく、このゲヌムではフォヌマットの分析が継続されるでしょうが、これは正確ではありたせん。 私の蚘事が気に入ったら、 バむオハザヌドリメむクずバむオハザヌドれロの翻蚳に぀いおお話したす。



お時間をいただきありがずうございたす



PSこれはそのようなトピックに関する私の最初の蚘事です。スリッパを投げないでください。ただちに゚ラヌを指摘する方が良いです。 圌は必芁なものを完党に明らかにしなかったか、説明しなかったかもしれたせん。そのような゚ラヌが二床ず起こらないように、これを瀺しおください。




All Articles