frida从入门到入门

  1. 1. 入门Frida
    1. 1.1. 安装
    2. 1.2. 注入方式
    3. 1.3. Android
      1. 1.3.1. Hook Java
        1. 1.3.1.1. 主动调用函数
        2. 1.3.1.2. hook参数&返回值
        3. 1.3.1.3. hook被混淆的方法
        4. 1.3.1.4. 类型转换
        5. 1.3.1.5. 调用DEX类
        6. 1.3.1.6. 注册java类
        7. 1.3.1.7. 打印堆栈
        8. 1.3.1.8. rpc远程调用
      2. 1.3.2. Hook Nagtive
        1. 1.3.2.1. 判断指令集
        2. 1.3.2.2. hook so 函数
        3. 1.3.2.3. 将so函数注册到js中
      3. 1.3.3. 工具&插件
        1. 1.3.3.1. objection的使用
        2. 1.3.3.2. frida-trace的使用
        3. 1.3.3.3. frida-gadget
    4. 1.4. Windows
    5. 1.5. Ios
  2. 2. frida进阶
    1. 2.1. Stlker
    2. 2.2. socket
  3. 3. anti-frida
    1. 3.1. 检测方案
    2. 3.2. 定位检测so的位置
    3. 3.3. 分析、还原动态注册函数
  4. 4. Android脱壳应用

入门Frida

安装

需要python环境
pip install frida-tools 终端输入该命令即可完成安装

注入方式

frida -l xx.js 进程ID|进程名|-F 注入到现有进程(时机较晚)
frida -l xx.js -f 文件目录 --no-pause 创建进程后注入(时机较早)
延迟注入可使用js代码实现

1
setTimeout(函数,延时毫秒)

Android

Hook Java

Hook java层首先需要被包裹在Java.perform(function(){})中,当需要调用Java类时使用Java.use('类名')来调用其中$init表示构造函数,$new表示实例化对象.overload('type')表示重载函数成员方法和成员属性名字相同时默认hook成员方法,如需hook成员属性在成员属性前加“_”来表示

主动调用函数

  • 静态调用
    • 使用Java.use().class.implementation=function(){hook代码}
  • 动态调用
    • 使用choose(class,callback)其中callback有两个属性
      • onMatch:function(instance),onMatch表示进入hook,instance表示在内存中找到的实例
      • onComplete:function(),表示完成hook

hook参数&返回值

部分情况下直接打印返回值并不能直观的体现需要通过类型转换得到需要的数据,如DumpHex,可以通过$className得到class的属性后在转换(可能会得到undefind),如果为Json类的可以直接用JSON.Stringify()转换成字符串

hook被混淆的方法

有些被混淆方法名可能是不可视的字符如如果以这个字符命名的函数是合法的但是又有可能因为字符集的原因看不到函数名,这就要将其编码打印出来再通过打印的字符来调用此方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}
)

类型转换

Java.cast(arg,type)强转类型

调用DEX类

如果有现成的安卓源码可以将其编译后解压apk得到class.dex,使用Java.openClassFile('dex路径').lord()加载dex类

注册java类

如果不想调用dex,可以将java代码翻译成js代码用

1
2
3
4
5
6
7
Java.registerClass({
neme:'加载类的名字不要和已有的类明重复'
implements: 可选,
methods: {
类的属性和方法
}
})

打印堆栈

调用安卓自带的API实现打印堆栈

