Frida学习
本文最后更新于 576 天前,其中的信息可能已经有所发展或是发生改变。

知识

  • 网上对frida的检测通常会使用openat、open、strstr、pthread_create、snprintf、sprintf、readlinkat等一系列函数。

命令记录

# frida启动
adb shell
su
/data/local/tmp/frida-server-16.4.7-android-arm64

# 端口转发
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

# 查看应用pid
frida-ps -U

# 命令注入
frida -U -f '小黑盒' -l xhh.js

frida hook模板

Python模板

import frida
import sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)       

with open('./webview.js','r', encoding='UTF-8') as f:
    jscode1 = f.read()

#process = frida.get_remote_device().enumerate_processes()
#print(process)
# get_usb_device改成get_remote_device方法,get_usb_device有的电脑会报错
process = frida.get_remote_device().attach('蜜雪冰城')  # 'App名称', 或 App应用的 Process Pid 15325
script = process.create_script(jscode1)  # 把js的hook脚本注入到进程里面
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()

frida Hook调用的so的函数

function hook_dlsym() {
    var count = 0
    console.log("=== HOOKING dlsym ===")
    var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"),
        {
            onEnter: function (args) {
                const name = ptr(args[1]).readCString()
                // const module = Process.findModuleByAddress(ptr(this.returnAddress))
                console.log("[dlsym]", name)
                if (name == "pthread_create") {
                    count++
                }
            }
        }
    )
    return Interceptor
}

function hook_dlopen() {
    var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    console.log("[LOAD]", path)
                    if (path.indexOf("libmsaoaidsec.so") > -1) {
                        hook_dlsym()
                    }
                }
            },
        }
    )
    return interceptor
}

var dlopen_interceptor = hook_dlopen()

hook so层函数模板

function hook_sub_A6854(){
    Java.perform(function () {

        var a45d = Module.findExportByName("libhbsecurity.so")
        a45d = a45d.add(0xA6854)
        Interceptor.attach(a45d,{
            onEnter:function(arg){
                console.log(arg[1].readCString())
            },
            onLeave:function(ret){
                console.log(ret.readCString())
            }
        })

    });
}

registerNative

第一种

这个函数的作用就不赘述了;因为从第三个参数能看到jni函数的映射关系,而很多加解密函数都是Java层声明、在so层实现的,所以这个函数格外重要;下面这段代码可以动态获取registerNative函数地址,并且打印第三个参数的内容:

function hook_libart() {
    var module_libart = Process.findModuleByName("libart.so");
    var symbols = module_libart.enumerateSymbols();     //枚举模块的符号

    var addr_GetStringUTFChars = null;
    var addr_FindClass = null;
    var addr_GetStaticFieldID = null;
    var addr_SetStaticIntField = null;
    var addr_RegisterNatives = null;        

    for (var i = 0; i < symbols.length; i++) {
        var name = symbols[i].name;
        if (name.indexOf("art") >= 0) {//动态获取各个函数的地址
            if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) {
                if (name.indexOf("GetStringUTFChars") >= 0) {
                    console.log(name);
                    addr_GetStringUTFChars = symbols[i].address;
                } else if (name.indexOf("FindClass") >= 0) {
                    console.log(name);
                    addr_FindClass = symbols[i].address;
                } else if (name.indexOf("GetStaticFieldID") >= 0) {
                    console.log(name);
                    addr_GetStaticFieldID = symbols[i].address;
                } else if (name.indexOf("SetStaticIntField") >= 0) {
                    console.log(name);
                    addr_SetStaticIntField = symbols[i].address;
                } else if (name.indexOf("RegisterNatives") >= 0) {
                    console.log(name);
                    addr_RegisterNatives = symbols[i].address;
                }
            }
        }
    }

    if (addr_RegisterNatives) {
        Interceptor.attach(addr_RegisterNatives, {
            onEnter: function (args) {
                console.log("addr_RegisterNatives:", hexdump(args[2]));    //打印第三个参数,也就是java和native映射的数组首地址
                console.log("addr_RegisterNatives name:", ptr(args[2]).readPointer().readCString())//java层函数名称
                console.log("addr_RegisterNatives sig:", ptr(args[2]).add(Process.pointerSize).readPointer().readCString());//函数参数
                console.log("addr_RegisterNatives addr:", ptr(args[2]).add(Process.pointerSize+Process.pointerSize));//native函数入口地址
            }, onLeave: function (retval) {

            }
        });
    }
}

 注意:因为一个jni函数注册只调用一次registerNative,所以这里建议用frida -U --no-pause -f com.xxxx.xxxx -l xxxx.js命令注入js,同时启动目标app;如果人为开启目标app,再运行frida,可能regiserNative函数已经执行过了!

第二种

