2024年12月7日土曜日

おバカ人狼Feignの解析(2)

 さて、パッチ形式でのデータ取りは出来たものの、Feignに手を入れないで動かすのもやってみたい。どこから手をつければいいのかな。

と、その前にFeignはオンラインゲームです。どうやって動作検証を行うのでしょうか。Among Usの場合はローカルネットワークだけで動くモードがあります。こちらは抜け道を使うと一個のPCでフルの15人まで動かすことだって可能です。

でもFeignはオンラインにつなぐしかありません。二つのPCでSteam2アカウント使えばゲームの流れはつかめます。が、吊り=ゲーム終了となってしまうためまだ足りません。もう少し多様なシチュエーションを作るには最低あと二つ。てことでスマホとタブレットを持ってきて4アカ。ギリギリです。

さて、Feignのメモリを覗きこむ。そのためにはWin32APIのReadProcessMemoryを使用する必要があります。プロセスのハンドルとかはC#のProcess周りで取ることが出来そう。

では、これらを使ってどこを読めばいいのか?本格的にFeignの解析が必要です。

Unityで作られたゲームはIL2CPPという技術でコードの難読化も兼ねてネイティブコードに変換されています。Il2CppDumperとかIl2CppInspectorというツールで解析が出来るようです。

が、どちらも本家のアップデートが止まっています。試しにFeignに使ってみると対応しないVersionですとのこと。Forkをいくつか見てると更新が続いてるのがある、試してみると・・いけた。

入手した情報からアドレスの割り出しをしていく。見た感じstaticオブジェクトのポインタがわかって、staticのフィールド、インスタンスのフィールドのオフセットがわかったので順々にたどればいいのね。LobbyManagerはstaticクラスだったのでここから辿って辿っていくとusersは取れそう・・・。取れた。

GameControllerは動的に生成されてるので、インスタンスを誰か保持していないかな、と探すと同じく動的に生成されるクラスしか保持していない。あれ?詰んだ臭くない?

よく考えなおしてみよう。staticのオブジェクトも生成される場所はヒープのはずだからその周辺に確保されるはず。メモリサーチでクラス情報を保持しているオブジェクトらしきものを列挙してみる・・・。複数出てくる。

真のオブジェクトはplayers、deadPlayersというListを持っているはずだから、そのオフセットにListを持つものに絞ると・・・まだ複数ある。ゲームごとにオブジェクトを作成して破棄しきれていないらしい。あちこちで保持しあってるからGC効かないんだろうな。

こうなってくると、ゲーム開始直前のロビー人数とplayersが等しく、deadPlayersが0のゲームを現在のゲームと決め撃つしかなく、それで何とか取れたっぽい。

playersは生きているプレイヤー、deadPlayersは死んでいるプレイヤーに違いないから、これで生死を判定しようかな。あれ?両方に名前があるプレイヤーがいるぞ。もしかしてキルされたらplayersからdeadPlayersに移すけど、蘇生されたらdeadPlayersはそのままにplayersに再追加してる?そういう仮定で検出すると、あってるぽい。

てことでメモリ監視方式も完成。手動/メモリ監視/Mod経由の3通り全部乗せが出来ました。テストプレイで代替問題ないことを確認して数日後。

本体アップデートが来ました。こうなってくるとアドレスも変わってるだろうし、パッチもそのまま当たるかわからないし。いやぁ、アプデに追従するのは大変だなぁって実感しました。ある程度需要はあるだろうし、公開してもいいんだけどね。

おバカ人狼Feignの解析(1)

 久しぶりになんか書いてみる。

時々友人とおバカ人狼と言われるFeignをやってます。

人狼系ゲームをDiscord VCでプレイするとき、どうしても必要になるのがミュート作業。夜時間はスピーカーミュート、死亡した場合はミュートが定番です。もちろん、手動でやってもいいんですがスマホでやってる人は下手するとゲームが落ちてしまいます。

最初に考えたのが、手動ミュートボット。PCで二窓出来る人が代表して昼時間/夜時間の切り替え、死亡処理をボタンポチポチしてする方法。

C#でDiscord.Netを使ったり、サーバーで動かすためにpythonに移植したり。これでも十分便利なんですが、やっぱ操作ミスとか忘れがたまにあるので何とか自動化したい。

二パターン考えました。

・BepinExを使ってパッチを当てる。(AmongUsなどのModと同じ)
・Feignのメモリを覗き見る。(AmongUsCaptureやBetterCrewLinkと同じ)

まぁAmong UsでもModを作ってるし、パッチを当てるほうが早いかなと思いBepinExを導入。

BepinExが変換したDLLを読むことでクラス情報が一覧できます。

どのような処理をしているかは簡単にはわからないのですが、今回は動作を変更するためではなく状態を読み取るのが目的なのでとにかくクラス名、メソッド名、フィールド・プロパティ名から動作を推測していきます。

まず、ゲームロビーとゲーム自体の制御をする部分があるはず。ロビーについてはLobbyManagerというそれらしいものを発見。usersというListも発見し明らかにこれでメンバーが取れそうです。

メンバー変更のタイミングもOn~joinやら~Leave~やらがあるのでLogとってみたり、デバッガアタッチして見たりで大丈夫そうなことを確認。

じゃあ、ゲーム進行はというとこれまたGameControllerというそのままの物を発見。stateというフィールドがありここでゲーム状態を保持してそう。メソッドにもProcessStateChangeというものがありLogを出してみるとここでstate移行してるのも間違いない。

ってな感じで解析を進めていきました。

この情報をBotアプリに転送すればオートミュートの出来上がり!

・・・ただ、Modのインストールからしないといけないのはめんどくさいし、アプリ一個で済む形を作ってみたい。自分がいない時に誰かに貸せるようにしたいしね。

でも、そこからが結構大変でした・・・。