读书笔记:《安卓Frida逆向与抓包实战》
主动调用是强制一个函数去执行,被动调用是APP按照正常逻辑去执行函数。
在Java中,类的函数可分为两种:类函数和实例方法。通俗的讲,就是静态的方法和动态的方法。
在Frida中主动调用的类型会根据方法类型区分开。如果是类函数的主动调用,直接使用Java.use()函数找到类进行调用即可;如果是实例方法的主动调用,则需要在找到对应的实例后对方法进行调用。这里用到了Frida中非常重要的一个API函数Java.choose(),这个函数可以在Java的堆中寻找指定类的实例。
package com.roysue.demo01;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
fun(50, 30);
Log.d("godam.string", fun("LoWeRcAsE Me!!!!"));
}
}
void fun(int x, int y){
Log.d("godam", String.valueOf(x + y));
}
String fun(String x){
return x.toLowerCase();
}
void secret(){
Log.d("dadsda", "this is secret func");
}
static void staticSecret(){
Log.d("dasdad", "this is static secret func");
}
}
然后使用frida触发添加的secret函数
然而我报错了,查了下是因为Android版本
https://github.com/frida/frida/issues/1156
和这里报错一模一样
Frida will not work on Android 10 when using the "Java.choose" function, I was able to run it on android 9 and 8.1 instead, with same apk and frida version of course.
然后我买了nexus手机,一直连不上电脑,发现是线的问题,换了跟线就行了,然后又发现无法连接到虚拟机,发现是USB服务没开,又用services.msc把USB服务打开了,一波三折了属于是。
APP代码
package com.roysue.demo01;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private String total = "hello";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
fun(50, 30);
Log.d("godam.string", fun("LoWeRcAsE Me!!!!"));
}
}
void fun(int x, int y){
Log.d("godam", String.valueOf(x + y));
}
String fun(String x){
return x.toLowerCase();
}
void secret(){
total += " secretFunc";
Log.d("dadsda", "this is secret func");
}
static void staticSecret(){
Log.d("dasdad", "this is static secret func");
}
}
脚本代码
function CallSecretFunc(){
Java.perform(function() {
//active calling of dynamic functions
Java.choose('com.roysue.demo01.MainActivity',{
onMatch: function(instance){
instance.secret()
},
onComplete: function(){
}
})
})
}
function getTotalValue(){
Java.perform(function(){
var MainActivity = Java.use('com.roysue.demo01.MainActivity')
Java.choose('com.roysue.demo01.MainActivity',{
onMatch: function(instance){
console.log('total value = ', instance.total.value)
},
onComplete: function(){
console.log('search Complete')
}
})
})
}
setImmediate(getTotalValue)
rpc.exports = {
CallsecretFunc : CallSecretFunc,
gettotalvalue : getTotalValue
};
然后写成python脚本是
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("{*} {0}".format(message['payload']))
else:
print(message)
device = frida.get_usb_device()
process = device.attach('com.roysue.demo02')
with open('4.js') as f:
jscode = f.read()
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
command = ""
while 1 == 1:
command = input("\nEnter command:\n1:exit\n2:Call secret function\n3:Get Total Value\nchoice:")
if command == "1":
break
elif command == "2":
script.exports.callsecretfunc()
elif command == "3":
script.exports.gettotalvalue()
REPL模式:Python的交互模式
在Objection REPL界面中,使用空格键可以有提示
使用android hooking list classes
可以看内存中所有的类
使用android hooking search classes
查询包含特定关键字的类
使用android hooking search methods
可以搜索所有包含key关键字的方法
相当耗时而且报错了
使用android hooking list class_methods
查看类的所有方法。
除了上述Java类的相关内容,Android中四大组件也值得关注,Objection也提供了支持
android hooking list activities
service,receivers和providers也可以用
Hook相关命令:android hooking watch class_method
android hooking watch class_method java.io.File.$init --dump-args --dump-backtrace --dump-return
执行结果
–dump-args :打印函数的参数
–dump-backtrace :调用堆栈
–dump-return:返回值
有些函数的明文和密文非常有可能放在参数和返回值中,而打印调用栈可以让分析者快速进行调用链的溯源。
Registering job说明已经把函数加入到Objection的作用系统中了。使用jobs list就可以查看
当在手机中设置点击时,就触发了hook,看到Backtrace之后打印的调用栈,可以清楚地看到这个构造函数的调用来源。根据arguments可以看到打开的文件路径,以及文件名。测试结束后可以根据作业的ID删除,取消对函数的Hookjobs kill
除了直接Hook一个函数之外,Objection也可以通过执行android hooking watch class
对指定classname中所有函数Hook(不包括构造函数的hook)。
这里的调用顺序是自上而下的
最后学习主动调用在Objection中的应用。
对实例搜索:android heap search instances
,输出中显示的handle(句柄)十分重要,在之后的主动调用中都是以十六进制的Handle值作为实例的句柄调用和执行函数
调用实例方法:android heap execute
P114页
使用execute执行带参数的函数会报错,需要先执行以下命令
android heap evaluate
然后使用如下内容
console.log('File is canWrite? =>', clazz.canWrite())
clazz.setWritable(false)
console.log('File is canWrite? =>', clazz.canWrite())
Objection设定clazz用于代表handle值对应的实例,同时函数canWrite()用于返回这个实例所打开的文件是否可写,setWritable()用于修改对应文件是否可写的属性。
遍历手机上的所有activity列表:android hooking list activitues
启动目标活动:android intent launch_activity com.example.junior.CalculatorActivity
通过静态分析源码查找响应类或函数
可以用android hooking list class_methods com.exqmple.junior.CalculatorActivity
验证函数是否存在
之后Hook响应函数:android hooking watch class_method com.example.junior.CalculatorActivity.caculate --dump-args --dump-backtrace --dump-return
验证类是否存在:android hooking list classes
,之后可以再~/.objexction目录下的objection.log文件中找相关日志(cat objection.log ! grep com.example.junior.util.Arith
)。
之后就可以对函数实现Hook了android hooking watch class_method comexample.junior.util.Arith.sub --dump-args --dump-backtrace --dump-return
function main(){
Java.perform(function(){
var Arith = Java.use('com.example.junior.util.Arith')
Arith.sub.overload('java.lang.String','java.lang.String').implementation = function(str, str2){
var result = this.sub(str, str2)
console.log('str, str2, result =>', str, str2, result)
//print Java call stack
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
return result
}
})
}
setImmediate(main)
这里打印Java调用栈的代码,其实是将Android开发中,获取调用栈的函数Log.getStackTraceString(Throwable e)
翻译为JavaScript函数
Frida注入frida -UF -l hook.js
,-UF命令表示使用USB方式。
上面的123直接传递是不对的,正确的传入字符串参数的方法应当这样
var JavaString = Java.use('java.lang.String')
var result = this.sub(str, JavaString.$new('123'))
Frida脚本中Java函数的主动调用,如果是静态函数,只需要获取类对象即可直接完成函数的主动调用,如果是实例函数,只需要优先获取到类的实例对象即可完成函数的主动调用。
function callSub(a, b){
var Arith = Java.use('com.example.junior.Arith')
var javaString = Java.use('Java.lang.String')
var result = Arith.sub(JavaString.$new(a), JavaString.$new(b))
console.log(a, "-", b, "=", result)
}
rpc.exports = {
sub : CallSub,
};
这样就可以使用Python进行hook,大规模利用时,可以将loader.js脚本中的get_usb_device()
函数更名为get_device_manager().add_remote_device('
,即可断开adb的连接,通过网络模式对APP进行注入和hook工作。