前言
Frida是一款基于python + javascript 的hook框架,通殺android\ios\linux\win\osx等各平臺陶衅,由于是基于腳本的交互匀伏,因此相比xposed和substrace cydia更加便捷昂拂,本文重點介紹Frida在android下面的使用碟绑。Frida的官網(wǎng)為:http://www.frida.re/
安裝和搭建Frida環(huán)境
首先要保證你的android手機已經(jīng)root。通過pip安裝frida:
pip install frida
下載frida-server:
frida_server的下載地址:https://github.com/frida/frida/releases
到android手機上并且運行
adb push frida-server /data/local/tmp/
adb shell
su
cd /data/local/tmp/
chmod 777 frida-server
./frida-server
轉發(fā)android TCP端口到本地:
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
測試frida環(huán)境碘举,如果出現(xiàn)android手機的進程列表說明搭建成功:
frida-ps -R
PID Name
2700 acceleratord
2713 adbd
2798 agnsscontrol
2799 agnsslog
2195 akmd09911
8078 android.process.acore
31283 android.process.media
2185 atcmdserver
4939 chargelogcat
2796 chr_logd
22856 com.android.browser
7912 com.android.contacts
22417 com.android.gallery3d
....
得到android手機當前最前端Activity所在的進程
其中get_front_app.py的內容如下:
import frida
rdev = frida.get_remote_device()
front_app = rdev.get_frontmost_application()
print front_app
枚舉android手機所有的進程
enum_process.py內容如下:
import frida
rdev = frida.get_remote_device()
processes = rdev.enumerate_processes()
for process in processes:
print process
枚舉某個進程加載的所有模塊以及模塊中的導出函數(shù)
import frida
rdev = frida.get_remote_device()
session = rdev.attach("com.tencent.mm") #如果存在兩個一樣的進程名可以采用rdev.attach(pid)的方式
modules = session.enumerate_modules()
for module in modules:
print module
export_funcs = module.enumerate_exports()
print "\tfunc_name\tRVA"
for export_func in export_funcs:
print "\t%s\t%s"%(export_func.name,hex(export_func.relative_address))
hook android的native函數(shù)
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("com.tencent.mm")
scr = """
Interceptor.attach(Module.findExportByName("libc.so" , "open"), {
onEnter: function(args) {
send("open("+Memory.readCString(args[0])+","+args[1]+")");
},
onLeave:function(retval){
}
});
"""
script = session.create_script(scr)
def on_message(message ,data):
print message
script.on("message" , on_message)
script.load()
sys.stdin.read()
hook android的java層函數(shù)
如下代碼為hook微信(測試版本為6.3.13忘瓦,不同版本由于混淆名字的隨機生成的原因或者代碼改動導致類名不一樣)
com.tencent.mm.sdk.platformtools.ay類的隨機數(shù)生成函數(shù),讓微信猜拳隨機(type=2)殴俱,而搖色子總是為6點(type=5)
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("com.tencent.mm")
scr = """
Java.perform(function () {
var ay = Java.use("com.tencent.mm.sdk.platformtools.ay");
ay.pu.implementation = function(){
var type = arguments[0];
send("type="+type);
if (type == 2)
{
return this.pu(type);
}
else
{
return 5;
}
};
});
"""
script = session.create_script(scr)
def on_message(message ,data):
print message
script.on("message" , on_message)
script.load()
sys.stdin.read()
通過frida向android進程注入dex
import frida, sys, optparse, re
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
var context = currentApplication.getApplicationContext();
var pkgName = context.getPackageName();
var dexPath = "%s";
var entryClass = "%s";
Java.openClassFile(dexPath).load();
console.log("inject " + dexPath +" to " + pkgName + " successfully!")
Java.use(entryClass).%s("%s");
console.log("call entry successfully!")
});
"""
def checkRequiredArguments(opts, parser):
missing_options = []
for option in parser.option_list:
if re.match(r'^\[REQUIRED\]', option.help) and eval('opts.' + option.dest) == None:
missing_options.extend(option._long_opts)
if len(missing_options) > 0:
parser.error('Missing REQUIRED parameters: ' + str(missing_options))
if __name__ == "__main__":
usage = "usage: python %prog [options] arg\n\n" \
"example: python %prog -p com.android.launcher " \
"-f /data/local/tmp/test.apk " \
"-e com.parker.test.DexMain/main " \
"\"hello fridex!\""
parser = optparse.OptionParser(usage)
parser.add_option("-p", "--package", dest="pkg", type="string",
help="[REQUIRED]package name of the app to be injected.")
parser.add_option("-f", "--file", dest="dexPath", type="string",
help="[REQUIRED]path of the dex")
parser.add_option("-e", "--entry", dest="entry", type="string",
help="[REQUIRED]the entry function Name.")
(options, args) = parser.parse_args()
checkRequiredArguments(options, parser)
if len(args) == 0:
arg = ""
else:
arg = args[0]
pkgName = options.pkg
dexPath = options.dexPath
entry = options.entry.split("/")
if len(entry) > 1:
entryClass = entry[0]
entryFunction = entry[1]
else:
entryClass = entry[0]
entryFunction = "main"
process = frida.get_usb_device(1).attach(pkgName)
jscode = jscode%(dexPath, entryClass, entryFunction, arg)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running fridex')
script.load()
sys.stdin.read()
通過注入拋出異常代碼實現(xiàn)跟蹤程序調用棧
在<<Android 軟件安全與逆向分析>>這本書中第八章有介紹通過重打包寫入異常代碼進行棧跟蹤政冻,但是這樣比較麻煩枚抵,使用frida注入更方便。
frida的相關資源
https://github.com/dweinstein/awesome-frida
http://jaq.alibaba.com/community/art/show?articleid=816
https://koz.io/using-frida-on-android-without-root/
http://www.ninoishere.com/frida-learn-by-example/
https://github.com/TheCjw/Frida-Android-Scripts