上文中我们使用C语言编写了一个简单的程序,并且使用OllyDbg逆向查看每一个函数的入口地址。并且使用调用堆栈查看调用每一个函数前的汇编指令。通过观察在调用每一个函数前汇编语言是如何操作和准备输入参数的,我们就可以编写HOOK程序,截取函数调用过程中的数据。本文将以《使用OllyDbg逆向查找HOOK地址和寄存器使用》中的C语言作为样例,编写一个HOOK程序实现内存注入,截取函数h()的两个参数,并打印在屏幕上。
Frida简介
Frida是一个跨语言的框架。使用Frida,可以编写各种平台的HOOK程序。目前网上有很多基于Frida的教程,主要覆盖如何实现Android程序的HOOK。目前Frida支持Windows、macOS、Linux、iOS、Android、JavaScript等。提供的API支持JavaScript、C语言、和Swift。详情可以参考Frida官方文档。
Frida原理
网上有很多Frida原理的介绍。简单来说Frida就是一款开源的动态插装(dynamic instrumentation)框架。目前非常有名的动态插装工具还有Intel的Pin等。使用Frida可以进行注入(Inject)、拦截(Intercept)、代码跟踪(Stalk)。在本文中我们主要使用Frida的拦截技术。首先通过劫持(attach)对应的进程,随后使用拦截器(Interceptor)拦截对应的内存地址,在对应内存地址进行保护现场(所有的寄存器)、调用拦截函数、恢复现场、返回调用等。,这些工作除了拦截函数是我自己定义编写的,其他都由Frida拦截器自动完成。具体可以参考Frida的本文了解。
使用Frida编写HOOK脚本
我将在Windows平台使用Frida框架编写一个简单的HOOK脚本。HOOK目标是自己用C语言写的函数h。该函数有两个整形输入参数,HOOK脚本将截取这两个输入参数,并打印在屏幕上。HOOK脚本将使用Python和Javascript编写。在编写HOOK脚本前,我们需要知道HOOK地址,函数h的两个输入参数的寄存器地址和操作。这些详情在文章《使用OllyDbg逆向查找HOOK地址和寄存器使用》已经详细描述,再继续本文之前可以先参考该文。
使用Frida编写HOOK脚本:代码实现
代码非常简单,如果会写Python和Javascript的话,以下代码的功能是一目了然的。代码如下:
# -*- coding: UTF-8 -*- # py3 from __future__ import print_function import frida import codecs import sys def main(target_process): session = frida.attach(target_process) with codecs.open('js/watch.js', 'r', 'utf-8') as f: source = f.read() script = session.create_script(source) script.load() print("[!] Ctrl+D on UNIX, Ctrl+Z on Windows/cmd.exe to detach from instrumented program.\n\n") sys.stdin.read() session.detach() if __name__ == '__main__': main('hello.exe')
上面是python部分的代码,调用Frida并加载watch.js
文件。这个JS文件中的内容才是真正HOOK地址和查看内存寄存器的。
var ModAddress=Process.findModuleByName('hello.exe'); console.log('Mod Address:' + ModAddress.base); var hookAddress=ModAddress.base.add('0x15D5') console.log('Hook Address: ' + hookAddress); Interceptor.attach(hookAddress, { onEnter: function (args) { var esp = this.context.esp; console.log("两个参数分别为:" + Memory.readU32(esp.add('0x4')) + "和" + Memory.readU32(esp.add('0x8'))) } })
代码比较简单。这里需要提一下,在函数被调用之前,两个参数分别被压入了ESP寄存器。如图所示:
但是我们的HOOK地址是函数的入口地址0x004015D5,并不是函数调用前的CALL地址0x00401720。而在函数被调用的入口位置有一个push的汇编指令,导致ESP寄存器新压入了一个数值,ESP寄存器指针上移了4个字节(我猜测因为是32位程序,因此一个指针偏移是4个字节)。所以两个参数的地址分别为esp.add('0x4')
和esp.add('0x8')
。
HOOK脚本效果展示
脚本准备就绪以后,我们分别开两个终端窗口。一个窗口运行C程序,一个窗口运行HOOK脚本。然后我们可以在窗口中看出,当调用函数h()时,HOOK脚本会打印传入的两个参数。如图所示:
总结
使用Frida可以非常容易创建HOOK脚本实现内存注入。本文中使用Python加Javascript开发了一个简单的HOOK脚本。通过HOOK到正确的内存地址,监听寄存器的变化情况,最终通过读取寄存器把函数的两个调用参数打印到屏幕上。HOOK脚本只能添加函数调用监听,查看函数调用时的各种数据。如果要主动调用内存中的函数,还需要开发主动调用脚本。这部分内容将在后期的文章中介绍。
相关阅读:
《使用Frida实现内存注入主动调用函数》
扫码联系船长