Frida是一个多平台的hook框架,功能强大,不仅可以进行常规的Hook工作,还可以完成内存扫描,脱壳等工作,Frida是Python API,但是是JavaScript调试逻辑,核心是用C编写的,并将Google的V8引擎注入到目标进程中,在这些进程中,JavaScript可以完全访问内存,挂钩函数甚至调用进程内的本机函数来执行;
Frida的安装非常简单,但是Python版本可能需要3.5以上:
pip install frida
pip install frida-tools
如果报错可以下载源码包安装:
查看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 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);
}
});
HOOK Linux API:
var pro_addr = Module.findBaseAddress('re');
var show_addr = Number(pro_addr) + Number(0x6f0);
var exports = Module.enumerateExportsSync("/lib/x86_64-linux-gnu/libc.so.6");
for(var i=0;i<exports.length;i++){
if(exports[i].name == "printf"){
var printf_addr = exports[i].address;
send("name: "+exports[i].name+" address: "+exports[i].address);
}
}
send("pro_addr: " + pro_addr);
send("show_addr: 0x" + show_addr.toString(16));
send("printf: " + printf_addr);
var st1 = Memory.allocUtf8String("I_Love_You %s!");
var st2 = Memory.allocUtf8String("sir");
Interceptor.attach(ptr(printf_addr),{
onEnter: function(args) {
args[0] = ptr(st1);
args[1] = ptr(st2);
}
});