_dyld系列hook

TODO:

【已解决】iOS反越狱检测:优化findRealImageCount改为调用_dyld_get_image_vmaddr_slide计算逻辑
【已解决】iOS反越狱检测:_dyld_image_count和_dyld_get_image_name返回hook后的值
【已解决】iOS反越狱检测:如何hook绕过_dyld_image_count和_dyld_get_image_name
【已解决】iOS反越狱检测:优化findRealImageCount改为调用_dyld_get_image_vmaddr_slide计算逻辑
【已解决】iOS反越狱检测:_dyld_get_image_name的hook绕过
【已解决】iOS反越狱检测:_dyld_image_count和_dyld_get_image_name改为普通hook逻辑
【已解决】iOS反越狱检测:dyld的_dyld_image_count和_dyld_get_image_name
【已解决】iOS反越狱检测:_dyld_register_func_for_add_image和_dyld_register_func_for_remove_image

/*==============================================================================
Hook: _dyld_image_count(), _dyld_get_image_name(), _dyld_get_image_header(), _dyld_get_image_vmaddr_slide()
==============================================================================*/

/*
https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
   _dyld_image_count,
   _dyld_get_image_header,
   _dyld_get_image_vmaddr_slide,
   _dyld_get_image_name,
   _dyld_register_func_for_add_image,
   _dyld_register_func_for_remove_image,
   NSVersionOfRunTimeLibrary,
   NSVersionOfLinkTimeLibrary,
   _NSGetExecutablePath
*/

uint32_t _dyld_image_count(void);
//uint32_t orig__dyld_image_count(void);
//uint32_t _logos_orig$_ungrouped$_dyld_image_count(void);
//uint32_t (*_logos_orig$_ungrouped$_dyld_image_count)(void);
//static uint32_t (*_logos_orig$_ungrouped$_dyld_image_count)(void);

const struct mach_header* _dyld_get_image_header(uint32_t image_index);
const char* _dyld_get_image_name(uint32_t image_index);
intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index);

void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));

int32_t NSVersionOfRunTimeLibrary(const char* libraryName);

int32_t NSVersionOfLinkTimeLibrary(const char* libraryName);

int _NSGetExecutablePath(char* buf, uint32_t* bufsize);

const int IMAGE_INDEX_FAKE_END = IMAGE_INDEX_FAKE_START + IMAGE_INDEX_MAX_VALID_NUMBER;

// Global Variable
int gOrigImageCount = -1;
int gHookedImageCount = -1;
int gRealOrigImageCount = -1; // after hooked, image name/header/slide got hooked image count -> so need find real original image count

int* gJbDylibIdxList = NULL;
int gJbDylibIdxListLen = -1;

int* gHookedImgIdxList = NULL;
int gHookedImgIdxListLen = -1;

static int generateFakeImageIndex(int origImageIndex){
   int fakeImgIdx = origImageIndex + IMAGE_INDEX_FAKE_START;
   iosLogDebug("generateFakeImageIndex: origImageIndex=%d -> fakeImgIdx=%d", origImageIndex, fakeImgIdx);
   return fakeImgIdx;
}

static bool isFakeImageIndex(int curImageIndex){
   bool isFakeIdx = (curImageIndex >= IMAGE_INDEX_FAKE_START) && (curImageIndex < IMAGE_INDEX_FAKE_END);
   iosLogDebug("curImageIndex=%d -> isFakeIdx=%s", curImageIndex, boolToStr(isFakeIdx));
   return isFakeIdx;
}

static int fakeToRealImageIndex(int fakeImgageIndex){
   int realImageIndex = fakeImgageIndex - IMAGE_INDEX_FAKE_START;
   iosLogDebug("fakeImgageIndex=%d -> realImageIndex=%d", fakeImgageIndex, realImageIndex);
   return realImageIndex;
}

static void dbgPrintImgIdxList(int* imgIdxList){
   iosLogDebug("imgIdxList=%p", imgIdxList);

   if (NULL != imgIdxList){
       int curListIdx = 0;
       int curIdxValue = DYLD_IMAGE_INDEX_INVALID;
       curIdxValue = imgIdxList[curListIdx];
       if (DYLD_IMAGE_INDEX_INVALID == curIdxValue) {
           iosLogDebug("[%d] %d", curListIdx, curIdxValue);
       }

       while(DYLD_IMAGE_INDEX_INVALID != curIdxValue){
           iosLogDebug("[%d] %d", curListIdx, curIdxValue);

           ++curListIdx;
           curIdxValue = imgIdxList[curListIdx];
       }

       int listCount = curListIdx;
       iosLogDebug("end listCount=%d", listCount);
   }
}

