# akatsuki patcher / technical report i reviewed the supplied loader and the stable DLL returned by its service on 11 october 2026. this is the detail behind the blog post: what the code changes, what it collects, and how that report gets into a score request. i didn't run the loader, patcher, game client or generated application code. the paths below come from static inspection, not a packet capture. the part that bothered me is the reporting code in the downloaded patcher. it collects information about other programs, including visible window titles, executable paths and executable hashes. it turns that into JSON, encrypts it with AES, and substitutes it for `fs` in osu!'s request builder. the gameplay changes and this reporting live in the same injected library. i also traced what `fs` originally meant in the decoded client i've had since july. that reference supplies an encrypted string of six gameplay and visual settings. its recovered managed code adds no process list or native-auth output to that plaintext. what i can establish here is a change to the request field; a direct patch to `osu!auth.dll` is still unproven. ## exact artifacts | file | bytes | sha-256 | | --- | ---: | --- | | Supplied `akatsuki_patcher.exe` | 1,378,202 bytes | `87f8dadbe18cac959194dfb173eea9f1076cc8943279cb018e40b25073193002` | | Extracted `Akatsuki.Loader.dll` | 1,136,640 bytes | `b2e738596763639c8c80d27f19aae775b3f7f351b617655d179051f8052efea0` | | Replacement loader offered on 11 october, `20261005.exe` | 1,381,274 bytes | `1c1a0aee7b79b4836241dd8b496531842330ffdc6769796ac95d6a8903fcda9b` | | Replacement's `Akatsuki.Loader.dll` | 1,139,712 bytes | `1bb0473924dff5d0c13c1a479215c94fd33e3acbbf36c9cf232307f71a0ee6c1` | | Acquired stable payload, `patcher-stable-20261007.dll` | 3,674,624 bytes | `7dbc3efcbff2dba1d38ba2fecec46ed69828d38349eb2e1f087ae640aadb5441` | | Payload's decompressed `Native.x86.dll` | 610,816 bytes | `2cbd7373d29279021c5c347b70476eb7b4cb7a444acc8b3ba00eea93a2d4dd62` | the payload identifies itself as `Akatsuki.MonoPatcher`, version 1.0.0.0, x86 CIL. on 11 october, my stable request returned HTTP 302 to `s3.ca-central-1.wasabisys.com/akatsuki.pw/patchers/stable-20261007.dll`, followed by HTTP 200. i used macOS curl with certificate checks enabled. an earlier Python TLS failure didn't mean the file was unavailable. i've left the temporary signed query out of this report. i downloaded this DLL separately from the service. it isn't embedded in the supplied exe. the service can give another branch or a later request different code, which is why i've kept the exact hashes above. the metadata i saved also offered loader version `20261005`. its expected MD5 differs from the supplied exe, so the updater's comparison would lead it to replace itself. i acquired the replacement as bytes and checked its MD5 against the metadata. the network, update, auth-wait and injection paths match the supplied loader. the relevant changes concern checking an unelevated game launch and reporting launch errors. all 24 managed embedded resource payloads match between the two loaders. the separate replacement review is retained in `deeper-review/replacement-loader/report.md`. the metadata response has SHA-256 `80b3ee2e73b6c2d25df1702de9069731b91a802414dd63b398241f33d228776c`. ## what happens to osu! the loader's code launches osu! with `-devserver akatsuki.gg`, copies the downloaded library into the game process and calls `Akatsuki.Patcher.Main.Initialize`. the payload finds methods in the entry assembly and installs MonoMod runtime/IL hooks. the game changes i traced are memory hooks; i haven't tested their installation on a running client. the entry-assembly selection is in `0x0600010e`, `IL_0d7f`. it calls `Assembly.GetEntryAssembly` and falls back to the patcher's own assembly. target discovery then uses that assembly's modules and types. feature registration is in `0x060002df`; the installation path is `Main.Initialize` → `0x060000db` → `0x060000dc` → `0x060000fe`. these are the feature groups i recovered from the registration code. i've kept the target names so someone else can follow the same hooks. the descriptions come from the settings text and code; they don't prove the hooks successfully apply to every osu! build. | Feature group | Registered targets or effect | | --- | --- | | Core | `GetString`, `ASyncLoader`, `CheckMods`, `InitializeOptions`, `RefreshOptions`, `CanExpandOptions`; option text, mod handling, async-load callbacks and settings UI | | Always Allow Failing | `CheckFailed`; changes the RX/AP guard so failing and NF/SD/PF behavior can be enabled | | Mod Selection Leaderboard | `ModSelectionChanged`, `CheckMods`; mod-selection/leaderboard handling | | Always Show Misses | `RenderHit`, `IncreaseScoreHit`; RX/AP miss behavior | | Local User Score | `LoadLocalUserScore`; local-score handling | | UR Text | `LogHitError`, `TextSprite`; unstable-rate display and scale | | Player Update | `PlayerUpdate`, `LogHitError`; gameplay/display updates including live performance | | Notelock | `CheckClickAction`; optional notelock removal, with a separate submission guard | | Live PP Leaderboard | `PlayerUpdate`, `LogHitError`; ranking by live PP | | Failed Hit Error Markers | `LogHitError`, `CheckClickAction`, `RenderHit`; failed-tap error markers | | Gameplay Mods Overlay | `GameplayModsLoad`; keeps selected mods visible | | Live Mod Audio | `PreviewAudioLoad`, `PreviewTrackConstructor`, playback-rate getters/setters; DT/NC/HT preview changes | | Audio Pitch | `ApplyPlaybackRate`, `GetCurrentPlaybackRate`; pitch adjustment | | Nightcore Beat | `NightcoreBeatUpdate`; optional Nightcore percussion removal | | Instafade Circles | `HitCircleAnimate`; immediate circle disappearance on a successful hit | | Slider Body Customization | Renderer discovery plus `CanExpandOptions`/`CheckMods`; border, opacity and saturation controls | | Slider Preview | Preview renderer and `CursorTrailUpdate`/`SetChildren`; preview lifecycle and drawing | | Score Dialog Mods | `ScoreDialog`; score-dialog mod display | | Cursor Trail | `CursorTrailUpdate` and renderer hooks; trail length/scale/color and local shaders | | Submission | `SubmitScore`, `AddParameter`; submission gating and replacement of the `fs` parameter | the embedded native DLL exports `akatsuki_live_pp_calculate`, `create`, `destroy` and `version`. its imports and strings fit a local pp calculator. i found no networking or window-capture API in that native component. the process reporter is in the managed parent library. i don't have a complete graph of indirect native calls, so that negative finding has a limit. ## collection and report fields this is the report's top-level schema: ```text client_hash detections method_coverage misc_detections running_processes timestamp ``` each `running_processes` entry has `title`, `path` and `hash`. `0x06000114` calls `Process.GetProcesses`, keeps the process ID and name, resolves its executable path and associates a visible window title. `0x06000122` builds the report list and excludes the current game by PID. it doesn't limit that list to suspected cheats. the window callback is `0x06000317`. it uses `GetWindowThreadProcessId`, `GetWindow`, `IsWindowVisible`, `GetWindowTextLength` and `GetWindowText` to select a first visible ownerless window for each PID. that isn't every window, every browser tab or the contents of a document. a title can still reveal a document name, conversation or page. those are examples of what this field can expose; i haven't captured anyone's actual window titles. the executable-path lookup uses `OpenProcess` and `QueryFullProcessImageName`. `0x06000121` reads executable files to calculate their hashes, with a cache based on path, file length and modification time. the underlying `0x060000ca` uses MD5. permissions and errors can leave fields unavailable. this path hashes the files; it doesn't upload their contents. `client_hash` is the MD5 of the selected entry assembly's file, cached by `0x060001c3`. the report constructor, `0x060002fd`, sets its timestamp with `DateTime.UtcNow.Ticks`. the other sections cover method integrity and heuristic detections. i recovered these field names: ```text detections: name, hash, instructions method_coverage: target, status, method, return_type, parameters, baseline_hash, details misc_detections: name, details running_processes: title, path, hash ``` `0x06000123` and `0x06000124` prepare and compare method baselines. the lower scanner, `0x06000126`, uses `VirtualQuery`, `Marshal.Copy`, Iced instruction decoding and MD5 on code in the current process. its extra targets include mouse-position routines, private initialization, playback-rate routines, `PlayerUpdate` and `CheckFlashlightHax`. two checks in `0x06000115` stood out. one looks for `gosumemory` or `gosumemory.exe` and a path containing `package`, then creates an `assist.games` detection with an executable hash and folder location. another checks for Discord Canary and whether the windows username equals `user`, ignoring case. the labels it emits don't prove someone is cheating. the full username isn't a separate report field, but paths can contain it, and the second label reveals that username-equality condition. i found `assist.games` used as a detection label in this path. i didn't establish an outbound connection to that address. ## local files and other side effects the main payload directly writes `%LOCALAPPDATA%\Akatsuki\config.ini`, nine example shaders under `%LOCALAPPDATA%\Akatsuki\shaders`, and `Logs\patcher.log` beside the entry assembly. settings are written through a temporary file in the same directory, then replaced or moved into place. an in-process timer and exit handlers flush them. those handlers don't register windows startup. the native pp resource is decompressed into a MemoryStream and passed to an in-memory PE mapper. that chain doesn't extract a DLL to disk. in the main-payload inventory i found no direct `osu!.exe` overwrite, program launch, registry autorun, scheduled task or service registration. that check doesn't cover native internals, indirect reflection, dependencies or the loader. it also doesn't erase the memory hooks and process reporting described here. the supporting file is `deeper-review/payload-side-effects/report.md`. ## how the report enters the request the report enters through the game's existing request builder. here's the hook chain, including the method references: 1. `Submission` is registered as required in `0x060002df`, at `IL_1048`–`IL_1049`. 2. the hook plan in `0x060002cd` connects `AddParameter` to manipulator `0x06000110`. 3. that manipulator inserts `0x06000111` at the start of the original method and stores its result back into the value argument. 4. `0x06000111` checks whether the name is `fs`. other values pass through. `fs` gets the output of `0x06000113`. 5. the recovered generated body behind `0x06000113` runs the method scan, process snapshot, heuristics and report construction. the body behind `0x060002fe` calls `Newtonsoft.Json.JsonConvert.SerializeObject`. 6. `0x060000c8` encrypts the UTF-8 JSON with AES, using an embedded 16-byte key, CBC and PKCS#7. a GUID-derived 16-character string supplies the IV. the ciphertext is Base64 encoded. 7. the final value is `Base64(UTF8(marker + "|" + ciphertext_base64 + "|" + iv_string))`. the marker comes from `0x06000112` and is constant in this build. the marker and AES key are separate values. this establishes a change to osu!'s request-building path. the loader also waits for `osu!auth.dll` during initialization, but that wait doesn't establish a hook into the DLL. i haven't found a hook specifically targeting it. i traced the original managed `fs` producer separately, below. ## original fs producer in the supplied client the july reference's `Score.submit` method, `0x06002b6c`, is an Eazfuscator VM wrapper. offline arithmetic and metadata parsing recovered its protected resource. all 173 RSA blocks passed PKCS#1 padding validation and yielded 42,327 bytes. the submission selector resolves to `0x7854`, an 8,048-byte VM method. i recovered the first 970 operations before a later protected block outside the relevant `fs` path. none of those application operations was executed. the argument flow i recovered is: ```text request.AddParameter( "fs", Base64(RijndaelCBC256BlockEncrypt( Score.get_visualSettingsString(), UTF8(GetScoreSubmissionKey()), score_request_IV )) ) ``` this is explanatory pseudocode, not a complete decompilation. VM offset `0x480` loads the protected string key for `fs`; `0x494` calls `get_visualSettingsString`; `0x692` calls `pWebRequest.AddParameter`. the intervening stack stores keep the request, name, visual plaintext, key and IV separate. the plaintext passed to the stream writer comes from the visual getter. the original cipher is `RijndaelManaged`, with a **256-bit block size** and CBC. that isn't AES, which has 128-bit blocks. `GetScoreSubmissionKey` takes no arguments and formats a key using `General.VERSION`. the IV is shared with the earlier encrypted `score` field; the routine accepts that Base64 IV or generates one if it's absent. the version-derived key and IV are cipher inputs, not extra plaintext fields. the visual getter calls `String.Format("{0}:{1}:{2}:{3}:{4}:{5}", ...)`. in order, its inputs are dim level, sample disabling, skin disabling, storyboard disabling, background visibility and whether the score had a storyboard. the recovered managed block adds no process list, window list or native-auth output to that string. the akatsuki hook replaces its ciphertext with the patcher's AES-encrypted JSON envelope. the separate `osu.AC.load` and `unload` wrappers call `LoadLibrary("osu!auth.dll")`, resolve `Load` and `Unload`, and invoke void delegates with no arguments. that tells me how the managed client loads the native module. it doesn't reveal what the module does inside at runtime. this producer trace is established for the supplied reference. matching surrounding getter and request routines in the fresh official executable support the comparison. i haven't independently decrypted that executable's protected VM body or observed a matching-build runtime. the recovery material is retained in `deeper-review/client-vm/report.md`. akatsuki's pinned public [route registration](https://github.com/osuAkatsuki/score-service/blob/2ae09bdaf872d49cf6ebe494140942d3a007766d/app/api/__init__.py#L43) accepts POST `/web/osu-submit-modular-selector.php`. its [handler](https://github.com/osuAkatsuki/score-service/blob/2ae09bdaf872d49cf6ebe494140942d3a007766d/app/api/score_sub.py#L138) accepts `fs`, keeps it in the submission event and forwards successful passed-score events to AMQP. the [nginx configuration](https://github.com/osuAkatsuki/hetzner-infra/blob/52cf3d5ad60420e39c040b1881b2216faf9b8dd3/config/nginx/sites-enabled/web.conf#L3) maps `osu.akatsuki.gg` and `.pw` `/web/` routes to that service. this supports the intended carrier. i haven't verified the live deployed commit or downstream report processing. ## public description and assessment on 11 october i reviewed the [download page](https://akatsuki.gg/patcher), [FAQ](https://akatsuki.gg/doc/faq), [troubleshooting guide](https://akatsuki.gg/doc/patcher_troubleshooting) and [terms](https://akatsuki.gg/doc/tos). the download page advertises relax gameplay features, and the FAQ explains injection as the reason for security warnings. the troubleshooting guide recommends disabling defender and exclusions for the patcher and temporary folder. the terms contain game rules. i couldn't find this process/window-title reporting explained on those four pages. that doesn't establish what every version or every other notice says. my concern is the collection of information about unrelated programs and its substitution into a score request. i consider that invasive and spyware-like. the evidence here doesn't establish criminal intent, a legal violation, credential theft, a keylogger or a remote-control backdoor. the blog post explains the malware assessment and the legal questions separately. ## verification and remaining limits the supplied exe is unchanged. static decoding covered all 1,453 declared managed methods: 1,371 bodies and 69,853 instructions, with zero body-decoding failures. all 1,170 hidden-string decoder calls were recovered. the three generated-code builders were reconstructed as data: a 68-byte marker method, a 332-byte report/envelope method and a 1,445-byte JSON method. no generated application CIL was run. an independent decoder reproduced the marker, and a second analysis cross-checked the schemas, AES parameters and `fs` hook. i found no separately coded network client or extra fixed network destination in the main patcher. the three reflection dispatchers are confined to the three recovered IL builders. the dependency pass below extends that check, but it remains a static review of these exact bytes. ## deeper review of dependencies, server routing and client code all 23 payload resources were decoded. twelve of the thirteen managed dependency DLLs match official NuGet package members byte for byte. the remaining Iced 1.20.0 DLL is smaller: all 1,692 method names occur upstream, 1,664 normalized bodies match, and the 28 differences reviewed concern formatting/encoding feature removal and Boolean lowering. that fits a reduced-feature build, though it doesn't establish its exact source or build provenance. the pass covered 16,881 declarations, 15,846 CIL bodies and 369,816 instructions with zero body failures. i found no extra network or process/window reporter there. the reporting described above belongs to the patcher's own assembly. the retained dependency report is `deeper-review/dependencies/dependency-report.md`. the public server code shows a configured AMQP handoff. public infrastructure names an AMQP processor service but doesn't expose its relevant queue binding or report decoder. the 33 public SQL migrations don't establish a store for these reports. broker permissions and backup rules don't tell me who can read the reports or how long they're kept. the supporting server review is `deeper-review/server-routing/README.md`. the older `decoded.exe` and its symbol-name map are files i've had since july. their exact build and tool provenance are unknown. the names helped identify the routines: `Score.get_visualSettingsString` formats six ordinary gameplay/visual fields, and `pWebRequest.AddParameter` adds a supplied name and value to a dictionary. the VM trace above establishes the connection between that getter and `fs` in this reference. i also retained a fresh official stable40 manifest and its matching executable/auth DLL. the executable and decoded reference have the same type/method counts, but different MVIDs and reordered identifiers. the fresh executable has unique matching opcode-pattern candidates for the visual getter, `0x060040a9`, and score-submission methods, `0x060040b4` and `0x060040c4`. its request class matches all 48 method opcode sequences and the normalized method/36-field signature layouts. that identifies `0x06001a24` as the AddParameter candidate. an independent raw-byte review also matched the 126-byte visual getter and 14-byte dictionary-add routine. these are code correlations, not whole-file identity or VM equivalence. the comparison is retained in `deeper-review/client-correlation/README.md`. the separately acquired native `osu!auth.dll` has two Load/Unload exports, heavily obfuscated entry code and a large high-entropy data section. named imports alone don't settle its behaviour because it can resolve APIs dynamically. the bounded native review established neither a direct patch to this DLL nor a link from it to the intercepted `fs` producer. that review is retained in `deeper-review/auth-native/report.md`. what i still haven't established is the windows packet capture, which fields succeed under real windows permissions, whether the hooks install successfully on a running client, the fresh client's protected submission body, or native-auth runtime behaviour. i also don't have the live service's deployed commit or its downstream decryption, storage, access and retention practices. other branches, historical versions and disclosure outside the reviewed pages are outside these findings. the remote DLL can change without the local loader changing. i traced the reporting path manually, following the calls and checking the decoded instructions against the reconstructed methods. i used offline Python decoders and arithmetic/metadata models to extract and check the underlying byte data. the parsing libraries were dnfile, dncil and pefile. `decode_payload.py`, `decrypt_strings.py` and `static_builder_model.py` are retained with their JSON and annotated CIL outputs. the retained material includes the managed-code inventory, native-boundary notes, decoded strings, call references and reconstructed method bodies. those records support the code trace above. they don't turn this static review into an observed runtime capture. ## about this copy i rewrote the prose on 12 october 2026 so it's easier to read alongside the post. the file hashes, method references and findings still refer to the 11 october review. the unedited source report is retained locally with SHA-256 `cbb8d89386b501d2725a45ca9a49a51b79e392792f7aea206c889e57c0ad8418`. local absolute paths are redacted, and temporary signed download queries are omitted. filenames for supporting local records are retained as references; they are not public download links. for the request-by-request view, see the [network report](/inside-the-akatsuki-patcher/evidence/network-report/).