FRIDA RPC
简介
- 无需做具体的算法还原,利用App来当成加密机器,传入对应的参数返回加密结果
- 适用于加密无法还原或者不好还原的时候采用此方法
- 跟Python爬虫结合来用的
基本姿势
获取设备:
Device(id="FA6970303786", name="Pixel", type='usb')
- 获取USB设备:
frida.get_usb_device()
参数可以设置超时时间 一般可以不用给参数 - 获取远程设备:
frida.get_remote_device()
- 获取USB设备:
注入:
Session(pid=应用PID)
- 包名注入:
frida.get_usb_device().attach("包名")
- PID注入:
frida.get_usb_device().attach(PID)
双进程应用就用PID注入
- 包名注入:
- Spwan:
PID =frida.get_usb_device().spwan(["包名"])
以挂起的方式创建进程 返回应用PID FRIDA与Python交互
- send:在JS代码中发送代码给Python,与
console
的区别在于,console
仅仅只是输出,send
可以把代码发送给Python
再次处理 - recv:在JS代码里阻塞接收Python发来的数据
recv(function(obj){csole.log(obj.data)}).wait();
- script.post():
script.post({"data": "value"})
在Python
代码里发送数据到JS
配合recv
使用
- send:在JS代码中发送代码给Python,与
- Rpc:主动调用 传入对应参数获取返回值
rpc.exports = {rpcfunc:xx}
开放让外部访问的接口script.exports.rpcfunc()
Python调用阻塞控制台防止程序结束:
input("已开启服务监听 输入任意数字回车退出程序 >> ")
sys.stdin.read()
PS:在py文件里修改JS代码需要重新注入,python里没有自动注入功能
注入
# -*- coding: UTF-8 -*-
import frida, sys
with codecs.open('./hook.js', 'r', 'utf-8') as f:
source = f.read()
process = frida.get_usb_device().attach('com.dodonew.online') # 包名注入
process = frida.get_usb_device().attach(9999) # PID注入
script = process.create_script(source )
script.load()
print("开始运行")
sys.stdin.read()
Spwan
# -*- coding: UTF-8 -*-
import frida, sys
with codecs.open('./hook.js', 'r', 'utf-8') as f:
source = f.read()
device = frida.get_usb_device()
print("device: ", device)
pid = device.spawn(["com.dodonew.online"]) # 以挂起方式创建进程 得到应用PID
print("pid: ", pid)
process = device.attach(pid) # 注入进程
print("process: ", process)
script = process.create_script(source ) # 创建脚本
script.load() # 加载脚本
device.resume(pid) # 加载完脚本, 恢复进程运行
print("开始运行")
sys.stdin.read()
非标准端口及多设备连接
非标准端口
# -*- coding: UTF-8 -*- import frida, sys with codecs.open('./hook.js', 'r', 'utf-8') as f: source = f.read() process = frida.get_device_manager().add_remote_device('192.168.x.xx:8888').attach('包名') script = process.create_script(source) script.load() print("开始运行") sys.stdin.read()
多设备
# -*- coding: UTF-8 -*- import frida, sys with codecs.open('./hook.js', 'r', 'utf-8') as f: source = f.read() process1 = frida.get_device_manager().add_remote_device('192.168.x.xx1:8888').attach('包名') process2 = frida.get_device_manager().add_remote_device('192.168.x.xx2:8888').attach('包名') script1 = process.create_script(source) script2 = process.create_script(source) script1.load() script2.load() print("开始运行") sys.stdin.read()
send 打印输出
send
是在python
层定义的on_message
回调函数,jscode
内所有的信息都被监控script.on('message', on_message)
,当输出信息的时候on_message
函数会拿到其数据再通过format
转换, 其最重要的功能也是最核心的是能够直接将数据以json
格式输出,当然数据是二进制的时候也依然是可以使用send
,十分方便,我们来看代码示例以及执行效果。- 可以看出这里两种方式输出的不同的效果,
console
直接输出了[object Object]
,无法输出其正常的内容,因为jni_env
实际上是一个对象,但是使用send
的时候会自动将对象转json
格式输出。通过对比,我们就知道send
的好处啦~ - 与
console
的区别在于,console
仅仅只是输出,send
可以把代码发送给Python再次处理
# -*- coding: utf-8 -*- import frida import sys def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) jscode = """ Java.perform(function () { var jni_env = Java.vm.getEnv(); console.log(jni_env); send(jni_env); }); """ process = frida.get_usb_device().attach('com.roysue.roysueapplication') script = process.create_script(jscode) script.on('message', on_message) # 注册事件 参数1:message固定值 script.load() sys.stdin.read() 运行脚本效果如下: roysue@ubuntu:~/Desktop/Chap09$ python Chap03.py [object Object] [*] {'handle': '0xdf4f8000', 'vm': {}}
- 可以看出这里两种方式输出的不同的效果,
recv
- 等待
Python
返回结果:recv(function(obj){csole.log(obj.data)}).wait();
Python
传递数据给 JS:script.post({'data': 100}
# -*- coding: UTF-8 -*-
import frida, sys
import time
jsCode = """
Java.perform(function(){
var RequestUtil = Java.use('com.dodonew.online.http.RequestUtil');
RequestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c){
console.log('data: ', a);
console.log('desKey: ', b);
console.log('desIV: ', c);
var retval = this.encodeDesMap(a, b, c);
console.log('retval: ', retval);
return retval;
}
var Utils = Java.use('com.dodonew.online.util.Utils');
Utils.md5.implementation = function(a){
console.log('MD5 string: ', a);
var retval = this.md5(a);
send(retval); // 发送数据到Python
recv(function(obj){ // 接收返回数据
console.log(JSON.stringify(obj));
console.log("Python:", obj.data);
retval = obj.data;
}).wait();
return retval;
}
});
"""
def messageFunc(message, data):
print(message)
if message["type"] == 'send':
print(u"[*] {0}".format(message['payload']))
time.sleep(10)
script.post({"data": "0e8315152843b943563031945032e957"}) # python 传递数据到js
else:
print(message)
process = frida.get_usb_device().attach('com.dodonew.online')
script = process.create_script(jsCode)
script.on('message', messageFunc)
script.load()
print("开始运行")
sys.stdin.read()
Rpc 调用
主动调用 传入对应参数获取返回值
rpc.exports = {rpcfunc:xx}
在JS代码里需要开放让外部访问的接口script.exports.rpcfunc()
Python调用
PS:如果导出函数为 rpcFunc
那么调用就要加下划线 script.exports.rpc_func
# -*- coding: UTF-8 -*-
import codecs, frida
jscode = """
function call_dodonew(data) {
var retval = ""
Java.perform(function () {
// 主动调用
retval = Java.use('com.dodonew.online.util.Utils').md5(data);
})
return retval
}
// 接口导出
rpc.exports = {
call: call_dodonew // 导出名(可自定义):导出方法
}
"""
device = frida.get_usb_device()
PID = device.spawn(["com.dodonew.online"])
process = device.attach(PID)
script = process.create_script(jscode)
script.load()
device.resume(PID)
# 传入参数 调用接口
result = script.exports.call("equtype=ANDROID&loginImei=Androidnull&timeStamp=1626790668522&userPwd=a12345678&username=15968079477&key=sdlkjsdljf0j2fsjk")
print("[+] result :=> ", result)
input("已开启服务监听 输入任意数字回车退出程序 >> ")
Python Rpc Spider
- Python 爬虫
RPC
用法
import requests, json
import frida
jsCode = """
function getParams(username, passward){
var result;
Java.perform(function(){
var time = new Date().getTime();
var signData = 'equtype=ANDROID&loginImei=Android352689082129358&timeStamp=' +
time + '&userPwd=' + passward + '&username=' + username + '&key=sdlkjsdljf0j2fsjk';
var Utils = Java.use('com.dodonew.online.util.Utils');
var sign = Utils.md5(signData).toUpperCase();
// console.log('sign: ', sign);
var encryptData = '{"equtype":"ANDROID","loginImei":"Android352689082129358","sign":"'+
sign +'","timeStamp":"'+ time +'","userPwd":"' + passward + '","username":"' + username + '"}';
var RequestUtil = Java.use('com.dodonew.online.http.RequestUtil');
var Encrypt = RequestUtil.encodeDesMap(encryptData, '65102933', '32028092');
// console.log('Encrypt: ', Encrypt);
result = Encrypt;
});
return result;
}
function deCipher(CipherText) {
// 响应解密
var retval = ""
Java.perform(function () {
// 主动调用
var desKey = "65102933"
var desIv = "32028092"
retval = Java.use('com.dodonew.online.http.RequestUtil').decodeDesJson(CipherText, desKey, desIv)
})
return retval
}
rpc.exports = {
getparams: getParams,
decipher: deCipher
};
"""
process = frida.get_usb_device().attach("com.dodonew.online")
script = process.create_script(jsCode)
print('[*] Running ...')
script.load()
# Rpc getParams
cipherText = script.exports.getParams('16688889999', 'a12345678')
url = 'http://api.dodovip.com/api/user/login'
data = json.dumps({"Encrypt": cipherText})
r = requests.post(url=url, data=data, headers={
"content-type": "application/json; charset=utf-8",
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 10; Pixel Build/QP1A.191005.007.A3)"
})
print("[*] 请求参数 :=> " + data)
print("[*] 响应 :=> " + r.text)
print("[*] 响应解密 :=> " + script.exports.deCipher(r.text))
输出结果如下:
[*] Running ...
[*] 请求参数 :=> {"Encrypt": "NIszaqFPos1vd0pFqKlB42Np5itPxaNH//FDsRnlBfgL4lcVxjXii02ggCULFNaejvFwr3Wqwkal\nNRmYlAjU4jhnxbz9qIVFiYpm44XB2z9x8NGrTBjlh5Qgamn8ersP7XCKbLrLJuo5/eUS0yYua7Az\nIN51JHzz6atnjN6SfD9T5QHa14rrvyzd3EL9qll5kjLIrtpnM1NCkjM5dVFrKW8gmECUIV+37R7+\n7l3q0oA=\n"}
[*] 响应 :=> 2v+DC2gq7RuAC8PE5GZz5wH3/y9ZVcWhFwhDY9L19g9iEd075+Q7xwewvfIN0g0ec/NaaF43/S0=
[*] 响应解密 :=> {"code":-1,"message":"账号或密码错误","data":{}}
RCP FASTAPI 本地服务部署
安装库
pip install fastapi
pip install uvicorn
import codecs
import frida
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn
jsCode = """
function getParams(username, passward) {
var result;
Java.perform(function () {
var time = new Date().getTime();
var signData = 'equtype=ANDROID&loginImei=Android352689082129358&timeStamp=' +
time + '&userPwd=' + passward + '&username=' + username + '&key=sdlkjsdljf0j2fsjk';
var Utils = Java.use('com.dodonew.online.util.Utils');
var sign = Utils.md5(signData).toUpperCase();
// console.log('sign: ', sign);
var encryptData = '{"equtype":"ANDROID","loginImei":"Android352689082129358","sign":"' +
sign + '","timeStamp":"' + time + '","userPwd":"' + passward + '","username":"' + username + '"}';
var RequestUtil = Java.use('com.dodonew.online.http.RequestUtil');
var Encrypt = RequestUtil.encodeDesMap(encryptData, '65102933', '32028092');
// console.log('Encrypt: ', Encrypt);
result = Encrypt;
});
return result;
}
function deCipher(CipherText) {
// 响应解密
var retval = ""
Java.perform(function () {
// 主动调用
var desKey = "65102933"
var desIv = "32028092"
retval = Java.use('com.dodonew.online.http.RequestUtil').decodeDesJson(CipherText, desKey, desIv)
})
return retval
}
rpc.exports = {
getparams: getParams,
decipher: deCipher
};
"""
process = frida.get_usb_device().attach("com.dodonew.online")
script = process.create_script(jsCode)
script.load()
app = FastAPI() # 初始化
@app.get("/getParams") # get请求触发
async def getEchoApi(user, pwd):
# http://127.0.0.1:8080/getParams?user=16688889999&pwd=a12345678
result = script.exports.getParams(user, pwd)
return {"Encrypt": result}
# 声明参数模型
class Item(BaseModel):
data: str
@app.post("/deCipher")
async def getEchoApi(item: Item):
result = script.exports.decipher(item.data)
return result
if __name__ == '__main__':
uvicorn.run(app, port=8080) # 本地监听端口
Fiddler数据转发
什么是fiddler数据转发
- 使用fiddler拦截数据包,把数据包发送到指定服务器
- 优点:更加万能,不需要逆向,只需抓包找到相应数据链接即可
- 缺点:这是个半自动的爬取数据的方案
什么是fiddler script
- 想要实现fiddler数据转发,就需要使用fiddler script,这是fiddler功能的扩展,用代码去控制fiddler