Android APP渗透测试(1)-Frida

简介

Frida是一个多平台的hook框架,功能强大,不仅可以进行常规的Hook工作,还可以完成内存扫描,脱壳等工作,Frida是Python API,但是是JavaScript调试逻辑,核心是用C编写的,并将Google的V8引擎注入到目标进程中,在这些进程中,JavaScript可以完全访问内存,挂钩函数甚至调用进程内的本机函数来执行;

安装

Frida的安装非常简单,但是Python版本可能需要3.5以上:

pip install frida
pip install frida-tools

如果报错可以下载源码包安装:

  1. 下载frida-tools源码包。
  2. 修改setup.py文件,将prompt-toolkit的版本要求去掉。
  3. sudo python setup.py install

查看frida的版本:
version
然后根据不同平台去下载相应frida-server,根据CPU找到相应的服务器server:
frida_sever

基本框架

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文件进行分析
pwd
通过分析我们发现逻辑在5617752.dex当中:
支付
我们找到购买积分等的支付逻辑,这里我们可以看到我们可以尝试通过修改oVar.b()的返回值,让他等于9000,看能否内购:

Java.perform(function(){
	var pay = Java.use("com.cz.babySister.alipay.o");
	pay.b.implementation = function(){
		return "9000";
	}
});

查找userinfo之类的类,看看能否修改用户信息,从而修改积分:
userinfo
完整Frida脚本:

import frida
import sys

def PrintMessage(message,data):
    if(message["type"] == "send"):
        print("[*] var {0}".format(message['payload']))
    else:
        print(message)

'''
def PrintMessage(message, data):
    if message['type'] == 'send':
        try:
           print(json.dumps(json.loads(message['payload'].encode('utf8')), sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False))
        except:
            print("[*] {0}".format(message['payload']))
 
 
    elif message['type'] == 'error':
        for i in message:
            if i == "type":
                print("[*] %s" % "error:")
                continue
            if type(message[i]) is str:
                print("[*] %s" %
                      i + ":\n    {0}".format(message[i].replace('\t', '    ')))
            else:
                print("[*] %s" %
                      i + ":\n    {0}".format(message[i]))
    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()

vip
但是很不幸的是,只样很容易被封号的,封号之后连同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);
   }
});

无线连接时附加:

# server
frida-server -l 192.168.56.101
host = '192.168.2.101'
manager = frida.get_device_manager()
remote_device = manager.add_remote_device(host)
p = remote_device.attach("re")
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Frida是个轻量级so级别的hook框架,它可以帮助逆向人员对指定的进程的so模块进行分析。它主要提供了功能简单的python接口和功能丰富的js接口,使得hook函数和修改so编程化,值得一提的是接口中包含了主控端与目标进程的交互接口,由此我们可以即时获取信息并随时进行修改。使用frida可以获取进程的信息(模块列表,线程列表,库导出函数),可以拦截指定函数和调用指定函数,可以注入代码,总而言之,使用frida我们可以对进程模块进行手术刀式剖析。 它主要的工作方式是将脚本库注入到目标进程,在目标进程执行脚本。这里需要注意的是,它是将脚本库注入到已经启动的进程,但并不是说,对于进程初始化所做的动作,frida无能为力,frida提供了一个接口spawn,可以启动并暂时挂起进程,然后待我们布置好hook代码后再恢复进程运行,但是这个时间很短,大概2秒,也可能是我的使用姿势不对,求大佬指正。 此外,frida提供了相关的文档,但是frida官网提供的关于python接口的文档实在是少的可怜,连工具命令行的参数都没有,这点需要下载frida的python接口的源代码自己去分析了。值得高兴的一点是,Frida官网提供的js接口的文档稍微详细一些,并附有一些可喜的例子。 除了用于脚本编程的接口外,frida还提供了一些简单的工具,比如查看进程列表,追踪某个库函数等。 剩下就是关于frda学习路线了,Frida的学习还是蛮简单的,只需要了解两方面的内容: 1)主控端和目标进程的交互(message) 2)Python接口和js接口(查文档)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值