Mobile App Testing With Automation Trickery in Frida

By John Labelle ·

When you spend a lot of time doing security testing on mobile apps like I do, you begin to worry that a large part of your life will be spent rebooting mobile apps that have stopped responding. Frida (https://www.frida.re/) is a powerful testing tool and I love using it, but something I have had to come to terms with is: Stomping your way through an application's runtime is occasionally going to provoke its ire. And programming defensively is one thing, but I can't exactly blame a developer for not thinking, "How do I handle it if every parameter in this function is passed a null reference, instead of the data I painstakingly parsed from the server?"

mobile-app-testing

Nonetheless, it's a big waste of time - if the app crashes, I frequently have to log back in to the app, which is time consuming. Recently I was looking for bug bounties in the ChatWork app (https://go.chatwork.com/), which is more or less analogous to Slack. While I was successfully bypassing the certificate pinning, it kept crashing, which was annoying to me. I was working with Frida, so a thought occurred to me - I can automate this, right? I just need to:

  • Detect what page I'm on
  • If I'm on the login page, enter my details and then hit the 'login' button
  • Otherwise, do nothing

So simple. What could possibly go wrong? My first draft looked a little like this:

var login =setInterval(function(){
   Java.perform(function(){
      var BufferType= Java.use('android.widget.TextView$BufferType');
      var SPANNABLE = BufferType.valueOf("SPANNABLE");
      Java.choose("com.chatwork.android.shard.activity.LoginActivity",{
         onMatch: function(activity){
            if(activity.hasWindowFocus()){
               activity._f.value.setText("john.labelle@optiv.com",SPANNABLE);
               activity._g.value.setText("[REDACTED]",SPANNABLE);
               activity.d();
               clearInterval(login);
            }
         },
         onComplete:doNothing
      });
   });
},500);

 
Because I don't have access to the original application source code, I see the ProGuarded member names for the 'username' and 'password' text boxes (f and g, which becomes _f and _g in Frida syntax). Similarly, the d() method is called by the 'login' button. By checking for the login screen every half second, I will eventually detect it and then login to the application, right?

This code looks reasonably correct, but is also wrong for a very non-obvious reason. When you insert text into a text box in this way, the observing UI thread needs to redraw that text box... but the thread that Frida runs on is not an animator thread, and so enterprising UI automators are presented with the cryptic error:

Error: android.util.AndroidRuntimeException: Animators may only be run on Looper threads

My first instinct was, "Well, I know the main thread is an animator, so I'll simply call Java.scheduleOnMainThread(fn)..." Unfortunately, attempting to use this function in an asynchronous callback produced a Byzantine nightmare of errors and I didn't feel inclined to debug the Frida runtime. I needed something lazier.

After many hours of digging in despair through the Android API, I did the unthinkable: I read the documentation for the Android Looper class. It provides the rather useful prepare() method (https://developer.android.com/reference/android/os/Looper.html#prepare()):  

prepare
prepare() added in API level 1

void prepare ()
Initialize the current thread as a looper. This gives you a chance to create handlers that then reference this looper, before actually starting the loop. Be sure to call loop() after calling this method, and end it by calling quit().

 
Is the Frida thread an Android thread? Will there be performance penalties for making the main Frida thread an animation thread? Will I really reboot the application enough times to justify the enormous time sink of making this work automatically? I didn't think about any of these questions, I just gave it a try:

var login =setInterval(function(){
   Java.perform(function(){
      var BufferType= Java.use('android.widget.TextView$BufferType');
      var SPANNABLE = BufferType.valueOf("SPANNABLE");
      Java.choose("com.chatwork.android.shard.activity.LoginActivity",{
         onMatch: function(activity){
            if(activity.hasWindowFocus()){
               var looper=Java.use("android.os.Looper");
               looper.prepare();
               activity._f.value.setText("john.labelle@optiv.com",SPANNABLE);
               activity._g.value.setText("[REDACTED]",SPANNABLE);
               activity.d();
               clearInterval(login);
            }
         },
         onComplete:doNothing
      });
   });
},500);

 
Which indeed does the trick. Long story short, you can indeed make the Frida thread an animation thread on Android, and this will allow you to drive mobile application user interfaces and speed up your mobile app security testing. Give it a try and let me know how it works in the comments!

john-labelle

John Labelle

Senior Consultant

John is a senior security consultant with Optiv’s application security team. In this role, he specializes in source code review.