本文不包含具體編寫插件的內(nèi)容吨瞎,只是從Tweak的原理去探究怎么防護插件的注入。
生成一個Tweak插件
有兩種方式生成Tweak插件,一種是MonkeyDev爷恳,一種是Theos。
安裝MonkeyDev
MonkeyDev安裝與說明https://github.com/AloneMonkey/MonkeyDev
Monkey的使用
- MonkeyApp 重簽名app象踊,可以選擇一個脫殼的ipa直接跑起來debug
- MonkeyPod 通過pod集成插件
- Command-line 命令行工具
- Tweak 越獄插件
我們選擇Logos Tweak 來創(chuàng)建插件
項目中xm就是需要編寫的hook文件温亲、mm生成的目標文件
plist是注入目標的配置,截圖上默認的是springboard應(yīng)用
build settings里的設(shè)置杯矩,有三個空的需要填一下栈虚,如果是ssl登錄,則不需要填寫密碼史隆。最后一個是安裝插件的時候魂务,殺掉的目標進程。
安裝Theos
Thoes的安裝與使用https://github.com/theos/theos
編譯生成Tweak
從編譯產(chǎn)物中泌射,可看出是一個dylib動態(tài)庫文件
原理
Tweak通過dyld insert librarys 環(huán)境變量 插入到系統(tǒng)里
插入動態(tài)庫的核心源碼
// load any inserted libraries(越獄的環(huán)境都是用這個U辰)
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
通過dyld源碼查看到環(huán)境變量sEnv.DYLD_INSERT_LIBRARIES不為空的時候,會插入動態(tài)庫熔酷,所以我們繼續(xù)看源碼環(huán)境變量相關(guān)的部分
在insert之前pruneEnvironmentVariables這行代碼表示移除相關(guān)的環(huán)境變量孤紧,因此我們只要關(guān)注!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache這個判斷為true就可以
if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
}
繼續(xù)查看源碼看到hasRestrictedSegment這個函數(shù),Mach-O里如果包含了Restricted段就可以是值為true.
// support chrooting from old kernel
bool isRestricted = false;
bool libraryValidation = false;
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
// issetugid 這個函數(shù)不能在上架的app使用
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
isRestricted = true;
}
bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
// On OS X CS_RESTRICT means the program was signed with entitlements
if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) {
isRestricted = true;
}
// Library Validation loosens searching but requires everything to be code signed
if ( flags & CS_REQUIRE_LV ) {
isRestricted = false;
libraryValidation = true;
}
}
gLinkContext.allowAtPaths = !isRestricted;
gLinkContext.allowEnvVarsPrint = !isRestricted;
gLinkContext.allowEnvVarsPath = !isRestricted;
gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP;
gLinkContext.allowClassicFallbackPaths = !isRestricted;
gLinkContext.allowInsertFailures = false;
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;
}
防護
通過上邊的源碼纯陨,得到了一個結(jié)論坛芽,只要在Mach-O里包含了__RESTRICT段就能防止insert library。圖里在otherLinker里增加了幾個參數(shù)
增加完后翼抠,用MachOview查看內(nèi)容咙轩,Mach-O里成功的增加了__RESTRICT段,防護住了應(yīng)用插件
再突破
修改Mach-O,使用MachOview阴颖,修改完成后保存活喊。然后需要重簽名后運行,重簽名后量愧,bundleId變了钾菊,可以通過其他方式監(jiān)測Hook情況
再防護
應(yīng)用程序內(nèi)校驗Mach-O情況帅矗,通過上邊的源碼hasRestrictedSegment函數(shù),去查看是否__RESTRICT段被破壞煞烫。
后續(xù)
hook hasRestrictedSegment 方法浑此,繼續(xù)突破與防護,永無止境滞详。