Skip to content

borgox/osu-collector-reverse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

osu!collector Reverse Engineering Notes

This write-up documents how I unpacked, inspected, and patched the osu!collector desktop application (Electron-based). It is intentionally more formal, adds technical depth, and includes short explanations for readers who are newer to reverse engineering. No redistributed binaries or proprietary assets are included. Educational use only.

I could not test inside a VM because Hyper-V/VirtualBox are currently unavailable on my system; all testing was on bare-metal Windows.

Quick Orientation

  • Goal: understand and neutralize auth/integrity checks in the Electron app bundle while learning the internals.
  • Scope: reverse engineering only; no distribution of paid content or patched binaries.
  • Audience: reverse-engineering hobbyists; newcomers get inline notes defining tools and concepts.

What osu!collector Is

osu!collector hosts user-curated beatmap collections. The desktop app is a subscription download ($1.99/mo at the time of writing) for Windows, macOS, and Linux. I analyzed the Windows build because the platform tooling (x64dbg, PE layout) is familiar and the integrity enforcement is representative of typical Electron apps.

Tools and Environment

  • Node.js + asar CLI for unpacking/packing Electron archives.
  • VS Code for navigation and deobfuscation aids (search, formatting).
  • x64dbg for static and live patching of the PE executable.
  • Basic beautifier for minified JS (any reputable online tool works).

Quick Primer (for newer readers)

  • Electron: a framework that packages a Chromium runtime plus a Node.js process; app code is typically shipped inside an ASAR archive.
  • ASAR: an archive format used by Electron. It is not compressed or encrypted; think of it as a tar-like bundle with a header + file table.
  • Integrity check: Chromium verifies the ASAR hash to ensure files were not modified. If the computed hash differs from the manifest hash, the app aborts.
  • x64dbg: a Windows debugger/disassembler that allows breakpoints, memory inspection, and binary patching. Useful for bypassing integrity checks at the native layer.

Locating and Unpacking the Bundle

Installation path observed: %USERPROFILE%/AppData/Local/osu_collector_desktop/app-2.1.0/resources/app.asar.

Steps to unpack:

  1. Install the ASAR tool: npm install -g asar.
  2. Extract: asar extract app.asar app_extracted (input is the archive, output is the target folder).
  3. Beautify key JS files for readability. Minified Electron bundles often retain symbol-like names; formatting reduces friction when tracing call graphs.

Directory Layout (post-extract)

package.json
.vite/
    build/
        icons/
        main.js          <-- main process logic
        preload.js       <-- runs before renderer loads
    renderer/main_window/
        assets/
            index-BWxB8AvL.js
            index-cA78DHNn.css
        icons/
        index.html

Working notes on early string/function findings are in notes.txt.

Authentication Flow and Target Function

While scanning main.js, I traced usage of a JWT-like token accessor (oo()). The function Nr was the primary consumer. The obfuscation (short symbols, inlined helpers) made direct edits error-prone, so I isolated the function and compared it against a patched variant.

Key idea: short-circuit the auth-dependent logic so the renderer could proceed without a valid token. For newer readers: a JWT (JSON Web Token) is a signed JSON blob; if a client bypasses verification or skips attaching it, server-side checks should still block access, but local gating can sometimes be removed.

Repacking After JS Edits

  1. Ensure package.json remains at the archive root; Electron expects it there.
  2. Pack: asar pack app_extracted app.asar.
  3. Replace the original app.asar with the repacked one in resources/.

At this point the app failed to launch, which led to two distinct issues.

Issue 1: Typos from Obfuscation

Because symbol names were short and similar, I introduced typos while rewriting Nr. The fix was simply to reformat the file, double-check identifiers, and align the patched code with the beautified original. Lesson: when dealing with heavily minified JS, avoid manual retyping; copy + adjust minimal deltas.

Issue 2: ASAR Integrity Check Failure

