macOS Apps - Inspecting, debugging and Fuzzing
Análise Estática
otool & objdump & nm
jtool2 & Disarm
Você pode baixar disarm daqui.
Você pode baixar jtool2 aqui ou instalá-lo com brew
.
jtool está obsoleto em favor do disarm
Codesign / ldid
Codesign
pode ser encontrado no macOS, enquanto ldid
pode ser encontrado no iOS
SuspiciousPackage
SuspiciousPackage é uma ferramenta útil para inspecionar arquivos .pkg (instaladores) e ver o que há dentro antes de instalá-los.
Esses instaladores têm scripts bash preinstall
e postinstall
que autores de malware geralmente abusam para persistir o malware.
hdiutil
Esta ferramenta permite montar imagens de disco da Apple (.dmg) para inspecioná-las antes de executar qualquer coisa:
It will be mounted in /Volumes
Binaries empacotados
Verifique a alta entropia
Verifique as strings (se quase não houver string compreensível, empacotado)
O empacotador UPX para MacOS gera uma seção chamada "__XHDR"
Análise estática de Objective-C
Metadados
Observe que programas escritos em Objective-C mantêm suas declarações de classe quando compilados em binaries Mach-O. Tais declarações de classe incluem o nome e tipo de:
As interfaces definidas
Os métodos da interface
As variáveis de instância da interface
Os protocolos definidos
Observe que esses nomes podem ser ofuscados para dificultar a reversão do binário.
Chamada de função
Quando uma função é chamada em um binário que usa Objective-C, o código compilado, em vez de chamar essa função, chamará objc_msgSend
. Que chamará a função final:
Os parâmetros que essa função espera são:
O primeiro parâmetro (self) é "um ponteiro que aponta para a instância da classe que deve receber a mensagem". Ou, mais simplesmente, é o objeto sobre o qual o método está sendo invocado. Se o método for um método de classe, isso será uma instância do objeto da classe (como um todo), enquanto para um método de instância, self apontará para uma instância instanciada da classe como um objeto.
O segundo parâmetro, (op), é "o seletor do método que lida com a mensagem". Novamente, mais simplesmente, isso é apenas o nome do método.
Os parâmetros restantes são quaisquer valores que são necessários pelo método (op).
Veja como obter essas informações facilmente com lldb
em ARM64 nesta página:
x64:
Argumento | Registrador | (para) objc_msgSend |
1º argumento | rdi | self: objeto sobre o qual o método está sendo invocado |
2º argumento | rsi | op: nome do método |
3º argumento | rdx | 1º argumento para o método |
4º argumento | rcx | 2º argumento para o método |
5º argumento | r8 | 3º argumento para o método |
6º argumento | r9 | 4º argumento para o método |
7º+ argumento | rsp+ (na pilha) | 5º+ argumento para o método |
Despejar metadados ObjectiveC
Dynadump
Dynadump é uma ferramenta para class-dump de binaries Objective-C. O github especifica dylibs, mas isso também funciona com executáveis.
No momento da escrita, este é atualmente o que funciona melhor.
Ferramentas regulares
class-dump
class-dump é a ferramenta original que gera declarações para as classes, categorias e protocolos em código formatado em ObjetiveC.
É antiga e não é mantida, então provavelmente não funcionará corretamente.
ICDump
iCDump é um dump de classe Objective-C moderno e multiplataforma. Comparado às ferramentas existentes, o iCDump pode ser executado independentemente do ecossistema da Apple e expõe bindings em Python.
Análise estática de Swift
Com binários Swift, uma vez que há compatibilidade com Objective-C, às vezes você pode extrair declarações usando class-dump, mas nem sempre.
Com os comandos jtool -l
ou otool -l
é possível encontrar várias seções que começam com o prefixo __swift5
:
Você pode encontrar mais informações sobre as informações armazenadas nesta seção neste post do blog.
Além disso, binários Swift podem ter símbolos (por exemplo, bibliotecas precisam armazenar símbolos para que suas funções possam ser chamadas). Os símbolos geralmente têm as informações sobre o nome da função e atributos de uma maneira feia, então eles são muito úteis e existem "demanglers" que podem obter o nome original:
Análise Dinâmica
Observe que, para depurar binários, o SIP precisa ser desativado (csrutil disable
ou csrutil enable --without debug
) ou copiar os binários para uma pasta temporária e remover a assinatura com codesign --remove-signature <caminho-do-binário>
ou permitir a depuração do binário (você pode usar este script)
Observe que, para instrumentar binários do sistema, (como cloudconfigurationd
) no macOS, o SIP deve ser desativado (apenas remover a assinatura não funcionará).
APIs
macOS expõe algumas APIs interessantes que fornecem informações sobre os processos:
proc_info
: Este é o principal que fornece muitas informações sobre cada processo. Você precisa ser root para obter informações de outros processos, mas não precisa de direitos especiais ou portas mach.libsysmon.dylib
: Permite obter informações sobre processos por meio de funções expostas pelo XPC, no entanto, é necessário ter a autorizaçãocom.apple.sysmond.client
.
Stackshot & microstackshots
Stackshotting é uma técnica usada para capturar o estado dos processos, incluindo as pilhas de chamadas de todas as threads em execução. Isso é particularmente útil para depuração, análise de desempenho e compreensão do comportamento do sistema em um ponto específico no tempo. No iOS e macOS, o stackshotting pode ser realizado usando várias ferramentas e métodos, como as ferramentas sample
e spindump
.
Sysdiagnose
Esta ferramenta (/usr/bini/ysdiagnose
) basicamente coleta muitas informações do seu computador executando dezenas de comandos diferentes, como ps
, zprint
...
Deve ser executada como root e o daemon /usr/libexec/sysdiagnosed
possui autorizações muito interessantes, como com.apple.system-task-ports
e get-task-allow
.
Seu plist está localizado em /System/Library/LaunchDaemons/com.apple.sysdiagnose.plist
, que declara 3 MachServices:
com.apple.sysdiagnose.CacheDelete
: Exclui arquivos antigos em /var/rmpcom.apple.sysdiagnose.kernel.ipc
: Porta especial 23 (kernel)com.apple.sysdiagnose.service.xpc
: Interface de modo usuário através da classe Obj-CLibsysdiagnose
. Três argumentos em um dicionário podem ser passados (compress
,display
,run
)
Logs Unificados
MacOS gera muitos logs que podem ser muito úteis ao executar um aplicativo tentando entender o que ele está fazendo.
Além disso, existem alguns logs que conterão a tag <private>
para ocultar algumas informações identificáveis do usuário ou do computador. No entanto, é possível instalar um certificado para divulgar essas informações. Siga as explicações aqui.
Hopper
Painel esquerdo
No painel esquerdo do Hopper, é possível ver os símbolos (Labels) do binário, a lista de procedimentos e funções (Proc) e as strings (Str). Essas não são todas as strings, mas as definidas em várias partes do arquivo Mac-O (como cstring ou objc_methname
).
Painel do meio
No painel do meio, você pode ver o código desassemblado. E você pode vê-lo em uma desassemblagem bruta, como gráfico, como decompilado e como binário clicando no ícone respectivo:
Clicando com o botão direito em um objeto de código, você pode ver referências para/de aquele objeto ou até mesmo mudar seu nome (isso não funciona em pseudocódigo decompilado):
Além disso, na parte inferior do meio, você pode escrever comandos python.
Painel direito
No painel direito, você pode ver informações interessantes, como o histórico de navegação (para saber como você chegou à situação atual), o gráfico de chamadas onde você pode ver todas as funções que chamam esta função e todas as funções que esta função chama, e informações sobre variáveis locais.
dtrace
Permite que os usuários acessem aplicativos em um nível extremamente baixo e fornece uma maneira para os usuários rastrearem programas e até mesmo mudarem seu fluxo de execução. Dtrace usa probes que são colocadas em todo o kernel e estão em locais como o início e o fim das chamadas de sistema.
DTrace usa a função dtrace_probe_create
para criar uma probe para cada chamada de sistema. Essas probes podem ser acionadas no ponto de entrada e saída de cada chamada de sistema. A interação com o DTrace ocorre através de /dev/dtrace, que está disponível apenas para o usuário root.
Para habilitar o Dtrace sem desativar completamente a proteção SIP, você pode executar no modo de recuperação: csrutil enable --without dtrace
Você também pode dtrace
ou dtruss
binários que você compilou.
As probes disponíveis do dtrace podem ser obtidas com:
O nome da sonda consiste em quatro partes: o provedor, módulo, função e nome (fbt:mach_kernel:ptrace:entry
). Se você não especificar alguma parte do nome, o Dtrace aplicará essa parte como um curinga.
Para configurar o DTrace para ativar sondas e especificar quais ações realizar quando elas forem acionadas, precisaremos usar a linguagem D.
Uma explicação mais detalhada e mais exemplos podem ser encontrados em https://illumos.org/books/dtrace/chp-intro.html
Exemplos
Execute man -k dtrace
para listar os scripts DTrace disponíveis. Exemplo: sudo dtruss -n binary
Na linha
script
dtruss
kdebug
É uma ferramenta de rastreamento do kernel. Os códigos documentados podem ser encontrados em /usr/share/misc/trace.codes
.
Ferramentas como latency
, sc_usage
, fs_usage
e trace
a utilizam internamente.
Para interagir com kdebug
, usa-se sysctl
sobre o namespace kern.kdebug
e os MIBs que podem ser encontrados em sys/sysctl.h
, tendo as funções implementadas em bsd/kern/kdebug.c
.
Para interagir com kdebug com um cliente personalizado, geralmente esses são os passos:
Remover configurações existentes com KERN_KDSETREMOVE
Definir rastreamento com KERN_KDSETBUF e KERN_KDSETUP
Usar KERN_KDGETBUF para obter o número de entradas do buffer
Obter o próprio cliente do rastreamento com KERN_KDPINDEX
Habilitar rastreamento com KERN_KDENABLE
Ler o buffer chamando KERN_KDREADTR
Para corresponder cada thread ao seu processo, chamar KERN_KDTHRMAP.
Para obter essas informações, é possível usar a ferramenta da Apple trace
ou a ferramenta personalizada kDebugView (kdv).
Observe que Kdebug está disponível apenas para 1 cliente por vez. Portanto, apenas uma ferramenta com k-debug pode ser executada ao mesmo tempo.
ktrace
As APIs ktrace_*
vêm de libktrace.dylib
, que envolvem as de Kdebug
. Assim, um cliente pode simplesmente chamar ktrace_session_create
e ktrace_events_[single/class]
para definir callbacks em códigos específicos e, em seguida, iniciá-lo com ktrace_start
.
Você pode usar este mesmo com SIP ativado
Você pode usar como clientes a utilidade ktrace
:
Or tailspin
.
kperf
Isso é usado para fazer um perfil de nível de kernel e é construído usando chamadas Kdebug
.
Basicamente, a variável global kernel_debug_active
é verificada e, se estiver definida, chama kperf_kdebug_handler
com o código Kdebug
e o endereço do quadro do kernel chamando. Se o código Kdebug
corresponder a um selecionado, ele obtém as "ações" configuradas como um bitmap (ver osfmk/kperf/action.h
para as opções).
Kperf também possui uma tabela MIB sysctl: (como root) sysctl kperf
. Esses códigos podem ser encontrados em osfmk/kperf/kperfbsd.c
.
Além disso, um subconjunto da funcionalidade do Kperf reside em kpc
, que fornece informações sobre contadores de desempenho da máquina.
ProcessMonitor
ProcessMonitor é uma ferramenta muito útil para verificar as ações relacionadas a processos que um processo está realizando (por exemplo, monitorar quais novos processos um processo está criando).
SpriteTree
SpriteTree é uma ferramenta que imprime as relações entre processos.
Você precisa monitorar seu mac com um comando como sudo eslogger fork exec rename create > cap.json
(o terminal que inicia isso requer FDA). E então você pode carregar o json nesta ferramenta para ver todas as relações:
FileMonitor
FileMonitor permite monitorar eventos de arquivo (como criação, modificações e exclusões) fornecendo informações detalhadas sobre tais eventos.
Crescendo
Crescendo é uma ferramenta GUI com a aparência e a sensação que os usuários do Windows podem conhecer do Procmon da Microsoft Sysinternal. Esta ferramenta permite que a gravação de vários tipos de eventos seja iniciada e parada, permite a filtragem desses eventos por categorias como arquivo, processo, rede, etc., e fornece a funcionalidade de salvar os eventos gravados em um formato json.
Apple Instruments
Apple Instruments são parte das ferramentas de desenvolvedor do Xcode – usadas para monitorar o desempenho de aplicativos, identificar vazamentos de memória e rastrear a atividade do sistema de arquivos.
fs_usage
Permite seguir as ações realizadas por processos:
TaskExplorer
Taskexplorer é útil para ver as bibliotecas usadas por um binário, os arquivos que está utilizando e as conexões de rede. Ele também verifica os processos binários contra o virustotal e mostra informações sobre o binário.
PT_DENY_ATTACH
No este post do blog você pode encontrar um exemplo sobre como depurar um daemon em execução que usou PT_DENY_ATTACH
para impedir a depuração mesmo que o SIP estivesse desativado.
lldb
lldb é a ferramenta de facto para depuração de binários no macOS.
Você pode definir o sabor intel ao usar lldb criando um arquivo chamado .lldbinit
na sua pasta inicial com a seguinte linha:
Dentro do lldb, despeje um processo com process save-core
(lldb) Comando | Descrição |
run (r) | Inicia a execução, que continuará sem interrupções até que um ponto de interrupção seja atingido ou o processo termine. |
process launch --stop-at-entry | Inicia a execução parando no ponto de entrada |
continue (c) | Continua a execução do processo depurado. |
nexti (n / ni) | Executa a próxima instrução. Este comando irá pular chamadas de função. |
stepi (s / si) | Executa a próxima instrução. Ao contrário do comando nexti, este comando irá entrar nas chamadas de função. |
finish (f) | Executa o restante das instruções na função atual (“frame”) retorna e para. |
control + c | Pausa a execução. Se o processo foi executado (r) ou continuado (c), isso fará com que o processo pare ...onde quer que esteja executando atualmente. |
breakpoint (b) |
breakpoint delete <num> |
help | help breakpoint #Obter ajuda do comando breakpoint help memory write #Obter ajuda para escrever na memória |
reg | |
x/s <reg/endereço de memória> | Exibe a memória como uma string terminada em nulo. |
x/i <reg/endereço de memória> | Exibe a memória como instrução de assembly. |
x/b <reg/endereço de memória> | Exibe a memória como byte. |
print object (po) | Isso imprimirá o objeto referenciado pelo parâmetro po $raw
Note que a maioria das APIs ou métodos Objective-C da Apple retornam objetos, e, portanto, devem ser exibidos via o comando “print object” (po). Se po não produzir uma saída significativa, use |
memory | memory read 0x000.... memory read $x0+0xf2a memory write 0x100600000 -s 4 0x41414141 #Escreve AAAA nesse endereço memory write -f s $rip+0x11f+7 "AAAA" #Escreve AAAA no addr |
disassembly | dis #Desmonta a função atual dis -n <funcname> #Desmonta a função dis -n <funcname> -b <basename> #Desmonta a função dis -c 6 #Desmonta 6 linhas dis -c 0x100003764 -e 0x100003768 # De um add até o outro dis -p -c 4 # Começa no endereço atual desmontando |
parray | parray 3 (char **)$x1 # Verifica array de 3 componentes no reg x1 |
image dump sections | Imprime o mapa da memória do processo atual |
image dump symtab <library> |
|
Ao chamar a função objc_sendMsg
, o registrador rsi contém o nome do método como uma string terminada em nulo (“C”). Para imprimir o nome via lldb faça:
(lldb) x/s $rsi: 0x1000f1576: "startMiningWithPort:password:coreCount:slowMemory:currency:"
(lldb) print (char*)$rsi:
(char *) $1 = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:"
(lldb) reg read $rsi: rsi = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:"
Análise Anti-Dinâmica
Detecção de VM
O comando
sysctl hw.model
retorna "Mac" quando o host é um MacOS mas algo diferente quando é uma VM.Brincando com os valores de
hw.logicalcpu
ehw.physicalcpu
, alguns malwares tentam detectar se é uma VM.Alguns malwares também podem detectar se a máquina é baseada em VMware com base no endereço MAC (00:50:56).
Também é possível descobrir se um processo está sendo depurado com um código simples como:
if(P_TRACED == (info.kp_proc.p_flag & P_TRACED)){ //processo sendo depurado }
Ele também pode invocar a chamada de sistema
ptrace
com a flagPT_DENY_ATTACH
. Isso impede que um depurador se anexe e trace.Você pode verificar se a função
sysctl
ouptrace
está sendo importada (mas o malware poderia importá-la dinamicamente)Como observado neste artigo, “Defeating Anti-Debug Techniques: macOS ptrace variants” : “A mensagem Process # exited with status = 45 (0x0000002d) é geralmente um sinal claro de que o alvo de depuração está usando PT_DENY_ATTACH”
Core Dumps
Core dumps são criados se:
kern.coredump
sysctl está definido como 1 (por padrão)Se o processo não era suid/sgid ou
kern.sugid_coredump
é 1 (por padrão é 0)O limite
AS_CORE
permite a operação. É possível suprimir a criação de core dumps chamandoulimit -c 0
e reabilitá-los comulimit -c unlimited
.
Nesses casos, o core dump é gerado de acordo com o sysctl kern.corefile
e geralmente armazenado em /cores/core/.%P
.
Fuzzing
ReportCrash analisa processos que falham e salva um relatório de falha no disco. Um relatório de falha contém informações que podem ajudar um desenvolvedor a diagnosticar a causa de uma falha.
Para aplicativos e outros processos executando no contexto de launchd por usuário, o ReportCrash é executado como um LaunchAgent e salva relatórios de falha nos ~/Library/Logs/DiagnosticReports/
do usuário.
Para daemons, outros processos executando no contexto de launchd do sistema e outros processos privilegiados, o ReportCrash é executado como um LaunchDaemon e salva relatórios de falha nos /Library/Logs/DiagnosticReports
do sistema.
Se você está preocupado com relatórios de falha sendo enviados para a Apple, você pode desativá-los. Se não, os relatórios de falha podem ser úteis para descobrir como um servidor falhou.
Sleep
Enquanto fuzzing em um MacOS, é importante não permitir que o Mac entre em modo de suspensão:
systemsetup -setsleep Never
pmset, Preferências do Sistema
SSH Disconnect
Se você estiver fuzzing via uma conexão SSH, é importante garantir que a sessão não vá expirar. Portanto, altere o arquivo sshd_config com:
TCPKeepAlive Yes
ClientAliveInterval 0
ClientAliveCountMax 0
Internal Handlers
Confira a página a seguir para descobrir como você pode encontrar qual aplicativo é responsável por manipular o esquema ou protocolo especificado:
macOS File Extension & URL scheme app handlersEnumerating Network Processes
Isso é interessante para encontrar processos que estão gerenciando dados de rede:
Ou use netstat
ou lsof
Libgmalloc
Fuzzers
Funciona para ferramentas de linha de comando
Ele "simplesmente funciona" com ferramentas GUI do macOS. Observe que alguns aplicativos do macOS têm requisitos específicos, como nomes de arquivos exclusivos, a extensão correta, necessidade de ler os arquivos do sandbox (~/Library/Containers/com.apple.Safari/Data
)...
Alguns exemplos:
Mais Informações sobre Fuzzing no MacOS
Referências
Last updated