MASTG-TOOL-0039: Frida para iOS
O Frida oferece suporte à interação com o runtime Objective-C por meio da API ObjC. Você poderá fazer hook e chamar funções tanto Objective-C quanto nativas dentro do processo e suas bibliotecas nativas. Seus snippets JavaScript têm acesso completo à memória, por exemplo, para ler e/ou gravar quaisquer dados estruturados.
Aqui estão algumas tarefas que as APIs do Frida oferecem e que são relevantes ou exclusivas no iOS:
- Instanciar objetos Objective-C e chamar métodos de classe estáticos e não estáticos (API ObjC).
- Rastrear chamadas de métodos Objective-C e/ou substituir suas implementações (API Interceptor).
- Enumerar instâncias ativas de classes específicas através da varredura da heap (API ObjC).
- Escanear a memória do processo em busca de ocorrências de uma string (API Memory).
- Interceptar chamadas de funções nativas para executar seu próprio código na entrada e saída da função (API Interceptor).
Lembre-se de que no iOS você também pode se beneficiar das ferramentas integradas fornecidas ao instalar o Frida, que incluem o Frida CLI (frida), frida-ps, frida-ls-devices e frida-trace, para citar alguns.
Há um recurso do frida-trace exclusivo no iOS que vale destacar: rastrear APIs Objective-C usando a flag -m e wildcards. Por exemplo, rastrear todos os métodos que incluem "HTTP" em seu nome e pertencem a qualquer classe cujo nome comece com "NSURL" é tão simples quanto executar:
frida-trace -U YourApp -m "*[NSURL* *HTTP*]"
Para um início rápido, você pode percorrer os exemplos de iOS.
Instalando o Frida no iOS¶
Para conectar o Frida a um app iOS, você precisa de uma maneira de injetar o runtime do Frida nesse app. Isso é fácil de fazer em um dispositivo com jailbreak, pois você pode instalar o frida-server por meio de uma loja de aplicativos de terceiros, como Sileo. Abra o Sileo e adicione o repositório do Frida navegando até Gerenciar -> Fontes -> Editar -> Adicionar e inserindo https://build.frida.re. Você então deve conseguir encontrar e instalar o pacote do Frida.
Por padrão, o frida-server escuta apenas na interface local, exigindo que você conecte o dispositivo via USB. Se quiser expor o frida-server na interface pública, modifique /var/jb/Library/LaunchDaemons/re.frida.server.plist e adicione dois itens ao ProgramArguments conforme mostrado abaixo:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <d>
<key>Label</key>
<string>re.frida.server</string>
<key>Program</key>
<string>/var/jb/usr/sbin/frida-server</string>
<key>ProgramArguments</key>
<array>
<string>/var/jb/usr/sbin/frida-server</string>
<string>-l</string>
<string>0.0.0.0</string>
</array>
<key>UserName</key>
<string>root</string>
<key>POSIXSpawnType</key>
<string>Interactive</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>5</integer>
<key>ExecuteAllowed</key>
<true/>
</dict>
</plist>
Uma vez instalado, o servidor do Frida será executado automaticamente com privilégios de root, permitindo que você injete código facilmente em qualquer processo.
Danger
Expor o frida-server na interface pública permitirá que qualquer pessoa conectada na mesma rede injete código em qualquer processo em execução no dispositivo. Você só deve fazer isso em um ambiente controlado de laboratório.
Usando o Frida no iOS¶
Conecte seu dispositivo via USB e verifique se o Frida funciona executando o comando frida-ps com a flag -U. Isso deve retornar a lista de processos em execução no dispositivo:
$ frida-ps -U
PID Name
--- ----------------
963 Mail
952 Safari
416 BTServer
422 BlueTool
791 CalendarWidget
451 CloudKeychainPro
239 CommCenter
764 ContactsCoreSpot
(...)
Bindings do Frida¶
Para estender a experiência de scripting, o Frida oferece bindings para linguagens de programação como Python, C, NodeJS e Swift.
Tomando o Python como exemplo, a primeira coisa a notar é que não são necessárias etapas adicionais de instalação. Inicie seu script Python com import frida e você estará pronto. Veja o seguinte script que simplesmente executa o snippet JavaScript anterior:
# frida_python.py
import frida
session = frida.get_usb_device().attach('com.android.chrome')
source = """
Java.perform(function () {
var view = Java.use("android.view.View");
var methods = view.class.getMethods();
for(var i = 0; i < methods.length; i++) {
console.log(methods[i].toString());
}
});
"""
script = session.create_script(source)
script.load()
session.detach()
Neste caso, executar o script Python (python3 frida_python.py) terá o mesmo resultado do exemplo anterior: ele imprimirá todos os métodos da classe android.view.View no terminal. No entanto, você pode querer trabalhar com esses dados a partir do Python. Usar send em vez de console.log enviará dados em formato JSON do JavaScript para o Python. Por favor, leia os comentários no exemplo abaixo:
# python3 frida_python_send.py
import frida
session = frida.get_usb_device().attach('com.android.chrome')
# 1. queremos armazenar os nomes dos métodos em uma lista
android_view_methods = []
source = """
Java.perform(function () {
var view = Java.use("android.view.View");
var methods = view.class.getMethods();
for(var i = 0; i < methods.length; i++) {
send(methods[i].toString());
}
});
"""
script = session.create_script(source)
# 2. esta é uma função de callback, apenas métodos contendo "Text" serão adicionados à lista
def on_message(message, data):
if "Text" in message['payload']:
android_view_methods.append(message['payload'])
# 3. instruímos o script a executar nosso callback sempre que uma mensagem for recebida
script.on('message', on_message)
script.load()
# 4. fazemos algo com os dados coletados, neste caso apenas os imprimimos
for method in android_view_methods:
print(method)
session.detach()
Isso efetivamente filtra os métodos e imprime apenas os que contêm a string "Text":
$ python3 frida_python_send.py
public boolean android.view.View.canResolveTextAlignment()
public boolean android.view.View.canResolveTextDirection()
public void android.view.View.setTextAlignment(int)
public void android.view.View.setTextDirection(int)
public void android.view.View.setTooltipText(java.lang.CharSequence)
...
No final, cabe a você decidir onde deseja trabalhar com os dados. Às vezes será mais conveniente fazê-lo a partir do JavaScript e em outros casos o Python será a melhor escolha. Claro, você também pode enviar mensagens do Python para o JavaScript usando script.post. Consulte a documentação do Frida para obter mais informações sobre envio e recebimento de mensagens.