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

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

2021年12月12日日曜日

Among Us 管理ツール少しいじりました。

 前回のMod管理ツール、Reactorを使ってるMODには対応できていなかったので
複数DLLがある場合を仮定してフォルダ管理する形式に変更してみた。

あと、AutoMuteUsやBetter-CrewLinkを使うことを想定して、そちらも同時に
立ち上げられるようにしてみた。

ついでになんとなく.NET5に移行。
単一実行ファイルにできるとか、Zip解凍にアップデートが来ているとかで。


よくあるエンジニアが適当に作ったUIです。

こんなショートカットをまずウインドウにドロップ。
するとSteamなら
Epicなら
となるはず。
Steamの場合はもうひと手順必要で

で表示されるフォルダを開いて
をウィンドウにドロップすると上二つがそろう。
ここでTheOtherRolesなり、Town Of MossなりのModのZipファイルを取ってきて
ウインドウにドロップ。
するとAmong UsフォルダにMod本体以外を展開してリストにModが追加される。

これ以降はDLL本体だけでも大丈夫なんですが、Town Of MossはReactor.DLLが必要なのでZipで登録するほうが楽ですね。

ここまですればMOD無しならVanilla、MODなら好きなMODを選択してゲーム開始を押せば
DLL本体を自動で消したりコピーしたりでAmong Usが起動します。

下二つはDiscode連携するAmongMuteUs用のキャプチャツールや近アモ用のBetter-ClewLinkを同様に登録できます。
チェックを入れると本体起動前にそれらを起動できるので便利かなと。

というのを使ってみたい人っているんですかね?

2021年12月6日月曜日

Among Us MOD管理ツール

 久しぶりにツール作ってます。

Among Us面白いですよね。色んな配信者の方見てるうちに自分でもやってみようと。

今はMODを入れて遊ぶのが特に流行っていますが、そのMODの中にとある推し配信者の方が作ったスキンが入ってるものがありまして。

でもMODない人と遊ぶには外したり、また違うMOD入れたり・・・。
ちょっとめんどくさいなと思って管理ツール作成中です。


ほんとに簡単なツールなんですが、アモアスの起動ショートカットとアモアス本体フォルダを最初に登録します。
Steamだと両方登録しないとだめですが、Epicだと起動ショートカットだけで両方登録されます。

下の表にMODをドロップするとModを管理して選択が出来るようになります。
Zipをドロップするとアモアス本体フォルダに展開するので、まだMOD入れたことない人は最初はZipからですね。
それ以降はDLLだけドロップでOKです。
DLLはMOD名+バージョンにリネームして管理します。

下の表で素の状態でプレイしたいときはVanillaを選ぶとDLLなしで、MODは入れたいものを選択するとDLLをコピーしてゲーム開始になります。

ただ、まだThe Other Role系しか確認してないんですよね。
なぜか最近MOD入れると起動して認識はするんだけどゲーム開始できないので確認できたとも言えないんですよね。
あと結構ファイルいじるので公開するのは怖いんですよね。
どうしようかな?


2019年12月7日土曜日

OVMFのバグ

今ちょっといろいろあってUEFIアプリを書いてます。
検証をQEMU上で行ってるんですが、なぜかプログラムを実行するたびに使用可能メモリが減っていきます。
実機でテストすると全く起きない。
どこまで削ったら起きなくなるのか突き詰めていったら、見つかりました。
TestString()という、画面表示できる文字列か検証するAPI。
これを呼ぶとメモリが減っていきました。

で、QEMU用のUEFIっていうのはオープンソースのOVMFっていう実装を使ってます。
なのでソースみりゃわかるかなと。

ダウンロードしてきてGREPかけて・・・多分これ。

EFI_STATUS
EFIAPI
GraphicsConsoleConOutTestString (
  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
  IN  CHAR16                           *WString
  )
{
  EFI_STATUS            Status;
  UINT16                Count;

  EFI_IMAGE_OUTPUT      *Blt;

  Blt   = NULL;
  Count = 0;

  while (WString[Count] != 0) {
    Status = mHiiFont->GetGlyph (
                         mHiiFont,
                         WString[Count],
                         NULL,
                         &Blt,
                         NULL
                         );
    if (Blt != NULL) {
      FreePool (Blt);
      Blt = NULL;
    }
    Count++;

    if (EFI_ERROR (Status)) {
      return EFI_UNSUPPORTED;
    }
  }

  return EFI_SUCCESS;
}
Bltをfreeしてるってことは内部でメモリ割り当ててるのね。
これは怪しいのでGetGryphの実装を探してみると、たぶんこれ

