簡介
Frida是一個多平臺的hook框架,功能強大,不僅可以進行常規的Hook工作,還可以完成內存掃描,脫殼等工作,Frida是Python API,但是是JavaScript調試邏輯,核心是用C編寫的,並將Google的V8引擎注入到目標進程中,在這些進程中,JavaScript可以完全訪問內存,掛鉤函數甚至調用進程內的本機函數來執行;
安裝
Frida的安裝非常簡單,但是Python版本可能需要3.5以上:
pip install frida
pip install frida-tools
如果報錯可以下載源碼包安裝:
- 下載frida-tools源碼包。
- 修改setup.py文件,如附圖2所示,將prompt-toolkit的版本要求去掉。
- sudo python setup.py install
查看frida的版本:
然後根據不同平臺去下載相應frida-server,根據CPU找到相應的服務器server:
基本框架
Android:
import frida
import sys
def PrintMessage(message,data):
if(message["type"] == "send"):
print("[*] var {0}".format(message["payload"]))
else:
print(message)
jscode = '''
Java.perform(function(){
});
'''
p = frida.get_usb_device().attach("com.cn.packname")
script = p.create_script(jscode)
script.on('message',PrintMessage)
script.load()
sys.stdin.read()
實例
這裏有一個APP,界面內容我們看看就好,但是居然要積分會員:
查殼:
很明顯,我們需要先脫殼:
脫殼腳本:
#-*- coding:utf-8 -*-
# coding=utf-8
import frida
import sys
def PrintMessage(message,data):
if(message["type"] == "send"):
print("[*] var {0}".format(message['payload']))
else:
print(message)
# 9.0 arm:
arm9 = "_ZN3art13DexFileLoader10OpenCommonEPKhjS2_jRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEEPNS0_12VerifyResultE"
# 7.0 arm:
arm7 = "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"
package = sys.argv[1]
print("dex 導出目錄爲: /data/data/%s"%(package))
device = frida.get_usb_device()
pid = device.spawn(package)
print("pid: %d"%(pid))
session = device.attach(pid)
src = """
Java.perform(function(){
var exports = Module.enumerateExportsSync("libart.so");
for(var i=0;i<exports.length;i++){
if(exports[i].name == "%s"){
var openMemory = new NativePointer(exports[i].address);
}
}
Interceptor.attach(openMemory, {
onEnter: function (args) {
var begin = args[1]
send("magic : " + Memory.readUtf8String(begin))
var address = parseInt(begin,16) + 0x20
var dex_size = Memory.readInt(ptr(address))
send("dex_size :" + dex_size)
var file = new File("/data/data/%s/" + dex_size + ".dex", "wb")
file.write(Memory.readByteArray(begin, dex_size))
file.flush()
file.close()
var send_data = {}
send_data.base = parseInt(begin,16)
send_data.size = dex_size
send(send_data)
},
onLeave: function (retval) {
if (retval.toInt32() > 0) {
}
}
});
});
"""%(arm7,package)
script = session.create_script(src)
script.on("message" , PrintMessage)
script.load()
device.resume(pid)
sys.stdin.read()
然後從/data/data/com.cz.babySister
目錄拿到dex文件進行分析
通過分析我們發現邏輯在5617752.dex
當中:
我們找到購買積分等的支付邏輯,這裏我們可以看到我們可以嘗試通過修改oVar.b()
的返回值,讓他等於9000
,看能否內購:
Java.perform(function(){
var pay = Java.use("com.cz.babySister.alipay.o");
pay.b.implementation = function(){
return "9000";
}
});
查找userinfo之類的類,看看能否修改用戶信息,從而修改積分:
完整Frida腳本:
import frida
import sys
def PrintMessage(message,data):
if(message["type"] == "send"):
print("[*] var {0}".format(message['payload']))
else:
print(message)
jscode = '''
Java.perform(function(){
var pay = Java.use("com.cz.babySister.alipay.o");
pay.b.implementation = function(){
return "9000";
}
var jifen = Java.use("com.cz.babySister.javabean.UserInfo");
jifen.getJifen.implementation = function(){
return "10000";
}
});
'''
p = frida.get_usb_device().attach("com.cz.babySister")
script = p.create_script(jscode)
script.on('message',PrintMessage)
script.load()
sys.stdin.read()
但是很不幸的是,只樣很容易被封號的,封號之後連同android_id
會一起被拉黑,如果是模擬器可以換android_id
,否則只能用腳本:
var sec = Java.use("android.provider.Settings$Secure");
sec.getString.implementation = function(arg1,arg2){
return "5c80b60f12f73207";
}
其他腳本
Linux:
函數調用
"""
var add = new NativeFunction(ptr("%s"), 'void', ['int','int']);
add(0,1);
"""%(int(sys.argv[1],16))
函數返回值替換:
"""
//var st = Memory.allocUtf8String(" I love you!");
Interceptor.attach(ptr("%s"), {
onEnter: function(args) {
send("args[0]: " + args[0].toInt32());
send("args[1]: " + args[1].toInt32());
},
onLeave: function(retval){
send(retval.toInt32());
retval.replace("7777");
}
});
"""%(int(sys.argv[1],16))
Android:
so層string替換:
"""
Java.perform(function(){
Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridasostring_fridaSoString_FridaSo"),{
onEnter: function(args) {
send("Hook start");
send("args[2]=" + args[2]);
},
onLeave: function(retval){
send("return:"+retval);
var env = Java.vm.getEnv();
var jstrings = env.newStringUtf("tamper");
retval.replace(jstrings);
}
});
});
"""
so層普通替換:
"""
Java.perform(function(){
Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridaso_FridaSoDefine_FridaSo"),{
onEnter: function(args) {
send("Hook start");
send("args[2]=" + args[2]);
send("args[3]=" + args[3]);
},
onLeave: function(retval){
send("return:"+retval);
retval.replace(1234);
}
});
});
"""
導入導出表:
Java.perform(function(){
var imports = Module.enumerateImportsSync(""libhello.so"");
for(var i = 0; i < imports.length; i++) {
if(imports[i].name == 'strncat'){
send(imports[i].name + "": "" + imports[i].address);
break;
}
}
var exports = Module.enumerateExportsSync(""libhello.so"");
for(var i = 0; i < exports.length; i++) {
if(exports[i].name.indexOf('add') != -1){
send(exports[i].name + "": "" + exports[i].address);
break;
}
}
for(var i = 0; i < imports.length; i++) {
send(imports[i].name + "": "" + imports[i].address);
}
var exports = Module.enumerateExportsSync(""libhello.so"");
for(var i = 0; i < exports.length; i++) {
send(exports[i].name + "": "" + exports[i].address);
}
});