Function operation of Frida usage

Frida interface function introduction

Frida is a so level hook framework, which can help developers and security personnel analyze the so module of the specified process. It mainly provides a Python interface with simple functions and a JS interface with rich functions, which makes hook functions and modified so programmable. The interface includes the interface between the main control end and the target process.

The interaction interface of the target process is divided into:

  • JS interface
    Functions include but are not limited to process operation, module operation, memory operation, function operation, thread operation, network communication, data flow operation, file operation, database operation and register operation.
  • Python interface
    It provides less functions, which are basically used to obtain process, module and function operations.

Frida has many functions, so there is no need for everyone to master. My current requirement is to modify the function parameter value and get the function return value when the program is running. Next, I will discuss these two functions through JS and Python script.


How to inject Android system

  1. Open an APP and jump to the page you want to inject
  2. Through the adb shell dumpsys activity top, get the details of the (top-level) Activity in the current Android system that interacts with users
  3. Decompile the APK file, check the code according to the information provided in the previous step, then locate the function you want to hook, and check the transfer parameter and return value of the function
  4. Write js injection code and run the script to inject into the function

Step 1 Demo - Open APP page

I wrote an Android Demo myself. Here is the code.

Step 2 Demo - get top level Activity information

adb shell dumpsys activity top

TASK com.example.myapplication id=190
  ACTIVITY com.example.myapplication/.MainActivity 6b2b7a5 pid=31745
    Local Activity e71a071 State:
      mResumed=false mStopped=true mFinished=false
      mChangingConfigurations=false
      mCurrentConfig={1.0 ?mcc?mnc zh_CN ldltr sw411dp w411dp h659dp 420dpi nrml port finger -keyb/v/h -nav/h s.4}
      mLoadersStarted=true
      Active Fragments in 6e3c2de:
        #0: ReportFragment{a0dccbf #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
          mFragmentId=#0 mContainerId=#0 mTag=androidx.lifecycle.LifecycleDispatcher.report_fragment_tag
          mState=3 mIndex=0 mWho=android:fragment:0 mBackStackNesting=0
          mAdded=true mRemoving=false mResumed=false mFromLayout=false mInLayout=false
          mHidden=false mDetached=false mMenuVisible=true mHasMenu=false
          mRetainInstance=false mRetaining=false mUserVisibleHint=true
          mFragmentManager=FragmentManager{6e3c2de in HostCallbacks{6fc8f8c}}
          mHost=android.app.Activity$HostCallbacks@6fc8f8c
          Child FragmentManager{f8df6d5 in ReportFragment{a0dccbf}}:
            FragmentManager misc state:
              mHost=android.app.Activity$HostCallbacks@6fc8f8c
              mContainer=android.app.Fragment$1@e652eea
              mParent=ReportFragment{a0dccbf #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
              mCurState=3 mStateSaved=true mDestroyed=false
      Added Fragments:
        #0: ReportFragment{a0dccbf #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
      FragmentManager misc state:
        mHost=android.app.Activity$HostCallbacks@6fc8f8c
        mContainer=android.app.Activity$HostCallbacks@6fc8f8c
        mCurState=3 mStateSaved=true mDestroyed=false
    ViewRoot:
      mAdded=true mRemoved=false
      mConsumeBatchedInputScheduled=false
      mConsumeBatchedInputImmediatelyScheduled=false
      mPendingInputEventCount=0
      mProcessInputEventsScheduled=false
      mTraversalScheduled=false      mIsAmbientMode=false
      android.view.ViewRootImpl$NativePreImeInputStage: mQueueLength=0
      android.view.ViewRootImpl$ImeInputStage: mQueueLength=0
      android.view.ViewRootImpl$NativePostImeInputStage: mQueueLength=0
    Choreographer:
      mFrameScheduled=false
      mLastFrameTime=64637557 (5732266 ms ago)
    View Hierarchy:
      com.android.internal.policy.PhoneWindow$DecorView{85c75db V.E...... R....... 0,0-1080,1920}
        android.widget.LinearLayout{da91878 V.E...... ........ 0,0-1080,1794}
          android.view.ViewStub{b442b51 G.E...... ......I. 0,0-0,0 #10203b0 android:id/action_mode_bar_stub}
          android.widget.FrameLayout{2df4fb6 V.E...... ........ 0,63-1080,1794}
            androidx.appcompat.widget.ActionBarOverlayLayout{2294b7 V.E...... ........ 0,0-1080,1731 #7f070030 app:id/decor_content_parent}
              androidx.appcompat.widget.ContentFrameLayout{4934424 V.E...... ........ 0,147-1080,1731 #1020002 android:id/content}
                androidx.constraintlayout.widget.ConstraintLayout{94f2b8d V.E...... ........ 0,0-1080,1584}
                  androidx.appcompat.widget.AppCompatTextView{208b142 V.ED..... ........ 191,724-890,861 #7f07008d app:id/tv}
              androidx.appcompat.widget.ActionBarContainer{ec1c553 V.ED..... ........ 0,0-1080,147 #7f070008 app:id/action_bar_container}
                androidx.appcompat.widget.Toolbar{3d27e90 V.E...... ........ 0,0-1080,147 #7f070006 app:id/action_bar}
                  androidx.appcompat.widget.AppCompatTextView{68ff389 V.ED..... ........ 42,38-196,109}
                  androidx.appcompat.widget.ActionMenuView{c749f8e V.E...... ......ID 1080,0-1080,147}
                androidx.appcompat.widget.ActionBarContextView{c1963af G.E...... ......I. 0,0-0,0 #7f07000e app:id/action_context_bar}
        android.view.View{b88f3bc V.ED..... ........ 0,1794-1080,1920 #1020030 android:id/navigationBarBackground}
        android.view.View{2fb3f45 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
    Looper (main, tid 1) {69f269a}
      (Total messages: 0, polling=false, quitting=false)
    Local FragmentActivity e71a071 State:
      mCreated=true mResumed=false mStopped=true    FragmentManager misc state:
      mHost=androidx.fragment.app.FragmentActivity$HostCallbacks@4a28bcb
      mContainer=androidx.fragment.app.FragmentActivity$HostCallbacks@4a28bcb
      mCurState=2 mStateSaved=true mStopped=true mDestroyed=false

Step 3 Demo - view code

                  . If you decompile and open the APK developed by others, it's probably confused, but it's the same. It's just to change the class name, function name, variable name into a, b, c.. It's just to increase the difficulty of looking at the code, but the calling process is the same.

public class MainActivity extends AppCompatActivity {

    private TextView testview;
    private String returnvalule;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testview = findViewById(R.id.tv);

        testview.setText("Score 1:60 \n");
        returnvalule = addnumber("60");
        testview.append("addnumber()Return value of:" + returnvalule);
    }

    private String addnumber(String nubs){
        testview.append("Score 2:" + nubs + "\n");
        return "I am the default return value⊙_⊙";
    }
}

Step 4 demonstration - JS with Python script injection

import frida
import sys

jscode = """
/* This field marks whether the Java virtual machine (for example, Dalvik or ART) has been loaded. Before operating anything in Java, confirm whether the value is true */
if(Java.available){
    /* 111,222,333 The purpose of these log s is to see where the process goes */
    console.log("111");
    
    /* Java.perform(function(){ ... Javascript When the code is successfully attached to the target process, our core code should be written in it. It's a fixed format */
    Java.perform(function(){
        
        /* Java.use Method is used to declare a Java class, which must be declared before using a Java class. For example, to declare a String class, specify the complete class name var StringClass=Java.use("java.lang.String"); */
        var MainActivity = Java.use("com.example.myapplication.MainActivity");
        console.log("222");
        
        /* Class. Function. Overload (parameter type). Implementation = function (parameter name){ */
        MainActivity.addnumber.overload("java.lang.String").implementation = function(nubs){
            console.log("333");
            
            /* Pass parameter to addnumber function to get return value of addnumber function */
            console.log(this.addnumber("77"));
            
            /* Modify the return value of the addnumber function */
            return "I am Mysticbinary!";
        }
    });

}
"""

def on_message(message, data):
    if message['type'] == 'send':
        print(" {0}".format(message['payload']))
    else:
        print(message)

# Find USB device and attach to target process
session = frida.get_usb_device().attach('com.example.myapplication')

# Create script in target process
script = session.create_script(jscode)

# Register message callback
script.on('message', on_message)

# Load the created javascript script
script.load()

# Read system input
sys.stdin.read()

Call Description:

Script injection Description:

  1. Open the interface you want to inject
  2. Run the code in step 4 above
  3. Android goes back to the main interface of the system and enters again, mainly to trigger the function
  4. View run results

Code run result:

app injection result:


Reference articles

https://www.cnblogs.com/mysticbinary/p/12012935.html
https://frida.re/docs/javascript-api/
https://bbs.pediy.com/thread-226846.htm
https://www.52pojie.cn/forum.php?mod=viewthread&tid=931872

Keywords: Android Java Python Fragment

Added by russ8 on Tue, 10 Dec 2019 19:27:37 +0200