title: Find manually registered (obfuscated) native function address
body: <p>I'm trying to understand an Android app which contains a native method named <code>foo</code> in class <code>com.app.Bar</code> </p>
  
  <p>Inside class <code>Bar</code> there is a static clause that loads a shared object <code>System.loadLibrary("libfoo.so")</code> which I assume is build with <a href="https://developer.android.com/training/articles/perf-jni#native-libraries" rel="nofollow noreferrer">-fvisibility=hidden</a> because the only export is <code>JNI_OnLoad</code>, no <code>JNIEXPORT void JNICALL Java_com_app_Bar_foo</code> which means <code>public native int foo</code> does not follow the <a href="https://stackoverflow.com/questions/32470463/is-the-naming-convention-for-java-native-interface-method-and-module-name">naming convention</a>. </p>
  
  <ol>
  <li>What is the process when <code>foo</code> is <a href="https://android.googlesource.com/platform/development/+/master/samples/SimpleJNI/jni/native.cpp#56" rel="nofollow noreferrer">invoked</a> ? </li>
  <li>How can I extract the address of <code>foo</code> ? I'm familiar with <a href="https://frida.re" rel="nofollow noreferrer">Frida</a></li>
  <li>Is there a way to output all <code>JNINativeMethod[] methods</code> ? </li>
  </ol>
  
  <p>What have I tried so far ? </p>
  
  <ol>
  <li><p><a href="https://github.com/maaaaz/jnianalyzer" rel="nofollow noreferrer">JNIAnalyzer</a> outputs 13K lines that look like <code>JNI_OnLoad@@Base+0x712e</code></p></li>
  <li><p>Frida script to try to find <code>foo</code> address (did not work)</p></li>
  </ol>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>function intercept(address) {
      try {
          Interceptor.attach(address, {
              onEnter: function(args) {
                  console.log("onEnter", address);
              },
              onLeave: function(ignored) {}
          });
      } catch (e) {
          console.error(e);
      }
  }
  function Main() {
      var dlopen = new NativeFunction(Module.findExportByName(null, 'dlopen'), 'pointer', ['pointer', 'int']);
      var dlsym = new NativeFunction(Module.findExportByName(null, 'dlsym'), 'pointer', ['pointer', 'pointer']);
      Process.enumerateModulesSync().forEach(function(m) {
          if (m.name === "libfoo.so") {
              console.log("Module", JSON.stringify(m));
              var handle = dlopen(Memory.allocUtf8String(m.path), 1);
              var symb = Memory.allocUtf8String("foo");
              var exports = Module.enumerateExportsSync(m.name);
              console.log(JSON.stringify({
                  handle: handle,
                  symb: symb,
                  dlsym: dlsym(handle, symb),
                  exports: exports.map(function(ex){ return ex.address + ": " + ex.name })
              }, null, 2));
              // intercept all exports
              exports.forEach(function(ex){
                  intercept(ex.address);
              });
              // explicit intercept foo by known offset
              intercept(m.base.add(0x22334)); // this outputs "Error: unable to intercept function at 0x86c96328; please file a bug"
          }
      });
  
      console.log("sleep..");
      Thread.sleep(1.5);
      console.log("invoke", Java.use('com.clazz.foo').signToken("A".repeat(32)));
  }
  Java.perform(Main);</code></pre>
  </div>
  </div>
  </p>