准备:
jadx_gui: apk反编译,获取apk包名、查看apk源码。
frida:hook框架: 用来植入js脚本,修改源程序实现拦截数据,获取想要的数据。
adb: 电脑操作真机或模拟器。
夜神模拟器: 用来测试。
frida-server: 用来实现通信,这里可以理解为服务端,用adb发送到手机修改权限并运行,注意要和本地python安装的frida版本一致,下载的版本也有区别,有些是不能使用的。
- -pip install frida: 这里的frida是安装在本地的,可以理解为客户端
- -pip install frida-tools: 一些工具
Demo:
import frida
import sys
import os
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
function classExists(className) {
var exists = false;
try {
var clz = Java.use(className);
exists = true;
} catch (err) {
}
return exists;
};
function checkLoadDex(className, dexfile) {
if (!classExists(className)) {
Java.openClassFile(dexfile).load();
}
};
function toJSONString(obj) {
try {
checkLoadDex("com.alibaba.fastjson.JSON", "/data/local/tmp/fastjson.dex");
var clz = Java.use("com.alibaba.fastjson.JSON");
var toJSONStringMehtod = clz.toJSONString.overload("java.lang.Object");
return toJSONStringMehtod.call(clz, obj);
} catch (err) {
}
return "{}";
};
function fromJSONString(jsonStr, classObj) {
try {
checkLoadDex("com.alibaba.fastjson.JSON", "/data/local/tmp/fastjson.dex");
var jsonObject = Java.use("com.alibaba.fastjson.JSONObject");
var obj = jsonObject.parseObject(jsonStr, classObj.class);
return obj;
} catch (err) {
}
return null;
};
function newHashSet() {
var HashSetClz = Java.use('java.util.HashSet');
return HashSetClz.$new();
};
var Base64 = {
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function(e) {
var t = "";
var n, r, i, s, o, u, a;
var f = 0;
e = Base64._utf8_encode(e);
while (f < e.length) {
n = e.charCodeAt(f++);
r = e.charCodeAt(f++);
i = e.charCodeAt(f++);
s = n >> 2;
o = (n & 3) << 4 | r >> 4;
u = (r & 15) << 2 | i >> 6;
a = i & 63;
if (isNaN(r)) {
u = a = 64;
} else if (isNaN(i)) {
a = 64;
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a);
}
return t;
},
decode: function(e) {
var t = "";
var n, r, i;
var s, o, u, a;
var f = 0;
e = e.replace(/[^A-Za-z0-9+/=]/g, "");
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++));
o = this._keyStr.indexOf(e.charAt(f++));
u = this._keyStr.indexOf(e.charAt(f++));
a = this._keyStr.indexOf(e.charAt(f++));
n = s << 2 | o >> 4;
r = (o & 15) << 4 | u >> 2;
i = (u & 3) << 6 | a;
t = t + String.fromCharCode(n);
if (u != 64) {
t = t + String.fromCharCode(r);
}
if (a != 64) {
t = t + String.fromCharCode(i);
}
}
t = Base64._utf8_decode(t);
return t;
},
_utf8_encode: function(e) {
e = e.replace(/rn/g, "n");
var t = "";
for (var n = 0; n < e.length; n++) {
var r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192);
t += String.fromCharCode(r & 63 | 128);
} else {
t += String.fromCharCode(r >> 12 | 224);
t += String.fromCharCode(r >> 6 & 63 | 128);
t += String.fromCharCode(r & 63 | 128);
}
}
return t;
},
_utf8_decode: function(e) {
var t = "";
var n = 0;
var r = 0;
var c1;
var c2 = 0;
while (n < e.length) {
r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
n++;
} else if (r > 191 && r < 224) {
c2 = e.charCodeAt(n + 1);
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
n += 2;
} else {
c2 = e.charCodeAt(n + 1);
c1 = e.charCodeAt(n + 2);
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c1 & 63);
n += 3;
}
}
return t;
}
};
rpc.exports = {
hooker: function (param1) {
Java.perform(function () {
console.log(param1);
/* 参数
传参:
long int64
重写:
普通参数
int int
long long
byte [B
boolean boolean
float float
容器类
String java.lang.String
HashMap java.util.HashMap
ArrayList java.util.ArrayList
HashSet java.util.HashSet
LinkedList java.util.LinkedList
eg:
var hashMap_clz = Java.use('java.util.HashMap');
var map_clz = Java.use('java.util.Map');
var hashMap = hashMap_clz.$new();
var map = Java.cast(hashMap, map_clz);
自定义类
com.tencent.mm.protocal.protobuf.cqt
com.tencent.mm.an.q
*/
// hook代码实现数据截取、篡改
//普通方法hook
var main = Java.use("com.example.pt_fun.MainActivity");
main.add.implementation = function (a, b) {
console.log("Hook start ...");
send(a);
send(b);
send("Success");
return this.add("2", "2");
};
//构造方法hook
var money = Java.use("com.example.pt_fun.Money");
money.$init.implementation = function (a, b) {
console.log("Hook start ...");
send(a);
send(b);
send("Success");
return this.$init(2000, "美元");
};
//重载方法hook
var money = Java.use("com.example.pt_fun.Money");
money.getInfo.overload("int").implementation = function (a) {
console.log("Hook start ...");
send(a);
send("Success");
//return this.getInfo(88888);
return "重载方法hook成功"
};
//对象参数
var main = Java.use("com.example.pt_fun.MainActivity");
var money = Java.use("com.example.pt_fun.Money");
main.mobj.implementation = function (obj) {
console.log("Hook start ...");
//send(obj);
send(obj.getInfo());
//获取属性值(一)
send(obj.type.value);
//修改属性
obj.type.value = "英镑";
var m = money.$new(1000, "台币");
send(m.getInfo());
return this.mobj(m);
};
//修改属性(映射)
var main = Java.use("com.example.pt_fun.MainActivity");
var money = Java.use("com.example.pt_fun.Money");
var clazz = Java.use("java.lang.Class");
main.mobj.implementation = function (obj) {
var countid = Java.cast(obj.getClass(), clazz).getDeclaredField("count");
countid.setAccessible(true);
var value = countid.get(obj);
console.log(value);
countid.setInt(obj, 11111);
return this.mobj(obj);
};
// 注册类,即实现接口
var ab_clz = Java.use('com.example.pt_fun.interface');
var cust_ab_clz = Java.registerClass({
name: 'com.example.pt_fun.interface.lllx1',
implements: [ab_clz],
methods: {
add(a, b) {
return a+b;
}
}
});
cust_ab_clz.$new().add(1, 2);
//枚举所有的类和方法
Java.enumerateLoadedClasses({
onMatch: function (name, handle) {
if (name.indexOf("com.tencent.mm") != -1) {
console.log("name: ", name);
var clazz = Java.use(name);
console.log("clazz: ", clazz);
var methods = clazz.class.getDeclaredMethods();
for (var i = 0; i < methods.length; i++) {
console.log("methods: ", methods[i]);
}
Thread.sleep(0.1);
}
},
onComplete: function () {
console.log("okok");
}
});
});
}
};
"""
# 端口转发
os.system("adb forward tcp:27042 tcp:27042")
# 连接并获取运行的apk包名,即进程
process = frida.get_remote_device().attach('com.example.pt_fun')
# 植入脚本,拦截数据
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
# 控制台打印信息
sys.stdin.read()
测试apk:
点击下载
自己去首页找找包名叫PT_fun。