Initial commit

This commit is contained in:
pancake committed 2025-09-18 12:55:25 +02:00
commit 25ca0a9a6a
5 files changed
+595

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
# Simple Makefile for the r2unity project
# Compiler and flags
CC = gcc
CFLAGS = -Wall -Wextra -g -I./third_party/radare2/libr/include $(shell pkg-config --cflags r_core r_util 2>/dev/null || echo "")
LDFLAGS = $(shell pkg-config --libs r_core r_util 2>/dev/null || echo "")
# Project name
EXEC = r2unity
# Source files and object files
SRCS = $(wildcard src/*.c) $(wildcard src/lib/*.c)
OBJS = $(SRCS:.c=.o)
# Default target
all: $(EXEC)
# Linking the executable
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
# Compiling source files into object files
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
# Clean up build artifacts
clean:
rm -f $(EXEC) $(OBJS)
# Phony targets
.PHONY: all clean
+96
View File
@@ -0,0 +1,96 @@
# r2unity
A lightweight, dependency-free C tool to parse Unity's `global-metadata.dat` and generate [radare2](https://github.com/radareorg/radare2) scripts for analyzing `libil2cpp.so` binaries.
## Motivation
The Unity engine, when using its IL2CPP backend, compiles C# code into C++ and then into a native library (`libil2cpp.so` on Android/Linux, `GameAssembly.dll` on Windows, etc.). To understand the original code structure, reverse engineers need to parse the `global-metadata.dat` file, which contains all the type information, method names, and pointers into the native library.
While powerful tools like [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) exist, they are often written in higher-level languages (like C#) and produce generic outputs (like C++ headers or JSON files).
**r2unity** takes a different approach by focusing on a single goal: **deep integration with radare2**. It is written in plain C for maximum performance, portability, and zero runtime dependencies. The output is a ready-to-use `.r2` script, allowing analysts to immediately apply this metadata within their radare2 sessions.
This project is inspired by the functionality of Il2CppDumper and the metadata parsing concepts from projects like [lib-global-metadata](https://github.com/axstin/lib-global-metadata).
## Features
- **Parses `global-metadata.dat`:** Extracts classes, methods, and their relative addresses.
- **Generates Radare2 Scripts:** Creates `.r2` files to name functions and add comments in your analysis session.
- **Written in Plain C:** No dependencies beyond a standard C compiler (like `gcc` or `clang`).
- **Lightweight and Fast:** Designed for speed and minimal memory footprint.
- **Cross-Platform:** Aims to be easily compilable on Linux, macOS, and Windows.
## Getting Started
### Prerequisites
- A C compiler (e.g., `gcc`, `clang`)
- `make`
### Installation
```bash
git clone https://github.com/your-username/r2unity.git
cd r2unity
make
```
## Usage
The primary goal is to generate a script that radare2 can use to annotate the `libil2cpp.so` binary.
1. **Extract Files:** Obtain the `global-metadata.dat` file and the main binary (e.g., `libil2cpp.so`) from your target application.
2. **Run r2unity:** Execute `r2unity` with the metadata file and the base address where the binary is loaded. The output will be an `.r2` script.
```bash
./r2unity /path/to/global-metadata.dat > symbols.r2
```
3. **Launch Radare2:** Open the binary in radare2, sourcing the generated script with the `-i` flag.
```bash
r2 -i symbols.r2 /path/to/libil2cpp.so
```
Radare2 will execute the script, defining flags (function names) for all the methods found in the metadata.
### Example Output (`symbols.r2`)
The generated script will contain a series of radare2 commands:
```
# r2 script generated by r2unity
# Found 50,000+ methods in global-metadata.dat
f Some.Namespace.ClassName.MethodName @ 0x1a2b3c
CCu Method: Some.Namespace.ClassName.MethodName @ 0x1a2b3c
f Another.Class.Update @ 0x2b3c4d
CCu Method: Another.Class.Update @ 0x2b3c4d
f Player.Stats.GetHealth @ 0x3c4d5e
CCu Method: Player.Stats.GetHealth @ 0x3c4d5e
...
```
## Philosophy
- **Do One Thing Well:** Focus solely on creating `r2` scripts from Unity metadata.
- **Native and Dependency-Free:** Avoid the complexity of runtimes like .NET or Python. A single, static binary is the goal.
- **Integrate, Don't Just Convert:** The output should be directly consumable by the target tool (`radare2`) to provide immediate value.
## Contributing
Contributions are welcome! Please feel free to open an issue or submit a pull request. As this is a C project, please adhere to a clean and readable coding style.
## Acknowledgments
- The [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) project for being the gold standard in Unity IL2CPP reversing.
- The [lib-global-metadata](https://github.com/axstin/lib-global-metadata) project for its clear implementation of metadata parsing.
- The [radare2](https://github.com/radareorg/radare2) community.
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
+128
View File
@@ -0,0 +1,128 @@
#include "lib.h"
#include <r_util.h>
#include <r_util/r_buf.h>
R_API R2UnityMetadata *r2unity_parse_metadata (RBuffer *buf) {
if (!buf) {
return NULL;
}
R2UnityMetadata *meta = R_NEW0 (R2UnityMetadata);
if (!meta) {
return NULL;
}
r_buf_seek (buf, 0, R_BUF_SET);
uint32_t magic = r_buf_read_le32 (buf);
if (magic != IL2CPP_MAGIC) {
R_FREE (meta);
return NULL;
}
r_buf_seek (buf, 4, R_BUF_SET);
size_t header_size = 0;
// First read a small part to get the version
uint32_t sanity;
int32_t version;
if (r_buf_read (buf, (ut8 *)&sanity, 4) != 4) {
R_FREE (meta);
return NULL;
}
if (r_buf_read (buf, (ut8 *)&version, 4) != 4) {
R_FREE (meta);
return NULL;
}
if (sanity != IL2CPP_MAGIC) {
R_FREE (meta);
return NULL;
}
meta->version = version;
switch (version) {
case 24:
header_size = sizeof (Il2CppGlobalMetadataHeader_v24) - 8; // subtract sanity and version already read
break;
case 27:
header_size = sizeof (Il2CppGlobalMetadataHeader_v27) - 8;
break;
case 29:
header_size = sizeof (Il2CppGlobalMetadataHeader_v29) - 8;
break;
default:
R_FREE (meta);
return NULL;
}
if (r_buf_read (buf, (ut8 *)&meta->header.v24.stringLiteralOffset, header_size) != (st64)header_size) {
R_FREE (meta);
return NULL;
}
meta->strings = r_buf_new_slice (buf, meta->header.v24.stringOffset, meta->header.v24.stringSize);
meta->string_literals = r_buf_new_slice (buf, meta->header.v24.stringLiteralOffset, meta->header.v24.stringLiteralSize);
return meta;
}
R_API void r2unity_free_metadata (R2UnityMetadata *meta) {
if (!meta) {
return;
}
r_buf_free (meta->strings);
r_buf_free (meta->string_literals);
R_FREE (meta);
}
R_API const char *r2unity_get_string (R2UnityMetadata *meta, uint32_t index) {
if (!meta || !meta->strings || index >= r_buf_size (meta->strings)) {
return NULL;
}
// Read until null terminator
char *str = NULL;
ut64 len = 0;
ut8 byte;
while (r_buf_read_at (meta->strings, index + len, &byte, 1) == 1 && byte != 0) {
len++;
}
if (len > 0) {
str = R_NEWS (char, len + 1);
if (str) {
r_buf_read_at (meta->strings, index, (ut8 *)str, len);
str[len] = 0;
}
}
return str;
}
R_API Il2CppTypeDefinition *r2unity_get_type_definitions (R2UnityMetadata *meta, size_t *count) {
if (!meta || !count) {
return NULL;
}
*count = meta->header.v24.typeDefinitionsSize / sizeof (Il2CppTypeDefinition);
if (*count == 0) {
return NULL;
}
Il2CppTypeDefinition *types = R_NEWS (Il2CppTypeDefinition, *count);
if (!types) {
return NULL;
}
r_buf_seek (meta->strings, meta->header.v24.typeDefinitionsOffset, R_BUF_SET);
if (r_buf_read (meta->strings, (ut8 *)types, meta->header.v24.typeDefinitionsSize) != meta->header.v24.typeDefinitionsSize) {
R_FREE (types);
return NULL;
}
return types;
}
R_API Il2CppMethodDefinition *r2unity_get_method_definitions (R2UnityMetadata *meta, size_t *count) {
if (!meta || !count) {
return NULL;
}
*count = meta->header.v24.methodsSize / sizeof (Il2CppMethodDefinition);
if (*count == 0) {
return NULL;
}
Il2CppMethodDefinition *methods = R_NEWS (Il2CppMethodDefinition, *count);
if (!methods) {
return NULL;
}
r_buf_seek (meta->strings, meta->header.v24.methodsOffset, R_BUF_SET);
if (r_buf_read (meta->strings, (ut8 *)methods, meta->header.v24.methodsSize) != meta->header.v24.methodsSize) {
R_FREE (methods);
return NULL;
}
return methods;
}
+276
View File
@@ -0,0 +1,276 @@
#ifndef R2UNITY_LIB_H
#define R2UNITY_LIB_H
#include <r_util.h>
#define IL2CPP_MAGIC 0xFAB11BAF
typedef struct {
uint32_t sanity;
int32_t version;
uint32_t stringLiteralOffset;
int32_t stringLiteralSize;
uint32_t stringLiteralDataOffset;
int32_t stringLiteralDataSize;
uint32_t stringOffset;
int32_t stringSize;
uint32_t eventsOffset;
int32_t eventsSize;
uint32_t propertiesOffset;
int32_t propertiesSize;
uint32_t methodsOffset;
int32_t methodsSize;
uint32_t parameterDefaultValuesOffset;
int32_t parameterDefaultValuesSize;
uint32_t fieldDefaultValuesOffset;
int32_t fieldDefaultValuesSize;
uint32_t fieldAndParameterDefaultValueDataOffset;
int32_t fieldAndParameterDefaultValueDataSize;
uint32_t fieldMarshaledSizesOffset;
int32_t fieldMarshaledSizesSize;
uint32_t parametersOffset;
int32_t parametersSize;
uint32_t fieldsOffset;
int32_t fieldsSize;
uint32_t genericParametersOffset;
int32_t genericParametersSize;
uint32_t genericParameterConstraintsOffset;
int32_t genericParameterConstraintsSize;
uint32_t genericContainersOffset;
int32_t genericContainersSize;
uint32_t nestedTypesOffset;
int32_t nestedTypesSize;
uint32_t interfacesOffset;
int32_t interfacesSize;
uint32_t vtableMethodsOffset;
int32_t vtableMethodsSize;
uint32_t interfaceOffsetsOffset;
int32_t interfaceOffsetsSize;
uint32_t typeDefinitionsOffset;
int32_t typeDefinitionsSize;
uint32_t imagesOffset;
int32_t imagesSize;
uint32_t assembliesOffset;
int32_t assembliesSize;
uint32_t fieldRefsOffset;
int32_t fieldRefsSize;
uint32_t referencedAssembliesOffset;
int32_t referencedAssembliesSize;
uint32_t attributesInfoOffset;
int32_t attributesInfoSize;
uint32_t attributeTypesOffset;
int32_t attributeTypesSize;
uint32_t unresolvedVirtualCallParameterTypesOffset;
int32_t unresolvedVirtualCallParameterTypesSize;
uint32_t unresolvedVirtualCallParameterRangesOffset;
int32_t unresolvedVirtualCallParameterRangesSize;
uint32_t windowsRuntimeTypeNamesOffset;
int32_t windowsRuntimeTypeNamesSize;
uint32_t windowsRuntimeStringsOffset;
int32_t windowsRuntimeStringsSize;
uint32_t exportedTypeDefinitionsOffset;
int32_t exportedTypeDefinitionsSize;
} Il2CppGlobalMetadataHeader_v24;
typedef struct {
uint32_t sanity;
int32_t version;
uint32_t stringLiteralOffset;
int32_t stringLiteralSize;
uint32_t stringLiteralDataOffset;
int32_t stringLiteralDataSize;
uint32_t stringOffset;
int32_t stringSize;
uint32_t eventsOffset;
int32_t eventsSize;
uint32_t propertiesOffset;
int32_t propertiesSize;
uint32_t methodsOffset;
int32_t methodsSize;
uint32_t parameterDefaultValuesOffset;
int32_t parameterDefaultValuesSize;
uint32_t fieldDefaultValuesOffset;
int32_t fieldDefaultValuesSize;
uint32_t fieldAndParameterDefaultValueDataOffset;
int32_t fieldAndParameterDefaultValueDataSize;
uint32_t fieldMarshaledSizesOffset;
int32_t fieldMarshaledSizesSize;
uint32_t parametersOffset;
int32_t parametersSize;
uint32_t fieldsOffset;
int32_t fieldsSize;
uint32_t genericParametersOffset;
int32_t genericParametersSize;
uint32_t genericParameterConstraintsOffset;
int32_t genericParameterConstraintsSize;
uint32_t genericContainersOffset;
int32_t genericContainersSize;
uint32_t nestedTypesOffset;
int32_t nestedTypesSize;
uint32_t interfacesOffset;
int32_t interfacesSize;
uint32_t vtableMethodsOffset;
int32_t vtableMethodsSize;
uint32_t interfaceOffsetsOffset;
int32_t interfaceOffsetsSize;
uint32_t typeDefinitionsOffset;
int32_t typeDefinitionsSize;
uint32_t imagesOffset;
int32_t imagesSize;
uint32_t assembliesOffset;
int32_t assembliesSize;
uint32_t fieldRefsOffset;
int32_t fieldRefsSize;
uint32_t referencedAssembliesOffset;
int32_t referencedAssembliesSize;
uint32_t attributesInfoOffset;
int32_t attributesInfoSize;
uint32_t attributeTypesOffset;
int32_t attributeTypesSize;
uint32_t unresolvedVirtualCallParameterTypesOffset;
int32_t unresolvedVirtualCallParameterTypesSize;
uint32_t unresolvedVirtualCallParameterRangesOffset;
int32_t unresolvedVirtualCallParameterRangesSize;
uint32_t windowsRuntimeTypeNamesOffset;
int32_t windowsRuntimeTypeNamesSize;
uint32_t windowsRuntimeStringsOffset;
int32_t windowsRuntimeStringsSize;
uint32_t exportedTypeDefinitionsOffset;
int32_t exportedTypeDefinitionsSize;
uint32_t rgctxEntriesOffset;
int32_t rgctxEntriesSize;
} Il2CppGlobalMetadataHeader_v27;
typedef struct {
uint32_t sanity;
int32_t version;
uint32_t stringLiteralOffset;
int32_t stringLiteralSize;
uint32_t stringLiteralDataOffset;
int32_t stringLiteralDataSize;
uint32_t stringOffset;
int32_t stringSize;
uint32_t eventsOffset;
int32_t eventsSize;
uint32_t propertiesOffset;
int32_t propertiesSize;
uint32_t methodsOffset;
int32_t methodsSize;
uint32_t parameterDefaultValuesOffset;
int32_t parameterDefaultValuesSize;
uint32_t fieldDefaultValuesOffset;
int32_t fieldDefaultValuesSize;
uint32_t fieldAndParameterDefaultValueDataOffset;
int32_t fieldAndParameterDefaultValueDataSize;
uint32_t fieldMarshaledSizesOffset;
int32_t fieldMarshaledSizesSize;
uint32_t parametersOffset;
int32_t parametersSize;
uint32_t fieldsOffset;
int32_t fieldsSize;
uint32_t genericParametersOffset;
int32_t genericParametersSize;
uint32_t genericParameterConstraintsOffset;
int32_t genericParameterConstraintsSize;
uint32_t genericContainersOffset;
int32_t genericContainersSize;
uint32_t nestedTypesOffset;
int32_t nestedTypesSize;
uint32_t interfacesOffset;
int32_t interfacesSize;
uint32_t vtableMethodsOffset;
int32_t vtableMethodsSize;
uint32_t interfaceOffsetsOffset;
int32_t interfaceOffsetsSize;
uint32_t typeDefinitionsOffset;
int32_t typeDefinitionsSize;
uint32_t imagesOffset;
int32_t imagesSize;
uint32_t assembliesOffset;
int32_t assembliesSize;
uint32_t fieldRefsOffset;
int32_t fieldRefsSize;
uint32_t referencedAssembliesOffset;
int32_t referencedAssembliesSize;
uint32_t attributesInfoOffset;
int32_t attributesInfoSize;
uint32_t attributeTypesOffset;
int32_t attributeTypesSize;
uint32_t unresolvedVirtualCallParameterTypesOffset;
int32_t unresolvedVirtualCallParameterTypesSize;
uint32_t unresolvedVirtualCallParameterRangesOffset;
int32_t unresolvedVirtualCallParameterRangesSize;
uint32_t windowsRuntimeTypeNamesOffset;
int32_t windowsRuntimeTypeNamesSize;
uint32_t windowsRuntimeStringsOffset;
int32_t windowsRuntimeStringsSize;
uint32_t exportedTypeDefinitionsOffset;
int32_t exportedTypeDefinitionsSize;
uint32_t rgctxEntriesOffset;
int32_t rgctxEntriesSize;
uint32_t rgctxEntriesDataOffset;
int32_t rgctxEntriesDataSize;
} Il2CppGlobalMetadataHeader_v29;
typedef union {
Il2CppGlobalMetadataHeader_v24 v24;
Il2CppGlobalMetadataHeader_v27 v27;
Il2CppGlobalMetadataHeader_v29 v29;
} Il2CppGlobalMetadataHeader;
typedef struct {
int32_t version;
Il2CppGlobalMetadataHeader header;
RBuffer *strings;
RBuffer *string_literals;
} R2UnityMetadata;
typedef struct {
uint32_t nameIndex;
uint32_t namespaceIndex;
int32_t byvalTypeIndex;
int32_t declaringTypeIndex;
int32_t parentIndex;
int32_t elementTypeIndex;
int32_t genericContainerIndex;
uint32_t flags;
int32_t fieldStart;
int32_t methodStart;
int32_t eventStart;
int32_t propertyStart;
int32_t nestedTypesStart;
int32_t interfacesStart;
int32_t vtableStart;
int32_t interfaceOffsetsStart;
uint16_t method_count;
uint16_t property_count;
uint16_t field_count;
uint16_t event_count;
uint16_t nested_type_count;
uint16_t vtable_count;
uint16_t interfaces_count;
uint16_t interface_offsets_count;
uint32_t bitfield;
uint32_t token;
} Il2CppTypeDefinition;
typedef struct {
uint32_t nameIndex;
int32_t declaringType;
int32_t returnType;
int32_t parameterStart;
int32_t genericContainerIndex;
uint32_t token;
uint16_t flags;
uint16_t iflags;
uint16_t slot;
uint16_t parameterCount;
} Il2CppMethodDefinition;
R_API R2UnityMetadata *r2unity_parse_metadata (RBuffer *buf);
R_API void r2unity_free_metadata (R2UnityMetadata *meta);
R_API const char *r2unity_get_string (R2UnityMetadata *meta, uint32_t index);
R_API Il2CppTypeDefinition *r2unity_get_type_definitions (R2UnityMetadata *meta, size_t *count);
R_API Il2CppMethodDefinition *r2unity_get_method_definitions (R2UnityMetadata *meta, size_t *count);
#endif
+64
View File
@@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include "lib/lib.h"
void print_usage(const char *prog_name) {
fprintf(stderr, "Usage: %s <executable> <path/to/global-metadata.dat>\n", prog_name);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
print_usage(argv[0]);
return 1;
}
const char *exe_path = argv[1];
const char *metadata_path = argv[2];
RBuffer *buf = r_buf_new_file (metadata_path, O_RDONLY, 0);
if (!buf) {
perror("Error opening file");
return 1;
}
printf("# r2 script generated by r2unity\n");
printf("# Input file: %s\n\n", metadata_path);
R2UnityMetadata *meta = r2unity_parse_metadata (buf);
if (!meta) {
fprintf(stderr, "Failed to parse metadata\n");
r_buf_free (buf);
return 1;
}
size_t type_count;
Il2CppTypeDefinition *types = r2unity_get_type_definitions (meta, &type_count);
if (types) {
for (size_t i = 0; i < type_count; i++) {
const char *type_name = r2unity_get_string (meta, types[i].nameIndex);
const char *namespace_name = r2unity_get_string (meta, types[i].namespaceIndex);
if (type_name) {
printf("f %s.%s @ 0x0\n", namespace_name ? namespace_name : "", type_name);
}
}
R_FREE (types);
}
size_t method_count;
Il2CppMethodDefinition *methods = r2unity_get_method_definitions (meta, &method_count);
if (methods) {
for (size_t i = 0; i < method_count; i++) {
const char *method_name = r2unity_get_string (meta, methods[i].nameIndex);
if (method_name) {
printf("f %s @ 0x0\n", method_name);
printf("CCu Method: %s @ 0x0\n", method_name);
}
}
R_FREE (methods);
}
r2unity_free_metadata (meta);
r_buf_free (buf);
return 0;
}