static void getJbDylibImgIdxList(int origImageCount, int** outJbDylibIdxList,  int* jbDylibIdxListLen){
   iosLogDebug("origImageCount=%d", origImageCount);

   int intSize = sizeof(int);
   int mallocCount = IMAGE_INDEX_MAX_JAILBREAK + 1;
   int mallocSize = intSize * mallocCount;
   iosLogDebug("intSize=%d, mallocCount=%d, mallocSize=%d", intSize, mallocCount, mallocSize);

   int curListIdx = 0;

   int* jbDylibIdxList = (int *)malloc(mallocSize);
   iosLogDebug("jbDylibIdxList=%p", jbDylibIdxList);

   if (NULL != jbDylibIdxList) {
       for (int origImgIdx = 0 ; origImgIdx < origImageCount; ++origImgIdx) {
           int fakeImgIdx = generateFakeImageIndex(origImgIdx);
           iosLogDebug("origImgIdx=%d, fakeImgIdx=%d", origImgIdx, fakeImgIdx);
           const char* curImageName = _dyld_get_image_name(fakeImgIdx);
           iosLogDebug("curImageName=%{public}s", curImageName);

           bool isJbDylib = isJailbreakDylib(curImageName);
           iosLogDebug("isJbDylib=%s", boolToStr(isJbDylib));

           if(isJbDylib){
               jbDylibIdxList[curListIdx] = origImgIdx;
               iosLogInfo("curImageName=%{public}s -> origImgIdx=%d, jbDylibIdxList[%d]=%d", curImageName, origImgIdx, curListIdx, jbDylibIdxList[curListIdx]);
               ++curListIdx;
           }
       }

       int curListCount = curListIdx;

       if (jbDylibIdxListLen) {
           *jbDylibIdxListLen = curListCount;
           iosLogDebug("*jbDylibIdxListLen=%d", *jbDylibIdxListLen);
       }

       int curListEndIdx = curListCount;
       jbDylibIdxList[curListEndIdx] = DYLD_IMAGE_INDEX_INVALID;
       iosLogDebug("list end, jbDylibIdxList[%d]=%d", curListEndIdx, jbDylibIdxList[curListEndIdx]);

       dbgPrintImgIdxList(jbDylibIdxList);

       if (outJbDylibIdxList) {
           // Note: here for 0 jailbreak dylib, also means get OK
           *outJbDylibIdxList = jbDylibIdxList;
       }
   }

   iosLogInfo("origImageCount=%d -> outJbDylibIdxList=%p, *outJbDylibIdxList=%p, jbDylibIdxList=%p, *jbDylibIdxListLen=%d", origImageCount, outJbDylibIdxList, outJbDylibIdxList ? *outJbDylibIdxList : NULL, jbDylibIdxList, jbDylibIdxListLen ? *jbDylibIdxListLen : 0);
}

static void initDylibImageIdxList(void) {
   // init for _dyld_image_count and related
   if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
       getJbDylibImgIdxList(gOrigImageCount, &gJbDylibIdxList, &gJbDylibIdxListLen);
       gHookedImageCount = gOrigImageCount - gJbDylibIdxListLen;
       iosLogInfo("gOrigImageCount=%d, gJbDylibIdxList=%p, gJbDylibIdxListLen=%d -> gHookedImageCount=%d", gOrigImageCount, gJbDylibIdxList, gJbDylibIdxListLen, gHookedImageCount);
   }
}

static void generateHookedImageIndexList(int* jbDylibIdxList, int jbDylibIdxListLen, int origImgageCount, int** outHookedImgIdxList, int* outHookedImgIdxListLen){
   int* hookedImgIdxList = (int*)malloc(sizeof(int) * (origImgageCount + 1));
   int curListIdx = 0;
   for(int curImgIdx = 0; curImgIdx < origImgageCount; curImgIdx++){
       bool isJbDylibIdx = false;
       if (jbDylibIdxListLen > 0){
           isJbDylibIdx = isIntInList(curImgIdx, jbDylibIdxList, jbDylibIdxListLen);
       }

       iosLogDebug("curImgIdx=%d, isJbDylibIdx=%s", curImgIdx, boolToStr(isJbDylibIdx));

       if(!isJbDylibIdx){
           hookedImgIdxList[curListIdx] = curImgIdx;
           ++curListIdx;
       }
   }

   int hookedImgIdxListLen = curListIdx;
   // set end
   int hookedImgIdxListEndIdx = hookedImgIdxListLen;
   hookedImgIdxList[hookedImgIdxListEndIdx] = DYLD_IMAGE_INDEX_INVALID;

   dbgPrintImgIdxList(hookedImgIdxList);

   // return result
   *outHookedImgIdxList = hookedImgIdxList;
   *outHookedImgIdxListLen = hookedImgIdxListLen;

   iosLogInfo("-> outHookedImgIdxList=%p, *outHookedImgIdxList=%p, *outHookedImgIdxListLen=%d", outHookedImgIdxList, *outHookedImgIdxList, *outHookedImgIdxListLen);
}

