3

I'm trying to extract symbols from an Android Unity game to detect something related to the game which I can intercept and replace variables.

The following code will enumerate symbols from libmain.so, libmono.so & libunity.so but not UnityEngine.*.dll.so, it seems they did not get addresses (retval equals 0x0), I'm assuming they did not load yet (that's way no address), what function is in-charge on loading them ? can I hook it and than enumerate symbols ? any help ?

Interceptor.attach(Module.findExportByName(null, 'dlopen'), {
    onEnter: function(args) {
        this.lib = Memory.readUtf8String(args[0]);
    },
    onLeave: function(retval) {
        console.log(
            this.lib.substr(this.lib.lastIndexOf('/') + 1, this.lib.length) +
            ' [ ' + retval + ' ] \n' +
            Module.enumerateExportsSync(this.lib).map(function(x) {
                return x.name
            })
        );
    }
});

output:

libmain.so [ 0xac651e74 ] 
__aeabi_unwind_cpp_pr0,JNI_OnLoad,__aeabi_unwind_cpp_pr1,__aeabi_unwind_cpp_pr2,__gnu_Unwind_Restore_VFP_D,__gnu_Unwind_Restore_VFP,__gnu_Unwind_Restore_VFP_D_16_to_31,__gnu_Unwind_Restore_WMMXD,__gnu_Unwind_Restore_WMMXC,restore_core_regs,_Unwind_GetCFA,__gnu_Unwind_RaiseException,__gnu_Unwind_ForcedUnwind,__gnu_Unwind_Resume,__gnu_Unwind_Resume_or_Rethrow,_Unwind_Complete,_Unwind_DeleteException,_Unwind_VRS_Get,_Unwind_VRS_Set,__gnu_Unwind_Backtrace,__gnu_unwind_execute,_Unwind_VRS_Pop,__gnu_Unwind_Save_VFP_D,__gnu_Unwind_Save_VFP,__gnu_Unwind_Save_VFP_D_16_to_31,__gnu_Unwind_Save_WMMXD,__gnu_Unwind_Save_WMMXC,__restore_core_regs,___Unwind_RaiseException,_Unwind_RaiseException,___Unwind_Resume,_Unwind_Resume,___Unwind_Resume_or_Rethrow,_Unwind_Resume_or_Rethrow,___Unwind_ForcedUnwind,_Unwind_ForcedUnwind,___Unwind_Backtrace,_Unwind_Backtrace,__gnu_unwind_frame,_Unwind_GetRegionStart,_Unwind_GetLanguageSpecificData,_Unwind_GetDataRelBase,_Unwind_GetTextRelBase
libunity.so [ 0xb399b004 ] 
_ZNSt6vectorIcSaIcEE17_M_default_appendEj,_ZNSt6vectorIjSaIjEE17_M_default_appendEj,_ZdaPv,_ZdlPv,_Znaj,_Znwj,_ZdlPvRKSt9nothrow_t,_ZnwjRKSt9nothrow_t,_ZdaPvRKSt9nothrow_t,_ZnajRKSt9nothrow_t,_ZNSt6vectorIiSaIiEE17_M_default_appendEj,_ZNSt8_Rb_treeIiiSt9_IdentityIiESt4lessIiESaIiEE12_M_erase_auxESt23_Rb_tree_const_iteratorIiES7_,_ZNSt8_Rb_treeIiiSt9_IdentityIiESt4lessIiESaIiEE16_M_insert_uniqueIRKiEESt4pairISt17_Rb_tree_iteratorIiEbEOT_,_ZNSt8_Rb_treeIiiSt9_IdentityIiESt4lessIiESaIiEE5eraseERKi,_ZNSt8_Rb_treeIiiSt9_IdentityIiESt4lessIiESaIiEE8_M_eraseEPSt13_Rb_tree_nodeIiE,_ZNSt6vectorIfSaIfEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPfS1_EEjRKf,_ZNSt6vectorIfSaIfEE19_M_emplace_back_auxIJfEEEvDpOT_,_ZNSt6vectorISt4pairIiiESaIS1_EE17_M_default_appendEj,_ZNSt6vectorIiSaIiEE19_M_emplace_back_auxIJRKiEEEvDpOT_,_ZNSt6vectorIiSaIiEE19_M_emplace_back_auxIJiEEEvDpOT_,_ZNSt6vectorISt4pairIijESaIS1_EE19_M_emplace_back_auxIJS1_EEEvDpOT_,_ZNSt6vectorISt4pairIijESaIS1_EEaSERKS3_,_ZSt17__rotate_adaptiveIN9__gnu_cxx17__normal_iteratorIPSt4pairIijESt6vectorIS3_SaIS3_EEEES4_iET_S9_S9_S9_T1_SA_T0_SA_,_ZSt8__rotateIN9__gnu_cxx17__normal_iteratorIPSt4pairIijESt6vectorIS3_SaIS3_EEEEEvT_S9_S9_St26random_access_iterator_tag,_ZSt8__rotateIPiEvT_S1_S1_St26random_access_iterator_tag,_ZNSt6vectorIhSaIhEE17_M_default_appendEj,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE16_M_insert_uniqueIRKjEESt4pairISt17_Rb_tree_iteratorIjEbEOT_,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE16_M_insert_uniqueISt23_Rb_tree_const_iteratorIjEEEvT_S9_,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE16_M_insert_uniqueIjEESt4pairISt17_Rb_tree_iteratorIjEbEOT_,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE29_M_get_insert_hint_unique_posESt23_Rb_tree_const_iteratorIjERKj,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE7_M_copyEPKSt13_Rb_tree_nodeIjEPS7_,_ZNSt8_Rb_treeIjjSt9_IdentityIjESt4lessIjESaIjEE8_M_eraseEPSt13_Rb_tree_nodeIjE,_ZNSt6vectorIS_IfSaIfEESaIS1_EE19_M_emplace_back_auxIJRKS1_EEEvDpOT_,_ZNSt8_Rb_treeIiiSt9_IdentityIiESt4lessIiESaIiEE16_M_insert_uniqueIiEESt4pairISt17_Rb_tree_iteratorIiEbEOT_,_ZNSt6vectorIjSaIjEE19_M_emplace_back_auxIJRKjEEEvDpOT_,_ZNSt6vectorIiSaIiEE13_M_insert_auxIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_,_ZNSt6vectorIiSaIiEE13_M_insert_auxIJiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_,_ZNSt8_Rb_treeIPKvS1_St9_IdentityIS1_ESt4lessIS1_ESaIS1_EE16_M_insert_uniqueIS1_EESt4pairISt17_Rb_tree_iteratorIS1_EbEOT_,_ZNSt8_Rb_treeIPKvS1_St9_IdentityIS1_ESt4lessIS1_ESaIS1_EE8_M_eraseEPSt13_Rb_tree_nodeIS1_E,_ZNSt6vectorIjSaIjEE13_M_assign_auxIN9__gnu_cxx17__normal_iteratorIPjS1_EEEEvT_S7_St20forward_iterator_tag,_ZNSt6vectorIjSaIjEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPjS1_EEjRKj,_ZNSt6vectorIiSaIiEE13_M_assign_auxISt23_Rb_tree_const_iteratorIiEEEvT_S5_St20forward_iterator_tag,_ZNSt8_Rb_treeIiSt4pairIKiiESt10_Select1stIS2_ESt4lessIiESaIS2_EE22_M_emplace_hint_uniqueIJRKSt21piecewise_construct_tSt5tupleIJRS1_EESD_IJEEEEESt17_Rb_tree_iteratorIS2_ESt23_Rb_tree_const_iteratorIS2_EDpOT_,_ZNSt8_Rb_treeIiSt4pairIKiiESt10_Select1stIS2_ESt4lessIiESaIS2_EE29_M_get_insert_hint_unique_posESt23_Rb_tree_const_iteratorIS2_ERS1_,_ZNSt8_Rb_treeIiSt4pairIKiiESt10_Select1stIS2_ESt4lessIiESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E,_ZNSt6vectorIfSaIfEE17_M_default_appendEj,_ZNSt6vectorIjSaIjEE19_M_emplace_back_auxIJjEEEvDpOT_,UnitySendMessage,_ZNSt6vectorIcSaIcEE19_M_emplace_back_auxIJcEEEvDpOT_,_ZNSt6vectorIhSaIhEE15_M_range_insertIPhEEvN9__gnu_cxx17__normal_iteratorIS3_S1_EET_S7_St20forward_iterator_tag,_ZNSt6vectorIhSaIhEE19_M_emplace_back_auxIJhEEEvDpOT_,JNI_OnLoad,JNI_OnUnload,_ZNSt8_Rb_treeIiSt4pairIKi9sigactionESt10_Select1stIS3_ESt4lessIiESaIS3_EE22_M_emplace_hint_uniqueIJRKSt21piecewise_construct_tSt5tupleIJRS1_EESE_IJEEEEESt17_Rb_tree_iteratorIS3_ESt23_Rb_tree_const_iteratorIS3_EDpOT_,_ZNSt8_Rb_treeIiSt4pairIKi9sigactionESt10_Select1stIS3_ESt4lessIiESaIS3_EE29_M_get_insert_hint_unique_posESt23_Rb_tree_const_iteratorIS3_ERS1_,_ZNSt8_Rb_treeIiSt4pairIKi9sigactionESt10_Select1stIS3_ESt4lessIiESaIS3_EE8_M_eraseEPSt13_Rb_tree_nodeIS3_E,_ZNSt8_Rb_treeIiSt4pairIKiiESt10_Select1stIS2_ESt4lessIiESaIS2_EE16_M_insert_uniqueIS0_IiiEEES0_ISt17_Rb_tree_iteratorIS2_EbEOT_,_ZNSt8_Rb_treeIiSt4pairIKiiESt10_Select1stIS2_ESt4lessIiESaIS2_EE7_M_copyEPKSt13_Rb_tree_nodeIS2_EPSA_,_ZN9__gnu_cxx21_Hashtable_prime_listImE16__stl_prime_listE,_ZNSt8_Rb_treeIyySt9_IdentityIyESt4lessIyESaIyEE12_M_erase_auxESt23_Rb_tree_const_iteratorIyES7_,_ZNSt8_Rb_treeIyySt9_IdentityIyESt4lessIyESaIyEE16_M_insert_uniqueIRKyEESt4pairISt17_Rb_tree_iteratorIyEbEOT_,_ZNSt8_Rb_treeIyySt9_IdentityIyESt4lessIyESaIyEE5eraseERKy,_ZNSt8_Rb_treeIyySt9_IdentityIyESt4lessIyESaIyEE8_M_eraseEPSt13_Rb_tree_nodeIyE,_ZNSt6vectorISt4pairIS0_IttEfESaIS2_EE17_M_default_appendEj,_ZNSt6vectorIhSaIhEE19_M_emplace_back_auxIJRKhEEEvDpOT_,_ZNSt6vectorIySaIyEE19_M_emplace_back_auxIJRKyEEEvDpOT_,_ZNSt6vectorIS_IySaIyEESaIS1_EE19_M_emplace_back_auxIJRKS1_EEEvDpOT_,_ZNSt6vectorIhSaIhEE15_M_range_insertIN9__gnu_cxx17__normal_iteratorIPhS1_EEEEvS6_T_S7_St20forward_iterator_tag,_ZNSt6vectorIhSaIhEE15_M_range_insertIPKcEEvN9__gnu_cxx17__normal_iteratorIPhS1_EET_S9_St20forward_iterator_tag,_ZNSt6vectorIhSaIhEE15_M_range_insertIPKhEEvN9__gnu_cxx17__normal_iteratorIPhS1_EET_S9_St20forward_iterator_tag,_ZNSt6vectorIhSaIhEE15_M_range_insertIPcEEvN9__gnu_cxx17__normal_iteratorIPhS1_EET_S8_St20forward_iterator_tag,_ZNSt6vectorIxSaIxEE19_M_emplace_back_auxIJRKxEEEvDpOT_,_ZNSt6vectorIhSaIhEEaSERKS1_,_ZNSt6vectorItSaItEEaSERKS1_
libmono.so [ 0xb399b3f4 ] 
mono_jit_tls_id,mono_tracev,mono_jit_trace_calls,mono_break_on_exc,mono_compile_aot,mono_breakpoint_info_index,mono_aot_only,mono_build_date,mono_use_imt,mono_do_signal_chaining,mono_inject_async_exc_method,mono_break_at_bb_bb_num,mono_inject_async_exc_pos,mono_do_x86_stack_align,mono_break_at_bb_method,mono_pmip,mono_dont_free_global_codeman,mono_print_method_from_ip,mono_debug_lookup_source_location,mono_debug_free_source_location,mono_domain_get,mono_jit_info_table_find,g_free,mono_method_full_name,mono_mempool_alloc0,mono_code_manager_reserve,mono_code_manager_new,mono_type_generic_inst_is_valuetype,mono_type_get_underlying_type,mono_class_enum_basetype,mono_method_get_header,mono_jit_stats,mono_type_get_name,mono_inst_name,mono_class_from_mono_type,mono_metadata_signature_alloc,mono_mempool_alloc,mono_get_root_domain,mono_free_verify_list,mono_loader_get_last_error,mono_compile_method,mono_type_size,mono_metadata_generic_class_is_valuetype,mono_class_min_align,mono_environment_exitcode_get,mono_mempool_destroy,mono_thread_exit,mono_thread_attach,mono_domain_set,mono_jit_thread_attach,mono_thread_current,mono_debugger_thread_created,mono_thread_attach_aborted_cb,mono_debugger_thread_cleanup,mono_profiler_get_events,mono_class_vtable,mono_class_init,mono_runtime_class_init,mono_metadata_field_info,mono_lookup_internal_call,mono_image_rva_map,mono_thread_interruption_request_flag,mono_ldstr,mono_metadata_blob_heap,mono_ldtoken,mono_lookup_pinvoke_call,mono_type_get_object,mono_method_signature,mono_code_manager_new_dynamic,mono_method_get_generic_container,mono_code_manager_commit,mono_class_inflate_generic_method,mono_mempool_new,mono_debug_using_mono_debugger,mono_local_deadce,mono_runtime_invoke,mono_get_exception_execution_engine,mono_exception_from_name_msg,mono_loader_error_prepare_exception,mono_get_exception_bad_image_format,.....,GC_push_all_stack,GC_delete_thread,GC_lookup_thread,GC_start_blocking,GC_end_blocking,VER_1
mscorlib.dll.so [ 0x0 ] 

