【Android】Frida Hook 文件读写操作

前言

在挖掘客户端漏洞的时候,通常会关注应用对什么文件进行了读写操作,当我们能控制被读的文件或观测到敏感写入的文件,通常可以造成一定危害。本文详细介绍了如何通过frida监控文件读写操作。

相关知识

1. 读写相关调用api

在Linux系统下,文件的创建、读取、追加、写入和删除等操作涉及到以下系统调用:

  • open:打开文件,用于创建和打开文件。
  • fopen: 打开文件
  • read:从文件中读取数据。
  • write:向文件中写入数据。
  • lseek:移动文件指针。
  • close:关闭文件。
  • unlink:删除文件。

2. Interceptor.attach

在Frida中,**Interceptor.attach()**是一个用于拦截函数调用的函数。它可以让我们在目标进程中拦截任意函数调用,并在函数调用前或函数调用后执行自定义的JavaScript代码。

Interceptor.attach()函数的语法如下:

Interceptor.attach(target, callbacks)

其中,target参数指定要拦截的函数,可以是函数名或函数地址。callbacks参数是一个回调函数对象,用于定义拦截函数调用时要执行的代码。回调函数对象可以包含以下函数:

  • onEnter(args):在函数调用之前执行的回调函数,args参数是一个数组,包含了函数调用时的参数。
  • onLeave(retval):在函数调用之后执行的回调函数,retval参数是函数调用的返回值。

3. Interceptor

在Frida中,Interceptor对象是一个用于拦截函数调用和修改函数实现的工具集。除了**Interceptor.attach()**函数外,Interceptor对象还提供了以下几个常用的方法:

  • Interceptor.replace(target, replacement):用于替换函数实现。target参数指定要替换的函数,可以是函数名或函数地址;replacement参数是一个JavaScript函数,用于替换原函数的实现。替换函数的参数和返回值需要和原函数保持一致。以下是一个使用Interceptor.replace()函数替换strcmp()函数实现的示例:
// Replace the implementation of strcmp() 
function Interceptor.replace(
	Module.findExportByName("libc.so", "strcmp"), 
	new NativeCallback(function (str1, str2) {   
		console.log("strcmp() called with arguments: " + Memory.readUtf8String(str1) + ", " + Memory.readUtf8String(str2));   
		return 0; 
	}, 'int', ['pointer', 'pointer'])); 
  • Interceptor.attachOnce(target, callbacks):用于一次性拦截函数调用。与Interceptor.attach()函数不同,Interceptor.attachOnce()函数只会拦截一次函数调用,然后自动解除拦截。这个方法的参数和Interceptor.attach()函数相同。

  • Interceptor.detachAll():用于解除所有拦截器。这个方法会解除所有通过Interceptor.attach()和Interceptor.attachOnce()函数添加的拦截器。

  • Interceptor.flush():用于刷新所有修改过的函数实现。在修改函数实现后,需要调用Interceptor.flush()函数来刷新目标进程中的函数缓存。这样做可以确保修改后的函数实现能够生效。

4. Module.findExportByName()

Module.findExportByName()是一个用于查找指定模块中导出函数地址的函数。它可以通过模块名称和函数名来查找函数地址,并返回一个指向函数地址的NativePointer对象。

Module.findExportByName()函数的语法如下:

Module.findExportByName(moduleName, symbolName)

其中,moduleName参数是要查找的模块名称,可以是模块文件名或模块名称字符串;symbolName参数是要查找的函数名称,可以是函数名字符串或函数地址。需要注意的是,如果moduleName是模块文件名,则需要包含文件路径信息,并且路径分隔符需要使用反斜杠(\)转义。

如果第一个参数是NULL,则表示在全局进行搜索函数。

以下是一个使用Module.findExportByName()函数查找libc.so模块中的printf()函数地址的示例:

var printfAddr = Module.findExportByName("libc.so", "printf");
console.log("printf() address: " + printfAddr);

在上述示例中,我们使用Module.findExportByName()函数查找libc.so模块中的printf()函数地址,并将结果输出到控制台。

需要注意的是,Module.findExportByName()函数只能在已经加载的模块中查找函数地址。如果要查找未加载的模块中的函数地址,可以使用Module.load()函数动态加载模块,并使用Module.getExportByName()函数查找函数地址。

监控文件读写的Frida脚本

基于上述相关知识,写出下列frida脚本

function hook_file_read() {

    // Hook open、fopen、read函数
    Interceptor.attach(Module.findExportByName(null, "open"), {
        onEnter: function (args) {
            var path = Memory.readUtf8String(args[0]);
            this.path = path;
        },
        onLeave: function (retval) {
            if (retval > 0) {
                console.log("[open] path: " + this.path);


            }
        }
    });

    Interceptor.attach(Module.findExportByName(null, "fopen"), {
        onEnter: function (args) {
            var path = Memory.readUtf8String(args[0]);
            this.path = path;
        },
        onLeave: function (retval) {
            if (retval != 0) {
                console.log("[fopen] path: " + this.path);

            }
        }
    });

    Interceptor.attach(Module.findExportByName(null, "read"), {
        onEnter: function (args) {
            this.fd = args[0].toInt32();
            this.buf = args[1];
            this.size = args[2].toInt32();
        },
        onLeave: function (retval) {
            if (retval > 0 && this.fd > 0) {
                var data = Memory.readByteArray(this.buf, retval >>> 0);
                console.log("[read] fd: " + this.fd + ", size: " + retval + ", data: " + data);
            }
        }
    });
    // 3. Hook the write system call
    Interceptor.attach(Module.findExportByName(null, "write"), {
        onEnter: function (args) {
            // Get the file descriptor and buffer address
            var fd = args[0].toInt32();
            var buf = args[1];

            // Log the write call
            console.log("[write] fd: " + fd + ", buf: " + buf);
        }
    });

    // 4. Hook the lseek system call
    Interceptor.attach(Module.findExportByName(null, "lseek"), {
        onEnter: function (args) {
            // Get the file descriptor and offset
            var fd = args[0].toInt32();
            var off = args[1].toInt32();

            // Log the lseek call
            console.log("[lseek] fd: " + fd + ", offset: " + off);
        }
    });

    // 5. Hook the close system call
    Interceptor.attach(Module.findExportByName(null, "close"), {
        onEnter: function (args) {
            // Get the file descriptor
            var fd = args[0].toInt32();

            // Log the close call
            console.log("[close] fd: " + fd);
        }
    });

    // 6. Hook the unlink system call
    Interceptor.attach(Module.findExportByName(null, "unlink"), {
        onEnter: function (args) {
            // Get the file path
            var path = Memory.readUtf8String(args[0]);

            // Log the unlink call
            console.log("[unlink] path: " + path);
        }
    });


}

// 调用hook_file_read函数
setImmediate(hook_file_read);

运行实例:
【Android】Frida Hook 文件读写操作_第1张图片

后话

Interceptor.attach非常强大,主要还是看挖掘思路,往往能起到非常好的辅助效果。

你可能感兴趣的:(frida,Android,逆向,文件读写,Hook)