static void reInitImgCountIfNeed(int curOrigCount) {
   if (curOrigCount != gOrigImageCount) {
       iosLogInfo("curOrigCount=%d != gOrigImageCount=%d, need init", curOrigCount, gOrigImageCount);
       gOrigImageCount = curOrigCount;
       initDylibImageIdxList();

       bool foundJbLib = (NULL != gJbDylibIdxList) && (gJbDylibIdxListLen > 0);
       if (foundJbLib) {
           generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, gOrigImageCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
       }
   }
}

// static int hookedToOrigImageIndex(int hookedImageIndex, int* jbDylibIdxList, int jbDylibIdxListLen, int origImgageCount){
static int hookedToOrigImageIndex(int hookedImageIndex){
   int origImgIdx = DYLD_IMAGE_INDEX_INVALID;

   // int* hookedImgIdxList = NULL;
   // int hookedImgIdxListLen = 0;
   // generateHookedImageIndexList(jbDylibIdxList, jbDylibIdxListLen, origImgageCount, &hookedImgIdxList, &hookedImgIdxListLen);

   // int hookedImgIdxListMaxIdx = hookedImgIdxListLen - 1;
   int hookedImgIdxListMaxIdx = gHookedImgIdxListLen - 1;
   iosLogDebug("hookedImgIdxListMaxIdx=%d", hookedImgIdxListMaxIdx);

   if (hookedImageIndex <= hookedImgIdxListMaxIdx){
       // origImgIdx = hookedImgIdxList[hookedImageIndex];
       origImgIdx = gHookedImgIdxList[hookedImageIndex];
       iosLogDebug("hookedImageIndex=%d <= hookedImgIdxListMaxIdx=%d -> origImgIdx=%d", hookedImageIndex, hookedImgIdxListMaxIdx, origImgIdx);
   } else {
       origImgIdx = DYLD_IMAGE_INDEX_INVALID;
       iosLogDebug("hookedImageIndex=%d > hookedImgIdxListMaxIdx=%d -> origImgIdx=%d", hookedImageIndex, hookedImgIdxListMaxIdx, origImgIdx);
   }

   // if (NULL != hookedImgIdxList){
   //     free(hookedImgIdxList);
   // }

   return origImgIdx;
}

static int findRealImageCount(void){
   iosLogDebug("%s", "");

   int realImageCount = 0;
   int hookedImageCount = _dyld_image_count();
//        int origImageCount = orig__dyld_image_count();
   iosLogDebug("hookedImageCount=%d", hookedImageCount);

   // find real count
   int curImgIdx = hookedImageCount;

   // use: _dyld_get_image_vmaddr_slide

   long retSlide = _dyld_get_image_vmaddr_slide(generateFakeImageIndex(curImgIdx));
   iosLogDebug("[%d] -> retSlide=%ld", curImgIdx, retSlide);
   while(DYLD_IMAGE_SLIDE_INVALID != retSlide){
       ++curImgIdx;
       retSlide = _dyld_get_image_vmaddr_slide(generateFakeImageIndex(curImgIdx));
       iosLogDebug("[%d] -> retSlide=%ld", curImgIdx, retSlide);
   }

   // // use: _dyld_get_image_name
   // const char* retImgName = _dyld_get_image_name(generateFakeImageIndex(curImgIdx));
   // iosLogDebug("[%d] -> retImgName=%s", curImgIdx, retImgName);
   // while(NULL != retImgName){
   //     ++curImgIdx;
   //     retImgName = _dyld_get_image_name(generateFakeImageIndex(curImgIdx));
   //     iosLogDebug("[%d] -> retImgName=%s", curImgIdx, retImgName);
   // }

   // // use: _dyld_get_image_header
   // const struct mach_header* retImgHeader = _dyld_get_image_header(generateFakeImageIndex(curImgIdx));
   // iosLogDebug("[%d] -> retImgHeader=%p", curImgIdx, retImgHeader);
   // while(NULL != retImgHeader){
   //     ++curImgIdx;
   //     retImgHeader = _dyld_get_image_header(generateFakeImageIndex(curImgIdx));
   //     iosLogDebug("[%d] -> retImgHeader=%p", curImgIdx, retImgHeader);
   // }

   realImageCount = curImgIdx;
   iosLogDebug("realImageCount=%d", realImageCount);
   return realImageCount;
}