UnityEngine.dll.so [ 0x0 ] 

UnityEngine.CoreModule.dll.so [ 0x0 ] 

UnityEngine.AccessibilityModule.dll.so [ 0x0 ] 

UnityEngine.ParticleSystemModule.dll.so [ 0x0 ] 

UnityEngine.PhysicsModule.dll.so [ 0x0 ] 

UnityEngine.VehiclesModule.dll.so [ 0x0 ] 

UnityEngine.ClothModule.dll.so [ 0x0 ] 

UnityEngine.AIModule.dll.so [ 0x0 ] 

UnityEngine.AnimationModule.dll.so [ 0x0 ] 

UnityEngine.TextRenderingModule.dll.so [ 0x0 ] 

UnityEngine.UIModule.dll.so [ 0x0 ] 

UnityEngine.TerrainPhysicsModule.dll.so [ 0x0 ] 

UnityEngine.IMGUIModule.dll.so [ 0x0 ] 

UnityEngine.UnityWebRequestModule.dll.so [ 0x0 ] 

UnityEngine.UnityWebRequestAudioModule.dll.so [ 0x0 ] 

UnityEngine.UnityWebRequestTextureModule.dll.so [ 0x0 ] 

UnityEngine.UnityWebRequestWWWModule.dll.so [ 0x0 ] 