EFI_STATUS
EFIAPI
HiiGetGlyph (
  IN  CONST EFI_HII_FONT_PROTOCOL    *This,
  IN  CHAR16                         Char,
  IN  CONST EFI_FONT_DISPLAY_INFO    *StringInfo,
  OUT EFI_IMAGE_OUTPUT               **Blt,
  OUT UINTN                          *Baseline OPTIONAL
  )
{
~中略~
  *Blt = Image;
~後略
Imageが実体ね。これを探してみると

  Image = (EFI_IMAGE_OUTPUT *) AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT));
  if (Image == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }
  Image->Width   = Cell.Width;
  Image->Height  = Cell.Height;

  if (Image->Width * Image->Height > 0) {
    Image->Image.Bitmap = AllocateZeroPool (Image->Width * Image->Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    if (Image->Image.Bitmap == NULL) {
      FreePool (Image);
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;
    }

はい。Imageは構造体で、Bitmapという要素にもメモリ割り当ててるね。
ってことでBltだけfreeするのはアウトですね。
とりあえずTestStringを呼ばなきゃいいんだけど、UEFIの実装ってまちまちでフォントも入ってるかテストしてみなきゃわからないんですよねー。
実際に"|”と”}”が実装されていない環境もありました。"{"はあるのにw。

これ直してビルドすればいいんだけど、環境整えるのがめんどくさいw
誰か直してくれないかなぁ?

2019年11月30日土曜日

ケ〇マス?

久々の投稿です。すっかり忘れてたというか

作ったのはちょっと前ですが・・・。
テレビのクイズ番組で人気のコーナー。
アプリ作ってくれるところ募集とかなんとか言っました。
いいお題ではあるな、と思ったのでものは試しに作ってみた。
4,5時間はかかったかな。

PC更新したばかりなのでまずUnity入れなおして、使い方を思い出して・・・まだよくわかんない。

とりあえず問題の管理だよなぁ。単純に盤面を記録して、正解の読み方、間違った読み方のデータを並べ解けばいいのかな?
ってことでこんな感じ。

巴,祇,藺,草
危,険,案,蹴
医,眩,家,木
剣,園,山,計
基,軸,子,通
薬,奈,馬,鞠
巴,ともえ
巴奈馬,パナマ
祇園,ぎおん
藺草,いぐさ
草木,くさき
危険,きけん
案山子,かかし
蹴鞠,けまり
医薬,いやく
家計,かけい
基軸,きじく
剣,つるぎ
眩,めまい
木通,あけび

誤読データ準備するのがしんどいぞ・・・

2018年3月21日水曜日

2月勝率

気づいたらもう3月も終盤。
また更新忘れてました。

最近のニュースであれば、とうとう星14武器を手に入れました。
パルチザンでした・・・。



とりあえず、頑張って強化もしたしせっかくなのでメインで使っていきたいと思います。

で、バトルアリーナですが相変わらず・・・

特に2月終盤にはバーチャロンコラボで武器種チェンジが入ったんですが初日の混乱で勝ちを稼いだ以外は負け続き。3月序盤まであったんですが結局勝ちきれなかったです。
ライドスラッシャー、面白かったけどなー。使いきれませんでした。

3月はまた武器チェンジでさらに変化があったけど、それはまた別の話、ということで・・・。

2018年2月14日水曜日

1月勝率

長いこと忘れてました。

原因としては、緊急のドラゴン退治頑張ってたこととスマホゲーのイベント頑張ってたあたりかな・・・。
緊急と緊急の合間でアリーナ行くには時間が半端でなかなか行けなかったり。
まぁ、

結果はこちら。1月は結局150戦程度しかしてませんね。

最終日はレジェンド落ちギリギリでやめるというヘタレさでした。
負けるときはほんとに何の役にも立たないレベルで死にまくり、勝っても味方に助けられることのほうが多いくらい。

気になって各ステージの勝率見たらこれはひどい、としか言いようのない勝率でしたw



最終日、これどうなってるんだろ?…8戦2勝6敗でした。まぁ、これはやめますよね。

2月はまだ30戦くらいしかしてなくて勝率6割。かろうじてレジェンドに上がったとこです。
月末はバーチャロンコラボでまた荒れるだろうから少しポイントは稼いでおきたいところです。頑張ってみますか。

2018年1月2日火曜日

12月勝率

あけましておめでとうございます。

プログラムあーでもないこーでもないと微調整してたら12月も終わってしまいました。
やっぱり今の段階で公開してみるのがいいのかな。

さて、バトルアリーナの戦績です。


トータル的には5割は勝てていますが、内容がなかなかひどかったです。
ARPででかい赤帯の日なんかは勝率2割でゲーム中にもぼろくそ言われる始末でした。
その影響で若干プレイ数も減っていたり・・・。
そのほかの日の内容がよかったかというと味方に助けられた試合のほうが多いんですけどね。
そう考えると、勝率はあまり内容を示していないのかな、と思う今日この頃です。
キルレートとかのほうが大事そうですね。プレイ中に記録するのは大変なんだけどなぁ。

2017年12月18日月曜日

勝率アプリ進捗

やればやるほど深みにはまる…。

色々Google DriveのAPI調べてたらアップロードもそんなに手間ではなかった…、というか楽だったのでアップロードも共有じゃなくAPI経由ってことにしました。

じゃあ、共有は何に使おうかな?と思って考えた結果スクリーンショットをとれるようにしたらいいんじゃないか、と思いつき調べて実装。
ためしにTwitterで投稿してみたら背景が透明…。背景色を指定したらOKでした。

今悩んでるのはデータが大きくなりすぎたらどうしようか、という問題。
自分でデータ取り始めた10月中旬から12月現在まで1100ゲームくらいで48KB程度何だけど、がっつりやる人だとひと月でどれくらいやるのか・・・。
月に1万ポイント稼ぐ人もいるので少なくとも500ゲームはあるのかな?
そうすると、月に30KBくらい行く可能性もあって、一年で360KB?大したことない気もするけどそのうちデータのロードに時間はかかるようになるだろうし、グラフはそもそも一年表示に耐えられるのか。

そう考えてまずは直近3か月のグラフ表示できるようにするかと思いついて先ほど実装。
勝率データも古いのは生データじゃなくて統計データを保持するようにしなきゃダメかな?とか考えてるけど生データ捨てるなんてもったいない気もする。

実データだけじゃなくて、適当に大量のデータ生成して実験しないとダメかな。
こうして公開するのは先延ばしになる一方でした。

追記
ランダムデータ作ってみた。6割勝つ計算で時間は適当。
約一年間に18000ゲーム、800KBくらい。
さすがに全期間は描画に時間がかかるけど、3か月描画はそんなに変わらず読み込み自体も重くもなかったっぽいしまた悩みどころ。

2017年12月9日土曜日

アプリ公開の準備。

試しにどれくらいの作業が必要なのか実験。
Googledrive使用のためにパーミッション要求してるのでプライバシーポリシー準備しろとか色々な問題が。

blogのページでもよさげなので簡単にかいてみて、アプリ内でも警告出すようにしてとりあえずあとは公開ボタン押せば良いだけ?

なかなか勇気がでないですなー。そもそも公開ページには自分の実データ晒してたり中々踏ん切りつかないですね。

2017年12月5日火曜日

勝率カウンタ Windows版

そういえば、Windowsでもグラフ表示機能がないわけないよな?と調べてみたら普通に標準で入ってた・・・。
流石.NETですね。

なので、実装してみた。
右上のボタンから


期間指定も同じように実装。
まぁ、とりあえずではこんなものかな?

ついでだから11月のステージ別勝率を。



なんというか、日によってバラバラすぎてわかんないやw
コメントでも出てたけど何戦したのかわからないと何とも、かな。


2017年12月2日土曜日

勝率カウンタ進捗

というわけで、月別表示実装してみました。

スクショだと細かくて見にくいかな。
簡単な細工でこんなこと出来るのは楽しいですね。

ついでにGoogleDriveからのダウンロードの件について。
アップロードは以前共有のシステム使って実現しましたが、こっちはゴリゴリ書かないとでした。
といってもあちこちからサンプルコード引っ張ってきただけだったり。
困ったのはGoogleDrive上で同じファイル名のものが複数あると区別がつかない点。
仕方なくタイムスタンプで区別するか、と思ったらタイムゾーンの変更の仕方が見つからない。
リファレンスにもそれ系の記述が見当たらないので存在しないのかな?
もうちょっと調べてみよう。

あとはネット使うのでエラー処理いろいろ考えないといけないのが面倒。
やっぱ完成はしない気がする・・・。

11月のアリーナ勝率

さて、12月です。 アリーナのランキングもリセットされたことですし、先月データを見てみます。 ARP推移はこちら。

上がっては落ちこんでの繰り返しです。瞬間的に2200超えることもあるんですが負けが多すぎますね。

で、勝率がこちら

累計勝率が50を割ったままなかなか回復出来てないです。
やっぱ負けが多すぎですね

ほんとはキルレートなんかもデータ取りたいんですが試合中やってられない・・・。
しかしグラフにまとめるのもめんどいなー。特にろうそく足。プログラムで出力させるほうが早いんですよね。

Androidアプリは一応GoogleDrive連携を簡単に組んだのでPCで取ってるデータを簡単に移せるようになったしグラフを月別に表示できる機能も作るかな?