在應用開發過程中,我們不僅僅需要完成正常的業務邏輯,考慮應用性能、代碼健壯相關的問題,我們有時還需要考慮到應用安全的問題。
那麼應用安全的問題涉及到很多方面。比如防止靜态分析的,代碼混淆、邏輯混淆;防止重簽名的,應用ID檢測、甚至是代碼的HASH檢測等等。那麼這篇文章我想聊聊關于代碼的注入檢測,因為發現随着iOS系統的更新,我們防護的手段發生了一些變化。
代碼注入的方式
代碼注入的方式大緻分為兩種
- 越獄注入:通過修改
環境變量的值,來插入動态庫并執行DYLD_INSERT_LIBRARIES
- 非越獄注入:
- 直接将自定義的Framwork或者dylib庫打包進入APP并重簽名。
- 利用yololib修改MachO檔案,添加庫路徑.在應用啟動時,dyld會加載并執行.
早期防護方式
在工程的Build Settings中找到Other Linker Flages 并添加字段
-Wl,-sectcreate,__RESTRICT,__raestrict,/dev/null
此操作的作用是在可執行檔案中添加一個Section.我們使用MachOView分析如下:
當MachO檔案中擁有這個字段,那麼我們通過越獄環境插入動态庫的方式就會失效.起到防護的作用.其原理在DYLD源碼中可以分析到.
dyld源碼分析
首先這裡分析的
DYLD源碼版本是519.2.2版本.
我們可以通過檢索DYLD_INSERT_LIBRARIES定位到_main函數加載插入動态庫的代碼如下.
// load any inserted libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
但是早在這個環境變量判斷之前,dyld已經做了一個判斷
if ( gLinkContext.processIsRestricted ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
}
如果判斷出程序是restricted!也就是目前程序是限制插入動态庫的!就會調用pruneEnvironmentVariables函數移除相關的環境變量.
那麼我們的processIsRestricted值什麼時候為true呢?
繼續分析源碼可以發現兩個關鍵函數影響其值.其中 hasRestrictedSegment 函數專門檢測RESTRICT段
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
gLinkContext.processIsRestricted = true;
}
通過注釋也能發現.任意程序的__RESTRICT段設定為restricted動态庫插入将被限制.
我們進入到processIsRestricted函數内,實作如下.
#if __MAC_OS_X_VERSION_MIN_REQUIRED
static bool hasRestrictedSegment(const macho_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
//dyld::log("seg name: %s\n", seg->segname);
if (strcmp(seg->segname, "__RESTRICT") == 0) {
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
if (strcmp(sect->sectname, "__restrict") == 0)
return true;
}
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
return false;
}
是以通過添加Other Linker Flags 在MachO中設定RESTRICT段指派為restricted可以用來防護越獄的代碼注入.
但是新版的dyld源碼中去掉了__RESTRICT檢測.從iOS10開始,這種防護手段已失效
DYLD_INSERT_LIBRARIES 檢測
那麼既然dyld加載過程不再檢測__RESTRICT段了我們就手動的檢測
DYLD_INSERT_LIBRARIES
環境變量.通過函數可檢視目前程序環境變量的值.
char *env = getenv("DYLD_INSERT_LIBRARIES");
NSLog(@"%s",env);
在沒有插入動态庫時,env為null.
那麼一旦為自己的應用寫入插件時,我們就可以看到控制台的輸出
2019-01-03 19:20:37.285 antiInject[7482:630392] /Library/MobileSubstrate/MobileSubstrate.dylib
白名單檢測
那麼上面的檢測隻可以檢測越獄環境中的代碼注入,在非越獄環境中,逆向工程師可以利用yololib工具注入動态庫.是以我們可以檢索一下自己的應用程式所加載的動态庫是否是我們源程式所有
bool HKCheckWhitelist(){
int count = _dyld_image_count();
for (int i = 0; i < count; i++) {
//周遊拿到庫名稱!
const char * imageName = _dyld_get_image_name(i);
//判斷是否在白名單内,應用本身的路徑是不确定的,是以要除外.
if (!strstr(libraries, imageName)&&!strstr(imageName, "/var/mobile/Containers/Bundle/Application")) {
printf("該庫非白名單之内!!\n%s",imageName);
return NO;
}
}
return YES;
}
其中libraries變量是白名單.
小編給大家推薦一個iOS技術交流群:551346706!群内提供資料結構與算法、底層進階、swift、逆向、底層面試題整合文檔等免費資料!讓我們來互相學習,為了今年的跳槽加分吧!