HookDetection

反注入:注入检查机制,获取加载的模块名,判断是否都在白名单中;hook 检测:method swizzle、符号表替换、inline hook

Posted by kunnan on August 17, 2018

前言

针对注入的方式进行反注入,现在先回一下注入的方式有哪些?

  • iOS 中常见的hook方式

restrict section

当旧版的dylb 检测到存在__RESTRICT__restrict 这样的section时候,DYLD_INSERT_LIBRARIES 环境变量会被忽略,导致注入失败。

  • 在Project的 Other Linker Flags 增加

    -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
      
    

在新版的dylb以及iOS10中,发现dylb 已经不检测这个section.而且option 自带unrestrict。

  • 去掉保护: anti RESTRICT

    • ps -e | grep /var 找到AppBinary路径

    • 二进制编辑器(iHex等)修改__RESTRICT和__restrict为其他值。(比如:__RRRRRRRR和__rrrrrrrr。保证长度不变就行啦)

    • ldid -S AppBinary 重签名。或者Cydia中安装 AppSync

注入检测

通过_dylb_get_image_name判断加载模块中是否有异常进行注入检测

search _dyld_get_image_name

  • const char* *_dyld_get_image_name*(uint32_t image_index)

    • 发现其他不在白名单内的库:调用 _dyld_image_count() 获得加载的动态库的数量,_dyld_get_image_name() 获得名字,然后遍历他们的名字,看看有没有 “MobileSubstrate” 关键字,有的话就是越狱的

      int ckeckInjector() 
      {
      	// see if libSystem is in list of images
      	uint32_t count = _dyld_image_count();
      	for(uint32_t i=0; i < count; ++i) {
      		const char*  name = _dyld_get_image_name(i);
      		if ( strstr(name, "DynamicLibraries") != NULL ) {
      			PASS("always-libSystem");
      			return 1;
      		}
      	}
          
      	FAIL("always-libSystem");
      	return 0;
      }
          
      
    • app crashes when libSystem cannot be found

      int main() 
      {
      	// see if libSystem is in list of images
      	uint32_t count = _dyld_image_count();
      	for(uint32_t i=0; i < count; ++i) {
      		const char*  name = _dyld_get_image_name(i);
      		if ( strstr(name, "/libSystem.") != NULL ) {
      			PASS("always-libSystem");
      			return 0;
      		}
      	}
          
      	FAIL("always-libSystem");
      	return 0;
      }
          
      

hook 检测CheckHook

