介绍
Frida是一款功能强大的动态分析工具,可以使用它的API来编写各种自定义的脚本和插件。而Objection是基于Frida API开发的一个工具,它将常见的Frida脚本和插件整合到一起,并提供了一个命令行接口,使用户可以方便地使用这些功能,而无需编写复杂的脚本。
Objection的目标是使移动应用程序的安全评估更加简单和高效。它提供了许多常见的功能,如反调试、禁用证书绑定、绕过安全检查、修改应用程序的行为等等。这些功能都是通过调用Frida的API来实现的,但是Objection将它们封装成易于使用的命令行接口,从而降低了使用门槛,并提高了效率。
GitHub: https://github.com/sensepost/objection
安装说明
安装
pip3 install objection
参数说明
objection
Usage: objection [OPTIONS] COMMAND [ARGS]...
选项:
-N,--network 使用网络连接而不是USB进行连接。[默认值:False]
-h,--host TEXT 使用的主机地址。[默认值:127.0.0.1]
-p,--port INTEGER 连接使用的端口号。[默认值:27042]
-ah,--api-host TEXT API服务器绑定的主机地址。[默认值:127.0.0.1]
-ap,--api-port INTEGER API服务器绑定的端口号。[默认值:8888]
-g,--gadget TEXT 要连接的Frida Gadget/进程的名称。[默认值:Gadget]
-S,--serial TEXT 要连接的设备序列号。
-d,--debug 启用调试模式并输出详细信息。(包括堆栈跟踪中的代理源映射)
--help 显示此帮助消息并退出。
命令:
api 在无头模式下启动Objection API服务器。
device-type 获取附加设备的信息。
explore 启动Objection exploration REPL。
patchapk 使用frida-gadget.so修补APK。
patchipa 使用FridaGadget dylib修补IPA。
run 运行单个Objection命令。
signapk 使用Objection密钥对APK进行zipalign和签名。
version 打印当前版本并退出。
启动objection
以sieve
为目标,注入目标中
默认是连接USB,如果有多个USB,就需要用-S指定
Objection 在启动时会自动注入Frida Agent到目标应用程序中
objection -S emulator-5554 -g com.mwr.example.sieve explore
启动后,不知道输入什么命令,可以按tab,他会帮你提示,支持tab键自动补齐,也可以通过help 命令
来查看对应命令的说明,如help android
。
启动activity/service
获取activity列表
android hooking list activities
启动activity
android intent launch_activity com.mwr.example.sieve.PWList
获取service列表
android hooking list services
启动service
android intent launch_service com.mwr.example.sieve.CryptoService
内存操作
内存搜索
memory search "abcde" --string
内存查看
# memory dump <base address> <size to dump> <local destination>
memory dump from_base 0xf7778765 200 main
搜索类
列出所有加载的类
android hooking list classes
搜索类
# android hooking search classes 关键词
android hooking search classes sieve
搜索方法
列出指定类的方法
# android hooking list class_methods 类名
android hooking list class_methods com.mwr.example.sieve.MainLoginActivity
搜索方法
# android hooking search methods 关键词
android hooking search methods com.mwr.example.sieve.MainLoginActivity
主动执行方法
先尝试找到该类的实例(和Java.choose
类似)
android heap search instances com.mwr.example.sieve.MainLoginActivity
# res
Class instance enumeration complete for com.mwr.example.sieve.MainLoginActivity
Hashcode Class toString()
--------- --------------------------------------- -----------------------------------------------
135299310 com.mwr.example.sieve.MainLoginActivity com.mwr.example.sieve.MainLoginActivity@81080ee
尝试调用这个实例的com.mwr.example.sieve.MainLoginActivity.loginSuccessful()
方法
android heap execute 135299310 loginSuccessful
# res
Handle 135299310 is to class
com.mwr.example.sieve.MainLoginActivity
Executing method: loginSuccessful()
也可以直接hook这个实例,去编写js脚本;默认实例是clazz
android heap evaluate 135299310
# res
(The hashcode at `135299310` will be available as the `clazz` variable.)
console.log(clazz);
console.log(clazz.loginSuccessful());
JavaScript capture complete. Evaluating...
Handle 135299310 is to class
com.mwr.example.sieve.MainLoginActivity
com.mwr.example.sieve.MainLoginActivity@81080ee
undefined
生成hook代码
# android hooking generate simple 类名
android hooking generate simple com.mwr.example.sieve.MainLoginActivity
生成的框架代码如下,我们只需要根据要hook的逻辑进行修改即可,其中arguments
就是传入的参数。
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.sendFailed.implementation = function() {
//
return clazz.sendFailed.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.loginSuccessful.implementation = function() {
//
return clazz.loginSuccessful.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onCreateOptionsMenu.implementation = function() {
//
return clazz.onCreateOptionsMenu.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.loginFailed.implementation = function() {
//
return clazz.loginFailed.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onOptionsItemSelected.implementation = function() {
//
return clazz.onOptionsItemSelected.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.checkKeyResult.implementation = function() {
//
return clazz.checkKeyResult.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.login.implementation = function() {
//
return clazz.login.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.connected.implementation = function() {
//
return clazz.connected.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.firstLaunchResult.implementation = function() {
//
return clazz.firstLaunchResult.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.welcomeUser.implementation = function() {
//
return clazz.welcomeUser.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.setPinResult.implementation = function() {
//
return clazz.setPinResult.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.unbind.implementation = function() {
//
return clazz.unbind.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.openSettings.implementation = function() {
//
return clazz.openSettings.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.initaliseActivity.implementation = function() {
//
return clazz.initaliseActivity.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.setPin.implementation = function() {
//
return clazz.setPin.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onResume.implementation = function() {
//
return clazz.onResume.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onBackPressed.implementation = function() {
//
return clazz.onBackPressed.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onStart.implementation = function() {
//
return clazz.onStart.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onActivityResult.implementation = function() {
//
return clazz.onActivityResult.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onCreate.implementation = function() {
//
return clazz.onCreate.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.onPause.implementation = function() {
//
return clazz.onPause.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.setKeyResult.implementation = function() {
//
return clazz.setKeyResult.apply(this, arguments);
}
});
Java.perform(function() {
var clazz = Java.use('com.mwr.example.sieve.MainLoginActivity');
clazz.checkPinResult.implementation = function() {
//
return clazz.checkPinResult.apply(this, arguments);
}
});
监听类和方法
hook类中所有的方法
# android hooking watch class 类名
android hooking watch class com.mwr.example.sieve.MainLoginActivity
# res
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.checkKeyResult(boolean)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.checkPinResult(boolean)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.connected()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.firstLaunchResult(int)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.login(android.view.View)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onActivityResult(int, int, android.content.Intent)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onBackPressed()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onCreate(android.os.Bundle)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onCreateOptionsMenu(android.view.Menu)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onOptionsItemSelected(android.view.MenuItem)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onPause()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onResume()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.onStart()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.sendFailed()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.setKeyResult(boolean)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.setPinResult(boolean)
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.initaliseActivity()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.loginFailed()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.loginSuccessful()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.openSettings()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.setPin()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.unbind()
(agent) Hooking com.mwr.example.sieve.MainLoginActivity.welcomeUser()
(agent) Registering job 963225. Type: watch-class for: com.mwr.example.sieve.MainLoginActivity
hook后,输入jobs list
可以查看该类下的方法都被hook
job list
# res
Job ID Hooks Type
------ ----- --------------------------------------------------------
963225 23 watch-class for: com.mwr.example.sieve.MainLoginActivity
正常操作app,当hook的方法被调用时,会自动将这些方法输出。
hook对应方法的参数、返回值和调用栈
# android hooking watch class_method <方法名> --dump-args --dump-return --dump-backtrace
android hooking watch class_method com.mwr.example.sieve.MainLoginActivity.login --dump-args --dump-return --dump-backtrace
当这个方法被调用时,会输出相关的信息
com.mwr.example.sieve on (Xiaomi: 6.0.1) [usb] # (agent) [880894] Called com.mwr.example.sieve.MainLoginActivity.login(android.view.View)
(agent) [880894] Backtrace:
com.mwr.example.sieve.MainLoginActivity.login(Native Method)
java.lang.reflect.Method.invoke(Native Method)
android.view.View$DeclaredOnClickListener.onClick(View.java:4453)
android.view.View.performClick(View.java:5204)
android.view.View$PerformClick.run(View.java:21153)
android.os.Handler.handleCallback(Handler.java:739)
android.os.Handler.dispatchMessage(Handler.java:95)
android.os.Looper.loop(Looper.java:148)
android.app.ActivityThread.main(ActivityThread.java:5647)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:745)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:635)
(agent) [880894] Arguments com.mwr.example.sieve.MainLoginActivity.login(android.widget.Button{8efa50c VFED..C.. ...P.... 0,879-1872,1004 #7f080010 app:id/mainlogin_button_login})
(agent) [880894] Return Value: (none)
其他技巧
其他命令
查看命令的帮助
objection explore --hel
# 如同步执行命令
objection -g com.xxx.xxx -S emulator-5554 explore --startup-command 'android hooking search classes sieve'
显示当前应用程序环境的目录信息。
env
导入js脚本(需要结合frida使用,不然会有找不到pid的问题,参考issue)
import <js脚本路径>
关闭root检测
android root disable
绕过ssl pinning检测
android sslpinning disable
设置proxy代理(不是很好用)
android proxy set 172.20.10.2 8080
获取当前的activity
android hooking get current_activity
截图并保存到本地
android ui screenshot ./1.png
结合frida
部分情况,比如存在root检测这种,需要在app启动时进行hook,但是直接用objection
提供的-s
或者-S
参数又不能生效,或者有各种问题,因此可以直接结合frida来进行,也很方便好用。
# 绕过root检测
frida -D emulator-5554 -l root_detection_bypass.js -f owasp.mstg.uncrackable1
# 获取pid
frida-ps -D emulator-5554 -a
# objection attach
objection -g 4093 -S emulator-5554 explore