Author SHA1 Message Date
pancake c7d47be528 Fix ELF fast parser bounds checks 2026-10-04 23:20:14 +02:00
dependabot[bot] 67471a35d3 Bump softprops/action-gh-release from 3.0.0 to 3.0.3 2026-10-04 23:18:56 +02:00
dependabot[bot] 12620d1557 Bump actions/setup-python from 6 to 7 2026-10-04 23:18:41 +02:00
dependabot[bot] 06a21356e7 Bump actions/checkout from 6 to 7 2026-10-04 23:18:26 +02:00
pancake 26d3db86c3 Use sdb_num_setf instead char[]+snprintf+sdb_num_set 2026-09-30 12:09:51 +02:00
pancake 6681f1af93 Pin r2v to 6.2.2 2026-09-30 11:54:43 +02:00
pancake 08876f2aa3 Binary dists optimized for r2pm -b 2026-09-30 11:02:37 +02:00
pancake 60267a76e8 Release 1.2.6 2026-09-07 14:17:29 +02:00
pancake 57cda9236e Add typed IL2CPP method metadata 2026-08-25 16:13:22 +02:00
pancake 80701514cd Add support for Windows builds in the ci 2026-08-08 12:51:04 +02:00
pancake 6afe8d65d2 Release 1.2.4 2026-08-08 12:34:00 +02:00
pancake 45dc3b8243 Simpler fix #7 for the nongnu echo - thanks @stygian-ash 2026-08-08 12:34:00 +02:00
pancake 57da6163f6 Fix build for ABIVERSION >= 125 (r2-6.2.0) - thanks @stygian-ash 2026-08-08 12:33:51 +02:00
pancake 1c7b79308a Use r2's gha v3 2026-07-17 16:47:43 +02:00
pancake 2d2299c1c7 Add support for sidercar .resS dumpers 2026-07-17 00:37:31 +02:00
pancake b468bcd592 Add support for resource packs 2026-07-16 21:49:29 +02:00
pancake 474d01e9e5 Add support fr BGDatabases resource files 2026-07-16 21:25:31 +02:00
pancake c9a64a8635 Initial suppot for SerializedFile v22 container 2026-07-16 21:02:30 +02:00
pancake a50a18325c Document all resource formats used in Unity 2026-07-16 20:37:59 +02:00
pancake 540e6ffb16 Release 1.2.2 2026-07-06 19:35:51 +02:00
pancake 2b0320c64f Fix reverse wrapper validation and ELF cleanup 2026-06-26 09:24:45 +02:00
pancake 51701f2fb9 Add r2flags to the native CodeRegistration tables 2026-06-25 02:08:59 +02:00
pancake 5bac14c42e More informative in the native_tables in json 2026-06-25 01:46:49 +02:00
pancake 7420757a62 Improve fuzzy codegenModule symbol resolution 2026-06-25 01:11:04 +02:00
pancake 71f2189d84 Cleanup the CodeRegistration symbol resolution logic 2026-06-25 00:44:51 +02:00
24 changed files with 4887 additions and 348 deletions

No files matched your search

