Frida Hook(十一) Process类获取模块/内存详解

admin 2024年9月26日13:10:33评论198 views字数 7315阅读24分23秒阅读模式

基础介绍

Frida 的 Process 类是用于与目标进程进行交互的重要接口。它提供了一组方法,允许你获取有关目标进程的信息,并与进程中的各种组件(如模块、线程、内存等)进行交互。通过使用 Process 类,你可以实现动态分析、调试和逆向工程的各种任务。这篇可能会看起来无聊,所以在基础介绍章节基本也把函数用处写出来了,大家可以先收藏等用到的时候再拿出来看

Frida Hook(十一)  Process类获取模块/内存详解

该类中常用的属性和方法的功能如下所示:

  • id:返回当前进程的pid。

  • arch:返回当前进程的架构。(如 x86, x64, arm, arm64)

  • platform:返回当前进程的平台。(例如Linux、Windows)

  • pageSize:返回虚拟内存页的大小。    

  • pointerSize:返回指针的大小,32位的App应用程序中为4字节,64位的App应用程序中为8字节。

  • getCurrentThreadId():返回当前线程id。

  • findModuleByName(name):用于查找指定名称的模块(也就是共享库或动态链接库)在目标进程中的信息

  • getModuleByName(name):同findModuleByName一样,只是找不到的时候抛出异常错误

  • findModuleByAddress(address): 用于根据给定的地址查找模块,返回模块信息

  • getModuleByAddress(address):同上,只是找不到的时候抛出异常错误

  • enumerateModules():直接获取当前进程中所有的模块

  • enumerateThreads():直接获取当前线程中所有的模块

  • findRangeByAddress(address):通过地址来寻找内存范围,可以用来查看某段内存区域的基址、大小、权限等,如果找不到则返回null。该函数可以用来简易判断传入的值是否为内存地址

  • getRangeByAddress(address):同findRangeByAddress,区别是如果找不到则抛出错误。

  • setExceptionHandler(callback):设置异常回调,但发生异常的时候调用去处理异常或者打印异常

小技巧:基本上get开头的方法与find的开头的方法输入一致,功能也一样的;只是返回值有区别;如果find开头找不到模块会返回null,get开头如果找不到模块会抛出错误。因此,更推荐使用find开头

详解常用方法

通过名称获取模块信息(findModuleByName)

在 Frida 中用于查找指定名称的模块(也就是共享库或动态链接库)在目标进程中的信息。这个方法返回一个包含模块详细信息的对象,如果指定名称的模块存在于目标进程中。如果模块不存在,则返回 null。    

用处

  • 获取模块信息: 你可以使用 findModuleByName 获取模块的基地址、大小和路径等信息。

  • 模块定位: 在进行内存分析或修改时,定位特定模块的基地址非常重要。

  • 调试和逆向工程: 当你需要在特定的模块中进行函数钩取或代码注入时,确定模块的准确位置是必要的。

方法签名

Process.findModuleByName(name)

参数:

  • name (字符串): 模块的名称,例如 libc.soexample.dll

返回值:

  • 如果找到匹配的模块,返回一个模块信息对象,其中包含 basesizepath 等字段。

  • 如果未找到匹配的模块,返回 null

示例

假设你正在分析一个 Linux 应用程序,并且你想查找 libc.so 模块的基地址。你可以使用 findModuleByName 方法来实现这一点。下面是一个详细的示例:

JavaScript                  
// 使用 Frida 脚本获取 libm.so 模块的信息                  
var module = Process.findModuleByName('libm.so');                  
                 
if (module !== null) {                  
console.log('Module Name:', module.name); // 输出模块名称                  
console.log('Base Address:', module.base); // 输出模块基地址                  
console.log('Size:', module.size); // 输出模块大小                  
console.log('Path:', module.path); // 输出模块路径                  
} else {                  
console.log('Module not found');                  
}
       

在这个示例中,脚本会尝试找到名为 libm.so 的模块。如果模块存在,脚本将输出该模块的基本信息,包括名称、基地址、大小和路径。如果模块不存在,则输出 "Module not found"。

通过内存地址获取模块信息(findModuleByAddress)

findModuleByAddress 方法是 Frida 中的一个功能,用于根据给定的地址查找模块。这个方法非常有用,特别是在你知道一个特定地址并且需要找到包含该地址的模块时。通过这种方式,你可以获取关于模块的详细信息,如模块的基地址和大小等。

方法签名

Process.findModuleByAddress(address)

参数:

  • address (指针): 一个表示内存地址的指针,该地址必须是目标进程中的有效地址。