根据不同的方法定制不同的检查方法

  • Method Swizzling: 原理是替换imp,通过dladdr函数得到imp地址所在的模块info.dli_fname,如果不是主二进制模块,就可以认定是被hook 了

    bool CheckHookForOC(const char* clsname,const char* selname){
        Dl_info info;
        SEL sel = sel_registerName(selname);
        Class cls = objc_getClass(clsname);
        Method method = class_getInstanceMethod(cls, sel);
        if(!method){
            method = class_getClassMethod(cls, sel);
        }
        IMP imp = method_getImplementation(method);
          
        if(!dladdr((void*)imp, &info)){
            return false;
        }
          
        printf("%s\n", info.dli_fname);
          
        if(!strncmp(info.dli_fname, "/System/Library/Frameworks", 26)){
            return false;
        }
          
        if(!strcmp(info.dli_fname, _dyld_get_image_name(0))){
            return false;
        }
          
        return true;
    }
      
    
  • 符号表替换: 因为fishhook是基于懒加载符号表和非懒加载符号表进行替换的,因此通过遍历符号表中的指针就可以判断app是否被hook 了。

    • 非懒加载的指针指向真实的地址,而懒加载的指针在没有解析到真实的地址之前指向__stub_helper;so ,遍历符号表中的每一个指针,然后判断指针是不是指向__stub_helper或者系统模块,就可以判断是否被hook。
      • 结合fishhook代码以及macho文件结构的基础分析,结合dladdr函数来实现代码。
      • dladdr查看函数的内存空间信息, 验证函数的指针来自程序, 还是苹果的库, 还是未知.
  • inline hook: 替换函数内部的前几条指令跳转

    • cydia substrate: `通过inline hook的方式修改目标函数内存中的汇编指令,使其调转到自己的代码块,以达到修改程序的目的,同时支持method swizzle
    • 检测方法: 分析函数的内存前几条指令中有没有跳转来判断是不是inline hook。
      • 如果hook 替换某条指令或者patch内存中的一些指令,这个时候采用“计算代码的哈希值进行验证”

其他方式检测,比如使用callStackSymbols

  • [NSThread callStackSymbols]

    %hook NSBundle
    - (NSString *)bundleIdentifier
    {
    %log;
      
    NSArray* symbos = [NSThread callStackSymbols];// 调用栈  NSArray<NSString *> *callStackSymbols
        NSRange range = [[symbos objectAtIndex:1] rangeOfString:@"1   WeChat"];//    1   WeChat                              0x0000000101da865c _ZN13ClearDataItem11compareTimeERKNSt3__110shared_ptrIS_EES4_ + 772608
    NSString* old = %orig;
      
    if (range.location != NSNotFound){
      
    return @"com.tencent.xin";//  可以自己定制返回一些;判断检测是否包含其他动态库的名称`libweixinDylib.dylib `,或者是否包含`logos_method` 字符,如果有就可以认定hook 了
    }
      
    return old;
    }
    %end
      
    

    image

    image

完整性校验

从文件完整性(检查文件load command 的修改)、是否重签名、bid 等方面入手验证是否程序被修改

load command

  • 获取加载的动态库: 读取load command 中的 LC_LOAD_DYLIB、LC_LOAD_WEAK_DYLIB信息

      
    //获取加载的动态库
    NSArray* MachOParser::find_load_dylib(){
        NSMutableArray* array = [[NSMutableArray alloc] init];;
        mach_header_t *header = (mach_header_t*)base;
        segment_command_t *cur_seg_cmd;
        uintptr_t cur = (uintptr_t)this->base + sizeof(mach_header_t);
        for (uint i = 0; i < header->ncmds; i++,cur += cur_seg_cmd->cmdsize) {
            cur_seg_cmd = (segment_command_t*)cur;
            if(cur_seg_cmd->cmd == LC_LOAD_DYLIB || cur_seg_cmd->cmd == LC_LOAD_WEAK_DYLIB){
                dylib_command *dylib = (dylib_command*)cur_seg_cmd;
                char* name = (char*)((uintptr_t)dylib + dylib->dylib.name.offset);
                NSString* dylibName = [NSString stringWithUTF8String:name];
                [array addObject:dylibName];
            }
        }
        return [array copy];
    }
      
    

代码检测:获取内存中运行代码的md5值

如果内存中的代码被修改了,那么获取的md5的值就会不一样。

  • 获取运行代码md5

    NSString* MachOParser::get_text_data_md5(){
        NSMutableString *result = [NSMutableString string];
        section_info_t *section = this->find_section("__TEXT", "__text");
        local_addr startAddr =  section->addr;
        unsigned char hash[CC_MD5_DIGEST_LENGTH];
        CC_MD5((const void *)startAddr, (CC_LONG)section->section->size, hash);
        for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
            [result appendFormat:@"%02x",hash[i]];
        }
        return [result copy];
    }
      
    

重签名检测

  • 判断bid

    • 可能存在被hook的情况

      %hook NSBundle
      - (NSString *)bundleIdentifier
      {
      %log;
      //  LOGSTACK();
      NSArray* symbos = [NSThread callStackSymbols];// 调用栈  NSArray<NSString *> *callStackSymbols
          
          NSRange range = [[symbos objectAtIndex:1] rangeOfString:@"1   WeChat"];//    1   WeChat                              0x0000000101da865c _ZN13ClearDataItem11compareTimeERKNSt3__110shared_ptrIS_EES4_ + 772608
          
          NSLog(@"symbos  :%@", symbos);
          
      NSString* old = %orig;
          
      if (range.location != NSNotFound){
      WXLog(@"orgin:%@,change to:com.tencent.xin", old);
      return @"com.tencent.xin";
      }
          
      WXLog(@"bundle id :%@", old);
      return old;
      }
          
      %end
          
      
      • 如果目前此代码制作用于WeChat,因此不用判断进行名称,直接ret 特定bid 即可
  • 获取embedded.mobileprovision文件信息

  • 从可执行文件的LC_CODE_SIGNATURE 读取信息

    • MachOParser.mm
      • 1.拿到当前的签名文件的签名信息和编译前的签名信息比对

See Also

 #import <mach-o/dyld.h>  
 
 int count = _dyld_image_count();
 for (int i=0; i<count; i++) {
     printf("%s", _dyld_get_image_name(i));
     // 如果 _dyld_get_image_name() 里面包含 MobileSubstrate 就是越狱了
 }
 
  • void -[ClientCheckMgr registerAddImageCallBack](void * self, void * _cmd) {

    void -[ClientCheckMgr registerAddImageCallBack](void * self, void * _cmd) {
        sp = sp - 0x50;
        r4 = self;
        if ([*(("/System/Library/Frameworks/MultipeerConnectivity.framework/MultipeerConnectivity" | 0xd50000) + 0x2c8781c) isiOS9plus] != 0x0) {
                r5 = _dyld_image_count();
                if (r5 != 0x0) {
                        r10 = sp + 0x20;
                        r11 = 0x0;
                        do {
                                r0 = _dyld_get_image_header(r11);
                                r8 = r0;
                                if (dladdr(r0, r10) != 0x0) {
                                        [r4 addImage:stack[2036]];
                                }
                                else {
                                        stack[2035] = @selector(logWithLevel:module:errorCode:file:line:func:format:);
                                        r0 = @class(iConsole);
                                        stack[2031] = "";
                                        asm { strd       r1, r2, [sp, #0x48 + var_44] };
                                        objc_msgSend(r0, stack[2035], 0x2, "__MMClientCacheManager", 0x0, stack[2029], stack[2030], stack[2031]);
                                }
                                r11 = r11 + 0x1;
                        } while (r5 != r11);
                }
        }
        _dyld_register_func_for_add_image(0x2c878dd);
        return;
    }
    
  • NSBundle _ivarDescription

 2018-08-23 15:09:49.683283 WeChat[351:52520]  hook NSBundle orgin:com.tencent.xin,change to:com.tencent.xin  NSBundle _ivarDescription:<NSBundle: 0x174090a40>:
 in NSBundle:
 	_flags (unsigned long): 100859904
 	_cfBundle (id): <__NSCFType: 0x108800560>
 	_reserved2 (unsigned long): 0
 	_principalClass (Class): (null)
 	_initialPath (id): @"/var/containers/Bundle/Application/00EC395E-4EC3-4B32-9CC7-6BE7C1164177/weixin.app"
 	_resolvedPath (id): @"/var/containers/Bundle/Application/00EC395E-4EC3-4B32-9CC7-6BE7C1164177/weixin.app"
 	_reserved3 (id): nil
 	_lock (id): <NSLock: 0x1740de370>
 in NSObject:
 
/Users/devzkn/bin//knpost HookDetection 反注入: 注入检查机制,获取加载的模块名,判断是否都在白名单中;hook 检测:method swizzle、符号表替换、inline hook -t security
#原来""的参数,需要自己加上""

转载请注明: > HookDetection