文章目录
- 安装frida
- 安装python3.7
- 设置环境变量
- 安装pycharm和nodejs
- 使用frida
- 将frida-server push到手机设备中
- 端口转发
- 安装apk
- 使用jadx查看java代码
- 运行frida-server
- frida源码阅读
- frida hook方法
- Frida Java层hoook
- JavaHook.java
- JavaHook.js
- Frida native层hook 一
- NativeHook.js
安装frida
Frida框架简介
Frida是一款基于Python + JavaScript的Hook与调试框架。
Firda是一款易用的跨平Hook工具,Java层到Native层的Hook无所不能,是一种动态
的插桩工具,可以插入代码到原生 App 的内存空间中,动态的去监视和修改行为,
原生平台包括Win、Mac、Linux、Android、iOS全平台。
环境配置步骤:
1.安装Python环境 3.7
2.安装frida模块
打开Py输入命令:
pip install frida
pip install frida-tools
pip uninstall frida
pip uninstall frida-tools
pip install frida==15.1.27
pip install frida-tools==10.6.2
google pixels 3a
frida=16.1.3
frida-tools=12.2.1
3.frida-server下载地址:
查看版本信息:frida --version
https://github.com/frida/frida/releases
下载安装frida server 版本和类型对应,框架和设备对应
4.安装PyCharm
5.启动frida-server
6.端口转发
adb forward tcp:27042 tcp:27042
7.测试
frida-ps -U
frida -U -f com.qianyu.fridaapp --no-pause
安装python3.7
设置环境变量
安装pycharm和nodejs
创建工程的时候,记得将鼠标所在的两个勾选框选上
使用frida
将frida-server push到手机设备中
端口转发
安装apk
使用jadx查看java代码
运行frida-server
我使用15.1.27版本的frida版本,在安卓手机上会报错。所以我果断升级了frida版本
sargo:/data/local/tmp # ./frida-server-15.1.27-android-arm64
{"type":"error",
"description":"Error: Unable to determine ClassLinker field offsets",
"stack":"Error: Unable to determine ClassLinker field offsets\n
at Ye (frida/node_modules/frida-java-bridge/lib/android.js:400:1)\n
at frida/node_modules/frida-java-bridge/lib/memoize.js:4:1\n
at ze (frida/node_modules/frida-java-bridge/lib/android.js:193:1)\n
at Oe (frida/node_modules/frida-java-bridge/lib/android.js:16:1)\n
at _tryInitialize (frida/node_modules/frida-java-bridge/index.js:29:1)\n
at new _ (frida/node_modules/frida-java-bridge/index.js:21:1)\n
at Object.4../lib/android (frida/node_modules/frida-java-bridge/index.js:332:1)\n
at o (frida/node_modules/browser-pack/_prelude.js:1:1)\n
at frida/node_modules/browser-pack/_prelude.js:1:1\n
at Object.22.frida-java-bridge (frida/runtime/java.js:1:1)",
"fileName":"frida/node_modules/frida-java-bridge/lib/android.js","lineNumber":400,"columnNumber":1}
frida源码阅读
frida hook方法
注意点:
- frida和frida-server版本号必须要一致
- hook的时候,必须要运行hook的程序,否则报错
Frida Java层hoook
JavaHook.java
# -*- coding: utf-8 -*-
import os
import sys
import frida
import codecs
def message(message, data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('NDKDemo')
if not os.path.isfile('./JavaHook.js'):
raise TypeError("./JavaHook.js does not exist")
with codecs.open('./JavaHook.js', 'r', 'UTF-8') as file:
js_code = file.read()
script = process.create_script(js_code)
script.on("message", message)
script.load()
# script.exports.test()
# script.exports.test()
# script.exports.test()
# script.exports.test()
sys.stdin.read()
# frida使用非标准端口
# /data/local/tmp # ./fs_12.7.22_arm64 -l 127.0.0.1:31928 默认端口: 27046
# process = frida.get_device_manager().add_remote_device('127.0.0.1:31928').attach('FridaApp')
# if not os.path.isfile('./JavaHook.js'):
# raise TypeError("./JavaHook.js does not exist")
# with codecs.open('./JavaHook.js', 'r', 'UTF-8') as file:
# js_code = file.read()
# script = process.create_script(js_code)
# script.on("message", message)
# script.load()
# sys.stdin.read()
JavaHook.js
// HOOK普通方法、静态方法
function Test01(){
let loginActivity = Java.use('com.yijincc.ndkdemo.LoginActivity');
loginActivity.login.implementation = function () {
console.log("=============login===============");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log(arguments[0]);
console.log(arguments[1]);
this.login(arguments[0],arguments[1]);
}
}
// HOOK构造方法、重载方法
function Test02(){
let intent = Java.use('android.content.Intent');
intent.$init.overload('android.content.Context', 'java.lang.Class').implementation = function () {
console.log("=============intent===============");
console.log(arguments[0]);
console.log(arguments[1]);
this.$init(arguments[0],arguments[1]);
}
}
// HOOK内部类
function Test03(){
let loginActivity$1 = Java.use('com.yijincc.ndkdemo.LoginActivity$1');
loginActivity$1.onClick.implementation = function () {
console.log("=============onClick===============");
console.log(arguments[0]);
this.onClick(arguments[0]);
}
}
// 主动调用构造方法
function Test04(){
let money = Java.use('com.yijincc.fridaapp.Money');
let obj = money.$new(1000,'RMB');
console.log(obj.getInfo());
}
// 操作对象里面的成员变量
function Test05(){
let money = Java.use('com.yijincc.fridaapp.Money');
let obj = money.$new(10000,'RMB');
console.log(obj.name.value);
console.log(obj.num.value);
console.log('===============================');
obj.name.value = 'RMB';
obj.num.value = 10000000;
console.log(obj.name.value);
console.log(obj.num.value);
}
// 主动调用普通方法
function Test06(){
let money = Java.use('com.yijincc.fridaapp.Money');
let obj = money.$new(2000,'RMB');
console.log(obj.getInfo());
}
// 获取当前类已有的实例实现主动调用普通方法
function Test07(){
Java.choose('com.yijincc.ndkdemo.MainActivity',{
onMatch: function(obj){ // 枚举时调用
console.log(obj);
console.log(obj.name.value);
console.log(obj.age.value);
console.log(obj.sex.value);
console.log(obj.rand('B',99))
}, onComplete: function(){ // 枚举完成后调用
console.log("end");
}
});
}
// 主动调用静态方法
function Test08(){
let mainActivity = Java.use('com.yijincc.ndkdemo.MainActivity');
console.log(mainActivity.isRel(444,444));
}
// HOOK打印堆栈信息
// function Test09(){
// let money = Java.use('com.yijincc.fridaapp.Money');
// money.getInfo.implementation = function () {
// console.log("=============getInfo===============");
// console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
// return this.getInfo();
// }
// }
// HOOK指定类的所有方法
function Test10(){
let money = Java.use('com.yijincc.ndkdemo.MainActivity');
let methods = money.class.getDeclaredMethods();
for(let j = 0; j < methods.length; j++){
let methodName = methods[j].getName();
console.log(methodName);
for(let k = 0; k < money[methodName].overloads.length; k++){
money[methodName].overloads[k].implementation = function(){
console.log('==========='+methodName+'===========');
for(let i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
console.log('===========end===========');
return this[methodName].apply(this, arguments);
}
}
}
}
// 枚举已加载的所有类与枚举类的所有方法
function Test11(){
let classes = Java.enumerateLoadedClassesSync();
for(let i = 0; i < classes.length; i++){
if(classes[i].indexOf("com.") !== -1){
console.log("clazz:"+classes[i]);
let clazz = Java.use(classes[i]);
let methods = clazz.class.getDeclaredMethods();
for(let j = 0; j < methods.length; j++){
console.log("method:"+methods[j]);
}
}
}
}
// hook动态加载dex文件
function Test12(){
// Java.enumerateLoadedClasses({
// onMatch: function (name, handle) {
// if (name.indexOf("com.example") >= 0) {
// console.log(name);
// let class6 = Java.use(name);
// class6.check.implementation = function () {
// console.log("check:", this);
// return true;
// };
// }
// }, onComplete: function () {}
// });
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")) {
console.log(loader);
Java.classFactory.loader = loader; //切换classloader
}
} catch (error) {}
}, onComplete: function () {}
});
let DynamicCheck = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");
console.log(DynamicCheck);
DynamicCheck.check.implementation = function () {
console.log("DynamicCheck.check");
return true;
}
}
// 动态加载dex文件
function Test13(){
// jar -cvf dex.jar com/example/androiddemo/StringUtils.class
// dx --dex --output=dex.dex dex.jar
let dex= Java.openClassFile("/data/local/tmp/dex.dex");
dex.load();
let stringUtils = Java.use("com.example.androiddemo.StringUtils");
console.log(stringUtils.tohexString("1234567890"));
}
rpc.exports = {
test:function () {
Java.choose('com.yijincc.ndkdemo.MainActivity',{
onMatch: function(obj){ // 枚举时调用
console.log(obj);
console.log(obj.name.value);
console.log(obj.age.value);
console.log(obj.sex.value);
console.log(obj.rand('B',99))
}, onComplete: function(){ // 枚举完成后调用
console.log("============end============");
}
});
}
}
Java.perform(function () {
Test01();
// Test02(); // 重载方法
// Test03(); // 内部类
// Test04() // 主动调用构造方法,创建一个对象
// Test07();
// Test08();
// Test10(); // 打印所有的方法
// Test11();
//Test13();
});
Frida native层hook 一
通过IDA找到要hook的native函数(静态)
base64魔改
## NativeHook.py
# -*- coding: utf-8 -*-
import os
import sys
import frida
import codecs
def message(message, data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('NDKDemo')
if not os.path.isfile('./NativeHook.js'):
raise TypeError("./NativeHook.js does not exist")
with codecs.open('./NativeHook.js', 'r', 'UTF-8') as file:
js_code = file.read()
script = process.create_script(js_code)
script.on("message", message)
script.load()
sys.stdin.read()
# frida使用非标准端口
# /data/local/tmp # ./fs_12.7.22_arm64 -l 127.0.0.1:31928 默认端口: 27046
# process = frida.get_device_manager().add_remote_device('127.0.0.1:31928').attach('Android_crackme')
# if not os.path.isfile('./NativeHook.js'):
# raise TypeError("./NativeHook.js does not exist")
# with codecs.open('./NativeHook.js', 'r', 'UTF-8') as file:
# js_code = file.read()
# script = process.create_script(js_code) # 创建脚本
# script.on("message", message)
# script.load()
# sys.stdin.read()
NativeHook.js
// HOOK导出函数
function Test01(){
// 用于在指定的模块中查找导出函数的地址
let funGetFlag = Module.findExportByName("libnative-lib.so", "Java_com_yijincc_ndkdemo_LoginActivity_login");
send("native: " + funGetFlag); // 基地址
Interceptor.attach(funGetFlag, {
onEnter: function(args){
send("============getFlag===============");
send(args[0]);
send(args[1]);
},
onLeave: function(retval){
send("============result===============");
send(retval);
// 获取JNIEnv*
let env = Java.vm.tryGetEnv();
// 将jstring 转换 const char*
let str=env.getStringUtfChars(retval,0);
send(str.readCString());
}
});
}
// HOOK未导出函数
function Test02(){
// 绝对地址=so模块起始地址(基地址)+偏移地址
let baseAddr = Module.findBaseAddress("libnative-lib.so");
send("baseAddr:"+baseAddr);
// 指令集 分为ARM指令、thumb指令
// ARM指令地址不变 thumb指令地址+1 sub_ 开头的函数 这种函数只能使用这种方式来进行
Interceptor.attach(baseAddr.add(0x15F08), {
onEnter: function(args){
send("============encrypt===============");
send(args[0]);
send(args[1]);
send(args[2]);
console.log(hexdump(args[2], {
offset: 0,
length: 16,
header: true,
ansi: false
}));
// 获取JNIEnv*
let env = Java.vm.tryGetEnv();
// 将jstring 转换 const char*
let str=env.getStringUtfChars(args[2],0);
send(str.readCString());
},
onLeave: function(retval){
send("============result===============");
send(retval);
// 获取JNIEnv*
let env = Java.vm.tryGetEnv();
// 将jstring 转换 const char*
let str=env.getStringUtfChars(retval,0);
send(str.readCString());
}
});
}
// HOOK枚举导入函数信息
function Test03(){
send("Test03")
let imports = Module.enumerateImportsSync("libnative-lib.so");
send(imports)
for(let i=0;i<imports.length;i++){
// if(imports[i].name.indexOf('raise') !== -1){
send(imports[i]);
// }
}
}
// HOOK枚举导出函数信息
function Test04(){
send("Test04-start")
send(device)
let exports = Module.enumerateExportsSync("libnative-lib.so");
send(exports)
for(let i=0;i<exports.length;i++){
if(exports[i].name.indexOf('Java_') !== -1){
send("name:"+exports[i].name+" address:"+exports[i].address);
}
}
send("Test04-end")
}
// 遍历模块列表信息
function Test05(){
Process.enumerateModules({
onMatch: function(exp){
send(exp)
if(exp.name.indexOf('libnative-lib.so') !== -1){
send('enumerateModules find');
send(exp);
return 'stop';
}
},
onComplete: function(){
send('enumerateModules stop');
}
});
}
// 读写内存数据
function Test06(){
let mem_addr=Memory.alloc(20);
//Memory.writeInt(mem_addr,0x1234567890abcdef);
Memory.writeLong(mem_addr,0x1234567890abcdef);
// console.log(hexdump(mem_addr));
console.log(hexdump(mem_addr, {
offset: 0,
length: 20,
header: true,
ansi: true
}));
}
// 使用frida api读写文件
function Test07(){
let file = new File("/data/data/com.yijincc.ndkdemo/yijincc.txt", "w");
file.write("hello world!!!\\n");
file.flush();
file.close();
}
// 基于主动调用libc.so里面的函数实现文件的读写操作
function Test08(){
let addr_fopen = Module.findExportByName("libc.so", "fopen");
send("1")
let addr_fputs = Module.findExportByName("libc.so", "fputs");
send("2")
let addr_fclose = Module.findExportByName("libc.so", "fclose");
send("3")
let fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]);
send("4")
let fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);
send("5")
let fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);
send("6")
let filename = Memory.allocUtf8String("/data/data/com.yijincc.ndkdemo/yijincc.txt");
send("7")
let open_mode = Memory.allocUtf8String("w");
send("8")
let file = fopen(filename, open_mode);
send("9")
let buffer = Memory.allocUtf8String("hello world!!!\\n");
send("10")
let result = fputs(buffer, file);
send("11")
send("fputs:" + result);
fclose(file);
}
Java.perform(function () {
// Test01();
// Test02();
// Test03();
// Test04();
// Test05();
// Test06();
// Test07();
Test08();
})