UnityEngine.UNETModule.dll.so [ 0x0 ] 

UnityEngine.DirectorModule.dll.so [ 0x0 ] 

UnityEngine.UnityAnalyticsModule.dll.so [ 0x0 ] 

UnityEngine.CrashReportingModule.dll.so [ 0x0 ] 

UnityEngine.PerformanceReportingModule.dll.so [ 0x0 ] 

UnityEngine.UnityConnectModule.dll.so [ 0x0 ] 

UnityEngine.WebModule.dll.so [ 0x0 ] 

UnityEngine.ARModule.dll.so [ 0x0 ] 

UnityEngine.VRModule.dll.so [ 0x0 ] 

UnityEngine.UIElementsModule.dll.so [ 0x0 ] 

UnityEngine.StyleSheetsModule.dll.so [ 0x0 ] 

UnityEngine.AudioModule.dll.so [ 0x0 ] 

UnityEngine.GameCenterModule.dll.so [ 0x0 ] 

UnityEngine.GridModule.dll.so [ 0x0 ] 

UnityEngine.ImageConversionModule.dll.so [ 0x0 ] 

UnityEngine.InputModule.dll.so [ 0x0 ] 

UnityEngine.JSONSerializeModule.dll.so [ 0x0 ] 

UnityEngine.ParticlesLegacyModule.dll.so [ 0x0 ] 