static void reInitAllRelated(void) {
   // if (gRealOrigImageCount <= 0) {
   //     // invalid, need reinit
   //     gRealOrigImageCount = findRealImageCount();
   //     iosLogInfo("gRealOrigImageCount=%d", gRealOrigImageCount);
   // }

   int curOrigCount = -1;

   int curHookedImageCount = _dyld_image_count();
   if (gJbDylibIdxListLen > 0) {
       curOrigCount = curHookedImageCount + gJbDylibIdxListLen;
   } else {
       curOrigCount = findRealImageCount();
   }
   iosLogDebug("curHookedImageCount=%d, gJbDylibIdxListLen=%d -> curOrigCount=%d", curHookedImageCount, gJbDylibIdxListLen, curOrigCount);

   if (curOrigCount != gOrigImageCount) {
       iosLogInfo("curOrigCount=%d != gOrigImageCount=%d -> reinit image index list", curOrigCount, gOrigImageCount);

       reInitImgCountIfNeed(curOrigCount);
       iosLogInfo("after reinit, gOrigImageCount=%d, gHookedImgIdxList=%p, gHookedImgIdxListLen=%d", gOrigImageCount, gHookedImgIdxList, gHookedImgIdxListLen);
   } else {
       iosLogDebug("gJbDylibIdxList=%p, gJbDylibIdxListLen=%d, gHookedImgIdxList=%p, gHookedImgIdxListLen=%d", gJbDylibIdxList, gJbDylibIdxListLen, gHookedImgIdxList, gHookedImgIdxListLen);

       if ((NULL == gJbDylibIdxList) || (gJbDylibIdxListLen <= 0)) {
           reInitImgCountIfNeed(curOrigCount);
       }

       if ((NULL == gHookedImgIdxList) || (gHookedImgIdxListLen <= 0)) {
           generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, curOrigCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
       }
   }
}

static int getOrigImageIndex(int hookedImageIndex){
   iosLogDebug("hookedImageIndex=%d", hookedImageIndex);

   int origImgIdx = DYLD_IMAGE_INDEX_INVALID;

   // uint32_t origImageCount_byLogos = (*_logos_orig$_ungrouped$_dyld_image_count)();
   // os_log(OS_LOG_DEFAULT, "hook_dyld getOrigImageIndex: origImageCount_byLogos=%d", origImageCount_byLogos);

//     uint32_t origImageCount = findRealImageCount();
//     iosLogDebug("origImageCount=%d", origImageCount);
//
//    int* jbDylibImgIdxList = NULL;
//    int jbDylibImgIdxListLen = -1;
//    getJbDylibImgIdxList(origImageCount, &jbDylibImgIdxList, &jbDylibImgIdxListLen);
//    iosLogDebug("jbDylibImgIdxList=%p,jbDylibImgIdxListLen=%d", jbDylibImgIdxList, jbDylibImgIdxListLen);

   reInitAllRelated();

   // if (jbDylibImgIdxListLen > 0){
   if (gJbDylibIdxListLen > 0){
       // check input image index validation
       // int origImgMaxIdx = origImageCount - 1;
       // int origImgMaxIdx = gRealOrigImageCount - 1;
       int origImgMaxIdx = gOrigImageCount - 1;
       // int hookedImgMaxIdx = origImgMaxIdx - jbDylibImgIdxListLen;
       int hookedImgMaxIdx = origImgMaxIdx - gJbDylibIdxListLen;
       iosLogDebug("origImgMaxIdx=%d, hookedImgMaxIdx=%d, hookedImageIndex=%d", origImgMaxIdx, hookedImgMaxIdx, hookedImageIndex);

       if(hookedImageIndex > hookedImgMaxIdx){
           // invalid
           iosLogError("input image index invalid, hookedImageIndex=%d > hookedImgMaxIdx=%d", hookedImageIndex, hookedImgMaxIdx);
           origImgIdx = DYLD_IMAGE_INDEX_INVALID;
       } else {
           // valid
           // origImgIdx = hookedToOrigImageIndex(hookedImageIndex, jbDylibImgIdxList, jbDylibImgIdxListLen,  origImageCount);
           origImgIdx = hookedToOrigImageIndex(hookedImageIndex);
       }
   } else {
       // no jailbreak dylib image index list
       origImgIdx = hookedImageIndex;
   }

   iosLogDebug("hookedImageIndex=%d -> origImgIdx=%d", hookedImageIndex, origImgIdx);

   // if (NULL != jbDylibImgIdxList){
   //     free(jbDylibImgIdxList);
   // }

   return origImgIdx;
}

