颤抖吧,iGrimace!手把手教你hook以root权限运行的App


#1

做过iOS积分墙(OfferWall)的朋友都知道,这类Apple明令禁止的App虽然上不了台面,但却既能给墙上的各种App增加曝光率,又能给开发者带来可观的收益。
image
一涉及到金钱,各种小聪明就蠢蠢欲动了——类似于游戏外挂,刷积分墙的工具也能给背后的那一双双手带来不菲的现金收入。这些工具中,由论坛一位不方便透露姓名的朋友制作的■■■■■■■■就是很有代表性的一款越狱插件,它只在地下市场流通,能够轻松将越狱iOS伪装成一台台全新的机器,骗过各种积分墙App的检察,把一个个钱币硬生生地从积分墙上抠下来,装进自己的腰包😱(本文对积分墙和■■■■■■■■类工具本身不予置评,仅仅讨论hook root App的方法)
image
相信那些具备iOS逆向工程能力,又对■■■■■■■■有一定研究的朋友都能发现,■■■■■■■■的实际功能只占这个插件体积的1/3,另外的部分是作者精心构造的防护代码,用以防护盗版。因此,对于大多数越狱开发者来说,重写一个■■■■■■■■的工作量比破解它其实要小得多。但是明知山有虎,偏向虎山行,很多朋友偏偏想要破解它,来挑战自己,锻炼技术。■■■■■■■■因为含有reboot等功能,需要以root权限运行,而CydiaSubstrate对root权限的App是无效的(mobile权限的代码怎么能够影响root权限的代码?),所以书上讲的基于CydiaSubstrate的tweak对root权限的App不起作用。那么问题来了,学挖掘机技术到底哪家强?

一、谁在阻止dylib注入root权限的App?

从这个帖子,我们知道,现在iOS上的绝大多数以root权限运行的App,都是通过setuid + bash来实现的;而这个帖子又说,可执行文件如果被setuid或setgid,那么DYLD_环境变量会被dyld无视,因此DYLD_INSERT_LIBRARIES不起作用,tweak自然无效。这两点的综合作用,造成了root App的高保护性,没有dylib的加持,想要修改这类App,貌似就只剩静态patch这一条路了。事实上,很多破解版也是这么做的——他们吭哧吭哧地阅读汇编代码,把一个个校验判断给nop掉。事情真的有这么复杂吗?

二、让dyld对DYLD_INSERT_LIBRARIES旧情复燃?

这个帖子提到的switch case语句可以得知,当sRestrictedReason为restrictedBySetGUid时,DYLD_INSERT_LIBRARIES因setuid而被dyld忽略。我们顺着这条线索,看看restrictedBySetGUid是怎么来的:

