mirror of
https://github.com/radareorg/r2unity.git
synced 2026-11-07 06:36:00 -10:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afb7e4368c | ||
|
|
c2f219c95a | ||
|
|
4a35664331 | ||
|
|
31cb6bce07 | ||
|
|
ae094c666a | ||
|
|
ccaa476cac | ||
|
|
0bcdb6b8aa | ||
|
|
212085e7fb | ||
|
|
aa3c6c6254 | ||
|
|
8d46d02b38 | ||
|
|
a0c7354c37 | ||
|
|
67471a35d3 | ||
|
|
12620d1557 | ||
|
|
06a21356e7 | ||
|
|
26d3db86c3 | ||
|
|
6681f1af93 | ||
|
|
08876f2aa3 | ||
|
|
60267a76e8 | ||
|
|
57cda9236e |
No files matched your search
@@ -1,5 +1,8 @@
|
||||
name: CI
|
||||
|
||||
env:
|
||||
R2V: 6.2.6
|
||||
|
||||
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@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@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
|
||||
- name: Install deps
|
||||
run: pip install meson ninja --break-system-packages
|
||||
@@ -69,7 +76,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Install radare2 from git
|
||||
id: r2
|
||||
@@ -89,7 +96,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -99,6 +106,8 @@ jobs:
|
||||
- name: Install radare2
|
||||
id: r2
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
|
||||
- name: Build Debian package
|
||||
run: make -C dist/debian SUDO=
|
||||
@@ -119,7 +128,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.12.x
|
||||
|
||||
@@ -134,6 +143,8 @@ jobs:
|
||||
- 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
|
||||
@@ -166,11 +177,47 @@ jobs:
|
||||
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 }}
|
||||
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:
|
||||
@@ -181,12 +228,13 @@ jobs:
|
||||
- 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
|
||||
|
||||
@@ -203,11 +251,18 @@ jobs:
|
||||
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 }}
|
||||
|
||||
@@ -22,3 +22,6 @@
|
||||
|
||||
# Third-party reference checkouts (radare2, il2cpp dumper, etc.)
|
||||
/third_party/
|
||||
|
||||
# r2pm binary packages
|
||||
/r2unity-*.zip
|
||||
@@ -46,10 +46,18 @@ all: $(EXEC)
|
||||
|
||||
r2 plugin: $(PLUGINS)
|
||||
|
||||
test: $(EXEC) $(PLUGINS)
|
||||
python3 test/test_import.py
|
||||
|
||||
install-plugin: $(PLUGINS)
|
||||
@[ -n "$(R2_USER_PLUGINS)" ] || (echo "r2 not found; cannot resolve R2_USER_PLUGINS"; exit 1)
|
||||
mkdir -p "$(R2_USER_PLUGINS)"
|
||||
cp -f $(PLUGINS) "$(R2_USER_PLUGINS)/"
|
||||
# Replace the inode so macOS does not reuse a stale code-signature cache.
|
||||
@for plugin in $(PLUGINS); do \
|
||||
name=$${plugin##*/}; \
|
||||
cp -f "$$plugin" "$(R2_USER_PLUGINS)/.$$name.tmp" && \
|
||||
mv -f "$(R2_USER_PLUGINS)/.$$name.tmp" "$(R2_USER_PLUGINS)/$$name" || exit 1; \
|
||||
done
|
||||
|
||||
uninstall-plugin:
|
||||
@[ -n "$(R2_USER_PLUGINS)" ] || (echo "r2 not found; cannot resolve R2_USER_PLUGINS"; exit 1)
|
||||
@@ -59,13 +67,46 @@ uninstall-plugin:
|
||||
fmt:
|
||||
clang-format-radare2 src/**/*.c
|
||||
|
||||
user-install: install-plugin
|
||||
user-install: $(EXEC) install-plugin
|
||||
mkdir -p "$(R2PM_BINDIR)"
|
||||
cp -f $(EXEC) "$(R2PM_BINDIR)"
|
||||
cp -f $(EXEC) "$(R2PM_BINDIR)/.$(EXEC).tmp"
|
||||
mv -f "$(R2PM_BINDIR)/.$(EXEC).tmp" "$(R2PM_BINDIR)/$(EXEC)"
|
||||
|
||||
user-uninstall: uninstall-plugin
|
||||
rm -f "$(R2PM_BINDIR)/$(EXEC)"
|
||||
|
||||
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
|
||||
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
|
||||
# R2PM_OS/ARCH/BITS require a recent r2pm; fall back to uname when missing
|
||||
R2PM_OS_SH = $(shell r2pm -H R2PM_OS 2>/dev/null)
|
||||
R2PM_ARCH_SH = $(shell r2pm -H R2PM_ARCH 2>/dev/null)
|
||||
R2PM_BITS_SH = $(shell r2pm -H R2PM_BITS 2>/dev/null)
|
||||
ifeq ($(R2PM_OS_SH),)
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
R2PM_OS_SH = darwin
|
||||
else
|
||||
R2PM_OS_SH = linux
|
||||
endif
|
||||
endif
|
||||
ifeq ($(R2PM_ARCH_SH),)
|
||||
ifeq ($(shell uname -m),x86_64)
|
||||
R2PM_ARCH_SH = x86
|
||||
else ifneq (,$(filter $(shell uname -m),aarch64 arm64))
|
||||
R2PM_ARCH_SH = arm
|
||||
else
|
||||
R2PM_ARCH_SH = $(shell uname -m)
|
||||
endif
|
||||
endif
|
||||
ifeq ($(R2PM_BITS_SH),)
|
||||
R2PM_BITS_SH = 64
|
||||
endif
|
||||
BINDIST_ZIP = r2unity-$(shell r2 -qv)-$(R2PM_OS_SH)-$(R2PM_ARCH_SH)-$(R2PM_BITS_SH).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)
|
||||
|
||||
@@ -88,4 +129,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 test
|
||||
@@ -93,19 +93,46 @@ After installing the plugins, open a Unity binary in r2 and use:
|
||||
|
||||
```text
|
||||
r2unity? show help
|
||||
r2unity-A import classes and method flags (same as .r2unity-c*)
|
||||
r2unity-AA also import method comments, strings, native tables and interop
|
||||
r2unity-AAA also analyze native references (aar)
|
||||
r2unity-AAAA also perform native analysis (aaa)
|
||||
r2unity-D detect and cache companion file paths
|
||||
r2unity-L open/select and map the IL2CPP native library
|
||||
r2unity-c[*j] list classes, or emit an import script / JSON
|
||||
r2unity-i[j] show metadata summary
|
||||
r2unity-s apply managed method flags/comments
|
||||
r2unity-s* print the r2 commands instead of applying them
|
||||
r2unity-z[j] list managed string literals
|
||||
r2unity-P[*j] list P/Invoke entries
|
||||
r2unity-R[*j] list reverse-P/Invoke entries
|
||||
r2unity-z[+j] list managed string literals (+ imports them)
|
||||
r2unity-P[+*j] list P/Invoke entries (+ imports flags and comments)
|
||||
r2unity-R[+*j] list reverse-P/Invoke entries (+ imports known wrappers)
|
||||
r2unity-S emit managed-assembly SBOM text summary
|
||||
r2unity-Sj emit managed-assembly CycloneDX JSON
|
||||
```
|
||||
|
||||
Use `r2unity-A` (equivalent to `.r2unity-c*`) to import classes and method
|
||||
flags. The script loads the native companion library automatically, so
|
||||
`s sym.unity.<class>.<method>` followed by `pd` shows the method's native code.
|
||||
Methods without a native implementation retain address zero and do not get
|
||||
a seekable flag.
|
||||
|
||||
The analysis levels are cumulative. `r2unity-AA` adds method signatures and
|
||||
comments, native registration tables, managed strings, P/Invoke method flags
|
||||
and comments, and known reverse-P/Invoke wrappers. It maps `global-metadata.dat`
|
||||
read-only at an unused address range named `r2unity.metadata`. Seek to
|
||||
`str.unity.<index>` to read a literal's actual bytes; these addresses refer to
|
||||
the mapped metadata, rather than runtime managed string objects. You can also
|
||||
import strings or interop separately with `r2unity-z+`, `r2unity-P+`, and
|
||||
`r2unity-R+`.
|
||||
|
||||
`r2unity-AAA` performs all of those imports and then runs `aar` to find native
|
||||
references. `r2unity-AAAA` additionally runs `aaa` for deeper native analysis.
|
||||
These two levels take longer on large binaries.
|
||||
|
||||
Set `r2unity.metadata` and `r2unity.library` manually when auto-detection is not
|
||||
enough. The `bin_r2unity` plugin also lets radare2/rabin2 treat
|
||||
enough. iOS detection checks both the app's `Data` directory and
|
||||
`Frameworks/UnityFramework.framework/Data` for managed metadata.
|
||||
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. It also recognizes loose Unity
|
||||
SerializedFile v22 inputs such as `sharedassets*.assets`:
|
||||
|
||||
+8
-4
@@ -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`
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+12
-2
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'r2unity',
|
||||
'c',
|
||||
version: '1.2.4',
|
||||
version: '1.2.9',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'warning_level=2',
|
||||
@@ -91,7 +91,7 @@ if build_plugins
|
||||
warning('r2 was not found; installing plugins under ' + r2_plugindir)
|
||||
endif
|
||||
|
||||
shared_library(
|
||||
core_plugin = shared_library(
|
||||
'core_r2unity',
|
||||
files('src/r2/core_r2unity.c') + lib_sources,
|
||||
include_directories: lib_inc,
|
||||
@@ -115,4 +115,14 @@ if build_plugins
|
||||
install_dir: r2_plugindir,
|
||||
)
|
||||
|
||||
# test_import.py is not always present (it is not committed yet on every
|
||||
# branch); only wire the test when the file exists
|
||||
fs = import('fs')
|
||||
python = find_program('python3', required: false)
|
||||
if python.found() and fs.exists('test' / 'test_import.py')
|
||||
test('r2unity-class-import', python,
|
||||
args: [files('test/test_import.py'), core_plugin],
|
||||
)
|
||||
endif
|
||||
|
||||
endif
|
||||
+17
-7
@@ -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;
|
||||
|
||||
+13
-7
@@ -156,6 +156,7 @@ typedef struct {
|
||||
R2UnityMetadata *meta;
|
||||
Il2CppImageDefinition *images;
|
||||
Il2CppTypeDefinition *types;
|
||||
Il2CppMethodDefinition *methods;
|
||||
size_t image_count;
|
||||
size_t type_count;
|
||||
size_t method_count;
|
||||
@@ -166,13 +167,14 @@ static bool method_tables_init(R2UnityMethodTables *tables, R2UnityMetadata *met
|
||||
tables->meta = meta;
|
||||
tables->images = r2unity_get_images (meta, &tables->image_count);
|
||||
tables->types = r2unity_get_type_definitions (meta, &tables->type_count);
|
||||
tables->method_count = (size_t)r2unity_metadata_section_count (meta, R2U_SEC_METHODS);
|
||||
return tables->images && tables->image_count && tables->types && tables->type_count && tables->method_count;
|
||||
tables->methods = r2unity_get_method_definitions (meta, &tables->method_count);
|
||||
return tables->images && tables->image_count && tables->types && tables->type_count && tables->methods && tables->method_count;
|
||||
}
|
||||
|
||||
static void method_tables_fini(R2UnityMethodTables *tables) {
|
||||
R_FREE (tables->images);
|
||||
R_FREE (tables->types);
|
||||
R_FREE (tables->methods);
|
||||
}
|
||||
|
||||
static int image_index_by_name(R2UnityMethodTables *tables, const char *module_name) {
|
||||
@@ -215,23 +217,27 @@ static size_t copy_image_method_table(R2UnityNativeView *view, R2UnityMethodTabl
|
||||
if (image->typeStart < 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t local = 0;
|
||||
size_t copied = 0;
|
||||
size_t start = (size_t)image->typeStart;
|
||||
size_t end = R_MIN (tables->type_count, start + image->typeCount);
|
||||
for (size_t ti = start; ti < end && local < table_count; ti++) {
|
||||
for (size_t ti = start; ti < end; ti++) {
|
||||
const Il2CppTypeDefinition *td = &tables->types[ti];
|
||||
if (td->methodStart < 0) {
|
||||
local += td->method_count;
|
||||
continue;
|
||||
}
|
||||
for (size_t k = 0; k < td->method_count && local < table_count; k++, local++) {
|
||||
for (size_t k = 0; k < td->method_count; k++) {
|
||||
size_t mi = (size_t)td->methodStart + k;
|
||||
if (mi >= tables->method_count) {
|
||||
continue;
|
||||
}
|
||||
ut32 token = tables->methods[mi].token;
|
||||
ut32 rid = token & 0x00ffffff;
|
||||
if ((token >> 24) != 0x06 || !rid || rid > table_count) {
|
||||
continue;
|
||||
}
|
||||
ut64 raw = 0;
|
||||
if (!read_ptr_at (view, table_va + (ut64)local * view->ptr_size, &raw)) {
|
||||
// CodeGenModule method pointers are indexed by the MethodDef token RID.
|
||||
if (!read_ptr_at (view, table_va + (ut64)(rid - 1) * view->ptr_size, &raw)) {
|
||||
continue;
|
||||
}
|
||||
ut64 addr = code_va_from_raw (view, raw);
|
||||
|
||||
+125
@@ -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
|
||||
? ¶meters[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);
|
||||
|
||||
@@ -130,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;
|
||||
@@ -264,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);
|
||||
|
||||
+17
-4
@@ -148,25 +148,33 @@ static bool try_ios(R2UnityPaths *p, const char *abs, const char *dir, const cha
|
||||
* Flat iOS layout: Game.app/UnityFramework (older builds)
|
||||
* Extracted app root: Game/Data plus the main Mach-O beside Data
|
||||
* Metadata: Game.app/Data/Managed/Metadata/global-metadata.dat
|
||||
* Game.app/Frameworks/UnityFramework.framework/Data/Managed/Metadata/global-metadata.dat
|
||||
* Game.app/Data/Raw/Managed/Metadata/global-metadata.dat (newer) */
|
||||
char *app_dir = NULL;
|
||||
if (r_str_endswith (dir, ".framework") && !r_str_casecmp (base, "UnityFramework")) {
|
||||
char *frameworks = r_file_dirname (dir);
|
||||
app_dir = r_file_dirname (frameworks);
|
||||
free (frameworks);
|
||||
} else if (r_str_endswith (dir, ".app")) {
|
||||
app_dir = strdup (dir);
|
||||
} else {
|
||||
app_dir = strdup (dir);
|
||||
}
|
||||
char *metadata = find_ios_metadata (app_dir);
|
||||
char *framework_dir = r_file_new (app_dir, "Frameworks", "UnityFramework.framework", NULL);
|
||||
char *metadata = find_ios_metadata (framework_dir);
|
||||
char *data_dir = metadata? r_file_new (framework_dir, "Data", NULL): NULL;
|
||||
free (framework_dir);
|
||||
if (!metadata) {
|
||||
metadata = find_ios_metadata (app_dir);
|
||||
if (metadata) {
|
||||
data_dir = r_file_new (app_dir, "Data", NULL);
|
||||
}
|
||||
}
|
||||
if (!metadata) {
|
||||
free (app_dir);
|
||||
return false;
|
||||
}
|
||||
p->platform = strdup ("ios");
|
||||
p->metadata = metadata;
|
||||
p->data_dir = r_file_new (app_dir, "Data", NULL);
|
||||
p->data_dir = data_dir;
|
||||
if (!r_str_casecmp (base, "UnityFramework")) {
|
||||
p->il2cpp_binary = strdup (abs);
|
||||
} else if (!take_if_exists (&p->il2cpp_binary,
|
||||
@@ -314,6 +322,11 @@ static char *expand_app_bundle(const char *dir) {
|
||||
* Mach-O, whose basename is project-specific. */
|
||||
static char *expand_ios_root_dir(const char *dir) {
|
||||
char *metadata = find_ios_metadata (dir);
|
||||
if (!metadata) {
|
||||
char *framework = r_file_new (dir, "Frameworks", "UnityFramework.framework", NULL);
|
||||
metadata = find_ios_metadata (framework);
|
||||
free (framework);
|
||||
}
|
||||
if (!metadata) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+2
-2
@@ -822,7 +822,7 @@ static void emit_class_r2(R2UnityMetadata *meta,
|
||||
}
|
||||
free (base);
|
||||
}
|
||||
printf ("ic+%s @ 0\n", r2klass);
|
||||
printf ("'@0x0'ic+ %s\n", r2klass);
|
||||
|
||||
int mstart = td->methodStart;
|
||||
for (int k = 0; methods && k < td->method_count && mstart >= 0 && (size_t)(mstart + k) < method_count; k++) {
|
||||
@@ -833,7 +833,7 @@ static void emit_class_r2(R2UnityMetadata *meta,
|
||||
}
|
||||
char *mname = method_name_or_fallback (meta, &methods[mi], mi);
|
||||
char *r2meth = r2_ic_name ("method", mname, mi);
|
||||
printf ("ic+%s.%s @ 0x%" PFMT64x "\n", r2klass, r2meth, addr);
|
||||
printf ("'@0x%" PFMT64x "'ic+ %s.%s\n", addr, r2klass, r2meth);
|
||||
free (r2meth);
|
||||
free (mname);
|
||||
}
|
||||
|
||||
+126
-60
@@ -189,15 +189,10 @@ static void fill_metadata_sdb(R2UnityBinObj *obj) {
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
char key[192];
|
||||
snprintf (key, sizeof (key), "sections.%s.offset", name);
|
||||
sdb_num_set (kv, key, sec.offset, 0);
|
||||
snprintf (key, sizeof (key), "sections.%s.size", name);
|
||||
sdb_num_set (kv, key, sec.size, 0);
|
||||
snprintf (key, sizeof (key), "sections.%s.count", name);
|
||||
sdb_num_set (kv, key, r2unity_metadata_section_count (meta, (R2UMetadataSectionId)i), 0);
|
||||
snprintf (key, sizeof (key), "sections.%s.entry_size", name);
|
||||
sdb_num_set (kv, key, r2unity_metadata_section_entry_size (meta, (R2UMetadataSectionId)i), 0);
|
||||
sdb_num_setf (kv, sec.offset, 0, "sections.%s.offset", name);
|
||||
sdb_num_setf (kv, sec.size, 0, "sections.%s.size", name);
|
||||
sdb_num_setf (kv, r2unity_metadata_section_count (meta, (R2UMetadataSectionId)i), 0, "sections.%s.count", name);
|
||||
sdb_num_setf (kv, r2unity_metadata_section_entry_size (meta, (R2UMetadataSectionId)i), 0, "sections.%s.entry_size", name);
|
||||
}
|
||||
sdb_num_set (kv, "counts.types", r2unity_metadata_section_count (meta, R2U_SEC_TYPE_DEFINITIONS), 0);
|
||||
sdb_num_set (kv, "counts.methods", r2unity_metadata_section_count (meta, R2U_SEC_METHODS), 0);
|
||||
@@ -225,40 +220,26 @@ static void fill_serialized_file_sdb(R2UnityBinObj *obj) {
|
||||
sdb_num_set (kv, "counts.scripts", sf->script_count, 0);
|
||||
sdb_num_set (kv, "counts.externals", sf->external_count, 0);
|
||||
for (size_t i = 0; i < sf->external_count; i++) {
|
||||
char key[128];
|
||||
snprintf (key, sizeof (key), "externals.%zu.path", i);
|
||||
sdb_set (kv, key, sf->externals[i].path, 0);
|
||||
sdb_setf (kv, sf->externals[i].path, 0, "externals.%zu.path", i);
|
||||
}
|
||||
for (size_t i = 0; i < sf->object_count; i++) {
|
||||
R2UnitySerializedObject *asset = &sf->objects[i];
|
||||
char key[128];
|
||||
snprintf (key, sizeof (key), "objects.%zu.path_id", i);
|
||||
sdb_num_set (kv, key, (ut64)asset->path_id, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.class_id", i);
|
||||
sdb_num_set (kv, key, (ut64)(st64)asset->class_id, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.class", i);
|
||||
sdb_set (kv, key, r2unity_serialized_class_name (asset->class_id), 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.offset", i);
|
||||
sdb_num_set (kv, key, asset->offset, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.size", i);
|
||||
sdb_num_set (kv, key, asset->size, 0);
|
||||
sdb_num_setf (kv, (ut64)asset->path_id, 0, "objects.%zu.path_id", i);
|
||||
sdb_num_setf (kv, (ut64)(st64)asset->class_id, 0, "objects.%zu.class_id", i);
|
||||
sdb_setf (kv, r2unity_serialized_class_name (asset->class_id), 0, "objects.%zu.class", i);
|
||||
sdb_num_setf (kv, asset->offset, 0, "objects.%zu.offset", i);
|
||||
sdb_num_setf (kv, asset->size, 0, "objects.%zu.size", i);
|
||||
if (asset->name) {
|
||||
snprintf (key, sizeof (key), "objects.%zu.name", i);
|
||||
sdb_set (kv, key, asset->name, 0);
|
||||
sdb_setf (kv, asset->name, 0, "objects.%zu.name", i);
|
||||
}
|
||||
if (asset->payload_size) {
|
||||
snprintf (key, sizeof (key), "objects.%zu.payload.offset", i);
|
||||
sdb_num_set (kv, key, asset->payload_offset, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.payload.size", i);
|
||||
sdb_num_set (kv, key, asset->payload_size, 0);
|
||||
sdb_num_setf (kv, asset->payload_offset, 0, "objects.%zu.payload.offset", i);
|
||||
sdb_num_setf (kv, asset->payload_size, 0, "objects.%zu.payload.size", i);
|
||||
}
|
||||
if (asset->stream_path) {
|
||||
snprintf (key, sizeof (key), "objects.%zu.stream.path", i);
|
||||
sdb_set (kv, key, asset->stream_path, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.stream.offset", i);
|
||||
sdb_num_set (kv, key, asset->stream_offset, 0);
|
||||
snprintf (key, sizeof (key), "objects.%zu.stream.size", i);
|
||||
sdb_num_set (kv, key, asset->stream_size, 0);
|
||||
sdb_setf (kv, asset->stream_path, 0, "objects.%zu.stream.path", i);
|
||||
sdb_num_setf (kv, asset->stream_offset, 0, "objects.%zu.stream.offset", i);
|
||||
sdb_num_setf (kv, asset->stream_size, 0, "objects.%zu.stream.size", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,23 +259,15 @@ static void fill_bgdatabase_sdb(R2UnityBinObj *obj) {
|
||||
sdb_num_set (kv, "counts.strings", db->string_count, 0);
|
||||
for (size_t i = 0; i < db->addon_count; i++) {
|
||||
R2UnityBGDatabaseAddon *addon = &db->addons[i];
|
||||
char key[128];
|
||||
snprintf (key, sizeof (key), "addons.%zu.type", i);
|
||||
sdb_set (kv, key, addon->type, 0);
|
||||
snprintf (key, sizeof (key), "addons.%zu.payload.offset", i);
|
||||
sdb_num_set (kv, key, addon->payload_offset, 0);
|
||||
snprintf (key, sizeof (key), "addons.%zu.payload.size", i);
|
||||
sdb_num_set (kv, key, addon->payload_size, 0);
|
||||
sdb_setf (kv, addon->type, 0, "addons.%zu.type", i);
|
||||
sdb_num_setf (kv, addon->payload_offset, 0, "addons.%zu.payload.offset", i);
|
||||
sdb_num_setf (kv, addon->payload_size, 0, "addons.%zu.payload.size", i);
|
||||
}
|
||||
for (size_t i = 0; i < db->table_count; i++) {
|
||||
R2UnityBGDatabaseTable *table = &db->tables[i];
|
||||
char key[128];
|
||||
snprintf (key, sizeof (key), "tables.%zu.offset", i);
|
||||
sdb_num_set (kv, key, table->offset, 0);
|
||||
snprintf (key, sizeof (key), "tables.%zu.size", i);
|
||||
sdb_num_set (kv, key, table->size, 0);
|
||||
snprintf (key, sizeof (key), "tables.%zu.fields", i);
|
||||
sdb_num_set (kv, key, table->field_count, 0);
|
||||
sdb_num_setf (kv, table->offset, 0, "tables.%zu.offset", i);
|
||||
sdb_num_setf (kv, table->size, 0, "tables.%zu.size", i);
|
||||
sdb_num_setf (kv, table->field_count, 0, "tables.%zu.fields", i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,13 +281,9 @@ static void fill_resource_pack_sdb(R2UnityBinObj *obj) {
|
||||
sdb_num_set (kv, "counts.resources", pack->entry_count, 0);
|
||||
for (size_t i = 0; i < pack->entry_count; i++) {
|
||||
R2UnityResourcePackEntry *entry = &pack->entries[i];
|
||||
char key[128];
|
||||
snprintf (key, sizeof (key), "resources.%zu.name", i);
|
||||
sdb_set (kv, key, entry->name, 0);
|
||||
snprintf (key, sizeof (key), "resources.%zu.offset", i);
|
||||
sdb_num_set (kv, key, entry->payload_offset, 0);
|
||||
snprintf (key, sizeof (key), "resources.%zu.payload_size", i);
|
||||
sdb_num_set (kv, key, entry->payload_size, 0);
|
||||
sdb_setf (kv, entry->name, 0, "resources.%zu.name", i);
|
||||
sdb_num_setf (kv, entry->payload_offset, 0, "resources.%zu.offset", i);
|
||||
sdb_num_setf (kv, entry->payload_size, 0, "resources.%zu.payload_size", i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,17 +804,32 @@ static RBinSymbol *new_symbol(const char *name, ut64 paddr, ut64 size, const cha
|
||||
if (!sym) {
|
||||
return NULL;
|
||||
}
|
||||
#if R2_ABIVERSION >= 138
|
||||
sym->attr.size = (ut32)R_MIN (size, UT32_MAX);
|
||||
sym->attr.flags = attr;
|
||||
sym->attr.lang = R_BIN_LANG_NONE;
|
||||
#else
|
||||
sym->size = (ut32)R_MIN (size, UT32_MAX);
|
||||
sym->type = type;
|
||||
sym->attr = attr;
|
||||
sym->ordinal = ordinal;
|
||||
sym->lang = R_BIN_LANG_NONE;
|
||||
#endif
|
||||
sym->type = type;
|
||||
sym->ordinal = ordinal;
|
||||
if (classname) {
|
||||
sym->classname = strdup (classname);
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
static void set_symbol_signature(RBinSymbol *sym, const char *name, const char *signature) {
|
||||
if (!sym || !sym->name || !name || !signature) {
|
||||
return;
|
||||
}
|
||||
free (sym->name->name);
|
||||
sym->name->name = strdup (name);
|
||||
r_bin_name_update (sym->name, signature);
|
||||
}
|
||||
|
||||
#if R2UNITY_BIN_VEC_ABI
|
||||
typedef RVecRBinSymbol R2UnitySymbolSink;
|
||||
|
||||
@@ -1016,6 +1000,8 @@ static bool symbols_fill(RBinFile *bf, R2UnitySymbolSink *ret) {
|
||||
|
||||
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, ¶meter_count);
|
||||
ut64 method_entry = r2unity_metadata_section_entry_size (meta, R2U_SEC_METHODS);
|
||||
Il2CppMetadataSection method_sec;
|
||||
r2unity_metadata_section (meta, R2U_SEC_METHODS, &method_sec);
|
||||
@@ -1036,12 +1022,29 @@ static bool symbols_fill(RBinFile *bf, R2UnitySymbolSink *ret) {
|
||||
classname = r2unity_type_fullname (meta, td, type_idx, R2U_NAME_FALLBACK_TYPE);
|
||||
}
|
||||
RBinSymbol *sym = new_symbol (name, method_sec.offset + i * method_entry, method_entry, R_BIN_TYPE_METH_STR, method_attr (methods[i].flags, mn), classname, ordinal++);
|
||||
if (sym) {
|
||||
char *signature = r2unity_method_signature (meta, &methods[i], td, type_idx,
|
||||
types, type_count, parameters, parameter_count, true);
|
||||
set_symbol_signature (sym, name, signature);
|
||||
free (signature);
|
||||
sym->rtype = r2unity_type_name_from_index (meta, types, type_count,
|
||||
methods[i].returnType, true);
|
||||
#if R2_ABIVERSION >= 138
|
||||
sym->attr.lang = R_BIN_LANG_CIL;
|
||||
sym->arg_count = methods[i].parameterCount
|
||||
+ ((methods[i].flags & 0x0010)? 0: 1);
|
||||
sym->cc_arg_count = sym->arg_count;
|
||||
sym->ret_count = sym->rtype && !strcmp (sym->rtype, "void")? 0: 1;
|
||||
sym->arg_prefix = "a";
|
||||
#endif
|
||||
}
|
||||
append_symbol (ret, sym);
|
||||
free (classname);
|
||||
free (mn);
|
||||
free (name);
|
||||
}
|
||||
|
||||
R_FREE (parameters);
|
||||
R_FREE (methods);
|
||||
R_FREE (types);
|
||||
R_FREE (asms);
|
||||
@@ -1087,7 +1090,11 @@ static RList *classes(RBinFile *bf) {
|
||||
if (klass) {
|
||||
klass->index = (int)i;
|
||||
klass->addr = type->offset;
|
||||
#if R2_ABIVERSION >= 138
|
||||
klass->attr.lang = R_BIN_LANG_NONE;
|
||||
#else
|
||||
klass->lang = R_BIN_LANG_NONE;
|
||||
#endif
|
||||
r_list_append (ret, klass);
|
||||
}
|
||||
free (name);
|
||||
@@ -1100,7 +1107,11 @@ static RList *classes(RBinFile *bf) {
|
||||
RBinClass *klass = r_bin_class_new (name, NULL, R_BIN_ATTR_NONE);
|
||||
klass->index = (int)i;
|
||||
klass->addr = obj->bgdb->tables[i].offset;
|
||||
#if R2_ABIVERSION >= 138
|
||||
klass->attr.lang = R_BIN_LANG_NONE;
|
||||
#else
|
||||
klass->lang = R_BIN_LANG_NONE;
|
||||
#endif
|
||||
r_list_append (ret, klass);
|
||||
free (name);
|
||||
}
|
||||
@@ -1115,6 +1126,10 @@ static RList *classes(RBinFile *bf) {
|
||||
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, ¶meter_count);
|
||||
size_t field_count = 0;
|
||||
Il2CppFieldDefinition *fields = r2unity_get_field_definitions (meta, &field_count);
|
||||
Il2CppMetadataSection type_sec;
|
||||
Il2CppMetadataSection method_sec;
|
||||
r2unity_metadata_section (meta, R2U_SEC_TYPE_DEFINITIONS, &type_sec);
|
||||
@@ -1125,16 +1140,48 @@ static RList *classes(RBinFile *bf) {
|
||||
for (size_t i = 0; types && i < type_count; i++) {
|
||||
char *ns = get_string (obj, types[i].namespaceIndex);
|
||||
char *tn = get_string (obj, types[i].nameIndex);
|
||||
char *base = r2unity_type_name_from_index (meta, types, type_count,
|
||||
types[i].parentIndex, false);
|
||||
char *fallback = NULL;
|
||||
if (!tn || !*tn) {
|
||||
fallback = r_str_newf ("type.%zu", i);
|
||||
}
|
||||
RBinClass *klass = r_bin_class_new ((tn && *tn)? tn: fallback, NULL, type_attr (types[i].flags));
|
||||
RBinClass *klass = r_bin_class_new ((tn && *tn)? tn: fallback, base, type_attr (types[i].flags));
|
||||
klass->index = (int)i;
|
||||
klass->addr = type_sec.offset + i * type_entry;
|
||||
klass->lang = R_BIN_LANG_NONE;
|
||||
#if R2_ABIVERSION >= 138
|
||||
klass->attr.lang = R_BIN_LANG_CIL;
|
||||
#else
|
||||
klass->lang = R_BIN_LANG_CIL;
|
||||
#endif
|
||||
if (ns && *ns) {
|
||||
#if R2_ABIVERSION >= 138
|
||||
klass->attr.ns = strdup (ns);
|
||||
#else
|
||||
klass->ns = strdup (ns);
|
||||
#endif
|
||||
}
|
||||
int field_start = types[i].fieldStart;
|
||||
int fields_in_type = types[i].field_count;
|
||||
for (int k = 0; fields && k < fields_in_type && field_start >= 0
|
||||
&& (size_t)(field_start + k) < field_count; k++) {
|
||||
size_t fi = (size_t)(field_start + k);
|
||||
char *field_name = get_string (obj, fields[fi].nameIndex);
|
||||
char *field_type = r2unity_type_name_from_index (meta, types, type_count,
|
||||
fields[fi].typeIndex, true);
|
||||
#if R2UNITY_BIN_VEC_ABI
|
||||
RBinField *field = RVecRBinField_emplace_back (&klass->fields);
|
||||
if (field) {
|
||||
field->name = r_bin_name_new (R_STR_ISNOTEMPTY (field_name)? field_name: "field");
|
||||
field->type = r_bin_name_new (R_STR_ISNOTEMPTY (field_type)? field_type: "unknown");
|
||||
#if R2_ABIVERSION >= 138
|
||||
field->attr.kind = R_BIN_FIELD_KIND_FIELD;
|
||||
field->attr.lang = R_BIN_LANG_CIL;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
free (field_type);
|
||||
free (field_name);
|
||||
}
|
||||
int start = types[i].methodStart;
|
||||
int count = types[i].method_count;
|
||||
@@ -1149,16 +1196,35 @@ static RList *classes(RBinFile *bf) {
|
||||
char *mname = r2unity_method_fullname (meta, &methods[mi], td, type_idx, R2U_NAME_WITH_PARAMS | R2U_NAME_FALLBACK_TYPE);
|
||||
char *raw = get_string (obj, methods[mi].nameIndex);
|
||||
RBinSymbol *sym = new_symbol (mname? mname: raw, method_sec.offset + mi * method_entry, method_entry, R_BIN_TYPE_METH_STR, method_attr (methods[mi].flags, raw), NULL, (int)mi);
|
||||
if (sym) {
|
||||
char *signature = r2unity_method_signature (meta, &methods[mi], td, type_idx,
|
||||
types, type_count, parameters, parameter_count, true);
|
||||
set_symbol_signature (sym, mname? mname: raw, signature);
|
||||
free (signature);
|
||||
sym->rtype = r2unity_type_name_from_index (meta, types, type_count,
|
||||
methods[mi].returnType, true);
|
||||
#if R2_ABIVERSION >= 138
|
||||
sym->attr.lang = R_BIN_LANG_CIL;
|
||||
sym->arg_count = methods[mi].parameterCount
|
||||
+ ((methods[mi].flags & 0x0010)? 0: 1);
|
||||
sym->cc_arg_count = sym->arg_count;
|
||||
sym->ret_count = sym->rtype && !strcmp (sym->rtype, "void")? 0: 1;
|
||||
sym->arg_prefix = "a";
|
||||
#endif
|
||||
}
|
||||
append_class_method (klass, sym);
|
||||
free (raw);
|
||||
free (mname);
|
||||
}
|
||||
r_list_append (ret, klass);
|
||||
free (base);
|
||||
free (fallback);
|
||||
free (tn);
|
||||
free (ns);
|
||||
}
|
||||
|
||||
R_FREE (fields);
|
||||
R_FREE (parameters);
|
||||
R_FREE (methods);
|
||||
R_FREE (types);
|
||||
return ret;
|
||||
|
||||
+251
-50
@@ -13,13 +13,18 @@ static const char *g_help_msg[] = {
|
||||
"Usage:", "r2unity[-subcmd]", " Unity IL2CPP analyzer",
|
||||
"r2unity", "", "show this help",
|
||||
"r2unity?", "", "show this help",
|
||||
"r2unity-A", "", "import classes and method flags (same as .r2unity-c*)",
|
||||
"r2unity-AA", "", "also import method comments, strings, native tables and interop",
|
||||
"r2unity-AAA", "", "also analyze native references (aar)",
|
||||
"r2unity-AAAA", "", "also perform native analysis (aaa)",
|
||||
"r2unity-c", "[*j]", "enumerate classes, inheritance, methods, and fields",
|
||||
"r2unity-D", "", "auto-detect companion files from current binary path",
|
||||
"r2unity-L", "", "open/select and map the IL2CPP native library",
|
||||
"r2unity-i", "[j]", "summary (metadata version, type/method counts)",
|
||||
"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)",
|
||||
"r2unity-z", "[+j]", "list managed string literals (+ imports flags and string metadata)",
|
||||
"r2unity-P", "[+*j]", "list P/Invoke (managed -> native, + imports flags and comments)",
|
||||
"r2unity-R", "[+*j]", "list reverse-P/Invoke (native -> managed, + imports wrappers)",
|
||||
"r2unity-S", "[j]", "emit managed-assembly SBOM (text or JSON)", "Variables:", "", "",
|
||||
"r2unity.metadata", "", "path to global-metadata.dat",
|
||||
"r2unity.library", "", "path to IL2CPP native library",
|
||||
@@ -287,6 +292,28 @@ static const char *resolve_library_path(RCore *core) {
|
||||
return R_STR_ISNOTEMPTY (v)? v: NULL;
|
||||
}
|
||||
|
||||
static int cmd_load_library(RCore *core) {
|
||||
const char *path = resolve_library_path (core);
|
||||
if (!path) {
|
||||
R_LOG_ERROR ("r2unity.library is not set. Run r2unity-D or set it manually");
|
||||
return 1;
|
||||
}
|
||||
RBinFile *bf = r_bin_file_find_by_name (core->bin, path);
|
||||
if (bf) {
|
||||
if (!r_core_bin_set_by_fd (core, bf->fd) || !r_io_use_fd (core->io, bf->fd)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (!current_binary_matches_path (core, path)) {
|
||||
RIODesc *desc = r_core_file_open (core, path, R_PERM_R, UT64_MAX);
|
||||
if (!desc || !r_core_bin_load (core, path, UT64_MAX)) {
|
||||
R_LOG_ERROR ("cannot load IL2CPP native library: %s", path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
r_core_block_read (core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static R2UnityMetadata *open_metadata(RCore *core, RBuffer **out_buf) {
|
||||
const char *path = resolve_metadata_path (core);
|
||||
if (!path) {
|
||||
@@ -334,6 +361,7 @@ static int cmd_detect(RCore *core, bool as_json) {
|
||||
}
|
||||
R2UnityPaths *p = r2unity_detect_paths (bin);
|
||||
if (!p) {
|
||||
R_LOG_WARN ("could not find global-metadata.dat companion for %s", bin);
|
||||
if (as_json) {
|
||||
PJ *pj = pj_new ();
|
||||
pj_o (pj);
|
||||
@@ -342,8 +370,6 @@ static int cmd_detect(RCore *core, bool as_json) {
|
||||
pj_end (pj);
|
||||
r_cons_println (core->cons, pj_string (pj));
|
||||
pj_free (pj);
|
||||
} else {
|
||||
R_LOG_ERROR ("could not detect Unity IL2CPP layout from %s", bin);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -352,6 +378,8 @@ static int cmd_detect(RCore *core, bool as_json) {
|
||||
}
|
||||
if (p->il2cpp_binary) {
|
||||
r_config_set (core->config, "r2unity.library", p->il2cpp_binary);
|
||||
} else {
|
||||
R_LOG_WARN ("could not find IL2CPP native library companion for %s", bin);
|
||||
}
|
||||
if (as_json) {
|
||||
PJ *pj = pj_new ();
|
||||
@@ -441,26 +469,8 @@ static bool enrich_reverse_pinvokes_native(RCore *core, R2UnityMetadata *meta, c
|
||||
return path && *path && r2unity_enrich_reverse_pinvokes_native (meta, path, &opts, items, count);
|
||||
}
|
||||
|
||||
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 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) {
|
||||
@@ -685,10 +695,10 @@ static void emit_class_r2(RCore *core,
|
||||
} else {
|
||||
r_cons_printf (core->cons, "# class %s\n", name? name: r2klass);
|
||||
}
|
||||
r_cons_printf (core->cons, "ic+%s @ 0\n", r2klass);
|
||||
r_cons_printf (core->cons, "'@0x0'ic+ %s\n", r2klass);
|
||||
if (base) {
|
||||
char *r2base = r2_ic_name ("type", base, 0, false);
|
||||
r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2base);
|
||||
r_cons_printf (core->cons, "'@0x0'ic+ %s:%s\n", r2klass, r2base);
|
||||
free (r2base);
|
||||
}
|
||||
free (base);
|
||||
@@ -697,7 +707,7 @@ static void emit_class_r2(RCore *core,
|
||||
char *iname = type_name_from_index (meta, types, type_count, interfaces[td->interfacesStart + k], false);
|
||||
if (iname) {
|
||||
char *r2iface = r2_ic_name ("type", iname, 0, false);
|
||||
r_cons_printf (core->cons, "ic+%s:%s @ 0\n", r2klass, r2iface);
|
||||
r_cons_printf (core->cons, "'@0x0'ic+ %s:%s\n", r2klass, r2iface);
|
||||
free (r2iface);
|
||||
}
|
||||
free (iname);
|
||||
@@ -711,7 +721,10 @@ static void emit_class_r2(RCore *core,
|
||||
addr = addr > 0x1000? addr: 0;
|
||||
char *mname = method_name_or_fallback (meta, &methods[mi], mi);
|
||||
char *r2meth = r2_ic_name ("method", mname, mi, true);
|
||||
r_cons_printf (core->cons, "ic+%s.%s @ 0x%" PFMT64x "\n", r2klass, r2meth, addr);
|
||||
r_cons_printf (core->cons, "'@0x%" PFMT64x "'ic+ %s.%s\n", addr, r2klass, r2meth);
|
||||
if (addr) {
|
||||
r_cons_printf (core->cons, "'@0x%" PFMT64x "'f sym.unity.%s.%s 1\n", addr, r2klass, r2meth);
|
||||
}
|
||||
free (r2meth);
|
||||
free (mname);
|
||||
}
|
||||
@@ -722,7 +735,7 @@ static void emit_class_r2(RCore *core,
|
||||
char *fname = field_name_or_fallback (meta, &fields[fi], fi);
|
||||
char *r2field = r2_ic_name ("field", fname, fi, false);
|
||||
char *ftype = type_name_from_index (meta, types, type_count, fields[fi].typeIndex, true);
|
||||
r_cons_printf (core->cons, "ic+%s..%s %s @ 0\n",
|
||||
r_cons_printf (core->cons, "'@0x0'ic+ %s..%s %s\n",
|
||||
r2klass,
|
||||
r2field,
|
||||
ftype? ftype: "unknown");
|
||||
@@ -787,6 +800,9 @@ static int cmd_classes(RCore *core, char mode) {
|
||||
r_cons_println (core->cons, "# r2 script generated by r2unity-c");
|
||||
r_cons_printf (core->cons, "# Input file: %s\n", metadata_path && *metadata_path? metadata_path: "-");
|
||||
print_native_result (core, &native_result);
|
||||
if (has_ptrs) {
|
||||
r_cons_println (core->cons, "r2unity-L");
|
||||
}
|
||||
if (!has_ptrs) {
|
||||
r_cons_println (core->cons, "# Method addresses default to 0; run r2unity-D or set r2unity.library to recover native addresses.");
|
||||
}
|
||||
@@ -823,6 +839,9 @@ static int cmd_symbols(RCore *core, char mode) {
|
||||
R_LOG_ERROR ("r2unity.library is not set. Run r2unity-D or set it manually");
|
||||
return 1;
|
||||
}
|
||||
if (!mode && cmd_load_library (core)) {
|
||||
return 1;
|
||||
}
|
||||
RBuffer *buf = NULL;
|
||||
R2UnityMetadata *meta = open_metadata (core, &buf);
|
||||
if (!meta) {
|
||||
@@ -832,6 +851,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, ¶meter_count);
|
||||
size_t img_count = 0;
|
||||
Il2CppImageDefinition *images = r2unity_get_images (meta, &img_count);
|
||||
|
||||
@@ -852,6 +873,9 @@ static int cmd_symbols(RCore *core, char mode) {
|
||||
pj_ka (pj, "methods");
|
||||
} else if (mode == '*') {
|
||||
print_native_result (core, &native_result);
|
||||
if (has_ptrs) {
|
||||
r_cons_println (core->cons, "r2unity-L");
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != 'j') {
|
||||
@@ -876,12 +900,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);
|
||||
|
||||
@@ -890,6 +918,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
|
||||
? ¶meters[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);
|
||||
@@ -903,11 +958,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++;
|
||||
}
|
||||
@@ -921,11 +976,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);
|
||||
@@ -937,6 +992,8 @@ static int cmd_symbols(RCore *core, char mode) {
|
||||
|
||||
free (attrs);
|
||||
free (imgname);
|
||||
free (return_type);
|
||||
free (signature);
|
||||
free (fullname);
|
||||
}
|
||||
|
||||
@@ -956,6 +1013,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);
|
||||
@@ -963,6 +1021,46 @@ static int cmd_symbols(RCore *core, char mode) {
|
||||
}
|
||||
|
||||
/* ---------- r2unity-z (string literals) ---------- */
|
||||
static bool map_metadata(RCore *core, ut64 size, ut64 *base) {
|
||||
const char *path = resolve_metadata_path (core);
|
||||
RIODesc *desc = r_io_desc_get_byuri (core->io, path);
|
||||
if (desc) {
|
||||
RList *maps = r_io_map_get_by_fd (core->io, desc->fd);
|
||||
RListIter *iter;
|
||||
RIOMap *map;
|
||||
r_list_foreach (maps, iter, map) {
|
||||
if (!map->delta && r_itv_size (map->itv) >= size
|
||||
&& r_io_map_get_at (core->io, r_io_map_begin (map)) == map) {
|
||||
*base = r_io_map_begin (map);
|
||||
r_list_free (maps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
r_list_free (maps);
|
||||
}
|
||||
*base = 0;
|
||||
if (!r_io_map_locate (core->io, base, size, 0x1000)) {
|
||||
R_LOG_ERROR ("cannot find space to map metadata");
|
||||
return false;
|
||||
}
|
||||
int fd = r_io_fd_get_current (core->io);
|
||||
bool opened = !desc;
|
||||
if (opened) {
|
||||
desc = r_io_open_nomap (core->io, path, R_PERM_R, 0);
|
||||
r_io_use_fd (core->io, fd);
|
||||
}
|
||||
RIOMap *map = desc? r_io_map_add (core->io, desc->fd, R_PERM_R, 0, *base, size): NULL;
|
||||
if (!map) {
|
||||
if (opened && desc) {
|
||||
r_io_desc_close (desc);
|
||||
}
|
||||
R_LOG_ERROR ("cannot map metadata: %s", path);
|
||||
return false;
|
||||
}
|
||||
r_io_map_set_name (map, "r2unity.metadata");
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cmd_strings(RCore *core, char mode) {
|
||||
RBuffer *buf = NULL;
|
||||
R2UnityMetadata *meta = open_metadata (core, &buf);
|
||||
@@ -972,9 +1070,20 @@ static int cmd_strings(RCore *core, char mode) {
|
||||
size_t count = 0;
|
||||
Il2CppStringLiteral *lits = r2unity_get_string_literals (meta, &count);
|
||||
if (!lits) {
|
||||
R_LOG_ERROR ("no string literals found");
|
||||
if (mode != '+') {
|
||||
R_LOG_ERROR ("no string literals found");
|
||||
}
|
||||
close_metadata (meta, buf);
|
||||
return 1;
|
||||
return mode == '+'? 0: 1;
|
||||
}
|
||||
ut64 base = 0;
|
||||
if (mode == '+') {
|
||||
if (!map_metadata (core, r_buf_size (buf), &base)) {
|
||||
R_FREE (lits);
|
||||
close_metadata (meta, buf);
|
||||
return 1;
|
||||
}
|
||||
r_flag_set (core->flags, "r2unity.metadata", base, r_buf_size (buf));
|
||||
}
|
||||
PJ *pj = NULL;
|
||||
if (mode == 'j') {
|
||||
@@ -983,21 +1092,35 @@ static int cmd_strings(RCore *core, char mode) {
|
||||
pj_kb (pj, "ok", true);
|
||||
pj_kn (pj, "count", (ut64)count);
|
||||
pj_ka (pj, "strings");
|
||||
} else {
|
||||
} else if (mode != '+') {
|
||||
r_cons_printf (core->cons, "# managed string literals (count=%zu)\n", count);
|
||||
r_cons_printf (core->cons, "# idx\tdata_off\tlen\ttext\n");
|
||||
}
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ut8 *bytes = NULL;
|
||||
size_t len = 0;
|
||||
ut32 data_off = meta->stringLiteralDataOffset + lits[i].dataIndex;
|
||||
if (!r2unity_read_string_literal (meta, &lits[i], &bytes, &len)) {
|
||||
if (mode != 'j') {
|
||||
r_cons_printf (core->cons, "%zu\t0x%x\t%u\t<invalid>\n", i, data_off, lits[i].length);
|
||||
ut64 data_off = (ut64)meta->stringLiteralDataOffset + lits[i].dataIndex;
|
||||
if (lits[i].dataIndex > (ut64)meta->stringLiteralDataSize
|
||||
|| !r2unity_read_string_literal (meta, &lits[i], &bytes, &len)) {
|
||||
if (mode != 'j' && mode != '+') {
|
||||
r_cons_printf (core->cons, "%zu\t0x%" PFMT64x "\t%u\t<invalid>\n", i, data_off, lits[i].length);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (mode == 'j') {
|
||||
if (mode == '+') {
|
||||
char flag[64];
|
||||
snprintf (flag, sizeof (flag), "str.unity.%zu", i);
|
||||
ut64 addr = base + data_off;
|
||||
r_flag_set (core->flags, flag, addr, len);
|
||||
if (len && len <= INT_MAX) {
|
||||
char *text = r_str_ndup ((const char *)bytes, (int)len);
|
||||
if (text) {
|
||||
r_meta_set_with_subtype (core->anal, R_META_TYPE_STRING,
|
||||
R_STRING_ENC_UTF8, addr, len, text);
|
||||
free (text);
|
||||
}
|
||||
}
|
||||
} else if (mode == 'j') {
|
||||
pj_o (pj);
|
||||
pj_kn (pj, "idx", (ut64)i);
|
||||
pj_kn (pj, "data_off", (ut64)data_off);
|
||||
@@ -1013,7 +1136,7 @@ static int cmd_strings(RCore *core, char mode) {
|
||||
} else {
|
||||
char *text = r_str_ndup ((const char *)bytes, (int)len);
|
||||
char *escaped = text? r_str_escape (text): NULL;
|
||||
r_cons_printf (core->cons, "%zu\t0x%x\t%u\t\"%s\"\n", i, data_off, lits[i].length, escaped? escaped: "");
|
||||
r_cons_printf (core->cons, "%zu\t0x%" PFMT64x "\t%u\t\"%s\"\n", i, data_off, lits[i].length, escaped? escaped: "");
|
||||
free (escaped);
|
||||
free (text);
|
||||
}
|
||||
@@ -1082,7 +1205,48 @@ static void print_interop_wrapper_flag(RCore *core, const char *name, ut64 wrapp
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_interop(RCore *core, const R2UnityInterop *it, bool reverse, ut64 addr) {
|
||||
if (!addr || !it->name) {
|
||||
return;
|
||||
}
|
||||
char *name = interop_flag_name (reverse? "sym.unity.reverse": "sym.unity.pinvoke", it);
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
if (!reverse) {
|
||||
char *indexed = r_str_newf ("%s.%d", name, it->method_index);
|
||||
free (name);
|
||||
name = indexed;
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
r_flag_set (core->flags, name, addr, 1);
|
||||
if (reverse && !r_anal_get_function_at (core->anal, addr)) {
|
||||
r_anal_create_function (core->anal, name, addr, R_ANAL_FCN_TYPE_FCN, NULL);
|
||||
}
|
||||
char *comment = reverse
|
||||
? r_str_newf ("ReversePInvoke %s [%s]", it->name, interop_kind_label (it->kind))
|
||||
: it->dll_name
|
||||
? r_str_newf ("PInvoke %s -> %s!%s", it->name, it->dll_name,
|
||||
it->entry_name? it->entry_name: it->name)
|
||||
: r_str_newf ("PInvoke %s -> <unresolved>", it->name);
|
||||
const char *previous = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
|
||||
if (comment && (!previous || !strstr (previous, comment))) {
|
||||
char *combined = previous? r_str_newf ("%s\n%s", previous, comment): strdup (comment);
|
||||
if (combined) {
|
||||
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, combined);
|
||||
free (combined);
|
||||
}
|
||||
}
|
||||
free (comment);
|
||||
free (name);
|
||||
}
|
||||
|
||||
static int cmd_interop(RCore *core, bool reverse, char mode) {
|
||||
if (mode == '+' && cmd_load_library (core)) {
|
||||
return 1;
|
||||
}
|
||||
RBuffer *buf = NULL;
|
||||
R2UnityMetadata *meta = open_metadata (core, &buf);
|
||||
if (!meta) {
|
||||
@@ -1092,6 +1256,11 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
|
||||
R2UnityInterop *items = reverse
|
||||
? r2unity_enumerate_reverse_pinvokes (meta, &n)
|
||||
: r2unity_enumerate_pinvokes (meta, &n);
|
||||
R2UnityNativeResult native_result = { 0 };
|
||||
size_t method_count = r2unity_metadata_section_count (meta, R2U_SEC_METHODS);
|
||||
if (!reverse && mode == '+' && n) {
|
||||
find_method_pointers (core, meta, resolve_library_path (core), &native_result);
|
||||
}
|
||||
if (reverse) {
|
||||
const char *lib = resolve_library_path (core);
|
||||
if (lib) {
|
||||
@@ -1144,6 +1313,13 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
|
||||
}
|
||||
}
|
||||
pj_end (pj);
|
||||
} else if (mode == '+') {
|
||||
ut64 addr = reverse? it->wrapper_va: 0;
|
||||
if (!reverse && native_result.method_ptrs && it->method_index >= 0
|
||||
&& (size_t)it->method_index < method_count) {
|
||||
addr = native_result.method_ptrs[it->method_index];
|
||||
}
|
||||
apply_interop (core, it, reverse, addr);
|
||||
} else if (mode == '*') {
|
||||
if (it->name) {
|
||||
const char *prefix = reverse? "sym.unity.reverse": "sym.unity";
|
||||
@@ -1190,10 +1366,25 @@ static int cmd_interop(RCore *core, bool reverse, char mode) {
|
||||
r_cons_printf (core->cons, "# summary: %s=%zu\n", reverse? "reverse_pinvokes": "pinvokes", n);
|
||||
}
|
||||
r2unity_free_interop (items, n);
|
||||
r2unity_native_result_fini (&native_result);
|
||||
close_metadata (meta, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cmd_analyze(RCore *core, unsigned level) {
|
||||
if (r_core_cmd0 (core, ".r2unity-c*") != 0) {
|
||||
return false;
|
||||
}
|
||||
if (level >= 2 && (cmd_symbols (core, 0) || cmd_strings (core, '+')
|
||||
|| cmd_interop (core, false, '+') || cmd_interop (core, true, '+'))) {
|
||||
return false;
|
||||
}
|
||||
if (level >= 3 && r_core_cmd0 (core, "aar") != 0) {
|
||||
return false;
|
||||
}
|
||||
return level < 4 || r_core_cmd0 (core, "aaa") == 0;
|
||||
}
|
||||
|
||||
/* ---------- r2unity-S (SBOM) ---------- */
|
||||
static int cmd_sbom(RCore *core, char mode) {
|
||||
const char *metadata_path = resolve_metadata_path (core);
|
||||
@@ -1233,16 +1424,21 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
|
||||
}
|
||||
rest++;
|
||||
/* "r2unity-" followed by the subcommand letter, optionally a modifier
|
||||
*('j' for JSON, '*' for r2 commands), optionally whitespace + args. */
|
||||
*('j' for JSON, '*' for r2 commands, '+' to import), or repeated 'A's. */
|
||||
char sub = *rest;
|
||||
char mode = 0;
|
||||
unsigned level = 1;
|
||||
if (sub) {
|
||||
rest++;
|
||||
if (*rest == 'j' || *rest == '*') {
|
||||
while (sub == 'A' && *rest == 'A') {
|
||||
level++;
|
||||
rest++;
|
||||
}
|
||||
if (sub != 'A' && (*rest == 'j' || *rest == '*' || *rest == '+')) {
|
||||
mode = *rest;
|
||||
rest++;
|
||||
}
|
||||
if (*rest && *rest != ' ' && *rest != '\t') {
|
||||
if (level > 4 || (*rest && *rest != ' ' && *rest != '\t')) {
|
||||
r_cons_cmd_help (core->cons, g_help_msg);
|
||||
return true;
|
||||
}
|
||||
@@ -1252,10 +1448,14 @@ static bool r2unity_call(RCorePluginSession *cps, const char *input) {
|
||||
}
|
||||
|
||||
switch (sub) {
|
||||
case 'A':
|
||||
return cmd_analyze (core, level);
|
||||
case 'c':
|
||||
return cmd_classes (core, mode) == 0;
|
||||
case 'D':
|
||||
return cmd_detect (core, mode == 'j') == 0;
|
||||
case 'L':
|
||||
return cmd_load_library (core) == 0;
|
||||
case 'i':
|
||||
return cmd_info (core, mode == 'j') == 0;
|
||||
case 's':
|
||||
@@ -1322,6 +1522,7 @@ RCorePlugin r_core_plugin_r2unity = {
|
||||
R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_CORE,
|
||||
.data = &r_core_plugin_r2unity,
|
||||
.version = R2_VERSION
|
||||
.version = R2_VERSION,
|
||||
.pkgname = "r2unity"
|
||||
};
|
||||
#endif
|
||||
Reference in new issue
Block a user