The challenge
"Reassemble evidence distributed across base and configuration splits."
| answer_id | Question | Format |
|---|---|---|
answer |
"For case MOB-26-BE982536CB in package com.huntress.mobile.d26.vbe982536cbed: Reassemble evidence distributed across base and configuration splits. Submit only the exact backend answer code returned for this case." | pattern: "ANS- followed by exactly 16 uppercase hexadecimal characters; no spaces or explanatory text"; exact |
The Time Trial asked the same for its own case; the Mastery asked it once per delivery (10 deliveries, 10 answers).
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "base and configuration splits" | Android split APKs: one base. plus split APKs, delivered together as an . bundle |
The artifact was split_, a ZIP of base. and feature.. The solver only looked for . files, hence its stop |
| "evidence distributed across" | The answer isn't in one APK | v1: one part of the answer sits in the feature split's JSON asset. v2: the base app's code reads a 16-byte asset from the split |
| "Reassemble" | Put the pieces back in order | v1: the Day 10 split rule (parts[i] goes to slot order[i]) once the feature part fills its empty slot |
| "the exact backend answer code returned for this case" | The code the app would send to its loopback backend | The backend was never contacted; the value comes from the bundle alone |
| "Split Decision" (title) | Split APKs | Confirmed the reading |
Safety
Static analysis only. Nothing was installed or executed. The bundles were opened as ZIP files and their assets read as JSON or bytes; the DEX code was read in jadx and evaluated by the solver's bounded interpreter, which models only what it knows (int/long arithmetic, a few library calls, and now the split-asset read) under a step budget and stops on anything else. The app's loopback backend was never contacted. For this write-up the bundles were only listed (unzip -l), their puzzle assets printed, and their DEX string tables searched for the control names. The APKs stayed on our Kali VM.
What was in the files
- Single Solve:
split_decision.apks, 21,536 bytes:base.apk(12,704) andfeature.apk(8,552). base.apkcarries the old v1 asset:{"schema":"huntress.mobile.puzzle.v1","day":26,"codec":"split","data":"", "parts":["","ANS-7","E37D…"],"order":[2,0,1]}. Its first part is empty.feature.apk(splitarena_feature, no code) carriesassets/huntress_feature.json:{"codec":"split","day":26,"feature":"arena_feature","part":"4754…","part_index":0}.- Filling
parts[0]from the feature split, then applying the Day 10 rule, givesANS-7E37…EA01. - Time Trial:
split_decision_time_trial.apks, 21,535 bytes, the same bundle shape, but a different kind of puzzle: the base asset is v2 ("codec":"analysis", no data) and the feature split holds a 16-byteassets/huntress_feature.binand no JSON. In jadx,Release.code(ctx)→token()→Core.mergeSplit82(scanMergec4(mixFeature62(K))), andmergeSplit82callsAssets.readSplit("huntress_feature.bin"):ctx.getApplicationInfo().splitSourceDirs,new ZipFile(path),getEntry("assets/" + name), read 16 bytes. The two big-endian longs are mixed in (the high one XORed, the low one added), thenMix.f1,Guard.finPartf9, an XOR constant, and 16 hex digits. - Mastery: 10 deliveries, all v2. Sets 1–9 are plain
.apkfiles with earlier days' controls (from the DEX string tables); set 10 is a split bundle with today's control.
| Set | Day | Control | Artifact |
|---|---|---|---|
| 1 | 2 | deep_ |
., 12,704 |
| 2 | 5 | backup_ |
., 12,704 |
| 3 | 7 | javascript_ |
., 12,704 |
| 4 | 10 | network_ |
., 12,845 (+ res/, resources.) |
| 5 | 12 | sqlite_ |
., 12,704 |
| 6 | 14 | predictable_ |
., 12,704 |
| 7 | 18 | string_ |
., 12,704 |
| 8 | 20 | dynamic_ |
., 12,768 (+ assets/) |
| 9 | 24 | emulator_ |
., 12,704 |
| 10 | 26 | split_ |
., 21,535 (base + feature, huntress_) |
So, as on Days 24 and 25, the Single kept the v1 template and the timed modes used v2. What was new is that the two differed in kind: a v1 reader for the Single's JSON part said nothing about a v2 app that reads its split at run time.
Single Solve
The solver: stopped, nothing sent
started_at 2026-10-26 09:01:00.090 ET (solver; artifact 01-split_decision.apks, 21,536 bytes)
deadline none (Single Solve)
answers none
result STOP: answer: can't match artifact 'split_decision.apks' among []; not submitting.
Timed modes not started.
The Day 25 solver collects .apk files only, so the bundle gave it an empty list. Stopping was right: nothing was sent.
By hand (helper)
The helper unpacked the bundle, read both JSON assets, and worked out one answer: the feature part into parts[part_index], then the Day 10 slot rule (accepted on 2026-10-10). The result, ANS-7E37…EA01, has 16 hex digits and fits the format. It was sent on the solver's open attempt.
started_at 2026-10-26 09:01:00.090 ET (same attempt, submitted by the helper)
answers 1 ANS-7E37…EA01
result CORRECT flag 28426043…882b (09:03:02.447)
The solver update (helper)
The helper built a new solver: the Day 25 one plus split-bundle support:
- .apks bundles: the bundle must hold exactly one base.apk and only flat *.apk members. Every split with a huntress_feature.json must fill an empty parts[] slot (no overlaps), and no part may stay empty afterwards. A base that needs no feature parts goes to the Day 25 decoder unchanged. Plain .apk downloads keep the Day 25 path, so the mixed Masteries still work.
- v2 base in a bundle (added after the Time Trial expired): a Day 26 interpreter that models getApplicationInfo() on the release Context, the splitSourceDirs field (that field only), and java.util.zip.ZipFile over the bundle's own split APKs, with each member's digest checked against the split's MANIFEST.MF. The Day 24 loop is reused with three asserted edits.
Checks: its tests, 10 of 10 (the Single answer, delivery names, a missing feature split stops, overlapping parts stop, two bases or nested members stop, plain .apk keeps the Day 25 path, the TT answer equals an independent hand transcription of the jadx code, Day 25 stops on the TT base, the TT without its split stops, a tampered split asset stops). A regression check against every answer the Game Master had already accepted: 358 of 358 reproduce, 0 mismatched.
Time Trial
First run (ranked): clock expired, nothing sent
09:04:33 start with our local folder name (`…split-decision__time_trial`): 404 challenge_unavailable, no attempt
started_at 2026-10-26 09:04:59.215 ET (ranked, 31 attempts left)
deadline 2026-10-26 09:05:49.215 ET (50 s)
answers none
result EXPIRED (the Day 25 interpreter stopped on Context.getApplicationInfo(); the v2 split read was new)
The first start used our local folder name (__time_trial) instead of the route (/time_trial) and got a 404; the second started the ranked attempt. The solver then had only the v1 bundle support, so the v2 base stopped on an unmodelled call and the 50 s clock ran out with nothing sent. The fix above was written offline; it gives ANS-4F1B…038C for this expired variant, the same as the hand transcription.
Retry (untimed): CORRECT
One untimed retry (unranked, a fresh variant): we allowed ourselves one untimed retry of a failed first timed run.
started_at 2026-10-26 09:08:30.596 ET
deadline none (untimed retry)
answers 1 ANS-2DF2…5A67
retry 62 ms after started_at
artifact 1000 ms after started_at (21,535 bytes)
solve 1017 ms after started_at (17 ms)
submit 1933 ms after started_at
result CORRECT flag 9210355a…afb1 (09:08:31.877)
So the lane has no ranked Time Trial time today.
Mastery
CORRECT on the first attempt, ranked (10 deliveries, 320 s clock; Day 25 had 9 APKs and 290 s). Sets 1–9 went through the Day 25 path; set 10 through the new split control.
started_at 2026-10-26 09:08:38.153 ET
deadline 2026-10-26 09:13:58.153 ET (320 s)
answers 10 (ANS-212F…4E1C … ANS-3D19…E078)
start 133 ms after started_at
artifact 873 ms after started_at
solve 1151 ms after started_at (278 ms)
submit 1663 ms after started_at
result CORRECT flag 331d201f…c437 (09:08:39.424)
Afterwards a regression rehearsal against the Day 25 solver gave 150 identical, 4 newly solved (all Day 26) and 0 changed, and at 09:09:08 ET this solver became our default Android solver for the following days.
Time to solve
All three modes were solved by 09:08:39 ET, 8 min 39 s after release (7 min 40 s from the first start request on this lane). The Single Solve was solved by hand; the ranked Time Trial expired with nothing sent, and the Time Trial was then solved on an untimed (unranked) retry; the Mastery was ranked and went through on the first submit.
Takeaways
- The Single and the timed modes can differ in kind, not just in values. The Single's v1 JSON part said nothing about the TT's v2 code reading a split asset. Before a ranked Time Trial, check the timed variant's control against the solver, not only the Single's answer.
- A new container breaks the collector before the decoder. The solver never reached the puzzle: it found no
.apkin an.apks. Container shapes (.apks,.xapk, nested splits) belong in the pre-build list along with new controls. - The expired TT cost the ranked time, not a wrong answer. The interpreter stopped on an unmodelled call instead of guessing, so nothing wrong was sent, and the one untimed retry got the CORRECT.
- Model only the one field and the bundle's own files. The split read was modelled as exactly
splitSourceDirsand aZipFileover the bundle's own digest-checked splits; anything else still stops. - Use the route, not a local folder name.
…__time_trialis our work folder; the Game Master route is…/time_trial. The 404 came 25 s before the ranked start. - The Mastery mix keeps growing. 10 deliveries today, with Day 10's
network_configand Day 24'semulator_detectionin the v2 mix and today's split control last.