篇幅有限
完整内容及源码关注公众号:ReverseCode,发送 冲
动静态函数主动调用
静态函数 use
function static() {
Java.perform(function () {//只要是java的代码都要跑在Java.perform里面
Java.use("com.example.junior.util.Utils").dip2px.implementation = function (context, float) {
//return null;
var result = this.dip2px(context, 100)
console.log("context,float,result ==> ", context, float, result);
// 打印log
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return 26;
}
Java.use('com.xx.app.ShufferMap').show.implementation = function (map) {
console.log(JSON.stringify(map));
var hm = Java.use('java.util.HashMap').$new();
hm.put("user","dajianbang");
hm.put("pass","87654321");
hm.put("code","123456");
return this.show(hm);
}
var myBytes = StringClass.$new("Hello World").getBytes();
var base64Class = Java.use("android.util.Base64");
var result = Java.use("com.xiaojianbang.app.RSA").encrypt(myBytes);
console.log("result is :", result);
console.log(JSON.stringify(result));
console.log("base64 result is :", base64Class.encodeToString(result,0));
})
}
动态函数 choose
function dynamic(){
Java.perform(function(){
// hook 函数
Java.choose("com.example.junior.CalculatorActivity",{
onMatch:function(instance){
console.log("found instance =>",instance);
console.log("instance showText is =>",instance.clear("666"))
console.log("instance showText is =>",instance.showText.value)
},onComplete:function(){
console.log('Search complete')
}
})
// hook 对象
Java.choose("com.xiaojianbang.app.Money",{
onMatch : function(instance){
console.log("find it!!", instance.getInfo());
},
onComplete: function(){
console.log("compelete!!!");
}
})
})
}
动静态变量修改
静态变量 use
function staticField(){
Java.perform(function(){
var divscale = Java.use("com.example.junior.util.Arith").DEF_DIV_SCALE.value;
console.log("divscale1 is =>",divscale);
Java.use("com.example.junior.util.Arith").DEF_DIV_SCALE.value=20;
divscale = Java.use("com.example.junior.util.Arith").DEF_DIV_SCALE.value;
console.log("divscale2 is =>",divscale);
})
}
动态变量 choose
function dynamicField(){
Java.perform(function(){
Java.choose("com.example.junior.CalculatorActivity",{
onMatch:function(instance){
console.log("found instance =>",instance);
console.log("instance showText is =>",instance.showText.value)
instance.showText.value = "123"
},onComplete:function(){
console.log('Search complete')
}
})
})
}
有参构造主动调用
// Utils.test(new Money(200,"美元"))
Java.perform(function () {
var money = Java.use('com.xx.app.Money') // 如果要自定义实例化就要获取并重写
var utils = Java.use('com.xx.app.Utils');
utils.test.overload("com.xx.app.Money").implementation = function (obj) {
var myBytes = StringClass.$new("Hello World").getBytes();
// 重新实例化 $new()
var mon = money.$new(999,'我的天')
return mon.getInfo(); // 根据需求return
}
});
Hook构造函数
// We need to replace .$init() instead of .$new(), since .$new() = .alloc() + .init()
Java.perform(function () {
var money = Java.use('com.xx.app.Money');
money.$init.implementation = function (a, b) {
console.log("构造函数Hook中...", a, b);
return this.$init(a, b);
}
// hook 构造方法 $init
var MoneyClass = Java.use("com.kevin.app.Money");
MoneyClass.$init.overload().implementation = function () {
console.log("hook Money $init");
this.$init();
}
var StringClass = Java.use("java.lang.String");
var MoneyClass = Java.use("com.xiaojianbang.app.Money");
MoneyClass.$init.overload('java.lang.String', 'int').implementation = function (x, y) {
console.log('hook Money init');
var myX = StringClass.new("Hello World!");
var myY = 9999;
this.$init(myX, myY);
}
});
枚举类所有方法
//Hook类的所有方法
Java.perform(function(){
var md5 = Java.use("com.xxx.xxx.xxx");
var methods = md5.class.getDeclaredMethods();
for(var j = 0; j < methods.length; j++){
var methodName = methods[j].getName();
console.log(methodName);
}
});
function main() {
Java.perform(function () {
// return String[] class name
var classList = Java.enumerateLoadedClassesSync();
for (var i = 0; i < classList.length; i++) {
var targetClass = classList[i];
if (targetClass.indexOf("com.xiaojianbang.app.Money") != -1) {
console.log("hook the class: ", targetClass);
var TargetClass = Java.use(targetClass);
// 利用反射获取类中的所有方法
var methodsList = TargetClass.class.getDeclaredMethods();
for (var k = 0; k < methodsList.length; k++) {
console.log(methodsList[k].getName());
// hook methodName 这个类的所有方法(难点在于每个方法的参数是不同的)
for (var k = 0; k < TargetClass[methodName].overloads.length; k++) {
TargetClass[methodName].overloads[k].implementation = function () {
// 这是 hook 逻辑
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
return this[methodName].apply(this, arguments); // 重新调用
}
}
}
}
}
})
}
function main(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch: function(name,handle){
if (name.indexOf("com.xiaojianbang.app.Money") != -1){
console.log(name,handle);
// 利用反射 获取类中的所有方法
var TargetClass = Java.use(name);
// return Method Object List
var methodsList = TargetClass.class.getDeclaredMethods();
for (var i = 0; i < methodsList.length; i++){
// Method Objection getName()
console.log(methodsList[i].getName());
}
}
},
onComplete: function(){
console.log("complete!!!")
}
})
})
}
特殊不可见字符
当方法名被混淆时֏,打印出来所有的类名%D6%8F
,用编码后的字符串hook
Java.perform(
function x() {
var targetClass = "com.example.hooktest.MainActivity";
var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();
for (var i in methods) {
console.log(methods[i].toString());
console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")));
}
hookCls[decodeURIComponent("%D6%8F")].implementation = function (x) {
console.log("original call: fun(" + x + ")");
var result = this[decodeURIComponent("%D6%8F")](900);
return result;
}
}
)
wallbreaker
function main(){
Java.perform(function(){
var Class = Java.use("java.lang.Class");
function inspectObject(obj){
var obj_class = Java.cast(obj.getClass(), Class);
var fields = obj_class.getDeclaredFields();
var methods = obj_class.getMethods();
console.log("Inspectiong " + obj.getClass().toString());
console.log("\t Fields:")
for (var i in fields){
console.log("\t\t" + fields[i].toString());
}
console.log("\t Methods:")
for (var i in methods){
console.log("\t\t" + methods[i].toString())
}
}
Java.choose("com.baidu.lbs.waimai.WaimaiActivity",{
onComplete: function(){
console.log("complete!");
},
onMatch: function(instance){
console.log("find instance", instance);
inspectObject(instance);
}
})
})
}
setImmediate(main)
枚举所有类
Java.perform(function (){
console.log("\n[*] enumerating classes...");
Java.enumerateLoadedClasses({
onMatch: function(_className){
console.log("[*] found instance of '"+_className+"'");
},
onComplete: function(){
console.log("[*] class enuemration complete");
}
});
});
枚举接口实现
获取指定包下所有类的接口实现
function searchInterface(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onComplete: function(){},
onMatch: function(name,handle){
if (name.indexOf("com.r0ysue.a0526printout") > -1) { // 使用包名进行过滤
console.log("find class");
var targetClass = Java.use(name);
var interfaceList = targetClass.class.getInterfaces(); // 使用反射获取类实现的接口数组
if (interfaceList.length > 0) {
console.log(name) // 打印类名
for (var i in interfaceList) {
console.log("\t", interfaceList[i].toString()); // 直接打印接口名称
}
}
}
}
})
})
}
多个ClassLoader时枚举指定类所有关联的接口实现和父子类关系
Java.perform(function () {
console.log("start")
// 枚举classLoader设置含有关键类的classLoader
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.jdd.motorfans.modules.detail.RecordFinishListener")) {
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
// 枚举所有的类
Java.enumerateLoadedClasses({
onMatch: function (className) {
if (className.toString().indexOf("RecordFinishListener") > 0 &&
className.toString().indexOf("$") > 0
) {
console.log("found => ", className)
// 使用反射获取类实现的接口数组
var interFaces = Java.use(className).class.getInterfaces();
if(interFaces.length>0){
console.log("interface is => ");
for(var i in interFaces){
console.log("\t",interFaces[i].toString())
}
}
// 获取该类的父类名包含指定名称
if (Java.use(className).class.getSuperclass()) {
var superClass = Java.use(className).class.getSuperclass().getName();
// console.log("superClass is => ",superClass);
if (superClass.indexOf("XC_MethodHook") > 0) {
console.log("found class is => ", className.toString())
traceClass(className);
}
}
}
}, onComplete: function () {
console.log("search completed!")
}
})
console.log("end2")
})
Hook Char&Byte
Java.perform(function () {
// 调用 自写dex
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
// 打印CharArray数组
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){
// Java.array('char', [ '一','去','二','三','里' ]);
// Java.use('java.lang.String').$new(Java.array('char', [ '烟','村','四','五','家']))
// Java.array("java.lang.String",["一","二","三"]);
var result = this.toString(charArray);
var result1 = JSON.stringify(charArray);
console.log("charArray,result:",charArray,result)
console.log(ArrayClass.toString(result));
console.log("charArray Object Object:",gson.$new().toJson(charArray));
return result;
}
// 打印char字符
var CharClass = Java.use("java.lang.Character");
CharClass.toString.overload("char").implementation = function(inputChar){
var result = this.toString(inputChar);
console.log("inputChar, result: ", inputChar, result);
return result;
}
// byteArray
Java.use("java.util.Arrays").toString.overload('[B').implementation = function(byteArray){
var result = this.toString(byteArray);
var result1 = JSON.stringify(byteArray);
console.log("byteArray,result):",byteArray,result)
console.log("byteArray Object Object:",gson.$new().toJson(byteArray));
return result;
}
var StringClass = Java.use("java.lang.String");
var byteArray = StringClass.$new("Hello World").getBytes();
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
var gson = Java.use("com.r0ysue.gson.Gson");
console.log(gson.$new().toJson(byteArray));
// // console byte[]
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
console.log(ByteString.of(byteArray).hex()); // byte转16进制字符串
// // 创建自定义Java数组 并打印
var MyArray = Java.array("byte",[13,4,4,2]);
console.log(gson.$new().toJson(MyArray));
});
Hook Map
遍历打印
function main() {
Java.perform(function () {
var targetClass = Java.use("com.xiaojianbang.app.ShufferMap");
targetClass.show.implementation = function (map) {
// 遍历 map
var result = "";
var it = map.keySet().iterator();
while (it.hasNext()) {
var keyStr = it.next();
var valueStr = map.get(keyStr);
result += valueStr;
}
console.log("result :", result);
// 修改 map
map.put("pass", "fxxk");
map.put("code", "Hello World");
console.log(JSON.stringify(map));
return this.show(map);
}
})
}
setImmediate(main);
cast打印 HashMap
function main() {
Java.perform(function () {
var HashMapNode = Java.use("java.util.HashMap$Node");
var targetClass = Java.use("com.xiaojianbang.app.ShufferMap");
var targetClass.show.implementation = function (map) {
var result = "";
var iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
console.log("entry", iterator.next());
var entry = Java.cast(iterator.next(), HashMapNode);
console.log(entry.getKey());
console.log(entry.getValue());
return += entry.getValue();
}
console.log("result is :", result);
}
})
}
function main(){
Java.perform(function(){
var targetClass = Java.use("com.xiaojianbang.app.ShufferMap");
var HashMap = Java.use('java.util.HashMap');
targetClass.show.implementation = function(map){
// 直接调用 toString()
var args_map = Java.cast(param_hm, HashMap);
console.log("打印hashmap: -> " + map.toString() + args_map.toString());
return this.show.apply(this,arguments);
}
})
}
setImmediate(main);
Hook 重载
function hookdecodeimgkey() {
Java.perform(function () {
var base64 = Java.use("android.util.Base64")
Java.use("com.ilulutv.fulao2.other.i.b").b.overload('[B', '[B', 'java.lang.String').implementation = function (key, iv, image) {
var result = this.b(key, iv, image);
console.log("key", base64.encodeToString(key, 0));
console.log("iv", base64.encodeToString(iv, 0));
return result;
}
var UtilsClass = Java.use("com.kevin.app.Utils");
// 重载无参方法
UtilsClass.test.overload().implementation = function () {
console.log("hook overload no args");
return this.test();
}
// 重载有参方法 - 基础数据类型
UtilsClass.test.overload('int').implementation = function(num){
console.log("hook overload int args");
var myNum = 9999;
var oriResult = this.test(num);
console.log("oriResult is :" + oriResult);
return this.test(myNum);
}
// 重载有参方法 - 引用数据类型
UtilsClass.test.overload('com.kevin.app.Money').implementation = function(money){
console.log("hook Money args");
return this.test(money);
}
// hook 指定方法的所有重载
var ClassName = Java.use("com.xiaojianbang.app.Utils");
var overloadsLength = ClassName.test.overloads.length;
for (var i = 0; i < overloadsLength; i++){
ClassName.test.overloads[i].implementation = function () {
// 遍历打印 arguments
for (var a = 0; a < arguments.length; a++){
console.log(a + " : " + arguments[a]);
}
// 调用原方法
return this.test.apply(this,arguments);
}
}
})
}
Hook 内部类
function main(){
Java.perfor(function(){
// hook 内部类
// 内部类使用$进行分隔 不使用.
var InnerClass = Java.use("com.xiaojianbang.app.Money$innerClass");
// 重写内部类的 $init 方法
InnerClass.$init.overload("java.lang.String","int").implementation = function(x,y){
console.log("x: ",x);
console.log("y: ",y);
this.$init(x,y);
}
})
}
setImmediate(main)
Hook 匿名类
// 接口, 抽象类, 不可以被new
// 接口, 抽象类 要使用必须要实例化, 实例化不是通过new, 而是通过实现接口方法, 继承抽象类等方式
// new __接口__{} 可以理解成 new 了一个实现接口的匿名类, 在匿名类的内部(花括号内),实现了这个接口
function main(){
Java.perform(function(){
// hook 匿名类
// 匿名类在 smail中以 $1, $2 等方式存在, 需要通过 java 行号去 smail 找到准确的匿名类名称
var NiMingClass = Java.use("com.xiaojianbang.app.MainActivity$1");
NiMingClass.getInfo.implementation = function (){
return "kevin change 匿名类";
}
})
}
setImmediate(main)
Hook 枚举类
function enumPrint(){
Java.perform(function(){
Java.choose("com.r0ysue.a0526printout.Signal",{
onComplete: function(){},
onMatch: function(instance){
console.log('find it ,',instance);
console.log(instance.class.getName());
}
})
})
}
Hook 动态加载dex
function main(){
Java.perform(function(){
Java.enumerateClassLoaders({
onMatch : function(loader){
try {
// loadClass or findClass 并指定classLoader
if (loader.loadClass("com.xiaojianbang.app.Dynamic")){
Java.classFactory.loader = loader;
var hookClass = Java.use("com.xiaojianbang.app.Dynamic");
console.log("success hook it :", hookClass);
}
} catch (error) {
}
},
onComplete: function () {
console.log("complete !!! ")
}
})
})
}
setImmediate(main);
经常在加壳的 app 中, 没办法正确找到正常加载 app 类的 classloader
function hook() {
Java.perform(function () {
Java.enumerateClassLoadersSync().forEach(function (classloader) {
try {
console.log("classloader", classloader);
classloader.loadClass("com.kanxue.encrypt01.MainActivity");
Java.classFactory.loader = classloader;
var mainActivityClass = Java.use("com.kanxue.encrypt01.MainActivity");
console.log("mainActivityClass", mainActivityClass);
} catch (error) {
console.log("error", error);
}
});
})
}
trace类调用栈
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
// trace a specific Java Method
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;
var targetClass = targetClassMethod.slice(0, delim)
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
// hook all class_method
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
console.warn("\n*** entered " + targetClassMethod);
// print backtrace
// Java.perform(function() {
// var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// console.log("\nBacktrace:\n" + bt);
// });
// print args
if (arguments.length) console.log();
for (var j = 0; j < arguments.length; j++) {
console.log("arg[" + j + "]: " + arguments[j]);
}
// print retval
var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
console.log("\nretval: " + retval);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.warn("\n*** exiting " + targetClassMethod);
return retval;
}
}
}
function traceClass(targetClass) {
//Java.use是新建一个对象哈,大家还记得么?
var hook = Java.use(targetClass);
//利用反射的方式,拿到当前类的所有方法
var methods = hook.class.getDeclaredMethods();
// var methods = hook.class.getMethods();
//建完对象之后记得将对象释放掉哈
hook.$dispose;
//将方法名保存到数组中
var parsedMethods = [];
methods.forEach(function (method) {
parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
});
//去掉一些重复的值
var targets = uniqBy(parsedMethods, JSON.stringify);
//对数组中所有的方法进行hook,traceMethod也就是第一小节的内容
targets.forEach(function (targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
}
调用栈打印
function printStacks(name){
console.log("====== printStacks start ====== " + name + "==============================")
// sample 1
var throwable = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
console.log(throwable);
// sample 2
var exception = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
console.log(exception);
console.log("====== printStacks end ======== " + name + "==============================")
}
手动注册类
Java.perform(function () {
var Runnable = Java.use("java.lang.Runnable");
var saveImg = Java.registerClass({
name: "com.roysue.runnable",
implements: [Runnable],
fields: {
bm: "android.graphics.Bitmap",
},
methods: {
$init: [{
returnType: "void",
argumentTypes: ["android.graphics.Bitmap"],
implementation: function (bitmap) {
this.bm.value = bitmap;
}
}],
run: function () {
var path = "/sdcard/Download/tmp/" + guid() + ".jpg"
console.log("path=> ", path)
var file = Java.use("java.io.File").$new(path)
var fos = Java.use("java.io.FileOutputStream").$new(file);
this.bm.value.compress(Java.use("android.graphics.Bitmap$CompressFormat").JPEG.value, 100, fos)
fos.flush();
fos.close();
}
}
});
Java.use("android.graphics.BitmapFactory").decodeByteArray.overload('[B', 'int', 'int', 'android.graphics.BitmapFactory$Options').implementation = function (data, offset, length, opts) {
var result = this.decodeByteArray(data, offset, length, opts);
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
var runnable = saveImg.$new(result);
runnable.run()
return result;
}
})
Java.perform(function() {
// https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SECURE
var FLAG_SECURE = 0x2000;
var Runnable = Java.use("java.lang.Runnable");
var DisableSecureRunnable = Java.registerClass({
name: "me.bhamza.DisableSecureRunnable",
implements: [Runnable],
fields: {
activity: "android.app.Activity",
},
methods: {
$init: [{
returnType: "void",
argumentTypes: ["android.app.Activity"],
implementation: function (activity) {
this.activity.value = activity;
}
}],
run: function() {
var flags = this.activity.value.getWindow().getAttributes().flags.value; // get current value
flags &= ~FLAG_SECURE; // toggle it
this.activity.value.getWindow().setFlags(flags, FLAG_SECURE); // disable it!
console.log("Done disabling SECURE flag...");
}
}
});
Java.choose("com.example.app.FlagSecureTestActivity", {
"onMatch": function (instance) {
var runnable = DisableSecureRunnable.$new(instance);
instance.runOnUiThread(runnable);
},
"onComplete": function () {}
});
});
Hook Click
var jclazz = null;
var jobj = null;
function getObjClassName(obj) {
if (!jclazz) {
var jclazz = Java.use("java.lang.Class");
}
if (!jobj) {
var jobj = Java.use("java.lang.Object");
}
return jclazz.getName.call(jobj.getClass.call(obj));
}
function watch(obj, mtdName) {
var listener_name = getObjClassName(obj);
var target = Java.use(listener_name);
if (!target || !mtdName in target) {
return;
}
// send("[WatchEvent] hooking " + mtdName + ": " + listener_name);
target[mtdName].overloads.forEach(function (overload) {
overload.implementation = function () {
//send("[WatchEvent] " + mtdName + ": " + getObjClassName(this));
console.log("[WatchEvent] " + mtdName + ": " + getObjClassName(this))
return this[mtdName].apply(this, arguments);
};
})
}
function OnClickListener() {
Java.perform(function () {
//以spawn启动进程的模式来attach的话
Java.use("android.view.View").setOnClickListener.implementation = function (listener) {
if (listener != null) {
watch(listener, 'onClick');
}
return this.setOnClickListener(listener);
};
//如果frida以attach的模式进行attch的话
Java.choose("android.view.View$ListenerInfo", {
onMatch: function (instance) {
instance = instance.mOnClickListener.value;
if (instance) {
console.log("mOnClickListener name is :" + getObjClassName(instance));
watch(instance, 'onClick');
}
},
onComplete: function () {
}
})
})
}
setImmediate(OnClickListener);
Hook Activity
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
//console.log(Object.getOwnPropertyNames(Activity));
Activity.startActivity.overload('android.content.Intent').implementation=function(p1){
console.log("Hooking android.app.Activity.startActivity(p1) successfully,p1="+p1);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log(decodeURIComponent(p1.toUri(256)));
this.startActivity(p1);
}
Activity.startActivity.overload('android.content.Intent', 'android.os.Bundle').implementation=function(p1,p2){
console.log("Hooking android.app.Activity.startActivity(p1,p2) successfully,p1="+p1+",p2="+p2);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log(decodeURIComponent(p1.toUri(256)));
this.startActivity(p1,p2);
}
Activity.startService.overload('android.content.Intent').implementation=function(p1){
console.log("Hooking android.app.Activity.startService(p1) successfully,p1="+p1);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log(decodeURIComponent(p1.toUri(256)));
this.startService(p1);
}
})
frida主线程运行
使用一些方法的时候出现报错 on a thread that has not called Looper.prepare()
Java.perform(function() {
var Toast = Java.use('android.widget.Toast');
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
Java.scheduleOnMainThread(function() {
Toast.makeText(context, "Hello World", Toast.LENGTH_LONG.value).show();
})
})
过滤打印
function hook_lnf() {
var activate = false;
Java.perform(function(){
var hashmapClass = Java.use("java.util.HashMap");
hashmapClass.put.implementation = function(key,value){
if (activate){
console.log("key:", key, "value:", value);
}
return this.put(key,value);
};
});
Java.perform(function () {
var lnfClazz = Java.use("tb.lnf");
lnfClazz.a.overload('java.util.HashMap', 'java.util.HashMap', 'java.lang.String',
'java.lang.String', 'boolean').implementation = function (hashmap, hashmap2, str, str2, z) {
printHashMap("hashmap", hashmap);
printHashMap("hashmap2", hashmap2);
console.log("str", str);
console.log("str2", str2);
console.log("boolean", z);
activate = true;
var result = this.a(hashmap, hashmap2, str, str2, z);
activate = false
printHashMap("result", result);
return result;
};
})
}
禁止退出
function hookExit(){
Java.perform(function(){
console.log("[*] Starting hook exit");
var exitClass = Java.use("java.lang.System");
exitClass.exit.implementation = function(){
console.log("[*] System.exit.called");
}
console.log("[*] hooking calls to System.exit");
})
}
setImmediate(hookExit);
修改设备参数
// frida hook 修改设备参数
Java.perform(function() {
var TelephonyManager = Java.use("android.telephony.TelephonyManager");
//IMEI hook
TelephonyManager.getDeviceId.overload().implementation = function () {
console.log("[*]Called - getDeviceId()");
var temp = this.getDeviceId();
console.log("real IMEI: "+temp);
return "867979021642856";
};
// muti IMEI
TelephonyManager.getDeviceId.overload('int').implementation = function (p) {
console.log("[*]Called - getDeviceId(int) param is"+p);
var temp = this.getDeviceId(p);
console.log("real IMEI "+p+": "+temp);
return "867979021642856";
};
//IMSI hook
TelephonyManager.getSimSerialNumber.overload().implementation = function () {
console.log("[*]Called - getSimSerialNumber(String)");
var temp = this.getSimSerialNumber();
console.log("real IMSI: "+temp);
return "123456789";
};
//////////////////////////////////////
//ANDOID_ID hook
var Secure = Java.use("android.provider.Settings$Secure");
Secure.getString.implementation = function (p1,p2) {
if(p2.indexOf("android_id")<0) return this.getString(p1,p2);
console.log("[*]Called - get android_ID, param is:"+p2);
var temp = this.getString(p1,p2);
console.log("real Android_ID: "+temp);
return "844de23bfcf93801";
}
//android的hidden API,需要通过反射调用
var SP = Java.use("android.os.SystemProperties");
SP.get.overload('java.lang.String').implementation = function (p1) {
var tmp = this.get(p1);
console.log("[*]"+p1+" : "+tmp);
return tmp;
}
SP.get.overload('java.lang.String', 'java.lang.String').implementation = function (p1,p2) {
var tmp = this.get(p1,p2)
console.log("[*]"+p1+","+p2+" : "+tmp);
return tmp;
}
// hook MAC
var wifi = Java.use("android.net.wifi.WifiInfo");
wifi.getMacAddress.implementation = function () {
var tmp = this.getMacAddress();
console.log("[*]real MAC: "+tmp);
return tmp;
}
})
请求调用栈
var class_Socket = Java.use("java.net.Socket");
class_Socket.getOutputStream.overload().implementation = function(){
send("getOutputSteam");
var result = this.getOutputStream();
var bt = Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Exception").$new();
)
console.log("Backtrace:" + bt);
send(result);
return result;
}
上下文Context
function getContext(){
Java.perform(function(){
var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
console.log(currentApplication);
var context = currentApplication.getApplicationContext();
console.log(context);
var packageName = context.getPackageName();
console.log(packageName);
console.log(currentApplication.getPackageName());
})
}
RPC
frida 传递参数
function main(){
Java.perform(function () {
console.log("enter perform");
// 获取要hook的类
var TextViewClass = Java.use("android.widget.TextView");
// 要hook的方法
TextViewClass.setText.overload('java.lang.CharSequence').implementation = function (ori_input) {
console.log('enter', 'java.lang.CharSequence');
console.log('ori_input',ori_input.toString());
// 定义用于接受python传参的data
var receive_data;
// 将原参数传递给python 在python中进行处理
send(ori_input.toString());
// recv 从python接收传递的内容 默认传过来的是个json对象
recv(function (json_data) {
console.log('data from python ' + json_data.data);
receive_data = json_data.data;
console.log(typeof (receive_data));
}).wait(); //wait() 等待python处理 阻塞
// 转java字符串
receive_data = Java.use("java.lang.String").$new(receive_data);
this.setText(receive_data);
};
})
}
setImmediate(main);
python 处理收到的参数
# -*- coding: utf-8 -*-
__author__ = "K"
__time__ = "2020-08-06 09:48"
import sys
import time
import base64
import frida
from loguru import logger
def on_message(message,data):
logger.info(str(message)) # dict
logger.info(str(data) if data else "None")
if message['type'] == 'error':
logger.error('error:' + str(message['description']))
logger.error('stack: ' + str(message['stack']))
if message['type'] == 'send':
logger.info('get message [*] --> ' + message['payload'])
payload = message['payload']
# 处理逻辑 sending to the server: YWFhOmJiYg==
tmp = payload.split(':')
sts = tmp[0]
need_to_db64 = tmp[1]
user_pass = base64.b64decode(need_to_db64.encode()).decode()
mine_str = 'admin' + ':' + user_pass.split(':')[-1]
mine_b64_str = base64.b64encode(mine_str.encode()).decode()
mine_b64_str = sts + mine_b64_str
logger.info(mine_b64_str)
# python返回数据给js script.post
script.post({'data':mine_b64_str})
logger.info('python complete')
device = frida.get_usb_device()
# pid = device.spawn(['com.kevin.demo04'])
# time.sleep(1)
session = device.attach('com.kevin.demo02')
with open('./hulianhutong.js','r') as f:
script = session.create_script(f.read())
script.on("message",on_message)
script.load()
input()
强制类型转换
// Java.cast() 子类可以强转成父类, 父类不能转成子类
// 可以使用Java.cast()将子类强转成父类, 再调用父类的动态方法
function castDemo(){
Java.perform(function(){
var JuiceHandle = null; // 用来存储内存中找到的Juice对象
var WaterClass = Java.use("com.r0ysue.a0526printout.Water");
Java.choose("com.r0ysue.a0526printout.Juice",{
onComplete: function(){},
onMatch: function(instance){
JuiceHandle = instance;
console.log("instance:", instance);
// 调用Juice对象的方法
console.log(JuiceHandle.fillEnergy());
// 子类Juice转父类Water 并调用父类的动态方法
var WaterInstance = Java.cast(JuiceHandle,WaterClass);
console.log(WaterInstance.still(WaterInstance));
}
})
})
}
常见算法hook
Java.perform(function () {
var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) {
showStacks();
var result = this.$init(a, b);
send("======================================");
send("算法名:" + b + "|Dec密钥:" + bytesToString(a));
send("算法名:" + b + "|Hex密钥:" + bytesToHex(a));
return result;
}
var mac = Java.use('javax.crypto.Mac');
mac.getInstance.overload('java.lang.String').implementation = function (a) {
showStacks();
var result = this.getInstance(a);
send("======================================");
send("算法名:" + a);
return result;
}
mac.update.overload('[B').implementation = function (a) {
showStacks();
this.update(a);
send("======================================");
send("update:" + bytesToString(a))
}
mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
showStacks();
this.update(a, b, c)
send("======================================");
send("update:" + bytesToString(a) + "|" + b + "|" + c);
}
mac.doFinal.overload().implementation = function () {
showStacks();
var result = this.doFinal();
send("======================================");
send("doFinal结果:" + bytesToHex(result));
send("doFinal结果:" + bytesToBase64(result));
return result;
}
mac.doFinal.overload('[B').implementation = function (a) {
showStacks();
var result = this.doFinal(a);
send("======================================");
send("doFinal参数:" + bytesToString(a));
send("doFinal结果:" + bytesToHex(result));
send("doFinal结果:" + bytesToBase64(result));
return result;
}
var md = Java.use('java.security.MessageDigest');
md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) {
showStacks();
send("======================================");
send("算法名:" + a);
return this.getInstance(a, b);
}
md.getInstance.overload('java.lang.String').implementation = function (a) {
showStacks();
send("======================================");
send("算法名:" + a);
return this.getInstance(a);
}
md.update.overload('[B').implementation = function (a) {
showStacks();
send("======================================");
send("update:" + bytesToString(a))
return this.update(a);
}
md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
showStacks();
send("======================================");
send("update:" + bytesToString(a) + "|" + b + "|" + c);
return this.update(a, b, c);
}
md.digest.overload().implementation = function () {
showStacks();
send("======================================");
var result = this.digest();
send("digest结果:" + bytesToHex(result));
send("digest结果:" + bytesToBase64(result));
return result;
}
md.digest.overload('[B').implementation = function (a) {
showStacks();
send("======================================");
send("digest参数:" + bytesToString(a));
var result = this.digest(a);
send("digest结果:" + bytesToHex(result));
send("digest结果:" + bytesToBase64(result));
return result;
}
var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec');
ivParameterSpec.$init.overload('[B').implementation = function (a) {
showStacks();
var result = this.$init(a);
send("======================================");
send("iv向量:" + bytesToString(a));
send("iv向量:" + bytesToHex(a));
return result;
}
var cipher = Java.use('javax.crypto.Cipher');
cipher.getInstance.overload('java.lang.String').implementation = function (a) {
showStacks();
var result = this.getInstance(a);
send("======================================");
send("模式填充:" + a);
return result;
}
cipher.update.overload('[B').implementation = function (a) {
showStacks();
var result = this.update(a);
send("======================================");
send("update:" + bytesToString(a));
return result;
}
cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
showStacks();
var result = this.update(a, b, c);
send("======================================");
send("update:" + bytesToString(a) + "|" + b + "|" + c);
return result;
}
cipher.doFinal.overload().implementation = function () {
showStacks();
var result = this.doFinal();
send("======================================");
send("doFinal结果:" + bytesToHex(result));
send("doFinal结果:" + bytesToBase64(result));
return result;
}
cipher.doFinal.overload('[B').implementation = function (a) {
showStacks();
var result = this.doFinal(a);
send("======================================");
send("doFinal参数:" + bytesToString(a));
send("doFinal结果:" + bytesToHex(result));
send("doFinal结果:" + bytesToBase64(result));
return result;
}
var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec');
x509EncodedKeySpec.$init.overload('[B').implementation = function (a) {
showStacks();
var result = this.$init(a);
send("======================================");
send("RSA密钥:" + bytesToBase64(a));
return result;
}
var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec');
rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) {
showStacks();
var result = this.$init(a, b);
send("======================================");
//send("RSA密钥:" + bytesToBase64(a));
send("RSA密钥N:" + a.toString(16));
send("RSA密钥E:" + b.toString(16));
return result;
}
});
base64实现
# -*- coding: utf-8 -*-
import base64
DEFAULT = 0 # 默认模式, 每行不超过76个字符
NO_PADDING = 1 # 移除最后的=
NO_WRAP = 2 # 不换行,一行输出
CRLF = 4 # 采用win上的换行符
URL_SAVE = 8 # 采用urlsafe
def decode(content: str, flag: int) -> bytes:
missing_padding = len(content) % 4
if missing_padding != 0:
content = content.ljust(len(content) + (4 - missing_padding), "=")
if flag & URL_SAVE:
result = base64.urlsafe_b64decode(content.encode("utf-8"))
else:
result = base64.b64decode(content.encode("utf-8"))
return result
def encode(content: bytes, flag: int) -> str:
need_wrap = True
need_padding = True
lf = "\n"
if flag & NO_WRAP:
need_wrap = False
if flag & NO_PADDING:
need_padding = False
if flag & CRLF:
lf = "\r\n"
if flag & URL_SAVE:
result = base64.urlsafe_b64encode(content).decode("utf-8")
else:
result = base64.b64encode(content).decode("utf-8")
if not need_padding:
result = result.rstrip("=")
if need_wrap:
n = 76
output = lf.join([result[i:i + n] for i in range(0, len(result), n)])
else:
output = result
return output
常用转换模板
//工具相关函数
var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1));
function stringToBase64(e) {
var r, a, c, h, o, t;
for (c = e.length, a = 0, r = ''; a < c;) {
if (h = 255 & e.charCodeAt(a++), a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4),
r += '==';
break
}
if (o = e.charCodeAt(a++), a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2),
r += '=';
break
}
t = e.charCodeAt(a++),
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
r += base64EncodeChars.charAt(63 & t)
}
return r
}
function base64ToString(e) {
var r, a, c, h, o, t, d;
for (t = e.length, o = 0, d = ''; o < t;) {
do
r = base64DecodeChars[255 & e.charCodeAt(o++)];
while (o < t && r == -1);
if (r == -1)
break;
do
a = base64DecodeChars[255 & e.charCodeAt(o++)];
while (o < t && a == -1);
if (a == -1)
break;
d += String.fromCharCode(r << 2 | (48 & a) >> 4);
do {
if (c = 255 & e.charCodeAt(o++), 61 == c)
return d;
c = base64DecodeChars[c]
} while (o < t && c == -1);
if (c == -1)
break;
d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2);
do {
if (h = 255 & e.charCodeAt(o++), 61 == h)
return d;
h = base64DecodeChars[h]
} while (o < t && h == -1);
if (h == -1)
break;
d += String.fromCharCode((3 & c) << 6 | h)
}
return d
}
function hexToBase64(str) {
return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
function base64ToHex(str) {
for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
var tmp = bin.charCodeAt(i).toString(16);
if (tmp.length === 1)
tmp = "0" + tmp;
hex[hex.length] = tmp;
}
return hex.join("");
}
function hexToBytes(str) {
var pos = 0;
var len = str.length;
if (len % 2 != 0) {
return null;
}
len /= 2;
var hexA = new Array();
for (var i = 0; i < len; i++) {
var s = str.substr(pos, 2);
var v = parseInt(s, 16);
hexA.push(v);
pos += 2;
}
return hexA;
}
function bytesToHex(arr) {
var str = '';
var k, j;
for (var i = 0; i < arr.length; i++) {
k = arr[i];
j = k;
if (k < 0) {
j = k + 256;
}
if (j < 16) {
str += "0";
}
str += j.toString(16);
}
return str;
}
function stringToHex(str) {
var val = "";
for (var i = 0; i < str.length; i++) {
if (val == "")
val = str.charCodeAt(i).toString(16);
else
val += str.charCodeAt(i).toString(16);
}
return val
}
function stringToBytes(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++) {
ch = str.charCodeAt(i);
st = [];
do {
st.push(ch & 0xFF);
ch = ch >> 8;
}
while (ch);
re = re.concat(st.reverse());
}
return re;
}
//将byte[]转成String的方法
function bytesToString(arr) {
var str = '';
arr = new Uint8Array(arr);
for (var i in arr) {
str += String.fromCharCode(arr[i]);
}
return str;
}
function bytesToBase64(e) {
var r, a, c, h, o, t;
for (c = e.length, a = 0, r = ''; a < c;) {
if (h = 255 & e[a++], a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4),
r += '==';
break
}
if (o = e[a++], a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2),
r += '=';
break
}
t = e[a++],
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
r += base64EncodeChars.charAt(63 & t)
}
return r
}
function base64ToBytes(e) {
var r, a, c, h, o, t, d;
for (t = e.length, o = 0, d = []; o < t;) {
do
r = base64DecodeChars[255 & e.charCodeAt(o++)];
while (o < t && r == -1);
if (r == -1)
break;
do
a = base64DecodeChars[255 & e.charCodeAt(o++)];
while (o < t && a == -1);
if (a == -1)
break;
d.push(r << 2 | (48 & a) >> 4);
do {
if (c = 255 & e.charCodeAt(o++), 61 == c)
return d;
c = base64DecodeChars[c]
} while (o < t && c == -1);
if (c == -1)
break;
d.push((15 & a) << 4 | (60 & c) >> 2);
do {
if (h = 255 & e.charCodeAt(o++), 61 == h)
return d;
h = base64DecodeChars[h]
} while (o < t && h == -1);
if (h == -1)
break;
d.push((3 & c) << 6 | h)
}
return d
}
// 打印log
function showStacks() {
Java.perform(function () {
send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
});
}
// 字节数组转hex字符串
function bytesToHex(arr) {
var str = "";
for (var i = 0; i < arr.length; i++) {
var tmp = arr[i];
if (tmp < 0) {
tmp = (255 + tmp + 1).toString(16);
} else {
tmp = tmp.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
function bytesToBase64(arr) {
var str = "";
for (var i = 0; i < arr.length; i++) {
var tmp = arr[i];
if (tmp < 0) {
tmp = (255 + tmp + 1).toString(16);
} else {
tmp = tmp.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
function bytesToString(arr) {
var str = "";
for (var i = 0; i < arr.length; i++) {
var tmp = arr[i];
if (tmp < 0) {
tmp = (255 + tmp + 1).toString(16);
} else {
tmp = tmp.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
function byteToHexString(uint8arr) {
if (!uint8arr) {
return '';
}
var hexStr = '';
for (var i = 0; i < uint8arr.length; i++) {
var hex = (uint8arr[i] & 0xff).toString(16);
hex = (hex.length === 1) ? '0' + hex : hex;
hexStr += hex;
}
return hexStr.toUpperCase();
}
function stringToUint8Array(str){
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array
}
function str2arraybffer(str) {
var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function printBytes(b){
var hexstr = "";
for (var i=0; i< b.length; i++)
{
var uByte = (b[i]>>>0)&0xff;
var n = uByte.toString(16);
hexstr += "0x" + ("00" + n).slice(-2)+", ";
}
return hexstr;
}
//字节数组转十六进制字符串,对负值填坑
// 二进制数据(包括内存地址)在计算机中一般以16进制的方式表示
function Bytes2HexString(arrBytes) {
var str = "";
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num=arrBytes[i];
if (num < 0) {
//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
tmp =(255+num+1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
// 会转成有符号的数字 JSON.stringify(bytes)
function HexString2Bytes(str) {
var pos = 0;
var len = str.length;
if (len % 2 != 0) {
return null;
}
len /= 2;
var arrBytes = new Array();
for (var i = 0; i < len; i++) {
var s = str.substr(pos, 2);
var v = parseInt(s, 16);
// 转成有符号的 10进制
if (v > 127) { v = v - 256 }
// end
arrBytes.push(v);
pos += 2;
}
return arrBytes;
}
function jstring2Str(jstring) {
var ret;
Java.perform(function() {
var String = Java.use("java.lang.String");
ret = Java.cast(jstring, String);
});
return ret;
}
function jbyteArray2Array(jbyteArray) {
var ret;
Java.perform(function() {
var b = Java.use('[B');
var buffer = Java.cast(jbyteArray, b);
ret = Java.array('byte', buffer);
});
return ret;
}
function getParamType(obj) {
return obj == null ? String(obj) : Object.prototype.toString.call(obj).replace(/\[object\s+(\w+)\]/i, "$1") || "object";
}
打印class
function printClass(c){
var str = "-------------------------------\n";
str += "|" + JSON.stringify(c) + "\n";
var fields = c.getClass().getFields();
for(var index in fields){
var field = fields[index];
var fieldName = "";
var value = "";
try{
fieldName = field.getName();
value = field.get(c);
}catch(e){
}
if(fieldName == ""){
continue;
}
str += "|" + fieldName + ":" + printValue(value) + "\n";
}
str += "------------------------------\n\n\n";
return str;
}
function printValue(value){
try{
var newValue = Java.cast(value, Java.use("java.lang.Object"))
switch(newValue.getClass().getName()){
case "[B":
return printBytes(value)
}
return value;
}catch(e){
return value;
}
}
function printBytes(result){
try{
var ByteArrayOutputStreamClass = Java.use("java.io.ByteArrayOutputStream");
var out = ByteArrayOutputStreamClass.$new()
var ObjectOutputStreamClass = Java.use("java.io.ObjectOutputStream");
var sOut = ObjectOutputStreamClass.$new(out);
sOut.writeObject(result);
sOut.flush();
var bytes = out.toByteArray();
var argsArray = [];
for(var i = 0; i < bytes.length; i++) {
argsArray.push(bytes[i]);
}
return "["+argsArray.join(",")+"]";
}catch(e){
console.log(e);
return result;
}
}
本文由博客群发一文多发等运营工具平台 OpenWrite 发布
网友评论