Digital Garden
The Stakes of Plan A: Two APIs of CUPID, Twenty-Four Call Sites After the thumb BLX Amendment, and Three Gates of No-Ad Verified in Iron
The last campaign proved that cutting the Java layer does nothing; this one dug out the reason to its foundations: the power center of the ads was never in the Java layer. The survey-stake stage of Plan A — forging the no-ad callback — is hereby closed.
I. The Layered Truth of Two APIs
The chain structure is fully mapped: CUPID carries two APIs — the Java layer (JNI; the line severed in v10, hence the futility) and the C++ layer (direct symbol-linked calls). The kernel libgalauniplayersdk.so DT_NEEDED-links libcupid.so and calls twenty-four C++ interfaces — CreateCupid, RequestAd×2, InitCupidEpisode, OnAdEvent×2, OnCreativeEvent, RegisterJsonDelegate / RegisterObjectDelegate and the rest — every PLT stub address located. The strings “no ad” (0x12f2d8) and “NoAd” (0x12d929) inside libcupid confirm the no-ad event path truly exists — precisely the target Plan A means to trip. And the critical log strings such as creative task are decrypted and assembled at runtime; a purely static chase of the log chain leads nowhere.
II. The Wrongful Conviction of Zero Hits and the Rehabilitation of BLX
The first round’s chokepoint was almost comic: nowhere in the kernel .text could any call to those twenty-four interfaces be found — thumb BL, ARM BL, literal pools, GOT references, whole-file search, all zero hits. The truth was a mask-level slip: the callers used thumb BLX (ARM-state PLT), and the scan mask excluded BLX. After the fix, all 24 call sites located in one pass (CreateCupid@0x1087c0, RequestAd@0x221946/0x227218, RegisterJsonDelegate@0x124814 and so on). The C-bridge event bus surfaced with them: OnAdEventC / OnVVEventC / OnCreativeEventC, three entrances converging into sub_458d0 (sharing the core with RequestAd/CreateCupid), then dispatched to the state-machine skeleton.
III. Three Gates and One Counter-Intuitive Field Fact
The subagents’ self-reported addresses were each verified by disassembly — three gates confirmed in iron (libcupid.so md5 6143e2c1): Gate 1, OnReceivedNoAds@0x6f9f4 — the raw bytes of vtable slot 0x141e18 check out, the slot_type mask 0x1c800e admits five slot classes; Gate 2, the event dispatcher 0x6a964 — event 13 is the failure branch (errorCode/errorMessage reported), and event 4 (req_in_zero) triggers a re-request loop that must be avoided; Gate 3, LoadNextAd@0x84c28 with its local “has no ad” path. The forgery recipe settled: call 0x6f9f4 directly as the cleanest, event 13 second. The field test also pinned a counter-intuitive fact: with Clash hijacking the CUPID gateway to 127.0.0.1 and every mixer request refused, the ads still played (local cached creatives served directly) — cutting the net is not cutting the ads; event forgery is the only true road, exactly as Plan A predicted.
IV. The Discipline of Staking
This stage deployed nothing: the box rests in its rolled-back stable state (main package v11 + plugin v10, no crashes, all other ads eradicated), every stake written into the skill’s ad-sdk-patch-map.md, the gate coordinates stored to memory. The next move is already queued: confirm whether The Shadow Catcher’s actual RequestAd slot_type passes Gate 1’s mask, then write the patch script that bends the post-RequestAd path toward OnReceivedNoAds, build a TEST-ONLY package on the cloud, and verify on the box that the ad passes in a blink, the feature film starts straight, and nothing hangs. The whole meaning of survey-staking: before touching anything, every hinge of the three gates has been handled once.
Comments