├── README.md └── il2cpp.hpp /README.md: -------------------------------------------------------------------------------- 1 | # il2cpp-external-library 2 | 3 | Example usage: 4 | ``` 5 | int main( ) { 6 | il2cpp::initialize( ); 7 | 8 | auto base_player = il2cpp::find_klass( "Assembly-CSharp", "BasePlayer" ); 9 | if ( base_player ) { 10 | auto eyes = base_player->get_field( "eyes" ); 11 | printf( "%p\n", eyes ); 12 | } 13 | return 0; 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /il2cpp.hpp: -------------------------------------------------------------------------------- 1 | // include your own driver / memory library alongside all the dependencies from c++. 2 | 3 | #define OFFSET( func, type, offset ) type func { return driver::read< type >( reinterpret_cast< std::uintptr_t >( this ) + offset ); } 4 | 5 | namespace glb { 6 | inline std::uintptr_t game_assembly = 0; 7 | constexpr auto class_table_1 = 0x34AB9B8; 8 | constexpr auto class_table_2 = 0x34ABA78; 9 | constexpr auto class_table_3 = 0x34AB980; 10 | constexpr auto assemblies_begin = 0x34AB6A0; 11 | constexpr auto assemblies_end = assemblies_begin + sizeof( void* ); 12 | } 13 | 14 | struct il2cpp_field_t { 15 | OFFSET( offset( ), std::uint32_t, 0x18 ); 16 | 17 | std::string name( ) { 18 | return driver::read_string( driver::read< std::uintptr_t >( this ), 64 ); 19 | } 20 | }; 21 | 22 | struct il2cpp_klass_t { 23 | OFFSET( name_ptr( ), std::uintptr_t, 0x10 ); 24 | OFFSET( namespace_ptr( ), std::uintptr_t, 0x18 ); 25 | OFFSET( fields_table( ), std::uintptr_t, 0x80 ); 26 | OFFSET( static_fields_table( ), std::uintptr_t, 0xB8 ); 27 | OFFSET( fields_size( ), std::uint16_t, 0x120 ); 28 | 29 | il2cpp_field_t* get_field( const char* name ) { 30 | auto table = this->fields_table( ); 31 | if ( !table ) { 32 | return nullptr; 33 | } 34 | 35 | for ( auto current_field = table; current_field < table + ( this->fields_size( ) * 0x20 ); current_field += 0x20 ) { 36 | auto field_name = driver::read_string( driver::read< std::uintptr_t >( current_field ), 64 ); 37 | if ( !_stricmp( field_name.c_str( ), name ) ) { 38 | return reinterpret_cast< il2cpp_field_t* >( current_field ); 39 | } 40 | } 41 | return nullptr; 42 | } 43 | 44 | template< typename t = std::uintptr_t > 45 | t get_static_field( const char* name ) { 46 | auto field = this->get_field( name ); 47 | if ( !field ) { 48 | return 0; 49 | } 50 | 51 | return driver::read< t >( this->static_fields_table( ) + field->offset( ) ); 52 | } 53 | 54 | std::string name( ) { 55 | return driver::read_string< std::uintptr_t >( this->name_ptr( ), 64 ); 56 | } 57 | 58 | std::string ns( ) { 59 | return driver::read_string< std::uintptr_t >( this->namespace_ptr( ), 64 ); 60 | } 61 | }; 62 | 63 | struct il2cpp_image_t { 64 | il2cpp_klass_t* get_klass( const char* name, const char* ns = 0 ) { 65 | unsigned __int64 table_1 = driver::read< unsigned __int64 >( glb::game_assembly + glb::class_table_1 ); 66 | unsigned __int64 table_2 = driver::read< unsigned __int64 >( glb::game_assembly + glb::class_table_2 ); 67 | unsigned __int64 table_3 = driver::read< unsigned __int64 >( glb::game_assembly + glb::class_table_3 ); 68 | 69 | const auto class_idx = driver::read< std::uint32_t >( this + 0x18 ); 70 | for ( int i = 0; i < class_idx; i++ ) { // broken, will not find all classes outside of Assembly-CSharp, try to fix this yourself if I don't fix it before (you can bruteforce them but not ideal) 71 | int v2 = i + driver::read< int >( driver::read< int >( this + 0x28 ) ); 72 | if ( v2 == -1 ) { 73 | continue; 74 | } 75 | 76 | unsigned __int64 u1 = table_1 + driver::read< int >( table_2 + 0xA0 ) + 0x58 * v2; 77 | unsigned __int64 index_handle = (u1 - driver::read< int >( table_2 + 0xA0 ) - table_1 ) / 0x58; 78 | unsigned __int64 u2 = ( unsigned __int64 )0x8 * ( int )index_handle; 79 | 80 | auto klass = driver::read< il2cpp_klass_t* >( u2 + table_3 ); 81 | if ( !klass ) { 82 | continue; 83 | } 84 | 85 | auto klass_name = klass->name( ); 86 | auto klass_ns = klass->ns( ); 87 | 88 | if ( ns != 0 ) { 89 | if ( !_stricmp( klass_ns.c_str( ), ns ) && !_stricmp( klass_name.c_str( ), name ) ) { 90 | return reinterpret_cast< il2cpp_klass_t* >( klass ); 91 | } 92 | } 93 | else { 94 | if ( !_stricmp( klass_name.c_str( ), name ) ) { 95 | return reinterpret_cast< il2cpp_klass_t* >( klass ); 96 | } 97 | } 98 | } 99 | return nullptr; 100 | } 101 | }; 102 | 103 | struct il2cpp { 104 | static inline std::vector< std::pair< std::string, il2cpp_image_t* > > images{ }; 105 | 106 | static void initialize( ) { 107 | glb::game_assembly = driver::get_module< std::uintptr_t >( L"GameAssembly.dll" ); 108 | if ( !glb::game_assembly ) { 109 | return; 110 | } 111 | 112 | const auto assemblies_end = driver::read< std::uintptr_t >( glb::game_assembly + offsets::assemblies_end ); 113 | for ( auto current_assembly = driver::read< std::uintptr_t >( glb::game_assembly + offsets::assemblies_begin ); current_assembly < assemblies_end; current_assembly += sizeof( std::uintptr_t ) ) { 114 | images.emplace_back( driver::read_string( driver::read< std::uintptr_t >( driver::read< std::uintptr_t >( current_assembly ) + 0x18 ), 64 ), driver::read< il2cpp_image_t* >( driver::read< std::uintptr_t >( current_assembly ) ) ); 115 | } 116 | } 117 | 118 | template< typename t = il2cpp_klass_t* > 119 | static t find_klass( const char* assembly_name, const char* klass_name, const char* ns = 0 ) { 120 | for ( int i = 0; i < images.size( ); i++ ) { 121 | const auto image = &images[ i ]; 122 | if ( !_stricmp( image->first.c_str( ), assembly_name ) ) { 123 | const auto klass = image->second->get_klass( klass_name, ns ); 124 | if ( !klass ) { 125 | continue; 126 | } 127 | return ( t )klass; 128 | } 129 | } 130 | return nullptr; 131 | } 132 | 133 | static std::uintptr_t get_field_offset( const char* assembly, const char* klass_name, const char* field_name, const char* ns = 0 ) { 134 | const auto klass = find_klass( assembly, klass_name, ns ); 135 | if ( !klass ) { 136 | return 0; 137 | } 138 | 139 | const auto field = klass->get_field( field_name ); 140 | if ( !field ) { 141 | return 0; 142 | } 143 | 144 | return field->offset( ); 145 | } 146 | 147 | static std::uintptr_t get_static_field_offset( const char* assembly, const char* klass_name, const char* field_name, const char* ns = 0 ) { 148 | const auto klass = find_klass( assembly, klass_name, ns ); 149 | if ( !klass ) { 150 | return 0; 151 | } 152 | 153 | return klass->get_static_field( field_name ); 154 | } 155 | }; 156 | --------------------------------------------------------------------------------