Frida介面功能介紹 Frida是個so級別的hook框架,它可以幫助開發、安全人員對指定的進程的so模塊進行分析。它主要提供了功能簡單的Python介面和功能豐富的JS介面,使得hook函數和修改so可以編程化,介面中包含了主控端與目標進程的交互介面。 目標進程的交互介面分為: JS介面 功能包 ...
Frida介面功能介紹
Frida是個so級別的hook框架,它可以幫助開發、安全人員對指定的進程的so模塊進行分析。它主要提供了功能簡單的Python介面和功能豐富的JS介面,使得hook函數和修改so可以編程化,介面中包含了主控端與目標進程的交互介面。
目標進程的交互介面分為:
- JS介面
功能包括但不限於進程操作、模塊操作、記憶體操作、函數操作、線程操作、網路通信、數據流操作、文件操作、資料庫操作、寄存器操作。 - Python介面
提供的功能較少,基本都是用來獲取進程、模塊、函數操作。
Frida功能較多,暫時沒有需求要每個都掌握,我現在的需求就是在程式運行的時候修改函數傳參值、得到函數的返回值這種簡單操作,下麵通過JS配合Python腳本方式對這兩個功能進行探討。
註入Android系統的使用流程
- 打開一個APP應用,並跳轉到有你想註入的頁面
- 通過
adb shell dumpsys activity top
,獲取當前 Android 系統中與用戶交互(頂層) Activity 的詳細信息 - 反編譯APK文件,根據上一步提供的信息,進行代碼查看,然後定位到想hook的函數,查看的該函數的傳參和返回值
- 編寫js註入代碼,運行腳本註入到函數中
步驟1演示 - 打開APP頁面
自己寫的一個Android Demo,下麵有代碼。
步驟2演示 - 獲取頂層Activity信息
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
步驟3演示 - 查看代碼
這個是自己寫的android代碼,沒有混淆。如果你用反編譯的方式打開別人的代碼,大概率是混淆過的,不過也一樣用,無非是將類名、函數名、變數名變成a、b、c...,只是增加看代碼的難度而已,但是調用流程還是一樣的。
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("得分計次1:60 \n");
returnvalule = addnumber("60");
testview.append("addnumber()的返回值:" + returnvalule);
}
private String addnumber(String nubs){
testview.append("得分計次2:" + nubs + "\n");
return "我是預設的返回值⊙_⊙";
}
}
步驟4演示 - JS配合Python腳本註入
import frida
import sys
jscode = """
/* 這個欄位標記Java虛擬機(例如: Dalvik 或者 ART)是否已載入, 操作Java任何東西的之前,要確認這個值是否為true */
if(Java.available){
/* 111、222、333 打這些log的目的是看流程走到哪裡 */
console.log("111");
/* Java.perform(function(){ ... Javascript代碼成功被附加到目標進程時調用,我們核心的代碼要在裡面寫。是個固定格式 */
Java.perform(function(){
/* Java.use方法用於聲明一個Java類,在用一個Java類之前首先得聲明。比如聲明一個String類,要指定完整的類名var StringClass=Java.use("java.lang.String"); */
var MainActivity = Java.use("com.example.myapplication.MainActivity");
console.log("222");
/* 類.函數.overload(參數類型).implementation = function(形參名稱){ */
MainActivity.addnumber.overload("java.lang.String").implementation = function(nubs){
console.log("333");
/* 給addnumber函數傳參、得到addnumber函數的返回值 */
console.log(this.addnumber("77"));
/* 修改addnumber函數的返回值 */
return "I am Mysticbinary!";
}
});
}
"""
def on_message(message, data):
if message['type'] == 'send':
print(" {0}".format(message['payload']))
else:
print(message)
# 查找USB設備並附加到目標進程
session = frida.get_usb_device().attach('com.example.myapplication')
# 在目標進程里創建腳本
script = session.create_script(jscode)
# 註冊消息回調
script.on('message', on_message)
# 載入創建好的javascript腳本
script.load()
# 讀取系統輸入
sys.stdin.read()
調用說明:
腳本註入說明:
- 打開你想註入的界面
- 運行上面步驟4的代碼
- Android回退到系統主界面,再次進入一次,主要是為了觸發函數
- 查看運行結果
代碼運行結果:
app註入結果:
最後總結一下,難點一是操作步驟多,難點二是要看懂要註入的函數的用法,Frida提供的API介面其實非常簡單使用,最後感謝Frida作者開發出這麼優秀的框架,讓我們小白都能做註入。
參考文章
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