返回值:

  • 如果找到包含指定地址的模块,返回一个模块信息对象,其中包含 namebasesizepath 等字段。

  • 如果未找到包含指定地址的模块,返回 null

用处

定位模块: 通过指定地址,你可以找到包含该地址的模块,这在进行内存操作或分析时非常有用。

调试和逆向工程: 在调试时,如果你知道一个特定地址并需要了解它属于哪个模块,findModuleByAddress 可以帮助你定位相关模块。    

内存分析: 了解特定地址所在的模块可以帮助你更好地理解和操作该模块的内存区域。

示例

假设你正在分析一个 Linux 应用程序,并且你想查找包含地址 0x7f1234567890 的模块。你可以使用 findModuleByAddress 方法来实现这一点。下面是一个详细的示例:

JavaScript                  
// 使用 Frida 脚本查找包含特定地址的模块                  
var address = ptr('0x7f1234567890'); // 替换为你感兴趣的实际地址                  
var module = Process.findModuleByAddress(address);                  
                 
if (module !== null) {                  
console.log('Module Name:', module.name); // 输出模块名称                  
console.log('Base Address:', module.base); // 输出模块基地址                  
console.log('Size:', module.size); // 输出模块大小                  
console.log('Path:', module.path); // 输出模块路径                  
} else {                  
console.log('No module contains the given address');                  
}                  

详细说明

获取模块信息:

使用 Process.findModuleByAddress(address) 查找指定地址所在的模块。

如果模块存在,返回的对象将包含模块的 namebase(模块的基地址)、size(模块的大小)和 path(模块的路径)。

输出模块信息:

输出模块的名称、基地址、大小和路径,以便进一步分析和调试。

处理未找到模块的情况:

如果指定地址不在任何模块内,返回 null。在这种情况下,脚本将输出 "No module contains the given address"。

获取当前进程中所有的模块(enumerateModules)

方法签名

JavaScript                  
Process.enumerateModules()

参数:

  • 无需参数。

返回值:

  • 返回一个包含目标进程中所有模块信息的数组。每个模块的信息是一个对象,包含模块的详细信息,如名称、基地址、大小和路径。

用处

获取模块信息: 可以列出目标进程中所有加载的模块,获取它们的名称、基地址、大小和路径等信息。

调试和逆向工程: 在调试过程中,了解所有加载的模块有助于你找到感兴趣的模块,进行进一步分析或钩取。

内存分析: 了解每个模块的基地址和大小可以帮助你在内存中找到模块的具体位置。

示例

假设你正在分析一个应用程序,以下是如何使用 enumerateModules 方法来获取目标进程中的所有模块信息:

JavaScript                  
// 列出目标进程中所有的模块                  
var modules = Process.enumerateModules();                  
                 
modules.forEach(module => {                  
console.log('Module Name:', module.name);// 模块名称,例如 "libc.so"                  
console.log('Base Address:', module.base);// 模块基地址,例如 "0x7f1234560000"                  
console.log('Size:', module.size);// 模块大小,例如 "0x100000"                  
console.log('Path:', module.path);// 模块路径,例如 "/system/lib/libc.so"                  
});                  

详细说明    

获取模块信息:

通过 Process.enumerateModules() 可以获取到目标进程中所有加载的模块的信息。

每个模块的信息对象通常包括 name(模块名称)、base(模块基地址)、size(模块大小)和 path(模块路径)。

输出模块信息:

你可以遍历所有模块,输出每个模块的详细信息,以便进行进一步的分析或调试。

获取当前线程中所有的模块(enumerateThreads)

方法签名

JavaScript                  
javascript                  
复制代码                  
Process.enumerateThreads()

参数:

  • 无需参数。

返回值:

  • 返回一个包含目标进程中所有线程信息的数组。每个线程的信息是一个对象,包含线程的详细信息,如线程 ID 和上下文信息。

用处

获取线程信息: 可以列出目标进程中所有线程,获取它们的线程 ID 和上下文信息。

调试和分析: 在调试过程中,了解线程的上下文可以帮助你理解线程的执行状态,进行有效的调试。

逆向工程: 获取线程信息可以帮助你分析线程行为,特别是在多线程应用程序中,了解各个线程的状态是关键。    

示例

假设你正在调试一个多线程应用程序,以下是如何使用 enumerateThreads 方法来获取目标进程中的所有线程信息:

Bash                  
// 列出目标进程中的所有线程                  
var threads = Process.enumerateThreads();                  
                 