+137 -11
View File
@@ -1,5 +1,8 @@
name: CI
env:
R2V: 6.2.2
on:
push:
branches:
@@ -25,11 +28,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Build
run: make
@@ -46,11 +51,13 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Install deps
run: pip install meson ninja --break-system-packages
@@ -69,11 +76,11 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Install radare2 from git
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@v3
with:
from-git: true
@@ -89,7 +96,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -98,7 +105,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Build Debian package
run: make -C dist/debian SUDO=
@@ -110,6 +119,107 @@ jobs:
if-no-files-found: error
archive: false
windows:
name: Build Windows package
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v7
with:
python-version: 3.12.x
- name: Setup MSVC
uses: TheMrMilchmann/setup-msvc-dev@v4
with:
arch: x64
- name: Install build dependencies
run: python -m pip install meson ninja
- name: Install radare2
id: r2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Configure
run: meson setup build-windows --buildtype=release -Dplugins=enabled
- name: Build
run: meson compile -C build-windows
- name: Test
run: meson test -C build-windows --print-errorlogs
- name: Package Windows build
id: package
shell: pwsh
run: |
$project = meson introspect build-windows --projectinfo | ConvertFrom-Json
$package = "r2unity-$($project.version)-w64"
$files = @(
"build-windows\r2unity.exe",
"build-windows\core_r2unity.dll",
"build-windows\bin_r2unity.dll"
)
New-Item -ItemType Directory -Path $package | Out-Null
foreach ($file in $files) {
if (-not (Test-Path $file)) {
throw "Missing Windows build output: $file"
}
Copy-Item $file -Destination $package
}
$archive = "$package.zip"
Compress-Archive -Path "$package\*" -DestinationPath $archive -Force
"archive=$archive" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Package r2pm binary
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path bindist\plugins,bindist\bin | Out-Null
Copy-Item build-windows\core_r2unity.dll,build-windows\bin_r2unity.dll bindist\plugins
Copy-Item build-windows\r2unity.exe bindist\bin
Compress-Archive -Path bindist\* -DestinationPath "r2unity-$env:R2V-windows-x86-64.zip" -Force
- name: Upload Windows package artifact
uses: actions/upload-artifact@v7
with:
name: windows-package
path: |
${{ steps.package.outputs.archive }}
r2unity-*-windows-x86-64.zip
if-no-files-found: error
bindist:
name: Build r2pm binary (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install radare2
uses: radareorg/github-actions@v3
with:
version: ${{ env.R2V }}
- name: Package r2pm binary
run: make bindist
- name: Upload r2pm binary artifact
uses: actions/upload-artifact@v7
with:
name: bindist-${{ matrix.os }}
path: r2unity-*.zip
if-no-files-found: error
release:
name: Publish release
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
@@ -117,27 +227,42 @@ jobs:
- build
- meson
- debian
- windows
- bindist
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Download artifacts
- name: Download Debian package artifact
uses: actions/download-artifact@v8
with:
pattern: '*.deb'
path: dist/artifacts
merge-multiple: true
- name: Download Windows package artifact
uses: actions/download-artifact@v8
with:
name: windows-package
path: dist/artifacts
- name: Download r2pm binary artifacts
uses: actions/download-artifact@v8
with:
pattern: bindist-*
path: dist/artifacts
merge-multiple: true
- name: Generate release notes
run: ./release.sh notes > release-notes.md
- name: Create GitHub release
uses: softprops/[email protected].0
uses: softprops/[email protected].3
with:
name: ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
@@ -147,3 +272,4 @@ jobs:
fail_on_unmatched_files: true
files: |
dist/artifacts/*.deb
dist/artifacts/*.zip
+3
View File
@@ -22,3 +22,6 @@
# Third-party reference checkouts (radare2, il2cpp dumper, etc.)
/third_party/
# r2pm binary packages
/r2unity-*.zip
+15 -4
View File
@@ -30,7 +30,10 @@ BIN_PLUGIN_LDFLAGS = $(shell pkg-config --libs r_bin 2>/dev/null || echo "")
R2_USER_PLUGINS = $(shell r2 -H R2_USER_PLUGINS 2>/dev/null)
LIB_SRCS = $(wildcard src/lib/*.c) $(wildcard src/lib/bin/*.c)
BGDATABASE_LIB_SRCS = src/lib/bgdatabase.c
RESOURCE_PACK_LIB_SRCS = src/lib/resource_pack.c
BIN_ONLY_LIB_SRCS = $(BGDATABASE_LIB_SRCS) $(RESOURCE_PACK_LIB_SRCS)
LIB_SRCS = $(filter-out $(BIN_ONLY_LIB_SRCS),$(wildcard src/lib/*.c)) $(wildcard src/lib/bin/*.c)
LIB_OBJS = $(LIB_SRCS:.c=.o)
LEGACY_OBJS = src/lib/elf.o src/lib/macho.o src/lib/pe.o src/lib/native.o
CLI_SRCS = src/main.c
@@ -63,11 +66,19 @@ user-install: install-plugin
user-uninstall: uninstall-plugin
rm -f "$(R2PM_BINDIR)/$(EXEC)"
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
BINDIST_ZIP = r2unity-$(shell r2 -qv)-$(shell r2pm -H R2PM_OS)-$(shell r2pm -H R2PM_ARCH)-$(shell r2pm -H R2PM_BITS).zip
bindist: $(EXEC) $(PLUGINS)
rm -rf build/bindist $(BINDIST_ZIP)
$(MAKE) user-install R2_USER_PLUGINS=$(CURDIR)/build/bindist/plugins R2PM_BINDIR=$(CURDIR)/build/bindist/bin
cd build/bindist && zip -r $(CURDIR)/$(BINDIST_ZIP) plugins bin
$(EXEC): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
$(CONFIG_H): meson.build
echo '#define R2UNITY_VERSION "$(PROJECT_VERSION)"\n' > $@
echo '#define R2UNITY_VERSION "$(PROJECT_VERSION)"' > $@
$(CLI_OBJS): %.o: %.c $(CONFIG_H)
$(CC) $(CFLAGS) -c -o $@ $<
@@ -75,7 +86,7 @@ $(CLI_OBJS): %.o: %.c $(CONFIG_H)
$(CORE_PLUGIN): src/r2/core_r2unity.c $(LIB_SRCS)
$(CC) $(CORE_PLUGIN_CFLAGS) $(PLUGIN_SHFLAGS) -o $@ $^ $(CORE_PLUGIN_LDFLAGS)
$(BIN_PLUGIN): src/r2/bin_r2unity.c $(LIB_SRCS)
$(BIN_PLUGIN): src/r2/bin_r2unity.c $(LIB_SRCS) $(BIN_ONLY_LIB_SRCS)
$(CC) $(BIN_PLUGIN_CFLAGS) $(PLUGIN_SHFLAGS) -o $@ $^ $(BIN_PLUGIN_LDFLAGS)
%.o: %.c
@@ -85,4 +96,4 @@ clean:
rm -f $(EXEC) $(OBJS) $(LEGACY_OBJS) $(PLUGINS) $(CONFIG_H)
.PHONY: all clean plugin install-plugin uninstall-plugin fmt
.PHONY: all clean plugin install-plugin uninstall-plugin fmt bindist
+52 -2
View File
@@ -22,6 +22,14 @@ IL2CPP binary, and exposes managed metadata for reverse engineering.
SBOMs for managed assemblies.
- Provides both a core r2 command plugin and an `r_bin` plugin for direct
`global-metadata.dat` inspection.
- Parses IL2CPP `*.dll-resources.dat` packs and exposes named payloads through
radare2's resource listing and extraction APIs.
- Recognizes Unity SerializedFile v22 asset databases and exposes their
headers, object ranges, names, symbols, classes, dependencies, and resources.
- Resolves bounded `Texture2D`/`Mesh` `.resS` and `AudioClip`/`VideoClip`
`.resource` ranges beside loose SerializedFiles for `iU`/`iUx` extraction.
- Recognizes BGDatabase v6 repositories and whole-database saves, including
addon payloads, table regions, fields, symbols, and validated UTF-8 values.
## Build
@@ -48,7 +56,7 @@ r2pm -ci r2unity
```
`make` builds the CLI. `make plugin` builds `core_r2unity` and `bin_r2unity`.
`make user-install` installs both the CLI and plugins for the current user.
`make user-install` installs the CLI and plugins for the current user.
Meson builds the CLI by default, matching `make`. Use `-Dplugins=enabled` to
build the radare2 plugins too, or `-Dr2_plugindir=/path/to/plugins` to override
the plugin install directory.
@@ -99,7 +107,39 @@ r2unity-Sj emit managed-assembly CycloneDX JSON
Set `r2unity.metadata` and `r2unity.library` manually when auto-detection is not
enough. The `bin_r2unity` plugin also lets radare2/rabin2 treat
`global-metadata.dat` as a binary format, exposing sections, strings, symbols,
classes, imports, libraries, and header fields.
classes, imports, libraries, and header fields. It also recognizes loose Unity
SerializedFile v22 inputs such as `sharedassets*.assets`:
```sh
rabin2 -I sharedassets10.assets
rabin2 -S sharedassets10.assets
rabin2 -s sharedassets10.assets
rabin2 -U sharedassets10.assets
rabin2 -xU sharedassets10.assets
```
Streamed resources are extracted when the referenced `.resS` or `.resource`
file is beside its owning SerializedFile. Missing sidecars remain visible in
`-U` output, but extraction fails safely instead of reading the same offset
from the `.assets` file.
BGDatabase repositories and saves produced by `BGRepo.I.Save()` are handled by
the same `r2unity` bin plugin:
```sh
rabin2 -I SaveFile.dat
rabin2 -S SaveFile.dat
rabin2 -s SaveFile.dat
rabin2 -z SaveFile.dat
```
IL2CPP manifest-resource packs use the same bin plugin:
```sh
rabin2 -U System.Drawing.dll-resources.dat
rabin2 -jU System.Drawing.dll-resources.dat
rabin2 -xU -o extracted System.Drawing.dll-resources.dat
```
## Current Limits
@@ -110,5 +150,15 @@ classes, imports, libraries, and header fields.
recover native wrapper addresses or every `DllImportAttribute` detail.
- SBOM output covers managed assemblies only, not native dependencies or file
hashes.
- SerializedFile support currently targets format v22. Object naming and
payload decoding cover the common built-in classes needed by the reference
asset; arbitrary stripped type trees and managed script schemas remain to be
implemented.
- Loose sibling `.resS` and `.resource` extraction is supported for the known
v22 stream layouts. Archive-member paths and other version-specific object
layouts still require UnityFS and additional class fixtures.
- BGDatabase support currently targets the structurally validated v6 layout.
It exposes unknown proprietary field payloads as table sections until more
field types and format versions are recovered from additional fixtures.
Deep technical notes live in `doc/`.
+8 -4
View File
@@ -355,10 +355,14 @@ int32_t customAttributeIndex; /* removed at v24.1 */
int32_t typeIndex; /* -> MetadataRegistration.types */
```
Not decoded by r2unity today. Without it, every dumped method
degrades to `Method()` with no parameter types or names. Combined
with `parameterDefaultValues` (§2.4) this is what reconstructs real
C# signatures like `Foo(string name, int count = 0)`.
r2unity decodes this table and exposes parameter tokens, names, and
type indices in `r2unity-sj`; it also emits a canonical signature such
as `Namespace.Type.Foo(this;string:name,int:count):void`. Type indices
that refer directly to a type definition are resolved to source names.
Pointer, array, and constructed-generic indices still require the native
`MetadataRegistration.types` table and use `type_index.N` until that
table is available. `parameterDefaultValues` (§2.4) remains necessary
to reconstruct optional values such as `int count = 0`.
### 2.4 Default values: `fieldDefaultValues`, `parameterDefaultValues`,
`fieldAndParameterDefaultValueData`
+1105
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -119,7 +119,7 @@ Still missing:
Add or improve decoding for:
- parameters and full method signatures
- generic method/type substitutions in signatures
- default values for fields and parameters
- properties and events
- nested types
+32 -5
View File
@@ -1,7 +1,7 @@
project(
'r2unity',
'c',
version: '1.2.0',
version: '1.2.6',
default_options: [
'c_std=c99',
'warning_level=2',
@@ -17,8 +17,26 @@ add_project_arguments(
language: 'c',
)
r_util_dep = dependency('r_util')
r_bin_dep = dependency('r_bin')
r2 = find_program('r2', required: true)
r2_cflags = run_command(r2, '-H', 'R2_CFLAGS', check: true).stdout().strip().split()
r2_ldflags = run_command(r2, '-H', 'R2_LDFLAGS', check: true).stdout().strip().split()
r2_libs = run_command(r2, '-H', 'R2_LIBS', check: true).stdout().strip().split()
r2_fallback_dep = declare_dependency(
compile_args: r2_cflags,
link_args: r2_ldflags + r2_libs,
)
# Windows release ZIPs are relocatable and are not registered with pkg-config.
# Fall back to the paths reported by r2 when pkg-config cannot find its SDK.
r_util_dep = dependency('r_util', required: false)
if not r_util_dep.found()
r_util_dep = r2_fallback_dep
endif
r_bin_dep = dependency('r_bin', required: false)
if not r_bin_dep.found()
r_bin_dep = r2_fallback_dep
endif
conf = configuration_data()
conf.set_quoted('R2UNITY_VERSION', meson.project_version())
@@ -32,6 +50,7 @@ lib_sources = files(
'src/lib/bin/native.c',
'src/lib/bin/pe.c',
'src/lib/paths.c',
'src/lib/serialized_file.c',
'src/lib/sbom.c',
)
@@ -47,7 +66,10 @@ test('r2unity-help', r2unity, args: ['-h'])
test('r2unity-version', r2unity, args: ['-v'])
plugins_opt = get_option('plugins')
r_core_dep = dependency('r_core', required: plugins_opt)
r_core_dep = dependency('r_core', required: false)
if not r_core_dep.found() and not plugins_opt.disabled()
r_core_dep = r2_fallback_dep
endif
build_plugins = plugins_opt.enabled() or (
plugins_opt.auto() and r_core_dep.found() and r_bin_dep.found()
)
@@ -81,11 +103,16 @@ if build_plugins
shared_library(
'bin_r2unity',
files('src/r2/bin_r2unity.c') + lib_sources,
files(
'src/r2/bin_r2unity.c',
'src/lib/bgdatabase.c',
'src/lib/resource_pack.c',
) + lib_sources,
include_directories: lib_inc,
dependencies: [r_bin_dep],
name_prefix: '',
install: true,
install_dir: r2_plugindir,
)
endif
+389
View File
@@ -0,0 +1,389 @@
/* r2unity - MIT - Copyright 2025-2026 - pancake */
#define R_LOG_ORIGIN "r2unity.bgdatabase"
#include "bgdatabase.h"
#include <ctype.h>
#define R2U_BGDB_VERSION 6
#define R2U_BGDB_MAX_ADDONS 1024
#define R2U_BGDB_MAX_TABLES 65536
#define R2U_BGDB_MAX_FIELDS 65536
#define R2U_BGDB_MAX_STRING (1024 * 1024)
#define R2U_BGDB_MAX_STRINGS 1048576
typedef struct {
RBuffer *buf;
ut64 offset;
ut64 limit;
} R2UnityBGDatabaseReader;
static bool reader_has(const R2UnityBGDatabaseReader *r, ut64 size) {
return r && r->offset <= r->limit && size <= r->limit - r->offset;
}
static bool reader_read(R2UnityBGDatabaseReader *r, void *out, ut64 size) {
if (!reader_has (r, size)
|| r_buf_read_at (r->buf, r->offset, out, size) != (st64)size) {
return false;
}
r->offset += size;
return true;
}
static bool reader_skip(R2UnityBGDatabaseReader *r, ut64 size) {
if (!reader_has (r, size)) {
return false;
}
r->offset += size;
return true;
}
static bool reader_u32(R2UnityBGDatabaseReader *r, ut32 *out) {
ut8 raw[4];
if (!reader_read (r, raw, sizeof (raw))) {
return false;
}
*out = r_read_le32 (raw);
return true;
}
static bool reader_string(R2UnityBGDatabaseReader *r, char **out,
ut64 *data_offset, ut32 *string_size) {
ut32 length;
if (!reader_u32 (r, &length) || length > R2U_BGDB_MAX_STRING
|| !reader_has (r, length)) {
return false;
}
char *value = R_NEWS (char, (ut64)length + 1);
if (!value) {
return false;
}
if (length && r_buf_read_at (r->buf, r->offset,
(ut8 *)value, length) != (st64)length) {
free (value);
return false;
}
value[length] = 0;
if (data_offset) {
*data_offset = r->offset;
}
if (string_size) {
*string_size = length;
}
*out = value;
return reader_skip (r, length);
}
static bool addon_type_valid(const char *type) {
if (R_STR_ISEMPTY (type) || !r_str_startswith (type, "BansheeGz.BGDatabase.")) {
return false;
}
for (const ut8 *p = (const ut8 *)type; *p; p++) {
if (*p < 0x20 || *p > 0x7e) {
return false;
}
}
return strstr (type, ", BGDatabase") != NULL;
}
static bool utf8_printable(const ut8 *bytes, ut32 size) {
ut32 i = 0;
bool visible = false;
while (i < size) {
ut32 cp;
ut8 ch = bytes[i++];
if (ch < 0x80) {
if (!ch || (ch < 0x20 && ch != '\n' && ch != '\r' && ch != '\t')
|| ch == 0x7f) {
return false;
}
visible |= !isspace (ch);
continue;
}
ut32 need;
if (ch >= 0xc2 && ch <= 0xdf) {
cp = ch & 0x1f;
need = 1;
} else if (ch >= 0xe0 && ch <= 0xef) {
cp = ch & 0x0f;
need = 2;
} else if (ch >= 0xf0 && ch <= 0xf4) {
cp = ch & 0x07;
need = 3;
} else {
return false;
}
if (need > size - i) {
return false;
}
for (ut32 j = 0; j < need; j++) {
ut8 continuation = bytes[i++];
if ((continuation & 0xc0) != 0x80) {
return false;
}
cp = (cp << 6) | (continuation & 0x3f);
}
if ((need == 2 && cp < 0x800) || (need == 3 && cp < 0x10000)
|| (cp >= 0xd800 && cp <= 0xdfff) || cp > 0x10ffff) {
return false;
}
visible = true;
}
return visible;
}
static bool append_table(R2UnityBGDatabase *db, ut64 offset,
ut64 name_field_offset, ut32 field_count, ut16 name_field_type) {
if (db->table_count >= R2U_BGDB_MAX_TABLES) {
return false;
}
size_t count = db->table_count + 1;
R2UnityBGDatabaseTable *tables = realloc (db->tables,
count * sizeof (*tables));
if (!tables) {
return false;
}
db->tables = tables;
R2UnityBGDatabaseTable *table = &db->tables[db->table_count++];
memset (table, 0, sizeof (*table));
table->offset = offset;
table->name_field_offset = name_field_offset;
table->field_count = field_count;
table->name_field_type = name_field_type;
return true;
}
static bool collect_tables(R2UnityBGDatabase *db, const ut8 *data,
ut32 declared_count) {
ut64 start = db->body_offset + 4;
for (ut64 at = start; at <= db->size && db->size - at >= 8; at++) {
if (r_read_le32 (data + at) != 4) {
continue;
}
if (memcmp (data + at + 4, "name", 4)) {
continue;
}
/* A v6 table starts with its field count, followed by the canonical
* name-field descriptor: u32 kind, u16 type, 16-byte id, string. */
if (at < start + 26) {
continue;
}
ut64 name_field = at - 22;
ut64 table_offset = name_field - 4;
ut32 field_count = r_read_le32 (data + table_offset);
ut32 field_kind = r_read_le32 (data + name_field);
ut16 field_type = r_read_le16 (data + name_field + 4);
const ut8 *id = data + name_field + 6;
if (!field_count || field_count > R2U_BGDB_MAX_FIELDS
|| field_kind > 0x1000 || !field_type) {
continue;
}
bool nonzero = false;
for (size_t i = 0; i < sizeof (id); i++) {
nonzero |= id[i] != 0;
}
if (!nonzero || !append_table (db, table_offset, name_field,
field_count, field_type)) {
if (!nonzero) {
continue;
}
return false;
}
at += 7;
}
if (db->table_count != declared_count) {
return false;
}
for (size_t i = 0; i < db->table_count; i++) {
ut64 end = i + 1 < db->table_count
? db->tables[i + 1].offset: db->size;
if (end <= db->tables[i].offset) {
return false;
}
db->tables[i].size = end - db->tables[i].offset;
}
return true;
}
static bool append_string(R2UnityBGDatabase *db, ut64 prefix_offset,
ut32 size, const ut8 *bytes) {
if (db->string_count >= R2U_BGDB_MAX_STRINGS) {
return false;
}
size_t count = db->string_count + 1;
R2UnityBGDatabaseString *strings = realloc (db->strings,
count * sizeof (*strings));
if (!strings) {
return false;
}
db->strings = strings;
R2UnityBGDatabaseString *string = &db->strings[db->string_count++];
memset (string, 0, sizeof (*string));
string->value = R_NEWS (char, (ut64)size + 1);
if (!string->value) {
db->string_count--;
return false;
}
memcpy (string->value, bytes, size);
string->value[size] = 0;
string->prefix_offset = prefix_offset;
string->offset = prefix_offset + 4;
string->size = size;
return true;
}
static bool collect_strings(R2UnityBGDatabase *db, const ut8 *data) {
for (ut64 at = 0; at <= db->size && db->size - at >= 6;) {
bool opaque = false;
for (size_t i = 0; i < db->addon_count; i++) {
R2UnityBGDatabaseAddon *addon = &db->addons[i];
ut64 end = addon->payload_offset + addon->payload_size;
if (at >= addon->payload_offset && at < end) {
at = end;
opaque = true;
break;
}
}
if (opaque || at > db->size || db->size - at < 6) {
continue;
}
ut32 length = r_read_le32 (data + at);
if (length < 2
|| length > R2U_BGDB_MAX_STRING
|| length > db->size - at - 4) {
at++;
continue;
}
const ut8 *bytes = data + at + 4;
if (!utf8_printable (bytes, length)) {
at++;
continue;
}
bool ok = append_string (db, at, length, bytes);
if (!ok) {
return false;
}
at += 4 + length;
}
return true;
}
static R2UnityBGDatabase *parse_internal(RBuffer *buf, bool strings) {
ut64 size = r_buf_size (buf);
if (size < R2UNITY_BGDB_HEADER_SIZE + 8) {
return NULL;
}
R2UnityBGDatabase *db = R_NEW0 (R2UnityBGDatabase);
db->buf = r_buf_new_slice (buf, 0, size);
if (!db->buf) {
free (db);
return NULL;
}
db->size = size;
R2UnityBGDatabaseReader r = {
.buf = db->buf,
.offset = 0,
.limit = size,
};
ut32 addon_count;
if (!reader_u32 (&r, &db->version) || db->version != R2U_BGDB_VERSION
|| !reader_read (&r, db->repository_id, sizeof (db->repository_id))
|| !reader_u32 (&r, &db->header_value)
|| !reader_u32 (&r, &addon_count)
|| !addon_count || addon_count > R2U_BGDB_MAX_ADDONS) {
goto fail;
}
db->addon_count = addon_count;
db->addons = R_NEWS0 (R2UnityBGDatabaseAddon, addon_count);
if (!db->addons) {
goto fail;
}
bool recognized = false;
for (size_t i = 0; i < db->addon_count; i++) {
R2UnityBGDatabaseAddon *addon = &db->addons[i];
addon->offset = r.offset;
if (!reader_string (&r, &addon->type, &addon->type_offset,
&addon->type_size)) {
goto fail;
}
recognized |= addon_type_valid (addon->type);
ut32 payload_size;
addon->payload_size_offset = r.offset;
if (!reader_u32 (&r, &payload_size)) {
goto fail;
}
addon->payload_offset = r.offset;
addon->payload_size = payload_size;
if (!reader_skip (&r, payload_size)) {
goto fail;
}
}
if (!recognized) {
goto fail;
}
db->body_offset = r.offset;
ut32 table_count;
if (!reader_u32 (&r, &table_count) || !table_count
|| table_count > R2U_BGDB_MAX_TABLES || size > INT_MAX) {
goto fail;
}
int data_size = 0;
ut8 *data = r_buf_read_all (db->buf, &data_size);
bool valid = data && (ut64)data_size == size
&& collect_tables (db, data, table_count)
&& (!strings || collect_strings (db, data));
free (data);
if (!valid) {
goto fail;
}
return db;
fail:
r2unity_bgdatabase_free (db);
return NULL;
}
bool r2unity_bgdatabase_check(RBuffer *buf) {
R_RETURN_VAL_IF_FAIL (buf, false);
R2UnityBGDatabase *db = parse_internal (buf, false);
if (!db) {
return false;
}
r2unity_bgdatabase_free (db);
return true;
}
R2UnityBGDatabase *r2unity_bgdatabase_parse(RBuffer *buf) {
R_RETURN_VAL_IF_FAIL (buf, NULL);
return parse_internal (buf, true);
}
char *r2unity_bgdatabase_id_string(const ut8 id[16]) {
R_RETURN_VAL_IF_FAIL (id, NULL);
char *result = R_NEWS (char, 33);
if (!result) {
return NULL;
}
for (size_t i = 0; i < 16; i++) {
snprintf (result + i * 2, 3, "%02x", id[i]);
}
return result;
}
void r2unity_bgdatabase_free(R2UnityBGDatabase *db) {
if (!db) {
return;
}
for (size_t i = 0; i < db->addon_count; i++) {
free (db->addons[i].type);
}
for (size_t i = 0; i < db->string_count; i++) {
free (db->strings[i].value);
}
free (db->addons);
free (db->tables);
free (db->strings);
r_unref (db->buf);
free (db);
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef R2UNITY_BGDATABASE_H
#define R2UNITY_BGDATABASE_H
#include "lib.h"
#define R2UNITY_BGDB_HEADER_SIZE 0x1c
typedef struct {
ut64 offset;
ut64 type_offset;
ut32 type_size;
char *type;
ut64 payload_size_offset;
ut64 payload_offset;
ut64 payload_size;
} R2UnityBGDatabaseAddon;
typedef struct {
ut64 offset;
ut64 size;
ut64 name_field_offset;
ut32 field_count;
ut16 name_field_type;
} R2UnityBGDatabaseTable;
typedef struct {
ut64 prefix_offset;
ut64 offset;
ut32 size;
char *value;
} R2UnityBGDatabaseString;
typedef struct {
RBuffer *buf;
ut64 size;
ut32 version;
ut8 repository_id[16];
ut32 header_value;
ut64 body_offset;
R2UnityBGDatabaseAddon *addons;
size_t addon_count;
R2UnityBGDatabaseTable *tables;
size_t table_count;
R2UnityBGDatabaseString *strings;
size_t string_count;
} R2UnityBGDatabase;
bool r2unity_bgdatabase_check(RBuffer *buf);
R2UnityBGDatabase *r2unity_bgdatabase_parse(RBuffer *buf);
void r2unity_bgdatabase_free(R2UnityBGDatabase *db);
char *r2unity_bgdatabase_id_string(const ut8 id[16]);
#endif
+20 -8
View File
@@ -30,6 +30,10 @@ static void elf_write_word(ut8 *p, ut64 v, bool is64) {
}
}
static bool elf_range_in_file(const ElfImg *e, ut64 off, ut64 size) {
return size <= e->size && off <= e->size - size;
}
static bool elf_load(const char *path, ElfImg *e) {
memset (e, 0, sizeof (*e));
size_t size = 0;
@@ -58,13 +62,16 @@ static bool elf_load(const char *path, ElfImg *e) {
if (type != 1 && type != 2) {
continue;
}
ElfSeg *s = &e->segs[e->nsegs++];
ElfSeg *s = &e->segs[e->nsegs];
s->type = type;
s->flags = r_read_le32 (ph + 4);
s->offset = r_read_le64 (ph + 8);
s->vaddr = r_read_le64 (ph + 16);
s->filesz = r_read_le64 (ph + 32);
s->memsz = r_read_le64 (ph + 40);
if (s->filesz && elf_range_in_file (e, s->offset, s->filesz)) {
e->nsegs++;
}
}
} else {
ut32 phoff = r_read_le32 (e->file + 0x1c);
@@ -80,13 +87,16 @@ static bool elf_load(const char *path, ElfImg *e) {
if (type != 1 && type != 2) {
continue;
}
ElfSeg *s = &e->segs[e->nsegs++];
ElfSeg *s = &e->segs[e->nsegs];
s->type = type;
s->offset = r_read_le32 (ph + 4);
s->vaddr = r_read_le32 (ph + 8);
s->filesz = r_read_le32 (ph + 16);
s->memsz = r_read_le32 (ph + 20);
s->flags = r_read_le32 (ph + 24);
if (s->filesz && elf_range_in_file (e, s->offset, s->filesz)) {
e->nsegs++;
}
}
}
return true;
@@ -104,7 +114,7 @@ static const ut8 *elf_ptr_at_size(ElfImg *e, ut64 va, ut64 size) {
continue;
}
ut64 delta = va - s->vaddr;
if (delta + size <= s->filesz && s->offset + delta + size <= e->size) {
if (size <= s->filesz && delta <= s->filesz - size && elf_range_in_file (e, s->offset + delta, size)) {
return e->file + s->offset + delta;
}
return NULL;
@@ -170,7 +180,7 @@ static void elf_apply_relocs(ElfImg *e, ut64 base) {
break;
}
}
if (!dyn_off || !dyn_sz || dyn_off + dyn_sz > e->size) {
if (!dyn_off || !dyn_sz || !elf_range_in_file (e, dyn_off, dyn_sz)) {
return;
}
ut64 rela_off = 0, rela_sz = 0, rela_ent = e->is64? 24: 12;
@@ -198,7 +208,7 @@ static void elf_apply_relocs(ElfImg *e, ut64 base) {
}
}
const ut64 type_mask = e->is64? 0xffffffffULL: 0xffULL;
for (ut64 i = 0; rela_off && rela_ent && i + rela_ent <= rela_sz; i += rela_ent) {
for (ut64 i = 0; rela_off && rela_ent == (e->is64? 24: 12) && i + rela_ent <= rela_sz; i += rela_ent) {
const ut8 *rp = elf_ptr_at_size (e, rela_off + i, rela_ent);
if (!rp) {
break;
@@ -214,7 +224,7 @@ static void elf_apply_relocs(ElfImg *e, ut64 base) {
}
}
}
for (ut64 i = 0; rel_off && rel_ent && i + rel_ent <= rel_sz; i += rel_ent) {
for (ut64 i = 0; rel_off && rel_ent == (e->is64? 16: 8) && i + rel_ent <= rel_sz; i += rel_ent) {
const ut8 *rp = elf_ptr_at_size (e, rel_off + i, rel_ent);
if (!rp) {
break;
@@ -230,7 +240,7 @@ static void elf_apply_relocs(ElfImg *e, ut64 base) {
}
}
}
if (!e->is64 || !relr_off || !relr_sz || !relr_ent) {
if (!e->is64 || !relr_off || !relr_sz || relr_ent != 8) {
return;
}
ut64 curr = 0;
@@ -282,7 +292,9 @@ bool r2unity_find_method_pointers_elf(R2UnityMetadata *meta, const char *path, c
};
size_t section_count = 0;
R2UnityNativeSection *sections = elf_sections (&e, &section_count);
bool ok = r2unity_native_run_view (meta, &view, sections, section_count, options, result);
view.sections = sections;
view.section_count = section_count;
bool ok = r2unity_native_resolve (meta, &view, NULL, options, result);
R_FREE (sections);
elf_free (&e);
return ok;
+4 -1
View File
@@ -140,6 +140,7 @@ static R2UnityNativeSection *macho_sections(MachO *mo, size_t *count) {
for (size_t i = 0; i < mo->nsegs; i++) {
const MachSeg *s = &mo->segs[i];
ut64 vsize = s->vmsize? s->vmsize: s->filesize;
out[i].name = s->name;
out[i].vaddr = s->vmaddr;
out[i].vsize = vsize;
out[i].size = R_MIN (s->filesize, vsize);
@@ -169,7 +170,9 @@ bool r2unity_find_method_pointers_macho(R2UnityMetadata *meta, const char *path,
};
size_t section_count = 0;
R2UnityNativeSection *sections = macho_sections (&mo, &section_count);
bool ok = r2unity_native_run_view (meta, &view, sections, section_count, options, result);
view.sections = sections;
view.section_count = section_count;
bool ok = r2unity_native_resolve (meta, &view, NULL, options, result);
R_FREE (sections);
macho_free (&mo);
return ok;
+607 -190
View File
File diff suppressed because it is too large. Load diff
+5 -7
View File
@@ -14,9 +14,12 @@ typedef struct {
ut64 base_vaddr;
ut64 text_lo;
ut64 text_hi;
const struct r2unity_native_section_t *sections;
size_t section_count;
} R2UnityNativeView;
typedef struct {
typedef struct r2unity_native_section_t {
const char *name;
ut64 vaddr;
ut64 vsize;
ut64 size;
@@ -24,12 +27,7 @@ typedef struct {
bool is_data;
} R2UnityNativeSection;
bool r2unity_native_parse_code_registration(R2UnityMetadata *meta, R2UnityNativeView *view, ut64 code_registration_va, R2UnityNativeSource source, R2UnityNativeResult *result);
ut64 r2unity_native_resolve_override(const R2UnityNativeOptions *options, const char *const *names);
bool r2unity_native_result_has_ptrs(const R2UnityNativeResult *result);
void r2unity_native_take_heuristic_result(R2UnityNativeResult *result, ut64 *method_ptrs, int ptr_size);
bool r2unity_native_scan_sections(R2UnityMetadata *meta, R2UnityNativeView *view, const R2UnityNativeSection *sections, size_t section_count, R2UnityNativeResult *result);
bool r2unity_native_run_view(R2UnityMetadata *meta, R2UnityNativeView *view, const R2UnityNativeSection *sections, size_t section_count, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
bool r2unity_native_resolve(R2UnityMetadata *meta, R2UnityNativeView *view, RBinFile *bf, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
bool r2unity_find_method_pointers_elf(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
bool r2unity_find_method_pointers_macho(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
bool r2unity_find_method_pointers_pe(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
+4 -1
View File
@@ -146,6 +146,7 @@ static R2UnityNativeSection *pe_sections(PeImg *pe, size_t *count) {
for (size_t i = 0; i < pe->nsecs; i++) {
const PeSec *s = &pe->secs[i];
ut64 vsize = s->vsize? s->vsize: s->filesize;
out[i].name = s->name;
out[i].vaddr = s->vaddr;
out[i].vsize = vsize;
out[i].size = R_MIN (s->filesize, vsize);
@@ -175,7 +176,9 @@ bool r2unity_find_method_pointers_pe(R2UnityMetadata *meta, const char *path, co
};
size_t section_count = 0;
R2UnityNativeSection *sections = pe_sections (&pe, &section_count);
bool ok = r2unity_native_run_view (meta, &view, sections, section_count, options, result);
view.sections = sections;
view.section_count = section_count;
bool ok = r2unity_native_resolve (meta, &view, NULL, options, result);
R_FREE (sections);
pe_free (&pe);
return ok;
+125
View File
@@ -295,6 +295,12 @@ static ut64 field_definition_entry_size(R2UnityMetadata *meta) {
return 4 + type_index_size + 4;
}
static ut64 parameter_definition_entry_size(R2UnityMetadata *meta) {
const bool compact_indices = (meta->version >= 35);
const int type_index_size = compact_indices? meta->typeIndexSize: 4;
return 4 + 4 + type_index_size;
}
/* Detect v24.0 vs v24.1+ (both land on disk as version==24).
*
* At v24.1 the ImageDefinition row grew by 8 bytes (added
@@ -507,6 +513,8 @@ R_API ut64 r2unity_metadata_section_entry_size(R2UnityMetadata *meta, R2UMetadat
return method_definition_entry_size (meta);
case R2U_SEC_FIELDS:
return field_definition_entry_size (meta);
case R2U_SEC_PARAMETERS:
return parameter_definition_entry_size (meta);
case R2U_SEC_TYPE_DEFINITIONS:
return type_definition_entry_size (meta);
case R2U_SEC_IMAGES:
@@ -820,6 +828,123 @@ R_API Il2CppFieldDefinition *r2unity_get_field_definitions(R2UnityMetadata *meta
return fields;
}
R_API Il2CppParameterDefinition *r2unity_get_parameter_definitions(R2UnityMetadata *meta, size_t *count) {
R_RETURN_VAL_IF_FAIL (meta && count, NULL);
const bool compact_indices = (meta->version >= 35);
const int type_index_size = compact_indices? meta->typeIndexSize: 4;
const ut64 entry = parameter_definition_entry_size (meta);
ut8 *buf = read_metadata_table (meta, R2U_SEC_PARAMETERS, entry, count);
if (!buf) {
return NULL;
}
Il2CppParameterDefinition *parameters = R_NEWS (Il2CppParameterDefinition, *count);
if (!parameters) {
R_FREE (buf);
return NULL;
}
for (size_t i = 0; i < *count; i++) {
const ut8 *p = buf + i * entry;
parameters[i].nameIndex = read_u32p (&p);
parameters[i].token = read_u32p (&p);
parameters[i].typeIndex = read_indexp (&p, type_index_size);
}
R_FREE (buf);
return parameters;
}
static const char *csharp_type_alias(const char *name) {
static const struct {
const char *fullname;
const char *alias;
} aliases[] = {
{ "System.Void", "void" },
{ "System.Boolean", "bool" },
{ "System.Byte", "byte" },
{ "System.SByte", "sbyte" },
{ "System.Char", "char" },
{ "System.Decimal", "decimal" },
{ "System.Double", "double" },
{ "System.Single", "float" },
{ "System.Int16", "short" },
{ "System.UInt16", "ushort" },
{ "System.Int32", "int" },
{ "System.UInt32", "uint" },
{ "System.Int64", "long" },
{ "System.UInt64", "ulong" },
{ "System.IntPtr", "nint" },
{ "System.UIntPtr", "nuint" },
{ "System.Object", "object" },
{ "System.String", "string" },
};
for (size_t i = 0; i < R_ARRAY_SIZE (aliases); i++) {
if (!strcmp (name, aliases[i].fullname)) {
return aliases[i].alias;
}
}
return NULL;
}
R_API char *r2unity_type_name_from_index(R2UnityMetadata *meta, const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index, bool fallback) {
R_RETURN_VAL_IF_FAIL (meta, NULL);
if (type_index >= 0) {
for (size_t i = 0; types && i < type_count; i++) {
if (types[i].byvalTypeIndex != type_index) {
continue;
}
char *name = r2unity_type_fullname (meta, &types[i], i, R2U_NAME_FALLBACK_TYPE);
if (name) {
const char *alias = csharp_type_alias (name);
if (alias) {
free (name);
return strdup (alias);
}
}
return name;
}
}
return fallback? r_str_newf ("type_index.%d", type_index): NULL;
}
R_API char *r2unity_method_signature(R2UnityMetadata *meta, const Il2CppMethodDefinition *method, const Il2CppTypeDefinition *owner, size_t owner_index, const Il2CppTypeDefinition *types, size_t type_count, const Il2CppParameterDefinition *parameters, size_t parameter_count, bool fallback) {
R_RETURN_VAL_IF_FAIL (meta && method, NULL);
char *return_type = r2unity_type_name_from_index (meta, types, type_count, method->returnType, fallback);
char *method_name = r2unity_method_fullname (meta, method, owner, owner_index,
fallback? R2U_NAME_FALLBACK_TYPE: 0);
if (!method_name) {
free (return_type);
return NULL;
}
RStrBuf *sb = r_strbuf_new (method_name);
r_strbuf_append (sb, "(");
bool is_instance = !(method->flags & 0x0010);
if (is_instance) {
r_strbuf_append (sb, "this;");
}
for (size_t i = 0; i < method->parameterCount; i++) {
st64 pi = (st64)method->parameterStart + (st64)i;
const Il2CppParameterDefinition *parameter = parameters
&& pi >= 0 && (ut64)pi < parameter_count
? &parameters[pi]: NULL;
char *type_name = parameter
? r2unity_type_name_from_index (meta, types, type_count, parameter->typeIndex, fallback)
: NULL;
char *parameter_name = parameter? r2unity_get_string (meta, parameter->nameIndex): NULL;
r_strbuf_appendf (sb, "%s%s", i? ",": "",
type_name? type_name: "unknown");
if (R_STR_ISNOTEMPTY (parameter_name)) {
r_strbuf_appendf (sb, ":%s", parameter_name);
} else {
r_strbuf_appendf (sb, ":arg%zu", i);
}
free (parameter_name);
free (type_name);
}
r_strbuf_appendf (sb, "):%s", return_type? return_type: "unknown");
free (method_name);
free (return_type);
return r_strbuf_drain (sb);
}
R_API int32_t *r2unity_get_type_index_table(R2UnityMetadata *meta, R2UMetadataSectionId id, size_t *count) {
R_RETURN_VAL_IF_FAIL (meta && count, NULL);
const bool compact_indices = (meta->version >= 35);
+36 -5
View File
@@ -3,8 +3,10 @@
#include <r_util.h>
struct r_bin_t;
struct r_bin_file_t;
#if !defined(R2_BIN_H) && !defined(R2_BIN_DWARF_H)
typedef struct r_bin_t RBin;
typedef struct r_bin_file_t RBinFile;
#endif
#define IL2CPP_MAGIC 0xFAB11BAF
#define R2UNITY_METADATA_BASE_SECTION_COUNT 31
@@ -128,6 +130,12 @@ typedef struct {
uint32_t token;
} Il2CppFieldDefinition;
typedef struct {
uint32_t nameIndex;
uint32_t token;
int32_t typeIndex;
} Il2CppParameterDefinition;
typedef struct {
uint32_t nameIndex;
int32_t assemblyIndex;
@@ -170,8 +178,7 @@ typedef struct {
enum {
R2U_INTEROP_PINVOKE = 1, /* managed -> native, [DllImport] */
R2U_INTEROP_REVERSE_PINVOKE = 2, /* native -> managed, [MonoPInvokeCallback] */
R2U_INTEROP_UNMANAGED_ONLY = 3, /* [UnmanagedCallersOnly] (.NET 5+) */
R2U_INTEROP_INTEROP_DATA = 4 /* Il2CppInteropData entry (opaque) */
R2U_INTEROP_UNMANAGED_ONLY = 3 /* [UnmanagedCallersOnly] (.NET 5+) */
};
/* A single P/Invoke or reverse-P/Invoke entry.
@@ -209,6 +216,24 @@ typedef enum {
R2U_NATIVE_SOURCE_HEURISTIC
} R2UnityNativeSource;
typedef enum {
R2U_NATIVE_TABLE_REVERSE_PINVOKE_WRAPPERS,
R2U_NATIVE_TABLE_GENERIC_METHOD_POINTERS,
R2U_NATIVE_TABLE_GENERIC_ADJUSTOR_THUNKS,
R2U_NATIVE_TABLE_INVOKER_POINTERS,
R2U_NATIVE_TABLE_UNRESOLVED_VIRTUAL_CALL_POINTERS,
R2U_NATIVE_TABLE_UNRESOLVED_INSTANCE_CALL_POINTERS,
R2U_NATIVE_TABLE_UNRESOLVED_STATIC_CALL_POINTERS,
R2U_NATIVE_TABLE_INTEROP_DATA,
R2U_NATIVE_TABLE_WINDOWS_RUNTIME_FACTORY_TABLE,
R2U_NATIVE_TABLE_COUNT
} R2UnityNativeTableId;
typedef struct {
ut64 va;
ut64 count;
} R2UnityNativeTable;
typedef struct {
const char *name;
ut64 va;
@@ -230,6 +255,7 @@ typedef struct {
ut64 metadata_registration_va;
ut64 method_pointers_va;
ut64 code_gen_modules_va;
R2UnityNativeTable tables[R2U_NATIVE_TABLE_COUNT];
int ptr_size;
} R2UnityNativeResult;
@@ -244,6 +270,9 @@ R_API bool r2unity_read_string_literal(R2UnityMetadata *meta, const Il2CppString
R_API Il2CppTypeDefinition *r2unity_get_type_definitions(R2UnityMetadata *meta, size_t *count);
R_API Il2CppMethodDefinition *r2unity_get_method_definitions(R2UnityMetadata *meta, size_t *count);
R_API Il2CppFieldDefinition *r2unity_get_field_definitions(R2UnityMetadata *meta, size_t *count);
R_API Il2CppParameterDefinition *r2unity_get_parameter_definitions(R2UnityMetadata *meta, size_t *count);
R_API char *r2unity_type_name_from_index(R2UnityMetadata *meta, const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index, bool fallback);
R_API char *r2unity_method_signature(R2UnityMetadata *meta, const Il2CppMethodDefinition *method, const Il2CppTypeDefinition *owner, size_t owner_index, const Il2CppTypeDefinition *types, size_t type_count, const Il2CppParameterDefinition *parameters, size_t parameter_count, bool fallback);
R_API int32_t *r2unity_get_type_index_table(R2UnityMetadata *meta, R2UMetadataSectionId id, size_t *count);
R_API Il2CppImageDefinition *r2unity_get_images(R2UnityMetadata *meta, size_t *count);
R_API int *r2unity_build_type_image_map(const Il2CppImageDefinition *images, size_t image_count, size_t type_count);
@@ -255,6 +284,7 @@ R_API R2UnityInterop *r2unity_enumerate_pinvokes(R2UnityMetadata *meta, size_t *
* Returns methods tagged with [MonoPInvokeCallback] or [UnmanagedCallersOnly].
* Pre-v29 metadata returns NULL (attribute ctor args live in generator stubs). */
R_API R2UnityInterop *r2unity_enumerate_reverse_pinvokes(R2UnityMetadata *meta, size_t *count);
R_API bool r2unity_enrich_reverse_pinvokes_native(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityInterop **items, size_t *count);
R_API void r2unity_free_interop(R2UnityInterop *items, size_t count);
R_API char *r2unity_sbom_tostring(R2UnityMetadata *meta, const char *exe_path, const char *metadata_path, R2UnitySbomFormat format);
R_API const char *r2unity_unity_range_from_wire(int wire);
@@ -264,12 +294,13 @@ R_API ut64 r2unity_metadata_section_entry_size(R2UnityMetadata *meta, R2UMetadat
R_API ut64 r2unity_metadata_section_count(R2UnityMetadata *meta, R2UMetadataSectionId id);
R_API ut64 r2unity_metadata_header_size(R2UnityMetadata *meta);
R_API const char *r2unity_native_source_name(R2UnityNativeSource source);
R_API const char *r2unity_native_table_name(R2UnityNativeTableId id);
R_API const char *const *r2unity_native_code_registration_names(void);
R_API const char *const *r2unity_native_metadata_registration_names(void);
R_API void r2unity_native_result_fini(R2UnityNativeResult *result);
R_API bool r2unity_find_method_pointers(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
R_API bool r2unity_find_method_pointers_simple(R2UnityMetadata *meta, const char *path, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
R_API bool r2unity_find_method_pointers_rbin(R2UnityMetadata *meta, struct r_bin_t *bin, struct r_bin_file_t *bf, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
R_API bool r2unity_find_method_pointers_rbin(R2UnityMetadata *meta, RBin *bin, RBinFile *bf, const R2UnityNativeOptions *options, R2UnityNativeResult *result);
/* Companion-file discovery.
*
+154
View File
@@ -0,0 +1,154 @@
/* r2unity - MIT - Copyright 2025-2026 - pancake */
#define R_LOG_ORIGIN "r2unity.resource_pack"
#include "resource_pack.h"
#define R2U_RESOURCE_PACK_MAX_ENTRIES 65536
#define R2U_RESOURCE_PACK_MAX_NAME 65536
typedef struct {
RBuffer *buf;
ut64 offset;
ut64 limit;
} R2UnityResourcePackReader;
static bool reader_read(R2UnityResourcePackReader *reader, void *out, ut64 size) {
if (reader->offset > reader->limit || size > reader->limit - reader->offset
|| r_buf_read_at (reader->buf, reader->offset, out, size) != (st64)size) {
return false;
}
reader->offset += size;
return true;
}
static bool reader_u32(R2UnityResourcePackReader *reader, ut32 *value) {
ut8 bytes[4];
if (!reader_read (reader, bytes, sizeof (bytes))) {
return false;
}
*value = r_read_le32 (bytes);
return true;
}
static bool valid_name(const ut8 *bytes, ut32 size) {
ut32 offset = 0;
bool visible = false;
while (offset < size) {
RRune rune;
int length = r_utf8_decode (bytes + offset, size - offset, &rune);
if (length < 1 || (ut32)length > size - offset
|| rune == 0 || (rune < 0x20 && rune != '\t')
|| rune == 0x7f) {
return false;
}
visible |= rune != ' ' && rune != '\t';
offset += length;
}
return visible;
}
static R2UnityResourcePack *parse_internal(RBuffer *buf) {
ut64 size = r_buf_size (buf);
if (size < 8) {
return NULL;
}
R2UnityResourcePack *pack = R_NEW0 (R2UnityResourcePack);
pack->buf = r_buf_new_slice (buf, 0, size);
if (!pack->buf) {
free (pack);
return NULL;
}
pack->size = size;
R2UnityResourcePackReader reader = {
.buf = pack->buf,
.offset = 0,
.limit = size,
};
ut32 count;
if (!reader_u32 (&reader, &pack->records_size)
|| pack->records_size < 4
|| pack->records_size > size - 4
|| !reader_u32 (&reader, &count)
|| !count || count > R2U_RESOURCE_PACK_MAX_ENTRIES) {
goto fail;
}
pack->payload_offset = 4 + pack->records_size;
reader.limit = pack->payload_offset;
pack->entry_count = count;
pack->entries = R_NEWS0 (R2UnityResourcePackEntry, count);
if (!pack->entries) {
goto fail;
}
for (size_t i = 0; i < pack->entry_count; i++) {
R2UnityResourcePackEntry *entry = &pack->entries[i];
ut32 payload_size;
entry->descriptor_offset = reader.offset;
if (!reader_u32 (&reader, &payload_size)) {
goto fail;
}
entry->payload_size = payload_size;
entry->name_size_offset = reader.offset;
if (!reader_u32 (&reader, &entry->name_size)
|| !entry->name_size
|| entry->name_size > R2U_RESOURCE_PACK_MAX_NAME
|| entry->name_size > reader.limit - reader.offset) {
goto fail;
}
entry->name_offset = reader.offset;
entry->name = R_NEWS (char, (ut64)entry->name_size + 1);
if (!entry->name
|| !reader_read (&reader, entry->name, entry->name_size)
|| !valid_name ((const ut8 *)entry->name, entry->name_size)) {
goto fail;
}
entry->name[entry->name_size] = 0;
}
if (reader.offset != pack->payload_offset) {
goto fail;
}
ut64 payload_offset = pack->payload_offset;
for (size_t i = 0; i < pack->entry_count; i++) {
R2UnityResourcePackEntry *entry = &pack->entries[i];
if (payload_offset > size || entry->payload_size > size - payload_offset) {
goto fail;
}
entry->payload_offset = payload_offset;
payload_offset += entry->payload_size;
}
if (payload_offset != size) {
goto fail;
}
return pack;
fail:
r2unity_resource_pack_free (pack);
return NULL;
}
bool r2unity_resource_pack_check(RBuffer *buf) {
R_RETURN_VAL_IF_FAIL (buf, false);
R2UnityResourcePack *pack = parse_internal (buf);
if (!pack) {
return false;
}
r2unity_resource_pack_free (pack);
return true;
}
R2UnityResourcePack *r2unity_resource_pack_parse(RBuffer *buf) {
R_RETURN_VAL_IF_FAIL (buf, NULL);
return parse_internal (buf);
}
void r2unity_resource_pack_free(R2UnityResourcePack *pack) {
if (!pack) {
return;
}
for (size_t i = 0; i < pack->entry_count; i++) {
free (pack->entries[i].name);
}
free (pack->entries);
r_unref (pack->buf);
free (pack);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef R2UNITY_RESOURCE_PACK_H
#define R2UNITY_RESOURCE_PACK_H
#include "lib.h"
typedef struct {
ut64 descriptor_offset;
ut64 name_size_offset;
ut64 name_offset;
ut32 name_size;
char *name;
ut64 payload_offset;
ut64 payload_size;
} R2UnityResourcePackEntry;
typedef struct {
RBuffer *buf;
ut64 size;
ut32 records_size;
ut64 payload_offset;
R2UnityResourcePackEntry *entries;
size_t entry_count;
} R2UnityResourcePack;
bool r2unity_resource_pack_check(RBuffer *buf);
R2UnityResourcePack *r2unity_resource_pack_parse(RBuffer *buf);
void r2unity_resource_pack_free(R2UnityResourcePack *pack);
#endif
+601
View File
@@ -0,0 +1,601 @@
/* r2unity - MIT - Copyright 2025-2026 - pancake */
#define R_LOG_ORIGIN "r2unity.serialized_file"
#include "serialized_file.h"
#include <ctype.h>
#define R2U_SERIALIZED_VERSION 22
#define R2U_SERIALIZED_HEADER_SIZE 0x30
#define R2U_MAX_TYPES 65536
#define R2U_MAX_OBJECTS 1048576
#define R2U_MAX_REFERENCES 1048576
#define R2U_MAX_STRING (1024 * 1024)
typedef struct {
RBuffer *buf;
ut64 offset;
ut64 limit;
bool big_endian;
} R2UnityReader;
static bool reader_has(const R2UnityReader *r, ut64 size) {
return r && r->offset <= r->limit && size <= r->limit - r->offset;
}
static bool reader_read(R2UnityReader *r, void *out, ut64 size) {
if (!reader_has (r, size) || r_buf_read_at (r->buf, r->offset, out, size) != (st64)size) {
return false;
}
r->offset += size;
return true;
}
static bool reader_skip(R2UnityReader *r, ut64 size) {
if (!reader_has (r, size)) {
return false;
}
r->offset += size;
return true;
}
static bool reader_align(R2UnityReader *r, ut64 alignment) {
ut64 aligned = R_ROUND (r->offset, alignment);
return aligned >= r->offset && reader_skip (r, aligned - r->offset);
}
static bool reader_u8(R2UnityReader *r, ut8 *out) {
return reader_read (r, out, sizeof (*out));
}
static bool reader_u16(R2UnityReader *r, ut16 *out) {
ut8 raw[2];
if (!reader_read (r, raw, sizeof (raw))) {
return false;
}
*out = r->big_endian? r_read_be16 (raw): r_read_le16 (raw);
return true;
}
static bool reader_u32(R2UnityReader *r, ut32 *out) {
ut8 raw[4];
if (!reader_read (r, raw, sizeof (raw))) {
return false;
}
*out = r->big_endian? r_read_be32 (raw): r_read_le32 (raw);
return true;
}
static bool reader_u64(R2UnityReader *r, ut64 *out) {
ut8 raw[8];
if (!reader_read (r, raw, sizeof (raw))) {
return false;
}
*out = r->big_endian? r_read_be64 (raw): r_read_le64 (raw);
return true;
}
static bool reader_cstring(R2UnityReader *r, char **out, ut64 *string_offset) {
ut64 start = r->offset;
ut64 length = 0;
ut8 ch = 0;
while (reader_has (r, 1)) {
if (!reader_u8 (r, &ch)) {
return false;
}
if (!ch) {
char *s = R_NEWS (char, length + 1);
if (!s) {
return false;
}
if (length && r_buf_read_at (r->buf, start, (ut8 *)s, length) != (st64)length) {
free (s);
return false;
}
s[length] = 0;
*out = s;
if (string_offset) {
*string_offset = start;
}
return true;
}
if (++length > R2U_MAX_STRING) {
return false;
}
}
return false;
}
static bool buffer_range(RBuffer *buf, ut64 offset, ut64 size) {
ut64 total = r_buf_size (buf);
return offset <= total && size <= total - offset;
}
static ut32 buffer_u32(const R2UnitySerializedFile *sf, ut64 offset) {
return r_buf_read_ble32_at (sf->buf, offset, sf->big_endian);
}
static ut64 buffer_u64(const R2UnitySerializedFile *sf, ut64 offset) {
return r_buf_read_ble64_at (sf->buf, offset, sf->big_endian);
}
static bool object_string(const R2UnitySerializedFile *sf, const R2UnitySerializedObject *object, ut64 at, char **out, ut64 *string_offset, ut32 *string_size, ut64 *next) {
ut64 end = object->offset + object->size;
if (end < object->offset || at > end || end - at < 4) {
return false;
}
ut32 length = buffer_u32 (sf, at);
ut64 data = at + 4;
if (length > R2U_MAX_STRING || data > end || length > end - data) {
return false;
}
char *s = R_NEWS (char, (ut64)length + 1);
if (!s) {
return false;
}
if (length && r_buf_read_at (sf->buf, data, (ut8 *)s, length) != length) {
free (s);
return false;
}
s[length] = 0;
*out = s;
if (string_offset) {
*string_offset = data;
}
if (string_size) {
*string_size = length;
}
if (next) {
ut64 aligned = R_ROUND (data + length, 4);
if (aligned < data || aligned > end) {
free (s);
*out = NULL;
return false;
}
*next = aligned;
}
return true;
}
static bool skip_type_tree(R2UnityReader *r) {
ut32 node_count;
ut32 string_size;
if (!reader_u32 (r, &node_count) || !reader_u32 (r, &string_size)) {
return false;
}
/* SerializedFile v19+ type-tree nodes include an eight-byte ref hash. */
const ut64 node_size = 32;
if (node_count > R2U_MAX_OBJECTS || node_count > UT64_MAX / node_size) {
return false;
}
return reader_skip (r, (ut64)node_count * node_size)
&& reader_skip (r, string_size);
}
static bool skip_dependencies(R2UnityReader *r) {
ut32 count;
if (!reader_u32 (r, &count) || count > R2U_MAX_TYPES) {
return false;
}
return reader_skip (r, (ut64)count * 4);
}
static bool read_serialized_type(R2UnityReader *r, bool type_tree, bool reference_type, R2UnitySerializedType *out) {
ut64 start = r->offset;
ut32 class_id;
ut8 stripped;
ut16 script_type;
if (!reader_u32 (r, &class_id) || !reader_u8 (r, &stripped) || !reader_u16 (r, &script_type)) {
return false;
}
out->class_id = (st32)class_id;
out->stripped = stripped != 0;
out->script_type_index = (st16)script_type;
if (out->class_id == 114 && !reader_skip (r, 16)) {
return false;
}
if (!reader_skip (r, 16)) {
return false;
}
if (type_tree && !skip_type_tree (r)) {
return false;
}
if (reference_type) {
char *class_name = NULL;
char *name_space = NULL;
char *assembly = NULL;
bool ok = reader_cstring (r, &class_name, NULL)
&& reader_cstring (r, &name_space, NULL)
&& reader_cstring (r, &assembly, NULL);
free (class_name);
free (name_space);
free (assembly);
if (!ok) {
return false;
}
} else if (type_tree && !skip_dependencies (r)) {
return false;
}
out->offset = start;
out->size = r->offset - start;
return true;
}
const char *r2unity_serialized_class_name(st32 class_id) {
switch (class_id) {
case 21:
return "Material";
case 28:
return "Texture2D";
case 43:
return "Mesh";
case 49:
return "TextAsset";
case 74:
return "AnimationClip";
case 83:
return "AudioClip";
case 91:
return "AnimatorController";
case 114:
return "MonoBehaviour";
case 150:
return "PreloadData";
case 328:
return "VideoClip";
default:
return "Object";
}
}
static bool class_has_name(st32 class_id) {
switch (class_id) {
case 21:
case 28:
case 43:
case 49:
case 74:
case 83:
case 91:
case 150:
case 328:
return true;
default:
return false;
}
}
static char *read_sidecar_path(const R2UnitySerializedFile *sf, ut64 offset,
ut32 size) {
char *path = R_NEWS (char, (ut64)size + 1);
if (!path || r_buf_read_at (sf->buf, offset, (ut8 *)path, size) != size) {
free (path);
return NULL;
}
path[size] = 0;
if (memchr (path, 0, size)
|| (!r_str_endswith (path, ".resS")
&& !r_str_endswith (path, ".resource"))) {
free (path);
return NULL;
}
return path;
}
static void parse_stream_info(R2UnitySerializedFile *sf,
R2UnitySerializedObject *object, ut64 after_name) {
ut64 end = object->offset + object->size;
/* Texture2D/Mesh StreamingInfo: u64 offset, u32 size, string path. */
for (ut64 at = after_name; at <= end && end - at >= 16; at += 4) {
ut64 stream_offset = buffer_u64 (sf, at);
ut32 stream_size = buffer_u32 (sf, at + 8);
ut32 path_size = buffer_u32 (sf, at + 12);
ut64 path_offset = at + 16;
if (!stream_size || !path_size || path_size > R2U_MAX_STRING
|| path_offset > end || path_size > end - path_offset) {
continue;
}
ut64 aligned_end = R_ROUND (path_offset + path_size, 4);
if (aligned_end != end) {
continue;
}
char *path = read_sidecar_path (sf, path_offset, path_size);
if (path) {
object->stream_path = path;
object->stream_offset = stream_offset;
object->stream_size = stream_size;
return;
}
}
/* AudioClip/VideoClip resource: string path, u64 offset, u64 size. */
for (ut64 at = after_name; at <= end && end - at >= 20; at += 4) {
ut32 path_size = buffer_u32 (sf, at);
ut64 path_offset = at + 4;
if (!path_size || path_size > R2U_MAX_STRING
|| path_offset > end || path_size > end - path_offset) {
continue;
}
ut64 stream_info = R_ROUND (path_offset + path_size, 4);
if (stream_info < path_offset || stream_info > end
|| end - stream_info < 16) {
continue;
}
ut64 stream_offset = buffer_u64 (sf, stream_info);
ut64 stream_size = buffer_u64 (sf, stream_info + 8);
if (!stream_size) {
continue;
}
char *path = read_sidecar_path (sf, path_offset, path_size);
if (path) {
object->stream_path = path;
object->stream_offset = stream_offset;
object->stream_size = stream_size;
return;
}
}
}
static void parse_texture_info(R2UnitySerializedFile *sf,
R2UnitySerializedObject *object, ut64 after_name) {
ut64 end = object->offset + object->size;
if (after_name <= end && end - after_name >= 28) {
object->width = buffer_u32 (sf, after_name + 8);
object->height = buffer_u32 (sf, after_name + 12);
object->texture_format = (st32)buffer_u32 (sf, after_name + 24);
object->has_texture_info = object->width > 0 && object->height > 0;
}
parse_stream_info (sf, object, after_name);
}
static bool parse_object_details(R2UnitySerializedFile *sf, R2UnitySerializedObject *object) {
ut64 after_name = object->offset;
if (class_has_name (object->class_id)) {
if (!object_string (sf, object, object->offset, &object->name,
&object->name_offset, &object->name_size, &after_name)) {
return false;
}
if (!*object->name) {
R_FREE (object->name);
}
} else if (object->class_id == 114) {
/* v22 MonoBehaviour: GameObject PPtr, enabled+alignment, Script PPtr, name. */
ut64 name_at = object->offset + 28;
if (!object_string (sf, object, name_at, &object->name,
&object->name_offset, &object->name_size, &after_name)) {
return false;
}
if (!*object->name) {
R_FREE (object->name);
}
}
if (object->class_id == 49) {
ut64 end = object->offset + object->size;
if (after_name > end || end - after_name < 4) {
return false;
}
ut32 payload_size = buffer_u32 (sf, after_name);
ut64 payload_offset = after_name + 4;
if (payload_offset > end || payload_size > end - payload_offset) {
return false;
}
object->payload_offset = payload_offset;
object->payload_size = payload_size;
} else if (object->class_id == 28) {
parse_texture_info (sf, object, after_name);
} else if (object->class_id == 43 || object->class_id == 83
|| object->class_id == 328) {
parse_stream_info (sf, object, after_name);
}
return true;
}
bool r2unity_serialized_file_check(RBuffer *buf) {
R_RETURN_VAL_IF_FAIL (buf, false);
ut64 actual_size = r_buf_size (buf);
if (actual_size < R2U_SERIALIZED_HEADER_SIZE) {
return false;
}
ut8 header[R2U_SERIALIZED_HEADER_SIZE];
if (r_buf_read_at (buf, 0, header, sizeof (header)) != sizeof (header)) {
return false;
}
ut32 version = r_read_be32 (header + 8);
if (version != R2U_SERIALIZED_VERSION || header[16] > 1) {
return false;
}
ut32 metadata_size = r_read_be32 (header + 20);
ut64 file_size = r_read_be64 (header + 24);
ut64 data_offset = r_read_be64 (header + 32);
if (file_size != actual_size || metadata_size < 16
|| data_offset < R2U_SERIALIZED_HEADER_SIZE || data_offset > file_size
|| metadata_size > data_offset - R2U_SERIALIZED_HEADER_SIZE) {
return false;
}
/* The metadata begins with a NUL-terminated Unity version string. */
ut8 first;
if (r_buf_read_at (buf, R2U_SERIALIZED_HEADER_SIZE, &first, 1) != 1 || !isdigit (first)) {
return false;
}
ut64 string_limit = R_MIN ((ut64)metadata_size, 64);
bool nul = false;
for (ut64 i = 0; i < string_limit; i++) {
ut8 ch;
if (r_buf_read_at (buf, R2U_SERIALIZED_HEADER_SIZE + i, &ch, 1) != 1) {
return false;
}
if (!ch) {
nul = i > 0;
break;
}
}
return nul;
}
R2UnitySerializedFile *r2unity_serialized_file_parse(RBuffer *buf) {
if (!r2unity_serialized_file_check (buf)) {
return NULL;
}
R2UnitySerializedFile *sf = R_NEW0 (R2UnitySerializedFile);
sf->buf = r_buf_new_slice (buf, 0, r_buf_size (buf));
if (!sf->buf) {
free (sf);
return NULL;
}
sf->version = r_buf_read_be32_at (sf->buf, 8);
sf->big_endian = r_buf_read8_at (sf->buf, 16) != 0;
sf->metadata_size = r_buf_read_be32_at (sf->buf, 20);
sf->file_size = r_buf_read_be64_at (sf->buf, 24);
sf->data_offset = r_buf_read_be64_at (sf->buf, 32);
sf->header_size = R2U_SERIALIZED_HEADER_SIZE;
R2UnityReader r = {
.buf = sf->buf,
.offset = sf->header_size,
.limit = sf->header_size + sf->metadata_size,
.big_endian = sf->big_endian,
};
ut32 platform;
ut8 type_tree;
ut32 type_count;
if (!reader_cstring (&r, &sf->unity_version, NULL)
|| !reader_u32 (&r, &platform)
|| !reader_u8 (&r, &type_tree)
|| !reader_u32 (&r, &type_count)
|| type_count > R2U_MAX_TYPES) {
goto fail;
}
sf->target_platform = (st32)platform;
sf->enable_type_tree = type_tree != 0;
sf->type_count = type_count;
sf->types = R_NEWS0 (R2UnitySerializedType, type_count);
if (type_count && !sf->types) {
goto fail;
}
for (size_t i = 0; i < sf->type_count; i++) {
if (!read_serialized_type (&r, sf->enable_type_tree, false, &sf->types[i])) {
goto fail;
}
}
ut32 object_count;
if (!reader_u32 (&r, &object_count) || object_count > R2U_MAX_OBJECTS) {
goto fail;
}
sf->object_count = object_count;
sf->objects = R_NEWS0 (R2UnitySerializedObject, object_count);
if (object_count && !sf->objects) {
goto fail;
}
for (size_t i = 0; i < sf->object_count; i++) {
R2UnitySerializedObject *object = &sf->objects[i];
ut64 path_id;
ut64 byte_start;
ut32 byte_size;
ut32 type_index;
if (!reader_align (&r, 4)) {
goto fail;
}
object->table_offset = r.offset;
if (!reader_u64 (&r, &path_id) || !reader_u64 (&r, &byte_start)
|| !reader_u32 (&r, &byte_size) || !reader_u32 (&r, &type_index)
|| type_index >= sf->type_count || byte_start > UT64_MAX - sf->data_offset) {
goto fail;
}
object->path_id = (st64)path_id;
object->type_index = type_index;
object->class_id = sf->types[type_index].class_id;
object->offset = sf->data_offset + byte_start;
object->size = byte_size;
if (!buffer_range (sf->buf, object->offset, object->size)) {
goto fail;
}
}
ut32 script_count;
if (!reader_u32 (&r, &script_count) || script_count > R2U_MAX_REFERENCES) {
goto fail;
}
sf->script_count = script_count;
sf->scripts = R_NEWS0 (R2UnitySerializedScript, script_count);
if (script_count && !sf->scripts) {
goto fail;
}
for (size_t i = 0; i < sf->script_count; i++) {
ut32 file_index;
ut64 path_id;
if (!reader_align (&r, 4) || !reader_u32 (&r, &file_index) || !reader_u64 (&r, &path_id)) {
goto fail;
}
sf->scripts[i].file_index = (st32)file_index;
sf->scripts[i].path_id = (st64)path_id;
}
ut32 external_count;
if (!reader_u32 (&r, &external_count) || external_count > R2U_MAX_REFERENCES) {
goto fail;
}
sf->external_count = external_count;
sf->externals = R_NEWS0 (R2UnitySerializedExternal, external_count);
if (external_count && !sf->externals) {
goto fail;
}
for (size_t i = 0; i < sf->external_count; i++) {
R2UnitySerializedExternal *external = &sf->externals[i];
char *empty = NULL;
ut32 type;
if (!reader_cstring (&r, &empty, NULL) || !reader_read (&r, external->guid, sizeof (external->guid))
|| !reader_u32 (&r, &type)
|| !reader_cstring (&r, &external->path, &external->path_offset)) {
free (empty);
goto fail;
}
free (empty);
external->type = (st32)type;
}
ut32 reference_type_count;
if (!reader_u32 (&r, &reference_type_count) || reference_type_count > R2U_MAX_TYPES) {
goto fail;
}
for (ut32 i = 0; i < reference_type_count; i++) {
R2UnitySerializedType type = {0};
if (!read_serialized_type (&r, sf->enable_type_tree, true, &type)) {
goto fail;
}
}
if (!reader_cstring (&r, &sf->user_information, NULL)) {
goto fail;
}
for (size_t i = 0; i < sf->object_count; i++) {
if (!parse_object_details (sf, &sf->objects[i])) {
goto fail;
}
}
return sf;
fail:
r2unity_serialized_file_free (sf);
return NULL;
}
void r2unity_serialized_file_free(R2UnitySerializedFile *sf) {
if (!sf) {
return;
}
for (size_t i = 0; i < sf->object_count; i++) {
free (sf->objects[i].name);
free (sf->objects[i].stream_path);
}
for (size_t i = 0; i < sf->external_count; i++) {
free (sf->externals[i].path);
}
free (sf->unity_version);
free (sf->types);
free (sf->objects);
free (sf->scripts);
free (sf->externals);
free (sf->user_information);
r_unref (sf->buf);
free (sf);
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef R2UNITY_SERIALIZED_FILE_H
#define R2UNITY_SERIALIZED_FILE_H
#include "lib.h"
typedef struct {
st32 class_id;
st16 script_type_index;
ut64 offset;
ut64 size;
bool stripped;
} R2UnitySerializedType;
typedef struct {
st64 path_id;
st32 class_id;
ut32 type_index;
ut64 table_offset;
ut64 offset;
ut64 size;
char *name;
ut64 name_offset;
ut32 name_size;
ut64 payload_offset;
ut64 payload_size;
char *stream_path;
ut64 stream_offset;
ut64 stream_size;
ut32 width;
ut32 height;
st32 texture_format;
bool has_texture_info;
} R2UnitySerializedObject;
typedef struct {
st32 file_index;
st64 path_id;
} R2UnitySerializedScript;
typedef struct {
char *path;
ut64 path_offset;
ut8 guid[16];
st32 type;
} R2UnitySerializedExternal;
typedef struct {
RBuffer *buf;
ut32 version;
ut32 metadata_size;
ut64 file_size;
ut64 data_offset;
ut64 header_size;
bool big_endian;
char *unity_version;
st32 target_platform;
bool enable_type_tree;
R2UnitySerializedType *types;
size_t type_count;
R2UnitySerializedObject *objects;
size_t object_count;
R2UnitySerializedScript *scripts;
size_t script_count;
R2UnitySerializedExternal *externals;
size_t external_count;
char *user_information;
} R2UnitySerializedFile;
bool r2unity_serialized_file_check(RBuffer *buf);
R2UnitySerializedFile *r2unity_serialized_file_parse(RBuffer *buf);
void r2unity_serialized_file_free(R2UnitySerializedFile *sf);
const char *r2unity_serialized_class_name(st32 class_id);
#endif
+61 -22
View File
@@ -5,7 +5,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <r2unity_config.h>
#include "lib/lib.h"
@@ -150,6 +149,18 @@ static void pj_native_result(PJ *pj, const R2UnityNativeResult *native) {
pj_kn (pj, "metadata_registration", native? native->metadata_registration_va: 0);
pj_kn (pj, "method_pointers", native? native->method_pointers_va: 0);
pj_kn (pj, "code_gen_modules", native? native->code_gen_modules_va: 0);
pj_ko (pj, "native_tables");
for (R2UnityNativeTableId i = 0; i < R2U_NATIVE_TABLE_COUNT; i++) {
const R2UnityNativeTable *table = native? &native->tables[i]: NULL;
if (!table || !table->va) {
continue;
}
pj_ko (pj, r2unity_native_table_name (i));
pj_kn (pj, "count", table->count);
pj_kn (pj, "va", table->va);
pj_end (pj);
}
pj_end (pj);
}
static void print_native_comment(const R2UnityNativeResult *native) {
@@ -265,8 +276,36 @@ static int interop_cmp(const void *a, const void *b) {
return strcmp (xn, yn);
}
static int emit_pinvokes(R2UnityMetadata *meta, const char *exe_path, bool is_json, bool is_r2, bool quiet) {
(void)exe_path;
static char *interop_flag_name(const char *prefix, const R2UnityInterop *it) {
char *name = it->image_name && *it->image_name
? r_str_newf ("%s.%s.%s", prefix, it->image_name, it->name)
: r_str_newf ("%s.%s", prefix, it->name);
if (name) {
r_name_filter (name, -1);
if (!r_name_check (name)) {
R_FREE (name);
}
}
return name;
}
static void print_interop_wrapper_flag(const char *name, ut64 wrapper_va) {
if (wrapper_va && name) {
printf ("'@0x%" PFMT64x "'f %s\n", wrapper_va, name);
printf ("'@0x%" PFMT64x "'af+ $$ %s\n", wrapper_va, name);
}
}
static void json_interop_wrapper(FILE *f, const R2UnityInterop *it) {
if (it->wrapper_va) {
fprintf (f, ",\"wrapper_va\":\"0x%" PFMT64x "\"", it->wrapper_va);
}
if (it->wrapper_index != UINT32_MAX) {
fprintf (f, ",\"wrapper_index\":%u", it->wrapper_index);
}
}
static int emit_pinvokes(R2UnityMetadata *meta, bool is_json, bool is_r2, bool quiet) {
size_t n = 0;
R2UnityInterop *items = r2unity_enumerate_pinvokes (meta, &n);
if (items && n > 1) {
@@ -309,18 +348,16 @@ static int emit_pinvokes(R2UnityMetadata *meta, const char *exe_path, bool is_js
if (!it->name) {
continue;
}
char buf[1024];
if (it->image_name && *it->image_name) {
snprintf (buf, sizeof (buf), "sym.unity.%s.%s", it->image_name, it->name);
} else {
snprintf (buf, sizeof (buf), "sym.unity.%s", it->name);
char *name = interop_flag_name ("sym.unity", it);
if (!name) {
continue;
}
r_name_filter (buf, -1);
if (it->dll_name) {
printf ("# PInvoke %s -> %s!%s\n", buf, it->dll_name, it->entry_name? it->entry_name: it->name);
printf ("# PInvoke %s -> %s!%s\n", name, it->dll_name, it->entry_name? it->entry_name: it->name);
} else {
printf ("# PInvoke %s -> <unresolved>\n", buf);
printf ("# PInvoke %s -> <unresolved>\n", name);
}
free (name);
}
} else {
if (!quiet) {
@@ -356,10 +393,12 @@ static const char *interop_kind_label(uint8_t kind) {
}
}
static int emit_reverse_pinvokes(R2UnityMetadata *meta, const char *exe_path, bool is_json, bool is_r2, bool quiet) {
(void)exe_path;
static int emit_reverse_pinvokes(R2UnityMetadata *meta, const char *exe_path, const R2UnityNativeOptions *native, bool is_json, bool is_r2, bool quiet) {
size_t n = 0;
R2UnityInterop *items = r2unity_enumerate_reverse_pinvokes (meta, &n);
if (exe_path && *exe_path) {
r2unity_enrich_reverse_pinvokes_native (meta, exe_path, native, &items, &n);
}
if (items && n > 1) {
qsort (items, n, sizeof (R2UnityInterop), interop_cmp);
}
@@ -377,6 +416,7 @@ static int emit_reverse_pinvokes(R2UnityMetadata *meta, const char *exe_path, bo
fprintf (f, "\",\"method\":\"");
json_escape (f, it->name? it->name: "");
fprintf (f, "\",\"token\":\"0x%08x\",\"flags\":\"0x%04x\",\"iflags\":\"0x%04x\",\"attribute\":\"%s\",\"confidence\":%u", it->token, it->flags, it->iflags, interop_kind_label (it->kind), (unsigned)it->confidence);
json_interop_wrapper (f, it);
fputc ('}', f);
}
fprintf (f, "]}\n");
@@ -386,14 +426,13 @@ static int emit_reverse_pinvokes(R2UnityMetadata *meta, const char *exe_path, bo
if (!it->name) {
continue;
}
char buf[1024];
if (it->image_name && *it->image_name) {
snprintf (buf, sizeof (buf), "sym.unity.reverse.%s.%s", it->image_name, it->name);
} else {
snprintf (buf, sizeof (buf), "sym.unity.reverse.%s", it->name);
char *name = interop_flag_name ("sym.unity.reverse", it);
if (!name) {
continue;
}
r_name_filter (buf, -1);
printf ("# ReversePInvoke %s [%s]\n", buf, interop_kind_label (it->kind));
printf ("# ReversePInvoke %s [%s]\n", name, interop_kind_label (it->kind));
print_interop_wrapper_flag (name, it->wrapper_va);
free (name);
}
} else {
if (!quiet) {
@@ -1083,8 +1122,8 @@ int main(int argc, char *argv[]) {
return 1;
}
int rc = reverse_pinvokes
? emit_reverse_pinvokes (meta, exe_path, json_one_line, r2_script, quiet)
: emit_pinvokes (meta, exe_path, json_one_line, r2_script, quiet);
? emit_reverse_pinvokes (meta, exe_path, &native_options, json_one_line, r2_script, quiet)
: emit_pinvokes (meta, json_one_line, r2_script, quiet);
r2unity_free_metadata (meta);
r_unref (buf);
free_symbol_overrides (&symbol_overrides);
+1199 -47
View File
File diff suppressed because it is too large. Load diff
+173 -40
View File
@@ -6,6 +6,8 @@
#include <r_lib.h>
#include "../lib/lib.h"
#define R2UNITY_NATIVE_TARGET_LIMIT 0x10000
// clang-format off
static const char *g_help_msg[] = {
"Usage:", "r2unity[-subcmd]", " Unity IL2CPP analyzer",
@@ -14,7 +16,7 @@ static const char *g_help_msg[] = {
"r2unity-c", "[*j]", "enumerate classes, inheritance, methods, and fields",
"r2unity-D", "", "auto-detect companion files from current binary path",
"r2unity-i", "[j]", "summary (metadata version, type/method counts)",
"r2unity-s", "[*j]", "apply/list managed method symbols as flags + comments",
"r2unity-s", "[*j]", "apply/list managed/native symbols as flags + comments",
"r2unity-z", "[j]", "list managed string literals",
"r2unity-P", "[*j]", "list P/Invoke (managed -> native)",
"r2unity-R", "[*j]", "list reverse-P/Invoke (native -> managed)",
@@ -163,6 +165,18 @@ static void pj_native_result(PJ *pj, const R2UnityNativeResult *result) {
pj_kn (pj, "metadata_registration", result? result->metadata_registration_va: 0);
pj_kn (pj, "method_pointers", result? result->method_pointers_va: 0);
pj_kn (pj, "code_gen_modules", result? result->code_gen_modules_va: 0);
pj_ko (pj, "native_tables");
for (R2UnityNativeTableId i = 0; i < R2U_NATIVE_TABLE_COUNT; i++) {
const R2UnityNativeTable *table = result? &result->tables[i]: NULL;
if (!table || !table->va) {
continue;
}
pj_ko (pj, r2unity_native_table_name (i));
pj_kn (pj, "count", table->count);
pj_kn (pj, "va", table->va);
pj_end (pj);
}
pj_end (pj);
}
static void print_native_result(RCore *core, const R2UnityNativeResult *result) {
@@ -175,6 +189,54 @@ static void print_native_result(RCore *core, const R2UnityNativeResult *result)
result? result->code_gen_modules_va: 0);
}
static void native_flag(RCore *core, bool print, const char *name, ut64 addr, ut32 size) {
if (print) {
r_cons_printf (core->cons, "'@0x%08"PFMT64x"'f %s %u\n", addr, name, size);
} else {
r_flag_set (core->flags, name, addr, size);
}
}
static void apply_native_tables(RCore *core, const R2UnityNativeResult *result, bool print) {
int ptr_size = result->ptr_size == 4? 4: 8;
for (R2UnityNativeTableId i = 0; i < R2U_NATIVE_TABLE_COUNT; i++) {
const R2UnityNativeTable *table = &result->tables[i];
if (!table->va) {
continue;
}
const char *name = r2unity_native_table_name (i);
ut32 size = (ut32)R_MIN ((ut64)UT32_MAX, R_MAX ((ut64)1, table->count * ptr_size));
char flag[128];
snprintf (flag, sizeof (flag), "r2unity.table.%s", name);
native_flag (core, print, flag, table->va, size);
ut64 count = R_MIN (table->count, (ut64)R2UNITY_NATIVE_TARGET_LIMIT);
if (i >= R2U_NATIVE_TABLE_INTEROP_DATA || !count) {
continue;
}
int len = (int)(count * ptr_size);
ut8 *buf = malloc (len);
if (!buf || !r_io_read_at (core->io, table->va, buf, len)) {
free (buf);
continue;
}
for (ut64 j = 0; j < count; j++) {
const ut8 *p = buf + j * ptr_size;
ut64 addr = (ptr_size == 8? r_read_le64 (p): (ut64)r_read_le32 (p)) & ~(ut64)1;
if (addr <= 0x1000 || !r_io_is_valid_offset (core->io, addr, R_PERM_X)) {
continue;
}
snprintf (flag, sizeof (flag), "sym.r2unity.%s.%" PFMT64u, name, j);
native_flag (core, print, flag, addr, 1);
if (print) {
r_cons_printf (core->cons, "'@0x%08" PFMT64x "'af+ $$ %s\n", addr, flag);
} else if (core->anal && !r_anal_get_function_at (core->anal, addr)) {
r_anal_create_function (core->anal, flag, addr, R_ANAL_FCN_TYPE_FCN, NULL);
}
}
free (buf);
}
}
/* Resolve (and on first use, cache into the eval vars) the metadata path for
* the current session. Returns a pointer owned by the RConfig, or NULL. */
static const char *resolve_metadata_path(RCore *core) {
@@ -373,26 +435,14 @@ static bool find_method_pointers(RCore *core, R2UnityMetadata *meta, const char
return ok;
}
static int type_definition_for_type_index(const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index) {
if (type_index >= 0) {
for (size_t i = 0; i < type_count; i++) {
if (types[i].byvalTypeIndex == type_index) {
return (int)i;
}
}
}
return -1;
static bool enrich_reverse_pinvokes_native(RCore *core, R2UnityMetadata *meta, const char *path, R2UnityInterop **items, size_t *count) {
R2UnityNativeOptions opts = { 0 };
native_options_from_core (core, &opts);
return path && *path && r2unity_enrich_reverse_pinvokes_native (meta, path, &opts, items, count);
}
static char *type_name_from_index(R2UnityMetadata *meta, const Il2CppTypeDefinition *types, size_t type_count, int32_t type_index, bool fallback) {
int idx = type_definition_for_type_index (types, type_count, type_index);
if (idx >= 0) {
return r2unity_type_fullname (meta, &types[idx], (size_t)idx, R2U_NAME_FALLBACK_TYPE);
}
if (fallback && type_index >= 0) {
return r_str_newf ("type_index.%d", type_index);
}
return NULL;
return r2unity_type_name_from_index (meta, types, type_count, type_index, fallback);
}
static char *field_name_or_fallback(R2UnityMetadata *meta, const Il2CppFieldDefinition *field, size_t index) {
@@ -764,6 +814,8 @@ static int cmd_symbols(RCore *core, char mode) {
Il2CppTypeDefinition *types = r2unity_get_type_definitions (meta, &type_count);
size_t method_count = 0;
Il2CppMethodDefinition *methods = r2unity_get_method_definitions (meta, &method_count);
size_t parameter_count = 0;
Il2CppParameterDefinition *parameters = r2unity_get_parameter_definitions (meta, &parameter_count);
size_t img_count = 0;
Il2CppImageDefinition *images = r2unity_get_images (meta, &img_count);
@@ -786,6 +838,9 @@ static int cmd_symbols(RCore *core, char mode) {
print_native_result (core, &native_result);
}
if (mode != 'j') {
apply_native_tables (core, &native_result, mode == '*');
}
ut64 applied = 0, listed = 0;
for (size_t j = 0; j < method_count; j++) {
Il2CppMethodDefinition *m = &methods[j];
@@ -805,12 +860,16 @@ static int cmd_symbols(RCore *core, char mode) {
if (!fullname) {
continue;
}
char *signature = r2unity_method_signature (meta, m, td, type_idx,
types, type_count, parameters, parameter_count, true);
char *return_type = r2unity_type_name_from_index (meta, types, type_count,
m->returnType, true);
char *imgname = img? r2unity_get_string (meta, img->nameIndex): NULL;
char flag_buf[1024];
if (imgname && *imgname) {
snprintf (flag_buf, sizeof (flag_buf), "sym.unity.%s.%s", imgname, fullname);
snprintf (flag_buf, sizeof (flag_buf), "sym.unity.%s.%s.%zu", imgname, fullname, j);
} else {
snprintf (flag_buf, sizeof (flag_buf), "sym.unity.%s", fullname);
snprintf (flag_buf, sizeof (flag_buf), "sym.unity.%s.%zu", fullname, j);
}
r_name_filter (flag_buf, -1);
@@ -819,6 +878,33 @@ static int cmd_symbols(RCore *core, char mode) {
if (mode == 'j') {
pj_o (pj);
pj_ks (pj, "name", fullname);
pj_kn (pj, "index", (ut64)j);
pj_hex (pj, "token", m->token, 8);
pj_string_or_null (pj, "signature", signature);
pj_string_or_null (pj, "return_type", return_type);
pj_kb (pj, "static", (m->flags & 0x0010) != 0);
pj_ka (pj, "parameters");
for (size_t k = 0; k < m->parameterCount; k++) {
st64 pi = (st64)m->parameterStart + (st64)k;
const Il2CppParameterDefinition *parameter = pi >= 0 && (ut64)pi < parameter_count
? &parameters[pi]: NULL;
char *parameter_name = parameter? r2unity_get_string (meta, parameter->nameIndex): NULL;
char *parameter_type = parameter
? r2unity_type_name_from_index (meta, types, type_count, parameter->typeIndex, true)
: NULL;
pj_o (pj);
pj_kn (pj, "index", k);
if (parameter) {
pj_ki (pj, "type_index", parameter->typeIndex);
pj_hex (pj, "token", parameter->token, 8);
}
pj_string_or_null (pj, "name", parameter_name);
pj_string_or_null (pj, "type", parameter_type);
pj_end (pj);
free (parameter_type);
free (parameter_name);
}
pj_end (pj);
pj_ks (pj, "flag", flag_buf);
if (imgname) {
pj_ks (pj, "image", imgname);
@@ -832,11 +918,11 @@ static int cmd_symbols(RCore *core, char mode) {
if (addr > 0x1000) {
r_cons_printf (core->cons, "'@0x%" PFMT64x "'f %s\n", addr, flag_buf);
if (imgname && *imgname) {
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: [%s]%s%s %s\n", addr, imgname, *attrs? " ": "", attrs, fullname);
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: [%s]%s%s %s\n", addr, imgname, *attrs? " ": "", attrs, signature? signature: fullname);
} else if (*attrs) {
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: %s %s\n", addr, attrs, fullname);
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: %s %s\n", addr, attrs, signature? signature: fullname);
} else {
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: %s\n", addr, fullname);
r_cons_printf (core->cons, "'@0x%" PFMT64x "'CCu Method: %s\n", addr, signature? signature: fullname);
}
listed++;
}
@@ -850,11 +936,11 @@ static int cmd_symbols(RCore *core, char mode) {
imgname,
*attrs? " ": "",
attrs,
fullname);
signature? signature: fullname);
} else if (*attrs) {
comment = r_str_newf ("Method: %s %s", attrs, fullname);
comment = r_str_newf ("Method: %s %s", attrs, signature? signature: fullname);
} else {
comment = r_str_newf ("Method: %s", fullname);
comment = r_str_newf ("Method: %s", signature? signature: fullname);
}
if (comment) {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, comment);
@@ -866,6 +952,8 @@ static int cmd_symbols(RCore *core, char mode) {
free (attrs);
free (imgname);
free (return_type);
free (signature);
free (fullname);
}
@@ -885,6 +973,7 @@ static int cmd_symbols(RCore *core, char mode) {
r2unity_native_result_fini (&native_result);
R_FREE (type2img);
R_FREE (images);
R_FREE (parameters);
R_FREE (methods);
R_FREE (types);
close_metadata (meta, buf);
@@ -982,6 +1071,35 @@ static const char *interop_kind_label(uint8_t kind) {
}
}
static void pj_interop_wrapper(PJ *pj, const R2UnityInterop *it) {
if (it->wrapper_va) {
pj_kn (pj, "wrapper_va", it->wrapper_va);
}
if (it->wrapper_index != UINT32_MAX) {
pj_kn (pj, "wrapper_index", it->wrapper_index);
}
}
static char *interop_flag_name(const char *prefix, const R2UnityInterop *it) {
char *name = it->image_name && *it->image_name
? r_str_newf ("%s.%s.%s", prefix, it->image_name, it->name)
: r_str_newf ("%s.%s", prefix, it->name);
if (name) {
r_name_filter (name, -1);
if (!r_name_check (name)) {
R_FREE (name);
}
}
return name;
}
static void print_interop_wrapper_flag(RCore *core, const char *name, ut64 wrapper_va) {
if (wrapper_va && name) {
r_cons_printf (core->cons, "'@0x%" PFMT64x "'f %s\n", wrapper_va, name);
r_cons_printf (core->cons, "'@0x%" PFMT64x "'af+ $$ %s\n", wrapper_va, name);
}
}
static int cmd_interop(RCore *core, bool reverse, char mode) {
RBuffer *buf = NULL;
R2UnityMetadata *meta = open_metadata (core, &buf);
@@ -992,6 +1110,12 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
R2UnityInterop *items = reverse
? r2unity_enumerate_reverse_pinvokes (meta, &n)
: r2unity_enumerate_pinvokes (meta, &n);
if (reverse) {
const char *lib = resolve_library_path (core);
if (lib) {
enrich_reverse_pinvokes_native (core, meta, lib, &items, &n);
}
}
if (items && n > 1) {
qsort (items, n, sizeof (R2UnityInterop), interop_cmp);
}
@@ -1022,6 +1146,7 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
pj_kn (pj, "flags", (ut64)it->flags);
pj_kn (pj, "iflags", (ut64)it->iflags);
pj_kn (pj, "confidence", (ut64)it->confidence);
pj_interop_wrapper (pj, it);
if (reverse) {
pj_ks (pj, "attribute", interop_kind_label (it->kind));
} else {
@@ -1039,27 +1164,35 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
pj_end (pj);
} else if (mode == '*') {
if (it->name) {
char flag_buf[1024];
const char *prefix = reverse? "sym.unity.reverse": "sym.unity";
if (it->image_name && *it->image_name) {
snprintf (flag_buf, sizeof (flag_buf), "%s.%s.%s", prefix, it->image_name, it->name);
} else {
snprintf (flag_buf, sizeof (flag_buf), "%s.%s", prefix, it->name);
char *name = interop_flag_name (prefix, it);
if (!name) {
free (attrs);
continue;
}
r_name_filter (flag_buf, -1);
if (reverse) {
r_cons_printf (core->cons, "# ReversePInvoke %s [%s]\n", flag_buf, interop_kind_label (it->kind));
r_cons_printf (core->cons, "# ReversePInvoke %s [%s]\n", name, interop_kind_label (it->kind));
} else if (it->dll_name) {
r_cons_printf (core->cons, "# PInvoke %s -> %s!%s\n", flag_buf, it->dll_name, it->entry_name? it->entry_name: it->name);
r_cons_printf (core->cons, "# PInvoke %s -> %s!%s\n", name, it->dll_name, it->entry_name? it->entry_name: it->name);
} else {
r_cons_printf (core->cons, "# PInvoke %s -> <unresolved>\n", flag_buf);
r_cons_printf (core->cons, "# PInvoke %s -> <unresolved>\n", name);
}
if (reverse) {
print_interop_wrapper_flag (core, name, it->wrapper_va);
}
free (name);
}
} else {
if (reverse) {
r_cons_printf (core->cons, "%s\t%s\t%s\t%s\t%u\n", it->image_name? it->image_name: "", it->name? it->name: "", interop_kind_label (it->kind), *attrs? attrs: "-", (unsigned)it->confidence);
} else {
r_cons_printf (core->cons, "%s\t%s\t%s\t%s\t%s\t%u\n", it->image_name? it->image_name: "", it->name? it->name: "", it->dll_name? it->dll_name: "<unresolved>", it->entry_name? it->entry_name: "<default>", *attrs? attrs: "-", (unsigned)it->confidence);
r_cons_printf (core->cons, "%s\t%s\t%s\t%s\t%s\t%u\n",
it->image_name? it->image_name: "",
it->name? it->name: "",
it->dll_name? it->dll_name: "<unresolved>",
it->entry_name? it->entry_name: "<default>",
*attrs? attrs: "-",
(unsigned)it->confidence);
}
}
free (attrs);
@@ -1110,7 +1243,7 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
/* "r2unity" alone, or "r2unity?" */
if (*rest == 0 || *rest == '?') {
r_core_cmd_help (core, g_help_msg);
r_cons_cmd_help (core->cons, g_help_msg);
return true;
}
if (*rest != '-') {
@@ -1128,7 +1261,7 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
rest++;
}
if (*rest && *rest != ' ' && *rest != '\t') {
r_core_cmd_help (core, g_help_msg);
r_cons_cmd_help (core->cons, g_help_msg);
return true;
}
}
@@ -1155,10 +1288,10 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
return cmd_sbom (core, mode) == 0;
case '?':
case 'h':
r_core_cmd_help (core, g_help_msg);
r_cons_cmd_help (core->cons, g_help_msg);
return true;
default:
r_core_cmd_help (core, g_help_msg);
r_cons_cmd_help (core->cons, g_help_msg);
return true;
}
}