iOS保护应用安全,拒绝forwardInvocation (╯°□°)╯︵ ┻━┻

在 iOS 逆向工程的论坛上看到了如何勾住一个类所有方法的帖子,然后基本都是用 Objective-C 里的 forwardInvocation: 来做的,例如

Hooked via forwardInvocation
Hooked via forwardInvocation

于是这里做了一个检测自己的类是否被这样给 hook 了的方法。

思路很简单,在需要保护的类中,增加一个变长参数的方法,当然名字要有迷惑性,然后用不定参数做判断。因为 Objective-C 的方法签名不会包含有关不定参数的信息,所以相对隐蔽,接下来举一个具体的例子。

@interface ProtectedClass : NSObject
 
// 一些需要保护的方法
- (void)someImportantMethod:(id)arg;
 
// 取一个有迷惑性的方法名
- (void)aConfusionMethodName:(id)arg, ... NS_REQUIRES_NIL_TERMINATION;
 
@end

这里的不定长参数方法不论是 runtime 还是静态,都会是 -[ProtectedClass aConfusionMethodName:] 的形式。然后,我们需要一个函数来触发不定长参数方法的调用。

static void inline protection() {
    // 这里的 Selector 字符串如何保护不是重点, 但是也有很多方法
    IMP target = method_getImplementation(class_getInstanceMethod([ProtectedClassclass], NSSelectorFromString(@"aConfusionMethodName:")));

    // 增加一个 int 类型的参数作为标记
    typedef void(*targetMethodImplmentation)(id, SEL, id, /* magic int */...);
    id obj = [[ProtectedClass alloc] init];
    ((targetMethodImplmentation)target)(obj, NSSelectorFromString(@"aConfusionMethodName:"), nil, 1, nil);
}

之后在这个不定长参数方法的实现中试着取出刚才传入的 1.

// 取一个有迷惑性的方法名
- (void)aConfusionMethodName:(id)arg, ... {
    // 在正式开始前做别的 (有意义/无意义均可)
    va_list arg_ptr;
    int j = 0;
    va_start(arg_ptr, arg);
    j = va_arg(arg_ptr, int);
    va_end(arg_ptr);
    if (j != 1) {
        NSLog(@"Error: forwarded message!");

        // 使用longjmp, 这样没有call stack保留
        // 保护这个迷惑性函数
        longjmp(protectionJMP, 1);
    } else {
        NSLog(@"So far so good.");
    }
}

如果发现不是 1 的话,那么就 exit? 那样还是 too young too simple 了,因为 exit 可能被 hook 了,从而无法按照我们的意愿退出程序。即使 exit 没有被 hook,攻击者使用 lldb 时断在 exit 上也能看到调用栈,从而暴露这个不定长参数方法。

解决之道可以利用 longjmp,longjmp 函数的使用会使调用栈清空,效果如下。

protected call stack
protected call stack

当然,longjmp 也是有可能被 hook 的,但是可以根据 longjmp 的实现自己来做清空调用栈的操作。

完整的例子在BlueCocoa/no_forward_invocation

声明: 本文为0xBBC原创, 转载注明出处喵~

《iOS保护应用安全,拒绝forwardInvocation (╯°□°)╯︵ ┻━┻》有1个想法

  1. 无意中发现这么个博客。现在编程已经是萌妹跟初中生的天下了吗,我这种老头已经没用了

发表评论

电子邮件地址不会被公开。 必填项已用*标注