今天我们将学习怎样在Python中使用Frida的'NativeFunction'创建并调用iOS本地函数,如果被创建的函数是本地的Python函数的话,接下来再使用RPC调用这一iOS函数,就好像它们是本地python函数一样。
这篇文章的灵感来自于前段时间@CodeColorist的一篇推文。
如果你还没运行过上述语句,请运行一下试试。打开Frida REPL并输入:
那么,在运行这条语句的时候,到底发生了什么呢?这条语句调用了AudioToolbox
框架中的AudioServicesPlaySystemSound
函数
Audio Toolbox framework提供了记录,回放和流分析的功能。这一框架中的系统音频服务为iOS设备的短音频播放和振动功能提供了一个C接口。
在Frida中:
现在我们可以把这条单行代码拆分成两行,方便进行分析:
这样的话,每一步的功能就很直观了:
接下来我们对其中每一步的意义进行详细分析。
我们需要的函数是AudioServicesPlaySystemSound
。这一函数由AudioToolbox
模块导出。你可以采用下述方式对它进行验证:
通过Module.findExportByName(moduleName, exportName)
(FridaModule的API)可以获取导出函数的绝对地址:
在这里获取到的地址,如无意外的话会和我们上面获取到的(0x186cf4e88
)相同。为了方便我们不再使用Module.enumerateExportsSync
获取地址,而是采用这一方法进行手动搜索。
要根据地址调用本地函数,我们还需要用Frida的NativeFunction创建一个本地函数对象,这一函数的结构是这样的:new NativeFunction(address, returnType, argTypes)
,其中
当使用Frida进行播放的时候,我推荐你多看看官方的API和例子:https://www.frida.re/docs/home/
现在,我们已经拿到address
了,接下来的工作就是要获取函数的returnType
和argTypes
,也就是要获取函数签名。这里不需要什么黑客技巧,假设你是一个开发者,现在你想要使用这个方法,你会从哪寻找自己需要的信息呢?没错,就是Apple docs :)
因此我们的代码是这样写的:
请注意NativeFunction
的第2个参数是返回值类型,第3个参数是输入类型的数组
现在我们已经把NativeFunction
存储在了play_sound
变量里,像调用普通函数一样调用play_sound()
并给它一个(int
)作为输入参数:play_sound(1007)
。
把所有这些放在一起:
也可以把上述语句重构成:
也就是相当于:
这样我们又得到了文章开头的那条单行代码 :)
通过搜索我们可以找到更多的用于播放音频的代码:
http://iphonedevwiki.net/index.php/AudioServices
音频文件存储在/System/Library/Audio/UISounds/
:
但如果只是要下载播放文件的话未免太无聊了。现在我们想要使用之前的单行代码构建一个Frida脚本(audiobox.js):
在使用frida -U Telegram -l audiobox.js
将这段代码加载进Frida REPL后,我们就可以简单地通过play('sms')
,play('lock')
等语句来播放所有的音频。
注意:在我们的例子中,我们把Frida的调试器附加到了Telegram这个app上,事实上,附加到哪个app并不重要,因为我们调用的是系统函数,如果你想要调用某个app的本地函数,那么你就需要附加到对应的app。
Frida提供了通过RPC调用函数的功能,比如说通过Python调用远程代码。这也就意味着我们可以像调用普通Python方法一样调用app中的方法。很棒吧?我们接下来对我们的Frida脚本进行重写,比如说audiobox_rpc.js:
写一个Python脚本的主要步骤如下:
Python代码如下(frida_rpc_player.py):
你可以通过在终端上输入python3 frida_rpc_player.py
运行它。
注意:为了在音频播放时显示一个警告,我添加了一个额外的函数到Frida脚本中。为了简化说明,在这里我没有进行详细的介绍,但是我推荐你看一看并分析一下脚本audiobox_rpc_alert.js中的所有步骤。在其中你可以看到自动触发以及消除警告的方法。
(译者注:这里有一个视频,因为markdown不支持直接放视频,所以我把链接贴上来了)
看起来很简单,对吧?我知道你想说:“太棒了,这样我就能去骚扰别人,让别人以为自己收到了消息。”
然而,这一技术只在你想要测试app,破解一些代码或是让app帮你自动完成一些任务时才有用(译者注:前面写脚本的步骤中提到需要通过usb连接手机,才能实现这些功能)。例如,如果app进行了一些加密/解密,并正确地实现了crypto,想要提取加密密钥几乎是不可能的,因为密钥被保存在Secure Enclave中。但是,细想一下,在app中已经提供了encrypt()/decrypt()
函数的前提下,为什么一定要费尽心思地去提取密钥并复现加密算法呢。
请记住,这些并不是NativeFunction
所特有的,你可以通过RPC使用你喜欢的任意代码。例如,你可以封装一个Objective-C函数,并以同样的方式使用它。
例如,我们可以写一个Frida脚本(openurl_rpc.js)来调用我之前的博客中写的函数:
现在你可以直接在Python中实现这个(见frida_rpc_openurl.py):
根据函数返回值我们可以决定下一步怎么做,正如你看到的,这取决于你的想象力和创造力。
这一播放iOS系统音频的实例在现实生活中并不能用于骚扰他人。然而,这一技术可以帮助你解决在分析app时遇到的问题。
在学习的过程中,深度理解这一技术的原理是关键。如果你只是简单的复制一些你从网上找到的脚本的话,它只能解决你短期的问题,从长远来看,对你并没有帮助。最后的建议,我希望你能在今后的日子里一直保持学习新知识。
如果你有好的建议,反馈或是问题,请联系我的Twitter :)
@grepharder
原文链接:https://grepharder.github.io/blog/0x04_calling_ios_native_functions_from_python_using_frida_and_rpc.html
翻译:看雪翻译小组 梦野间
校对:看雪翻译小组 lumou
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!