As described, what I do was very simple, so I did not provide much more detail. Apart from mprotect and mlock, no low-level stuff or techniques were used. If you need any other details, you can read the demo code.
#import "MallocGuardManager.h"
#include <malloc/malloc.h>
#import "UIKit/UIKit.h"
@interface MallocGuardManager()
{
uintptr_t *_buffers;
int _bufferCount;
int _cachePageSize;
BOOL _use_mlock;
}
@property (nonatomic, strong) NSMutableArray *container;
@property (nonatomic, strong) NSString *templateSource;
@property (nonatomic, strong) dispatch_queue_t queue;
@property (nonatomic, assign) BOOL started;
@property (nonatomic, assign) BOOL executing;
@property (nonatomic, assign) BOOL scheduled;
@end
@implementation MallocGuardManager
+ (instancetype)sharedInstance{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MallocGuardManager alloc] init];
});
return instance;
}
- (instancetype)init{
self = [super init];
if (self) {
_container = [[NSMutableArray alloc] init];
_cachePageSize = getpagesize();
_executing = NO;
_queue = dispatch_queue_create("com.malloc.guard", DISPATCH_QUEUE_SERIAL);
_use_mlock = NO; // If YES, NO crash will happen!!!!
}
return self;
}
#pragma mark - public
- (void)start{
if (self.started) {
return;
}
self.started = YES;
self.executing = YES;
[self scheduleNext];
}
- (void)stop{
self.started = NO;
self.executing = NO;
}
#pragma mark -
- (void)scheduleNext{
dispatch_async(self.queue, ^{
if (self.scheduled) {return;}
self.scheduled = YES;
NSTimeInterval delay = arc4random() % 7 + 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), self.queue, ^{
self.scheduled = NO;
[self setupGuardPages];
});
});
}
- (void)protect{
for (int index = 0; index < _bufferCount; index++) {
uintptr_t ptr = *(_buffers + index);
uintptr_t *isa = (uintptr_t *)ptr;
Log(@"protect:%d, 0x%lx, 0x%lx", index, ptr, *isa);
if (mprotect((void*)ptr, _cachePageSize, PROT_READ) == -1) {
Log(@"mprotect failed");
}
if (_use_mlock && mlock((const void *)ptr, _cachePageSize) == -1) {
Log(@"mlock failed");
}
Log(@"protected:%d, 0x%lx, 0x%lx", index, ptr, *isa);
}
}
- (void)createPages{
NSInteger total = 0;
NSInteger i = 0;
for (NSInteger index = 0; index < 20 && total < 20 * 1024 * 1024; index++) {
if (!self.executing) {
return;
}
NSInteger pageCout = arc4random() % (800*1024/_cachePageSize);
NSInteger strLen = MIN((pageCout*_cachePageSize + arc4random()% _cachePageSize), self.templateSource.length);
NSString *string = [self.templateSource substringWithRange:NSMakeRange(0, strLen)];
if ((uintptr_t)string % _cachePageSize == 0 && malloc_size((__bridge void *)string) > _cachePageSize) {
total += strLen;
[self.container addObject:string];
Log(@"add:%ld, %p, %lu", (long)i, string, (unsigned long)string.length);
i++;
}
}
}
- (void)unprotect{
for (int index = 0; index < _bufferCount; index++) {
uintptr_t ptr = *(_buffers + index);
uintptr_t *isa = (uintptr_t *)ptr;
uintptr_t *ptr1 = (uintptr_t *)(ptr+8);
uintptr_t *ptr2 = (uintptr_t *)(ptr+24);
Log(@"unprotect:%d, 0x%lx, 0x%lx, %ld, 0x%lx, 0x%lx", index, ptr, *isa, malloc_size((const void *)ptr), *ptr1, *ptr2);
if (*isa == 0) {
// It crashed, proving readonly protection working correctly!!!!
*isa = 0x10;
mprotect((void*)ptr, _cachePageSize, PROT_READ);
*isa = 0x20;
}
if (mprotect((void*)ptr, _cachePageSize, PROT_READ|PROT_WRITE) == -1) {
Log(@"mprotect fail");
}
Log(@"unprotected:%d, 0x%lx, 0x%lx", index, ptr, *isa);
[self.container removeObjectAtIndex:0];
if (_use_mlock && munlock((const void *)ptr, _cachePageSize) == -1) {
Log(@"munlock failed");
}
}
}
- (void)setupGuardPages{
@autoreleasepool {
[self unprotect];
if (_buffers) {
_bufferCount = 0;
free(_buffers);
_buffers = NULL;
}
if (self.executing == NO) {
self.templateSource = nil;
return;
}
if (self.templateSource.length == 0) {
self.templateSource = [@"" stringByPaddingToLength:800*1024
withString:@"5656"
startingAtIndex:0];
}
[self createPages];
_bufferCount = (int)self.container.count;
if (_bufferCount > 0) {
_buffers = malloc(_bufferCount * sizeof(uintptr_t));
int index = 0;
for (id object in self.container) {
uintptr_t ptr = (uintptr_t)object;
*(_buffers + index) = ptr;
index++;
}
}
}
[self protect];
[self scheduleNext];
}
@end
Topic:
App & System Services
SubTopic:
Core OS
Tags: