macOS IOKit
Basic Information
O I/O Kit é um framework de driver de dispositivo orientado a objetos e 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 em tempo real, suportando hardware diversificado.
Os drivers do IOKit basicamente exportam funções do kernel. Os tipos de parâmetros dessas funções são pré-definidos e são verificados. Além disso, semelhante ao XPC, o IOKit é apenas mais uma camada sobre as 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. Confira como obter as extensões do driver do firmware aqui.
Está escrito em C++. Você pode obter símbolos C++ demangled com:
As funções expostas do IOKit podem realizar verificações de segurança adicionais quando um cliente tenta chamar uma função, mas note que os aplicativos geralmente são limitados pelo sandbox com o qual as funções do IOKit podem interagir.
Drivers
No macOS, eles estão localizados em:
/System/Library/Extensions
Arquivos KEXT incorporados no 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 estã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 do 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 e o estado do hardware 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).
Você pode baixar IORegistryExplorer
das Xcode Additional Tools em https://developer.apple.com/download/all/ e inspecionar o macOS IORegistry através de uma interface gráfica.
No IORegistryExplorer, "planos" são usados para organizar e exibir os relacionamentos entre diferentes objetos no IORegistry. Cada plano representa um tipo específico de relacionamento ou uma visão particular da configuração de hardware e drivers do sistema. Aqui estão alguns dos planos comuns que você pode encontrar no IORegistryExplorer:
IOService Plane: Este é o plano mais geral, exibindo os objetos de serviço que representam drivers e nubs (canais de comunicação entre drivers). Ele mostra os relacionamentos de provedor-cliente entre esses objetos.
IODeviceTree Plane: Este plano representa as conexões físicas entre dispositivos à medida que são conectados ao sistema. É frequentemente usado para visualizar a hierarquia de dispositivos conectados via barramentos como USB ou PCI.
IOPower Plane: Exibe objetos e seus relacionamentos 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.
IOUSB Plane: Focado especificamente em dispositivos USB e seus relacionamentos, mostrando a hierarquia de hubs USB e dispositivos conectados.
IOAudio Plane: Este plano é para representar dispositivos de áudio e seus relacionamentos dentro do sistema.
...
Exemplo de Código de Comunicação de Driver
O seguinte código conecta-se ao serviço IOKit "YourServiceNameHere"
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 que a função que você deseja chamar recebeu).
Existem outras funções que podem ser usadas para chamar funções do IOKit além de IOConnectCallScalarMethod
, como IOConnectCallMethod
, IOConnectCallStructMethod
...
Reversão do ponto de entrada do driver
Você pode obter esses, por exemplo, de uma imagem de firmware (ipsw). Em seguida, carregue-a em seu descompilador favorito.
Você pode começar a descompilar a função externalMethod
, pois esta é a função do driver que receberá a chamada e chamará a função correta:
Aquela chamada horrível demangled significa:
Observe como na definição anterior o parâmetro self
está ausente, a boa definição 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 -> Edit function signature
e definir os tipos conhecidos:
O novo código decompilado ficará assim:
Para o próximo passo, precisamos ter definida a estrutura IOExternalMethodDispatch2022
. Ela é open source 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 alteração:
E como sabemos, lá temos um array de 7 elementos (verifique o código decompilado final), clique para criar um array de 7 elementos:
Após o array ser 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 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