在 iOS 逆向工程的论坛上看到了如何勾住一个类所有方法的帖子,然后基本都是用 Objective-C 里的 forwardInvocation: 来做的,例如
![Hooked via forwardInvocation](/wp-content/uploads/2017/08/hooked-via-forwardInvocation.png)
于是这里做了一个检测自己的类是否被这样给 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](/wp-content/uploads/2017/08/protected-call-stack.png)
当然,longjmp 也是有可能被 hook 的,但是可以根据 longjmp 的实现自己来做清空调用栈的操作。
完整的例子在no_forward_invocation
还是swift 安全
大概是的,但是也没有绝对的安全嘛
无意中发现这么个博客。现在编程已经是萌妹跟初中生的天下了吗,我这种老头已经没用了