gkms-localify-ios/GakumasLocalify/GakumasLocalify/Il2cppUtils.hpp

491 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
};
}
}