HOWTO use private KPIs in kernel extension
grep(1) kpi upon kextstat(8), you will find a special kernel extension named com.apple.kpi.private
:
$ kextstat | grep kpi
1 106 0xffffff7f80a00000 0x9e30 0x9e30 com.apple.kpi.bsd (17.7.0) 8CF59A67-A723-4FB8-A127-09A4B32E1874
2 10 0xffffff7f80f16000 0x3990 0x3990 com.apple.kpi.dsep (17.7.0) E085363B-71AD-4ED8-A63E-3EEC4F643ED9
3 134 0xffffff7f80a29000 0x21150 0x21150 com.apple.kpi.iokit (17.7.0) 695D2CC5-AB90-4CD4-9862-2C50A3A989B8
4 140 0xffffff7f80a0a000 0xd430 0xd430 com.apple.kpi.libkern (17.7.0) 26F1FC40-6578-4254-AF12-3D397814B5CB
5 126 0xffffff7f80a18000 0x3f60 0x3f60 com.apple.kpi.mach (17.7.0) 05B0DB8B-BF52-4A99-B18A-BE623583B448
6 81 0xffffff7f80a1c000 0xce70 0xce70 com.apple.kpi.private (17.7.0) 409868DB-B2D5-4249-A98A-3C2BC6E7A754
7 82 0xffffff7f80a8e000 0x5ea0 0x5ea0 com.apple.kpi.unsupported (17.7.0) 37241BAB-747F-4D47-BBD5-C01F3577D845
Unlike com.apple.kpi.unsupported, com.apple.kpi.private is used solely by Apple itself.
It located at /System/Library/Extensions/System.kext/PlugIns/Private.kext, you can nm(1) it to check its symbol table.
Taking strnstr as an example, to show how you can use this private KPI function.
It’s OK to compile the following kext listing:
#include <mach/mach_types.h>
#include <mach/kmod.h>
#include <libkern/libkern.h>
#include <string.h> /* strnstr is exported */
kern_return_t kext_null_start(kmod_info_t *ki, void *d)
{
char *name = strnstr(ki->name, "", KMOD_MAX_NAME);
char *ver = strnstr(ki->version, "", KMOD_MAX_NAME);
printf(">> %s %s\n", name, ver);
return KERN_SUCCESS;
}
kern_return_t kext_null_stop(kmod_info_t *ki, void *d)
{
return KERN_SUCCESS;
}
Use kextlibs(8) check its dependencies, it output:
$ kextlibs -c -xml -unsupported kext-null.kext
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.libkern</key>
<string>8.0d0</string>
<key>com.apple.kpi.private</key>
<string>8.0b1</string>
</dict>
Which for sure strnstr exported by com.apple.kpi.private.
When you load them via kextload(8) or kextutil(8), the kextd(8)(kext backing daemon) will prompts you:
$ sudo kextload kext-null.kext
/private/tmp/kext-null.kext failed to load - (libkern/kext) dependency load failed; check the system/kernel logs for errors or try kextutil(8).
# System log
...
default 13:12:04.352316 +0800 kextd Kext with invalid signatured (-67050) allowed: <OSKext 0x7ff6fdb2e590 [0x7fff8d099af0]> { URL = "file:///private/tmp/kext-null.kext/", ID = "cn.junkman.kext-null" }
default 13:12:04.396342 +0800 kextd /private/tmp/kext-null.kext has an Apple prefix but no copyright.
default 13:12:04.396376 +0800 kextd cn.junkman.kext-null's dependencies failed security checks; failing.
...
Apple certainly don’t want developers use the com.apple.kpi.private, yet still ways to jailbreak it.
If you dive deep into Apple source code and search upon the string “has an Apple prefix but no copyright”, you’ll find IOKitUser/IOKitUser/kext.subproj/OSKext.c’s __OSKextResolveDependencies snippet:
#ifndef IOKIT_EMBEDDED
/* If the kext links against the private KPI, we want to make an effort
* to ensure they are Apple-internal kexts. We do this by verifying that
* the kext's bundle identifier begins with "com.apple.", and by making
* sure the kext's info dictionary contains an Apple copyright string
* in either CFBundleGetInfoString (obsolete) or NSHumanReadableCopyright.
*/
if (aKext->loadInfo->flags.hasPrivateKPIDependency)
{
CFStringRef infoString = NULL; // do not release
CFStringRef readableString = NULL; // do not release
Boolean hasApplePrefix = false;
Boolean infoCopyrightIsValid = false;
Boolean readableCopyrightIsValid = false;
hasApplePrefix = CFStringHasPrefix(aKext->bundleID, __kOSKextApplePrefix);
infoString = (CFStringRef) OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR("CFBundleGetInfoString"));
if (infoString) {
char *infoCString = createUTF8CStringForCFString(infoString);
infoCopyrightIsValid = kxld_validate_copyright_string(infoCString);
SAFE_FREE(infoCString);
}
readableString = (CFStringRef) OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR("NSHumanReadableCopyright"));
if (readableString) {
char *readableCString = createUTF8CStringForCFString(readableString);
readableCopyrightIsValid =
kxld_validate_copyright_string(readableCString);
SAFE_FREE(readableCString);
}
if (!hasApplePrefix || (!infoCopyrightIsValid && !readableCopyrightIsValid)) {
OSKextLog(aKext, kOSKextLogErrorLevel | kOSKextLogDependenciesFlag,
"%s has an Apple prefix but no copyright.", kextPath);
__OSKextSetDiagnostic(aKext, kOSKextDiagnosticsFlagDependencies,
kOSKextDiagnosticNonAppleKextDeclaresPrivateKPIDependencyKey);
result = false;
goto finish;
}
}
#endif /* !IOKIT_EMBEDDED */
So it takes two criterions to load a private-dependent kext:
If the kext links against the private KPI, we want to make an effort to ensure they are Apple-internal kexts. We do this by verifying that the kext’s bundle identifier begins with “com.apple.”, and by making sure the kext’s info dictionary contains an Apple copyright string in either CFBundleGetInfoString (obsolete) or NSHumanReadableCopyright.
E.g.
-
Modify your kext bundle identifier begin with “com.apple.”
-
Make NSHumanReadableCopyright in form of “Copyright © XXXX Apple Inc. All rights reserved.”, which XXXX is a year number.
After change those properties in kext’s Info.plist, you can use private KPIs for sure.
$ cat Info.plist | grep -E "CFBundleIdentifier|NSHumanReadableCopyright" -A 1
<key>CFBundleIdentifier</key>
<string>com.apple.kext.3rd.kext-null</string>
--
<key>NSHumanReadableCopyright</key>
<string>Copyright © 0000 Apple Inc. All rights reserved.</string>
$ sudo kextload kext-null.kext
$ kextstat | grep kext-null
188 0 0xffffff7f83705000 0x2000 0x2000 com.apple.kext.3rd.kext-null (1) 927FE5AE-4720-36EC-897C-2675A0FE8913 <6 4>
$ sudo dmesg | grep ">>"
>> com.apple.kext.3rd.kext-null 1
Yet, not all symbols exported by Apple, for example, vnode_isautocandidate is exported in Kernel.framework’s sys/vnode.h, yet vnode_usecount and vnode_iocount not exported.
In such cases, you can simply extern it, it will link against com.apple.kpi.private naturally:
#include <mach/mach_types.h>
#include <libkern/libkern.h>
#include <sys/vnode.h>
/* Those symbols not exported by Kernel.framework */
extern int hz;
extern struct vnode *rootvp;
extern int vnode_usecount(vnode_t vp);
extern int vnode_iocount(vnode_t vp);
kern_return_t kext_null_start(kmod_info_t *ki, void *d)
{
printf(">> %s %s\n", ki->name, ki->version);
printf(">> hz: %d\n", hz);
printf(">> rootvp: %p use: %d io: %d\n",
rootvp, vnode_usecount(rootvp), vnode_iocount(rootvp));
}
kern_return_t kext_null_stop(kmod_info_t *ki, void *d)
{
return KERN_SUCCESS;
}
$ sudo kextload kext-null.kext
$ sudo dmesg | grep ">>"
>> com.apple.kext.3rd.kext-null 1
>> hz: 100
>> rootvp: <ptr> use: 2 io: 0
Though it takes no effort to jailbreak such restriction, I personally still not recommend you guys do this. Basically it for Apple internal use.
In an incoming article(If I’m free), I’ll illustrate HOWTO resolve kernel symbols even if they’re missing in KPIs.