在應用開發過程當中,咱們不單單須要完成正常的業務邏輯,考慮應用性能、代碼健壯相關的問題,咱們有時還須要考慮到應用安全的問題。面試
那麼應用安全的問題涉及到不少方面。好比防止靜態分析的,代碼混淆、邏輯混淆;防止重簽名的,應用ID檢測、甚至是代碼的HASH檢測等等。那麼這篇文章我想聊聊關於代碼的注入檢測,由於發現隨着iOS系統的更新,咱們防禦的手段發生了一些變化。算法
代碼注入的方式安全
代碼注入的方式大體分爲兩種bash
越獄注入:經過修改 DYLD_INSERT_LIBRARIES
環境變量的值,來插入動態庫並執行app
非越獄注入:函數
直接將自定義的Framwork或者dylib庫打包進入APP並重簽名工具
利用yololib修改MachO文件,添加庫路徑.在應用啓動時,dyld會加載並執行.源碼分析
在工程的Build Settings中找到Other Linker Flages 並添加字段性能
-Wl,-sectcreate,__RESTRICT,__raestrict,/dev/null
學習
此操做的做用是在可執行文件中添加一個Section.咱們使用MachOView分析以下:
當MachO文件中擁有這個字段,那麼咱們經過越獄環境插入動態庫的方式就會失效.起到防禦的做用.其原理在DYLD源碼中能夠分析到.
首先這裏分析的DYLD
源碼版本是519.2.2版本. 咱們能夠經過檢索DYLD_INSERT_LIBRARIES定位到_main函數加載插入動態庫的代碼以下.
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
複製代碼
可是早在這個環境變量判斷以前,dyld已經作了一個判斷
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段
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
gLinkContext.processIsRestricted = true;
}
複製代碼
經過註釋也能發現.任意進程的__RESTRICT段設置爲restricted動態庫插入將被限制. 咱們進入到processIsRestricted函數內,實現以下.
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變量是「白名單」.
小編推薦一個qq羣:551346706這個羣挺不錯的,我就是在羣裏看到的學習資料寫出的這篇文章,這篇文章有源碼和視頻資料須要的能夠了解一下,羣裏還有各大廠的面試題和算法。還有不少的大牛平時的工做問題也能解決。