static bool processRestricted(const macho_header* mainExecutableMH)
{
    // all processes with setuid or setgid bit set are restricted
    if ( issetugid() ) {
                sRestrictedReason = restrictedBySetGUid;
                return true;
        }

原来,当issetugid()返回1时,sRestrictedReason被设置为restrictedBySetGUid。如果要修改这个返回值,就必须patch dyld,这样就要一机一patch,不仅不具备通用性,还非常危险,我们不能冒这个险。因此DYLD_INSERT_LIBRARIES这条路基本可以放弃了,虽然旧爱还是最美,但也是时候放手追求新的生活鸟~

三、为什么必须是DYLD_INSERT_LIBRARIES?

到现在为止,我们讨论问题的大前提,都默认是要通过DYLD_INSERT_LIBRARIES这个环境变量来动态指定需要加载的dylib。虽然在越狱开发界,一众大佬一再强调,相对于传统hack,我们的优势就在于“动态hook”——但这不代表我们就可以完全丢掉传统的“静态patch”。既然DYLD_INSERT_LIBRARIES这个动态方法被限制得这么死,我们何不换个思路,看看从偏静态(用“偏静态”这个词的原因稍后会解释)的角度,我们能做些什么?

四、除DYLD_INSERT_LIBRARIES 外,dylib还有其他的加载方式吗?

还记得书上第19页对dylib的简单介绍吗?当一个App启动时,dyld把App需要的dylib加载进App的内存空间。注意这里的字眼:“App需要的dylib”——App需要的dylib,是怎么指定的呢?如果你对文件格式有所了解,就会知道,这些App运行所需要的信息,一般都存放在其MachO头部中,其中dylib的信息是由load commands指定的,
如图所示:


因为这些信息是以静态的方式存放在二进制文件里(不是由DYLD_INSERT_LIBRARIES动态指定),而又是由dyld动态加载的,所以我们给它起了个“偏静态”的名字。也就是说,在此App得到执行时,dyld会查看其MachO头部中的load commands,并把里面LC_LOAD_DYLIB相关的dylib给加载到进程的内存空间。

五、曲线救国,通过LC_LOAD_DYLIB实现dylib的加载

既然DYLD_INSERT_LIBRARIES被各种手段严格限制,动态的方式难以发挥作用,我们就另辟蹊径,用LC_LOAD_DYLIB来围魏救赵。看似神秘的LC_LOAD_DYLIB也只是MachO头部的一个load command而已,我们要做的,就是修改App可执行文件的头部,给它添加这么一个load command,并指定load我们构造的dylib就好。这个操作有点类似于碟中谍3里的颅内炸弹,显得十分高级😊
image
往MachO头部添加一个新的load command是一个细致活儿,像楼主这样对其头部结构不够熟悉的菜鸟,很容易因为一两个字节的偏差毁掉整个MachO文件。好在丹麦高手Asger Hautop Drewsen开发的insert_dylib正是为了解决这个问题而存在的,我们直接使用这个工具来完成操作就好了~

六、实例演示

下面,我就拿这个帖子里编写的一个root权限App来作为目标,将界面的红色背景修改为蓝色。
image

编写tweak

代码如下,很简单:

@interface RootViewController: UIViewController
@end

%hook RootViewController
- (void)loadView
{
        %orig;
        self.view.backgroundColor = [UIColor blueColor];
}
%end

用Theos环境编译后,注意保留生成的dylib,将其重命名为iosre.dylib后拷贝到iOS中,放在/var/tmp/下备用。(注意这里的dylib路径,与下面一致)

生成RootApp.app/RootApp

snakeninnysimac:rootapp snakeninny$ make
Making all for application RootApp...
 Copying resource directories into the application wrapper...
 Compiling main.m...
 Compiling RootAppApplication.mm...
 Compiling RootViewController.mm...
 Linking application RootApp...
 Stripping RootApp...
 Signing RootApp...

生成的可执行文件是/Users/snakeninny/Code/rootapp/obj/RootApp.app/RootApp。注意,这里不要移动RootApp的位置。

生成insert_dylib

image

用insert_dylib给RootApp添加一个LC_LOAD_DYLIB,指定加载iosre.dylib

snakeninnysimac:~ snakeninny$ /path/to/insert_dylib /var/tmp/iosre.dylib /Users/snakeninny/Code/rootapp/obj/RootApp.app/RootApp // 注意这里的dylib路径,与上面一致
The provided dylib path doesn't exist. Continue anyway? [y/n] y // OSX在本地找/var/tmp/iosre.dylib,当然找不到,因为它在iOS上
Binary is a fat binary with 3 archs.
LC_CODE_SIGNATURE load command found. Remove it? [y/n] y
LC_CODE_SIGNATURE load command found. Remove it? [y/n] y
LC_CODE_SIGNATURE load command found. Remove it? [y/n] y
Added LC_LOAD_DYLIB to all archs in /Users/snakeninny/Code/rootapp/obj/RootApp.app/RootApp_patched

insert_dylib在RootApp的同目录下生成了一个新文件,RootApp_patched,用它替换掉原来的RootApp:

snakeninnysimac:~ snakeninny$ mv /Users/snakeninny/Code/rootapp/obj/RootApp.app/RootApp_patched /Users/snakeninny/Code/rootapp/obj/RootApp.app/RootApp 

RootApp位于iOS上的/Applications/下,无需签名校验,因此我们不需要像这个帖子提到的那样安装AppSync来禁用iOS签名校验。

安装,测试

snakeninnysimac:rootapp snakeninny$ make package install // 这里的make package install会打包并安装我们修改过的RootApp,而不是生成新的RootApp
Making all for application RootApp...
 Copying resource directories into the application wrapper...
make[2]: Nothing to be done for `internal-application-compile'.
Making stage for application RootApp...
dpkg-deb: building package `com.naken.rootapp' in `./com.naken.rootapp_0.0.1-14_iphoneos-arm.deb'.
install.exec "cat > /tmp/_theos_install.deb; dpkg -i /tmp/_theos_install.deb && rm /tmp/_theos_install.deb" < "./com.naken.rootapp_0.0.1-14_iphoneos-arm.deb"
(Reading database ... 4934 files and directories currently installed.)
Preparing to replace com.naken.rootapp 0.0.1-13 (using /tmp/_theos_install.deb) ...
Unpacking replacement com.naken.rootapp ...
Setting up com.naken.rootapp (0.0.1-14) ...
snakeninnysimac:rootapp snakeninny$ 

启动RootApp,背景由红色变为了蓝色:
image
点击中间的黄色按钮,iOS重启了。很明显,/var/tmp/iosre.dylib被加载到了RootApp里,且其原功能不受影响。实验成功!

总结

同“防止tweak依附,App有高招;破解App保护,tweak留一手”一样,这篇帖子涉及到了一些MachO文件格式的内容,而绝大多数AppStore开发者对于这方面的知识可以说是一无所知。随着国内iOS逆向工程氛围的逐渐成熟,我们也都能感觉到,App攻防的战场逐渐由台前转为幕后,对iOS开发者的能力要求也越来越高。在可以预见的未来,iOS开发门槛只会越来越低,只有精于这个方向的人才方能具备足够的竞争力,在一片红海中脱颖而出。多学一些东西总归是没什么坏处,在对正向开发越来越熟悉的档口,逆向工程或许就是你脱胎换骨的点金石。


不越狱可以使用tweak吗?
iOS 11 dyld 报错 code signing blocked mmap()
#2

好厉害,过来顶一下


#3

怒顶狗神


#4

怒顶狗神


#5

狗神威武


#6

怒顶!!!!


#7

注入完 iosre.dylib 之后闪退啊


#8

Oct 16 16:47:01 FR9Q SpringBoard[28] : LICreateIconForImage passed NULL CGImageRef image
Oct 16 16:47:01 FR9Q networkd[148] : Analytics Engine: double ON for app: com.mobisentry.firstdemo
Oct 16 16:47:01 FR9Q ReportCrash[423] : MS:Notice: Injecting: (null) [ReportCrash] (847.21)
Oct 16 16:47:01 FR9Q ReportCrash[423] : ReportCrash acting against PID 422
Oct 16 16:47:01 FR9Q ReportCrash[423] : Formulating crash report for process FirstDemo[422]
Oct 16 16:47:01 FR9Q com.apple.launchd[1] (UIKitApplication:com.mobisentry.firstdemo[0x1e46][422]) : (UIKitApplication:com.mobisentry.firstdemo[0x1e46]) Job appears to have crashed: Trace/BPT trap: 5
Oct 16 16:47:01 FR9Q com.apple.launchd[1] (UIKitApplication:com.mobisentry.firstdemo[0x1e46]) : (UIKitApplication:com.mobisentry.firstdemo[0x1e46]) Throttling respawn: Will start in 2147483647 seconds
Oct 16 16:47:01 FR9Q backboardd[34] : Application ‘UIKitApplication:com.mobisentry.firstdemo[0x1e46]’ exited abnormally with signal 5: Trace/BPT trap: 5
Oct 16 16:47:01 FR9Q ReportCrash[423] : Saved crashreport to /var/mobile/Library/Logs/CrashReporter/FirstDemo_2014-10-16-164701_FR9Q.plist using uid: 0 gid: 0, synthetic_euid: 501 egid: 0
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.501350]: Async scan requested by “locationd” for 1 iterations with maxage=0 priority low on en0
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.501927]: Enqueuing command type: “Scan” pending commands: 0
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.502364]: Dequeuing command type: “Scan” pending commands: 0
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.502718]: Attempting Apple80211ScanAsync on en0
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.939167]: Completed Apple80211ScanAsync on en0 (0)
Oct 16 16:47:02 FR9Q wifid[75] : WiFi:[435142022.943008]: Async scan request completed for “locationd” (0)
Oct 16 16:47:08 FR9Q wifid[75] : WiFi:[435142028.108251]: WiFiLocaleManagerCheckLocale: trying to determine locale…