static int getRealOrOrigImageIndex(int inputImageIndex){
   iosLogDebug("inputImageIndex=%d", inputImageIndex);

   int realOrOrigImgIdx = DYLD_IMAGE_INDEX_INVALID;

   bool isFakeImgIdx = isFakeImageIndex(inputImageIndex);
   iosLogDebug("isFakeImgIdx=%s", boolToStr(isFakeImgIdx));

   if (isFakeImgIdx){
       realOrOrigImgIdx = fakeToRealImageIndex(inputImageIndex);
   } else {
       realOrOrigImgIdx = getOrigImageIndex(inputImageIndex);
   }

   iosLogDebug("inputImageIndex=%d -> realOrOrigImgIdx=%d", inputImageIndex, realOrOrigImgIdx);

   return realOrOrigImgIdx;
}


_dyld_image_count

%hookf(uint32_t, _dyld_image_count, void){
//    iosLogDebug();
   iosLogDebug("%s", "");

   uint32_t origCount = 0;
   int retImageCount = 0;

   if (cfgHookEnable_dyld){
       origCount = %orig();
       iosLogDebug("origCount=%d", origCount);
       retImageCount = origCount;

       if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
//            int* jbDylibIdxList = NULL;
//            int jbDylibIdxListLen = -1;
//            getJbDylibImgIdxList(origCount, &jbDylibIdxList, &jbDylibIdxListLen);
//            iosLogDebug("jbDylibIdxList=%p, jbDylibIdxListLen=%d", jbDylibIdxList, jbDylibIdxListLen);
//            retImageCount = origCount - jbDylibIdxListLen;
//
//            if(NULL != jbDylibIdxList){
//                free(jbDylibIdxList);
//            }

           reInitImgCountIfNeed(origCount);

           // if ((NULL == gHookedImgIdxList) || (gHookedImgIdxListLen <= 0)) {
           //     generateHookedImageIndexList(gJbDylibIdxList, gJbDylibIdxListLen, origCount, &gHookedImgIdxList, &gHookedImgIdxListLen);
           // }

           retImageCount = gHookedImageCount;
           // retImageCount = gOrigImageCount;
       }
   } else {
       origCount = %orig();
       retImageCount = origCount;
   }

   iosLogDebug("%sorigCount=%d -> retImageCount=%d", HOOK_PREFIX(cfgHookEnable_dyld), origCount, retImageCount);
   return retImageCount;
}


_dyld_get_image_name

%hookf(const char*, _dyld_get_image_name, uint32_t image_index){
   iosLogDebug("image_index=%d", image_index);
   const char* retImgName = NULL;

   if (cfgHookEnable_dyld){
       if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
           int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
           bool isValidImgIdx = (realOrOrigImgIdx >= 0);
           if (isValidImgIdx){
               const char* imgName = %orig(realOrOrigImgIdx);
               iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> imgName=%{public}s", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), imgName);
               retImgName = imgName;
           } else {
               iosLogError("fail to get real or origin image index for image_index=%d", image_index);
               retImgName = NULL;
           }
       } else {
           const char * firstImgName = NULL;
           char* randomDylibName = NULL;
           const char* imgName = %orig(image_index);
           bool isJbDylib = isJailbreakDylib(imgName);
           if (isJbDylib){
               if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
                   retImgName = NULL;
               } else if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) {
                   firstImgName = _dyld_get_image_name(0);
                   // normally is app self
                   // eg: /private/var/containers/Bundle/Application/B6327617-9ED7-4DED-AFAC-4D9C92D82377/Aweme.app/Aweme
                   retImgName = firstImgName;
               } else if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) {
                   char* randomName = randomStr(10, NULL);
                   asprintf(&randomDylibName, "/usr/lib/%s.dylib", randomName);
                   retImgName = randomDylibName;
               }
           } else {
               retImgName = imgName;
           }

           // for debug
           if (isJbDylib) {
               iosLogInfo("image_index=%d -> imgName=%{public}s -> isJbDylib=%s -> firstImgName=%{public}s, randomDylibName=%{public}s -> retImgName=%{public}s", image_index, imgName, boolToStr(isJbDylib), firstImgName, randomDylibName, retImgName);
           }
       }
   } else {
       retImgName = %orig(image_index);
   }

   iosLogDebug("%simage_index=%d -> retImgName=%{public}s", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retImgName);
   return retImgName;
}


_dyld_get_image_header

