抽离Cycript的choose功能

众所周知,cycript有一个非常便利的功能——choose(CLASS_NAME)。通过这个function,我们可以获取到当前进程中所有类名为CLASS_NAME的实例对象,在逆向的过程中能给我们节省不少时间。

然而,在编写tweak时却用不了这个function,当然不用这个也能写,但是有的话,也是一大便利。

请注意,以下代码使用GPL v3协议发布,即:

  • 无论软件以何种形式发布,都必须同时附上源代码。例如在 Web 上提供下载,就必须在二进制版本(如果有的话)下载的同一个页面,清楚地提供源代码下载的链接。如果以光盘形式发布,就必须同时附上源文件的光盘。
  • 开发或维护遵循 GPL 协议开发的软件的公司或个人,可以对使用者收取一定的服务费用。但还是一句老话——必须无偿提供软件的完整源代码,不得将源代码与服务做捆绑或任何变相捆绑销售。
  • 你可以去掉所有原作的版权信息,只要你保持开源,并且随源代码、二进制版附上 GPL 的许可证就行,让后人可以很明确地得知此软件的授权信息。GPL 精髓就是,只要使软件在完整开源 的情况下,尽可能使使用者得到自由发挥的空间,使软件得到更快更好的发展。

GPL v3翻译来自OSChina.

Choose.h,

//
//  choose.h
//  choose
//
//  Created by Ryza 15/7/4.
//  Copyright (c) 2015 [data deleted]. All rights reserved.
//
 
// 您必须了解:
/* GNU General Public License, Version 3 {{{ */
/* GPL协议 */
#warning 无论软件以何种形式发布,都必须同时附上源代码。例如在 Web 上提供下载,就必须在二进制版本(如果有的话)下载的同一个页面,清楚地提供源代码下载的链接。如果以光盘形式发布,就必须同时附上源文件的光盘。
#warning 开发或维护遵循 GPL 协议开发的软件的公司或个人,可以对使用者收取一定的服务费用。但还是一句老话——必须无偿提供软件的完整源代码,不得将源代码与服务做捆绑或任何变相捆绑销售。
#warning 你可以去掉所有原作的版权 信息,只要你保持开源,并且随源代码、二进制版附上 GPL 的许可证就行,让后人可以很明确地得知此软件的授权信息。GPL 精髓就是,只要使软件在完整开源 的情况下,尽可能使使用者得到自由发挥的空间,使软件得到更快更好的发展。
/* }}} */
// http://www.oschina.net/question/12_2826
 
/* Cycript - Optimizing JavaScript Compiler/Runtime
 * Copyright (C) 2009-2013  Jay Freeman (saurik)
 */
 
/* GNU General Public License, Version 3 {{{ */
/*
 * Cycript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * Cycript is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cycript.  If not, see <http://www.gnu.org/licenses/>.
 **/
/* }}} */
 
#import <Foundation/Foundation.h>

@interface choose : NSObject

+ (NSArray *)choose:(NSString *)className;

@end

Choose.mm,

//
//  choose.mm
//  choose
//
//  Created by Ryza 15/7/4.
//  Copyright (c) 2015 [data deleted]. All rights reserved.
//
 
// 您必须了解:
/* GNU General Public License, Version 3 {{{ */
/* GPL协议 */
#warning 无论软件以何种形式发布,都必须同时附上源代码。例如在 Web 上提供下载,就必须在二进制版本(如果有的话)下载的同一个页面,清楚地提供源代码下载的链接。如果以光盘形式发布,就必须同时附上源文件的光盘。
#warning 开发或维护遵循 GPL 协议开发的软件的公司或个人,可以对使用者收取一定的服务费用。但还是一句老话——必须无偿提供软件的完整源代码,不得将源代码与服务做捆绑或任何变相捆绑销售。
#warning 你可以去掉所有原作的版权 信息,只要你保持开源,并且随源代码、二进制版附上 GPL 的许可证就行,让后人可以很明确地得知此软件的授权信息。GPL 精髓就是,只要使软件在完整开源 的情况下,尽可能使使用者得到自由发挥的空间,使软件得到更快更好的发展。
/* }}} */
// http://www.oschina.net/question/12_2826
 