UnityEngine.Physics2DModule.dll.so [ 0x0 ] 

UnityEngine.ScreenCaptureModule.dll.so [ 0x0 ] 

UnityEngine.SpriteMaskModule.dll.so [ 0x0 ] 

UnityEngine.TerrainModule.dll.so [ 0x0 ] 

UnityEngine.TilemapModule.dll.so [ 0x0 ] 

UnityEngine.VideoModule.dll.so [ 0x0 ] 

UnityEngine.WindModule.dll.so [ 0x0 ] 

System.dll.so [ 0x0 ] 

Assembly-CSharp-firstpass.dll.so [ 0x0 ] 

Assembly-CSharp.dll.so [ 0x0 ] 

UnityEngine.UI.dll.so [ 0x0 ] 

UnityEngine.Networking.dll.so [ 0x0 ] 

UnityEngine.Timeline.dll.so [ 0x0 ] 

UnityEngine.SpatialTracking.dll.so [ 0x0 ] 

UnityEngine.Advertisements.Android.dll.so [ 0x0 ] 

Facebook.Unity.dll.so [ 0x0 ] 

Facebook.Unity.Settings.dll.so [ 0x0 ] 

Facebook.Unity.Android.dll.so [ 0x0 ] 

Facebook.Unity.IOS.dll.so [ 0x0 ] 

