macOS IOKit
Informação Básica
O I/O Kit é um framework de driver de dispositivo orientado a objetos de código aberto no kernel XNU, que lida com drivers de dispositivo carregados dinamicamente. Ele permite que código modular seja adicionado ao kernel dinamicamente, suportando hardware diversificado.
Os drivers IOKit basicamente exportam funções do kernel. Os tipos de parâmetros dessas funções são predefinidos e verificados. Além disso, semelhante ao XPC, o IOKit é apenas mais uma camada em cima das mensagens Mach.
O código do kernel IOKit XNU é de código aberto pela Apple em https://github.com/apple-oss-distributions/xnu/tree/main/iokit. Além disso, os componentes do IOKit no espaço do usuário também são de código aberto https://github.com/opensource-apple/IOKitUser.
No entanto, nenhum driver IOKit é de código aberto. De qualquer forma, de tempos em tempos, um lançamento de um driver pode vir com símbolos que facilitam a depuração. Verifique como obter as extensões do driver do firmware aqui.
É escrito em C++. Você pode obter símbolos C++ desembaralhados com:
As funções expostas do IOKit poderiam realizar verificações de segurança adicionais quando um cliente tenta chamar uma função, mas observe que os aplicativos geralmente são limitados pelo sandbox em relação às funções do IOKit com as quais podem interagir.
Drivers
No macOS, eles estão localizados em:
/System/Library/Extensions
Arquivos KEXT integrados ao sistema operacional OS X.
/Library/Extensions
Arquivos KEXT instalados por software de terceiros
No iOS, eles estão localizados em:
/System/Library/Extensions
Até o número 9, os drivers listados são carregados no endereço 0. Isso significa que eles não são drivers reais, mas parte do kernel e não podem ser descarregados.
Para encontrar extensões específicas, você pode usar:
Para carregar e descarregar extensões de kernel, faça:
IORegistry
O IORegistry é uma parte crucial do framework IOKit no macOS e iOS que serve como um banco de dados para representar a configuração de hardware e estado do sistema. É uma coleção hierárquica de objetos que representam todo o hardware e drivers carregados no sistema, e suas relações entre si.
Você pode obter o IORegistry usando o cli ioreg
para inspecioná-lo a partir do console (especialmente útil para iOS).
Pode baixar o IORegistryExplorer
nas Ferramentas Adicionais do Xcode em https://developer.apple.com/download/all/ e inspecionar o IORegistry do macOS através de uma interface gráfica.
No IORegistryExplorer, "planos" são usados para organizar e exibir as relações entre diferentes objetos no IORegistry. Cada plano representa um tipo específico de relação ou uma visualização particular da configuração de hardware e driver do sistema. Aqui estão alguns dos planos comuns que você pode encontrar no IORegistryExplorer:
Plano IOService: Este é o plano mais geral, exibindo os objetos de serviço que representam drivers e nubs (canais de comunicação entre drivers). Ele mostra as relações provedor-cliente entre esses objetos.
Plano IODeviceTree: Este plano representa as conexões físicas entre dispositivos conforme são conectados ao sistema. É frequentemente usado para visualizar a hierarquia de dispositivos conectados via barramentos como USB ou PCI.
Plano IOPower: Exibe objetos e suas relações em termos de gerenciamento de energia. Pode mostrar quais objetos estão afetando o estado de energia de outros, útil para depurar problemas relacionados à energia.
Plano IOUSB: Especificamente focado em dispositivos USB e suas relações, mostrando a hierarquia de hubs USB e dispositivos conectados.
Plano IOAudio: Este plano é para representar dispositivos de áudio e suas relações dentro do sistema.
...
Exemplo de Código de Comunicação de Driver
O código a seguir se conecta ao serviço IOKit "NomeDoSeuServiçoAqui"
e chama a função dentro do seletor 0. Para isso:
primeiro chama
IOServiceMatching
eIOServiceGetMatchingServices
para obter o serviço.Em seguida, estabelece uma conexão chamando
IOServiceOpen
.E finalmente chama uma função com
IOConnectCallScalarMethod
indicando o seletor 0 (o seletor é o número atribuído à função que você deseja chamar).
Existem outras funções que podem ser usadas para chamar funções IOKit além de IOConnectCallScalarMethod
como IOConnectCallMethod
, IOConnectCallStructMethod
...
Reversão do ponto de entrada do driver
Você pode obter essas, por exemplo, de uma imagem de firmware (ipsw). Em seguida, carregue-a em seu decompilador favorito.
Você pode começar a descompilar a função externalMethod
pois esta é a função do driver que estará recebendo a chamada e chamando a função correta:
Aquele chamada desembaraçada significa:
Observe como na definição anterior o parâmetro self
está faltando, a definição correta seria:
Na verdade, você pode encontrar a definição real em https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/Kernel/IOUserClient.cpp#L6388:
Com essas informações, você pode reescrever Ctrl+Right -> Editar assinatura da função
e definir os tipos conhecidos:
O novo código descompilado ficará assim:
Para o próximo passo, precisamos ter definida a estrutura IOExternalMethodDispatch2022
. É de código aberto em https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/IOKit/IOUserClient.h#L168-L176, você pode defini-la:
Agora, seguindo o (IOExternalMethodDispatch2022 *)&sIOExternalMethodArray
você pode ver muitos dados:
Altere o Tipo de Dados para IOExternalMethodDispatch2022:
após a mudança:
E como agora temos um array de 7 elementos (verifique o código descompilado final), clique para criar um array de 7 elementos:
Depois que o array for criado, você pode ver todas as funções exportadas:
Se você se lembra, para chamar uma função exportada do espaço do usuário, não precisamos chamar o nome da função, mas sim o número do seletor. Aqui você pode ver que o seletor 0 é a função initializeDecoder
, o seletor 1 é startDecoder
, o seletor 2 initializeEncoder
...
Last updated