/* Cycript - Optimizing JavaScript Compiler/Runtime
 * Copyright (C) 2009-2013  Jay Freeman (saurik)
 */
 
/* GNU General Public License, Version 3 {{{ */
/*
 * Cycript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * Cycript is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cycript.  If not, see <http://www.gnu.org/licenses/>.
 **/
/* }}} */
 
#import "choose.h"
 
#include <objc/runtime.h>
#include <malloc/malloc.h>
#include <mach/mach.h>
#include <set>
 
struct choice {
    std::set<Class> query_;
    std::set<id> result_;
};

struct ObjectStruct {
    Class isa_;
};

static kern_return_t read_memory(task_t task, vm_address_t address, vm_size_t size, void **data) {
    *data = reinterpret_cast<void *>(address);
    return KERN_SUCCESS;
}

static Class * copy_class_list(size_t &size) {
    size = objc_getClassList(NULL, 0);
    Class * data = reinterpret_cast<Class *>(malloc(sizeof(Class) * size));
    for (;;) {
        size_t writ = objc_getClassList(data, (int)size);
        if (writ <= size) {
            size = writ;
            return data;
        }

        Class * copy = reinterpret_cast<Class *>(realloc(data, sizeof(Class) * writ));
        if (copy == NULL) {
            free(data);
            return NULL;
        }
        data = copy;
        size = writ;
    }
}

static void choose_(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) {
    choice * choice = reinterpret_cast<struct choice *>(baton);
    for (unsigned i = 0; i < count; ++i) {
        vm_range_t &range = ranges[i];
        void * data = reinterpret_cast<void *>(range.address);
        size_t size = range.size;
        if (size < sizeof(ObjectStruct))
            continue;

        uintptr_t * pointers = reinterpret_cast<uintptr_t *>(data);
#ifdef __arm64__
        Class isa = reinterpret_cast<Class>(pointers[0] & 0x1fffffff8);
#else
        Class isa = reinterpret_cast<Class>(pointers[0]);
#endif
        std::set<Class>::const_iterator result(choice->query_.find(isa));
        if (result == choice->query_.end())
            continue;
        size_t needed = class_getInstanceSize(*result);
        size_t boundary = 496;
#ifdef __LP64__
        boundary *= 2;
#endif
        if ((needed <= boundary && (needed + 15) / 16 * 16 != size) || (needed > boundary && (needed + 511) / 512 * 512 != size))
            continue;
        choice->result_.insert(reinterpret_cast<id>(data));
    }
}

@implementation choose

+ (NSArray *)choose:(NSString *)className{
    vm_address_t * zones = NULL;
    unsigned size = 0;
    kern_return_t error = malloc_get_all_zones(0, &read_memory, &zones, &size);
    assert(error == KERN_SUCCESS);

    size_t number;
    Class * classes = copy_class_list(number);
    assert(classes != NULL);

    choice choice;
    Class _class = NSClassFromString(className);
    for (size_t i = 0; i != number; ++i) {
        for (Class current = classes[i]; current != Nil; current = class_getSuperclass(current)) {
            if (current == _class) {
                choice.query_.insert(classes[i]);
                break;
            }
        }
    }
    free(classes);

    for (unsigned i = 0; i != size; ++i) {
        const malloc_zone_t * zone = reinterpret_cast<const malloc_zone_t *>(zones[i]);
        if (zone == NULL || zone->introspect == NULL)
            continue;
        zone->introspect->enumerator(mach_task_self(), &choice, MALLOC_PTR_IN_USE_RANGE_TYPE, zones[i], &read_memory, &choose_);
    }

#if __has_feature(objc_arc)
    NSMutableArray * result = [[NSMutableArray alloc] init];
#else
    NSMutableArray * result = [[[NSMutableArrayalloc] init] autorelease];
#endif
    for (auto iter = choice.result_.begin(); iter != choice.result_.end(); iter++) {
        [result addObject:(id)*iter];
    }
    return result;
}
@end

你也可以在我的Github上得到这份代码。

Leave a Reply

Your email address will not be published. Required fields are marked *

two × 3 =