RTL.dll.so [ 0x0 ] 

System.Core.dll.so [ 0x0 ] 

2 Answers 2

4

Heh, it's some kind of necroposting, but why not?

Well, under hood unity3d use 2 variants: mono, that loads C# dlls and il2cpp, that convert it to C++ before build. For 2nd case u can use unity_metadata_loader or similar products, that takes C# metadata (description of classes mostly) with it's address offsets.

Basically you could need to wait for libil2cpp.so to load, then take it's base address, then calculate the in-memory offset by adding the base address. I use something like the following for this:

 var awaitForCondition = function (callback) {
     var int = setInterval(function () {
         var addr = Module.findBaseAddress('libil2cpp.so');
         if (addr) {
             console.log("Address found:", addr);
             clearInterval(int);
             callback(+addr);
             return;
         }
     }, 0);
 }
awaitForCondition((baseAddr)=>{
    var realAddr = ptr(baseAddr+offset);
});

In case of mono(your case, I think), you can try to read this post (russian language, but with code samples). Basically you need to hook mono's mono_image_open_full, if I remember correctly.

BTW, I'm not sure, what is Assembly-CSharp.dll.so, becouse of it's just C# dll, not so binary.

1
  • Thanks. used awaitForCondition :)
    – James W.
    Commented Apr 17, 2020 at 15:19
1

