マシンコードの変更:「選択」対 「遺伝子工学」





GMOに関する神話については、すでにここに記述されています 。 これに加えて、リバースエンジニアリングとマシンコードの修正の分野からの実際の例の分析と優れた類似性を共有したいと思います。



マシンコードの突然変異



例として、6502プロセッサを使用するNESプレフィックス(Dendyとして知られる)を取り上げます。コマンドシステムは非常に単純です-オペコードは常に1バイトで表され、256のそれぞれが何かを行います。 愚か者からの「保護」は提供されず、ほとんどすべてのランダムなバイトセットがプロセッサからの抵抗なしに実行されます。 したがって、ゲームのROMを取得し、その中のランダムなビットを修正することができます(「突然変異」と呼びます)-そして、さまざまな予想外の場所で面白いグリッチを観察し始めた後、一般的にゲームはおそらく実行可能になります。 YouTubeには、類似の動画の全ジャンルがあるようです。 このようにして得られたマシンコードはおそらくあまり正確ではありませんが、ほとんどの場合、プロセッサはそれを実行して何かをすることができます。



判明したように、この手法は楽しみのためだけでなく(予想外の不具合のあるおなじみのゲームをプレイするのは非常に面白いです)、あなた自身のために具体的な修正を得るためにも使用されます:彼らは多数の「突然変異体」を作り、望ましい効果が現れたものを探します 生物の胚が変異原にさらされ(遺伝暗号のランダムな変化につながる)、選択された現代の方法とまったく同じです。そして、成長可能なものから望ましい特性を持つものが選択されました。 このようにして得られた生物は、他の望ましくない突然変異のホストを付属物で受け取ります。 彼らは通常の見た目で徐々に交配することでそれらを取り除き、必要な形質と他の顕著な突然変異を最小限に抑えながら多かれ少なかれ正気な生物を達成します。 マシンコードでも同じことができます。



機械コードの「遺伝子工学」



直接の類推として、リバースエンジニアリングはここで提案します。 マシン(バイナリ)コードを研究し、それがどのように機能するか(全体または一部のみ)を理解した後、ターゲットエフェクトに確実につながる変更を正確に行います。



プログラマーは、マシンコードを分析するための膨大な数のツール(あらゆる種類の逆アセンブラー、デバッガーなど)を持っています。 マシンコード自体は人間が読むことを意図していないという事実にもかかわらず、人々が発明して文書化したため、それをより理解しやすい形式(アセンブリーリスト)に変換するツールを作成することは問題ではありませんでした。 また、調査中のプログラムのほとんどは人が書いたものである可能性が高いため、通常、コードを構成するものは論理的で理解しやすいように見えます。



遺伝学者がはるかに複雑であることは明らかです。 すべての新たな問題を明らかにする公式文書はありません。 遺伝コードは偶然に「書かれ」、「生き残り、適応し、子孫を与えることができる」という原則に従って選択されたため、論理的で最適なものとはほど遠いものであり、私たちが発明したプログラムコードの難読化は、単なるせせらぎのように思えます。 それにもかかわらず、これは、遺伝暗号を研究して変更しようとすることが不可能であることを意味するものではありません。



背景



私にとって、Dendyは常に単なるプレフィックスではありません。 私はそれを演奏しただけでなく、いくつかの簡単な修正のために手の中にはんだごてでかなりの時間を費やしました。 どこかに行く途中で、これらのゲームがどのように作成され、内部でどのように機能するかについてよく考えました。 確かに、あなたの多くはかつて同様の質問をしたことがあります。これが将来のIT従業員の性質です。



年が過ぎました。 いくつかの間隔で、エミューのテーマに没頭し、テーマサイトで新しいことをすべて学びましたが、アセンブラ6502とNESアーキテクチャの研究に挑戦することを敢えてしませんでした。 合理的および不合理の内部対立。 長い間、私はこれに時間を費やす必要がないと確信していましたが、...私は壊れました。 エミューシーンの愛好家がどんな面白いことをしているのかを見て、私は長年のアイデアを「私もできる!」という明るい考えで取り上げました。



きっとあなたの多くは、「9999-in-1」タイプのカートリッジを覚えています。これは、海でのロマンチックな物語、飛んでいるカモメ、心地よい音楽(Unchained Melody)の美しいメニューで有名でした。 このメニューを分解し、それに基づいてUnchained Nostalgiaのトリビュートデモを作成しました。







主なアイデアはゲームのリストではなく、スライドは音楽に切り替わります。 自由な空に、意味のある雲と星を描きました。 音楽と同期して自動スライド切り替えを実装し、手動で切り替える機能。 Dendy以外のシステムの再生速度とピッチの自動修正を追加しました(結局、このメニューは中国のソースクローン用に特別に作成されたものであり、元のメニューはNES NTSCでは速すぎ、NES PALでは低すぎることが判明したため、元のNESとは異なりました音); 好奇心Easter盛なイースターエッグをいくつも追加する誘惑に抵抗できませんでした。 つまり、使用されるアプローチは想像力を制限するものではなく、原則として、一定の時間内に変更を加えることができます。



ただし、似たようなアイデアは異なる人々の頭に浮かびます。



デモのリリース後、誰かが同じアイデアを実装しようとしたことを発見しました。 著者は、「破損者」を使用したことを示しました。これは通常、さまざまな不具合を含むゲームの破損バージョンを作成するために使用されます。



そのような「ランダムな」方法が実際にマシンコードを必要な効果で変更するために実際に使用されると誰かが以前に私に言っていたなら、私はおそらく信じなかっただろう。 確かに、このためには多くの時間と運が必要です。 一方、マシンコードを学習して理解する必要はありません。 そのようなハッキングの「生きた」例がすでに手元にあったので、その作業を調査する以外に何も残っていませんでした。







