diff --git a/CHANGELOG.md b/CHANGELOG.md index d670297..994c049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ All notable changes to KiouForge are documented here. ## [Unreleased] +### Added + +- **KIOU 1.1.0 support** (CFBundleVersion 15). All 17 hook sites re-verified against the 1.1.0 Mach-O; every struct offset the KIF pipeline reads is unchanged from 1.0.2. + ### Changed +- `TARGET_VERSION` now defaults to `1.1.0`; `1.0.1` and `1.0.2` remain buildable by passing it explicitly. The Makefile maps the version to KIOU-Hook's `KIOU_HOOK_TARGET_BUILD` so the dylib and the recipe always agree on which addresses to bake in, and errors out on an unknown version instead of silently falling back. +- The five direct-call RVAs the KIF pipeline uses (`GetUSIText`, `ToSFEN`, `ParseUSI`, `KIFWriteOptions..ctor`, `KIFWriter.Write`) now resolve through the KIOU-Hook catalog instead of being pinned in `Kif/Helpers.m`, so they follow `TARGET_VERSION` like every other site. +- `make deploy` accepts `THEOS_DEVICE_PORT`, needed when the device is reached through an iproxy forward on the host — there port 22 answers as the host rather than the phone. +- FPS override logging only reports transitions. KIOU re-applies its own target every ~5 s, and logging each one buried every other line (198 of 242 lines in one session). +- Bumped the `vendor/KIOU-Hook` submodule to `8ab5868` (per-version RVA headers under `rva/`, `recipes/v1_1_0.py`) and `shared` (Kanade) to develop, which carries the verify_sites overload resolution and the `caves.py` placeholder rows 1.1.0 needs. - `make deploy` now works on TrollStore Lite devices. `TROLLSTORE_HELPER` defaults to empty and the target SSH-discovers the helper binary at deploy time, preferring `TrollStoreLite.app` over `TrollStore.app` and filtering out stale `TrollStorePersistenceHelper.app` leftovers whose entitlements expire between sessions (which manifest as ssh 255 / SIGKILL 137). The IPA is staged in `/var/mobile/Documents/` instead of `/tmp/` — the trollstorehelper sandbox cannot read `/tmp/` and rejects an IPA there with error 166. The staged file is `chown mobile:mobile`-ed after `scp` so the helper can open it. Non-Lite setups keep working unchanged; operators who pinned `TROLLSTORE_HELPER` in their `.env` are unaffected. `REMOTE_STAGING_DIR` is exposed as an override for setups that stage elsewhere. ## [0.2.1] — 2026-06-26 diff --git a/Makefile b/Makefile index e0df669..bbf17a4 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,20 @@ TARGET_PROCESS := KIOU TARGET_BUNDLE_ID := com.neconome.shogi # Override on the command line: make ipa TARGET_VERSION=1.0.2 -TARGET_VERSION ?= 1.0.2 +TARGET_VERSION ?= 1.1.0 DECRYPTED_IPA ?= $(CURDIR)/assets/$(TARGET_VERSION)/Kiou-$(TARGET_VERSION).ipa + +# KIOU-Hook selects its per-version RVA header (vendor/KIOU-Hook/rva/) by +# CFBundleVersion, not by marketing version, so map one to the other here. +# Without the -D below the catalog silently falls back to its own default +# and the dylib gets built against another build's addresses. +KIOU_BUILD_1.0.1 := 11 +KIOU_BUILD_1.0.2 := 12 +KIOU_BUILD_1.1.0 := 15 +KIOU_HOOK_TARGET_BUILD := $(KIOU_BUILD_$(TARGET_VERSION)) +ifeq ($(KIOU_HOOK_TARGET_BUILD),) +$(error unknown TARGET_VERSION '$(TARGET_VERSION)'; known: 1.0.1 1.0.2 1.1.0) +endif IPA_RECIPE := recipes.__init__ KIOU_HOOK_DIR := $(CURDIR)/vendor/KIOU-Hook IPA_FRAMEWORK := UnityFramework @@ -70,6 +82,7 @@ endif $(TWEAK_NAME)_CFLAGS := -fobjc-arc -Wno-unused-function \ -D$(BUILD_COMMIT_DEFINE)=\"$(BUILD_COMMIT)\" \ -DKIOU_FORGE_VERSION=\"$(PACKAGE_VERSION)\" \ + -DKIOU_HOOK_TARGET_BUILD=$(KIOU_HOOK_TARGET_BUILD) \ -ISources/Chinlan -I$(TWEAK_SOURCES_DIR) \ -Ivendor/KIOU-Hook ifdef FINAL_RELEASE @@ -198,18 +211,26 @@ ipa:: chinlan # INSTALLED_IPA_BUNDLE_ID — bundle id used to relaunch the app # DEVICE_USER — SSH user (defaults to root) # REMOTE_STAGING_DIR — sandbox-visible dir to scp the IPA into +# THEOS_DEVICE_PORT — SSH port. Needed when the device is reached +# through an iproxy forward on the host +# (THEOS_DEVICE_IP=host.docker.internal), where +# port 22 answers as the host, not the phone. # --------------------------------------------------------------------------- TROLLSTORE_HELPER ?= INSTALLED_IPA_BUNDLE_ID ?= $(TARGET_BUNDLE_ID)$(if $(BUNDLE_ID_SUFFIX),.$(BUNDLE_ID_SUFFIX),) DEVICE_USER ?= root REMOTE_STAGING_DIR ?= /var/mobile/Documents +THEOS_DEVICE_PORT ?= 22 +# scp spells the port -P, ssh spells it -p. +DEVICE_SCP := scp -P $(THEOS_DEVICE_PORT) +DEVICE_SSH := ssh -p $(THEOS_DEVICE_PORT) .PHONY: deploy deploy: ipa @helper='$(TROLLSTORE_HELPER)'; \ if [ -z "$$helper" ]; then \ echo "==> discovering trollstorehelper on $(THEOS_DEVICE_IP)"; \ - helper=$$(ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + helper=$$($(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "find /var/jb/Applications /var/containers/Bundle/Application \ -maxdepth 4 -type f -name trollstorehelper 2>/dev/null \ | grep -v 'PersistenceHelper' \ @@ -222,15 +243,15 @@ deploy: ipa exit 1; \ fi; \ echo "==> helper: $$helper"; \ - echo "==> scp $(notdir $(IPA_OUT)) -> $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/"; \ - scp -q $(IPA_OUT) $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT)); \ - ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + echo "==> scp $(notdir $(IPA_OUT)) -> $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(THEOS_DEVICE_PORT):$(REMOTE_STAGING_DIR)/"; \ + $(DEVICE_SCP) -q $(IPA_OUT) $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT)); \ + $(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "chown mobile:mobile '$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))' 2>/dev/null || true"; \ echo "==> trollstorehelper install force $(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))"; \ - ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + $(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "$$helper install force $(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))" @echo "==> launching $(TARGET_PROCESS) ($(INSTALLED_IPA_BUNDLE_ID))" - @ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) 'sleep 1; (open $(INSTALLED_IPA_BUNDLE_ID) 2>/dev/null \ + @$(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) 'sleep 1; (open $(INSTALLED_IPA_BUNDLE_ID) 2>/dev/null \ || uiopen $(INSTALLED_IPA_BUNDLE_ID):// 2>/dev/null \ || echo "no launcher tool; start $(TARGET_PROCESS) manually")' diff --git a/README.ja.md b/README.ja.md index 8e719cb..453b891 100644 --- a/README.ja.md +++ b/README.ja.md @@ -12,7 +12,7 @@
-
+
@@ -136,21 +136,21 @@ KIOU はオンラインゲームのため頻繁にアップデートされ、古
### 機能 × バージョン対応表
-| 機能 | 1.0.1 (build 11) | 1.0.2 (build 12) |
-|---|:---:|:---:|
-| FPS Override | ✓ | ✓ |
-| AFK Guard | ✓ | ✓ |
-| Analysis Tune | ✓ | ✓ |
-| Kifu Autosave | ✓ | ✓ |
-| **アカウント切り替え** | — | ✓ |
+| 機能 | 1.0.1 (build 11) | 1.0.2 (build 12) | 1.1.0 (build 15) |
+|---|:---:|:---:|:---:|
+| FPS Override | ✓ | ✓ | ✓ |
+| AFK Guard | ✓ | ✓ | ✓ |
+| Analysis Tune | ✓ | ✓ | ✓ |
+| Kifu Autosave | ✓ | ✓ | ✓ |
+| **アカウント切り替え** | — | ✓ | ✓ |
-アカウント切り替えは 1.0.2 で追加されたフックサイトが必要なため、1.0.1 では非対応です。Jailed / JB ビルドは RVA がコンパイル時に固定されるため、常に**最新バージョンのみ**を対象とします。Patched IPA は `TARGET_VERSION` でビルド時にレシピを選択します。
+アカウント切り替えは 1.0.2 で追加されたフックサイトが必要なため、1.0.1 では非対応です。アドレスはどの配布形式でもビルド時に `TARGET_VERSION` で決まります。Patched IPA はレシピを、dylib は対応する RVA ヘッダを選択します。
### プラットフォーム
| | |
|---|---|
-| **最新対応 KIOU** | `1.0.2`(CFBundleVersion 12) |
+| **最新対応 KIOU** | `1.1.0`(CFBundleVersion 15) |
| **KIOU の最低 iOS バージョン** | 10.0(アプリの `MinimumOSVersion`) |
| **KiouForge の最低 iOS バージョン** | 13.0(`UIWindowScene` が必要) |
| **動作確認済み** | iOS 15.0 〜 26、arm64 |
@@ -187,22 +187,22 @@ TrollStore が使えないデバイス向けです。[Sideloadly](https://sidelo
**復号済み** の KIOU IPA が必要です([palera1n](https://palera.in/) + Filza または [TrollDecrypt](https://github.com/donato-fiore/TrollDecrypt) で取得できます)。App Store からダウンロードした IPA は FairPlay で暗号化されているため、そのままでは使用できません。
```sh
-# デフォルト(1.0.2)
-mkdir -p assets/1.0.2
-cp ~/Downloads/Kiou-1.0.2.ipa assets/1.0.2/
+# デフォルト(1.1.0)
+mkdir -p assets/1.1.0
+cp ~/Downloads/Kiou-1.1.0.ipa assets/1.1.0/
make ipa
# -> packages/ipa/KiouForge-patched.ipa
# バージョンを指定する場合
-make ipa TARGET_VERSION=1.0.1
+make ipa TARGET_VERSION=1.0.2
```
フックサイトを編集した後や KIOU のアップデート後にビルドする場合:
```sh
# 特定バージョンの dump + IPA でレシピを検証
-PYTHONPATH=shared:. TARGET_VERSION=1.0.2 python3 -m tools.verify_sites \
+PYTHONPATH=shared:vendor/KIOU-Hook TARGET_VERSION=1.1.0 python3 -m tools.verify_sites \
--recipe recipes \
- --index assets/1.0.2/dump.cs.index.json \
- --ipa assets/1.0.2/Kiou-1.0.2.ipa
+ --index assets/1.1.0/dump.cs.index.json \
+ --ipa assets/1.1.0/Kiou-1.1.0.ipa
```
diff --git a/README.md b/README.md
index f5f1148..588ed6c 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -179,23 +179,23 @@ server-side. The recommended target is always the **latest supported version**.
### Feature × version matrix
-| Feature | 1.0.1 (build 11) | 1.0.2 (build 12) |
-|---|:---:|:---:|
-| FPS Override | ✓ | ✓ |
-| AFK Guard | ✓ | ✓ |
-| Analysis Tune | ✓ | ✓ |
-| Kifu Autosave | ✓ | ✓ |
-| **Account Switching** | — | ✓ |
+| Feature | 1.0.1 (build 11) | 1.0.2 (build 12) | 1.1.0 (build 15) |
+|---|:---:|:---:|:---:|
+| FPS Override | ✓ | ✓ | ✓ |
+| AFK Guard | ✓ | ✓ | ✓ |
+| Analysis Tune | ✓ | ✓ | ✓ |
+| Kifu Autosave | ✓ | ✓ | ✓ |
+| **Account Switching** | — | ✓ | ✓ |
-Account Switching requires hook sites introduced in 1.0.2; the Jailed/JB build
-always targets the **latest** version only (RVAs are pinned at compile time).
-The Patched IPA build selects the recipe at build time via `TARGET_VERSION`.
+Account Switching requires hook sites introduced in 1.0.2. Every build shape
+pins its addresses at build time via `TARGET_VERSION`: the Patched IPA picks
+the recipe, and the dylib picks the matching RVA header.
### Platform
| | |
|---|---|
-| **Latest supported KIOU** | `1.0.2` (CFBundleVersion 12) |
+| **Latest supported KIOU** | `1.1.0` (CFBundleVersion 15) |
| **KIOU minimum iOS** | 10.0 (`MinimumOSVersion` in app bundle) |
| **KiouForge minimum iOS** | 13.0 (requires `UIWindowScene`) |
| **Tested on** | iOS 15.0 – 26, arm64 |
@@ -238,23 +238,23 @@ Filza, or [TrollDecrypt](https://github.com/donato-fiore/TrollDecrypt)). The
App Store download is FairPlay-encrypted and cannot be patched directly.
```sh
-# default (1.0.2)
-mkdir -p assets/1.0.2
-cp ~/Downloads/Kiou-1.0.2.ipa assets/1.0.2/
+# default (1.1.0)
+mkdir -p assets/1.1.0
+cp ~/Downloads/Kiou-1.1.0.ipa assets/1.1.0/
make ipa
# -> packages/ipa/KiouForge-patched.ipa
# target a specific version
-make ipa TARGET_VERSION=1.0.1
+make ipa TARGET_VERSION=1.0.2
```
Before building after editing hook sites or after a KIOU update:
```sh
# verify current recipe against a specific version's dump + IPA
-PYTHONPATH=shared:. TARGET_VERSION=1.0.2 python3 -m tools.verify_sites \
+PYTHONPATH=shared:vendor/KIOU-Hook TARGET_VERSION=1.1.0 python3 -m tools.verify_sites \
--recipe recipes \
- --index assets/1.0.2/dump.cs.index.json \
- --ipa assets/1.0.2/Kiou-1.0.2.ipa
+ --index assets/1.1.0/dump.cs.index.json \
+ --ipa assets/1.1.0/Kiou-1.1.0.ipa
```
diff --git a/Sources/KiouForge/Hook/FrameRate.m b/Sources/KiouForge/Hook/FrameRate.m
index 42353de..f4b58eb 100644
--- a/Sources/KiouForge/Hook/FrameRate.m
+++ b/Sources/KiouForge/Hook/FrameRate.m
@@ -16,7 +16,12 @@
static int32_t pickFPS(int32_t value) {
int32_t v = KIOUFeatureEnabled(KIOU_FEATURE_FPS_OVERRIDE)
? KIOUTargetFPS() : value;
- if (v != value) {
+ // KIOU re-applies its own target every ~5 s, so logging every
+ // override would bury everything else. Only report transitions.
+ static int32_t lastFrom = -1, lastTo = -1;
+ if (v != value && (value != lastFrom || v != lastTo)) {
+ lastFrom = value;
+ lastTo = v;
IPALog([NSString stringWithFormat:
@"[FPS] set_targetFrameRate %d -> %d (override)", value, v]);
}
diff --git a/Sources/KiouForge/Kif/Helpers.m b/Sources/KiouForge/Kif/Helpers.m
index af7f607..640d302 100644
--- a/Sources/KiouForge/Kif/Helpers.m
+++ b/Sources/KiouForge/Kif/Helpers.m
@@ -35,10 +35,10 @@
// KIOUKifTextFromGameController is the bridge into il2cpp — everything else is
// pure Foundation.
//
-// Background: GameController.GetKifuText (RVA 0x5D43D10) returns an in-app
-// SUMMARY ("001 ☗ ☗3八飛 … まで、☖後手の勝ち(詰み)"), NOT the standard
-// KIF 2.0 that desktop kifu viewers expect. We instead run the canonical
-// pipeline KIOU uses internally:
+// Background: GameController.GetKifuText returns an in-app SUMMARY
+// ("001 ☗ ☗3八飛 … まで、☖後手の勝ち(詰み)"), NOT the standard KIF 2.0
+// that desktop kifu viewers expect. We instead run the canonical pipeline
+// KIOU uses internally:
//
// GetUSIText(self) → "position startpos moves ..."
// ↓
@@ -71,18 +71,10 @@
// touched by the fill is ThinkingTimesMicros (per-move clock, queued for v0.4).
// ===========================================================================
-// ---------------------------------------------------------------------------
-// RVAs (KIOU 1.0.1 build 11). Same source of truth as KiouUsiProxy.
-// ---------------------------------------------------------------------------
-// RVAs pinned to KIOU 1.0.2 (dump.cs verified 2026-07-02). The previous
-// values were 1.0.1 leftovers pointing at unrelated methods — calling
-// through them at match-end crashed with a SIGSEGV inside GetUSIText
-// (LDR from a bogus X10 loaded from *(gameCtrl+0xB0)).
-#define RVA_GAMECTRL_GET_USI_TEXT 0x5D49970 // string GameController.GetUSIText(this)
-#define RVA_POSITION_TO_SFEN 0x5D49C70 // string Position.ToSFEN(this)
-#define RVA_USIPARSER_PARSE_USI 0x5D5CBB0 // static RecordManager USIParser.ParseUSI(string)
-#define RVA_KIFWRITEOPTIONS_CTOR 0x5D5925C // void KIFWriteOptions..ctor(this)
-#define RVA_KIFWRITER_WRITE 0x5D59264 // static string KIFWriter.Write(RecordManager, KIFWriteOptions)
+// The five methods this pipeline calls are direct-ABI catalog rows in
+// KIOU-Hook (hook_id = -1 — resolved and called, never hooked), so their
+// addresses come from KIOUHookSiteAddr and follow KIOU_HOOK_TARGET_BUILD
+// instead of being pinned to one app version here.
// KIFWriteOptions instance size needed for the raw-buffer trick. See the
// KIFOPTS_OFF_* constants in Internal.h for the field map. Last field
@@ -159,29 +151,41 @@
static KIFWriter_Write_t g_KIFWriter_Write = NULL;
static Position_ToSFEN_t g_PositionToSFEN = NULL;
+// KIOUHookSiteAddr returns 0 for a name the catalog doesn't carry; return
+// NULL rather than a pointer to unityBase itself so callers' existing null
+// checks catch it.
+static void *resolveSite(const char *name) {
+ uintptr_t addr = KIOUHookSiteAddr(name, g_unityBase);
+ if (addr == 0) {
+ IPALog([NSString stringWithFormat:@"[KIF] site unresolved: %s", name]);
+ return NULL;
+ }
+ return (void *)addr;
+}
+
// Resolve the il2cpp NativeFunction pointers we use. Idempotent and cheap;
// safe to call once per export call.
static void resolveIl2cppFunctions(void) {
if (g_unityBase == 0) return;
if (!g_GetUSIText) {
g_GetUSIText = (GameCtrl_GetUSIText_t)
- (void *)(g_unityBase + RVA_GAMECTRL_GET_USI_TEXT);
+ resolveSite(KIOU_HOOK_NAME_GAMECTRL_GET_USI_TEXT);
}
if (!g_ParseUSI) {
g_ParseUSI = (USIParser_ParseUSI_t)
- (void *)(g_unityBase + RVA_USIPARSER_PARSE_USI);
+ resolveSite(KIOU_HOOK_NAME_USIPARSER_PARSE_USI);
}
if (!g_KIFOpts_Ctor) {
g_KIFOpts_Ctor = (KIFWriteOptions_Ctor_t)
- (void *)(g_unityBase + RVA_KIFWRITEOPTIONS_CTOR);
+ resolveSite(KIOU_HOOK_NAME_KIFWRITEOPTIONS_CTOR);
}
if (!g_KIFWriter_Write) {
g_KIFWriter_Write = (KIFWriter_Write_t)
- (void *)(g_unityBase + RVA_KIFWRITER_WRITE);
+ resolveSite(KIOU_HOOK_NAME_KIFWRITER_WRITE);
}
if (!g_PositionToSFEN) {
g_PositionToSFEN = (Position_ToSFEN_t)
- (void *)(g_unityBase + RVA_POSITION_TO_SFEN);
+ resolveSite(KIOU_HOOK_NAME_POSITION_TO_SFEN);
}
}
diff --git a/control b/control
index e1ca59c..091f645 100644
--- a/control
+++ b/control
@@ -1,6 +1,6 @@
Package: work.tkgstrator.kiouforge
Name: KiouForge
-Version: 0.2.1
+Version: 0.2.2
Architecture: iphoneos-arm64
Description: KIOU (Shogi) local quality-of-life tool. Extends the frame-rate preset beyond 30/60, suppresses false AFK warnings during long-think sessions, and lets you tune the depth and hash used by the on-device post-game kifu analysis engine. Runs entirely client-side and never modifies any server-stored data. For authorized testing only.
Maintainer: NEVER KNOWS BEST
diff --git a/docs/plans/on-device-smoke.md b/docs/plans/on-device-smoke.md
index 8355a4d..36bfdc1 100644
--- a/docs/plans/on-device-smoke.md
+++ b/docs/plans/on-device-smoke.md
@@ -78,9 +78,10 @@ practice. Broader flows can layer on the same harness later.
frida-il2cpp-bridge`, or `pip install frida-tools` if we go Python).
The design below is bun-based to match KiouForge's existing tool
chain in `shared/tools/`.
-- **`assets/1.0.2/dump.cs.index.json`** (already checked in) — Frida
- script reads class + method names from it so RVAs never appear
- literally in the script (they change per app version).
+- **`assets/$TARGET_VERSION/dump.cs.index.json`** (already checked in
+ for 1.0.1 / 1.0.2 / 1.1.0) — Frida script reads class + method names
+ from it so RVAs never appear literally in the script (they change per
+ app version).
## Architecture
@@ -267,7 +268,7 @@ scripts/
case explicitly; user-visible failures still map to "SSH says
frida-server crashed" rather than false test failures.
- **Presenter signatures change per app version.** The script pins
- to 1.0.2 today; when 1.0.3 lands, presenter method names or
+ to 1.1.0 today; when the next build lands, presenter method names or
signatures may drift. Mitigation: keep the mapping (presenter,
method, args) in a small YAML config file, loaded per
`TARGET_VERSION`.
diff --git a/shared b/shared
index c29a372..b670411 160000
--- a/shared
+++ b/shared
@@ -1 +1 @@
-Subproject commit c29a372c7cd1d447c1f12a8b580986aba327e9ef
+Subproject commit b6704113065f4ccef308926d87895c49f5691187
diff --git a/vendor/KIOU-Hook b/vendor/KIOU-Hook
index 36a3ad1..8ab5868 160000
--- a/vendor/KIOU-Hook
+++ b/vendor/KIOU-Hook
@@ -1 +1 @@
-Subproject commit 36a3ad15d5f67cdcc3b68e7374049771c8a960a7
+Subproject commit 8ab586811f017520fe185631bc238c551d711adc