491 lines
18 KiB
C++
491 lines
18 KiB
C++
#pragma once
|
||
#include "Log.h"
|
||
#include <memory>
|
||
#include <unordered_map>
|
||
|
||
#include "../il2cpp_dump/Il2cppJson.hpp"
|
||
#include "../il2cpp_dump/il2cppTypes.hpp"
|
||
|
||
namespace Il2cppUtils {
|
||
using namespace GakumasLocal;
|
||
|
||
struct Il2CppClassHead {
|
||
// The following fields are always valid for a Il2CppClass structure
|
||
const void* image;
|
||
void* gc_desc;
|
||
const char* name;
|
||
const char* namespaze;
|
||
};
|
||
|
||
struct Il2CppObject
|
||
{
|
||
union
|
||
{
|
||
void* klass;
|
||
void* vtable;
|
||
};
|
||
void* monitor;
|
||
};
|
||
|
||
enum Il2CppTypeEnum
|
||
{
|
||
IL2CPP_TYPE_END = 0x00, /* End of List */
|
||
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
|
||
};
|
||
|
||
typedef struct Il2CppType
|
||
{
|
||
void* dummy;
|
||
unsigned int attrs : 16;
|
||
Il2CppTypeEnum type : 8;
|
||
unsigned int num_mods : 6;
|
||
unsigned int byref : 1;
|
||
unsigned int pinned : 1;
|
||
} Il2CppType;
|
||
|
||
struct Il2CppReflectionType
|
||
{
|
||
Il2CppObject object;
|
||
const Il2CppType* type;
|
||
};
|
||
|
||
struct Resolution_t {
|
||
int width;
|
||
int height;
|
||
int herz;
|
||
};
|
||
|
||
struct FieldInfo {
|
||
const char* name;
|
||
const Il2CppType* type;
|
||
uintptr_t parent;
|
||
int32_t offset;
|
||
uint32_t token;
|
||
};
|
||
|
||
struct MethodInfo {
|
||
uintptr_t methodPointer;
|
||
uintptr_t virtualMethodPointer;
|
||
uintptr_t invoker_method;
|
||
const char* name;
|
||
uintptr_t klass;
|
||
const Il2CppType* return_type;
|
||
const void* parameters;
|
||
uintptr_t methodDefinition;
|
||
uintptr_t genericContainer;
|
||
uint32_t token;
|
||
uint16_t flags;
|
||
uint16_t iflags;
|
||
uint16_t slot;
|
||
uint8_t parameters_count;
|
||
uint8_t is_generic : 1;
|
||
uint8_t is_inflated : 1;
|
||
uint8_t wrapper_type : 1;
|
||
uint8_t is_marshaled_from_native : 1;
|
||
};
|
||
|
||
static Il2CppClassHead* get_class_from_instance(const void* instance) {
|
||
#ifdef GKMS_IOS
|
||
return static_cast<Il2CppClassHead*>(*static_cast<void* const*>(__builtin_assume_aligned(instance, alignof(void*))));
|
||
#else
|
||
return static_cast<Il2CppClassHead*>(*static_cast<void* const*>(std::assume_aligned<alignof(void*)>(instance)));
|
||
#endif
|
||
}
|
||
|
||
inline const void* il2cpp_method_get_return_type(const void* method_info_ptr) {
|
||
if (!method_info_ptr) return nullptr;
|
||
|
||
#if defined(__aarch64__) || defined(__x86_64__) || defined(_M_X64)
|
||
// 64位架构:偏移 0x20 (32 bytes)
|
||
return (const void*)(*(uintptr_t*)((uintptr_t)method_info_ptr + 0x28));
|
||
#else
|
||
// 32位架构:偏移 0x10 (16 bytes)
|
||
return (const void*)(*(uintptr_t*)((uintptr_t)method_info_ptr + 0x14));
|
||
#endif
|
||
}
|
||
|
||
inline MethodInfo* il2cpp_class_get_method_from_name(void* klass, const char* name, int argsCount)
|
||
{
|
||
// Log::InfoFmt("il2cpp_class_get_method_from_name: %s", name);
|
||
auto ret = Il2cppJson::InvokeIl2cpp<MethodInfo*>("il2cpp_class_get_method_from_name", klass, name, argsCount);
|
||
// Log::InfoFmt("il2cpp_class_get_method_from_name finished: %p", ret);
|
||
return ret;
|
||
}
|
||
|
||
inline Il2cppJson::Class* GetClass(const std::string& assemblyName,
|
||
const std::string& nameSpaceName,
|
||
const std::string& className)
|
||
{
|
||
return Il2cppJson::GetClass(assemblyName, nameSpaceName, className);
|
||
}
|
||
|
||
inline Il2CppClassHead* GetIl2cppClassFromName(const char* assemblyName, const char* nameSpaceName, const char* className) {
|
||
Log::Info("GetMethodIl2cpp 0");
|
||
static auto domain = Il2cppJson::InvokeIl2cpp<void*>("il2cpp_domain_get");
|
||
if (!domain)
|
||
{
|
||
Log::ErrorFmt("GetMethodIl2cpp error: failed to get domain.");
|
||
return nullptr;
|
||
}
|
||
auto assembly = Il2cppJson::InvokeIl2cpp<void*>("il2cpp_domain_assembly_open", domain, assemblyName);
|
||
if (!assembly)
|
||
{
|
||
Log::ErrorFmt("GetMethodIl2cpp error: failed to get assembly: %s", assemblyName);
|
||
return nullptr;
|
||
}
|
||
|
||
// auto assembly = Il2cppJson::InvokeIl2cpp<void*>("il2cpp_domain_assembly_open+1", assemblyName);
|
||
// UnityResolve::Invoke<void*>("il2cpp_thread_attach", domain);
|
||
Log::Info("GetMethodIl2cpp 1");
|
||
auto image = Il2cppJson::InvokeIl2cpp<void*>("il2cpp_assembly_get_image", assembly);
|
||
if (!image) {
|
||
Log::ErrorFmt("GetMethodIl2cpp error: assembly %s not found.", assemblyName);
|
||
return nullptr;
|
||
}
|
||
Log::InfoFmt("GetMethodIl2cpp 2, assembly: %p, image: %p, nameSpace: %s, class: %s", assembly, image, nameSpaceName, className);
|
||
auto klass = Il2cppJson::InvokeIl2cpp<Il2CppClassHead*>("il2cpp_class_from_name", image, nameSpaceName, className);
|
||
Log::Info("GetMethodIl2cpp 3");
|
||
if (!klass) {
|
||
Log::ErrorFmt("GetMethodIl2cpp error: Class %s::%s not found.", nameSpaceName, className);
|
||
return nullptr;
|
||
}
|
||
return klass;
|
||
}
|
||
|
||
inline MethodInfo* GetMethodIl2cpp(const char* assemblyName, const char* nameSpaceName,
|
||
const char* className, const char* methodName, const int argsCount) {
|
||
auto klass = GetIl2cppClassFromName(assemblyName, nameSpaceName, className);
|
||
if (!klass) return nullptr;
|
||
Log::Info("GetMethodIl2cpp 4");
|
||
auto ret = Il2cppJson::InvokeIl2cpp<MethodInfo*>("il2cpp_class_get_method_from_name", klass, methodName, argsCount);
|
||
Log::Info("GetMethodIl2cpp 5");
|
||
if (!ret) {
|
||
Log::ErrorFmt("GetMethodIl2cpp error: method %s::%s.%s not found.", nameSpaceName, className, methodName);
|
||
return nullptr;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
inline Il2cppJson::Method* GetMethod(const char* assemblyName,
|
||
const char* nameSpaceName,
|
||
const char* className,
|
||
const char* methodName,
|
||
// const std::vector<std::string>& args = {})
|
||
const int argsCount)
|
||
{
|
||
return Il2cppJson::GetMethod(assemblyName, nameSpaceName, className, methodName, {});
|
||
/*
|
||
const auto method = GetMethodIl2cpp(assemblyName, nameSpaceName, className, methodName, argsCount);
|
||
if (!method) return std::nullopt;
|
||
|
||
return Il2cppJson::Method {
|
||
methodName, {}, argsCount, method->methodPointer
|
||
};*/
|
||
}
|
||
|
||
inline Il2cppJson::Method* GetMethod(const char* assemblyName,
|
||
const char* nameSpaceName,
|
||
const char* className,
|
||
const char* methodName,
|
||
const std::vector<std::string>& args = {})
|
||
{
|
||
return Il2cppJson::GetMethod(assemblyName, nameSpaceName, className, methodName, args);
|
||
}
|
||
|
||
inline void* GetMethodPointer(const char* assemblyName,
|
||
const char* nameSpaceName,
|
||
const char* className,
|
||
const char* methodName,
|
||
// const std::vector<std::string>& args = {})
|
||
const int argsCount)
|
||
{
|
||
const auto method = GetMethod(assemblyName, nameSpaceName, className, methodName, argsCount);
|
||
if (!method) return nullptr;
|
||
return reinterpret_cast<void*>(method->address);
|
||
}
|
||
|
||
inline void* GetMethodPointer(const char* assemblyName,
|
||
const char* nameSpaceName,
|
||
const char* className,
|
||
const char* methodName,
|
||
const std::vector<std::string>& args = {})
|
||
{
|
||
const auto method = GetMethod(assemblyName, nameSpaceName, className, methodName, args);
|
||
if (!method) return nullptr;
|
||
return reinterpret_cast<void*>(method->address);
|
||
}
|
||
|
||
template <typename RType>
|
||
static auto ClassGetFieldValue(void* obj, FieldInfo* field) -> RType {
|
||
return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset);
|
||
}
|
||
|
||
template <typename RType>
|
||
static auto ClassSetFieldValue(void* obj, FieldInfo* field, RType value) -> void {
|
||
*reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset) = value;
|
||
}
|
||
|
||
static void* get_system_class_from_reflection_type_str(const char* typeStr, const char* assemblyName = "mscorlib") {
|
||
using Il2CppString = Il2cppTypes::String;
|
||
|
||
static auto assemblyLoad = reinterpret_cast<void* (*)(Il2CppString*)>(
|
||
GetMethodPointer("mscorlib.dll", "System.Reflection",
|
||
"Assembly", "Load", {"*"})
|
||
);
|
||
static auto assemblyGetType = reinterpret_cast<Il2CppReflectionType * (*)(void*, Il2CppString*)>(
|
||
GetMethodPointer("mscorlib.dll", "System.Reflection",
|
||
"Assembly", "GetType", {"*"})
|
||
);
|
||
|
||
static auto reflectionAssembly = assemblyLoad(Il2CppString::New(assemblyName));
|
||
auto reflectionType = assemblyGetType(reflectionAssembly, Il2CppString::New(typeStr));
|
||
return Il2cppJson::InvokeIl2cpp<void*>("il2cpp_class_from_system_type", reflectionType);
|
||
}
|
||
|
||
inline bool il2cpp_class_is_enum(int64_t a1)
|
||
{
|
||
return (*(__uint8_t *)(a1 + 309) >> 2) & 1;
|
||
}
|
||
|
||
static std::unordered_map<std::string, std::unordered_map<int, std::string>> enumToValueMapCache{};
|
||
static std::unordered_map<int, std::string> EnumToValueMap(Il2CppClassHead* enumClass, bool useCache) {
|
||
std::unordered_map<int, std::string> ret{};
|
||
// auto isEnum = UnityResolve::Invoke<bool>("il2cpp_class_is_enum", enumClass);
|
||
auto isEnum = Il2cppUtils::il2cpp_class_is_enum(reinterpret_cast<int64_t>(static_cast<void*>(enumClass)));
|
||
|
||
if (isEnum) {
|
||
Il2cppUtils::FieldInfo* field = nullptr;
|
||
void* iter = nullptr;
|
||
|
||
std::string cacheName = std::string(enumClass->namespaze) + "::" + enumClass->name;
|
||
if (useCache) {
|
||
if (auto it = enumToValueMapCache.find(cacheName); it != enumToValueMapCache.end()) {
|
||
return it->second;
|
||
}
|
||
}
|
||
|
||
while ((field = Il2cppJson::InvokeIl2cpp<Il2cppUtils::FieldInfo*>("il2cpp_class_get_fields", enumClass, &iter))) {
|
||
// Log::DebugFmt("field: %s, off: %d", field->name, field->offset);
|
||
if (field->offset > 0) continue; // 非 static
|
||
if (strcmp(field->name, "value__") == 0) {
|
||
continue;
|
||
}
|
||
|
||
int value;
|
||
Il2cppJson::InvokeIl2cpp<void>("il2cpp_field_static_get_value_0+1", field, &value, 0LL);
|
||
// Log::DebugFmt("returnClass: %s - %s: 0x%x", enumClass->name, field->name, value);
|
||
std::string itemName = std::string(enumClass->name) + "_" + field->name;
|
||
ret.emplace(value, std::move(itemName));
|
||
}
|
||
|
||
if (useCache) {
|
||
enumToValueMapCache.emplace(std::move(cacheName), ret);
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
namespace Tools {
|
||
|
||
template <typename T = void*>
|
||
class CSListEditor {
|
||
public:
|
||
CSListEditor(void* list) {
|
||
list_klass = get_class_from_instance(list);
|
||
lst = list;
|
||
|
||
lst_get_Count_method = il2cpp_class_get_method_from_name(list_klass, "get_Count", 0);
|
||
lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
|
||
lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
|
||
lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
|
||
lst_Contains_method = il2cpp_class_get_method_from_name(list_klass, "Contains", 1);
|
||
|
||
lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
|
||
lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
|
||
lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
|
||
lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
|
||
lst_Contains = reinterpret_cast<lst_Contains_t>(lst_Contains_method->methodPointer);
|
||
}
|
||
|
||
void Add(T value) {
|
||
lst_Add(lst, value, lst_Add_method);
|
||
}
|
||
|
||
bool Contains(T value) {
|
||
return lst_Contains(lst, value, lst_Contains_method);
|
||
}
|
||
|
||
T get_Item(int index) {
|
||
return lst_get_Item(lst, index, lst_get_Item_method);
|
||
}
|
||
|
||
void set_Item(int index, T value) {
|
||
return lst_set_Item(lst, index, value, lst_set_Item_method);
|
||
}
|
||
|
||
int get_Count() {
|
||
return lst_get_Count(lst, lst_get_Count_method);
|
||
}
|
||
|
||
T operator[] (int key) {
|
||
return get_Item(key);
|
||
}
|
||
|
||
class Iterator {
|
||
public:
|
||
Iterator(CSListEditor<T>* editor, int index) : editor(editor), index(index) {}
|
||
|
||
T operator*() const {
|
||
return editor->get_Item(index);
|
||
}
|
||
|
||
Iterator& operator++() {
|
||
++index;
|
||
return *this;
|
||
}
|
||
|
||
bool operator!=(const Iterator& other) const {
|
||
return index != other.index;
|
||
}
|
||
|
||
private:
|
||
CSListEditor<T>* editor;
|
||
int index;
|
||
};
|
||
|
||
Iterator begin() {
|
||
return Iterator(this, 0);
|
||
}
|
||
|
||
Iterator end() {
|
||
return Iterator(this, get_Count());
|
||
}
|
||
|
||
void* lst;
|
||
void* list_klass;
|
||
private:
|
||
typedef T(*lst_get_Item_t)(void*, int, void* mtd);
|
||
typedef void(*lst_Add_t)(void*, T, void* mtd);
|
||
typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
|
||
typedef int(*lst_get_Count_t)(void*, void* mtd);
|
||
typedef bool(*lst_Contains_t)(void*, T, void* mtd);
|
||
|
||
MethodInfo* lst_get_Item_method;
|
||
MethodInfo* lst_Add_method;
|
||
MethodInfo* lst_get_Count_method;
|
||
MethodInfo* lst_set_Item_method;
|
||
MethodInfo* lst_Contains_method;
|
||
|
||
lst_get_Item_t lst_get_Item;
|
||
lst_set_Item_t lst_set_Item;
|
||
lst_Add_t lst_Add;
|
||
lst_get_Count_t lst_get_Count;
|
||
lst_Contains_t lst_Contains;
|
||
};
|
||
|
||
|
||
template <typename KT = void*, typename VT = void*>
|
||
class CSDictEditor {
|
||
public:
|
||
// @param dict: Dictionary instance.
|
||
// @param dictTypeStr: Reflection type. eg: "System.Collections.Generic.Dictionary`2[System.Int32, System.Int32]"
|
||
// CSDictEditor(void* dict, const char* dictTypeStr) {
|
||
// dic_klass = Il2cppUtils::get_system_class_from_reflection_type_str(dictTypeStr);
|
||
// initDict(dict);
|
||
// }
|
||
|
||
CSDictEditor(void* dict) {
|
||
dic_klass = get_class_from_instance(dict);
|
||
initDict(dict);
|
||
}
|
||
|
||
CSDictEditor(void* dict, void* dicClass) {
|
||
dic_klass = dicClass;
|
||
initDict(dict);
|
||
}
|
||
|
||
void Add(KT key, VT value) {
|
||
dic_Add(dict, key, value, Add_method);
|
||
}
|
||
|
||
bool ContainsKey(KT key) {
|
||
return dic_containsKey(dict, key, ContainsKey_method);
|
||
}
|
||
|
||
VT get_Item(KT key) {
|
||
return dic_get_Item(dict, key, get_Item_method);
|
||
}
|
||
|
||
VT operator[] (KT key) {
|
||
return get_Item(key);
|
||
}
|
||
|
||
void* dict;
|
||
void* dic_klass;
|
||
|
||
private:
|
||
void initDict(void* dict) {
|
||
// dic_klass = dicClass;
|
||
this->dict = dict;
|
||
|
||
get_Item_method = Il2cppJson::InvokeIl2cpp<MethodInfo*>("il2cpp_class_get_method_from_name", "get_Item", 1);
|
||
Add_method = Il2cppJson::InvokeIl2cpp<MethodInfo*>("il2cpp_class_get_method_from_name", "Add", 2);
|
||
ContainsKey_method = Il2cppJson::InvokeIl2cpp<MethodInfo*>("il2cpp_class_get_method_from_name", "ContainsKey", 1);
|
||
|
||
dic_get_Item = (dic_get_Item_t)get_Item_method->methodPointer;
|
||
dic_Add = (dic_Add_t)Add_method->methodPointer;
|
||
dic_containsKey = (dic_containsKey_t)ContainsKey_method->methodPointer;
|
||
}
|
||
|
||
typedef VT(*dic_get_Item_t)(void*, KT, void* mtd);
|
||
typedef VT(*dic_Add_t)(void*, KT, VT, void* mtd);
|
||
typedef VT(*dic_containsKey_t)(void*, KT, void* mtd);
|
||
|
||
CSDictEditor();
|
||
MethodInfo* get_Item_method;
|
||
MethodInfo* Add_method;
|
||
MethodInfo* ContainsKey_method;
|
||
dic_get_Item_t dic_get_Item;
|
||
dic_Add_t dic_Add;
|
||
dic_containsKey_t dic_containsKey;
|
||
};
|
||
|
||
}
|
||
}
|