#9

是不是缺少什么工具啊 注入失败 而且app 闪退啊


#10

你注入到了哪个App里?


#11

就是上一个例子中生成的RootApp.app啊


#12

就是上一个例子中生成的RootApp.app啊


#13

在不注入的情况下,你的RooApp可以正常工作吗?
我把RootApp的工程源码上传到了这里,你下载了编译一下,拿我的版本试试能不能成功注入dylib


#14

没注入之前是可以正常工作的


#15

注入完之后app 闪退 但是登录手机后台运行可以成功,只是中间的按钮没法交互 求大神解释一下原因(ps 没注入前 app出现的红色背景上的按钮 也不能交互)


#16

全部成功了 偶也


#17

你上面提到的问题都是怎么解决的,在帖子里说明一下,也给后来人提供一些帮助啊


#18

好啊,抽空。现在没注入iosre.dylib库之前点击按钮可以重启,重启之后点击按钮还可以重启,但是当注入之后,北京颜色可以改变,点击按钮重启之后,再点击app会闪退,是Demo就这样啊,还是我的步骤有问题


#19

闪退原因是什么?syslog里面查查看


#20

注入玩之后,可以重启了,但是第二次重启 程序闪退啊
日志如下:
Oct 23 14:33:19 FR9Q wifid[75] : WiFi:[435738799.858831]: Client identityservices set type to background application
Oct 23 14:33:19 FR9Q wifid[75] : WiFi:[435738799.859486]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd identityservices
Oct 23 14:33:19 FR9Q wifid[75] : WiFi:[435738799.859834]: Already connected to LinkSys2013.
Oct 23 14:33:19 FR9Q com.apple.imfoundation.IMRemoteURLConnectionAgent[522] : MS:Notice: Injecting: com.apple.imfoundation.IMRemoteURLConnectionAgent [com.apple.imfoundation.IMRemoteURLConnectionAgent] (847.21)
Oct 23 14:33:19 FR9Q wifid[75] : WiFi:[435738799.895152]: Creating client for “IMRemoteURLConne”
Oct 23 14:33:21 FR9Q SpringBoard[28] : LICreateIconForImage passed NULL CGImageRef image
Oct 23 14:33:21 FR9Q networkd[152] : Analytics Engine: double ON for app: com.mobisentry.rootapp
Oct 23 14:33:21 FR9Q ReportCrash[526] : MS:Notice: Injecting: (null) [ReportCrash] (847.21)
Oct 23 14:33:21 FR9Q ReportCrash[526] : ReportCrash acting against PID 524
Oct 23 14:33:21 FR9Q ReportCrash[526] : Formulating crash report for process RootApp[524]
Oct 23 14:33:21 FR9Q com.apple.launchd[1] (UIKitApplication:com.mobisentry.rootapp[0x2988][524]) : (UIKitApplication:com.mobisentry.rootapp[0x2988]) Job appears to have crashed: Trace/BPT trap: 5
Oct 23 14:33:21 FR9Q com.apple.launchd[1] (UIKitApplication:com.mobisentry.rootapp[0x2988]) : (UIKitApplication:com.mobisentry.rootapp[0x2988]) Throttling respawn: Will start in 2147483647 seconds
Oct 23 14:33:21 FR9Q backboardd[34] : Application ‘UIKitApplication:com.mobisentry.rootapp[0x2988]’ exited abnormally with signal 5: Trace/BPT trap: 5
Oct 23 14:33:21 FR9Q ReportCrash[526] : Saved crashreport to /Library/Logs/CrashReporter/RootApp_2014-10-23-143321_FR9Q.plist using uid: 0 gid: 0, synthetic_euid: 0 egid: 0
Oct 23 14:33:25 FR9Q wifid[75] : WiFi:[435738805.467847]: MIS state is Disabled
Oct 23 14:33:25 FR9Q wifid[75] : WiFi:[435738805.468378]: MIS state queried by “identityservices” is Disable
Oct 23 14:33:25 FR9Q wifid[75] : WiFi:[435738805.473084]: Client identityservices set type to normal application
Oct 23 14:33:25 FR9Q wifid[75] : WiFi:[435738805.473875]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd
Oct 23 14:33:27 FR9Q wifid[75] : WiFi:[435738807.730726]: Client identityservices set type to background application
Oct 23 14:33:27 FR9Q wifid[75] : WiFi:[435738807.731012]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd identityservices
Oct 23 14:33:27 FR9Q wifid[75] : WiFi:[435738807.731322]: Already connected to LinkSys2013.
Oct 23 14:33:31 FR9Q wifid[75] : WiFi:[435738811.373107]: MIS state is Disabled
Oct 23 14:33:31 FR9Q wifid[75] : WiFi:[435738811.373615]: MIS state queried by “identityservices” is Disable
Oct 23 14:33:31 FR9Q wifid[75] : WiFi:[435738811.377730]: Client identityservices set type to normal application
Oct 23 14:33:31 FR9Q wifid[75] : WiFi:[435738811.378338]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd
Oct 23 14:33:31 FR9Q wifid[75] : WiFi:[435738811.997320]: WiFi unquiescing requested by “locationd”
Oct 23 14:33:32 FR9Q wifid[75] : WiFi:[435738812.015318]: WiFi unquiescing requested by “locationd”
Oct 23 14:33:42 FR9Q wifid[75] : WiFi:[435738822.023478]: WiFi unquiescing requested by “locationd”
Oct 23 14:33:52 FR9Q wifid[75] : WiFi:[435738832.863926]: Client identityservices set type to background application
Oct 23 14:33:52 FR9Q wifid[75] : WiFi:[435738832.864218]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd identityservices
Oct 23 14:33:52 FR9Q wifid[75] : WiFi:[435738832.864530]: Already connected to LinkSys2013.
Oct 23 14:33:54 FR9Q wifid[75] : WiFi:[435738834.119713]: MIS state is Disabled
Oct 23 14:33:54 FR9Q wifid[75] : WiFi:[435738834.120213]: MIS state queried by “identityservices” is Disable
Oct 23 14:33:54 FR9Q wifid[75] : WiFi:[435738834.124242]: Client identityservices set type to normal application
Oct 23 14:33:54 FR9Q wifid[75] : WiFi:[435738834.124934]: BG Application: Not Present, BG Daemon: Present. Daemons: networkd lockdownd sharingd apsd
Oct 23 14:33:56 FR9Q wifid[75] : WiFi:[435738836.290840]: IMRemoteURLConne requesting removal of BGScan networks
Oct 23 14:33:56 FR9Q wifid[75] : WiFi:[435738836.294658]: No change in Background Scan candidates. Skip re-programming Background Scan
Oct 23 14:33:56 FR9Q wifid[75] : WiFi:[435738836.295721]: Already connected to LinkSys2013.
Oct 23 14:33:56 FR9Q wifid[75] : WiFi:[435738836.296210]: Removing client for “IMRemoteURLConne”
Oct 23 14:33:56 FR9Q wifid[75] : WiFi:[435738836.385078]: Client identityservices set type to background application