From a50a18325c38f8f56b6769aac7b1687e8b0b7ab9 Mon Sep 17 00:00:00 2001 From: pancake Date: Thu, 16 Jul 2026 20:37:59 +0200 Subject: [PATCH] Document all resource formats used in Unity --- doc/resources.md | 983 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 983 insertions(+) create mode 100644 doc/resources.md diff --git a/doc/resources.md b/doc/resources.md new file mode 100644 index 0000000..622f722 --- /dev/null +++ b/doc/resources.md @@ -0,0 +1,983 @@ +# Unity Resources, Assets, Sidecars, and Save Data + +This document describes the resource-like files found in Unity Player and +IL2CPP builds, how the formats relate to one another, what radare2 and +r2unity support today, and what remains to be implemented. + +The most important point is that similar-looking names refer to several +unrelated systems: + +```text +Unity content +├── data.unity3d / *.bundle Unity archive (usually UnityFS) +│ ├── resources.assets Unity SerializedFile object database +│ ├── sharedassets*.assets Unity SerializedFile object database +│ ├── *.resS streamed texture and mesh bytes +│ └── *.resource streamed audio and video bytes +│ +Managed/.NET resources +└── Data/Managed/Resources/*.dll-resources.dat + ├── IL2CPP manifest-resource table + └── payloads: XML, icons, binary data, or sometimes *.resources + └── *.resources .NET name/value resource dictionary + +Save data +└── Application.persistentDataPath / PlayerPrefs + └── filenames and formats selected by each game +``` + +Consequently: + +- `.assets`, `.resS`, and `.resource` normally contain read-only build + content, not saved game state. +- `.resources`, `.resource`, `.resS`, and `*-resources.dat` are distinct + formats despite their similar names. +- `.dat` is only a generic suffix. Its meaning must be inferred from its + path, filename, magic, structure, and the code that reads it. +- A literal `.res` file is not the same as Unity's case-sensitive `.resS` + sidecar. + +## 1. Format summary + +| File or suffix | Meaning | Self-contained? | Current support | +| --- | --- | --- | --- | +| `global-metadata.dat` | IL2CPP managed metadata | Yes, but native address correlation needs the matching IL2CPP binary | r2unity | +| `*.dll-resources.dat` | IL2CPP assembly manifest-resource pack | Yes | None | +| `*.resources` | Standard binary .NET resource dictionary | Yes | Outer payload only when embedded in a PE; no entry parser | +| `resources.assets` | Serialized assets originating in project `Resources` directories | Mostly; large payloads can be in sidecars | None | +| `sharedassets*.assets` | Serialized assets shared by scenes | Mostly; large payloads can be in sidecars | None | +| `globalgamemanagers.assets` | Assets referenced by global engine/project settings | Mostly; large payloads can be in sidecars | None | +| `level*`, `CAB-*`, `*.sharedAssets` | Other Unity SerializedFiles | Mostly; may reference sidecars and other SerializedFiles | None | +| `*.resS` | Raw streamed texture and mesh data | No; the owner supplies offsets, sizes, types, and names | None | +| `*.resource` | Raw streamed audio and video data | Usually no; the owner supplies offsets, sizes, types, and names | None | +| `data.unity3d`, `*.bundle` | Unity archive containing SerializedFiles and sidecars | Yes as a container | None | +| Addressables `catalog.bin` / `catalog.json` | Key-to-location and dependency mapping | No; it points at bundles and providers | None | +| Addressables `catalog.hash` | Catalog version/update fingerprint | No | None | +| literal `*.res` | Usually a compiled Windows resource file, or an application-specific file | Yes for Win32 `.res` | Linked PE resources only; no standalone parser | +| arbitrary `*.dat` | Application-specific data, cache, save, or one of the known `.dat` formats above | Unknown | Format-dependent | + +## 2. Unity's build-content graph + +Unity serializes content as a graph rather than as a directory of independent +media files. A typical asset is recovered by following several layers: + +```text +UnityFS archive member + ↓ +SerializedFile object-table entry + ↓ +class/type information and object payload + ↓ +optional stream reference: path + offset + size + ↓ +bytes in a .resS or .resource sidecar +``` + +Object references can also cross SerializedFiles. A scene object can point to +an asset in `sharedassets0.assets`, `resources.assets`, another bundle, or a +built-in resource file. Correct extraction therefore needs a deployment-wide +view, not merely a parser for one extension. + +Unity documents the high-level organization, but does not publish a complete +stable specification for every version of the low-level SerializedFile and +UnityFS encodings. It also warns that build layouts and filenames may change +between versions. An r2unity implementation must use versioned parsers and a +corpus covering old and current Unity releases. + +References: + +- [Unity build content output](https://docs.unity3d.com/ja/current/Manual/build-content-output.html) +- [Unity AssetBundle file format](https://docs.unity3d.com/6000.0/Documentation/Manual/assetbundles-file-format.html) +- [Unity AssetBundle compression](https://docs.unity3d.com/6000.0/Documentation/Manual/assetbundles-compression-format.html) + +## 3. Unity SerializedFiles: `.assets`, `level*`, and `CAB-*` + +### 3.1 Purpose + +A Unity SerializedFile is an object database. Common filenames include: + +- `resources.assets` +- `sharedassets0.assets`, `sharedassets1.assets`, and so on +- `globalgamemanagers` and `globalgamemanagers.assets` +- `level0`, `level1`, and other scene files +- `CAB-` members inside AssetBundles +- `PlayerBuild-` and accompanying shared-asset members +- built-in files such as `unity default resources` and + `unity_builtin_extra` + +The extension is not a reliable detector: several SerializedFiles have no +extension, while unrelated files can use `.assets`. SerializedFiles normally +do not begin with a textual magic string. + +### 3.2 Logical contents + +The versioned header and metadata describe at least: + +- metadata size and total file size +- SerializedFile format version +- object-data offset +- file endianness +- Unity engine version, when present +- target platform +- whether a serialized type tree is present +- serialized type/class definitions +- object records +- script type references +- external SerializedFile references +- reference types and user information in newer versions + +Each object record associates a path ID with a type and a byte range. The +object's stored offset is resolved relative to the file's data area. The +exact width, alignment, and interpretation of fields depend on the +SerializedFile version. + +The type information may contain a type tree describing serialized fields. +When the type tree is stripped, built-in Unity class layouts must be selected +from a versioned database. `MonoBehaviour` and `ScriptableObject` payloads +also require the corresponding managed script schema. For IL2CPP builds, +r2unity can use `global-metadata.dat` to recover script field names and types, +although the final Unity serialization layout still has to obey Unity's own +serialization rules and version-specific alignment. + +### 3.3 `resources.assets` + +`resources.assets` is not a .NET `.resources` file. It is a Unity +SerializedFile containing objects collected from project directories named +`Resources`. Unity includes those objects in the build even when no scene +directly references them, and `Resources.Load` resolves objects from this +content. + +Dependencies pulled in by a resource object may be stored alongside it or in +other shared files. The logical name used by `Resources.Load` is not simply a +filesystem entry in `resources.assets`; the SerializedFile objects and their +container/name metadata must be decoded. + +### 3.4 What should become an `RBinResource` + +Not every serialized Unity object should be presented as an extractable +resource. A useful mapping is: + +- sections/fields: file header, metadata, type trees, object table, and data + area +- symbols: object path IDs and known object names +- classes: Unity class IDs and recovered managed script types +- resources: payload-bearing objects such as `TextAsset`, `Texture2D`, + `Mesh`, `AudioClip`, `VideoClip`, shaders, fonts, and selected serialized + script blobs + +An `RBinResource` for a Unity asset should carry: + +- a logical object or asset name when recoverable +- the Unity class as its type +- the owning SerializedFile as its origin +- the inline or external offset and size +- a stable index and, where useful, the path ID + +Objects with bytes in a sidecar need special handling described in section 4. + +## 4. Streamed sidecars: `.resS` and `.resource` + +### 4.1 `.resS` + +Unity uses `.resS` sidecars primarily for large texture and mesh data. The +capital `S` is significant on case-sensitive filesystems. + +A `.resS` file is generally a byte store, not a self-describing object +database. The owning Texture2D, Mesh, or related object supplies a stream +reference containing some combination of: + +- sidecar or archive path +- byte offset +- byte size + +Without the owning SerializedFile, r2unity can identify the file only as a +raw data sidecar. It normally cannot assign names, types, or reliable record +boundaries. + +### 4.2 `.resource` + +Unity uses `.resource` sidecars for audio and video content. As with `.resS`, +the owning SerializedFile identifies the useful ranges. + +The contents are not guaranteed to be one universal codec. For example, the +Unity 6 iOS build inspected while preparing this document has a +`sharedassets0.resource` beginning with `FSB5`, an FMOD sound-bank signature. +Other builds can contain different audio/video encodings or several streamed +ranges. + +Opening a `.resource` file independently may reveal a recognizable inner +container, but that does not replace the Unity object metadata needed to map +the bytes back to `AudioClip` or `VideoClip` objects. + +### 4.3 Why the current r2 resource API is insufficient for sidecars + +`r_bin_file_get_resource_data()` currently obtains resource bytes by slicing +`resource->paddr .. resource->paddr + resource->size` from the current +`RBinFile` buffer. The `origin` member is descriptive metadata and is not +consulted for extraction. + +That model works for inline objects and `*.dll-resources.dat`, but not when a +resource listed while `resources.assets` is open resides in +`resources.assets.resS`. + +Possible solutions are: + +1. Add an optional `RBinPlugin` callback that returns an `RBuffer` for a + resource, falling back to the current-file slice when absent. +2. Represent sidecars as separate logical `RBinFile` objects and make the + owning object graph point to them. +3. Mount the entire Unity deployment or UnityFS archive as a filesystem and + resolve stream paths through that mount. + +The first option is the smallest generic radare2 enhancement. The second and +third remain useful for navigation and container analysis. + +## 5. UnityFS, `data.unity3d`, and AssetBundles + +### 5.1 Unity archives + +Modern AssetBundles and LZ4-compressed Player content normally use Unity's +archive format and begin with the NUL-terminated signature `UnityFS`. +`data.unity3d` is commonly such an archive. + +The archive header identifies the archive format and Unity/generator +versions, total size, compressed and uncompressed block-information sizes, +and flags. The block-information structure describes compressed content +blocks and directory nodes. Directory nodes name logical files such as: + +- `CAB-` SerializedFiles +- scene SerializedFiles +- `.resS` sidecars +- `.resource` sidecars +- other build products + +The low-level integers in common UnityFS headers are stored in big-endian +form even though most contained SerializedFiles target little-endian +platforms. A parser must treat container and member endianness separately. + +### 5.2 Compression + +Unity supports: + +- full-content LZMA compression +- chunked LZ4/LZ4HC compression +- uncompressed content + +LZ4 bundles divide content into independently decompressible blocks; Unity's +documentation describes 128 KiB chunks. A member can span block boundaries, +so extraction must reconstruct the logical uncompressed address space rather +than treating a directory offset as a raw file offset. + +radare2 already contains LZ4 decompression primitives. Full UnityFS support +also needs LZMA support or an optional portable dependency. + +### 5.3 Correct radare2 abstraction + +Archive members are files, not merely `RBinResource` records. UnityFS is best +implemented as either: + +- an `RBinXtrPlugin` that returns decompressed member buffers, or +- an `RFSPlugin` that mounts and lists archive members + +After extraction, the normal SerializedFile plugin can process `CAB-*`, +`.assets`, and scene members recursively. + +Older Unity releases also used `UnityRaw` and `UnityWeb` bundle variants. +They can be added after UnityFS support and require separate fixtures. + +## 6. IL2CPP `*.dll-resources.dat` + +### 6.1 Purpose and path + +IL2CPP externalizes assembly manifest resources into files under the Player +data directory's `Resources` directory. The generated runtime constructs the +name as: + +```text +/Resources/ + -resources.dat +``` + +Because an assembly image name normally includes `.dll`, common files are: + +```text +mscorlib.dll-resources.dat +System.Data.dll-resources.dat +System.Drawing.dll-resources.dat +``` + +This behavior is implemented by generated libil2cpp code in: + +```text +Il2CppOutputProject/IL2CPP/libil2cpp/icalls/mscorlib/ + System.Reflection/RuntimeAssembly.cpp +``` + +The runtime memory-maps the complete file and returns named resource slices +to managed reflection APIs such as `GetManifestResourceStream`. + +### 6.2 On-disk layout + +The observed and generated-runtime layout is: + +```c +int32_t resourceRecordsSize; +int32_t numberOfResources; + +for (int32_t i = 0; i < numberOfResources; i++) { + uint32_t resourceDataSize; + int32_t resourceNameSize; + uint8_t resourceName[resourceNameSize]; /* not NUL-terminated */ +} + +/* Resource payloads follow consecutively in record order. */ +uint8_t resourceData[]; +``` + +The first payload starts at: + +```text +4 + resourceRecordsSize +``` + +The offset of each later payload is the previous payload offset plus its +declared `resourceDataSize`. + +Current little-endian fixtures therefore decode as: + +```text +offset size meaning +0x00 4 resource-record area size, excluding this field +0x04 4 number of resources +0x08 4 first payload size +0x0c 4 first name size +0x10 ... first name bytes +... following descriptors +4+size ... concatenated payload bytes +``` + +Generated libil2cpp reads integers with native `memcpy` rather than explicit +byte swapping. Existing desktop/mobile fixtures are little-endian; a robust +implementation should use companion target information or reject ambiguous +endianness rather than silently misparsing unusual targets. + +### 6.3 Example payloads + +Fixtures inspected in `test/bins/other/unityapks` contain: + +- `System.Data.dll-resources.dat` + - one `System.Data.SqlClient.SqlMetaData.xml` XML document +- `System.Drawing.dll-resources.dat` + - `Mono.ico` + - `Information.ico` + - `Error.ico` + - `Warning.ico` + - `Question.ico` + - `Shield.ico` +- `mscorlib.dll-resources.dat` + - seven `collation.*.bin` payloads + +Manifest-resource payloads are opaque to the outer pack. A payload can be +XML, an image, an arbitrary binary, or a standard `.resources` dictionary. + +### 6.4 Detection and validation + +This format has no fixed magic. A plugin should combine a filename/path hint +with complete structural validation: + +- require a plausible nonnegative record-area size and resource count +- cap the number of entries and name length +- keep every descriptor inside the declared record area +- reject negative name lengths +- check every addition for integer overflow +- require the payload area to start inside the file +- require every payload to fit inside the remaining bytes +- optionally require descriptor consumption to equal the declared record + area exactly +- reject overlapping or trailing layouts unless a real fixture demonstrates + that Unity permits them + +Filename matching alone must not claim arbitrary `.dat` files. + +### 6.5 `RBinResource` mapping + +The format maps directly onto radare2's resource API: + +```text +RBinResource.name manifest resource name +RBinResource.type detected MIME/format or MANIFESTRESOURCE +RBinResource.encoding raw unless the outer data is actually encoded +RBinResource.origin assembly name or pack filename +RBinResource.paddr payload offset +RBinResource.vaddr payload offset for a non-VA file +RBinResource.size payload size +RBinResource.id UT64_MAX +RBinResource.index descriptor index +RBinResource.named true +``` + +This is the highest-value short-term resource implementation for r2unity: +the layout is small, deterministic, and immediately enables `iU`, `iUj`, and +`iUx`. + +## 7. Standard .NET `.resources` + +### 7.1 Relationship to assemblies and IL2CPP packs + +A `.resources` file is a compiled .NET name/value resource dictionary. It can +be standalone or embedded as one manifest resource in an assembly. Under +IL2CPP, the same manifest resource can become one payload in an assembly's +`*.dll-resources.dat` file. + +The layers must not be conflated: + +```text +System.Example.dll-resources.dat IL2CPP outer pack +└── Company.Product.Strings.resources + ├── MainWindow.Title .NET dictionary entry + ├── Error.Network .NET dictionary entry + └── Logo .NET dictionary entry +``` + +### 7.2 Header and tables + +Standard binary `.resources` files begin with the little-endian magic value: + +```text +0xBEEFCACE +``` + +The ResourceManager header contains: + +1. magic number +2. ResourceManager header version +3. number of bytes to skip past the remaining ResourceManager header +4. length-prefixed resource-reader type name +5. length-prefixed ResourceSet type name + +The ResourceReader data that follows contains versioned information including: + +- resource count +- serialized/user type count and names +- aligned name-hash table +- name-position table +- data-section offset +- name section +- typed value records in the data section + +Resource names are used as dictionary keys. Value records carry a type code +and can represent strings, byte arrays, streams, primitive values, or +user-defined serialized types. A safe reverse-engineering parser should +enumerate and extract raw value data without instantiating arbitrary managed +types. + +References: + +- [Microsoft: create resource files](https://learn.microsoft.com/en-us/dotnet/core/extensions/create-resource-files) +- [Microsoft: ResourceReader](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-resources-resourcereader) +- [Microsoft reference source: ResourceManager header](https://github.com/microsoft/referencesource/blob/master/mscorlib/system/resources/resourcemanager.cs) + +### 7.3 Current radare2 behavior + +radare2's PE parser handles two resource families: + +- native PE resource-directory entries +- embedded CLR `ManifestResource` rows whose implementation field indicates + that the payload resides in the current assembly + +For a managed resource, r2 reads the CLR resource-directory length prefix and +creates one `RBinResource` for the complete manifest payload. It does not +currently inspect the payload for `0xBEEFCACE` or enumerate the inner +`.resources` dictionary. + +A standalone `.resources` bin plugin belongs in radare2 rather than r2unity +because the format is generic .NET. It should also be usable recursively on +payloads obtained from PE or IL2CPP resource containers. + +## 8. Win32 `.res` + +A literal `.res` file usually means the binary output of the Windows resource +compiler. It is unrelated to Unity `.resS` and .NET `.resources`. + +A compiled `.res` file contains concatenated, DWORD-aligned entries. Each +entry declares its header and data sizes and describes: + +- resource type, either numeric or named +- resource name/identifier +- data version +- memory flags +- language +- resource version +- characteristics +- type-specific payload + +The linker transforms these entries into the resource directory embedded in +a PE executable or DLL. radare2 already enumerates the linked PE resource +tree, but no standalone `.res` parser was found in the current source. + +Reference: + +- [Microsoft: Resource File Formats](https://learn.microsoft.com/en-us/windows/win32/menurc/resource-file-formats) + +If a game ships a file with a lowercase `.res` suffix on a non-Windows +platform, the suffix alone is not enough to identify it. Inspect its magic, +path, and reader code before treating it as Win32 data. + +## 9. `.dat` files + +`.dat` has no common file format. Several unrelated files in one Unity build +can use it. + +### 9.1 `global-metadata.dat` + +`global-metadata.dat` is IL2CPP's managed metadata database. Its sanity value +is `0xFAB11BAF` and the next word identifies the metadata wire version. + +It stores such information as: + +- assemblies and images +- types, methods, fields, properties, and events +- generic metadata +- custom attributes and default-value blobs +- identifier strings +- managed `ldstr` literal data + +It does not contain Unity textures, scenes, prefabs, audio, or AssetBundle +object databases. See `doc/datvsbin.md` and `doc/strings.md` for the detailed +r2unity coverage. + +### 9.2 `*.dll-resources.dat` + +These files use the IL2CPP manifest-resource pack described in section 6. +They are identified by their path, filename, and validated record layout, not +by the generic `.dat` suffix. + +### 9.3 Catalogs, caches, and application files + +Packages or games can assign `.dat` to custom catalogs, caches, databases, +network content, or obfuscated/encrypted files. The only reliable definition +is the producing/consuming code. + +### 9.4 Saved games + +Unity does not define a universal binary save format. A developer can write +any bytes under `Application.persistentDataPath`, including: + +- JSON or XML +- a custom BinaryReader/BinaryWriter layout +- SQLite +- protocol buffers or MessagePack +- compressed data +- encrypted or authenticated data +- a mixture of versioned headers and application-specific records + +The `.dat` extension does not distinguish any of these. + +## 10. Saved state, PlayerPrefs, and StreamingAssets + +### 10.1 `Application.persistentDataPath` + +Unity exposes `Application.persistentDataPath` as a per-application directory +for data retained between runs. Platform paths differ, but filenames and +formats inside the directory are chosen by the game. + +Reference: + +- [Unity: Application.persistentDataPath](https://docs.unity3d.com/kr/current/ScriptReference/Application-persistentDataPath.html) + +An r2unity save-analysis workflow should therefore: + +1. identify the exact file path and platform +2. inspect magic, entropy, strings, and compression +3. search IL2CPP metadata for likely save/model/serializer types +4. locate file and serialization calls in the native IL2CPP binary +5. reconstruct a schema specific to that game and version + +r2unity can provide triage and schema-recovery assistance, but cannot provide +one parser for every `save.dat`. + +### 10.2 PlayerPrefs + +`PlayerPrefs` stores string, integer, and floating-point preferences through +platform-native storage. Current Unity documentation lists, among others: + +- Android SharedPreferences XML +- iOS `NSUserDefaults` +- macOS plist files +- the Windows registry +- Linux configuration paths +- UWP `playerprefs.dat` + +PlayerPrefs is not encrypted and should not be confused with arbitrary files +under `persistentDataPath`. + +Reference: + +- [Unity: PlayerPrefs](https://docs.unity3d.com/ja/current/ScriptReference/PlayerPrefs.html) + +### 10.3 StreamingAssets + +Files placed in a project's `StreamingAssets` directory are generally copied +into the build as files rather than converted into `resources.assets`. +Applications commonly use this area for databases, media, configuration, or +custom packs. These are shipped inputs and are often read-only on installed +platforms; writable state belongs under `persistentDataPath`. + +A `.dat` found in StreamingAssets is therefore likely application-supplied +content, not automatically a saved game. + +## 11. Addressables + +Addressables add a lookup layer above AssetBundles. A content catalog maps +keys and labels to physical locations, provider information, dependencies, +and bundle entries. + +Common artifacts include: + +- `catalog.json` or a binary/compressed local catalog +- `catalog.bin` in some package/build configurations +- `catalog.hash` +- platform directories containing `*.bundle` + +The hash determines whether a remote catalog differs from the local catalog. +The catalog does not replace AssetBundle or SerializedFile parsing; it tells +the loader which bundle and provider contain a requested address. + +Reference: + +- [Unity Addressables content catalogs](https://docs.unity3d.com/Packages/com.unity.addressables%402.7/manual/build-content-catalogs.html) + +A complete r2unity implementation should parse catalogs only after UnityFS +and SerializedFile support exists, then use them to build a deployment graph: + +```text +address / label + → catalog location + → bundle and dependencies + → UnityFS member + → SerializedFile object + → optional .resS/.resource slice +``` + +## 12. Quick identification guide + +| Leading bytes or evidence | Likely format | +| --- | --- | +| `UnityFS\0` | Modern Unity archive / AssetBundle | +| sanity `0xFAB11BAF` (`af 1b b1 fa` on little-endian disk) plus plausible version | `global-metadata.dat` | +| `0xBEEFCACE` (`ce ca ef be` on disk) | Standard .NET `.resources` | +| `FSB5` | FMOD sound bank, sometimes inside a Unity `.resource` sidecar | +| valid IL2CPP record area plus `*.dll-resources.dat` path/name | IL2CPP manifest-resource pack | +| plausible versioned header and Unity version string, but no textual magic | Unity SerializedFile | +| `PK\x03\x04`, gzip, SQLite, JSON, XML, etc. | Inner/common format; especially relevant for custom saves and StreamingAssets | +| no magic and high entropy | Possibly compressed, encrypted, packed, or simply raw media data | + +Identification must always validate lengths and offsets. Magic matching alone +is insufficient for hostile or corrupted input. + +## 13. radare2 resource API + +### 13.1 Data model + +Current radare2 exposes binary resources with `RBinResource`, whose useful +members include: + +- `name` +- `type` +- `encoding` +- `language` +- `timestamp` +- `origin` +- `vaddr`, `paddr`, and `size` +- numeric and textual identifiers +- resource index, type ID, language ID, and code page + +A bin plugin implements `load_resources(RBinFile *bf)` and appends entries to +`bf->bo->resources_vec`. Loading is lazy and cached. The generic extractor +validates that an inline range fits within the current file. + +Supported generic `encoding` transformations currently include raw data, +base64, data URIs, gzip/zlib, LZ4, and UTF-16 variants. `encoding` describes +the bytes of one resource; it must not be used as a substitute for mounting a +compressed container such as UnityFS. + +### 13.2 Commands + +radare2 commands are: + +```text +iU list resources and metadata +iU, table output/query +iU* emit resource flags as r2 commands +iUj JSON output +iUq address, size, type, and name +iUqq names only +iUx [directory] extract resources +ixU [directory] extraction alias +``` + +`rabin2` provides: + +```text +rabin2 -U file +rabin2 -jU file +rabin2 -xU [-o directory] file +``` + +Extraction sanitizes generated filenames and refuses to overwrite existing +files. + +### 13.3 r2hermes precedent + +The r2hermes HBC plugin demonstrates how a non-PE plugin can use the API. It +examines HBC string data for complete SVG, HTML, XML, PEM, and data-URI +payloads, then records the underlying byte range as an `RBinResource` with a +MIME type and origin. + +The same conservative classifier can be reused for complete documents or +data URIs in r2unity's managed `ldstr` literals. Ordinary strings should stay +in the string API and must not all become resources. + +## 14. Current r2unity and radare2 coverage + +### 14.1 r2unity today + +The current `bin_r2unity` plugin recognizes only the IL2CPP +`global-metadata.dat` sanity value and version. It exposes metadata sections, +strings, symbols, imports, classes, fields, and header information. It has no +`load_resources` callback. + +Companion discovery currently locates: + +- the main executable/input +- the IL2CPP native binary +- `global-metadata.dat` +- the Player Data directory + +It does not enumerate `Managed/Resources`, loose SerializedFiles, UnityFS +archives, sidecars, or Addressables content. + +The existing documentation correctly states that Unity asset, scene, prefab, +and AssetBundle data is not stored in `global-metadata.dat`. + +### 14.2 radare2 today + +radare2 provides the generic resource API and commands, and currently exposes: + +- native PE resource-directory entries +- embedded CLR ManifestResource payloads +- resources from plugins that implement `load_resources`, such as the + Pebble resource-pack plugin and r2hermes when installed + +No current parser was found for: + +- UnityFS +- Unity SerializedFiles +- Unity `.resS` or `.resource` sidecars +- IL2CPP `*.dll-resources.dat` +- nested/standalone .NET `.resources` entries +- standalone Windows `.res` + +Running the current `rabin2 -U` on the available +`mscorlib.dll-resources.dat` and `resources.assets` fixtures produces no +resource entries, confirming that the gap is parser implementation rather +than command wiring. + +## 15. Implementation roadmap + +### Stage 0: fixtures and format boundaries + +- preserve small valid fixtures for each format and version +- include malformed/truncated cases +- document which fixtures are distributable +- distinguish container, object database, sidecar, and inner payload APIs +- add file-identification tests before enabling weak/no-magic detection + +### Stage 1: IL2CPP manifest-resource packs in r2unity + +Add a dedicated library parser and bin plugin for +`*.dll-resources.dat`: + +- parse and validate descriptors +- expose records through `RBinResource` +- detect common payload types from extension and magic +- set the assembly/pack as `origin` +- support `iU`, JSON, flags, and safe extraction without new commands +- add unit and integration tests for the System.Data, System.Drawing, and + mscorlib fixtures + +This should be a separate plugin from `bin_r2unity` because the format has a +different detector and object lifetime from `global-metadata.dat`. + +### Stage 2: generic .NET `.resources` in radare2 + +Add a standalone parser that: + +- validates `0xBEEFCACE` and all table offsets +- enumerates dictionary keys and value records +- identifies built-in primitive, string, byte-array, and stream types +- exposes raw values safely without deserializing arbitrary objects +- works when opened directly +- can be invoked recursively on a PE or IL2CPP manifest-resource payload + +This belongs in radare2 because it benefits every managed binary format. + +### Stage 3: conservative resources from metadata literals + +Optionally add `load_resources` to the existing `bin_r2unity` plugin and +reuse the r2hermes classifier for: + +- complete XML documents +- SVG +- HTML +- PEM blocks +- data URIs + +This is heuristic convenience, not Unity asset parsing. All ordinary `ldstr` +literals remain strings. + +### Stage 4: Unity SerializedFile library and bin plugin + +Implement versioned parsing for: + +- headers and endianness +- metadata and type trees +- serialized types and class IDs +- object table and path IDs +- script types +- external references +- reference types and newer metadata additions +- object names and common built-in object layouts + +Expose structure through sections, fields, symbols, classes, and resources. +Start with loose current-version `.assets` fixtures, then expand across Unity +versions. + +### Stage 5: external sidecar resource access in radare2 + +Add a plugin-level resource-data resolver or equivalent abstraction so that a +resource owned by one `RBinFile` can return bytes from a companion buffer. + +Requirements include: + +- safe path resolution relative to the deployment/archive +- retaining or reopening companion buffers safely +- offset/size bounds checks against the sidecar +- showing the real origin in `iU`/JSON +- extraction without changing the current seek/file + +### Stage 6: `.resS` and `.resource` resolution in r2unity + +Decode streaming references for common classes and map them to sidecars: + +- Texture2D +- Mesh +- AudioClip +- VideoClip +- other version-specific streamed data objects + +Initially expose raw slices. Codec-specific plugins such as FSB5 can be added +independently or invoked on extracted buffers. + +### Stage 7: UnityFS extraction or mounting + +Implement: + +- `UnityFS` validation +- block-info placement and flags +- uncompressed blocks +- LZ4/LZ4HC blocks +- LZMA content +- directory nodes +- members spanning block boundaries +- recursive opening of SerializedFiles + +An extractor and a filesystem view are both useful, but one shared archive +library should implement parsing and decompression. + +### Stage 8: AssetBundle and Addressables semantics + +- decode AssetBundle container objects +- map asset names to path IDs +- follow external SerializedFile and bundle dependencies +- parse JSON and binary/compressed catalogs +- map addresses and labels to bundle members and Unity objects +- expose a cross-file object/resource graph + +### Stage 9: standalone Win32 `.res` in radare2 + +Reuse PE resource types and naming where possible, but parse the compiler's +concatenated `.res` representation directly. This is generic Windows support, +not Unity-specific work. + +### Stage 10: save-data triage + +Add discovery and analysis helpers rather than claiming a universal save +format: + +- identify PlayerPrefs and persistent-data locations +- recognize common inner formats and compression +- find likely save and serializer types in IL2CPP metadata +- correlate file I/O and serializer calls with native methods +- let per-game plugins/scripts describe recovered schemas + +## 16. Parser safety requirements + +All these formats are attacker-controlled inputs from radare2's perspective. +Every implementation should: + +- perform checked arithmetic before adding offsets and sizes +- cap counts, strings, type-tree depth, and decompressed sizes +- validate every range before reading or slicing +- reject overlapping or contradictory regions unless specified +- avoid native object deserialization for `.resources` and saves +- protect recursive container traversal from cycles +- protect dependency traversal from path escape +- treat compressed-size and uncompressed-size fields as untrusted +- include truncated and oversized fuzz cases +- avoid detector false positives for no-magic formats + +UnityFS, SerializedFile, and `.resources` parsers should be fuzzed separately +from higher-level object decoders so that structural bugs are isolated from +class-schema bugs. + +## 17. Recommended ownership split + +The clean project boundary is: + +### r2unity + +- `global-metadata.dat` +- `*.dll-resources.dat` +- Unity SerializedFile +- `.resS` / `.resource` stream resolution +- UnityFS and AssetBundle semantics +- Addressables lookup +- IL2CPP-assisted save-schema recovery + +### radare2 + +- generic `RBinResource` commands and extraction +- plugin-supplied/external resource-buffer support +- standalone and nested .NET `.resources` +- standalone Win32 `.res` +- reusable LZ4/LZMA primitives and container infrastructure + +Unity-specific parsers can remain out-of-tree in r2unity while maturing. Only +generic APIs and formats shared with non-Unity binaries need to enter radare2 +core. + +## 18. Practical first milestone + +The smallest useful milestone is: + +1. add `bin_r2unity_resources` for `*.dll-resources.dat` +2. populate `RBinResource` records with name, origin, offset, size, and type +3. test `iU`, `iUj`, and `iUx` on the existing resource-pack fixtures +4. add the optional document/data-URI classifier to managed literals +5. implement generic `.resources` parsing in radare2 next + +This produces immediate resource visibility without pretending that raw +`.resS`, arbitrary `.dat` saves, or full Unity object serialization are the +same problem. SerializedFile and UnityFS support can then grow on top of a +clear container/resource abstraction.