Frida用法之函数操作
程序员文章站
2022-03-21 17:25:08
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注入结果:
参考文章
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
上一篇: Fragment的介绍以及加载详细说明