Answer

    // Constants
    const kIgnoreArg = '-';

    // Utils
    function pmalloc() {
        return Memory.alloc(Process.pointerSize);
    }
    function debug() {
        send({ event: 'DEBUG', data: Array.prototype.slice.call(arguments).join(' ') });
    }

    // Globals
    var Metadata = {}; // < className, { pointer, methods < methodName, { pointer, args[], returnType } >, fields } >
    var Global = {}; // save global variables across hooks
    var MonoApi = {
        mono_image_get_table_rows: ['int', ['MonoImage*', 'int'/*table_id*/]],
        mono_class_get: ['MonoClass*', ['MonoImage*', 'int'/*type_token*/]],
        mono_class_get_parent: ['MonoClass*', ['MonoClass*']],
        mono_class_get_name: ['char*', ['MonoClass*']],
        mono_method_get_name: ['char*', ['MonoMethod*']],
        mono_class_get_methods: ['MonoMethod*', ['MonoClass*', 'iter*']],
        mono_class_get_fields: ['MonoClassField*', ['MonoClass*', 'iter*']],
        mono_signature_get_params: ['MonoType*', ['MonoMethod*', 'iter*']],
        mono_field_full_name: ['char*', ['MonoField*']],
        mono_class_get_namespace: ['char*', ['MonoClass*']],
        mono_type_full_name: ['char*', ['MonoType*']],
        mono_signature_get_return_type: ['MonoType*', ['MonoMethodSignature*']],
        mono_class_get_method_from_name: ['MonoMethod*', ['MonoClass*', 'name*', 'int'/*number of params. -1 for any*/]],
        mono_method_signature: ['MonoMethodSignature*', ['MonoMethod*']],
        /** gpointer mono_compile_method (MonoMethod *method)
         * http://docs.go-mono.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-unsorted.html */
        mono_compile_method: ['gpointer*'/* pointer to the native code produced.*/, ['MonoMethod*']],
        /**
         * char* mono_string_to_utf8 (MonoString *s)
         * @param    s  a System.String
         * @Description
         # TODO mono_free
         *       Returns the UTF8 representation for s. The resulting buffer needs to be freed with mono_free().
         *       deprecated Use mono_string_to_utf8_checked to avoid having an exception arbritraly raised.
         */
        mono_string_to_utf8: ['char*', ['System.String*']],
        getClassMethods: function (klass) {
            var method, methods = {}, iter = pmalloc();

            while ( !(method = MonoApi.mono_class_get_methods(klass, iter)).isNull() ) {
                var methodName = MonoApi.mono_method_get_name(method).readUtf8String();
                if (!methodName.startsWith('<') /*|| methodName.startsWith('.')*/) {
                    var methodRef = MonoApi.mono_class_get_method_from_name(klass, Memory.allocUtf8String(methodName), -1);
                    var monoSignature = MonoApi.mono_method_signature(methodRef);
                    var retType = MonoApi.mono_type_full_name(MonoApi.mono_signature_get_return_type(monoSignature)).readUtf8String();
                    var args = MonoApi.getSignatureParams(monoSignature);
                    methods[methodName] = { ref: methodRef, args: args, ret: retType };
                }
            }

            return methods;
        },
        getSignatureParams: function (monoSignature) {
            var params, fields = [], iter = pmalloc();

            while ( !(params = MonoApi.mono_signature_get_params(monoSignature, iter)).isNull() )
                fields.push( MonoApi.mono_type_full_name(params).readUtf8String() );

            return fields;
        },
        getClassFields: function (monoClass) {
            var field, fields = [], iter = pmalloc();

            while ( !(field = MonoApi.mono_class_get_fields(monoClass, iter)).isNull() )
                fields.push( 
                    MonoApi.mono_field_full_name(field).readUtf8String().split(':')[1] );

            return fields;
        },
        init: function() {
            var monoModule = Process.findModuleByName('mono.dll');
            debug("Process.findModuleByName('mono.dll') ? " + monoModule);
            if (!monoModule) {
                var monoThreadAttach = Module.findExportByName(null, 'mono_thread_attach');
                debug("monoThreadAttach ? " + monoThreadAttach);
                if (monoThreadAttach)
                    monoModule = Process.findModuleByAddress(monoThreadAttach);
            }
            if (!monoModule) throw new Error('Mono.dll not found');

            Object.keys(MonoApi).map(function(exportName) {
                var monoApiIter = MonoApi[exportName];
                if (typeof monoApiIter === 'object') {
                    var returnValue = monoApiIter[0].endsWith('*') ? 'pointer' : monoApiIter[0];
                    var argumentTypes = monoApiIter[1].map(function(t) { return t.endsWith('*') ? 'pointer' : t });
                    var exportAddress = Module.findExportByName(monoModule.name, exportName);
                    MonoApi[exportName] = new NativeFunction(exportAddress, returnValue, argumentTypes);
                }
            });
        }
    };

    function intercept(op) {
        var nothingSetSoJustLogMethodArguments = !op.argumentsKeys && !op.onEnterCallback && !op.onLeaveCallback;
        var method = Metadata[op.className].methods[op.methodName];
        debug('Intercepting', op.className + '#' + op.methodName, JSON.stringify(method));
        // TODO assert re compile is necessary
        var monoCompileMethod = MonoApi.mono_compile_method(method.ref);
        Interceptor.attach(monoCompileMethod, {
            onEnter: function (args) {
                var argsValues = {};
                for (var i = 0, l = method.args.length; i < l; i++) {
                    var key = op.argumentsKeys ? op.argumentsKeys[i] : i;
                    if (key === kIgnoreArg)
                        continue;
                    var j = i + 1;
                    switch (method.args[i]) {
                        case 'string':
                            argsValues[key] = MonoApi.mono_string_to_utf8(args[j]).readUtf8String();
                            break;
                        case 'long':
                        case 'int':
                            argsValues[key] = parseInt(args[j]);
                            break;
                        default:
                            argsValues[key] = args[j];
                            break;
                    }
                }

                if (nothingSetSoJustLogMethodArguments)
                    debug(op.className + '#' + op.methodName, JSON.stringify(argsValues, null, 2));

                if (op.onEnterCallback)
                    op.onEnterCallback(argsValues);
            },
            onLeave: function (retval) {
                if (op.onLeaveCallback)
                    op.onLeaveCallback(retval);
            }
        });
    }

    function getMetadata(monoImage) {
        // MONO_TABLE_TYPEDEF = 0x2; // https://github.com/mono/mono/blob/master/mono/metadata/blob.h#L56
        for (var i = 1, l = MonoApi.mono_image_get_table_rows(monoImage, 0x2); i < l; ++i) {
            // MONO_TOKEN_TYPE_DEF = 0x2000000 // https://github.com/mono/mono/blob/master/mono/metadata/tokentype.h#L16
            var mClass = MonoApi.mono_class_get(monoImage, 0x2000000 | i);
            var className = MonoApi.mono_class_get_name(mClass).readUtf8String();
            var classNameSpace = MonoApi.mono_class_get_namespace(mClass).readUtf8String();
            try {
                var parentClassName = MonoApi.mono_class_get_name( MonoApi.mono_class_get_parent(mClass) ).readUtf8String();
                if (parentClassName === 'MonoBehaviour' && classNameSpace === '') {
                    Metadata[className] = {
                        // namespace: classNameSpace,
                        ref: mClass,
                        methods: MonoApi.getClassMethods(mClass),
                        fields: MonoApi.getClassFields(mClass)
                    };
                }
            } catch (e) {
                debug("Error @ getMetadata/mono_class_get_parent", e);
            }
        }
        send({ event: 'METADATA', data: Metadata });
    }

    function hookMonoLoad() {
        // hooking the method in charge of loading the DLL files
        Interceptor.attach(Module.findExportByName(null, 'mono_assembly_load_from_full'), {
            onEnter: function (args) {
                // passing variables to onLeave scope using 'this'
                this._args = {
                    image: args[0], // MonoImage* Image to load the assembly from
                    fname: args[1].readUtf8String() // const char* assembly name to associate with the assembly
                    // status: args[2], // MonoImageOpenStatus* returns the status condition
                    // refonly: args[3] // gboolean Whether this assembly is being opened in "reflection-only" mode.
                };
            },
            onLeave: function (_retval) {
                // Return value: A valid pointer to a MonoAssembly* on success and the status will be set to MONO_IMAGE_OK
                //               or NULL on error.
                if (this._args.fname.endsWith('Assembly-CSharp.dll')) {
                    MonoApi.init();
                    getMetadata(this._args.image);
                    /*placeholder*/
                }
            }
        });
    }

    function awaitForCondition(func) {
        // From MDN: If this parameter is less than 10, a value of 10 is used. Note that the actual delay may be longer;
        var delay = 10; // Fight for CPU
        var intervalPointer = setInterval(function() {
            // The condition that asserts Mono's required resources can be hooked
            // TODO switch with intercepting dlopen wait for mono.dll ?
            // FIXME use Module.ensureInitialized(name)
            if (Module.findExportByName(null, 'mono_get_root_domain')) {
                clearInterval(intervalPointer);
                func(); // Executing the passed function
            }
        }, delay);
    }

    // Main
    Java.perform(awaitForCondition(hookMonoLoad));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.