
iOS 版古代文献の解読と記述について説明します。
iMazing などを利用して入手した .ipa ファイルの拡張子を zip に変更して展開する。もしくは Apple Silicon 搭載 macOS へインストールの場合は次の位置に。
/Application/イース6.app/Wrapper/ios.app
~/Library/Containers/(GUID)/Data/Library/Caches
└── ios.app
├── Data
│ ├── Managed C# 関連ファイル
│ ├── Raw オープニング動画
│ ├── Resources Unity 用
│ └── resource リソース本体
├── Frameworks 各種フレームワーク
└── そのほか いろいろ
大部分の情報は resource
ディレクトリにある。静的解析に更新パッケージは必須ではなく、アプリ本体にファイル一式が含まれている。
gameconfig
ディレクトリ以外は Unity の .bundle
ファイル形式で配置されており、内容は AssetStudioMod で閲覧できる。これらのディレクトリ構造は《わくふぁん》とほぼ同一である。
resource
├── TestServer
├── actor
├── anim
├── animation
├── audio 音声/SE/BGM
├── avatar
├── effect
├── gameconfig
│ ├── lua LuaJIT バイトコード
│ ├── multiple
│ ├── table テーブルデータ
│ └── task
├── live Live2D モデル
├── miscellaneous
├── multiple 多言語対応
├── scene
├── shader
├── sprite 2D画像
├── timeline
├── ui
├── video
└── voxel
gameconfig/lua
ディレクトリ以下には Lua スクリプトが LuaJIT のバイトコード形式で格納されており LuaJIT Decompiler v2 で復元できる。
gameconfig/table
ディレクトリのデータの解読方法は、可読性を復元した Lua スクリプトに書かれている。ImHex でパターン (後述) を書けば解読できる。テーブル数が多いので、類型を見極めて解読処理の自動生成を検討できると応用が利くだろう。なお、データは配列型を含む表形式になっている。
Lua は組込に特化したプロトタイプベースのオブジェクト指向のスクリプト言語である。重要な処理は C# 側やサーバー側で隠蔽されている……はず。Lua 特有の機能はあまり使われていないため、おおまかな制御の流れを追うのは容易である。
外部ライブラリと思われるものを除いて、特徴的なファイル名が採用されている。
lua
├── csvtable テーブルのモデル定義
│ ├── gacsvrequires.lua 全読込用
│ ├── gacsvtablemanager.lua 管理タイプ Csv
│ ├── gacsvtableposmanager.lua 管理タイプ Pos
│ └── gacsv*.lua 各テーブルのモデル定義
├── network ゲームサーバーとの通信関連
│ ├── galuanetwork.lua 通信処理
│ ├── gaprotodefine.lua 定数風 Luaテーブル定義
│ ├── gaprotoenum.lua 列挙型風 Luaテーブル定義
│ ├── gaprotohandler.lua フラット化
│ └── gaprotoregister.lua ID と メッセージ定義の対応付け
├── protobuf Protocol Buffers 関連
│ └── protos .proto ファイル
└── gagameclient.lua Lua 側の処理の起点
lua/protobuf/protos
ディレクトリには Protocol Buffers (以下 ProtoBuf) 用の定義ファイルが格納されている。これを用いて通信の解読と記述が可能である。
隠し要素があたかもしれない。
function uilogin.onbutton_notice(arg_18_0)
uinotice:SetShow(true)
if CS_GetPlatformId() == EPlatformType.EPlatformType_QuickSDKYS or CS_GetPlatformId() == EPlatformType.EPlatformType_RestarYS then
CS_ShowGameNotice()
end
var_0_0._noticeButtonClickCount = var_0_0._noticeButtonClickCount + 1
if var_0_0._noticeButtonClickCount > 20 then
GAServerList.bShowTest = true
end
if CS_GetPlatformId() == EPlatformType.EPlatformType_Zlong then
GAGameManager.doSdkCommand(ESdkCommand.LogEvent, "zlong|clickannouncements")
end
end
ログイン画面でお知らせボタンが押されたときの処理である。
察しが付くように、ほかの地域のパブリッシャー向けのコードも含まれている。
ちなみに、フラグによってサーバーリストに追加表示される対象は、通常のインターネットからは到達不能のようで、なにか支障のある事ではないと思われる。
プレイ中の主な通信先である AWS との通信内容を説明する。
ここで「電文」はアプリケーション層の単位データを、「メッセージ」は ProtoBuf 用語の message を指す。
パケットキャプチャの仕方や通信経路の工夫の仕方については割愛する。
サーバーとは TCP 接続1本で、送受信共にこの電文の繰り返しである。状況により、一つの TCP パケットに複数の電文が含まれていたり、一つの電文が複数の TCP パケットに跨がっていたりする。
通信内容は ProtoBuf でシリアライズされた平文である。ProtoBuf の守備範囲外となるメッセージ ID とメッセージの対応は lua/network/gaprotoregister.lua
で定義されている。Wireshark は ProtoBuf にも対応しているので、これらを踏まえて Lua で Dissector を書けば、通信内容をリアルタイムに見ることも可能である。また、TCP リレーを仕掛ければ、バケツリレーをしながら柔軟な処理も可能となる。
仕組みとして通信内容の改竄やパケットインジェクションへの備えは見られないが、状況にそぐわない通信の際には AWS から接続をぶち切られるだろう。
ProtoBuf の定義ファイルは次の要領で分割されている。簡体字の注釈なども見られ、各所で《わくふぁん》等を含む経緯が伺える。
*_protocol.proto
…… 通信で用いるメッセージの構造体*_define.proto
…… 各メッセージの中で用いる構造体メッセージ名は VersionReqC2A
のように「CamelCase+種別+送受信」の形式になっている。
種別は3種類。
Req
(Request) …… 応答を要する通信Ack
(Acknowledge) …… リクエストへの応答Ntf
(Notify) …… 応答不要の通知送受信は9種類。おそらく次の略記。
C2A
…… Client to Account-serverA2C
…… Account-server to ClientC2G
…… Client to Game-serverG2C
…… Game-server to ClientC2M
…… Client to Manage-serverM2C
…… Manage-server to ClientC2O
…… Client to crOss-serverO2C
…… crOss-server to ClientS2C
…… Server to Clientメッセージ内容は、多くの項目が optional
(省略可能) と repeated
(0個を含む配列) であり、JSON ライクなツリー状になっている。定義元と行き来できる IDE で閲覧すれば把握は容易だろう。
メッセージ中の文字列型には、次のいずれかで格納されている。
各テーブルは主に次のファイルで構成される。データはビッグエンディアンで格納されている。
table/*.data
…… データ本体table/*.text
…… データ中のテキストtable/*.pos
…… 索引情報lua/csvtable/gacsv*.lua
…… モデル定義共通的する依存処理等。
table/table.data
…… 管理タイプ Csv 詰合せデータtable/tablepos.data
…… 管理タイプ Pos 詰合せデータlua/csvtable/gacsvtablemanager.lua
…… 管理タイプ Csv 初期化処理lua/csvtable/gacsvtableposmanager.lua
…… 管理タイプ Pos 初期化処理lua/common/gastreamdata.lua
…… バッファ読込クラスlua/common/gastringfile.lua
…… テキスト読込クラスフィールド名プリフィックス。
i64
…… int64
n64
…… uint64
i
…… int32
f
…… float
b
…… bool
str
…… string
listI
…… int32[]
listF
…… float[]
listStr
…… string[]
配列構造。
uint16
配列の要素数文字列構造。
uint16
文字列のバイト数ImHex はドキュメントが寂しいのが難点。crystal.data
用のサンプルを載せる。
#pragma endian big
import type.base64;
fn base64_value(auto input) {
return type::impl::decode_base64(input.value);
};
fn value(auto input) {
return input.value;
};
struct String {
u16 length [[hidden]];
char value[length];
} [[sealed, format("value")]];
struct StringBase64 {
u16 length [[hidden]];
char value[length];
} [[sealed, format("base64_value")]];
struct ListInt32 {
u16 length [[hidden]];
u32 value[length];
};
struct ListFloat {
u16 length [[hidden]];
float value[length];
};
struct ListStringBase64 {
u16 length [[hidden]];
StringBase64 value[length];
};
struct AddrIndex {
u32 addr;
u16 index;
};
struct CsvManager {
u8 signature[16];
u32 data_length;
StringBase64 filename;
u16 item_count;
u32 addr1[item_count];
AddrIndex addr2[item_count];
};
struct Crystal {
u32 length;
s32 iID;
s32 iType;
String strName;
s32 iQuality;
StringBase64 strQualityIcon;
s32 iSlotGroup;
StringBase64 strIcon;
StringBase64 strImage;
StringBase64 strUiLive2D;
ListFloat listFUiLive2DOffset;
float fUIScale;
StringBase64 strUiLive2DBG;
float fUIScalePrizedraw;
ListFloat listFUiLive2DOffsetPrizedraw;
StringBase64 strUiLive2DPrizedraw;
StringBase64 strUiLive2DBGPrizedraw;
bool bSkillUpJudge;
s32 iShapeShiftID;
s32 iSkillComposeID;
s32 iComposeShapeShiftID;
s32 iServerLevel;
s32 iOrder;
bool bCanRecast;
s32 iRecastCost;
s32 iRecastCostNum;
s32 iLockCost;
s32 iLockMax;
s32 iRateUpNeedTimes;
s32 iFightPower;
ListStringBase64 listStrAttr;
ListInt32 listIAttrValue;
s32 iSingleTowerAttr;
s32 iNewCrystalType;
s32 iNewCrystalRank;
s32 iIllusionCrystalSkill;
};
CsvManager manager @ 0;
Crystal items[manager.item_count] @ (manager.addr1[0] + 19);
最後の謎のオフセット 19 の正体は
s32 や u32 より int32 や uint32 と書きたい場合はエイリアスを定義できる。
using int32 = s32;
using uint32 = u32;
using int64 = s64;
using uint64 = u64;
AssetStudioMod の [File] – [Load folder] で resource/live
を開いて [Export] – [Live2d Cubism models] – [All models] で抽出される。
Live2D の Cubism 5 Viewer for Unity で [ Load Json ] ボタンから *.model3.json
を開けば表示されるだろう。
個人的には daibola_idle
のまばたきを [ AutoEffect ] – [ EyeBlink ] で止めて [ Parameters ] – [ ParamEyeROpen ] を 0.00
にすると良いのではないかと。巴芳が「えっちなのはいけないと思います!」と言ったかは兎も角として、中国本国での表現規制に沿うようナーフされて daibola
の姿となった可能性はあたかも……。
Protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
まだコメントがありません。