1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-04 10:27:46 +01:00
Telegram/legacy/TelegraphKit/Freedom.mm
2015-10-01 19:19:52 +03:00

661 lines
19 KiB
Plaintext

/*
* This is the source code of Telegram for iOS v. 1.1
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Ernesto Guevara, 2014.
*/
#import "Freedom.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <map>
#include <inttypes.h>
#define FORCE_INLINE __attribute__((always_inline))
static inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
return (x << r) | (x >> (32 - r));
}
#define ROTL32(x,y) rotl32(x,y)
static FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i )
{
return p[i];
}
static FORCE_INLINE uint32_t fmix ( uint32_t h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
static void MurmurHash3_x86_32 ( const void * key, int len,
uint32_t seed, void * out )
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
for(int i = -nblocks; i; i++)
{
uint32_t k1 = getblock(blocks,i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
uint32_t k1 = 0;
switch(len & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 = fmix(h1);
*(uint32_t*)out = h1;
}
static int32_t murMurHashBytes32(void *bytes, int length)
{
int32_t result = 0;
MurmurHash3_x86_32(bytes, length, -137723950, &result);
return result;
}
static const char *freedomDecoratedClass = "freedomDecoratedClass";
static int (*freedom_getClassList)(Class *, int) = NULL;
static const char * (*freedom_class_getName)(Class cls) = NULL;
FreedomIdentifier FreedomIdentifierEmpty = (FreedomIdentifier){ .string = NULL, .key = 0 };
char *copyFreedomIdentifierValue(FreedomIdentifier identifier)
{
if (identifier.string == NULL)
return NULL;
int length = (int)strlen(identifier.string) / 2;
char *buf = (char *)malloc(length + 1);
buf[length] = 0;
for (int i = 0; i < length; i++)
{
int b = 0;
sscanf(identifier.string + i * 2, "%02x", &b);
buf[i] = ((char)b) ^ (((uint8_t *)&identifier.key)[i % 4]);
}
return buf;
}
Class freedomClass(uint32_t name)
{
static std::map<uint32_t, __unsafe_unretained Class> classMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
int classCount = freedom_getClassList(NULL, 0);
if (classCount > 0)
{
__unsafe_unretained Class *classList = (Class *)malloc(classCount * sizeof(Class));
objc_getClassList(classList, classCount);
for (int i = 0; i < classCount; i++)
{
const char *className = freedom_class_getName(classList[i]);
uint32_t hashName = (uint32_t)murMurHashBytes32((void *)className, (int)strlen(className));
classMap[hashName] = classList[i];
}
free(classList);
}
});
auto it = classMap.find(name);
if (it != classMap.end())
return it->second;
return nil;
}
Class freedomMakeClass(Class superclass, Class subclass)
{
if (superclass == Nil || subclass == Nil)
return nil;
int32_t randomId = 0;
arc4random_buf(&randomId, 4);
Class decoratedClass = objc_allocateClassPair(superclass, [[NSString alloc] initWithFormat:@"Decorated%" PRIx32 "", randomId].UTF8String, 0);
unsigned int count = 0;
Method *methodList = class_copyMethodList(subclass, &count);
if (methodList != NULL)
{
for (unsigned int i = 0; i < count; i++)
{
class_addMethod(decoratedClass, method_getName(methodList[i]), method_getImplementation(methodList[i]), method_getTypeEncoding(methodList[i]));
}
free(methodList);
}
objc_registerClassPair(decoratedClass);
return decoratedClass;
}
ptrdiff_t freedomIvarOffset(Class targetClass, uint32_t name)
{
ptrdiff_t result = -1;
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(targetClass, &count);
if (ivarList != NULL)
{
for (unsigned int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivarList[i]);
uint32_t hashName = (uint32_t)murMurHashBytes32((void *)ivarName, (int)strlen(ivarName));
//NSLog(@"%s -> 0x%x", ivarName, hashName);
if (hashName == name)
{
result = ivar_getOffset(ivarList[i]);
}
}
free(ivarList);
}
return result;
}
static bool consumeToken(const char *desc, int length, int &offset, const char *token)
{
size_t tokenLength = strlen(token);
if (offset + (int)tokenLength > length)
return false;
if (strncmp(desc + offset, token, tokenLength))
return false;
offset += tokenLength;
return true;
}
static bool consumeNumber(const char *desc, int length, int &offset, int &number)
{
if (offset + 1 > length)
return false;
int result = 0;
for (int i = offset; i < length; i++)
{
if (desc[i] < '0' || desc[i] > '9')
{
if (i != offset)
{
number = result;
offset = i;
return true;
}
return false;
}
else
result = result * 10 + (desc[i] - '0');
}
return false;
}
static bool consumeField(const char *desc, int length, int &offset, uint32_t &outName, int &outBitSize)
{
if (!consumeToken(desc, length, offset, "\""))
return false;
for (int i = offset; i < length; i++)
{
if (desc[i] == '"')
{
outName = (uint32_t)murMurHashBytes32((void *)(desc + offset), i - offset);
char buf[i - offset + 1];
memcpy(buf, desc + offset, i - offset);
buf[i - offset] = 0;
int newOffset = i + 1;
if (consumeToken(desc, length, newOffset, "b"))
{
int fieldLength = 0;
if (consumeNumber(desc, length, newOffset, fieldLength))
{
offset = newOffset;
outBitSize = fieldLength;
//NSLog(@"%s", buf);
return true;
}
else
return false;
}
else if (consumeToken(desc, length, newOffset, "I"))
{
offset = newOffset;
outBitSize = sizeof(unsigned int) * 8;
//NSLog(@"%s", buf);
return true;
}
else
return false;
break;
}
}
return false;
}
static int freedomBitfieldOffsetInBits(const char *desc, uint32_t name)
{
size_t length = strlen(desc);
int offset = 0;
if (!consumeToken(desc, (int)length, offset, "{?="))
return -1;
int currentBitOffset = 0;
while (true)
{
uint32_t fieldName = 0;
int bitLength = 0;
if (consumeField(desc, (int)length, offset, fieldName, bitLength))
{
if (fieldName == name)
return currentBitOffset;
currentBitOffset += bitLength;
}
else
break;
}
return -1;
}
static void freedomDumpBitfieldsByDescription(void *address, const char *desc)
{
size_t length = strlen(desc);
int offset = 0;
if (!consumeToken(desc, (int)length, offset, "{?="))
return;
int currentBitOffset = 0;
while (true)
{
uint32_t fieldName = 0;
int bitLength = 0;
if (consumeField(desc, (int)length, offset, fieldName, bitLength))
{
if (bitLength == 1)
{
uint8_t *value = ((uint8_t *)address) + currentBitOffset / 8 + (currentBitOffset % 8 == 0 ? 0 : 1);
NSLog(@" : %d", ((*value) & ((uint8_t)(1 << (8 - currentBitOffset % 8)))) != 0 ? 1 : 0);
}
currentBitOffset += bitLength;
}
else
break;
}
}
FreedomBitfield freedomIvarBitOffset(Class targetClass, uint32_t fieldName, uint32_t bitfieldName)
{
FreedomBitfield result = {.offset = -1, .bit = -1 };
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(targetClass, &count);
if (ivarList != NULL)
{
for (unsigned int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivarList[i]);
uint32_t hashName = (uint32_t)murMurHashBytes32((void *)ivarName, (int)strlen(ivarName));
//NSLog(@"%s -> 0x%x", ivarName, hashName);
if (hashName == fieldName)
{
ptrdiff_t ivarOffset = ivar_getOffset(ivarList[i]);
int bitOffset = freedomBitfieldOffsetInBits(ivar_getTypeEncoding(ivarList[i]), bitfieldName);
if (bitOffset >= 0)
{
result.offset = ivarOffset + bitOffset / 8 + (bitOffset % 8 == 0 ? 0 : 1);
result.bit = bitOffset % 8;
}
}
}
free(ivarList);
}
return result;
}
FreedomBitfield freedomIvarBitOffset2(Class targetClass, uint32_t fieldName, uint32_t bitfieldName)
{
FreedomBitfield result = {.offset = -1, .bit = -1 };
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(targetClass, &count);
if (ivarList != NULL)
{
for (unsigned int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivarList[i]);
uint32_t hashName = (uint32_t)murMurHashBytes32((void *)ivarName, (int)strlen(ivarName));
//NSLog(@"%s -> 0x%x", ivarName, hashName);
if (hashName == fieldName)
{
ptrdiff_t ivarOffset = ivar_getOffset(ivarList[i]);
int bitOffset = freedomBitfieldOffsetInBits(ivar_getTypeEncoding(ivarList[i]), bitfieldName);
if (bitOffset >= 0)
{
result.offset = ivarOffset + bitOffset / 8;
result.bit = bitOffset % 8;
}
}
}
free(ivarList);
}
return result;
}
void freedomSetBitfield(void *object, FreedomBitfield bitfield, int value)
{
if (object == nil || bitfield.offset < 0 || bitfield.bit < 0)
return;
uint8_t *bytePtr = (((uint8_t *)object) + bitfield.offset);
if (value != 0)
*bytePtr = (*bytePtr) | ((uint8_t)(1 << (bitfield.bit)));
else
*bytePtr = (*bytePtr) & ((uint8_t)~(1 << (bitfield.bit)));
}
int freedomGetBitfield(void *object, FreedomBitfield bitfield)
{
if (object == nil || bitfield.offset < 0 || bitfield.bit < 0)
return 0;
uint8_t *bytePtr = (((uint8_t *)object) + bitfield.offset);
return ((*bytePtr) & ((uint8_t)(1 << (bitfield.bit)))) != 0;
}
void freedomDumpBitfields(Class targetClass, void *object, uint32_t fieldName)
{
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(targetClass, &count);
if (ivarList != NULL)
{
for (unsigned int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivarList[i]);
uint32_t hashName = (uint32_t)murMurHashBytes32((void *)ivarName, (int)strlen(ivarName));
if (hashName == fieldName)
{
ptrdiff_t ivarOffset = ivar_getOffset(ivarList[i]);
freedomDumpBitfieldsByDescription(((uint8_t *)object) + ivarOffset, ivar_getTypeEncoding(ivarList[i]));
}
}
free(ivarList);
}
}
IMP freedomNativeImpl(Class targetClass, SEL selector)
{
return class_getMethodImplementation(class_getSuperclass(targetClass), selector);
}
Class adjustDecoratedClass(Class targetClass)
{
__unsafe_unretained Class decoratedClass = Nil;
__unsafe_unretained Class currentClass = targetClass;
while (currentClass != Nil)
{
__unsafe_unretained Class currentDecoratedClass = objc_getAssociatedObject(currentClass, freedomDecoratedClass);
if (currentDecoratedClass != Nil)
{
decoratedClass = currentDecoratedClass;
if (currentClass != targetClass)
{
const char *currentClassName = class_getName(targetClass);
Class adjustedDecoratedClass = objc_allocateClassPair(targetClass, [[NSString alloc] initWithFormat:@"%s_Super%" PRIx32 "", class_getName(decoratedClass), murMurHashBytes32((void *)currentClassName, (int)strlen(currentClassName))].UTF8String, 0);
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(decoratedClass, &methodCount);
for (unsigned int i = 0; i < methodCount; i++)
{
class_addMethod(adjustedDecoratedClass, method_getName(methodList[i]), method_getImplementation(methodList[i]), method_getTypeEncoding(methodList[i]));
}
free(methodList);
objc_registerClassPair(adjustedDecoratedClass);
decoratedClass = adjustedDecoratedClass;
objc_setAssociatedObject(targetClass, freedomDecoratedClass, adjustedDecoratedClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
break;
}
currentClass = class_getSuperclass(currentClass);
}
return decoratedClass;
}
id freedomAllocImpl(id self, SEL _cmd)
{
static id (*nativeImp)(id, SEL) = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
nativeImp = (id (*)(id, SEL))class_getMethodImplementation(object_getClass([NSObject class]), @selector(alloc));
});
id result = nativeImp(self, _cmd);
Class decoratedClass = adjustDecoratedClass(self);
if (decoratedClass != Nil)
object_setClass(result, decoratedClass);
return result;
}
static SEL freedomFindMethodName(uint32_t name, Class className, Method *methodList, unsigned int methodCount, const char **methodTypeEncoding)
{
for (unsigned int j = 0; j < methodCount; j++)
{
SEL methodName = method_getName(methodList[j]);
const char *selectorName = sel_getName(methodName);
uint32_t methodHashName = (uint32_t)murMurHashBytes32((void *)selectorName, (int)strlen(selectorName));
//NSLog(@"0x%" PRIx32 " -> %s", methodHashName, selectorName);
if (methodHashName == name)
{
if (methodTypeEncoding != NULL)
*methodTypeEncoding = method_getTypeEncoding(methodList[j]);
return methodName;
}
}
Class superClass = class_getSuperclass(className);
if (superClass != NULL)
{
unsigned int superClassMethodCount = 0;
Method *superClassMethodList = class_copyMethodList(superClass, &superClassMethodCount);
SEL result = freedomFindMethodName(name, superClass, superClassMethodList, superClassMethodCount, methodTypeEncoding);
free(superClassMethodList);
return result;
}
return NULL;
}
void freedomClassAutoDecorate(uint32_t name, __unused FreedomDecoration *classDecorations, __unused int numClassDecorations, FreedomDecoration *instanceDecorations, int numInstanceDecorations)
{
__unsafe_unretained Class className = freedomClass(name);
if (className != Nil)
{
Class decoratedClass = objc_allocateClassPair(className, [[NSString alloc] initWithFormat:@"Decorated%" PRIx32 "", name].UTF8String, 0);
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(className, &methodCount);
for (int i = 0; i < numInstanceDecorations; i++)
{
if (instanceDecorations[i].name != 0)
{
const char *methodTypeEncoding = NULL;
SEL methodName = freedomFindMethodName(instanceDecorations[i].name, className, methodList, methodCount, &methodTypeEncoding);
if (methodName != NULL)
class_addMethod(decoratedClass, methodName, instanceDecorations[i].imp, methodTypeEncoding);
else
{
#ifdef DEBUG
NSLog(@"[Freedom coulnd'n find method named 0x%" PRIx32 "]", instanceDecorations[i].name);
#endif
}
}
else if (instanceDecorations[i].newIdentifier.string != NULL && instanceDecorations[i].newEncoding.string != NULL)
{
char *identifier = copyFreedomIdentifierValue(instanceDecorations[i].newIdentifier);
char *encoding = copyFreedomIdentifierValue(instanceDecorations[i].newEncoding);
SEL selector = sel_getUid(identifier);
if (!sel_isMapped(selector))
selector = sel_registerName(identifier);
class_addMethod(decoratedClass, selector, instanceDecorations[i].imp, encoding);
free(identifier);
free(encoding);
}
else
{
#ifdef DEBUG
NSLog(@"[Freedom invalid decoration description]");
#endif
}
}
free(methodList);
objc_registerClassPair(decoratedClass);
objc_setAssociatedObject(className, freedomDecoratedClass, decoratedClass, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Class metaClass = object_getClass(className);
class_addMethod(metaClass, @selector(alloc), (IMP)&freedomAllocImpl, "@:@");
}
else
{
#ifdef DEBUG
NSLog(@"[Freedom coulnd'n find class named 0x%" PRIx32 "]", name);
assert(false);
#endif
}
}
IMP freedomImpl(id target, uint32_t name, SEL *selector)
{
if (target == nil)
return NULL;
Class targetClass = object_getClass(target);
if (targetClass == NULL)
return NULL;
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(targetClass, &methodCount);
SEL methodName = freedomFindMethodName(name, targetClass, methodList, methodCount, NULL);
free(methodList);
if (methodName != NULL)
{
if (selector != NULL)
*selector = methodName;
IMP result = class_getMethodImplementation(targetClass, methodName);
if (result == NULL)
{
#ifdef DEBUG
NSLog(@"[Freedom coulnd'n find method named 0x%" PRIx32 "]", name);
#endif
}
return result;
}
return NULL;
}
void freedomInit()
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
freedom_getClassList = &objc_getClassList;
freedom_class_getName = &class_getName;
});
}