1
2
3
4
function showStacks() {
send('调用栈输出
' + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

rpc远程调用

frida支持在js代码中使用rpc.exports关键字对js函数进行导出,导出方法可在python代码中通过script.exports.导出符号()进行调用

1
2
3
4
5
6
7
8
9
10
11
12
function Hook10(){
Java.perform(function(){
console.log("Frida Test Hook10");
// 主动调用类静态方法
var clszz = Java.use("cn.gemini.k.fridatest.FridaHook1");
console.log("func3_verify_static ret:"+clszz.func3_verify_static(">>>pwd<<<"));
});
}

rpc.exports = {
exporthook10: Hook10 //将方法导出为exporthook10符号,这里要注意导出名千万不能有大写字母或下划线
};

通过python调用

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
26
27
28
29
30
31
32
33
34
35
import frida
import sys

# 目标包名
appPacknName = "cn.gemini.k.fridatest"
scriptFile = "hook_script.js"

# 输出日志的回调方法
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)

device = frida.get_usb_device()
# spawn模式,找到目标包名并重启,在启动前注入脚本
pid = device.spawn([appPacknName])
session = device.attach(pid)
# 注意这里需要将device.attach(pid)这句代码写在前面,这样执行才符合预期(启动时程序白屏,等待下面这行代码来恢复执行)
# 其实在https://www.jianshu.com/p/b833fba1bffe这篇文章中有提到
device.resume(pid)

# 方式一: 通过js文件创建hook代码
with open(scriptFile, encoding='UTF-8') as f:
script = session.create_script(f.read())
# 方式二: 直接将hook代码写在python文件中
# script = session.create_script(js_code)

script.on("message", on_message)
script.load() # 把js代码注入到目标应用中
# 避免结束
# sys.stdin.read()

# frida RPC测试
script.exports.exporthook10()

Hook Nagtive

判断指令集

可以利用IDA 判断来Thumb指令集和Arm 指令集

  1. IDA - Options - General - number of opcode bytes - 设置为 4
  2. 查看 IDA VIew 中 opcode 的长度, 如果出现 2 个字节和 4 个字节的, 说明为 thumb 指令集
  3. 如果都是 4 个字节的, 说明是 arm 指令集
  4. 在 Thumb 指令集下, inline hook 的偏移地址需要进行 +1 操作

hook so 函数

hook so 函数首先需要知道该函数所在的内存地址,通过Module.findExportByName(moduleName|null, exportName)获取模块基址后加上偏移得到函数地址,用
Interceptor.attrach(addr,callbcak)hook函数的返回值&参数。
callback有一下两个属性
onEnter:function(arg)可以在这里修改arg参数的值
onLeave:function(retvalue)在这里修改函数的返回值注意:需要用retvalue.replace()来修改返回值

将so函数注册到js中

new Nativefunction(addr,retType,[argsType])

  • address : 函数地址
  • returnType : 指定返回类型
  • argTypes : 数组指定参数类型
  • 类型可选: void, pointer, int, uint, long, ulong, char, uchar, float, double, int8, uint8, int16, int32, uint32, int64, uint64; 参照函数所需的 type 来定义即可;
    参数内容需要开辟空间,注意类型,若无参数argType填[]即可

配合Interceptor.replace(ptr,new NativeCallback(function(){}),retType,[argsType])实现主动调用修改函数参数返回值

工具&插件

objection的使用

objection实现Java层hook everywhere,内存漫游,内存搜索,来达到快速逆向的目的

objection -g [packageName] explore打开交互界面调试该应用

命令 说明
memory list modules 枚举当前进程模块
memory list exports [lib_name] 查看指定模块的导出函数
memory list exports libart.so —json /root/libart.json 将结果保存到json文件中
memory search —string —offsets-only 搜索内存
search instances search instances com.xx.xx.class 堆内存中搜索指定类的实例, 可以获取该类的实例id
android heap execute [ins_id] [func_name] 直接调用指定实例下的方法
android heap execute [ins_id] 自定义frida脚本, 执行实例的方法
android root disable 尝试关闭app的root检测
android root simulate 尝试模拟root环境
android ui screenshot [image.png] 截图
android ui FLAG_SECURE false 设置FLAG_SECURE权限
android hooking list classes 列出内存中所有的类
android hooking search classes [search_name] 在内存中所有已加载的类中搜索包含特定关键词的类
android hooking search methods [search_name] 在内存中所有已加载的方法中搜索包含特定关键词的方法
android hooking generate simple [class_name] 生成hook代码
android hooking watch class_method com.xxx.xxx.methodName —dump-args —dump-backtrace —dump-return hook指定方法, 如果有重载会hook所有重载,如果有疑问可以看—dump-args : 打印参数—dump-backtrace : 打印调用栈—dump-return : 打印返回值
android hooking watch class com.xxx.xxx hook指定类, 会打印该类下的所有调用
android hooking set return_value com.xxx.xxx.methodName false 设置返回值(只支持bool类型)
android hooking list activities 枚举activity
android intent launch_activity [activity_class] 启动activity
android hooking list services 枚举services
android intent launch_service [services_class] 启动services
jobs list 查看任务列表
job kill [id] 删除任务
android sslpinning disable 关闭ssl校验
help android clipboard 获取Android剪贴板服务上的句柄并每5秒轮询一次用于数据。 如果发现新数据,与之前的调查不同,则该数据将被转储到屏幕上。
help android shell_exec [command] 执行命令

frida-trace的使用

frida-trace是一个动态跟踪函数调用的工具

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
26
27
用法:frida-trace[选项]target

选项:
——version显示程序的版本号和退出
-h,——help显示这个帮助信息和退出
-D ID,——device=ID连接到设备与给定的ID
-U,——usb连接到usb设备
-R,——远程连接到远程frida-server
-h HOST,——HOST =主机连接到远程frida-server在HOST上
-f FILE,——FILE =FILE spawn FILE
-F,——attach-frontmost附加到前端应用程序
-n NAME,——attach- NAME =NAME附加到NAME -p PID,——attach
- PID =PID附加到PID
—stdio=inherit|pipe stdio行为spawning时(默认为“inherit”)
—aux=选项设置spawn时的辅助选项,如“uid=(int)42”(支持的类型有:-O FILE,——options- FILE =FILE通过文本文件传递命令行选项
-I MODULE,——include- MODULE =MODULE include
-X MODULE,——exclude- MODULE =MODULE exclude MODULE -I FUNCTION,——include=FUNCTION include [MODULE]![FUNCTION]
-x FUNCTION,——exclude=FUNCTION排除[MODULE]![功能]
-a MODULE!OFFSET,模块!偏移
-m OBJC_METHOD,——include- MODULE -imports=MODULE include模块导入
-m OBJC_METHOD,——exclude-objc-method=OBJC_METHOD排除OBJC_METHOD
-j JAVA_METHOD,——include-java-method=JAVA_METHOD排除JAVA_METHOD
-J JAVA_METHOD,——exclude-java-method=JAVA_METHOD排除JAVA_METHOD
-s DEBUG_SYMBOL,——include-debug-symbol=DEBUG_SYMBOL包括DEBUG_SYMBOL -q,
-S PATH,——init-session=用于初始化会话的JavaScript文件的路径
-P PARAMETERS_JSON,——parameters=PARAMETERS_JSON参数为JSON,暴露为一个名为'parameters'的全局参数
-o output,——output= output将消息转储到文件

frida-gadget

Windows

Ios

frida进阶

Stlker

socket

anti-frida

检测方案

  • 文件名检测

  • 端口检测

  • 线程检测

  • Maps记录检测

  • TraceId检测

  • 内存检测

前两种检测很简单就可以过掉,修改文件名/端口即可

其他具体问题具体分析

定位检测so的位置

Android调用so库会使用dlopen函数,只需要hook这个函数,断在哪个so,就是那个so

安卓使用 System.loadLibrary("xxx")加载so函数,而它的上层函数是loadLibrary0(),再往上追可以发现是doload()返回方法netiveload()跟进去JVM_NativeLoad最终发现是dlopen()

frida-trace -U -f 包名 -i android_dlopen_ext

分析、还原动态注册函数

打开 idapro 载入so选择导出函数,找到JNI_Onload还原伪c,修改函数参数类型JNIEnv*,或者在GetEnv()第二个参数修改成JNIEnv* 修改后识别出函数名寻找RegisterNatives()第三个参数即为动态注册的函数

Android脱壳应用