function hook_RegisterNatives() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrRegisterNatives = null;
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];

        //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
        if (symbol.name.indexOf("art") >= 0 &&
                symbol.name.indexOf("JNI") >= 0 && 
                symbol.name.indexOf("RegisterNatives") >= 0 && 
                symbol.name.indexOf("CheckJNI") < 0) {
            addrRegisterNatives = symbol.address;
            console.log("RegisterNatives is at ", symbol.address, symbol.name);
        }
    }

    if (addrRegisterNatives != null) {
        Interceptor.attach(addrRegisterNatives, {
            onEnter: function (args) {
                console.log("[RegisterNatives] method_count:", args[3]);
                var env = args[0];
                var java_class = args[1];
                var class_name = Java.vm.tryGetEnv().getClassName(java_class);
                //console.log(class_name);

                var methods_ptr = ptr(args[2]);

                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));

                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));

                }
            }
        });
    }
}

setImmediate(hook_RegisterNatives);

frida将so中地址转换为字符串

function jstring2Str(jstring) { //从frida_common_funs.js中copy出来
    var ret;
    Java.perform(function() {
        var String = Java.use("java.lang.String");
        ret = Java.cast(jstring, String);//jstring->String
    });
    return ret;
 }

java层遍历hashmap

//不能遍历空map
function iterMap(map){
    var map_result = '';
    var keyset = map.keySet();
    var it = keyset.iterator();
    while(it.hasNext()){
        var keystr = it.next().toString();
        var valuestr = map.get(keystr).toString();
        map_result += valuestr;
    }
    return map_result
}

算法自吐脚本

算法自吐

Webview

关于webview:现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝、京东、聚划算等等。

那么如何对webview进行hook呢?

首先,先贴出frida对webview进行hook的js模板

Java.perform(function (){

var WebView = Java.use("android.webkit.WebView");

WebView.setWebContentsDebuggingEnabled.overload("boolean").implementation = function (s) {

// send(s.toString());

console.log("webview hook")

// this.loadUrl.overload("java.lang.String").call(this, s);
// console.log(this.)

this.setWebContentsDebuggingEnabled(true)

};

});

然后就运行hook的python模板代码,然后在浏览器(这里使用edge浏览器)输入edge://inspect/#devices ,然后就可以看到webview的信息,点击inspect按钮就会进入到DevTools界面,然后就可以对其进行调试操作了。

遇到的问题

frida注入进程报错Failed to spawn: unable to find application with identifier的一种解决思路
frida-ps -U -a 得到 identifier
然后
frida -U --pause -f com.max.xiaoheihe -l xhh.js注入

jadx

IDA

IDA动态调试

在b站上看到视频关于ida的动态调试,对整体的思路有了一定的认识。【非常详细-IDA动态调试安卓.so文件】
其中用到了Android Studio的ddms,但是在网上搜索ddms,发现早已被移除,在该视频评论下看到博主说用monitor,于是搜集关于monitor的资料,最终下载了Android Studio3.1版本,安装好后在SDK的tools文件夹下找到monitor.bat文件,双击运行报错,查找资料发现是java版本不对,于是安装java1.8,也就是java se8,配置好环境变量后重新运行monitor.bat,界面正常出现。后面步骤就是启动android_server64服务,并设置端口转发,接着就是下面的操作。

adb shell am start -D -n com.max.xiaoheihe/com.max.xiaoheihe.MainActivity

根据步骤输入此命令后报错,改为输入命令:

adb shell monkey -p com.max.xiaoheihe -c android.intent.category.LAUNCHER 1

后正常出现Waiting For Debugger弹窗,接着输入命令:

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=9675
#注:9675这个端口为在monitor中看到的app的端口

后monitor界面中红色的蜘蛛变为绿色,点击IDA中的运行按钮,直至弹窗消失。

伪指令及DC系列指令

ARM 伪指令它不是ARM 指令集中的指令,只是为了方便编译器编程而定义的指
令,使用时可以像其他ARM 指令一样使用,但在编译时这些指令将被等效的ARM
指令代替。

DCB它关联的伪指令有DCB、DCW、DCD、DCQ指令。它们都是用于分配一段内存单元,并对其进行做初始化工作。不过它们分配的内存空间大小不同。
下面就针对这四个伪指令做下区分
DCB表示:它分配一段字节的内存单元,它每个操作数都占有一个字节,操作数范围为-128~255的数值或字符串。

DCW表示:它分配一段半字的内存单元,它的每个操作数都占有两个字节,操作数是16位二进制数,取值范围为-32768~65535。

DCD表示:它分配一段字的内存单元,它的每个操作数都占有4个字节,操作数可以是32位的数字表达式,也可以是程序中的标号。

DCQ表示:它分配一段双字的内存单元,它的每个操作数都占有8个字节。

经验

1.JNI_OnLoad

如果打开so之后发现没有Java_xxx这样的函数开头一般都是在JNI_OnLoad中采用了动态注册方式,所以只需要找到JNI_OnLoad函数,然后找到RegisterNatives函数即可

我们如果手动注册过Native方法,都知道RegisterNatives函数的三个参数含义:
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
第一个参数:需要注册native函数的上层Java类第二个参数:注册的方法结构体信息第三个参数:需要注册的方法个数这里当然是重点看第二个参数,这里当然也需要知道方法结构体信息:

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

结构体包含三部分分别是:方法名、方法的签名、对应的native函数地址;

转载请注明:
作者:syy,出处:https://www.94i.top/index.php/2024/08/01/frida学习/
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