スライドは約2秒ごとに自動的に切り替わります(これは速すぎます)。 手動制御はありません。 スライドを変更すると、何らかの理由で画面が2回ちらつきます。 主なことは、それが機能することです! しかし、どのように?



手術の痕跡



カートリッジには、グラフィックのプログラムコードとタイルが別々に保存されます。 コードはPRG ROMにあり、タイルはCHR ROMにあります。 2つ目は標準ツールを使用して簡単に変更できます。そのため、Dendyの有名なゲームには多くのグラフィックハックがあり、コードはまったく変更されません。



元のメニューのCHR ROMと調査したハックを比較します。



顔には、直接的な「外科的」介入の痕跡があります。 何らかの理由で、数字、点、小さなカモメのタイルが切り取られました。 元のCHR ROMを返して、この方法で正確に隠されていたものを確認してみましょう。







少し明確になりました。 プログラムコードで入力が壊れていたため、メニューは常に下ボタンが押されていると見なします。 ゲームの名前の出力も壊れていました。 どうやら、ゲーム番号と小さなカモメ(これは選択したゲームを指すカーソルです)の出力を壊すことはできなかったので、CHR ROM内の対応するタイルをカットして非表示にする必要がありました。



...そしていくつかの魔法!



より深く掘ります。 逆アセンブラの助けを借りて、PRG ROMで「変更」されたバイトが正確に何をするかを確認します。

元のコード ハックコード
NMI:

PHA

TXA

PHA

TYA

PHA

LDA $1E

BEQ loc_C181

JSR sub_C592

loc_C181:

LDA #0

JSR sub_C428




NMI:

PHA

TXA

PHA

TYA

PHA

LDA $1E

BEQ loc_C181

JSR sub_C592

loc_C181:

LDA #0

JSR sub_C445




これは、次のフレームが描画されるときに呼び出されるNMI割り込みハンドラーです。 ご覧のとおり、呼び出された関数のアドレスは修正されています。 元の関数は、すべてのボタンのステータスを読み取り、それらの値をビットマスクの形式で1バイトで書き込むことに従事していました。 新しい呼び出しは、この関数の最後の命令のみを実行し、その時点でボタンのマスクが押された状態で、レジスタYからの結果を変数に保存します。 したがって、前のコードが実行された後に残るレジスタYのガベージ値が使用されます。 ほとんどの場合、ゴミ値0x07がここに表示されます。このバイナリは00000111のように見え、同時に押された下、左、右ボタンに対応します(メインコードは左および右ボタンを無視し、下ボタンを処理します) 。 ただし、スライドをYに切り替えた直後には、ガベージ値0xFFが残ります。これは、すべてのボタンが同時に押されたことを意味します。その結果、デバッグコードが呼び出され、元のメニューの画面にリビジョン番号が表示されます(左+開始+ B)これには、1つのフレームでのPPUのシャットダウンが伴います。 これにより、画面が二重にちらつきます。

元のコード ハックコード
LDA #0

STA $2000

STA $2001

LDA #$22

STA $2006

LDA #$AC

STA $2006




LDA #0

STA $2000

STA $2001

SBC #$22

STA $2006

LDA #$AC

STA $2006




ここでは、Left + Start + Bにリビジョン番号を表示するコードが壊れているため、上記の問題によりこのデバッグコードが毎回実行されても、画面にリビジョン番号は表示されません。 おかしな方法で判明しました。レジスタAで値Aを0x22に設定する命令は、レジスタAから数値0x22を減算する命令に置き換えられました。 ケースAは以前は常に0であるため、結果は0xDEです。 元のコードは、最初の画面ページ内にあるビデオメモリの0x22ACでPPUの出力の開始を設定しました。 新しいコードは、使用可能なビデオメモリをはるかに超えるアドレス0xDEACを設定します(最大許容アドレスは3FFFです)。その結果、碑文は「どこにも」表示されません。

元のコード ハックコード
ASL A

TAX

LDA $EAB3,X

STA $0A

LDA $EAB4,X

STA $0B

LDY #0

loc_C380:

LDA ( $0A ),Y

BEQ loc_C38B

STA $2007

INY

JMP loc_C380




ASL A

TAX

LDA $EAB3,X

STA $FA

LDA $EAB4,X

STA $0B

LDY #0

loc_C380:

LDA ( $FA ),Y

BEQ loc_C38B

STA $2007

INY

JMP loc_C380




ここで、ゲームの名前を表示する壊れたコードがありました。 出力行にポインターを書き込むとき、16ビットポインターの下位バイトは0x0Aではなく0xFAに書き込まれ、上位バイトは0x0Bにあるべきように書き込まれます。 0xFAが使用されなかったことは幸運であり、それ以上何も壊しませんでした。 次に、プログラムは、バイト0xFAと0xFBのペアが指す行を読み取ります(0xFBは常に0です)。 幸運なことに、すべてのポインターはゼロで満たされたメモリーを指しているため、ゲームの名前の行は表示されません。



結論



同様の「ランダム」な方法で作成された変更で、すべてが「有名にねじれている」と感じることができますか? ある破損は別の破損を支え、全体としてはどういうわけか必要なものに近い結果をもたらします。 この場合、ランダムなコードと一見したところコードの無関係な部分との間に信じられないほどの依存関係があり、わずかなさらなる修正(正しいもの自体でさえ)によって、すべてが崩壊する可能性があります。 完全なリバースエンジニアリングは、はるかに信頼性があります。 従来の育種と比較して、遺伝子工学についても同じことが言えます。



ファイルリンク






All Articles