赞
踩
macOS上安装python,下载地址:https://www.python.org/ftp/python/3.10.3/python-3.10.3-macos11.pkg
执行对应的文件:

1. 先安装 virtualenv
sudo pip3 install virtualenv
#有的时候可能需要 sudo 权限
2. 切换为env环境
source ~/Desktop/frida/bin/activate
3.安装frida环境
pip install frida==12.8.0
pip install frida-tools==5.3.0
安装成功标识:
(frida) ➜ Desktop frida --version
12.8.0
frida是平台原生app的Greasemonkey,说的专业一点,就是一种动态插桩工具,可以插入一些代码到原生app的内存空间去,(动态地监视和修改其行为),这些原生平台可以是Win、Mac、Linux、Android或者iOS。
Mac系统的frida版本要与Android中的版本号保持一致。
升级Mac系统中的frida为最新版本的命令:
sudo pip install --upgrade frida
或者安装指定版本的frida,这里安装frida的12.1.2版本:
pip install frida==12.1.2
我的root设备是Android4.4.4系统
下载frida-server到电脑中,地址:frida-server
选择合适的版本,我这里下载的是frida-server-12.1.2-android-arm.xz
下载到电脑要解压这个.xz文件,Mac系统如果不能识别这个文件,可到App Store安装一个叫"The Unarchiver"的工具。
解压后修改文件名为"frida-server",并复制文件到手机对应的目录下:
adb push frida-server /data/local/tmp/
继续在终端输入命令,为frida-server添加执行权限,并启动手机中的frida-server:
$ adb shell
shell@hammerhead:/ $ su
root@hammerhead:/ # cd /data/local/tmp/
root@hammerhead:/data/local/tmp # chmod 777 frida-server
root@hammerhead:/data/local/tmp # ./frida-server &
手机会重启,重启之后,mac新开一个终端输入命令检查手机上的frida是否运行成功:
frida-ps -U
成功则会打印手机中运行的进程:
PID Name
----- ----------------------------------------------
25851 adb
25767 adbd
31402 android.process.acore
31675 android.process.media
187 bridgemgrd
32228 com.android.defcontainer
...
...
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
https://www.52pojie.cn/forum.php?mod=viewthread&tid=931872
import frida
import sys
rdev = frida.get_remote_device()
front_app = rdev.get_frontmost_application()
print (front_app)
import frida
import sys
rdev = frida.get_remote_device()
processes = rdev.enumerate_processes()
for processe in processes:
print (processe)
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("com.mfw.roadbook") # 也可以使用attach(pid)的方式
modules = session.enumerate_modules()
for module in modules:
# print (module)
if module.name=="libmfw.so":
export_funcs = module.enumerate_exports()
for export_func in export_funcs:
print ("\t%s\t%s"%(export_func.name,hex(export_func.relative_address)))
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch: function(className) {
send(className);},
onComplete:function(){
send("done");
}
});
https://github.com/iGio90/FridaAndroidInjector
操作命令:
frida -U --no-pause -f package_name -l hook_RegisterNatives.js
frida -U -f com.kuaishou.android.common.kwguard //直接注入APP 在APP里面操作
操作案列:
https://juejin.im/post/5b1cc2b85188257d8c7d726c
https://www.anquanke.com/post/id/197657#h3-1
内存堆搜索与执行,提取内存信息。执行一些hook函数
https://jianshu.com/p/bab4f4714d98
//在堆上查找实例化的对象,示例代码如下!
Java.choose("b3nac.injuredandroid", {
onMatch: function (instance) {
console.log("Found instance: " + instance);
console.log("Result of secret func: " + instance.decrypt());
},
onComplete: function () { }
});
如果是自定义的实体类参数:比如okhttp的Request
var Request = Java.use('okhttp3.Request');
var Java_Request = Java.cast(arguments[j], Request);
console.log(Java_Request.url());
var Map = Java.use('java.util.HashMap');
var args_map = Java.cast(arguments[j], Map);
console.log(args_map.toString());
将参数强制转换成类对象,并打印里面的值
或者使用javascript里面的value
var1.data.value
https://langgithub.github.io/2019/08/01/frida%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/#okhttp%E4%B8%80%E8%88%AChook%E6%96%B9%E5%BC%8F
function abc(){ var base_address=Module.findBaseAddress('libcms.so') if (base_address!=null){ console.log("param:ok"); var str; Java.perform(function () { str = Java.use("java.lang.String"); }); Interceptor.attach(base_address.add(0x16e19), { onEnter: function (args) { // console.log("param1>>>>>>>" + args[0].readCString()); // console.log("param1>>>>>>>" + Memory.readUtf16String(args[0])); // readAnsiString console.log("hook success"); var s3 = Java.cast(args[3], str); var s5 = Java.cast(args[5], str); console.log("param2>>>>>>>" + args[2].toInt32()); console.log("param3>>>>>>>" + s3); const length = Java.vm.getEnv().getArrayLength(args[4]); var array=[]; for(var i=0;i<length;i++){ var obj=Java.vm.getEnv().getObjectArrayElement (args[4],i) var result=Java.vm.getEnv().stringFromJni(obj) array.push(result); } console.log("param4>>>>>>>" + JSON.stringify(array)); console.log("param5>>>>>>>" + s5); }, onLeave: function (retval) { } }); } } abc();
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; } })
frida-trace -U -i open XXXX会在本地生成一个open.js 修改里面的内容即可。
/* * Auto-generated by Frida. Please modify to match the signature of open. * This stub is currently auto-generated from manpages when available. * * For full API reference, see: https://frida.re/docs/javascript-api/ */ { /** * Called synchronously when about to call open. * * @this {object} - Object allowing you to store state for use in onLeave. * @param {function} log - Call this function with a string to be presented to the user. * @param {array} args - Function arguments represented as an array of NativePointer objects. * For example use args[0].readUtf8String() if the first argument is a pointer to a C string encoded as UTF-8. * It is also possible to modify arguments by assigning a NativePointer object to an element of this array. * @param {object} state - Object allowing you to keep state across function calls. * Only one JavaScript function will execute at a time, so do not worry about race-conditions. * However, do not use this to store function arguments across onEnter/onLeave, but instead * use "this" which is an object for keeping state local to an invocation. */ onEnter: function (log, args, state) { if((args[0].readUtf8String()).indexOf("")<0){ log(args[0].readUtf8String() ); } }, /** * Called synchronously when about to return from open. * * See onEnter for details. * * @this {object} - Object allowing you to access state stored in onEnter. * @param {function} log - Call this function with a string to be presented to the user. * @param {NativePointer} retval - Return value represented as a NativePointer object. * @param {object} state - Object allowing you to keep state across function calls. */ onLeave: function (log, retval, state) { } }
Android 加固应用Hook方式-Frida
Java.perform(function () { var application = Java.use('android.app.Application'); application.attach.overload('android.content.Context').implementation = function(context){ var result = this.attach(context); var classloader = context.getClassLoader(); Java.classFactory.loader = classloader; var yeyoulogin = Java.classFactory.use('com.zcm.主窗口'); console.log("yeyoulogin:"+ yeyoulogin); yeyoulogin.按钮_用户登录$被单击.implementation = function(arg){ console.log("retval:"+ this.返回值); } } });
列出加载的类:
Java.enumerateLoadedClasses( { "onMatch": function(className){ console.log(className) }, "onComplete":function(){} } ) Hook 动态加载类 获取构造函数的参数 Java.perform(function(){ //创建一个DexClassLoader的wapper var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); //hook 它的构造函数$init,我们将它的四个参数打印出来看看。 dexclassLoader.$init.implementation = function(dexPath,optimizedDirectory,librarySearchPath,parent){ console.log("dexPath:"+dexPath); console.log("optimizedDirectory:"+optimizedDirectory); console.log("librarySearchPath:"+librarySearchPath); console.log("parent:"+parent); //不破换它原本的逻辑,我们调用它原本的构造函数。 this.$init(dexPath,optimizedDirectory,librarySearchPath,parent); } console.log("down!"); }); 获取动态加载的类 Java.perform(function(){ var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); var hookClass = undefined; var ClassUse = Java.use("java.lang.Class"); dexclassLoader.loadClass.overload('java.lang.String').implementation = function(name){ //定义一个String变量,指定我们需要的类 var hookname = "cn.chaitin.geektan.crackme.MainActivityPatch"; //直接调用第二个重载方法,跟原本的逻辑相同。 var result = this.loadClass(name,false); //如果loadClass的name参数和我们想要hook的类名相同 if(name === hookname){ //则拿到它的值 hookClass = result; //打印hookClass变量的值 console.log(hookClass); send(hookClass); return result; } return result; } }); 通过Java.cast处理泛型方法(JAVA中Class<?>表示泛型),在调用动态加载方法 Java.perform(function(){ var hookClass = undefined; var ClassUse = Java.use("java.lang.Class"); var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); var constructorclass = Java.use("java.lang.reflect.Constructor"); var objectclass= Java.use("java.lang.Object"); dexclassLoader.loadClass.overload('java.lang.String').implementation = function(name){ var hookname = "cn.chaitin.geektan.crackme.MainActivityPatch"; var result = this.loadClass(name,false); if(name == hookname){ var hookClass = result; console.log("------------------------------CAST--------------------------------") //类型转换 var hookClassCast = Java.cast(hookClass,ClassUse); //调用getMethods()获取类下的所有方法 var methods = hookClassCast.getMethods(); console.log(methods); console.log("-----------------------------NOT CAST-----------------------------") //未进行类型转换,看看能否调用getMethods()方法 var methodtest = hookClass.getMethods(); console.log(methodtest); console.log("---------------------OVER------------------------") return result; } return result; } }); 利用getDeclaredConstructor()获取具有指定参数列表构造函数的Constructor 并实例化 Java.perform(function(){ var hookClass = undefined; var ClassUse = Java.use("java.lang.Class"); var objectclass= Java.use("java.lang.Object"); var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); var orininclass = Java.use("cn.chaitin.geektan.crackme.MainActivity"); var Integerclass = Java.use("java.lang.Integer"); //实例化MainActivity对象 var mainAc = orininclass.$new(); dexclassLoader.loadClass.overload('java.lang.String').implementation = function(name){ var hookname = "cn.chaitin.geektan.crackme.MainActivityPatch"; var result = this.loadClass(name,false); if(name == hookname){ var hookClass = result; var hookClassCast = Java.cast(hookClass,ClassUse); console.log("-----------------------------BEGIN-------------------------------------"); //获取构造器 var ConstructorParam =Java.array('Ljava.lang.Object;',[objectclass.class]); var Constructor = hookClassCast.getDeclaredConstructor(ConstructorParam); console.log("Constructor:"+Constructor); console.log("orinin:"+mainAc); //实例化,newInstance的参数也是Ljava.lang.Object; var instance = Constructor.newInstance([mainAc]); console.log("patchAc:"+instance); send(instance); console.log("--------------------------------------------------------------------"); return result; } return result; } }); 利用getDeclaredMethods(),获取本类中的所有方法 Java.perform(function(){ var hookClass = undefined; var ClassUse = Java.use("java.lang.Class"); var objectclass= Java.use("java.lang.Object"); var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); var orininclass = Java.use("cn.chaitin.geektan.crackme.MainActivity"); var Integerclass = Java.use("java.lang.Integer"); //实例化MainActivity对象 var mainAc = orininclass.$new(); dexclassLoader.loadClass.overload('java.lang.String').implementation = function(name){ var hookname = "cn.chaitin.geektan.crackme.MainActivityPatch"; var result = this.loadClass(name,false); if(name == hookname){ var hookClass = result; var hookClassCast = Java.cast(hookClass,ClassUse); console.log("-----------------------------BEGIN-------------------------------------"); //获取构造器 var ConstructorParam =Java.array('Ljava.lang.Object;',[objectclass.class]); var Constructor = hookClassCast.getDeclaredConstructor(ConstructorParam); console.log("Constructor:"+Constructor); console.log("orinin:"+mainAc); //实例化,newInstance的参数也是Ljava.lang.Object; var instance = Constructor.newInstance([mainAc]); console.log("MainActivityPatchInstance:"+instance); send(instance); console.log("----------------------------Methods---------------------------------"); var func = hookClassCast.getDeclaredMethods(); console.log(func); console.log("--------------------------Need Method---------------------------------"); console.log(func[0]); var f = func[0]; console.log("---------------------------- OVER---------------------------------"); return result; } return result; } }); 调用Method.invoke()去执行方法(invoke方法的第一个参数是执行这个方法的对象实例,第二个参数是带入的实际值数组,返回值是Object,也既是该方法执行后的返回值) f.invoke(instance,Array); read-std-string /* * Note: Only compatible with libc++, though libstdc++'s std::string is a lot simpler. */ function readStdString (str) { const isTiny = (str.readU8() & 1) === 0; if (isTiny) { return str.add(1).readUtf8String(); } return str.add(2 * Process.pointerSize).readPointer().readUtf8String(); } whereisnative Java.perform(function() { var SystemDef = Java.use('java.lang.System'); var RuntimeDef = Java.use('java.lang.Runtime'); var exceptionClass = Java.use('java.lang.Exception'); var SystemLoad_1 = SystemDef.load.overload('java.lang.String'); var SystemLoad_2 = SystemDef.loadLibrary.overload('java.lang.String'); var RuntimeLoad_1 = RuntimeDef.load.overload('java.lang.String'); var RuntimeLoad_2 = RuntimeDef.loadLibrary.overload('java.lang.String'); var ThreadDef = Java.use('java.lang.Thread'); var ThreadObj = ThreadDef.$new(); SystemLoad_1.implementation = function(library) { send("Loading dynamic library => " + library); stackTrace(); return SystemLoad_1.call(this, library); } SystemLoad_2.implementation = function(library) { send("Loading dynamic library => " + library); stackTrace(); SystemLoad_2.call(this, library); return; } RuntimeLoad_1.implementation = function(library) { send("Loading dynamic library => " + library); stackTrace(); RuntimeLoad_1.call(this, library); return; } RuntimeLoad_2.implementation = function(library) { send("Loading dynamic library => " + library); stackTrace(); RuntimeLoad_2.call(this, library); return; } function stackTrace() { var stack = ThreadObj.currentThread().getStackTrace(); for (var i = 0; i < stack.length; i++) { send(i + " => " + stack[i].toString()); } send("--------------------------------------------------------------------------"); } }); Non-ASCII int ֏(int x) { return x + 100; } 甚至有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook.<\br> 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; } } ) Hook 数据库 var SQLiteDatabase = Java.use('com.tencent.wcdb.database.SQLiteDatabase'); var Set = Java.use("java.util.Set"); var ContentValues = Java.use("android.content.ContentValues"); SQLiteDatabase.insert.implementation = function (arg1,arg2,arg3) { this.insert.call(this, arg1, arg2, arg3); console.log("[insert] -> arg1:" + arg1 + "\t arg2:" + arg2); var values = Java.cast(arg3, ContentValues); var sets = Java.cast(values.keySet(), Set); var arr = sets.toArray().toString().split(","); for (var i = 0; i < arr.length; i++){ console.log("[insert] -> key:" + arr[i] + "\t value:" + values.get(arr[i])); } };
错误1:
$frida-ps -Ua
Failed to enumerate applications: the connection is closed
我遇到的错误1和错误2,都是因为手机的Frida版本和Mac电脑的Frida版本不同导致的,安装相同版本的Frida就可解决。
使用frida强制启动chrome报错
$ frida -U --no-pause -f com.android.chrome
Traceback (most recent call last):
File "/Users/king/Documents/PythonProject/Tornado/test/venv/bin/frida", line 11, in <module>
sys.exit(main())
File "/Users/king/Documents/PythonProject/Tornado/test/venv/lib/python2.7/site-packages/frida_tools/repl.py", line 23, in main
from prompt_toolkit.shortcuts import create_prompt_application, create_output, create_eventloop
ImportError: cannot import name create_prompt_application
解决:
pip install ‘prompt-toolkit==1.0.15’
错误2:
Failed to spawn: unable to connect to remote frida-server
frida-server未启动
** 更新frida: **
sudo -H pip3 install --upgrade frida
sudo -H pip3 install --upgrade frida-tools
参考:
https://bbs.pediy.com/thread-252520.htm
Frida的操作教程:https://github.com/iddoeldor/frida-snippets#hook-overloads
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。