Alfredo Galli
Alfredo Galli

Reputation: 63

IOKit using dlopen

In my app I'm getting battery infos using this way (via IOKit).

static void print_raw_battery_state(io_registry_entry_t b_reg) {
CFBooleanRef            boo;
CFNumberRef             n;
int                     tmp;
int                     cur_cap = -1;
int                     max_cap = -1;
CFMutableDictionaryRef  prop = NULL;
IOReturn                ret;

ret = IORegistryEntryCreateCFProperties(b_reg, &prop, 0, 0);
if( (kIOReturnSuccess != ret) || (NULL == prop) )
{
    printf("Couldn't read battery status; error = 0%08x\n", ret);
    return;
}

boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSExternalConnectedKey));
printf("  external connected = %s\n", 
       (kCFBooleanTrue == boo) ? "yes" : "no");

boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSBatteryInstalledKey));
printf("  battery present = %s\n", 
       (kCFBooleanTrue == boo) ? "yes" : "no");

boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSIsChargingKey));
printf("  battery charging = %s\n", 
       (kCFBooleanTrue == boo) ? "yes" : "no");

n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSCurrentCapacityKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &cur_cap);
}
n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSCurrentCapacityKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &max_cap);
}

if( (-1 != cur_cap) && (-1 != max_cap) )
{
    printf("  cap = %d/%d\n", cur_cap, max_cap);

    gcurCapacity = cur_cap;//hab
    gmaxCapacity = max_cap;//hab

}

n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSTimeRemainingKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &tmp);
   NSLog(@"  time REM = %d:%02d\n", tmp/60, tmp%60);
printf("time cicA = %d:%02d\n", tmp/60, tmp%60);
    NSString *stringTimeRem = [NSString stringWithFormat:@"%d:%02d", tmp/60, tmp%60];
    [[NSUserDefaults standardUserDefaults] setObject:stringTimeRem forKey:@"stringREM"];
}

n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSAmperageKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &tmp);
    gcurrent = tmp;//hab
    printf("  current = %d\n", tmp);
}
n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSCycleCountKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &tmp);
    printf("  cycle count = %d\n", tmp);

    gloadcycles = tmp;//hab
}
n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSLocationKey));
if(n) {
    CFNumberGetValue(n, kCFNumberIntType, &tmp);
    printf("  location = %d\n", tmp);
}

printf("\n");

CFRelease(prop);
return;}

How can I access those infos using dlopen ? I need to get IOReturn and io_registry_entry_t using dlopen, so Apple won't probably find that IOKit is there. Thanks.

Upvotes: 1

Views: 1202

Answers (3)

Michael N
Michael N

Reputation: 559

#import "ViewController.h"
#import "IOKit/IOKitLib.h"
#import "IOKit/IOPSKeys.h"
#include <dlfcn.h>

@interface ViewController ()

@end        

@implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        static mach_port_t *s_kIOMasterPortDefault;
        static kern_return_t (*s_IORegistryEntryCreateCFProperties)(mach_port_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, UInt32 options);
        static mach_port_t (*s_IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
        static CFMutableDictionaryRef (*s_IOServiceMatching)(const char *name);

        static CFMutableDictionaryRef g_powerSourceService;
        static mach_port_t g_platformExpertDevice;

        static BOOL foundSymbols = NO;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{

            void* handle = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_LAZY);
            s_IORegistryEntryCreateCFProperties = dlsym(handle, "IORegistryEntryCreateCFProperties");
            s_kIOMasterPortDefault = dlsym(handle, "kIOMasterPortDefault");
            s_IOServiceMatching = dlsym(handle, "IOServiceMatching");
            s_IOServiceGetMatchingService = dlsym(handle, "IOServiceGetMatchingService");

            if (s_IORegistryEntryCreateCFProperties && s_IOServiceMatching && s_IOServiceGetMatchingService) {
                g_powerSourceService = s_IOServiceMatching("IOPMPowerSource");
                g_platformExpertDevice = s_IOServiceGetMatchingService(*s_kIOMasterPortDefault, g_powerSourceService);
                foundSymbols = (g_powerSourceService && g_platformExpertDevice);

            }
        });

        if (!foundSymbols) {

            return;

        }

        print_raw_battery_state(g_platformExpertDevice);

    }

    static void print_raw_battery_state(io_registry_entry_t b_reg) {

        CFBooleanRef            boo;
        CFNumberRef             n;
        int                     tmp;
        int                     cur_cap = -1;
        int                     max_cap = -1;
        CFMutableDictionaryRef  prop = NULL;
        IOReturn                ret;

        ret = IORegistryEntryCreateCFProperties(b_reg, &prop, 0, 0);

        if( (kIOReturnSuccess != ret) || (NULL == prop) )
        {
            printf("Couldn't read battery status; error = 0%08x\n", ret);
            return;
        }

        // battery dictionary

        NSLog(@"prop: %@", prop);

       printf("\n");

       CFRelease(prop);

       return;

    }

}

@end

In iOS 11 and 12, you will only get a few values.

Upvotes: 0

C0deH4cker
C0deH4cker

Reputation: 4055

H2CO3 is correct. Just #includeing IOKit doesn't affect the compiled code at all. It's when you directly use the functions defined in the IOKit headers and link against IOKit that the private framework can clearly be seen in your app. One form of obfuscation for hiding the private framework and symbols used is a simple cipher shift. For example, using H2CO3's example:

// Deciphers string in-place and returns a pointer to it
__attribute__((always_inline)) char* decipher(char* str) {
    int i, n = strlen(str);
    for(i = 0; i < n; i++) str[i]--;
    return str;
}
//...
char sym[] = "JPSfhjtuszFouszDsfbufDGQspqfsujft";
char fr[] = "Gsbnfxpslt";
char fmwk[] = "JPLju";
char lib[60];
decipher(fmwk);
sprintf(lib, "/System/Library/%s/%s.framework/Versions/A/%s", fr, fmwk, fmwk);
void* handle = dlopen(lib, RTLD_LAZY);
IOReturn (*fptr)(io_registry_event_t, CFMutableDictionaryRef*,i int, int);
fptr = dlsym(handle, decipher(sym));
// Now just call fptr() as if it were IORegistryEntryCreateCFProperties()

The difference between this and H2CO3's version is that this will prevent people manually analyzing the app from finding IOKit at all without a reasonable amount of expertise. Of course, a determined reverser can still find the usage of IOKit no matter how well you try to hide it. Obfuscation is useful here because App Store reviewers likely won't devote their time to reversing your app for days. Also, remember to strip your app before you submit it. I think stripping is enabled by default in Xcode, but I'm not sure. You'll need to figure that one out yourself.

Upvotes: 0

user529758
user529758

Reputation:

I need to get IOReturn and io_registry_entry_t using dlopen

Those are just types. They're declared in header files, and not compiled into the IOKit library. You can't get those using dlopen().

so Apple won't probably find that IOKit is there

Again, types aren't explicitly represented in the binary. If Apple finds out that you're using IOKit, then the reason for that will not be the use of these types. They're the function names that betray.

However, if you use dlopen() to retrieve a pointer to these functions, the strings representing function names will still be an open book to Apple's static analysis tools. You may have to do some additional obfuscation in order the binary not to expose directly the private function names:

NSString *secondPart = @"ateCFProperties";
NSString *firstPart = @"IORegistryEntryCre";

const char *fname = [[firstPart stringByAppendingString:secondPart] UTF8String];
IOReturn (*fptr)(io_registry_entry_t, CFMutableDictionaryRef *, int, int);
fptr = dlsym(dyld_handle, fname);

etc.

Upvotes: 2

Related Questions