The launch crash showed this log in %APPDATA%/osu!Collector App/logs/main.log:

[1768:1229/172006.552:FATAL:asar_util.cc(144)] Integrity check failed for asar archive (1787b267216be9c38c2e9b4e0bad07bcf736141f7beb409dc981e35e2108cfeb vs aebc6741d0f353da87756011b80bc72177613b56fa411e73897626195df936f0)

Meaning: the computed hash of app.asar differed from the expected hash baked into the executable. To proceed, I needed to bypass the native integrity check instead of only changing JS.

Native Patching (Bypassing Integrity)

Workflow:

  1. Open %USERPROFILE%/AppData/Local/osu_collector_desktop/app-2.1.0/osu!Collector App.exe in x64dbg.
  2. Search for the string "Integrity check failed for asar archive"; set a breakpoint nearby. Strings often sit close to the code that triggers them.
  3. Inspect the surrounding basic block. In my dump (see example.asm), two conditional jumps gated success:
    00007FF7AF039395 | cmp r8,rcx           ; compare expected vs actual hash pointers/values
    00007FF7AF039398 | 0F85 8A000000 | jne ...39428  ; jump on mismatch -> fail
    
    00007FF7AF0393C1 | test eax,eax         ; check return/status
    00007FF7AF0393C3 | 75 63           | jne ...39428  ; jump on error -> fail
  4. Patch both conditional jumps to NOPs (0x90) to force the success path:
    00007FF7AF039398 | nop
    00007FF7AF039399 | nop
    00007FF7AF03939A | nop
    00007FF7AF03939B | nop
    00007FF7AF03939C | nop
    00007FF7AF03939D | nop
    
    00007FF7AF0393C3 | nop
    00007FF7AF0393C4 | nop
  5. Save the patched binary as patched.exe, then rename the original to osu!Collector App.exe.bak and rename patched.exe back to the original name.

Why this works: removing the conditional branches prevents the failure paths from executing, so the app no longer aborts on hash mismatch. This is a classic integrity-bypass pattern in Electron/Chromium executables.

Renderer Observation: Dynamic Content

Even after patching, the visible UI was not coming from the unpacked HTML files. In main.js I observed the renderer loading a remote URL at runtime:

J.loadURL("https://osucollector.com/electron"), J.show(), J.isMinimized() && J.restore(), J.focus(), J

Translation for newer readers: the Electron main process opens a BrowserWindow and immediately points it at a remote web app, not the local bundled HTML. That means modifying the local index.html does not change the UI. The useful hook instead is preload.js, which runs before the remote page loads and can inject JavaScript into the renderer context.

Simple Preload Injection

Appending to preload.js:

window.addEventListener("DOMContentLoaded", () => {
    alert("Patched by github.com/borgox");
    console.log("Patched by github.com/borgox");
});

This demonstrates the injection point and confirms that code executes before the remote content renders. For deeper modifications, preload can expose safe APIs or hook network requests before the page loads.

Lessons and Cautions

  • Integrity matters: Electron apps often ship with ASAR hashing; changing JS alone may crash the app until the native check is bypassed.
  • Keep diffs minimal: when dealing with obfuscated JS, modify as little as possible and verify with a beautified reference to avoid typos.
  • Remote-rendered apps: if the main window loads a remote URL, local HTML edits will not affect the UI. Use preload scripts or network interception instead.
  • Legal/ethical: bypassing subscriptions or integrity checks can violate terms of service or law in your jurisdiction. These notes are for educational, non-commercial analysis only.

Change Log

  • Documented unpacking and repacking steps.
  • Added integrity-bypass details with opcode rationale.
  • Clarified renderer vs. preload behavior and provided a minimal preload injection example.

Disclaimer

If you own osu!collector: this document does not redistribute your binaries or reveal confidential secrets. It summarizes reverse-engineering steps for educational purposes.

About

A blog post on how i reverse engineered the osu!collector app

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors