#import "DJZMachController.h"
#import <objc/runtime.h>
#import <dlfcn.h>
#import <mach-o/ldsyms.h>
#include <limits.h>
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
#include <mach-o/getsect.h>
#include <vector>
struct PointerModifierNop {
template <typename ListType, typename T>
static T *modify(__unused const ListType &list, T *ptr) { return ptr; }
};
template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
uint32_t entsize() const {
return entsizeAndFlags & ~FlagMask;
}
uint32_t flags() const {
return entsizeAndFlags & FlagMask;
}
Element& getOrEnd(uint32_t i) const {
return *PointerModifier::modify(*this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));
}
Element& get(uint32_t i) const {
return getOrEnd(i);
}
size_t byteSize() const {
return byteSize(entsize(), count);
}
static size_t byteSize(uint32_t entsize, uint32_t count) {
return sizeof(entsize_list_tt) + count*entsize;
}
struct iterator;
const iterator begin() const {
return iterator(*static_cast<const List*>(this), 0);
}
iterator begin() {
return iterator(*static_cast<const List*>(this), 0);
}
const iterator end() const {
return iterator(*static_cast<const List*>(this), count);
}
iterator end() {
return iterator(*static_cast<const List*>(this), count);
}
struct iterator {
uint32_t entsize;
uint32_t index; // keeping track of this saves a divide in operator-
Element* element;
typedef std::random_access_iterator_tag iterator_category;
typedef Element value_type;
typedef ptrdiff_t difference_type;
typedef Element* pointer;
typedef Element& reference;
iterator() { }
iterator(const List& list, uint32_t start = 0)
: entsize(list.entsize())
, index(start)
, element(&list.getOrEnd(start))
{ }
const iterator& operator += (ptrdiff_t delta) {
element = (Element*)((uint8_t *)element + delta*entsize);
index += (int32_t)delta;
return *this;
}
const iterator& operator -= (ptrdiff_t delta) {
element = (Element*)((uint8_t *)element - delta*entsize);
index -= (int32_t)delta;
return *this;
}
const iterator operator + (ptrdiff_t delta) const {
return iterator(*this) += delta;
}
const iterator operator - (ptrdiff_t delta) const {
return iterator(*this) -= delta;
}
iterator& operator ++ () { *this += 1; return *this; }
iterator& operator -- () { *this -= 1; return *this; }
iterator operator ++ (int) {
iterator result(*this); *this += 1; return result;
}
iterator operator -- (int) {
iterator result(*this); *this -= 1; return result;
}
ptrdiff_t operator - (const iterator& rhs) const {
return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
}
Element& operator * () const { return *element; }
Element* operator -> () const { return element; }
operator Element& () const { return *element; }
bool operator == (const iterator& rhs) const {
return this->element == rhs.element;
}
bool operator != (const iterator& rhs) const {
return this->element != rhs.element;
}
bool operator < (const iterator& rhs) const {
return this->element < rhs.element;
}
bool operator > (const iterator& rhs) const {
return this->element > rhs.element;
}
};
};
class nocopy_t {
private:
nocopy_t(const nocopy_t&) = delete;
const nocopy_t& operator=(const nocopy_t&) = delete;
protected:
constexpr nocopy_t() = default;
~nocopy_t() = default;
};
template <typename T>
struct RelativePointer: nocopy_t {
int32_t offset;
T get() const {
if (offset == 0)
return nullptr;
uintptr_t base = (uintptr_t)&offset;
uintptr_t signExtendedOffset = (uintptr_t)(intptr_t)offset;
uintptr_t pointer = base + signExtendedOffset;
return (T)pointer;
}
};
struct method_t {
static const uint32_t smallMethodListFlag = 0x80000000;
method_t(const method_t &other) = delete;
// The representation of a "big" method. This is the traditional
// representation of three pointers storing the selector, types
// and implementation.
struct big {
SEL name;
const char *types;
IMP imp;
};
private:
bool isSmall() const {
return ((uintptr_t)this & 1) == 1;
}
// The representation of a "small" method. This stores three
// relative offsets to the name, types, and implementation.
struct small {
// The name field either refers to a selector (in the shared
// cache) or a selref (everywhere else).
RelativePointer<const void *> name;
RelativePointer<const char *> types;
RelativePointer<IMP> imp;
};
small &small() const {
return *(struct small *)((uintptr_t)this & ~(uintptr_t)1);
}
IMP remappedImp(bool needsLock) const;
void remapImp(IMP imp);
objc_method_description *getSmallDescription() const;
public:
static const auto bigSize = sizeof(struct big);
static const auto smallSize = sizeof(struct small);
// The pointer modifier used with method lists. When the method
// list contains small methods, set the bottom bit of the pointer.
// We use that bottom bit elsewhere to distinguish between big
// and small methods.
struct pointer_modifier {
template <typename ListType>
static method_t *modify(const ListType &list, method_t *ptr) {
if (list.flags() & smallMethodListFlag)
return (method_t *)((uintptr_t)ptr | 1);
return ptr;
}
};
big &big() const {
return *(struct big *)this;
}
SEL name() const {
if (isSmall()) {
return (SEL)small().name.get();
} else {
return big().name;
}
}
const char *types() const {
return isSmall() ? small().types.get() : big().types;
}
IMP imp(bool needsLock) const {
if (isSmall()) {
IMP imp = remappedImp(needsLock);
if (!imp)
imp = ptrauth_sign_unauthenticated(small().imp.get(),
ptrauth_key_function_pointer, 0);
return imp;
}
return big().imp;
}
SEL getSmallNameAsSEL() const {
return (SEL)small().name.get();
}
SEL getSmallNameAsSELRef() const {
return *(SEL *)small().name.get();
}
void setName(SEL name) {
if (isSmall()) {
*(SEL *)small().name.get() = name;
} else {
big().name = name;
}
}
void setImp(IMP imp) {
if (isSmall()) {
remapImp(imp);
} else {
big().imp = imp;
}
}
objc_method_description *getDescription() const {
return isSmall() ? getSmallDescription() : (struct objc_method_description *)this;
}
struct SortBySELAddress :
public std::binary_function<const struct method_t::big&,
const struct method_t::big&, bool>
{
bool operator() (const struct method_t::big& lhs,
const struct method_t::big& rhs)
{ return lhs.name < rhs.name; }
};
method_t &operator=(const method_t &other) {
big().name = other.name();
big().types = other.types();
big().imp = other.imp(false);
return *this;
}
};
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {
bool isUniqued() const;
bool isFixedUp() const;
void setFixedUp();
uint32_t indexOfMethod(const method_t *meth) const {
uint32_t i =
(uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
return i;
}
bool isSmallList() const {
return flags() & method_t::smallMethodListFlag;
}
bool isExpectedSize() const {
if (isSmallList())
return entsize() == method_t::smallSize;
else
return entsize() == method_t::bigSize;
}
method_list_t *duplicate() const {
method_list_t *dup;
if (isSmallList()) {
dup = (method_list_t *)calloc(byteSize(method_t::bigSize, count), 1);
dup->entsizeAndFlags = method_t::bigSize;
} else {
dup = (method_list_t *)calloc(this->byteSize(), 1);
dup->entsizeAndFlags = this->entsizeAndFlags;
}
dup->count = this->count;
std::copy(begin(), end(), dup->begin());
return dup;
}
};
struct PtrauthStrip {
template <typename T>
static T *sign(T *ptr, __unused const void *address) {
return ptr;
}
template <typename T>
static T *auth(T *ptr, __unused const void *address) {
return ptrauth_strip(ptr, ptrauth_key_process_dependent_data);
}
};
template<typename T, typename Auth>
struct WrappedPtr {
private:
T *ptr;
public:
WrappedPtr(T *p) {
*this = p;
}
WrappedPtr(const WrappedPtr<T, Auth> &p) {
*this = p;
}
WrappedPtr<T, Auth> &operator =(T *p) {
ptr = Auth::sign(p, &ptr);
return *this;
}
WrappedPtr<T, Auth> &operator =(const WrappedPtr<T, Auth> &p) {
*this = (T *)p;
return *this;
}
operator T*() const { return get(); }
T *operator->() const { return get(); }
T *get() const { return Auth::auth(ptr, &ptr); }
// When asserts are enabled, ensure that we can read a byte from
// the underlying pointer. This can be used to catch ptrauth
// errors early for easier debugging.
void validate() const {
#if !NDEBUG
char *p = (char *)get();
char dummy;
memset_s(&dummy, 1, *p, 1);
#endif
}
};
struct category_t {
const char *name;
Class cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
struct property_list_t *_classProperties;
};
@interface DJZMachController ()
@end
@implementation DJZMachController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
uint32_t imageCount = _dyld_image_count();
for (UInt32 i = 0; i < imageCount; i ++) {
const struct mach_header* mh = _dyld_get_image_header(i);
const char* path = _dyld_get_image_name(i);
NSString *name = [[NSString alloc] initWithUTF8String:path];
if ([name containsString:@"dylib"] || ![name containsString:[[NSBundle mainBundle] bundlePath]]) {
continue;;
}
unsigned long byteCount = 0;
category_t * const *list = ( category_t * const *)getsectiondata((const struct mach_header_64*)mh, "__DATA", "__objc_nlcatlist", &byteCount);
size_t count = byteCount / sizeof(category_t * const);
for (size_t i = 0; i < count; i ++) {
category_t *cat = list[i];
const method_list_t *mlist = cat->classMethods;
for (auto& meth : *mlist) {
SEL sel = meth.name();
NSLog(@"-----%@", NSStringFromSelector(sel));
}
}
}
}
@end
08-12
1万+