1、为什么使用frida的Python库
1、之前学习的frida更多是用于手工调试阶段,如果要用代码自动化处理,还需要其他语言接入,比如Python
2、 后续学习的frida算法转发方案和frida的rpc也需要用到Python,算法转发和rpc能给逆向带来无比便捷的体验
3、frida可以实时与python进行数据交互,可以把数据发送给python,等待python处理完后,接收返回值,frida再接着往下执行代码。Python提供的各种库,让代码编写更为简单
2、API学习
2.1、包名附加
import frida, sys
jsCode = """ ...... """
process = frida.get_usb_device().attach('进程名')#老版本frida用包名,新版本frida用进程名
script = process.create_script(jsCode)
script.load()
sys.stdin.read()
我这里以嘟嘟牛在线为例
# -*- coding: UTF-8 -*-
import frida,sys
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);
console.log(retval);
return retval;
}
});
"""
process = frida.get_usb_device().attach('嘟嘟牛在线')
script=process.create_script(jscode);
script.load();
sys.stdin.read();
在pycharm的控制台中已经打印出数据了
2.2、PID附加
先通过ps -A|grep dodo,获取PID
由于有的应用是双进程,这个时候使用包名附加就会冲突,因此可以使用PID附加
process = frida.get_usb_device().attach(11518)
script=process.create_script(jscode);
script.load();
sys.stdin.read();
很简单,只需要把attach里的包名/应用名改为PID进行了
2.3、spawn方式附加
# -*- coding: UTF-8 -*-
import frida,sys
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);
console.log(retval);
return retval;
}
});
"""
#get_remote_device
device = frida.get_usb_device()
print("device:",device);
pid = device.spawn(["com.dodonew.online"])#以挂起的方式创建进程
print("pid:",pid);
process = device.attach(pid);
print("process:",process);
script = process.create_script(jscode)
script.load()
device.resume(pid)#加载完脚本,恢复进程运行
sys.stdin.read()
先创建device,然后通过device.spawn挂起进程,通过device.attach(pid)附加到进程,加载脚本后通过resume恢复进程运行
2.4、连接非标准端口
先查看手机的ip
然后frida-server用指定端口的方法启动
process = frida.get_device_manager().add_remote_device('192.168.0.104:8888').attach('嘟嘟牛在线');
script = process.create_script(jscode);
script.load()
print("开始hook")
sys.stdin.read()
2.5、frida与Python的交互(send)
我们用frida去做hook的时候,js代码中通过console.log打印出想要的值,但是这个值无法交给python继续使用,因此需要用到send函数来将值传递给python中,具体操作如下:
# -*- coding: UTF-8 -*-
import frida,sys
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);
return retval;
}
});
"""
def messageFunc(message, data):
print(message)
if message["type"] == 'send':
print(u"[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_usb_device().attach('嘟嘟牛在线')
script = process.create_script(jscode)
script.load()
script.on('message', messageFunc)
print("开始hook")
sys.stdin.read()
我们通过script.on(‘message’, messageFunc)接受js里发来的消息,类似监听,然后messageFunc是回调函数,对message进行处理
我们打印出message,格式是key+value,通过判断key为send,打印出payload
2.6、frida与Python的交互(recv)
# -*- coding: UTF-8 -*-
import time
import frida,sys
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);
recv(
function(obj){
console.log(obj.data)
retval=obj.data
}
).wait();
return retval;
}
});
"""
def messageFunc(message, data):
print(message)
if message["type"] == 'send':
print(u"[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_usb_device().attach('嘟嘟牛在线')
script = process.create_script(jscode)
script.load()
script.on('message', messageFunc)
time.sleep(10)
script.post({'data': '12345678123456781234567812345678'})
print("开始hook")
sys.stdin.read()
我们在接收到message时,通过messageFunc处理,打印出了现在的md5加密后的结果,然后通过time.sleep(10)暂停10秒后再发送数据给js,发送的值为12345678123456781234567812345678。js中通过recv函数接受python发来的数据并作处理。
recv(
function(obj){
console.log(obj.data)
retval=obj.data
}
).wait();
recv的参数是一个回调函数,里面先打印了传来的数据,然后又把这个数据赋值给retval
2.7、frida的rpc
# -*- coding: UTF-8 -*-
import time
import frida,sys
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);
console.log('retval: ', retval);
return retval;
}
});
function test(data) {
return new Promise(function(resolve, reject) {
Java.perform(function() {
var result = Java.use('com.dodonew.online.util.Utils').md5(data);
console.log('result1: ', result);
resolve(result);
});
}).then(function(result) {
console.log('result2: ', result);
return result;
});
}
rpc.exports = {
rpcfunc: test
};
"""
device = frida.get_usb_device()
print("device:",device);
pid = device.spawn(["com.dodonew.online"])#以挂起的方式创建进程
print("pid:",pid);
process = device.attach(pid);
print("process:",process);
script = process.create_script(jscode)
script.load()
device.resume(pid)#加载完脚本,恢复进程运行
result = script.exports_sync.rpcfunc('equtype=ANDROID&loginImei=Androidnull&timeStamp=1626790668522&userPwd=a12345678&username=15968079477&key=sdlkjsdljf0j2fsjk')
print("开始hook")
print("result3:"+result)
sys.stdin.read()
通过rpc.exports导出函数,然后通过script.exports_sync.rpcfunc调用函数。这里要注意个问题,我们在js中定义函数test时,原本的代码如下:
function test(data){
var result = "";
Java.perform(function(){
result = Java.use('com.dodonew.online.util.Utils').md5(data);
console.log('result1: ', result);
});
console.log('result2: ', result);
return result;
}
这里有个问题,test被执行时,并不是顺序执行的, Java.perform会异步操作,因此,在 Java.perform()调用之后,代码会立即执行 console.log(‘result2: ‘, result),然后执行 return result;。这意味着 Java.use(‘com.dodonew.online.util.Utils’).md5(data)的执行尚未完成,因此 result 变量的值仍然为空字符串,而不是预期的 MD5 值。利用chatgpt对代码进行了修改后就可以正常打印了
2.8、frida算法转发
import requests, json
import frida
jsCode = """
function hookTest(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;
}
rpc.exports = {
rpcfunc: hookTest
};
"""
# 调用frida脚本
process = frida.get_device_manager().add_remote_device('192.168.1.106:8888').attach("嘟嘟牛在线")
script = process.create_script(jsCode)
print('[*] Running Whitebird')
script.load()
cipherText = script.exports.rpcfunc('18075042220', 'a12345678')
print(cipherText)
其实就是调用app中的加密算法
2.9、将python作为服务端转发
我们上面的测试代码都是用python写的,有的时候我们可能想用其他语言去实现,但是其他语言不一定有frida的rpc库,所以我们可以把python作为中间层,将python开启一个服务,这样就可以让别的语言访问,类似于函数的封装,然后提供一个接口。
pip3 install fastapi
pip3 install uvicorn
代码如下
import requests, json
import frida
import uvicorn
import fastapi
jsCode = """
function hookTest(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;
}
rpc.exports = {
rpcfunc: hookTest
};
"""
# 调用frida脚本
process = frida.get_device_manager().add_remote_device('192.168.1.106:8888').attach("嘟嘟牛在线")
script = process.create_script(jsCode)
print('[*] Running Whitebird')
script.load()
cipherText = script.exports.rpcfunc('18075042220', 'a12345678')
print(cipherText)
app = fastapi.FastAPI()
@app.get("/get")
async def getEchoApi(item_id, item_user, item_pass):
result = script.exports.rpcfunc(item_user, item_pass)
return {"item_id": item_id, "item_retval": result}
if __name__ == '__main__':
uvicorn.run(app, port = 8080)
构造访问的url:http://127.0.0.1:8080/get?item_id=100&item_user=18075042220&item_pass=a12345678
现在我们可以用其他语言直接发起get请求,传入对应的参数就可以得到加密后的结果
我们还可以写一个post请求
from curses import A_ALTCHARSET
import requests, json
import frida
import uvicorn
import fastapi
from pydantic import BaseModel
jsCode = """
function hookTest(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;
}
rpc.exports = {
rpcfunc: hookTest
};
"""
# 调用frida脚本
process = frida.get_device_manager().add_remote_device('192.168.1.106:8888').attach("嘟嘟牛在线")
script = process.create_script(jsCode)
print('[*] Running Whitebird')
script.load()
cipherText = script.exports.rpcfunc('18075042220', 'a12345678')
print(cipherText)
class Item(BaseModel):
item_id: str = None
item_user: str = None
item_pass: str = None
app = fastapi.FastAPI()
@app.post("/post")
async def getEchoApi(postData: Item):
result = script.exports.rpcfunc(postData.item_user, postData.item_pass)
return {"item_id": postData.item_id, "item_retval": result}
if __name__ == '__main__':
uvicorn.run(app, port = 8080)
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 767778848@qq.com