%hookf(const struct mach_header*, _dyld_get_image_header, uint32_t image_index){
   iosLogDebug("image_index=%d", image_index);
//    return %orig;
   const struct mach_header* retMachHeader = NULL;

   if (cfgHookEnable_dyld){
       if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
           int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
           bool isValidImgIdx = (realOrOrigImgIdx >= 0);
           if (isValidImgIdx){
               retMachHeader = %orig(realOrOrigImgIdx);
           } else {
               iosLogError("fail to get real or origin image index for image_index=%d", image_index);
               retMachHeader = NULL;
           }
           iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> retMachHeader=%p", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), retMachHeader);
       } else {
           bool isJbDylib = false;
           const struct mach_header* firstImgHeader = NULL;
           const char* imageName = _dyld_get_image_name(image_index);
           if (NULL == imageName){
               retMachHeader = NULL;
           } else {
               isJbDylib = isJailbreakDylib(imageName);
               if (isJbDylib){
                   if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
                       retMachHeader = NULL;
                   } else if ( (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) || (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) ) {
                       firstImgHeader = _dyld_get_image_header(0);
                       // normally is app self
                       retMachHeader = firstImgHeader;
                   }
               } else {
                   retMachHeader = %orig(image_index);
               }
           }

           // for debug
           if (isJbDylib) {
               iosLogInfo("image_index=%d -> imageName=%{public}s -> isJbDylib=%s -> firstImgHeader=%p -> retMachHeader=%p", image_index, imageName, boolToStr(isJbDylib), firstImgHeader, retMachHeader);
           }
       }
   } else {
       retMachHeader = %orig(image_index);
//        iosLogDebug("%simage_index=%d -> retMachHeader=%p", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retMachHeader);
   }

   iosLogDebug("%simage_index=%d -> retMachHeader=%p", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retMachHeader);
   return retMachHeader;
}


_dyld_get_image_vmaddr_slide

%hookf(intptr_t, _dyld_get_image_vmaddr_slide, uint32_t image_index){
   iosLogDebug("image_index=%d", image_index);
//    return %orig;
   long retSlide = DYLD_IMAGE_SLIDE_INVALID;

   if (cfgHookEnable_dyld){
       if (cfgCurDyldHookType == DYLD_HOOK_COMPLEX){
           int realOrOrigImgIdx = getRealOrOrigImageIndex(image_index);
           bool isValidImgIdx = (realOrOrigImgIdx >= 0);
           if (isValidImgIdx){
               retSlide = %orig(realOrOrigImgIdx);
           } else {
               iosLogError("fail to get real or origin image index for image_index=%d", image_index);
               retSlide = DYLD_IMAGE_SLIDE_INVALID;
           }
           iosLogDebug("image_index=%d -> realOrOrigImgIdx=%d -> isValidImgIdx=%s -> retSlide=0x%lx", image_index, realOrOrigImgIdx, boolToStr(isValidImgIdx), retSlide);
       } else {
           bool isJbDylib = false;
           long firtImgSlide = DYLD_IMAGE_SLIDE_INVALID;
           const char* imageName = _dyld_get_image_name(image_index);
           if (NULL == imageName){
               retSlide = DYLD_IMAGE_SLIDE_INVALID;
           } else {
               isJbDylib = isJailbreakDylib(imageName);
               if (isJbDylib){
                   if (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_NULL) {
                       retSlide = DYLD_IMAGE_SLIDE_INVALID;
                   } else if ( (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_FIRST) || (cfgCurDyldHookType == DYLD_HOOK_SIMPLE_RANDOM_NAME) ) {
                       firtImgSlide = _dyld_get_image_vmaddr_slide(0);
                       // normally is app self
                       retSlide = firtImgSlide;
                   }
               } else {
                   retSlide = %orig(image_index);
               }
           }

           // for debug
           if (isJbDylib) {
               iosLogInfo("image_index=%d -> imageName=%{public}s -> isJbDylib=%s -> firtImgSlide=0x%lx -> retSlide=0x%lx", image_index, imageName, boolToStr(isJbDylib), firtImgSlide, retSlide);
           }
       }
   } else {
       retSlide = %orig(image_index);
//        iosLogDebug("%simage_index=%d -> retSlide=0x%lx", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retSlide);
   }

   iosLogDebug("%simage_index=%d -> retSlide=0x%lx", HOOK_PREFIX(cfgHookEnable_dyld), image_index, retSlide);
   return retSlide;
}


其实无法hook,只能调试或注释掉

_dyld_register_func_for_add_image

%hookf(void, _dyld_register_func_for_add_image, void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)){
//    iosLogInfo("%sfunc=%p -> Omitted", HOOK_PREFIX(cfgHookEnable_dyld), func);
   iosLogInfo("%sfunc=%p", HOOK_PREFIX(cfgHookEnable_dyld), func);

//#ifndef XCODE_DEBUG
   %orig;
//    %orig(func);
//#endif
}


_dyld_register_func_for_remove_image

%hookf(void, _dyld_register_func_for_remove_image, void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)){
//    iosLogInfo("%sfunc=%p -> Omitted", HOOK_PREFIX(cfgHookEnable_dyld), func);
   iosLogInfo("%sfunc=%p", HOOK_PREFIX(cfgHookEnable_dyld), func);
   %orig;
}