threads.forEach(thread => {                  
console.log('Thread ID:', thread.id);// 线程 ID,例如 "1234"                  
console.log('Context:', thread.context);// 线程上下文信息,例如寄存器的状态                  
});                  

详细说明

获取线程信息:

通过 Process.enumerateThreads() 可以获取到目标进程中所有线程的信息。

每个线程的信息对象通常包括 id(线程 ID)和 context(线程上下文),其中上下文信息包含寄存器的状态和其他执行状态信息。

输出线程信息:

可以遍历所有线程,输出每个线程的 ID 和上下文信息,以便了解线程的执行状态。

通过内存地址获取内存段(findRangeByAddress)

方法签名

JavaScript                  
var range = Process.findRangeByAddress(address);

参数

address (Type: NativePointer): 目标内存地址。可以是通过 ptr 函数创建的指针对象。    

返回值

返回一个包含内存范围信息的对象,如果该地址在任何已分配的内存范围中。否则返回 null

用处

Process.findRangeByAddress 方法用于查找指定地址所在的内存范围。该方法适用于以下情况:

确认内存地址有效性:在进行内存读写或其他操作之前,确保目标地址在有效的内存范围内。

获取内存范围信息:了解内存范围的起始地址、大小、保护属性和映射文件,以便进一步操作或调试。

异常处理:在脚本中避免访问无效内存地址,减少因非法操作引起的崩溃。

举例

以下示例展示了如何使用 Process.findRangeByAddress 方法来查找并显示指定地址所在的内存范围信息:

Bash                  
// 目标内存地址                  
var address = ptr("0x7ffdf000");                  
                 
// 查找包含该地址的内存范围                  
var range = Process.findRangeByAddress(address);                  
                 
if (range !== null) {                  
console.log("Memory range found:");                  
console.log("Base address: " + range.base);                  
console.log("Size: " + range.size + " bytes");                  
console.log("Protection: " + range.protection);                  
if (range.file !== undefined) {                  
console.log("Mapped file: " + range.file);                  
}                  
} else {                  
console.log("No memory range contains the address " + address);                  
}                  
       

在这个例子中,我们首先定义了一个目标内存地址,然后使用 Process.findRangeByAddress 查找该地址所在的内存范围。脚本会输出该范围的基本信息,如起始地址、大小、保护属性等。如果地址不在任何内存范围内,则返回 null 并输出相应消息。

异常回调处理(setExceptionHandler)

方法签名

JavaScript                  
Process.setExceptionHandler(callback);

参数

callback (Type: function): 异常处理的回调函数,当进程中发生异常时会被调用。这个回调函数接收一个参数 details,包含异常的相关信息。

返回值

无返回值(void)。

用处

Process.setExceptionHandler 方法用于在目标进程中设置一个全局异常处理器。当进程遇到未处理的异常(如访问违例、非法指令等)时,Frida 会触发这个异常处理器。此功能对于调试和分析程序中的崩溃原因非常有用。你可以使用它来捕捉异常,并在异常发生时执行自定义逻辑,例如记录崩溃信息、调试跟踪或尝试恢复程序执行。

举例

以下示例展示了如何使用 Process.setExceptionHandler 方法来捕捉并处理进程中的异常:

Bash                  
// 设置全局异常处理器                  
Process.setExceptionHandler(function(details) {                  
console.log("Exception caught!");                  
console.log("Address: " + details.address);// 异常发生的地址                  
console.log("Type: " + details.type);// 异常类型(如 'access-violation')                  
console.log("Memory operation: " + details.memory.operation); // 内存操作类型(如 'read'、'write')                  
console.log("Memory address: " + details.memory.address); // 试图访问的内存地址                  
                 
// 返回 false 继续执行默认处理程序(可能导致进程崩溃)                  
// 返回 true 阻止默认处理程序(可以用于调试,避免进程崩溃)                  
return true;                  
});                  
       

在这个示例中,我们使用 Process.setExceptionHandler 设置了一个全局异常处理器。当进程中发生异常时,Frida 会调用提供的回调函数,并传递一个 details 对象,包含异常的详细信息。这个回调函数可以根据异常信息执行自定义逻辑,比如记录异常发生的地址、异常类型和内存操作细节。你可以通过返回 true 来阻止默认的异常处理程序(避免进程崩溃),或者返回 false 以允许默认处理程序继续执行(通常会导致进程终止)。    

原文始发于微信公众号(暴暴的皮卡丘):Frida Hook(十一) Process类获取模块/内存详解

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月26日13:10:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Frida Hook(十一) Process类获取模块/内存详解https://cn-sec.com/archives/3118642.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息