macOS IOKit

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Osnovne informacije

IO Kit je open-source, objektno orijentisani framework za drajvere uređaja u XNU jezgru, koji upravlja dinamički učitanim drajverima uređaja. Omogućava dodavanje modularnog koda u jezgro u hodu, podržavajući različit hardver.

IOKit drajveri će u osnovi izvoziti funkcije iz jezgra. Tipovi parametara ovih funkcija su unapred definisani i provereni. Osim toga, slično kao XPC, IOKit je samo još jedan sloj na vrhu Mach poruka.

IOKit XNU jezgro kod je otvoren od strane Apple-a na https://github.com/apple-oss-distributions/xnu/tree/main/iokit. Osim toga, IOKit komponente u korisničkom prostoru su takođe otvorenog koda https://github.com/opensource-apple/IOKitUser.

Međutim, nijedan IOKit drajver nije otvorenog koda. U svakom slučaju, povremeno izdanje drajvera može doći sa simbolima koji olakšavaju njegovo debagovanje. Pogledajte kako dobiti proširenja drajvera iz firmware-a ovde.**

Napisan je u C++. Možete dobiti demangle C++ simbole sa:

# Get demangled symbols
nm -C com.apple.driver.AppleJPEGDriver

# Demangled symbols from stdin
c++filt
__ZN16IOUserClient202222dispatchExternalMethodEjP31IOExternalMethodArgumentsOpaquePK28IOExternalMethodDispatch2022mP8OSObjectPv
IOUserClient2022::dispatchExternalMethod(unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

IOKit izložene funkcije mogu izvršiti dodatne sigurnosne provere kada klijent pokuša da pozove funkciju, ali imajte na umu da aplikacije obično su ograničene od strane peska-boksa sa kojim IOKit funkcijama mogu da interaguju.

Drajveri

U macOS-u se nalaze u:

  • /System/Library/Extensions

  • KEXT fajlovi ugrađeni u OS X operativni sistem.

  • /Library/Extensions

  • KEXT fajlovi instalirani od strane softvera trećih lica

U iOS-u se nalaze u:

  • /System/Library/Extensions

#Use kextstat to print the loaded drivers
kextstat
Executing: /usr/bin/kmutil showloaded
No variant specified, falling back to release
Index Refs Address            Size       Wired      Name (Version) UUID <Linked Against>
1  142 0                  0          0          com.apple.kpi.bsd (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
2   11 0                  0          0          com.apple.kpi.dsep (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
3  170 0                  0          0          com.apple.kpi.iokit (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
4    0 0                  0          0          com.apple.kpi.kasan (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
5  175 0                  0          0          com.apple.kpi.libkern (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
6  154 0                  0          0          com.apple.kpi.mach (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
7   88 0                  0          0          com.apple.kpi.private (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
8  106 0                  0          0          com.apple.kpi.unsupported (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
9    2 0xffffff8003317000 0xe000     0xe000     com.apple.kec.Libm (1) 6C1342CC-1D74-3D0F-BC43-97D5AD38200A <5>
10   12 0xffffff8003544000 0x92000    0x92000    com.apple.kec.corecrypto (11.1) F5F1255F-6552-3CF4-A9DB-D60EFDEB4A9A <8 7 6 5 3 1>

Do broja 9 navedeni drajveri su učitani na adresi 0. To znači da to nisu pravi drajveri već deo jezgra i ne mogu se istovremeno ukloniti.

Kako biste pronašli određene ekstenzije, možete koristiti:

kextfind -bundle-id com.apple.iokit.IOReportFamily #Search by full bundle-id
kextfind -bundle-id -substring IOR #Search by substring in bundle-id

Da biste učitali i isključili proširenja jezgra, uradite:

kextload com.apple.iokit.IOReportFamily
kextunload com.apple.iokit.IOReportFamily

IORegistry

IORegistry je ključni deo IOKit okvira u macOS-u i iOS-u koji služi kao baza podataka za predstavljanje hardverske konfiguracije i stanja sistema. To je hijerarhijska kolekcija objekata koja predstavlja sav hardver i drajvere učitane na sistemu, kao i njihove međusobne odnose.

IORegistry možete dobiti koristeći CLI ioreg kako biste ga pregledali sa konzole (posebno korisno za iOS).

ioreg -l #List all
ioreg -w 0 #Not cut lines
ioreg -p <plane> #Check other plane

Možete preuzeti IORegistryExplorer iz Dodatnih alata za Xcode sa https://developer.apple.com/download/all/ i pregledati macOS IORegistry kroz grafički interfejs.

U IORegistryExplorer-u, "ravni" se koriste za organizovanje i prikaz odnosa između različitih objekata u IORegistry-ju. Svaka ravan predstavlja određenu vrstu odnosa ili određeni prikaz hardvera sistema i konfiguracije drajvera. Evo nekih od uobičajenih ravni sa kojima možete da se susretnete u IORegistryExplorer-u:

  1. IOService Ravan: Ovo je najopštija ravan, koja prikazuje servisne objekte koji predstavljaju drajvere i nubove (kanale komunikacije između drajvera). Prikazuje odnose između pružalaca usluga i klijenata između ovih objekata.

  2. IODeviceTree Ravan: Ova ravan predstavlja fizičke veze između uređaja kako su povezani sa sistemom. Često se koristi za vizualizaciju hijerarhije uređaja povezanih putem busova poput USB-a ili PCI-ja.

  3. IOPower Ravan: Prikazuje objekte i njihove odnose u smislu upravljanja snagom. Može pokazati koji objekti utiču na stanje snage drugih, što je korisno za otklanjanje problema povezanih sa snagom.

  4. IOUSB Ravan: Specifično fokusirana na USB uređaje i njihove odnose, prikazujući hijerarhiju USB hubova i povezanih uređaja.

  5. IOAudio Ravan: Ova ravan služi za predstavljanje audio uređaja i njihovih odnosa unutar sistema.

  6. ...

Primer koda za komunikaciju sa drajverom

Sledeći kod se povezuje sa IOKit servisom "ImeVašegServisaOvde" i poziva funkciju unutar selektora 0. Za to:

  • prvo poziva IOServiceMatching i IOServiceGetMatchingServices da bi dobio servis.

  • Zatim uspostavlja vezu pozivajući IOServiceOpen.

  • I na kraju poziva funkciju sa IOConnectCallScalarMethod navodeći selektor 0 (selektor je broj koji je dodeljen funkciji koju želite da pozovete).

#import <Foundation/Foundation.h>
#import <IOKit/IOKitLib.h>

int main(int argc, const char * argv[]) {
@autoreleasepool {
// Get a reference to the service using its name
CFMutableDictionaryRef matchingDict = IOServiceMatching("YourServiceNameHere");
if (matchingDict == NULL) {
NSLog(@"Failed to create matching dictionary");
return -1;
}

// Obtain an iterator over all matching services
io_iterator_t iter;
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to get matching services");
return -1;
}

// Get a reference to the first service (assuming it exists)
io_service_t service = IOIteratorNext(iter);
if (!service) {
NSLog(@"No matching service found");
IOObjectRelease(iter);
return -1;
}

// Open a connection to the service
io_connect_t connect;
kr = IOServiceOpen(service, mach_task_self(), 0, &connect);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to open service");
IOObjectRelease(service);
IOObjectRelease(iter);
return -1;
}

// Call a method on the service
// Assume the method has a selector of 0, and takes no arguments
kr = IOConnectCallScalarMethod(connect, 0, NULL, 0, NULL, NULL);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to call method");
}

// Cleanup
IOServiceClose(connect);
IOObjectRelease(service);
IOObjectRelease(iter);
}
return 0;
}

Postoje druge funkcije koje se mogu koristiti za pozivanje IOKit funkcija osim IOConnectCallScalarMethod kao što su IOConnectCallMethod, IOConnectCallStructMethod...

Reversing driver entrypoint

Možete ih dobiti, na primer, iz firmver slike (ipsw). Zatim je učitajte u svoj omiljeni dekompajler.

Možete početi dekompajlirati funkciju externalMethod jer je ovo funkcija drajvera koja će primati poziv i pozivati odgovarajuću funkciju:

Taj užasni poziv demangle znači:

IOUserClient2022::dispatchExternalMethod(unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

Primetite kako je u prethodnoj definiciji propušten parametar self, dobra definicija bi bila:

IOUserClient2022::dispatchExternalMethod(self, unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

Zapravo, pravu definiciju možete pronaći na https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/Kernel/IOUserClient.cpp#L6388:

IOUserClient2022::dispatchExternalMethod(uint32_t selector, IOExternalMethodArgumentsOpaque *arguments,
const IOExternalMethodDispatch2022 dispatchArray[], size_t dispatchArrayCount,
OSObject * target, void * reference)

Sa ovim informacijama možete prepraviti Ctrl+Desno -> Uredi potpis funkcije i postaviti poznate tipove:

Novi dekompajlirani kod će izgledati ovako:

Za sledeći korak potrebno je da imamo definisanu strukturu IOExternalMethodDispatch2022. To je otvorenog koda na https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/IOKit/IOUserClient.h#L168-L176, možete je definisati:

Sada, prateći (IOExternalMethodDispatch2022 *)&sIOExternalMethodArray možete videti puno podataka:

Promenite tip podataka u IOExternalMethodDispatch2022:

nakon promene:

I sada, pošto znamo da imamo niz od 7 elemenata (proverite konačni dekompajlirani kod), kliknite da biste kreirali niz od 7 elemenata:

Nakon što je niz kreiran, možete videti sve izvezene funkcije:

Ako se sećate, da biste pozvali izvezenu funkciju iz korisničkog prostora, ne morate nazvati funkciju, već broj selektora. Ovde možete videti da je selektor 0 funkcija initializeDecoder, selektor 1 je startDecoder, selektor 2 initializeEncoder...

Last updated