Repository: AndnixSH/Auto-Il2cppDumper
Branch: master
Commit: 10ba671f7887
Files: 46
Total size: 146.7 KB
Directory structure:
gitextract_37fksbix/
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── Compile.bat
│ ├── java/
│ │ └── com/
│ │ └── il2cpp/
│ │ └── dumper/
│ │ └── MainActivity.java
│ ├── jni/
│ │ ├── Android.mk
│ │ ├── Application.mk
│ │ ├── Il2Cpp/
│ │ │ ├── il2cpp-api-functions.h
│ │ │ ├── il2cpp-class.h
│ │ │ ├── il2cpp-tabledefs.h
│ │ │ ├── il2cpp_dump.cpp
│ │ │ ├── il2cpp_dump.h
│ │ │ └── xdl/
│ │ │ ├── include/
│ │ │ │ └── xdl.h
│ │ │ ├── xdl.c
│ │ │ ├── xdl.map.txt
│ │ │ ├── xdl_iterate.c
│ │ │ ├── xdl_iterate.h
│ │ │ ├── xdl_linker.c
│ │ │ ├── xdl_linker.h
│ │ │ ├── xdl_lzma.c
│ │ │ ├── xdl_lzma.h
│ │ │ ├── xdl_util.c
│ │ │ └── xdl_util.h
│ │ ├── Includes/
│ │ │ ├── config.h
│ │ │ └── log.h
│ │ └── native-lib.cpp
│ └── res/
│ ├── drawable/
│ │ └── ic_launcher_background.xml
│ ├── drawable-v24/
│ │ └── ic_launcher_foreground.xml
│ ├── layout/
│ │ └── activity_main.xml
│ ├── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ └── values/
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
================================================
FILE: .gitignore
================================================
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Rikka
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Auto-Il2cppDumper
Il2CppDumper without Magisk/Zygisk, dump il2cpp data at runtime, can bypass protection, encryption and obfuscation.
This project is forked from BrianGIG [Auto-Il2cppDumper](https://github.com/BryanGIG/Auto-Il2cppDumper)
# Discontined
This project is no longer maintained. Due to the increasing variety and complexity of game protection and encryption methods, I have chosen to discontinue development. There are no plans for future commits, releases, or support.
# How to use
Download pre-compiled libs [HERE](https://github.com/AndnixSH/Auto-Il2cppDumper/releases) and follow steps below
Note: Non-root methods involves modifying APK file, you may need to bypass APK integrity or signature check if it's present. I won't get into the details of bypassing anything because it is simply out of the scope
## Method 1: Fake lib
This is a trick to load our own libunity.so or libmain.so and load game's renamed original lib librealunity.so or librealmain.so. Can't decide which lib? Try libmain.so first as it sometimes work better than libunity.so or vice versa
### Root
- Make sure you know the architecture of the game and your device before proceed
- Use any file manager app that can access root. Go to /data/data/(package name)/lib or /data/app/(obfuscated name)/(package name-obfuscated name)/lib
- Rename original libunity.so to librealunity.so or original libmain.so to librealmain.so
- Put our lib file libunity.so or libmain.so to the lib folder

### Non-root
Some games may have APK integrity and signature check. You may need to bypass it before adding Il2CppDumper
- Open APK file as ZIP using Winrar. 7zip or other zip utilities
- Rename game's libunity.so to librealunity.so or libmain.so to librealmain.so
- Put our fake lib file libunity.so or libmain.so to the lib folder

- Zipalign and sign the APK file
- Install the APK
### Method 2: Lib call
If renaming lib doesn't work, try this method.
- Decompile the game using Apktool
- Copy libil2cppdumper.so into the lib folder. Make sure only copy same ABIs as the target app, for example if target app has only armeabi-v7a, then you should only copy armeabi-v7a
- Search the main activity in AndroidManifest.xml. Example: com.gameloft.android.XamarinMainActivity
```xml
```
- Locate the said main activity in the smali folder, can be \smali_classes(number)\com\gameloft\android\XamarinMainActivity.smali if multidex). You must look through all smali classes until you found it
- Insert this lib call into onCreate function below `.locals` XX
```smali
const-string v0, "il2cppdumper"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
```
Like
```smali
.method protected onCreate(Landroid/os/Bundle;)V
.locals 2
const-string v0, "il2cppdumper"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
whatever code below
```
- Re-compile, zipalign, sign the APK
- Install the APK
### Dumping
- Run the game
- Wait a few seconds. Let the game load into main screen
- Once the dump is complete, it will save the dump.cs in /storage/emulated/0/Android/data/(Package name)/

- If there is no dump.cs, check logcat using Matlog or Android Studio. Game usually crash or freeze if dump fails
### Obfuscated names
Names can't be deobfuscated. Once they are obfuscated/renamed, it can't be reverted back to original, the game doesn't even kept any original names which makes obfuscation very useful. Instead, try to find older version without obfuscation, or debug the game using GG, frida, gdb, lldb or others. If you can't do any of these, maybe guess the functions and try one by one? :P
### Bypassing protection?
Bypassing is not my thing, but if you know, feel free to make a pull request.
# Usage (If you want to complie by yourself)
In config header `jni/Includes/config.h`:
`UseFakeLib` is to use it as root mode
`Sleep` is to delay dumping. Increase if getting issue with dumper, like if not fully dumped
# Credits
- Perfare [Zygisk-Il2CppDumper](https://github.com/Perfare/Zygisk-Il2CppDumper)
- BrianGIG [Auto-Il2cppDumper](https://github.com/BryanGIG/Auto-Il2cppDumper)
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 36
buildToolsVersion "36.1.0"
defaultConfig {
applicationId "com.il2cpp.dumper"
minSdkVersion 21
targetSdkVersion 36
versionCode 1
versionName "3.1"
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}
namespace 'com.il2cpp.dumper'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/Compile.bat
================================================
E:\Android-SDK\ndk\21.3.6528147\ndk-build
NDK_PROJECT_PATH=\
NDK_APPLICATION_MK=\jni\Application.mk
pause
================================================
FILE: app/src/main/java/com/il2cpp/dumper/MainActivity.java
================================================
package com.il2cpp.dumper;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
static {
System.loadLibrary("il2cppdumper");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
================================================
FILE: app/src/main/jni/Android.mk
================================================
LOCAL_PATH := $(call my-dir)
MAIN_LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := il2cppdumper
LOCAL_CFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w
LOCAL_CFLAGS += -fno-rtti -fno-exceptions -fpermissive
LOCAL_CPPFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w -Werror -s -std=c++17
LOCAL_CPPFLAGS += -Wno-error=c++11-narrowing -fms-extensions -fno-rtti -fno-exceptions -fpermissive
LOCAL_LDFLAGS += -Wl,--gc-sections,--strip-all, -llog
LOCAL_ARM_MODE := arm
LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/Il2Cpp/xdl/include
FILE_LIST := $(wildcard $(LOCAL_PATH)/Il2Cpp/xdl/*.c)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES += native-lib.cpp \
Il2Cpp/il2cpp_dump.cpp
LOCAL_LDLIBS := -llog -landroid -lGLESv2 -ldl
include $(BUILD_SHARED_LIBRARY)
================================================
FILE: app/src/main/jni/Application.mk
================================================
APP_ABI := all
APP_STL := c++_static
APP_OPTIM := release
APP_THIN_ARCHIVE := true
APP_PIE := true
================================================
FILE: app/src/main/jni/Il2Cpp/il2cpp-api-functions.h
================================================
#ifndef DO_API_NO_RETURN
#define DO_API_NO_RETURN(r, n, p) DO_API(r,n,p)
#endif
DO_API(int, il2cpp_init, (const char* domain_name));
DO_API(int, il2cpp_init_utf16, (const Il2CppChar * domain_name));
DO_API(void, il2cpp_shutdown, ());
DO_API(void, il2cpp_set_config_dir, (const char *config_path));
DO_API(void, il2cpp_set_data_dir, (const char *data_path));
DO_API(void, il2cpp_set_temp_dir, (const char *temp_path));
DO_API(void, il2cpp_set_commandline_arguments, (int argc, const char* const argv[], const char* basedir));
DO_API(void, il2cpp_set_commandline_arguments_utf16, (int argc, const Il2CppChar * const argv[], const char* basedir));
DO_API(void, il2cpp_set_config_utf16, (const Il2CppChar * executablePath));
DO_API(void, il2cpp_set_config, (const char* executablePath));
DO_API(void, il2cpp_set_memory_callbacks, (Il2CppMemoryCallbacks * callbacks));
DO_API(const Il2CppImage*, il2cpp_get_corlib, ());
DO_API(void, il2cpp_add_internal_call, (const char* name, Il2CppMethodPointer method));
DO_API(Il2CppMethodPointer, il2cpp_resolve_icall, (const char* name));
DO_API(void*, il2cpp_alloc, (size_t size));
DO_API(void, il2cpp_free, (void* ptr));
// array
DO_API(Il2CppClass*, il2cpp_array_class_get, (Il2CppClass * element_class, uint32_t rank));
DO_API(uint32_t, il2cpp_array_length, (Il2CppArray * array));
DO_API(uint32_t, il2cpp_array_get_byte_length, (Il2CppArray * array));
DO_API(Il2CppArray*, il2cpp_array_new, (Il2CppClass * elementTypeInfo, il2cpp_array_size_t length));
DO_API(Il2CppArray*, il2cpp_array_new_specific, (Il2CppClass * arrayTypeInfo, il2cpp_array_size_t length));
DO_API(Il2CppArray*, il2cpp_array_new_full, (Il2CppClass * array_class, il2cpp_array_size_t * lengths, il2cpp_array_size_t * lower_bounds));
DO_API(Il2CppClass*, il2cpp_bounded_array_class_get, (Il2CppClass * element_class, uint32_t rank, bool bounded));
DO_API(int, il2cpp_array_element_size, (const Il2CppClass * array_class));
// assembly
DO_API(const Il2CppImage*, il2cpp_assembly_get_image, (const Il2CppAssembly * assembly));
// class
DO_API(void, il2cpp_class_for_each, (void(*klassReportFunc)(Il2CppClass* klass, void* userData), void* userData));
DO_API(const Il2CppType*, il2cpp_class_enum_basetype, (Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_generic, (const Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_inflated, (const Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_assignable_from, (Il2CppClass * klass, Il2CppClass * oklass));
DO_API(bool, il2cpp_class_is_subclass_of, (Il2CppClass * klass, Il2CppClass * klassc, bool check_interfaces));
DO_API(bool, il2cpp_class_has_parent, (Il2CppClass * klass, Il2CppClass * klassc));
DO_API(Il2CppClass*, il2cpp_class_from_il2cpp_type, (const Il2CppType * type));
DO_API(Il2CppClass*, il2cpp_class_from_name, (const Il2CppImage * image, const char* namespaze, const char *name));
DO_API(Il2CppClass*, il2cpp_class_from_system_type, (Il2CppReflectionType * type));
DO_API(Il2CppClass*, il2cpp_class_get_element_class, (Il2CppClass * klass));
DO_API(const EventInfo*, il2cpp_class_get_events, (Il2CppClass * klass, void* *iter));
DO_API(FieldInfo*, il2cpp_class_get_fields, (Il2CppClass * klass, void* *iter));
DO_API(Il2CppClass*, il2cpp_class_get_nested_types, (Il2CppClass * klass, void* *iter));
DO_API(Il2CppClass*, il2cpp_class_get_interfaces, (Il2CppClass * klass, void* *iter));
DO_API(const PropertyInfo*, il2cpp_class_get_properties, (Il2CppClass * klass, void* *iter));
DO_API(const PropertyInfo*, il2cpp_class_get_property_from_name, (Il2CppClass * klass, const char *name));
DO_API(FieldInfo*, il2cpp_class_get_field_from_name, (Il2CppClass * klass, const char *name));
DO_API(const MethodInfo*, il2cpp_class_get_methods, (Il2CppClass * klass, void* *iter));
DO_API(const MethodInfo*, il2cpp_class_get_method_from_name, (Il2CppClass * klass, const char* name, int argsCount));
DO_API(const char*, il2cpp_class_get_name, (Il2CppClass * klass));
DO_API(void, il2cpp_type_get_name_chunked, (const Il2CppType * type, void(*chunkReportFunc)(void* data, void* userData), void* userData));
DO_API(const char*, il2cpp_class_get_namespace, (Il2CppClass * klass));
DO_API(Il2CppClass*, il2cpp_class_get_parent, (Il2CppClass * klass));
DO_API(Il2CppClass*, il2cpp_class_get_declaring_type, (Il2CppClass * klass));
DO_API(int32_t, il2cpp_class_instance_size, (Il2CppClass * klass));
DO_API(size_t, il2cpp_class_num_fields, (const Il2CppClass * enumKlass));
DO_API(bool, il2cpp_class_is_valuetype, (const Il2CppClass * klass));
DO_API(int32_t, il2cpp_class_value_size, (Il2CppClass * klass, uint32_t * align));
DO_API(bool, il2cpp_class_is_blittable, (const Il2CppClass * klass));
DO_API(int, il2cpp_class_get_flags, (const Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_abstract, (const Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_interface, (const Il2CppClass * klass));
DO_API(int, il2cpp_class_array_element_size, (const Il2CppClass * klass));
DO_API(Il2CppClass*, il2cpp_class_from_type, (const Il2CppType * type));
DO_API(const Il2CppType*, il2cpp_class_get_type, (Il2CppClass * klass));
DO_API(uint32_t, il2cpp_class_get_type_token, (Il2CppClass * klass));
DO_API(bool, il2cpp_class_has_attribute, (Il2CppClass * klass, Il2CppClass * attr_class));
DO_API(bool, il2cpp_class_has_references, (Il2CppClass * klass));
DO_API(bool, il2cpp_class_is_enum, (const Il2CppClass * klass));
DO_API(const Il2CppImage*, il2cpp_class_get_image, (Il2CppClass * klass));
DO_API(const char*, il2cpp_class_get_assemblyname, (const Il2CppClass * klass));
DO_API(int, il2cpp_class_get_rank, (const Il2CppClass * klass));
DO_API(uint32_t, il2cpp_class_get_data_size, (const Il2CppClass * klass));
DO_API(void*, il2cpp_class_get_static_field_data, (const Il2CppClass * klass));
// testing only
DO_API(size_t, il2cpp_class_get_bitmap_size, (const Il2CppClass * klass));
DO_API(void, il2cpp_class_get_bitmap, (Il2CppClass * klass, size_t * bitmap));
// stats
DO_API(bool, il2cpp_stats_dump_to_file, (const char *path));
DO_API(uint64_t, il2cpp_stats_get_value, (Il2CppStat stat));
// domain
DO_API(Il2CppDomain*, il2cpp_domain_get, ());
DO_API(const Il2CppAssembly*, il2cpp_domain_assembly_open, (Il2CppDomain * domain, const char* name));
DO_API(const Il2CppAssembly**, il2cpp_domain_get_assemblies, (const Il2CppDomain * domain, size_t * size));
// exception
DO_API_NO_RETURN(void, il2cpp_raise_exception, (Il2CppException*));
DO_API(Il2CppException*, il2cpp_exception_from_name_msg, (const Il2CppImage * image, const char *name_space, const char *name, const char *msg));
DO_API(Il2CppException*, il2cpp_get_exception_argument_null, (const char *arg));
DO_API(void, il2cpp_format_exception, (const Il2CppException * ex, char* message, int message_size));
DO_API(void, il2cpp_format_stack_trace, (const Il2CppException * ex, char* output, int output_size));
DO_API(void, il2cpp_unhandled_exception, (Il2CppException*));
DO_API(void, il2cpp_native_stack_trace, (const Il2CppException * ex, uintptr_t** addresses, int* numFrames, char** imageUUID, char** imageName));
// field
DO_API(int, il2cpp_field_get_flags, (FieldInfo * field));
DO_API(const char*, il2cpp_field_get_name, (FieldInfo * field));
DO_API(Il2CppClass*, il2cpp_field_get_parent, (FieldInfo * field));
DO_API(size_t, il2cpp_field_get_offset, (FieldInfo * field));
DO_API(const Il2CppType*, il2cpp_field_get_type, (FieldInfo * field));
DO_API(void, il2cpp_field_get_value, (Il2CppObject * obj, FieldInfo * field, void *value));
DO_API(Il2CppObject*, il2cpp_field_get_value_object, (FieldInfo * field, Il2CppObject * obj));
DO_API(bool, il2cpp_field_has_attribute, (FieldInfo * field, Il2CppClass * attr_class));
DO_API(void, il2cpp_field_set_value, (Il2CppObject * obj, FieldInfo * field, void *value));
DO_API(void, il2cpp_field_static_get_value, (FieldInfo * field, void *value));
DO_API(void, il2cpp_field_static_set_value, (FieldInfo * field, void *value));
DO_API(void, il2cpp_field_set_value_object, (Il2CppObject * instance, FieldInfo * field, Il2CppObject * value));
DO_API(bool, il2cpp_field_is_literal, (FieldInfo * field));
// gc
DO_API(void, il2cpp_gc_collect, (int maxGenerations));
DO_API(int32_t, il2cpp_gc_collect_a_little, ());
DO_API(void, il2cpp_gc_start_incremental_collection , ());
DO_API(void, il2cpp_gc_disable, ());
DO_API(void, il2cpp_gc_enable, ());
DO_API(bool, il2cpp_gc_is_disabled, ());
DO_API(void, il2cpp_gc_set_mode, (Il2CppGCMode mode));
DO_API(int64_t, il2cpp_gc_get_max_time_slice_ns, ());
DO_API(void, il2cpp_gc_set_max_time_slice_ns, (int64_t maxTimeSlice));
DO_API(bool, il2cpp_gc_is_incremental, ());
DO_API(int64_t, il2cpp_gc_get_used_size, ());
DO_API(int64_t, il2cpp_gc_get_heap_size, ());
DO_API(void, il2cpp_gc_wbarrier_set_field, (Il2CppObject * obj, void **targetAddress, void *object));
DO_API(bool, il2cpp_gc_has_strict_wbarriers, ());
DO_API(void, il2cpp_gc_set_external_allocation_tracker, (void(*func)(void*, size_t, int)));
DO_API(void, il2cpp_gc_set_external_wbarrier_tracker, (void(*func)(void**)));
DO_API(void, il2cpp_gc_foreach_heap, (void(*func)(void* data, void* userData), void* userData));
DO_API(void, il2cpp_stop_gc_world, ());
DO_API(void, il2cpp_start_gc_world, ());
DO_API(void*, il2cpp_gc_alloc_fixed, (size_t size));
DO_API(void, il2cpp_gc_free_fixed, (void* address));
// gchandle
DO_API(uint32_t, il2cpp_gchandle_new, (Il2CppObject * obj, bool pinned));
DO_API(uint32_t, il2cpp_gchandle_new_weakref, (Il2CppObject * obj, bool track_resurrection));
DO_API(Il2CppObject*, il2cpp_gchandle_get_target , (uint32_t gchandle));
DO_API(void, il2cpp_gchandle_free, (uint32_t gchandle));
DO_API(void , il2cpp_gchandle_foreach_get_target, (void(*func)(void* data, void* userData), void* userData));
// vm runtime info
DO_API(uint32_t, il2cpp_object_header_size, ());
DO_API(uint32_t, il2cpp_array_object_header_size, ());
DO_API(uint32_t, il2cpp_offset_of_array_length_in_array_object_header, ());
DO_API(uint32_t, il2cpp_offset_of_array_bounds_in_array_object_header, ());
DO_API(uint32_t, il2cpp_allocation_granularity, ());
// liveness
DO_API(void*, il2cpp_unity_liveness_allocate_struct, (Il2CppClass * filter, int max_object_count, il2cpp_register_object_callback callback, void* userdata, il2cpp_liveness_reallocate_callback reallocate));
DO_API(void, il2cpp_unity_liveness_calculation_from_root, (Il2CppObject * root, void* state));
DO_API(void, il2cpp_unity_liveness_calculation_from_statics, (void* state));
DO_API(void, il2cpp_unity_liveness_finalize, (void* state));
DO_API(void, il2cpp_unity_liveness_free_struct, (void* state));
// method
DO_API(const Il2CppType*, il2cpp_method_get_return_type, (const MethodInfo * method));
DO_API(Il2CppClass*, il2cpp_method_get_declaring_type, (const MethodInfo * method));
DO_API(const char*, il2cpp_method_get_name, (const MethodInfo * method));
DO_API(const MethodInfo*, il2cpp_method_get_from_reflection, (const Il2CppReflectionMethod * method));
DO_API(Il2CppReflectionMethod*, il2cpp_method_get_object, (const MethodInfo * method, Il2CppClass * refclass));
DO_API(bool, il2cpp_method_is_generic, (const MethodInfo * method));
DO_API(bool, il2cpp_method_is_inflated, (const MethodInfo * method));
DO_API(bool, il2cpp_method_is_instance, (const MethodInfo * method));
DO_API(uint32_t, il2cpp_method_get_param_count, (const MethodInfo * method));
DO_API(const Il2CppType*, il2cpp_method_get_param, (const MethodInfo * method, uint32_t index));
DO_API(Il2CppClass*, il2cpp_method_get_class, (const MethodInfo * method));
DO_API(bool, il2cpp_method_has_attribute, (const MethodInfo * method, Il2CppClass * attr_class));
DO_API(uint32_t, il2cpp_method_get_flags, (const MethodInfo * method, uint32_t * iflags));
DO_API(uint32_t, il2cpp_method_get_token, (const MethodInfo * method));
DO_API(const char*, il2cpp_method_get_param_name, (const MethodInfo * method, uint32_t index));
// profiler
#if IL2CPP_ENABLE_PROFILER
DO_API(void, il2cpp_profiler_install, (Il2CppProfiler * prof, Il2CppProfileFunc shutdown_callback));
DO_API(void, il2cpp_profiler_set_events, (Il2CppProfileFlags events));
DO_API(void, il2cpp_profiler_install_enter_leave, (Il2CppProfileMethodFunc enter, Il2CppProfileMethodFunc fleave));
DO_API(void, il2cpp_profiler_install_allocation, (Il2CppProfileAllocFunc callback));
DO_API(void, il2cpp_profiler_install_gc, (Il2CppProfileGCFunc callback, Il2CppProfileGCResizeFunc heap_resize_callback));
DO_API(void, il2cpp_profiler_install_fileio, (Il2CppProfileFileIOFunc callback));
DO_API(void, il2cpp_profiler_install_thread, (Il2CppProfileThreadFunc start, Il2CppProfileThreadFunc end));
#endif
// property
DO_API(uint32_t, il2cpp_property_get_flags, (PropertyInfo * prop));
DO_API(const MethodInfo*, il2cpp_property_get_get_method, (PropertyInfo * prop));
DO_API(const MethodInfo*, il2cpp_property_get_set_method, (PropertyInfo * prop));
DO_API(const char*, il2cpp_property_get_name, (PropertyInfo * prop));
DO_API(Il2CppClass*, il2cpp_property_get_parent, (PropertyInfo * prop));
// object
DO_API(Il2CppClass*, il2cpp_object_get_class, (Il2CppObject * obj));
DO_API(uint32_t, il2cpp_object_get_size, (Il2CppObject * obj));
DO_API(const MethodInfo*, il2cpp_object_get_virtual_method, (Il2CppObject * obj, const MethodInfo * method));
DO_API(Il2CppObject*, il2cpp_object_new, (const Il2CppClass * klass));
DO_API(void*, il2cpp_object_unbox, (Il2CppObject * obj));
DO_API(Il2CppObject*, il2cpp_value_box, (Il2CppClass * klass, void* data));
// monitor
DO_API(void, il2cpp_monitor_enter, (Il2CppObject * obj));
DO_API(bool, il2cpp_monitor_try_enter, (Il2CppObject * obj, uint32_t timeout));
DO_API(void, il2cpp_monitor_exit, (Il2CppObject * obj));
DO_API(void, il2cpp_monitor_pulse, (Il2CppObject * obj));
DO_API(void, il2cpp_monitor_pulse_all, (Il2CppObject * obj));
DO_API(void, il2cpp_monitor_wait, (Il2CppObject * obj));
DO_API(bool, il2cpp_monitor_try_wait, (Il2CppObject * obj, uint32_t timeout));
// runtime
DO_API(Il2CppObject*, il2cpp_runtime_invoke, (const MethodInfo * method, void *obj, void **params, Il2CppException **exc));
DO_API(Il2CppObject*, il2cpp_runtime_invoke_convert_args, (const MethodInfo * method, void *obj, Il2CppObject **params, int paramCount, Il2CppException **exc));
DO_API(void, il2cpp_runtime_class_init, (Il2CppClass * klass));
DO_API(void, il2cpp_runtime_object_init, (Il2CppObject * obj));
DO_API(void, il2cpp_runtime_object_init_exception, (Il2CppObject * obj, Il2CppException** exc));
DO_API(void, il2cpp_runtime_unhandled_exception_policy_set, (Il2CppRuntimeUnhandledExceptionPolicy value));
// string
DO_API(int32_t, il2cpp_string_length, (Il2CppString * str));
DO_API(Il2CppChar*, il2cpp_string_chars, (Il2CppString * str));
DO_API(Il2CppString*, il2cpp_string_new, (const char* str));
DO_API(Il2CppString*, il2cpp_string_new_len, (const char* str, uint32_t length));
DO_API(Il2CppString*, il2cpp_string_new_utf16, (const Il2CppChar * text, int32_t len));
DO_API(Il2CppString*, il2cpp_string_new_wrapper, (const char* str));
DO_API(Il2CppString*, il2cpp_string_intern, (Il2CppString * str));
DO_API(Il2CppString*, il2cpp_string_is_interned, (Il2CppString * str));
// thread
DO_API(Il2CppThread*, il2cpp_thread_current, ());
DO_API(Il2CppThread*, il2cpp_thread_attach, (Il2CppDomain * domain));
DO_API(void, il2cpp_thread_detach, (Il2CppThread * thread));
DO_API(Il2CppThread**, il2cpp_thread_get_all_attached_threads, (size_t * size));
DO_API(bool, il2cpp_is_vm_thread, (Il2CppThread * thread));
// stacktrace
DO_API(void, il2cpp_current_thread_walk_frame_stack, (Il2CppFrameWalkFunc func, void* user_data));
DO_API(void, il2cpp_thread_walk_frame_stack, (Il2CppThread * thread, Il2CppFrameWalkFunc func, void* user_data));
DO_API(bool, il2cpp_current_thread_get_top_frame, (Il2CppStackFrameInfo * frame));
DO_API(bool, il2cpp_thread_get_top_frame, (Il2CppThread * thread, Il2CppStackFrameInfo * frame));
DO_API(bool, il2cpp_current_thread_get_frame_at, (int32_t offset, Il2CppStackFrameInfo * frame));
DO_API(bool, il2cpp_thread_get_frame_at, (Il2CppThread * thread, int32_t offset, Il2CppStackFrameInfo * frame));
DO_API(int32_t, il2cpp_current_thread_get_stack_depth, ());
DO_API(int32_t, il2cpp_thread_get_stack_depth, (Il2CppThread * thread));
DO_API(void, il2cpp_override_stack_backtrace, (Il2CppBacktraceFunc stackBacktraceFunc));
// type
DO_API(Il2CppObject*, il2cpp_type_get_object, (const Il2CppType * type));
DO_API(int, il2cpp_type_get_type, (const Il2CppType * type));
DO_API(Il2CppClass*, il2cpp_type_get_class_or_element_class, (const Il2CppType * type));
DO_API(char*, il2cpp_type_get_name, (const Il2CppType * type));
DO_API(bool, il2cpp_type_is_byref, (const Il2CppType * type));
DO_API(uint32_t, il2cpp_type_get_attrs, (const Il2CppType * type));
DO_API(bool, il2cpp_type_equals, (const Il2CppType * type, const Il2CppType * otherType));
DO_API(char*, il2cpp_type_get_assembly_qualified_name, (const Il2CppType * type));
DO_API(bool, il2cpp_type_is_static, (const Il2CppType * type));
DO_API(bool, il2cpp_type_is_pointer_type, (const Il2CppType * type));
// image
DO_API(const Il2CppAssembly*, il2cpp_image_get_assembly, (const Il2CppImage * image));
DO_API(const char*, il2cpp_image_get_name, (const Il2CppImage * image));
DO_API(const char*, il2cpp_image_get_filename, (const Il2CppImage * image));
DO_API(const MethodInfo*, il2cpp_image_get_entry_point, (const Il2CppImage * image));
DO_API(size_t, il2cpp_image_get_class_count, (const Il2CppImage * image));
DO_API(const Il2CppClass*, il2cpp_image_get_class, (const Il2CppImage * image, size_t index));
// Memory information
DO_API(Il2CppManagedMemorySnapshot*, il2cpp_capture_memory_snapshot, ());
DO_API(void, il2cpp_free_captured_memory_snapshot, (Il2CppManagedMemorySnapshot * snapshot));
DO_API(void, il2cpp_set_find_plugin_callback, (Il2CppSetFindPlugInCallback method));
// Logging
DO_API(void, il2cpp_register_log_callback, (Il2CppLogCallback method));
// Debugger
DO_API(void, il2cpp_debugger_set_agent_options, (const char* options));
DO_API(bool, il2cpp_is_debugger_attached, ());
DO_API(void, il2cpp_register_debugger_agent_transport, (Il2CppDebuggerTransport * debuggerTransport));
// Debug metadata
DO_API(bool, il2cpp_debug_get_method_info, (const MethodInfo*, Il2CppMethodDebugInfo * methodDebugInfo));
// TLS module
DO_API(void, il2cpp_unity_install_unitytls_interface, (const void* unitytlsInterfaceStruct));
// custom attributes
DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_class, (Il2CppClass * klass));
DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_method, (const MethodInfo * method));
DO_API(Il2CppObject*, il2cpp_custom_attrs_get_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass * attr_klass));
DO_API(bool, il2cpp_custom_attrs_has_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass * attr_klass));
DO_API(Il2CppArray*, il2cpp_custom_attrs_construct, (Il2CppCustomAttrInfo * cinfo));
DO_API(void, il2cpp_custom_attrs_free, (Il2CppCustomAttrInfo * ainfo));
// Il2CppClass user data for GetComponent optimization
DO_API(void, il2cpp_class_set_userdata, (Il2CppClass * klass, void* userdata));
DO_API(int, il2cpp_class_get_userdata_offset, ());
DO_API(void, il2cpp_set_default_thread_affinity, (int64_t affinity_mask));
================================================
FILE: app/src/main/jni/Il2Cpp/il2cpp-class.h
================================================
typedef uint16_t Il2CppChar;
typedef uintptr_t il2cpp_array_size_t;
typedef int32_t TypeDefinitionIndex;
typedef int32_t GenericParameterIndex;
typedef char Il2CppNativeChar;
typedef struct Il2CppMemoryCallbacks Il2CppMemoryCallbacks;
typedef struct Il2CppImage Il2CppImage;
typedef struct Il2CppClass Il2CppClass;
typedef struct Il2CppArrayBounds Il2CppArrayBounds;
typedef struct Il2CppAssembly Il2CppAssembly;
typedef struct Il2CppArrayType Il2CppArrayType;
typedef struct Il2CppGenericClass Il2CppGenericClass;
typedef struct Il2CppReflectionType Il2CppReflectionType;
typedef struct MonitorData MonitorData;
typedef Il2CppClass Il2CppVTable;
typedef struct EventInfo EventInfo;
typedef struct FieldInfo FieldInfo;
typedef struct PropertyInfo PropertyInfo;
typedef struct Il2CppDomain Il2CppDomain;
typedef struct Il2CppException Il2CppException;
typedef struct Il2CppObject Il2CppObject;
typedef struct Il2CppReflectionMethod Il2CppReflectionMethod;
typedef struct Il2CppString Il2CppString;
typedef struct Il2CppThread Il2CppThread;
typedef struct Il2CppStackFrameInfo Il2CppStackFrameInfo;
typedef struct Il2CppManagedMemorySnapshot Il2CppManagedMemorySnapshot;
typedef struct Il2CppDebuggerTransport Il2CppDebuggerTransport;
typedef struct Il2CppMethodDebugInfo Il2CppMethodDebugInfo;
typedef struct Il2CppCustomAttrInfo Il2CppCustomAttrInfo;
typedef const struct ___Il2CppMetadataTypeHandle *Il2CppMetadataTypeHandle;
typedef const struct ___Il2CppMetadataGenericParameterHandle *Il2CppMetadataGenericParameterHandle;
typedef void (*Il2CppMethodPointer)();
typedef void (*il2cpp_register_object_callback)(Il2CppObject **arr, int size, void *userdata);
typedef void *(*il2cpp_liveness_reallocate_callback)(void *ptr, size_t size, void *userdata);
typedef void (*Il2CppFrameWalkFunc)(const Il2CppStackFrameInfo *info, void *user_data);
typedef size_t(*Il2CppBacktraceFunc)(Il2CppMethodPointer *buffer, size_t maxSize);
typedef const Il2CppNativeChar *(*Il2CppSetFindPlugInCallback)(const Il2CppNativeChar *);
typedef void (*Il2CppLogCallback)(const char *);
typedef enum {
IL2CPP_UNHANDLED_POLICY_LEGACY,
IL2CPP_UNHANDLED_POLICY_CURRENT
} Il2CppRuntimeUnhandledExceptionPolicy;
typedef enum {
IL2CPP_GC_MODE_DISABLED = 0,
IL2CPP_GC_MODE_ENABLED = 1,
IL2CPP_GC_MODE_MANUAL = 2
} Il2CppGCMode;
typedef enum Il2CppStat {
IL2CPP_STAT_NEW_OBJECT_COUNT,
IL2CPP_STAT_INITIALIZED_CLASS_COUNT,
IL2CPP_STAT_METHOD_COUNT,
IL2CPP_STAT_CLASS_STATIC_DATA_SIZE,
IL2CPP_STAT_GENERIC_INSTANCE_COUNT,
IL2CPP_STAT_GENERIC_CLASS_COUNT,
IL2CPP_STAT_INFLATED_METHOD_COUNT,
IL2CPP_STAT_INFLATED_TYPE_COUNT,
} Il2CppStat;
typedef enum Il2CppTypeEnum {
IL2CPP_TYPE_END = 0x00,
IL2CPP_TYPE_VOID = 0x01,
IL2CPP_TYPE_BOOLEAN = 0x02,
IL2CPP_TYPE_CHAR = 0x03,
IL2CPP_TYPE_I1 = 0x04,
IL2CPP_TYPE_U1 = 0x05,
IL2CPP_TYPE_I2 = 0x06,
IL2CPP_TYPE_U2 = 0x07,
IL2CPP_TYPE_I4 = 0x08,
IL2CPP_TYPE_U4 = 0x09,
IL2CPP_TYPE_I8 = 0x0a,
IL2CPP_TYPE_U8 = 0x0b,
IL2CPP_TYPE_R4 = 0x0c,
IL2CPP_TYPE_R8 = 0x0d,
IL2CPP_TYPE_STRING = 0x0e,
IL2CPP_TYPE_PTR = 0x0f,
IL2CPP_TYPE_BYREF = 0x10,
IL2CPP_TYPE_VALUETYPE = 0x11,
IL2CPP_TYPE_CLASS = 0x12,
IL2CPP_TYPE_VAR = 0x13,
IL2CPP_TYPE_ARRAY = 0x14,
IL2CPP_TYPE_GENERICINST = 0x15,
IL2CPP_TYPE_TYPEDBYREF = 0x16,
IL2CPP_TYPE_I = 0x18,
IL2CPP_TYPE_U = 0x19,
IL2CPP_TYPE_FNPTR = 0x1b,
IL2CPP_TYPE_OBJECT = 0x1c,
IL2CPP_TYPE_SZARRAY = 0x1d,
IL2CPP_TYPE_MVAR = 0x1e,
IL2CPP_TYPE_CMOD_REQD = 0x1f,
IL2CPP_TYPE_CMOD_OPT = 0x20,
IL2CPP_TYPE_INTERNAL = 0x21,
IL2CPP_TYPE_MODIFIER = 0x40,
IL2CPP_TYPE_SENTINEL = 0x41,
IL2CPP_TYPE_PINNED = 0x45,
IL2CPP_TYPE_ENUM = 0x55,
IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff
} Il2CppTypeEnum;
typedef struct Il2CppType {
union {
void *dummy;
TypeDefinitionIndex klassIndex;
const Il2CppType *type;
Il2CppArrayType *array;
GenericParameterIndex genericParameterIndex;
Il2CppGenericClass *generic_class;
} data;
unsigned int attrs: 16;
Il2CppTypeEnum type: 8;
unsigned int num_mods: 6;
unsigned int byref: 1;
unsigned int pinned: 1;
} Il2CppType;
typedef struct MethodInfo {
Il2CppMethodPointer methodPointer;
} MethodInfo;
typedef struct Il2CppObject {
union {
Il2CppClass *klass;
Il2CppVTable *vtable;
};
MonitorData *monitor;
} Il2CppObject;
typedef struct Il2CppArray {
Il2CppObject obj;
Il2CppArrayBounds *bounds;
il2cpp_array_size_t max_length;
void *vector[32];
} Il2CppArray;
================================================
FILE: app/src/main/jni/Il2Cpp/il2cpp-tabledefs.h
================================================
#pragma once
/*
* Field Attributes (21.1.5).
*/
#define FIELD_ATTRIBUTE_FIELD_ACCESS_MASK 0x0007
#define FIELD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000
#define FIELD_ATTRIBUTE_PRIVATE 0x0001
#define FIELD_ATTRIBUTE_FAM_AND_ASSEM 0x0002
#define FIELD_ATTRIBUTE_ASSEMBLY 0x0003
#define FIELD_ATTRIBUTE_FAMILY 0x0004
#define FIELD_ATTRIBUTE_FAM_OR_ASSEM 0x0005
#define FIELD_ATTRIBUTE_PUBLIC 0x0006
#define FIELD_ATTRIBUTE_STATIC 0x0010
#define FIELD_ATTRIBUTE_INIT_ONLY 0x0020
#define FIELD_ATTRIBUTE_LITERAL 0x0040
#define FIELD_ATTRIBUTE_NOT_SERIALIZED 0x0080
#define FIELD_ATTRIBUTE_SPECIAL_NAME 0x0200
#define FIELD_ATTRIBUTE_PINVOKE_IMPL 0x2000
/* For runtime use only */
#define FIELD_ATTRIBUTE_RESERVED_MASK 0x9500
#define FIELD_ATTRIBUTE_RT_SPECIAL_NAME 0x0400
#define FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL 0x1000
#define FIELD_ATTRIBUTE_HAS_DEFAULT 0x8000
#define FIELD_ATTRIBUTE_HAS_FIELD_RVA 0x0100
/*
* Method Attributes (22.1.9)
*/
#define METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK 0x0003
#define METHOD_IMPL_ATTRIBUTE_IL 0x0000
#define METHOD_IMPL_ATTRIBUTE_NATIVE 0x0001
#define METHOD_IMPL_ATTRIBUTE_OPTIL 0x0002
#define METHOD_IMPL_ATTRIBUTE_RUNTIME 0x0003
#define METHOD_IMPL_ATTRIBUTE_MANAGED_MASK 0x0004
#define METHOD_IMPL_ATTRIBUTE_UNMANAGED 0x0004
#define METHOD_IMPL_ATTRIBUTE_MANAGED 0x0000
#define METHOD_IMPL_ATTRIBUTE_FORWARD_REF 0x0010
#define METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG 0x0080
#define METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL 0x1000
#define METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED 0x0020
#define METHOD_IMPL_ATTRIBUTE_NOINLINING 0x0008
#define METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL 0xffff
#define METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK 0x0007
#define METHOD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000
#define METHOD_ATTRIBUTE_PRIVATE 0x0001
#define METHOD_ATTRIBUTE_FAM_AND_ASSEM 0x0002
#define METHOD_ATTRIBUTE_ASSEM 0x0003
#define METHOD_ATTRIBUTE_FAMILY 0x0004
#define METHOD_ATTRIBUTE_FAM_OR_ASSEM 0x0005
#define METHOD_ATTRIBUTE_PUBLIC 0x0006
#define METHOD_ATTRIBUTE_STATIC 0x0010
#define METHOD_ATTRIBUTE_FINAL 0x0020
#define METHOD_ATTRIBUTE_VIRTUAL 0x0040
#define METHOD_ATTRIBUTE_HIDE_BY_SIG 0x0080
#define METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK 0x0100
#define METHOD_ATTRIBUTE_REUSE_SLOT 0x0000
#define METHOD_ATTRIBUTE_NEW_SLOT 0x0100
#define METHOD_ATTRIBUTE_STRICT 0x0200
#define METHOD_ATTRIBUTE_ABSTRACT 0x0400
#define METHOD_ATTRIBUTE_SPECIAL_NAME 0x0800
#define METHOD_ATTRIBUTE_PINVOKE_IMPL 0x2000
#define METHOD_ATTRIBUTE_UNMANAGED_EXPORT 0x0008
/*
* For runtime use only
*/
#define METHOD_ATTRIBUTE_RESERVED_MASK 0xd000
#define METHOD_ATTRIBUTE_RT_SPECIAL_NAME 0x1000
#define METHOD_ATTRIBUTE_HAS_SECURITY 0x4000
#define METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT 0x8000
/*
* Type Attributes (21.1.13).
*/
#define TYPE_ATTRIBUTE_VISIBILITY_MASK 0x00000007
#define TYPE_ATTRIBUTE_NOT_PUBLIC 0x00000000
#define TYPE_ATTRIBUTE_PUBLIC 0x00000001
#define TYPE_ATTRIBUTE_NESTED_PUBLIC 0x00000002
#define TYPE_ATTRIBUTE_NESTED_PRIVATE 0x00000003
#define TYPE_ATTRIBUTE_NESTED_FAMILY 0x00000004
#define TYPE_ATTRIBUTE_NESTED_ASSEMBLY 0x00000005
#define TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM 0x00000006
#define TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM 0x00000007
#define TYPE_ATTRIBUTE_LAYOUT_MASK 0x00000018
#define TYPE_ATTRIBUTE_AUTO_LAYOUT 0x00000000
#define TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT 0x00000008
#define TYPE_ATTRIBUTE_EXPLICIT_LAYOUT 0x00000010
#define TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK 0x00000020
#define TYPE_ATTRIBUTE_CLASS 0x00000000
#define TYPE_ATTRIBUTE_INTERFACE 0x00000020
#define TYPE_ATTRIBUTE_ABSTRACT 0x00000080
#define TYPE_ATTRIBUTE_SEALED 0x00000100
#define TYPE_ATTRIBUTE_SPECIAL_NAME 0x00000400
#define TYPE_ATTRIBUTE_IMPORT 0x00001000
#define TYPE_ATTRIBUTE_SERIALIZABLE 0x00002000
#define TYPE_ATTRIBUTE_STRING_FORMAT_MASK 0x00030000
#define TYPE_ATTRIBUTE_ANSI_CLASS 0x00000000
#define TYPE_ATTRIBUTE_UNICODE_CLASS 0x00010000
#define TYPE_ATTRIBUTE_AUTO_CLASS 0x00020000
#define TYPE_ATTRIBUTE_BEFORE_FIELD_INIT 0x00100000
#define TYPE_ATTRIBUTE_FORWARDER 0x00200000
#define TYPE_ATTRIBUTE_RESERVED_MASK 0x00040800
#define TYPE_ATTRIBUTE_RT_SPECIAL_NAME 0x00000800
#define TYPE_ATTRIBUTE_HAS_SECURITY 0x00040000
/*
* Flags for Params (22.1.12)
*/
#define PARAM_ATTRIBUTE_IN 0x0001
#define PARAM_ATTRIBUTE_OUT 0x0002
#define PARAM_ATTRIBUTE_OPTIONAL 0x0010
#define PARAM_ATTRIBUTE_RESERVED_MASK 0xf000
#define PARAM_ATTRIBUTE_HAS_DEFAULT 0x1000
#define PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL 0x2000
#define PARAM_ATTRIBUTE_UNUSED 0xcfe0
// Flags for Generic Parameters (II.23.1.7)
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_NON_VARIANT 0x00
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_COVARIANT 0x01
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT 0x02
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK 0x03
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT 0x04
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_NOT_NULLABLE_VALUE_TYPE_CONSTRAINT 0x08
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_DEFAULT_CONSTRUCTOR_CONSTRAINT 0x10
#define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINT_MASK 0x1C
/**
* 21.5 AssemblyRefs
*/
#define ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG 0x00000001
#define ASSEMBLYREF_RETARGETABLE_FLAG 0x00000100
#define ASSEMBLYREF_ENABLEJITCOMPILE_TRACKING_FLAG 0x00008000
#define ASSEMBLYREF_DISABLEJITCOMPILE_OPTIMIZER_FLAG 0x00004000
================================================
FILE: app/src/main/jni/Il2Cpp/il2cpp_dump.cpp
================================================
//
// Created by Perfare on 2020/7/4.
//
#include "il2cpp_dump.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xdl.h"
#include "Includes/log.h"
#include "il2cpp-tabledefs.h"
#include "il2cpp-class.h"
#include
#define DO_API(r, n, p) r (*n) p
#include "il2cpp-api-functions.h"
#undef DO_API
static uint64_t il2cpp_base = 0;
const char *GetPackageName() {
//https://stackoverflow.com/questions/42918762/how-to-get-app-package-name-or-applicationid-using-jni-android
char *application_id[256];
FILE *fp = fopen("proc/self/cmdline", "r");
if (fp) {
fread(application_id, sizeof(application_id), 1, fp);
fclose(fp);
}
return (const char *) application_id;
}
void init_il2cpp_api(void *handle) {
#define DO_API(r, n, p) n = (r (*) p)dlsym(handle, #n)
// Do not use XDL yet because it doesn't support emulators
/*#define DO_API(r, n, p) { \
n = (r (*) p)xdl_sym(handle, #n, nullptr); \
if(!n) { \
LOGW("api not found %s", #n); \
} \
}*/
#include "il2cpp-api-functions.h"
#undef DO_API
}
std::string get_method_modifier(uint32_t flags) {
std::stringstream outPut;
auto access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
switch (access) {
case METHOD_ATTRIBUTE_PRIVATE:
outPut << "private ";
break;
case METHOD_ATTRIBUTE_PUBLIC:
outPut << "public ";
break;
case METHOD_ATTRIBUTE_FAMILY:
outPut << "protected ";
break;
case METHOD_ATTRIBUTE_ASSEM:
case METHOD_ATTRIBUTE_FAM_AND_ASSEM:
outPut << "internal ";
break;
case METHOD_ATTRIBUTE_FAM_OR_ASSEM:
outPut << "protected internal ";
break;
}
if (flags & METHOD_ATTRIBUTE_STATIC) {
outPut << "static ";
}
if (flags & METHOD_ATTRIBUTE_ABSTRACT) {
outPut << "abstract ";
if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_REUSE_SLOT) {
outPut << "override ";
}
} else if (flags & METHOD_ATTRIBUTE_FINAL) {
if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_REUSE_SLOT) {
outPut << "sealed override ";
}
} else if (flags & METHOD_ATTRIBUTE_VIRTUAL) {
if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_NEW_SLOT) {
outPut << "virtual ";
} else {
outPut << "override ";
}
}
if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
outPut << "extern ";
}
return outPut.str();
}
bool _il2cpp_type_is_byref(const Il2CppType *type) {
auto byref = type->byref;
if (il2cpp_type_is_byref) {
byref = il2cpp_type_is_byref(type);
}
return byref;
}
std::string dump_method(Il2CppClass *klass) {
std::stringstream outPut;
outPut << "\n\t// Methods\n";
void *iter = nullptr;
while (auto method = il2cpp_class_get_methods(klass, &iter)) {
//TODO attribute
if (method->methodPointer) {
outPut << "\t// RVA: 0x";
outPut << std::hex << (uint64_t) method->methodPointer - il2cpp_base;
outPut << " VA: 0x";
outPut << std::hex << (uint64_t) method->methodPointer;
} else {
outPut << "\t// RVA: 0x VA: 0x0";
}
/*if (method->slot != 65535) {
outPut << " Slot: " << std::dec << method->slot;
}*/
outPut << "\n\t";
uint32_t iflags = 0;
auto flags = il2cpp_method_get_flags(method, &iflags);
outPut << get_method_modifier(flags);
//TODO genericContainerIndex
auto return_type = il2cpp_method_get_return_type(method);
if (_il2cpp_type_is_byref(return_type)) {
outPut << "ref ";
}
auto return_class = il2cpp_class_from_type(return_type);
outPut << il2cpp_class_get_name(return_class) << " " << il2cpp_method_get_name(method)
<< "(";
auto param_count = il2cpp_method_get_param_count(method);
for (int i = 0; i < param_count; ++i) {
auto param = il2cpp_method_get_param(method, i);
auto attrs = param->attrs;
if (_il2cpp_type_is_byref(param)) {
if (attrs & PARAM_ATTRIBUTE_OUT && !(attrs & PARAM_ATTRIBUTE_IN)) {
outPut << "out ";
} else if (attrs & PARAM_ATTRIBUTE_IN && !(attrs & PARAM_ATTRIBUTE_OUT)) {
outPut << "in ";
} else {
outPut << "ref ";
}
} else {
if (attrs & PARAM_ATTRIBUTE_IN) {
outPut << "[In] ";
}
if (attrs & PARAM_ATTRIBUTE_OUT) {
outPut << "[Out] ";
}
}
auto parameter_class = il2cpp_class_from_type(param);
outPut << il2cpp_class_get_name(parameter_class) << " "
<< il2cpp_method_get_param_name(method, i);
outPut << ", ";
}
if (param_count > 0) {
outPut.seekp(-2, outPut.cur);
}
outPut << ") { }\n";
//TODO GenericInstMethod
}
return outPut.str();
}
std::string dump_property(Il2CppClass *klass) {
std::stringstream outPut;
outPut << "\n\t// Properties\n";
void *iter = nullptr;
while (auto prop_const = il2cpp_class_get_properties(klass, &iter)) {
//TODO attribute
auto prop = const_cast(prop_const);
auto get = il2cpp_property_get_get_method(prop);
auto set = il2cpp_property_get_set_method(prop);
auto prop_name = il2cpp_property_get_name(prop);
outPut << "\t";
Il2CppClass *prop_class = nullptr;
uint32_t iflags = 0;
if (get) {
outPut << get_method_modifier(il2cpp_method_get_flags(get, &iflags));
prop_class = il2cpp_class_from_type(il2cpp_method_get_return_type(get));
} else if (set) {
outPut << get_method_modifier(il2cpp_method_get_flags(set, &iflags));
auto param = il2cpp_method_get_param(set, 0);
prop_class = il2cpp_class_from_type(param);
}
if (prop_class) {
outPut << il2cpp_class_get_name(prop_class) << " " << prop_name << " { ";
if (get) {
outPut << "get; ";
}
if (set) {
outPut << "set; ";
}
outPut << "}\n";
} else {
if (prop_name) {
outPut << " // unknown property " << prop_name;
}
}
}
return outPut.str();
}
std::string dump_field(Il2CppClass *klass) {
std::stringstream outPut;
outPut << "\n\t// Fields\n";
auto is_enum = il2cpp_class_is_enum(klass);
void *iter = nullptr;
while (auto field = il2cpp_class_get_fields(klass, &iter)) {
//TODO attribute
outPut << "\t";
auto attrs = il2cpp_field_get_flags(field);
auto access = attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
switch (access) {
case FIELD_ATTRIBUTE_PRIVATE:
outPut << "private ";
break;
case FIELD_ATTRIBUTE_PUBLIC:
outPut << "public ";
break;
case FIELD_ATTRIBUTE_FAMILY:
outPut << "protected ";
break;
case FIELD_ATTRIBUTE_ASSEMBLY:
case FIELD_ATTRIBUTE_FAM_AND_ASSEM:
outPut << "internal ";
break;
case FIELD_ATTRIBUTE_FAM_OR_ASSEM:
outPut << "protected internal ";
break;
}
if (attrs & FIELD_ATTRIBUTE_LITERAL) {
outPut << "const ";
} else {
if (attrs & FIELD_ATTRIBUTE_STATIC) {
outPut << "static ";
}
if (attrs & FIELD_ATTRIBUTE_INIT_ONLY) {
outPut << "readonly ";
}
}
auto field_type = il2cpp_field_get_type(field);
auto field_class = il2cpp_class_from_type(field_type);
outPut << il2cpp_class_get_name(field_class) << " " << il2cpp_field_get_name(field);
//TODO 获取构造函数初始化后的字段值
if (attrs & FIELD_ATTRIBUTE_LITERAL && is_enum) {
uint64_t val = 0;
il2cpp_field_static_get_value(field, &val);
outPut << " = " << std::dec << val;
}
outPut << "; // 0x" << std::hex << il2cpp_field_get_offset(field) << "\n";
}
return outPut.str();
}
std::string dump_type(const Il2CppType *type) {
std::stringstream outPut;
auto *klass = il2cpp_class_from_type(type);
outPut << "\n// Namespace: " << il2cpp_class_get_namespace(klass) << "\n";
auto flags = il2cpp_class_get_flags(klass);
if (flags & TYPE_ATTRIBUTE_SERIALIZABLE) {
outPut << "[Serializable]\n";
}
//TODO attribute
auto is_valuetype = il2cpp_class_is_valuetype(klass);
auto is_enum = il2cpp_class_is_enum(klass);
auto visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK;
switch (visibility) {
case TYPE_ATTRIBUTE_PUBLIC:
case TYPE_ATTRIBUTE_NESTED_PUBLIC:
outPut << "public ";
break;
case TYPE_ATTRIBUTE_NOT_PUBLIC:
case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM:
case TYPE_ATTRIBUTE_NESTED_ASSEMBLY:
outPut << "internal ";
break;
case TYPE_ATTRIBUTE_NESTED_PRIVATE:
outPut << "private ";
break;
case TYPE_ATTRIBUTE_NESTED_FAMILY:
outPut << "protected ";
break;
case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM:
outPut << "protected internal ";
break;
}
if (flags & TYPE_ATTRIBUTE_ABSTRACT && flags & TYPE_ATTRIBUTE_SEALED) {
outPut << "static ";
} else if (!(flags & TYPE_ATTRIBUTE_INTERFACE) && flags & TYPE_ATTRIBUTE_ABSTRACT) {
outPut << "abstract ";
} else if (!is_valuetype && !is_enum && flags & TYPE_ATTRIBUTE_SEALED) {
outPut << "sealed ";
}
if (flags & TYPE_ATTRIBUTE_INTERFACE) {
outPut << "interface ";
} else if (is_enum) {
outPut << "enum ";
} else if (is_valuetype) {
outPut << "struct ";
} else {
outPut << "class ";
}
outPut << il2cpp_class_get_name(klass); //TODO genericContainerIndex
std::vector extends;
auto parent = il2cpp_class_get_parent(klass);
if (!is_valuetype && !is_enum && parent) {
auto parent_type = il2cpp_class_get_type(parent);
if (parent_type->type != IL2CPP_TYPE_OBJECT) {
extends.emplace_back(il2cpp_class_get_name(parent));
}
}
void *iter = nullptr;
while (auto itf = il2cpp_class_get_interfaces(klass, &iter)) {
extends.emplace_back(il2cpp_class_get_name(itf));
}
if (!extends.empty()) {
outPut << " : " << extends[0];
for (int i = 1; i < extends.size(); ++i) {
outPut << ", " << extends[i];
}
}
outPut << "\n{";
outPut << dump_field(klass);
outPut << dump_property(klass);
outPut << dump_method(klass);
//TODO EventInfo
outPut << "}\n";
return outPut.str();
}
void il2cpp_api_init(void *handle) {
LOGI("il2cpp_handle: %p", handle);
init_il2cpp_api(handle);
if (il2cpp_domain_get_assemblies) {
Dl_info dlInfo;
if (dladdr((void *) il2cpp_domain_get_assemblies, &dlInfo)) {
il2cpp_base = reinterpret_cast(dlInfo.dli_fbase);
}
LOGI("il2cpp_base: %" PRIx64"", il2cpp_base);
} else {
LOGE("Failed to initialize il2cpp api.");
return;
}
while (!il2cpp_is_vm_thread(nullptr)) {
LOGI("Waiting for il2cpp_init...");
sleep(1);
}
auto domain = il2cpp_domain_get();
il2cpp_thread_attach(domain);
}
void il2cpp_dump(const char *outDir) {
LOGI("dumping...");
size_t size;
auto domain = il2cpp_domain_get();
auto assemblies = il2cpp_domain_get_assemblies(domain, &size);
//geokar2006's bypass
//auto assemblies = Assembly$$GetAllAssemblies();
//LOGI("Assemblies size %d", assemblies->size());
std::stringstream imageOutput;
/*for (int i = 0; i < assemblies->size(); ++i) {
auto image = il2cpp_assembly_get_image((*assemblies)[i]);*/
for (int i = 0; i < size; ++i) {
auto image = il2cpp_assembly_get_image(assemblies[i]);
imageOutput << "// Image " << i << ": " << il2cpp_image_get_name(image) << "\n";
}
std::vector outPuts;
if (il2cpp_image_get_class) {
LOGI("Version greater than 2018.3");
//使用il2cpp_image_get_class
for (int i = 0; i < size; ++i) {
auto image = il2cpp_assembly_get_image(assemblies[i]);
/*for (auto &assemblie: *assemblies) {
auto image = il2cpp_assembly_get_image(assemblie);*/
std::stringstream imageStr;
imageStr << "\n// Dll : " << il2cpp_image_get_name(image);
auto classCount = il2cpp_image_get_class_count(image);
for (int j = 0; j < classCount; ++j) {
auto klass = il2cpp_image_get_class(image, j);
auto type = il2cpp_class_get_type(const_cast(klass));
//LOGD("type name : %s", il2cpp_type_get_name(type));
auto outPut = imageStr.str() + dump_type(type);
outPuts.push_back(outPut);
}
}
} else {
LOGI("Version less than 2018.3");
//使用反射
auto corlib = il2cpp_get_corlib();
auto assemblyClass = il2cpp_class_from_name(corlib, "System.Reflection", "Assembly");
auto assemblyLoad = il2cpp_class_get_method_from_name(assemblyClass, "Load", 1);
auto assemblyGetTypes = il2cpp_class_get_method_from_name(assemblyClass, "GetTypes", 0);
if (assemblyLoad && assemblyLoad->methodPointer) {
LOGI("Assembly::Load: %p", assemblyLoad->methodPointer);
} else {
LOGI("miss Assembly::Load");
return;
}
if (assemblyGetTypes && assemblyGetTypes->methodPointer) {
LOGI("Assembly::GetTypes: %p", assemblyGetTypes->methodPointer);
} else {
LOGI("miss Assembly::GetTypes");
return;
}
typedef void *(*Assembly_Load_ftn)(void *, Il2CppString *, void *);
typedef Il2CppArray *(*Assembly_GetTypes_ftn)(void *, void *);
for (int i = 0; i < size; ++i) {
auto image = il2cpp_assembly_get_image(assemblies[i]);
/*for (int i = 0; i < assemblies->size(); ++i) {
auto image = il2cpp_assembly_get_image((*assemblies)[i]);*/
std::stringstream imageStr;
auto image_name = il2cpp_image_get_name(image);
imageStr << "\n// Dll : " << image_name;
//LOGD("image name : %s", image->name);
auto imageName = std::string(image_name);
auto pos = imageName.rfind('.');
auto imageNameNoExt = imageName.substr(0, pos);
auto assemblyFileName = il2cpp_string_new(imageNameNoExt.data());
auto reflectionAssembly = ((Assembly_Load_ftn) assemblyLoad->methodPointer)(nullptr,
assemblyFileName,
nullptr);
auto reflectionTypes = ((Assembly_GetTypes_ftn) assemblyGetTypes->methodPointer)(
reflectionAssembly, nullptr);
auto items = reflectionTypes->vector;
for (int j = 0; j < reflectionTypes->max_length; ++j) {
auto klass = il2cpp_class_from_system_type((Il2CppReflectionType *) items[j]);
auto type = il2cpp_class_get_type(klass);
//LOGD("type name : %s", il2cpp_type_get_name(type));
auto outPut = imageStr.str() + dump_type(type);
outPuts.push_back(outPut);
}
}
}
auto outPath = std::string(outDir);
LOGI("Save dump file to %s", outPath.c_str());
std::ofstream outStream(outPath);
outStream << imageOutput.str();
auto count = outPuts.size();
for (int i = 0; i < count; ++i) {
outStream << outPuts[i];
}
outStream.close();
LOGI("dump done!");
}
================================================
FILE: app/src/main/jni/Il2Cpp/il2cpp_dump.h
================================================
//
// Created by Perfare on 2020/7/4.
//
#ifndef AUTO_IL2CPPDUMPER_IL2CPP_DUMP_H
#define AUTO_IL2CPPDUMPER_IL2CPP_DUMP_H
void il2cpp_api_init(void *handle);
void il2cpp_dump(const char *outDir);
const char* GetPackageName();
#endif //AUTO_IL2CPPDUMPER_IL2CPP_DUMP_H
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/include/xdl.h
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
//
// xDL version: 2.0.0
//
// xDL is an enhanced implementation of the Android DL series functions.
// For more information, documentation, and the latest version please check:
// https://github.com/hexhacking/xDL
//
#ifndef IO_GITHUB_HEXHACKING_XDL
#define IO_GITHUB_HEXHACKING_XDL
#include
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
// same as Dl_info:
const char *dli_fname; // Pathname of shared object that contains address.
void *dli_fbase; // Address at which shared object is loaded.
const char *dli_sname; // Name of nearest symbol with address lower than addr.
void *dli_saddr; // Exact address of symbol named in dli_sname.
// added by xDL:
size_t dli_ssize; // Symbol size of nearest symbol with address lower than addr.
const ElfW(Phdr) *dlpi_phdr; // Pointer to array of ELF program headers for this object.
size_t dlpi_phnum; // Number of items in dlpi_phdr.
} xdl_info_t;
//
// Default value for flags in both xdl_open() and xdl_iterate_phdr().
//
#define XDL_DEFAULT 0x00
//
// Enhanced dlopen() / dlclose() / dlsym().
//
#define XDL_TRY_FORCE_LOAD 0x01
#define XDL_ALWAYS_FORCE_LOAD 0x02
void *xdl_open(const char *filename, int flags);
void *xdl_close(void *handle);
void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size);
void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size);
//
// Enhanced dladdr().
//
int xdl_addr(void *addr, xdl_info_t *info, void **cache);
void xdl_addr_clean(void **cache);
//
// Enhanced dl_iterate_phdr().
//
#define XDL_FULL_PATHNAME 0x01
int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags);
//
// Custom dlinfo().
//
#define XDL_DI_DLINFO 1 // type of info: xdl_info_t
int xdl_info(void *handle, int request, void *info);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl.c
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
#include "xdl.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xdl_iterate.h"
#include "xdl_linker.h"
#include "xdl_lzma.h"
#include "xdl_util.h"
#ifndef __LP64__
#define XDL_LIB_PATH "/system/lib"
#else
#define XDL_LIB_PATH "/system/lib64"
#endif
#define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx))
#define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \
(SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE))
extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpadded"
typedef struct xdl {
char *pathname;
uintptr_t load_bias;
const ElfW(Phdr) *dlpi_phdr;
ElfW(Half) dlpi_phnum;
struct xdl *next; // to next xdl obj for cache in xdl_addr()
void *linker_handle; // hold handle returned by xdl_linker_force_dlopen()
//
// (1) for searching symbols from .dynsym
//
bool dynsym_try_load;
ElfW(Sym) *dynsym; // .dynsym
const char *dynstr; // .dynstr
// .hash (SYSV hash for .dynstr)
struct {
const uint32_t *buckets;
uint32_t buckets_cnt;
const uint32_t *chains;
uint32_t chains_cnt;
} sysv_hash;
// .gnu.hash (GNU hash for .dynstr)
struct {
const uint32_t *buckets;
uint32_t buckets_cnt;
const uint32_t *chains;
uint32_t symoffset;
const ElfW(Addr) *bloom;
uint32_t bloom_cnt;
uint32_t bloom_shift;
} gnu_hash;
//
// (2) for searching symbols from .symtab
//
bool symtab_try_load;
uintptr_t base;
ElfW(Sym) *symtab; // .symtab
size_t symtab_cnt;
char *strtab; // .strtab
size_t strtab_sz;
} xdl_t;
#pragma clang diagnostic pop
// load from memory
static int xdl_dynsym_load(xdl_t *self) {
// find the dynamic segment
ElfW(Dyn) *dynamic = NULL;
for (size_t i = 0; i < self->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
if (PT_DYNAMIC == phdr->p_type) {
dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr);
break;
}
}
if (NULL == dynamic) return -1;
// iterate the dynamic segment
for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) {
switch (entry->d_tag) {
case DT_SYMTAB: //.dynsym
self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr);
break;
case DT_STRTAB: //.dynstr
self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr);
break;
case DT_HASH: //.hash
self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]);
self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]);
break;
case DT_GNU_HASH: //.gnu.hash
self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0];
self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1];
self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2];
self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3];
self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16);
self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt]));
self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt]));
break;
default:
break;
}
}
if (NULL == self->dynsym || NULL == self->dynstr ||
(0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) {
self->dynsym = NULL;
self->dynstr = NULL;
self->sysv_hash.buckets_cnt = 0;
self->gnu_hash.buckets_cnt = 0;
return -1;
}
return 0;
}
static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) {
if (0 == data_len) return NULL;
if (data_offset >= file_sz) return NULL;
if (data_offset + data_len > file_sz) return NULL;
if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL;
void *data = malloc(data_len);
if (NULL == data) return NULL;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-statement-expression"
if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len)))
#pragma clang diagnostic pop
{
free(data);
return NULL;
}
return data;
}
static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) {
return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size);
}
static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
if (0 == data_len) return NULL;
if (data_offset >= mem_sz) return NULL;
if (data_offset + data_len > mem_sz) return NULL;
void *data = malloc(data_len);
if (NULL == data) return NULL;
memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len);
return data;
}
static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
}
static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) {
if (0 == data_len) return NULL;
if (data_offset >= mem_sz) return NULL;
if (data_offset + data_len > mem_sz) return NULL;
return (void *)((uintptr_t)mem + data_offset);
}
static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) {
return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size);
}
// load from disk and memory
static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz,
ElfW(Shdr) *shdr_debugdata) {
void *debugdata = NULL;
ElfW(Shdr) *shdrs = NULL;
int r = -1;
// get zipped .gnu_debugdata
uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata);
if (NULL == debugdata_zip) return -1;
// get unzipped .gnu_debugdata
size_t debugdata_sz;
if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz))
goto end;
// get ELF header
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata;
if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
// get section headers
shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff,
ehdr->e_shentsize * ehdr->e_shnum);
if (NULL == shdrs) goto end;
// get .shstrtab
if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx);
if (NULL == shstrtab) goto end;
// find .symtab & .strtab
for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
char *shdr_name = shstrtab + shdr->sh_name;
if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
// get & check associated .strtab section
if (shdr->sh_link >= ehdr->e_shnum) continue;
ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
if (SHT_STRTAB != shdr_strtab->sh_type) continue;
// get .symtab & .strtab
ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr);
if (NULL == symtab) continue;
char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab);
if (NULL == strtab) {
free(symtab);
continue;
}
// OK
self->symtab = symtab;
self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
self->strtab = strtab;
self->strtab_sz = shdr_strtab->sh_size;
r = 0;
break;
}
}
end:
free(debugdata_zip);
if (NULL != debugdata) free(debugdata);
if (NULL != shdrs) free(shdrs);
return r;
}
// load from disk and memory
static int xdl_symtab_load(xdl_t *self) {
if ('[' == self->pathname[0]) return -1;
int r = -1;
ElfW(Shdr) *shdrs = NULL;
char *shstrtab = NULL;
// get base address
uintptr_t vaddr_min = UINTPTR_MAX;
for (size_t i = 0; i < self->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]);
if (PT_LOAD == phdr->p_type) {
if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr;
}
}
if (UINTPTR_MAX == vaddr_min) return -1;
self->base = self->load_bias + vaddr_min;
// open file
int flags = O_RDONLY | O_CLOEXEC;
int file_fd;
if ('/' == self->pathname[0]) {
file_fd = open(self->pathname, flags);
} else {
char full_pathname[1024];
// try the fast method
snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname);
file_fd = open(full_pathname, flags);
if (file_fd < 0) {
// try the slow method
if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1;
file_fd = open(full_pathname, flags);
}
}
if (file_fd < 0) return -1;
struct stat st;
if (0 != fstat(file_fd, &st)) goto end;
size_t file_sz = (size_t)st.st_size;
// get ELF header
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base;
if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end;
// get section headers
shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff,
ehdr->e_shentsize * ehdr->e_shnum);
if (NULL == shdrs) goto end;
// get .shstrtab
if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end;
shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx);
if (NULL == shstrtab) goto end;
// find .symtab & .strtab
for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) {
char *shdr_name = shstrtab + shdr->sh_name;
if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) {
// get & check associated .strtab section
if (shdr->sh_link >= ehdr->e_shnum) continue;
ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link;
if (SHT_STRTAB != shdr_strtab->sh_type) continue;
// get .symtab & .strtab
ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr);
if (NULL == symtab) continue;
char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab);
if (NULL == strtab) {
free(symtab);
continue;
}
// OK
self->symtab = symtab;
self->symtab_cnt = shdr->sh_size / shdr->sh_entsize;
self->strtab = strtab;
self->strtab_sz = shdr_strtab->sh_size;
r = 0;
break;
} else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) {
if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) {
// OK
r = 0;
break;
}
}
}
end:
close(file_fd);
if (NULL != shdrs) free(shdrs);
if (NULL != shstrtab) free(shstrtab);
return r;
}
static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) {
if (NULL == getauxval) return NULL; // API level < 18
uintptr_t val = (uintptr_t)getauxval(type);
if (0 == val) return NULL;
// get base
uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val);
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL;
// ELF info
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
ElfW(Half) dlpi_phnum = ehdr->e_phnum;
// get bias
uintptr_t min_vaddr = UINTPTR_MAX;
for (size_t i = 0; i < dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
if (PT_LOAD == phdr->p_type) {
if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
}
}
if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL;
uintptr_t load_bias = base - min_vaddr;
// create xDL object
xdl_t *self;
if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL;
if (NULL == (self->pathname = strdup(pathname))) {
free(self);
return NULL;
}
self->load_bias = load_bias;
self->dlpi_phdr = dlpi_phdr;
self->dlpi_phnum = dlpi_phnum;
self->dynsym_try_load = false;
self->symtab_try_load = false;
return self;
}
static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
(void)size;
uintptr_t *pkg = (uintptr_t *)arg;
xdl_t **self = (xdl_t **)*pkg++;
const char *filename = (const char *)*pkg;
// check load_bias
if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0;
// check pathname
if ('[' == filename[0]) {
if (0 != strcmp(info->dlpi_name, filename)) return 0;
} else if ('/' == filename[0]) {
if ('/' == info->dlpi_name[0]) {
if (0 != strcmp(info->dlpi_name, filename)) return 0;
} else {
if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0;
}
} else {
if ('/' == info->dlpi_name[0]) {
if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0;
} else {
if (0 != strcmp(info->dlpi_name, filename)) return 0;
}
}
// found the target ELF
if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // return failed
if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) {
free(*self);
*self = NULL;
return 1; // return failed
}
(*self)->load_bias = info->dlpi_addr;
(*self)->dlpi_phdr = info->dlpi_phdr;
(*self)->dlpi_phnum = info->dlpi_phnum;
(*self)->dynsym_try_load = false;
(*self)->symtab_try_load = false;
return 1; // return OK
}
static xdl_t *xdl_find(const char *filename) {
// from auxv (linker, vDSO)
xdl_t *self = NULL;
if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME))
self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME);
else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME))
self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME);
// from auxv (app_process)
const char *basename, *pathname;
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
if (xdl_util_get_api_level() < __ANDROID_API_L__) {
basename = XDL_UTIL_APP_PROCESS_BASENAME_K;
pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K;
} else
#endif
{
basename = XDL_UTIL_APP_PROCESS_BASENAME;
pathname = XDL_UTIL_APP_PROCESS_PATHNAME;
}
if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname);
if (NULL != self) return self;
// from dl_iterate_phdr
uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename};
xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT);
return self;
}
static void *xdl_open_always_force(const char *filename) {
// always force dlopen()
void *linker_handle = xdl_linker_force_dlopen(filename);
if (NULL == linker_handle) return NULL;
// find
xdl_t *self = xdl_find(filename);
if (NULL == self)
dlclose(linker_handle);
else
self->linker_handle = linker_handle;
return (void *)self;
}
static void *xdl_open_try_force(const char *filename) {
// find
xdl_t *self = xdl_find(filename);
if (NULL != self) return (void *)self;
// try force dlopen()
void *linker_handle = xdl_linker_force_dlopen(filename);
if (NULL == linker_handle) return NULL;
// find again
self = xdl_find(filename);
if (NULL == self)
dlclose(linker_handle);
else
self->linker_handle = linker_handle;
return (void *)self;
}
void *xdl_open(const char *filename, int flags) {
if (NULL == filename) return NULL;
if (flags & XDL_ALWAYS_FORCE_LOAD)
return xdl_open_always_force(filename);
else if (flags & XDL_TRY_FORCE_LOAD)
return xdl_open_try_force(filename);
else
return xdl_find(filename);
}
void *xdl_close(void *handle) {
if (NULL == handle) return NULL;
xdl_t *self = (xdl_t *)handle;
if (NULL != self->pathname) free(self->pathname);
if (NULL != self->symtab) free(self->symtab);
if (NULL != self->strtab) free(self->strtab);
void *linker_handle = self->linker_handle;
free(self);
return linker_handle;
}
static uint32_t xdl_sysv_hash(const uint8_t *name) {
uint32_t h = 0, g;
while (*name) {
h = (h << 4) + *name++;
g = h & 0xf0000000;
h ^= g;
h ^= g >> 24;
}
return h;
}
static uint32_t xdl_gnu_hash(const uint8_t *name) {
uint32_t h = 5381;
while (*name) {
h += (h << 5) + *name++;
}
return h;
}
static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) {
uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name);
for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i;
i = self->sysv_hash.chains[i]) {
ElfW(Sym) *sym = self->dynsym + i;
if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue;
return sym;
}
return NULL;
}
static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) {
uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name);
static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;
size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt];
size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) |
(size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits);
// if at least one bit is not set, this symbol is surely missing
if ((word & mask) != mask) return NULL;
// ignore STN_UNDEF
uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt];
if (i < self->gnu_hash.symoffset) return NULL;
// loop through the chain
while (1) {
ElfW(Sym) *sym = self->dynsym + i;
uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset];
if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) {
if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) {
return sym;
}
}
// chain ends with an element with the lowest bit set to 1
if (sym_hash & (uint32_t)1) break;
i++;
}
return NULL;
}
void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) {
if (NULL == handle || NULL == symbol) return NULL;
if (NULL != symbol_size) *symbol_size = 0;
xdl_t *self = (xdl_t *)handle;
// load .dynsym only once
if (!self->dynsym_try_load) {
self->dynsym_try_load = true;
if (0 != xdl_dynsym_load(self)) return NULL;
}
// find symbol
if (NULL == self->dynsym) return NULL;
ElfW(Sym) *sym = NULL;
if (self->gnu_hash.buckets_cnt > 0) {
// use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol);
}
if (NULL == sym && self->sysv_hash.buckets_cnt > 0) {
// use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1)
sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol);
}
if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL;
if (NULL != symbol_size) *symbol_size = sym->st_size;
return (void *)(self->load_bias + sym->st_value);
}
// clang-format off
/*
* For internal symbols in .symtab, LLVM may add some suffixes (for example for thinLTO).
* The format of the suffix is: ".xxxx.[hash]". LLVM may add multiple suffixes at once.
* The symbol name after removing these all suffixes is called canonical name.
*
* Because the hash part in the suffix may change when recompiled, so here we only match
* the canonical name.
*
* IN ADDITION: According to C/C++ syntax, it is illegal for a function name to contain
* dot character('.'), either in the middle or at the end.
*
* samples:
*
* symbol name in .symtab lookup is match
* ---------------------- ---------------- --------
* abcd abc N
* abcd abcd Y
* abcd.llvm.10190306339727611508 abc N
* abcd.llvm.10190306339727611508 abcd Y
* abcd.llvm.10190306339727611508 abcd. N
* abcd.llvm.10190306339727611508 abcd.llvm Y
* abcd.llvm.10190306339727611508 abcd.llvm. N
* abcd.__uniq.513291356003753 abcd.__uniq.51329 N
* abcd.__uniq.513291356003753 abcd.__uniq.513291356003753 Y
*/
// clang-format on
static inline bool xdl_dsym_is_match(const char *str, const char *sym, size_t str_len) {
if (__predict_false(0 == str_len)) return false;
do {
if (*str != *sym) return __predict_false('.' == *str && '\0' == *sym);
str++;
sym++;
if ('\0' == *str) break;
} while (0 != --str_len);
return true;
}
void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) {
if (NULL == handle || NULL == symbol) return NULL;
if (NULL != symbol_size) *symbol_size = 0;
xdl_t *self = (xdl_t *)handle;
// load .symtab only once
if (!self->symtab_try_load) {
self->symtab_try_load = true;
if (0 != xdl_symtab_load(self)) return NULL;
}
// find symbol
if (NULL == self->symtab) return NULL;
for (size_t i = 0; i < self->symtab_cnt; i++) {
ElfW(Sym) *sym = self->symtab + i;
if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue;
// if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue;
if (!xdl_dsym_is_match(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue;
if (NULL != symbol_size) *symbol_size = sym->st_size;
return (void *)(self->load_bias + sym->st_value);
}
return NULL;
}
static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum,
uintptr_t addr) {
if (addr < load_bias) return false;
uintptr_t vaddr = addr - load_bias;
for (size_t i = 0; i < dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(dlpi_phdr[i]);
if (PT_LOAD != phdr->p_type) continue;
if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true;
}
return false;
}
static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) {
(void)size;
uintptr_t *pkg = (uintptr_t *)arg;
xdl_t **self = (xdl_t **)*pkg++;
uintptr_t addr = *pkg;
if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) {
// found the target ELF
if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // failed
if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) {
free(*self);
*self = NULL;
return 1; // failed
}
(*self)->load_bias = info->dlpi_addr;
(*self)->dlpi_phdr = info->dlpi_phdr;
(*self)->dlpi_phnum = info->dlpi_phnum;
(*self)->dynsym_try_load = false;
(*self)->symtab_try_load = false;
return 1; // OK
}
return 0; // mismatch
}
static void *xdl_open_by_addr(void *addr) {
if (NULL == addr) return NULL;
xdl_t *self = NULL;
uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr};
xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT);
return (void *)self;
}
static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) {
if (is_symtab) {
if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) false;
} else {
if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) false;
}
return ELF_ST_TYPE(sym->st_info) != STT_TLS && offset >= sym->st_value &&
offset < sym->st_value + sym->st_size;
}
static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) {
xdl_t *self = (xdl_t *)handle;
// load .dynsym only once
if (!self->dynsym_try_load) {
self->dynsym_try_load = true;
if (0 != xdl_dynsym_load(self)) return NULL;
}
// find symbol
if (NULL == self->dynsym) return NULL;
uintptr_t offset = (uintptr_t)addr - self->load_bias;
if (self->gnu_hash.buckets_cnt > 0) {
const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset;
for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) {
uint32_t n = self->gnu_hash.buckets[i];
if (n < self->gnu_hash.symoffset) continue;
do {
ElfW(Sym) *sym = self->dynsym + n;
if (xdl_sym_is_match(sym, offset, false)) return sym;
} while ((chains_all[n++] & 1) == 0);
}
} else if (self->sysv_hash.chains_cnt > 0) {
for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) {
ElfW(Sym) *sym = self->dynsym + i;
if (xdl_sym_is_match(sym, offset, false)) return sym;
}
}
return NULL;
}
static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) {
xdl_t *self = (xdl_t *)handle;
// load .symtab only once
if (!self->symtab_try_load) {
self->symtab_try_load = true;
if (0 != xdl_symtab_load(self)) return NULL;
}
// find symbol
if (NULL == self->symtab) return NULL;
uintptr_t offset = (uintptr_t)addr - self->load_bias;
for (size_t i = 0; i < self->symtab_cnt; i++) {
ElfW(Sym) *sym = self->symtab + i;
if (xdl_sym_is_match(sym, offset, true)) return sym;
}
return NULL;
}
int xdl_addr(void *addr, xdl_info_t *info, void **cache) {
if (NULL == addr || NULL == info || NULL == cache) return 0;
memset(info, 0, sizeof(Dl_info));
// find handle from cache
xdl_t *handle = NULL;
for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next)
if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break;
// create new handle, save handle to cache
if (NULL == handle) {
handle = (xdl_t *)xdl_open_by_addr(addr);
if (NULL == handle) return 0;
handle->next = *(xdl_t **)cache;
*(xdl_t **)cache = handle;
}
// we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum
info->dli_fbase = (void *)handle->load_bias;
info->dli_fname = handle->pathname;
info->dli_sname = NULL;
info->dli_saddr = 0;
info->dli_ssize = 0;
info->dlpi_phdr = handle->dlpi_phdr;
info->dlpi_phnum = (size_t)handle->dlpi_phnum;
// keep looking for: symbol name, symbol offset, symbol size
ElfW(Sym) *sym;
if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) {
info->dli_sname = handle->dynstr + sym->st_name;
info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
info->dli_ssize = sym->st_size;
} else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) {
info->dli_sname = handle->strtab + sym->st_name;
info->dli_saddr = (void *)(handle->load_bias + sym->st_value);
info->dli_ssize = sym->st_size;
}
return 1;
}
void xdl_addr_clean(void **cache) {
if (NULL == cache) return;
xdl_t *handle = *((xdl_t **)cache);
while (NULL != handle) {
xdl_t *tmp = handle;
handle = handle->next;
xdl_close(tmp);
}
*cache = NULL;
}
int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) {
if (NULL == callback) return 0;
return xdl_iterate_phdr_impl(callback, data, flags);
}
int xdl_info(void *handle, int request, void *info) {
if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1;
xdl_t *self = (xdl_t *)handle;
xdl_info_t *dlinfo = (xdl_info_t *)info;
dlinfo->dli_fbase = (void *)self->load_bias;
dlinfo->dli_fname = self->pathname;
dlinfo->dli_sname = NULL;
dlinfo->dli_saddr = 0;
dlinfo->dli_ssize = 0;
dlinfo->dlpi_phdr = self->dlpi_phdr;
dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum;
return 0;
}
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl.map.txt
================================================
{
global:
xdl_open;
xdl_close;
xdl_sym;
xdl_dsym;
xdl_addr;
xdl_addr_clean;
xdl_iterate_phdr;
xdl_info;
local:
*;
};
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_iterate.c
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
#include "xdl_iterate.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xdl.h"
#include "xdl_linker.h"
#include "xdl_util.h"
/*
* =========================================================================================================
* API-LEVEL ANDROID-VERSION SOLUTION
* =========================================================================================================
* 16 4.1 /proc/self/maps
* 17 4.2 /proc/self/maps
* 18 4.3 /proc/self/maps
* 19 4.4 /proc/self/maps
* 20 4.4W /proc/self/maps
* ---------------------------------------------------------------------------------------------------------
* 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
* 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3)
* ---------------------------------------------------------------------------------------------------------
* 23 >= 6.0 dl_iterate_phdr() + linker/linker64 from getauxval(3)
* =========================================================================================================
*/
extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *);
extern __attribute((weak)) unsigned long int getauxval(unsigned long int);
static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) {
uintptr_t min_vaddr = UINTPTR_MAX;
for (size_t i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
if (PT_LOAD == phdr->p_type) {
if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr;
}
}
return min_vaddr;
}
static int xdl_iterate_open_or_rewind_maps(FILE **maps) {
if (NULL == *maps) {
*maps = fopen("/proc/self/maps", "r");
if (NULL == *maps) return -1;
} else
rewind(*maps);
return 0;
}
static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) {
// open or rewind maps-file
if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1; // failed
char line[1024];
while (fgets(line, sizeof(line), *maps)) {
// check base address
uintptr_t start, end;
if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue;
if (base < start) break; // failed
if (base >= end) continue;
// get pathname
char *pathname = strchr(line, '/');
if (NULL == pathname) break; // failed
xdl_util_trim_ending(pathname);
// found it
strlcpy(buf, pathname, buf_len);
return 0; // OK
}
return -1; // failed
}
static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) {
uintptr_t *pkg = (uintptr_t *)arg;
xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++;
void *cb_arg = (void *)*pkg++;
FILE **maps = (FILE **)*pkg++;
uintptr_t linker_load_bias = *pkg++;
int flags = (int)*pkg;
// ignore invalid ELF
if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0;
// ignore linker if we have returned it already
if (linker_load_bias == info->dlpi_addr) return 0;
struct dl_phdr_info info_fixed;
info_fixed.dlpi_addr = info->dlpi_addr;
info_fixed.dlpi_name = info->dlpi_name;
info_fixed.dlpi_phdr = info->dlpi_phdr;
info_fixed.dlpi_phnum = info->dlpi_phnum;
info = &info_fixed;
// fix dlpi_phdr & dlpi_phnum (from memory)
if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) {
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr;
info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff);
info->dlpi_phnum = ehdr->e_phnum;
}
// fix dlpi_name (from /proc/self/maps)
if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) {
// get base address
uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info);
if (UINTPTR_MAX == min_vaddr) return 0; // ignore this ELF
uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr);
char buf[1024];
if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0; // ignore this ELF
info->dlpi_name = (const char *)buf;
}
// callback
return cb(info, size, cb_arg);
}
static uintptr_t xdl_iterate_get_linker_base(void) {
if (NULL == getauxval) return 0;
uintptr_t base = (uintptr_t)getauxval(AT_BASE);
if (0 == base) return 0;
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0;
return base;
}
static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base,
const char *pathname, uintptr_t *load_bias) {
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
struct dl_phdr_info info;
info.dlpi_name = pathname;
info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
info.dlpi_phnum = ehdr->e_phnum;
// get load bias
uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info);
if (UINTPTR_MAX == min_vaddr) return 0; // ignore invalid ELF
info.dlpi_addr = (ElfW(Addr))(base - min_vaddr);
if (NULL != load_bias) *load_bias = info.dlpi_addr;
return cb(&info, sizeof(struct dl_phdr_info), cb_arg);
}
static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
if (NULL == dl_iterate_phdr) return 0;
int api_level = xdl_util_get_api_level();
FILE *maps = NULL;
int r;
// dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27).
// Here we always try to get linker base address from auxv.
uintptr_t linker_load_bias = 0;
uintptr_t linker_base = xdl_iterate_get_linker_base();
if (0 != linker_base) {
if (0 !=
(r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias)))
return r;
}
// for other ELF
uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags};
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock();
r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg);
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock();
if (NULL != maps) fclose(maps);
return r;
}
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) {
FILE *maps = fopen("/proc/self/maps", "r");
if (NULL == maps) return 0;
int r = 0;
char buf1[1024], buf2[1024];
char *line = buf1;
uintptr_t prev_base = 0;
bool try_next_line = false;
while (fgets(line, sizeof(buf1), maps)) {
// Try to find an ELF which loaded by linker.
uintptr_t base, offset;
char exec;
if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset))
goto clean;
if ('-' == exec && 0 == offset) {
// r--p
prev_base = base;
line = (line == buf1 ? buf2 : buf1);
try_next_line = true;
continue;
} else if (exec == 'x') {
// r-xp
char *pathname = NULL;
if (try_next_line && 0 != offset) {
char *prev = (line == buf1 ? buf2 : buf1);
char *prev_pathname = strchr(prev, '/');
if (NULL == prev_pathname) goto clean;
pathname = strchr(line, '/');
if (NULL == pathname) goto clean;
xdl_util_trim_ending(prev_pathname);
xdl_util_trim_ending(pathname);
if (0 != strcmp(prev_pathname, pathname)) goto clean;
// we found the line with r-xp in the next line
base = prev_base;
offset = 0;
}
if (0 != offset) goto clean;
// get pathname
if (NULL == pathname) {
pathname = strchr(line, '/');
if (NULL == pathname) goto clean;
xdl_util_trim_ending(pathname);
}
if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean;
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base;
struct dl_phdr_info info;
info.dlpi_name = pathname;
info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff);
info.dlpi_phnum = ehdr->e_phnum;
// callback
if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break;
}
clean:
try_next_line = false;
}
fclose(maps);
return r;
}
#endif
int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) {
// iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86)
#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__
if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg);
#endif
// iterate by dl_iterate_phdr()
return xdl_iterate_by_linker(cb, cb_arg, flags);
}
int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) {
FILE *maps = NULL;
int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps);
if (NULL != maps) fclose(maps);
return r;
}
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_iterate.h
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
#ifndef IO_GITHUB_HEXHACKING_XDL_ITERATE
#define IO_GITHUB_HEXHACKING_XDL_ITERATE
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg);
int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags);
int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_linker.c
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2021-02-21.
#include "xdl_linker.h"
#include
#include
#include
#include
#include "xdl.h"
#include "xdl_iterate.h"
#include "xdl_util.h"
#define XDL_LINKER_SYM_MUTEX "__dl__ZL10g_dl_mutex"
#define XDL_LINKER_SYM_DLOPEN_EXT_N "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv"
#define XDL_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv"
#define XDL_LINKER_SYM_DLOPEN_O "__dl__Z8__dlopenPKciPKv"
#define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen"
typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *);
typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *);
static pthread_mutex_t *xdl_linker_mutex = NULL;
static void *xdl_linker_dlopen = NULL;
static void *xdl_linker_caller_addr[] = {
NULL, // default
NULL, // art
NULL // vendor
};
#ifndef __LP64__
#define XDL_LINKER_LIB "lib"
#else
#define XDL_LINKER_LIB "lib64"
#endif
static const char *xdl_linker_vendor_path[] = {
// order is important
"/vendor/" XDL_LINKER_LIB "/egl/", "/vendor/" XDL_LINKER_LIB "/hw/",
"/vendor/" XDL_LINKER_LIB "/", "/odm/" XDL_LINKER_LIB "/",
"/vendor/" XDL_LINKER_LIB "/vndk-sp/", "/odm/" XDL_LINKER_LIB "/vndk-sp/"};
static void xdl_linker_init_symbols_impl(void) {
// find linker from: /proc/self/maps (API level < 18) or getauxval (API level >= 18)
void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT);
if (NULL == handle) return;
int api_level = xdl_util_get_api_level();
if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) {
// == Android 5.x
xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
} else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
// == Android 7.x
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL);
if (NULL == xdl_linker_dlopen) {
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL);
xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL);
}
} else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) {
// == Android 8.x
xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL);
} else if (api_level >= __ANDROID_API_P__) {
// >= Android 9.0
xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL);
}
xdl_close(handle);
}
static void xdl_linker_init_symbols(void) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static bool inited = false;
if (!inited) {
pthread_mutex_lock(&lock);
if (!inited) {
xdl_linker_init_symbols_impl();
inited = true;
}
pthread_mutex_unlock(&lock);
}
}
void xdl_linker_lock(void) {
xdl_linker_init_symbols();
if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex);
}
void xdl_linker_unlock(void) {
if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex);
}
static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) {
for (size_t i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]);
if (PT_LOAD == phdr->p_type) {
return (void *)(info->dlpi_addr + phdr->p_vaddr);
}
}
return NULL;
}
static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) {
(void)size;
size_t *vendor_match = (size_t *)arg;
if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue
if (NULL == xdl_linker_caller_addr[0] && xdl_util_ends_with(info->dlpi_name, "/libc.so"))
xdl_linker_caller_addr[0] = xdl_linker_get_caller_addr(info);
if (NULL == xdl_linker_caller_addr[1] && xdl_util_ends_with(info->dlpi_name, "/libart.so"))
xdl_linker_caller_addr[1] = xdl_linker_get_caller_addr(info);
if (0 != *vendor_match) {
for (size_t i = 0; i < *vendor_match; i++) {
if (xdl_util_starts_with(info->dlpi_name, xdl_linker_vendor_path[i])) {
void *caller_addr = xdl_linker_get_caller_addr(info);
if (NULL != caller_addr) {
xdl_linker_caller_addr[2] = caller_addr;
*vendor_match = i;
}
}
}
}
if (NULL != xdl_linker_caller_addr[0] && NULL != xdl_linker_caller_addr[1] && 0 == *vendor_match) {
return 1; // finish
} else {
return 0; // continue
}
}
static void xdl_linker_init_caller_addr_impl(void) {
size_t vendor_match = sizeof(xdl_linker_vendor_path) / sizeof(xdl_linker_vendor_path[0]);
xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, &vendor_match, XDL_DEFAULT);
}
static void xdl_linker_init_caller_addr(void) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static bool inited = false;
if (!inited) {
pthread_mutex_lock(&lock);
if (!inited) {
xdl_linker_init_caller_addr_impl();
inited = true;
}
pthread_mutex_unlock(&lock);
}
}
void *xdl_linker_force_dlopen(const char *filename) {
int api_level = xdl_util_get_api_level();
if (api_level <= __ANDROID_API_M__) {
// <= Android 6.0
return dlopen(filename, RTLD_NOW);
} else {
xdl_linker_init_symbols();
if (NULL == xdl_linker_dlopen) return NULL;
xdl_linker_init_caller_addr();
void *handle = NULL;
if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) {
// == Android 7.x
xdl_linker_lock();
for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) {
if (NULL != xdl_linker_caller_addr[i]) {
handle =
((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, xdl_linker_caller_addr[i]);
if (NULL != handle) break;
}
}
xdl_linker_unlock();
} else {
// >= Android 8.0
for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) {
if (NULL != xdl_linker_caller_addr[i]) {
handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, xdl_linker_caller_addr[i]);
if (NULL != handle) break;
}
}
}
return handle;
}
}
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_linker.h
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2021-02-21.
#ifndef IO_GITHUB_HEXHACKING_XDL_LINKER
#define IO_GITHUB_HEXHACKING_XDL_LINKER
#ifdef __cplusplus
extern "C" {
#endif
void xdl_linker_lock(void);
void xdl_linker_unlock(void);
void *xdl_linker_force_dlopen(const char *filename);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_lzma.c
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-11-08.
#include "xdl_lzma.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xdl.h"
#include "xdl_util.h"
// LZMA library pathname & symbol names
#ifndef __LP64__
#define XDL_LZMA_PATHNAME "/system/lib/liblzma.so"
#else
#define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so"
#endif
#define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable"
#define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable"
#define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct"
#define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished"
#define XDL_LZMA_SYM_FREE "XzUnpacker_Free"
#define XDL_LZMA_SYM_CODE "XzUnpacker_Code"
// LZMA data type definition
#define SZ_OK 0
typedef struct ISzAlloc ISzAlloc;
typedef const ISzAlloc *ISzAllocPtr;
struct ISzAlloc {
void *(*Alloc)(ISzAllocPtr p, size_t size);
void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
};
typedef enum {
CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */
CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
CODER_STATUS_NOT_FINISHED, /* stream was not finished */
CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */
} ECoderStatus;
typedef enum {
CODER_FINISH_ANY, /* finish at any point */
CODER_FINISH_END /* block must be finished at the end */
} ECoderFinishMode;
// LZMA function type definition
typedef void (*xdl_lzma_crcgen_t)(void);
typedef void (*xdl_lzma_crc64gen_t)(void);
typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr);
typedef int (*xdl_lzma_isfinished_t)(const void *);
typedef void (*xdl_lzma_free_t)(void *);
typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode,
ECoderStatus *);
typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int,
ECoderFinishMode, ECoderStatus *);
// LZMA function pointor
static xdl_lzma_construct_t xdl_lzma_construct = NULL;
static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL;
static xdl_lzma_free_t xdl_lzma_free = NULL;
static void *xdl_lzma_code = NULL;
// LZMA init
static void xdl_lzma_init() {
void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD);
if (NULL == lzma) return;
xdl_lzma_crcgen_t crcgen = NULL;
xdl_lzma_crc64gen_t crc64gen = NULL;
if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end;
if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end;
if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL)))
goto end;
if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL)))
goto end;
if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end;
if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end;
crcgen();
crc64gen();
end:
xdl_close(lzma);
}
// LZMA internal alloc / free
static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) {
(void)p;
return malloc(size);
}
static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) {
(void)p;
free(address);
}
int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) {
size_t src_offset = 0;
size_t dst_offset = 0;
size_t src_remaining;
size_t dst_remaining;
ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free};
long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned
ECoderStatus status;
int api_level = xdl_util_get_api_level();
// init and check
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static bool inited = false;
if (!inited) {
pthread_mutex_lock(&lock);
if (!inited) {
xdl_lzma_init();
inited = true;
}
pthread_mutex_unlock(&lock);
}
if (NULL == xdl_lzma_code) return -1;
xdl_lzma_construct(&state, &alloc);
*dst_size = 2 * src_size;
*dst = NULL;
do {
*dst_size *= 2;
if (NULL == (*dst = realloc(*dst, *dst_size))) {
xdl_lzma_free(&state);
return -1;
}
src_remaining = src_size - src_offset;
dst_remaining = *dst_size - dst_offset;
int result;
if (api_level >= __ANDROID_API_Q__) {
xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code;
result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1,
CODER_FINISH_ANY, &status);
} else {
xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code;
result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining,
CODER_FINISH_ANY, &status);
}
if (SZ_OK != result) {
free(*dst);
xdl_lzma_free(&state);
return -1;
}
src_offset += src_remaining;
dst_offset += dst_remaining;
} while (status == CODER_STATUS_NOT_FINISHED);
xdl_lzma_free(&state);
if (!xdl_lzma_isfinished(&state)) {
free(*dst);
return -1;
}
*dst_size = dst_offset;
*dst = realloc(*dst, *dst_size);
return 0;
}
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_lzma.h
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-11-08.
#ifndef IO_GITHUB_HEXHACKING_XDL_LZMA
#define IO_GITHUB_HEXHACKING_XDL_LZMA
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_util.c
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
#include "xdl_util.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
bool xdl_util_starts_with(const char *str, const char *start) {
while (*str && *str == *start) {
str++;
start++;
}
return '\0' == *start;
}
bool xdl_util_ends_with(const char *str, const char *ending) {
size_t str_len = strlen(str);
size_t ending_len = strlen(ending);
if (ending_len > str_len) return false;
return 0 == strcmp(str + (str_len - ending_len), ending);
}
size_t xdl_util_trim_ending(char *start) {
char *end = start + strlen(start);
while (start < end && isspace((int)(*(end - 1)))) {
end--;
*end = '\0';
}
return (size_t)(end - start);
}
static int xdl_util_get_api_level_from_build_prop(void) {
char buf[128];
int api_level = -1;
FILE *fp = fopen("/system/build.prop", "r");
if (NULL == fp) goto end;
while (fgets(buf, sizeof(buf), fp)) {
if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) {
api_level = atoi(buf + 21);
break;
}
}
fclose(fp);
end:
return (api_level > 0) ? api_level : -1;
}
int xdl_util_get_api_level(void) {
static int xdl_util_api_level = -1;
if (xdl_util_api_level < 0) {
int api_level = android_get_device_api_level();
if (api_level < 0)
api_level = xdl_util_get_api_level_from_build_prop(); // compatible with unusual models
if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__;
__atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST);
}
return xdl_util_api_level;
}
================================================
FILE: app/src/main/jni/Il2Cpp/xdl/xdl_util.h
================================================
// Copyright (c) 2020-2023 HexHacking Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by caikelun on 2020-10-04.
#ifndef IO_GITHUB_HEXHACKING_XDL_UTIL
#define IO_GITHUB_HEXHACKING_XDL_UTIL
#include
#include
#include
#ifndef __LP64__
#define XDL_UTIL_LINKER_BASENAME "linker"
#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker"
#define XDL_UTIL_APP_PROCESS_BASENAME "app_process32"
#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32"
#define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process"
#define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process"
#else
#define XDL_UTIL_LINKER_BASENAME "linker64"
#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64"
#define XDL_UTIL_APP_PROCESS_BASENAME "app_process64"
#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64"
#endif
#define XDL_UTIL_VDSO_BASENAME "[vdso]"
#define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \
({ \
__typeof__(exp) _rc; \
do { \
errno = 0; \
_rc = (exp); \
} while (_rc == -1 && errno == EINTR); \
_rc; \
})
#ifdef __cplusplus
extern "C" {
#endif
bool xdl_util_starts_with(const char *str, const char *start);
bool xdl_util_ends_with(const char *str, const char *ending);
size_t xdl_util_trim_ending(char *start);
int xdl_util_get_api_level(void);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: app/src/main/jni/Includes/config.h
================================================
//
// Created by Perfare on 2020/7/4.
//
#ifndef AUTO_IL2CPPDUMPER_CONFIG_H
#define AUTO_IL2CPPDUMPER_CONFIG_H
// Try increase sleep time if having issues with the game. Decrease sleep time if anti-cheat triggering earlier
#define Sleep 2
// Uncomment for fake lib mode
// It is to load our fake libmain.so or libunity.so and load game's real librealmain.so or librealunity.so
#define UseFakeLib
#endif //AUTO_IL2CPPDUMPER_CONFIG_H
================================================
FILE: app/src/main/jni/Includes/log.h
================================================
//
// Created by Perfare on 2020/7/4.
//
#ifndef RIRU_IL2CPPDUMPER_LOG_H
#define RIRU_IL2CPPDUMPER_LOG_H
#include
#define LOG_TAG "Il2CppDumper"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#endif //RIRU_IL2CPPDUMPER_LOG_H
================================================
FILE: app/src/main/jni/native-lib.cpp
================================================
#include
#include
#include
#include
#include
#include
#include "Il2Cpp/il2cpp_dump.h"
#include "Includes/config.h"
#include "Includes/log.h"
bool isLibraryLoaded(const char *libraryName) {
char line[512] = {0};
FILE *fp = fopen("/proc/self/maps", "rt");
if (fp != nullptr) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, libraryName)) {
return true;
}
}
fclose(fp);
}
return false;
}
#define libTarget "libil2cpp.so"
void dump_thread() {
LOGI("Lib loaded");
do {
sleep(1);
} while (!isLibraryLoaded(libTarget));
//Waiting libil2cpp.so fully loaded.
LOGI("Waiting in %d...", Sleep);
sleep(Sleep);
auto il2cpp_handle = dlopen(libTarget, 4);
LOGI("Start dumping");
auto androidDataPath = std::string("/storage/emulated/0/Android/data/").append(
GetPackageName()).append("/").append(GetPackageName()).append("-dump.cs");
il2cpp_api_init(il2cpp_handle);
il2cpp_dump(androidDataPath.c_str());
}
//The idea from first Il2Cpp Dumper called PokemonGoDumper
//https://github.com/Jumboperson/PokemonGoDumper/blob/master/main.c#L569
void *pLibRealUnity = 0;
typedef jint(JNICALL *CallJNI_OnLoad_t)(JavaVM *vm, void *reserved);
typedef void(JNICALL *CallJNI_OnUnload_t)(JavaVM *vm, void *reserved);
CallJNI_OnLoad_t RealJNIOnLoad = 0;
CallJNI_OnUnload_t RealJNIOnUnload = 0;
#ifdef UseFakeLib
JNIEXPORT jint JNICALL CallJNIOL(JavaVM *vm, void *reserved) {
LOGI("OnLoad called");
std::thread(dump_thread).detach();
if (!pLibRealUnity)
pLibRealUnity = dlopen("librealmain.so", RTLD_NOW);
if (!pLibRealUnity)
pLibRealUnity = dlopen("librealunity.so", RTLD_NOW);
if (!RealJNIOnLoad)
RealJNIOnLoad = reinterpret_cast(dlsym(pLibRealUnity, "JNI_OnLoad"));
return RealJNIOnLoad(vm, reserved);
}
JNIEXPORT void JNICALL CallJNIUL(JavaVM *vm, void *reserved) {
LOGI("OnUnload called");
if (!pLibRealUnity)
pLibRealUnity = dlopen("librealmain.so", RTLD_NOW);
if (!pLibRealUnity)
pLibRealUnity = dlopen("librealunity.so", RTLD_NOW);
if (!RealJNIOnUnload)
RealJNIOnUnload = reinterpret_cast(dlsym(pLibRealUnity,
"JNI_OnUnload"));
RealJNIOnUnload(vm, reserved);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGI("Initialize JNI");
return CallJNIOL(vm, reserved);
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
LOGI("Unload JNI");
CallJNIUL(vm, reserved);
}
#else
__attribute__((constructor))
void lib_main() {
// Create a new thread so it does not block the main thread, means the game would not freeze
std::thread(dump_thread).detach();
}
#endif
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#FFBB86FC
#FF6200EE
#FF3700B3
#FF03DAC5
#FF018786
#FF000000
#FFFFFFFF
================================================
FILE: app/src/main/res/values/strings.xml
================================================
Auto-Il2cppDumper
================================================
FILE: app/src/main/res/values/themes.xml
================================================
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.13.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register('clean', Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'
rootProject.name = "Auto-Il2cppDumper"