其他调试内容

%hookf(int32_t, NSVersionOfRunTimeLibrary, const char* libraryName){
   int32_t rtLibVer = %orig;
   iosLogInfo("libraryName=%s -> rtLibVer=%d", libraryName, rtLibVer);
   return rtLibVer;
}

%hookf(int32_t, NSVersionOfLinkTimeLibrary, const char* libraryName){
   int32_t rtLtLibVer = %orig;
   iosLogInfo("libraryName=%s -> rtLtLibVer=%d", libraryName, rtLtLibVer);
   return rtLtLibVer;
}

%hookf(int, _NSGetExecutablePath, char* buf, uint32_t* bufsize){
   int extPathCpSize = %orig;
   iosLogInfo("buf=%{public}s,*bufsize=%d -> extPathCpSize=%d", buf, *bufsize, extPathCpSize);
   return extPathCpSize;
}


<think>我们被要求比较两个hook库:fishhook和CydiaSubstrate(原名为MobileSubstrate)。根据提供的引用,我们可以从几个方面进行分析:工作原理、适用范围、使用难度、稳定性以及防护能力。首先,引用[1]提到fishhook可以hook系统的函数,特别是C函数,通过监控runtime相关的函数来做到防护。引用[2]说明fishhook利用了dyld加载Mach-O文件的原理,即使在dyld3下也能工作。引用[3]指出fishhook主要用于hook系统C函数,而CydiaSubstrate则可以hook自定义的C函数和Objective-C方法。引用[4]展示了fishhook的一个实际应用:绕过ptrace反调试。下面我们从几个维度进行对比:###1.**工作原理**-**fishhook**:基于dyld的动态链接机制,通过重绑定符号(rebindsymbols)来替换系统库中的函数指针。它只能hook系统提供的C函数(在共享缓存中),不能hook自定义的C函数或Objective-C方法。-**CydiaSubstrate**:由越狱社区的大神saurik开发,它通过代码注入(注入动态库)和替换函数实现(使用MSHookFunction)来实现hook。它不仅可以hook系统C函数,还可以hook自定义的C函数和Objective-C方法(通过MSHookMessageEx)。###2.**适用范围**|特性|fishhook|CydiaSubstrate||---------------------|------------------------------|-------------------------------||系统C函数|✅|✅||自定义C函数|❌|✅||Objective-C方法|❌|✅||非越狱环境|✅(可用于AppStore应用)|❌(需要越狱环境)|注意:fishhook可以在非越狱环境下使用(例如在正常的App中),而CydiaSubstrate通常需要越狱环境。###3.**使用难度**-**fishhook**:相对简单,只需要引入头文件,然后定义重绑定结构体即可。例如,引用[4]中的例子展示了如何hookptrace函数。-**CydiaSubstrate**:需要编写一个动态库,并在其中使用MSHookFunction或MSHookMessageEx。配置相对复杂,通常需要打包为deb在越狱设备上安装。###4.**稳定性**-**fishhook**:由于只针对系统函数,且不修改代码段,而是修改符号表,因此相对稳定。但是,如果系统函数发生变化(例如iOS版本更新),可能需要调整。-**CydiaSubstrate**:功能强大,但由于直接修改内存和代码,可能会导致不稳定(如崩溃)。特别是在不同的iOS版本上,需要适配。###5.**防护能力**-引用[1]提到,通过fishhook可以hookruntime的函数(如method_exchangeImplementations),从而监控谁在hook我们的函数,起到一定的防护作用。-**CydiaSubstrate**:由于其强大的hook能力,它本身也可以被用于防护(例如,hook反调试函数),但更多是被用于攻击(如开发越狱插件)。###6.**其他特性**-**fishhook**:轻量级,只有两个文件(fishhook.h和fishhook.c),易于集成。-**CydiaSubstrate**:提供了一套完整的框架,包括动态库注入、hook管理、消息传递等。###总结对比表|对比维度|fishhook|CydiaSubstrate||------------------|-----------------------------------------------|-----------------------------------------------||**工作原理**|重绑定符号表|代码注入和函数替换||**Hook范围**|仅系统C函数|系统/自定义C函数、Objective-C方法||**使用环境**|非越狱和越狱均可|仅越狱环境||**使用难度**|简单|较复杂||**稳定性**|较高|中等(需适配不同iOS版本)||**防护应用**|可用于防护(监控hook行为)[^1]|主要用于越狱插件开发||**体积/依赖**|轻量(两个文件)|较重(需安装Substrate框架)|###适用场景建议-**fishhook**:适合在非越狱环境下hook系统C函数(如替换NSLog、hookptrace反调试[^4])或用于防护(监控runtime函数调用)。-**CydiaSubstrate**:适合越狱环境下的插件开发,需要hook任意C函数或Objective-C方法。###相关问题1.如何在非越狱环境下使用fishhook替换系统函数?2.CydiaSubstrate如何实现Objective-C方法的hook?3.如何检测和防御fishhook的符号重绑定?4.在iOS逆向工程中,fishhook和CydiaSubstrate分别有哪些典型应用案例?[^1]:引用[1][^2]:引用[2][^3]:引用[3][^4]:引用[4]</think>### Fishhook 与 Cydia Substrate 对比分析 以下从核心原理、适用范围、稳定性、防护能力和使用场景五个维度进行对比: --- #### 1. **核心原理对比** | 特性 | Fishhook | Cydia Substrate | |---------------------|-------------------------------------------|------------------------------------------| | **工作原理** | 基于 **dyld 符号重绑定**<br>修改 Mach-O 文件的符号表(`LC_DYSYMTAB`)[^2] | 基于 **代码注入 + 运行时劫持**<br>通过注入动态库直接修改函数指令(ARM 指令级Hook)[^3] | | **技术层级** | 用户态动态链接层(`dyld` 加载阶段) | 内核/用户态双重劫持(更底层) | | **依赖项** | 无需越狱(可在 App Store 应用中使用) | 需越狱环境(依赖 MobileLoader 注入) | > **关键差异**: > - Fishhook 仅能 Hook **系统 C 函数**(如 `ptrace`, `NSLog`)[^2][^4] > - Cydia Substrate 可 Hook **任意函数**(包括自定义 C 函数和 Objective-C 方法)[^3] --- #### 2. **适用范围对比** | 场景 | Fishhook | Cydia Substrate | |---------------------|-----------------------------------|----------------------------------| | **系统 C 函数** | ✅ 原生支持(如 `printf`, `open`) | ✅ 支持 | | **自定义 C 函数** | ❌ 无法 Hook [^3] | ✅ 支持(指令级覆盖) | | **Objective-C 方法**| ❌ 不支持 | ✅ 核心能力(通过 `MSHookMessageEx`) | | **非越狱环境** | ✅ 可用 | ❌ 不可用 | --- #### 3. **稳定性与兼容性** - **Fishhook**: - 兼容 dyld2/dyld3(iOS 13+ 仍有效)[^2] - 轻量级(仅 2 个文件),但易被检测: ```c // 检测示例:比较函数指针 if (ptrace != &sys_ptrace) { /* 发现Hook */ } ``` - **Cydia Substrate**: - 强兼容性(支持 ARM/ARM64),但需随 iOS 版本更新适配 - 易引发崩溃(指令覆盖错误时) --- #### 4. **防护能力对比** | 防护场景 | Fishhook | Cydia Substrate | |---------------------|-----------------------------------|----------------------------------| | **反调试绕过** | ✅ 示例:Hook `ptrace`[^4] | ✅ 更彻底(可 Hook `sysctl`等) | | **Anti-Hook 检测** | ❌ 自身易被检测 | ✅ 可对抗符号表检测 | | **运行时防护** | 仅能监控部分系统 Hook [^1] | ✅ 完整监控所有方法调用 | > **Fishhook 防护局限**: > 虽然可监控 `method_exchangeImplementations` 等 Runtime 调用[^1],但无法阻止 Substrate 的指令级 Hook。 --- #### 5. **典型使用场景** - **Fishhook 适用**: - 非越狱环境下的轻量级 Hook(如绕过基础反调试) - 监控系统函数调用(如记录 `NSLog` 输出)[^2] - **Cydia Substrate 适用**: - 越狱插件开发(如游戏修改、自动化脚本) - 深度逆向(Hook 私有框架函数) --- ### 总结建议 | **需求** | **推荐工具** | |-------------------------|--------------------| | 非越狱环境 + 系统函数 Hook | ✅ Fishhook | | 越狱环境 + 深度定制 | ✅ Cydia Substrate | | 对抗高级反作弊 | ❌ 两者均不足(需结合内核绕过) | > **风险提示**:两者均可能触发游戏反作弊(如腾讯 ACE),服务器行为分析是更有效的检测手段。 --- ### 相关问题 1. 如何在非越狱环境中实现 Objective-C 方法 Hook? 2. Fishhook 的符号重绑定机制在 dyld3 下如何工作? 3. Cydia Substrate 的指令级 Hook 具体如何实现? 4. 现代 iOS 反作弊系统如何检测 Fishhook 和 Substrate? 5. 如何结合 Fishhook 和 Runtime 实现双向防护? [^1]: 引用[1] [^2]: 引用[2] [^3]: 引用[3] [^4]: 引用[4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黑猫成仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值