APP渗透抓不到包/安卓逆向--Frida
Frida的基本使用
目录
android检测代理
应用程序检测到使用代理后就会抓包异常
IS_ICS_OR_LATER
相关文章:
https://www.it610.com/article/1280480273930141696.htm
解决方法:
使用VPN原理的软件进行抓包:
黄鸟 postaman charles
证书校验
单向认证:
特征:抓包可以抓到,但是报错,400、ssl
解决方法:Xposed+JustTrustMe
双向认证(rsa算法):
特征:略
解决方法:证书+密码
Frida入门
小技巧:
这串代码可以在notepad++中直接运行python脚本,x.py指的是要执行的py路径
cmd /k python "c://x.py" & ECHO & PAUSE & EXIT
frida-ps -U 可以查看apk的包名称
更加推荐使用如下语句,可以自动获取手机最上层app的包名:
adb shell dumpsys window | grep mCurrentFocus
首先安装frida模块(python3.9)
PC端:
方案一(推荐安装低版本frida,兼容性好):
pip install frida==14.2.18
pip install frida-tools==9.2.5
pip install objection==1.8.4
如果在这一步一直卡着安装不成功就试试方案二
方案二:
到https://pypi.org/project/frida/#files下载frida-12.7.16-py3.7-win-amd64.egg。
并把它放到E:\Program Files\Python37\Lib\site-packages中。
此时执行pip install frida就OK了
测试:
打开powershell,
输入frida-ps -U
,此时无报错说明成功
设备端:frida-server 14.2.18
下载地址:https://github.com/frida/frida/releases
判断模拟器手机的架构
大部分为两种情况:
手机端下载 frida-server-版本-android-arm64.xz
模拟器下载 frida-server-版本-android-x86.xz
adb -s 设备名 shell getprop ro.product.cpu.abi //我的是32位x86
下载对应版本并解压:
连接模拟器
我这里使用的是逍遥模拟器,21503是逍遥模拟器的默认端口
(夜神:adb connect 127.0.0.1:62001)
其他adb命令补充
adb kill-server
adb start-server
adb devices 查看设备列表
adb install f:/1.apk #表示安装apk文件
adb -s 设备名 shell getprop ro.product.cpu.abi # 查看cpu架构,用于判断适合安装哪个版本的frida
adb connect 127.0.0.1:21503
adb shell
adb push 文件路径 /data/local/tmp
cd /data/local/tmp/
ls
chmod 777 frida-server-15.1.22-android-x86 //赋予frida执行权限
./frida-server-15.1.22-android-x86 //运行frida,此时frida就已经跑起来了
最后转发一下端口:
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
注意:
如果开了两个模拟器则需要匹配使用哪一个模拟器:
adb devices #列出模拟器列表
adb -s 模拟器名 shell
问题集合
byte类型hook
当遇到byte格式数据(类似于下图),需要hook时
解决方法
var string=Java.use('java.lang.String');
string.$new(arguments[0]);
例子:
hexdump输出16进制
参考实战hook陌陌so层【43分43秒】
模板
Frida模板【普通方法】
补充:
查看软件包名方法(其他方法):
adb shell pm list packages -3
adb shell am monitor //需要先运行此命令,再运行需要查看包名的APP
更推荐如下方法获取手机最顶层app包名
adb shell dumpsys window | grep mCurrentFocus
import frida,sys
jscode="""
Java.perform(function(){
var utils = Java.use('包名.类名')
utils.函数.implementation = function(函数值1,函数值2){ //如果是构造方法这里还能utils.$init.inplementation
console.log("Hook Start...");
send("Success!");
return this.函数(函数值1,函数值2); //同理,这里就写return this.$init(函数1,函数2)
}
});
"""
def message(message,data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('app包名')
script=process.create_script(jscode)
script.on("message",message)
script.load()
sys.stdin.read()
Frida模板【构造方法】
构造方法指的是new一个对象
import frida,sys
jscode="""
Java.perform(function(){
var utils = Java.use('包名.类名')
utils.$init.implementation = function(函数值1,函数值2){
console.log("Hook Start...");
send("Success!");
return this.$init(函数值1,函数值2);
}
});
"""
def message(message,data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('app包名')
script=process.create_script(jscode)
script.on("message",message)
script.load()
sys.stdin.read()
Frida模板【重载方法】
add(1,2)
add(1,2,3)
重载就是函数名字一样,例如:
import frida,sys
jscode="""
Java.perform(function(){
var utils = Java.use('包名.类名')
utils.函数.overload("类型").implementation = function(函数值1,函数值2){ //类型指的是int,java.lang.String等
console.log("Hook Start...");
send("Success!");
return this.函数(函数值1,函数值2);
}
});
"""
def message(message,data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('app包名')
script=process.create_script(jscode)
script.on("message",message)
script.load()
sys.stdin.read()
Frida模板【构造对象参数】
需要hook的代码段如下:
import frida,sys
jscode="""
Java.perform(function(){
var utils = Java.use('包名.类名')
utils.函数.overload("com.xiaojianbang.app.Money").implementation = function(obj){ //因为这里的类型为对象,所以写上对象的路径
console.log("Hook Start...");
send("Success!");
send(obj.getInfo())
var mon = mon.$new(1000,'港币') //自己构造一个对象
return mon.getInfo()
}
});
"""
def message(message,data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('app包名')
script=process.create_script(jscode)
script.on("message",message)
script.load()
sys.stdin.read()
推荐技术文章!:
https://bbs.pediy.com/thread-272270.htm
进阶补充
安装应用
adb install -r E:/a.apk
知识拓展
启动方式
frida也有其他的启动方式,在我上文中我是直接使用了python来启动frida,实质上frida提供了更多的启动方式:
attack模式,程序运行时进行hook
frida -UF -l frida.js
spwan模式,重启APP进行hook
frida -U -f com.apple.ExampleCode
objection用法
可以不写代码,轻松hook某个方法的调用栈以及对应参数,可用于hook前的分析:
进入命令行
objection -g 程序包名 explore
监听对应方法的参数、调用栈、返回值
android hooking watch class_method 类名.方法名 --dump-args --dump-backtrace --dump-return
监听对应类下的所有方法,运用场景例如点击一个按钮,知道哪些方法被触发了
android hooking watch class 类名
搜索出完整的包名或类名,然后配合watch进行监听一个类
android hooking search classes/methods 类名/方法名
关闭监听方法调用
jobs list
jobs kill ID
利用objection 加载wallbreaker(https://github.com/hluwa/Wallbreaker)插件,实现获取程序内存中的类或者对象:
plugin load FILE_PATH
plugin wallbreaker classsearch CLASSNAME
plugin wallbreaker classdump --fullname com.test.CLASSNAME //获取具体的class代码
plugin wallbreaker objectsearch OBJECTNAME // 例如有一个new Student(),需要被创建了才能搜索到,所以这里搜索的是内存中的对象
plugin wallbreaker objectdump 0x00 //search到的地址填在这里
利用objection直接调用某程序类,进入命令行后输入以下命令即可执行到目标类:
例如直接进入第一关,第二关等等。
android intent launch_activity com.example.MainActivity
frida快速定位hook点
adb shell dumpsys activity top|findstr ACTIVITY
firda复杂类型的打印输出
下载相关工具包:
连接待补充:
1、将r0gson.dex放入frida同级目录
2、firda代码定义如下
function main(){
Java.
firda主动调用某方法
// 实现firda主动对某方法进行调用
function invoke() {
Java.perform(function () {
// 第一种主动调用,从内存中找到实例对象,再进行调用
Java.choose('com.dist.TestClass', {
onMatch: function (instance) {
instance['Value'].value = "可以调用一个变量用于修改该值(可以是非静态变量)。第二种方式,直接用jadx复制变量为frida片段。"
instance['_Value'].value = "如果属性名和方法名相同,需要加下划线在前方"
instance.runtest();
console.log("invoke success!")
}, oncomplete: function () {
console.log("search completed!")
}
})
// 第二种主动调用 创建一个新的实例对象,再进行调用
// 这里顺便再扩展如何构建一个复杂的参数,拿runtest(Student stu)举例,即传递的参数不是常规的string类型
let test_ins = Java.use("com.dist.TestClass").$new()
let stu_ins = Java.use("com.test.Student").$new()
let arr_ins = Java.use("java.util.ArrayList").$new()
let age_ins = Java.use("java.lang.Integer").$new(10)
arr_ins.add("123")
stu_ins.setFriends(arr_ins)
stu_ins.setStuName("st1")
stu_ins.setAge(age_ins)
test_ins.runtest(stu_ins);
console.log(test_ins.getTest())
study
// 实现打印复杂类型
function main() {
Java.perform(function () {
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
Java.use("xxx.xxx.xxx")['test'].implementation = function (param) {
console.log(gson.$new().toJSON(param))
return this.test(param)
}
})
}
// 实现firda主动对某方法进行调用
function invoke() {
Java.perform(function () {
// 第一种主动调用,从内存中找到实例对象,再进行调用
Java.choose('com.dist.TestClass', {
onMatch: function (instance) {
instance['Value'].value = "可以调用一个变量用于修改该值(可以是非静态变量)。第二种方式,直接用jadx复制变量为frida片段。"
instance['_Value'].value = "如果属性名和方法名相同,需要加下划线在前方"
instance.runtest();
console.log("invoke success!")
}, oncomplete: function () {
console.log("search completed!")
}
})
// 第二种主动调用 创建一个新的实例对象,再进行调用
// 这里顺便再扩展如何构建一个复杂的参数,拿runtest(Student stu)举例,即传递的参数不是常规的string类型
let test_ins = Java.use("com.dist.TestClass").$new()
let stu_ins = Java.use("com.test.Student").$new()
let arr_ins = Java.use("java.util.ArrayList").$new()
let age_ins = Java.use("java.lang.Integer").$new(10)
arr_ins.add("123")
stu_ins.setFriends(arr_ins)
stu_ins.setStuName("st1")
stu_ins.setAge(age_ins)
test_ins.runtest(stu_ins);
console.log(test_ins.getTest())
// 使用工具来构造复杂参数
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
let gson_ins = Java.use("com.r0ysue.gson.Gson").$new()
let json_tmp = {stuName : 'st1', stuAge : 10, friends : [123, 456],map : null}
let stu_ins2 = gson_ins.fromJson(JSON.stringify(json_tmp),java.use("com.test.Student").class)
let test_ins2 = Java.use("com.dist.TestClass").$new()
test_ins2.runtest(stu_ins2)
})
// 场景: 目标方法不在当前classloader中,在其他classloader,所以无法直接hook,需要切换到其他classloader才能捕获到函数
function meiju(){
Java.perform(function(){
Java.enumerateClassLoaders({
onMatch:function(loader){
console.log('classLoader' + loader);
try{
if(loader.findClass("com.DynamicCheck")){
console.log("this class is true!"+loader);
Java.classFactory.loader=loader;
}
}catch (error){
console.log(error);
}
},oncomplete:function(){
console.log("enum success!");
}
})
console.log("此时的classloader已经修改为了新的classloader,所以能够寻找到DynamicCheck")
let DynamicCheck = Java.use("com.xxx.DynamicCheck")
DynamicCheck["check"].implementation = function(){
let result = this["check"]();
result = true;
return result;
}
})
}
}
frida打印其他对象
var Bundle = Java.use('com.target.class');
if (needprintdata !== null) {
var realFinderObject = Java.cast(needprintdata, Bundle);
var finderObjectClass = realFinderObject.getClass();
var fields = finderObjectClass.getDeclaredFields();
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
var fieldName = field.getName();
var fieldValue = field.get(realFinderObject);
console.log("FinderObject " + fieldName + ": " + fieldValue);
}
} else {
console.log("c object is null");
}
frida打印list
var list = Java.cast(b, Java.use("java.util.List"));
var size = list.size();
console.log(size)
var arrayList = [];
for (var i = 0; i < size; i++) {
arrayList.push(list.get(i).toString());
}
// 输出数组
console.log("List elements: ", arrayList);
frida打印Map
var keySet = map.keySet().toArray();
// 遍历键集合,并打印对应的键值对
for (var i = 0; i < keySet.length; i++) {
var key = keySet[i].toString();
var value = map.get(key);
if (value !== null) {
value = value.toString();
} else {
value = "null";
}
console.log("\nmap key: " + key + ", value: " + value);
frida打印byte[]
实现将[0,0,0],这类对象输出成正常的字符串
var byteArray = Java.array('byte', bArr)
var str = '';
for (var i = 0; i < byteArray.length; i++) {
// 获取字节值并转换为无符号整数
var byteValue = byteArray[i] & 0xff;
// 将无符号整数转换为字符,并添加到字符串中
str += String.fromCharCode(byteValue);
}
// 输出转换后的字符串
console.log("encrycptDataStr", str);
另外将字符串恢复为byte[]如下:
var byteArrayValues = new Array(data.length).fill(0);
var byteArray2 = Java.array('byte', byteArrayValues);
// 遍历字符串中的每个字符,将其转换为对应的字节,并放入字节数组中
for (var j = 0; j < data.length; j++) {
byteArray2[j] = data.charCodeAt(j);
}
console.log("修改后转换为byte:", byteArray2)
frida Didn’t find class
unable to find process with name ‘xxx’
import frida
import sys
def main():
# 连接设备
try:
rdev = frida.get_remote_device()
print("[*] 成功连接到设备")
except Exception as e:
print(f"[!] 连接设备失败:{e}")
sys.exit(1)
# 枚举应用程序
applications = rdev.enumerate_applications()
print("[*] 枚举的应用程序:")
for app in applications:
print(f" - {app.name} ({app.identifier})")
# 检查目标应用是否运行
target_app = None
for app in applications:
if app.identifier == "com.xxx.xxx":
target_app = app
break
if not target_app:
print("[!] 目标应用未找到或未运行")
sys.exit(1)
print(f"[*] 发现目标应用:{target_app.name} (PID: {target_app.pid})")
# 附加到应用进程
try:
session = rdev.attach(target_app.pid)
print("[*] 成功附加到应用进程")
except Exception as e:
print(f"[!] 附加应用进程失败:{e}")
sys.exit(1)
# 加载 Hook 脚本
try:
with open("hook.js", "r") as f:
src = f.read()
script = session.create_script(src)
print("[*] 加载 Hook 脚本")
# 消息处理函数
def on_message(message, data):
if message["type"] == "send":
print(f"[*] {message['payload']}")
elif message["type"] == "error":
print(f"[!] Error: {message['stack']}")
else:
print(message, data)
script.on("message", on_message)
script.load()
except Exception as e:
print(f"[!] 加载脚本失败:{e}")
sys.exit(1)
# 阻塞等待输入
print("[*] 按 Ctrl+C 退出")
sys.stdin.read()
if __name__ == "__main__":
main()
更多推荐
所有评论(0)