Run an App as root on iOS

Hi everyone, what’s up!

After the writing of “Run a daemon (as root) on iOS”, some of you were asking about how to write an App with root privilege. So here comes the tutorial. Our target today is an App with root privileges. When you press a button, iOS reboots.

Part I Basic theory

1. App ownership

Please take a look at tutorial one if you have no idea what’s ownership. After reading that, you know all Apps under /Applications/ are owned by root:wheel.

Note, chown root:wheel is done by Theos’ fauxsu, which is necessary in this post and also explained in tutorial one!

2. User identifier

According to wikipedia and this page, we know that the owner of a file is different from the owner of a process, the latter of which is often referred to as user identifier.

So to run the App as root, we need to change the real user id or effective user id of the app to 0, you can think of them as parallel to “su root” and “sudo”.

Let’s take a look at the default user ids of an App.

3. setuid permission

According to this doc

When set-user identification (setuid) permission is set on an executable file, a process that runs this file is granted access based on the owner of the file (usually root), rather than the user who is running the executable file.

which provides a way to modify the user ids of a process. This is the key feature we’re making use of, soon you’ll see what we can do with it.

Part II Composing

First creat a new App with Theos.

FunMaker-MBP:Code snakeninny$ nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/activator_event
  [2.] iphone/application_modern
  [3.] iphone/cydget
  [4.] iphone/flipswitch_switch
  [5.] iphone/framework
  [6.] iphone/ios7_notification_center_widget
  [7.] iphone/library
  [8.] iphone/notification_center_widget
  [9.] iphone/preference_bundle_modern
  [10.] iphone/tool
  [11.] iphone/tweak
  [12.] iphone/xpc_service
Choose a Template (required): 2
Project Name (required): iOSREbooter 
Package Name [com.yourcompany.iosrebooter]: com.naken.iosrebooter
Author/Maintainer Name [snakeninny]: snakeninny
[iphone/application_modern] Class name prefix (two or more characters) [XX]: RB
Instantiating iphone/application_modern in iosrebooter/...
Done.

Then modify the contents of RBRootViewController.m

...
- (void)addButtonTapped:(id)sender {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
	NSLog(@"iOSRE: %d, %d, %d", getuid(), geteuid(), system("reboot"));
#pragma GCC diagnostic pop	
	[_objects insertObject:[NSDate date] atIndex:0];
	[self.tableView insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:0 inSection:0] ] withRowAnimation:UITableViewRowAnimationAutomatic];
}
...

Compile and install this App on your device. Launch iOSREbooter, hit the + button on the top right corner, and take a look at the output in syslog:

Sep 26 15:56:25 FunMaker-SE iOSREbooter[795] <Warning>: iOSRE: 501, 501, 256

Ok, the real user id and effective user id of process iOSREbooter are both 501, i.e. mobile, and reboot failed for sure. So how do we reset the user ids of this process? Yes, it’s setuid permission/bit, and all we need is a makefile feature, like this:

after-stage::
	$(ECHO_NOTHING)chmod +s $(THEOS_STAGING_DIR)/Applications/iOSREbooter.app/iOSREbooter$(ECHO_END)

With this 2 lines of script, Theos will automatically set the setuid bit of our executable binary /Applications/iOSREbooter.app/iOSREbooter.

With setuid bit, we can call setuid() or seteuid() inside iOSREbooter to modify real/effective user ids. Let’s use setuid() this time:

Note, most of the time you only need to seteuid rather than setuid (Think about su root vs. sudo, but we don’t have sudo on iOS BTW)!

#import "RBAppDelegate.h"

int main(int argc, char *argv[]) {
	@autoreleasepool {
	        setuid(0);		
		return UIApplicationMain(argc, argv, nil, NSStringFromClass(RBAppDelegate.class));
	}
}

And our Makefile looks like this:

export THEOS_DEVICE_IP = localhost
export THEOS_DEVICE_PORT = 2222
export ARCHS = armv7 arm64
export TARGET = iphone:clang:latest:8.0

include $(THEOS)/makefiles/common.mk

APPLICATION_NAME = iOSREbooter
iOSREbooter_FILES = main.m RBAppDelegate.m RBRootViewController.m
iOSREbooter_FRAMEWORKS = UIKit CoreGraphics

include $(THEOS_MAKE_PATH)/application.mk

after-stage::
	$(ECHO_NOTHING)chmod +s $(THEOS_STAGING_DIR)/Applications/iOSREbooter.app/iOSREbooter$(ECHO_END)

after-install::
	install.exec "su mobile -c uicache"
	install.exec "killall \"iOSREbooter\"" || true

Compile and relaunch the app, you’ll find it crash at start. Why? The reason is that backboardd is running as mobile, it can’t launch a root process, so iOSREbooter got killed. How do we deal with this situation?

If you have ever looked close at Cydia.app or iFile.app, you’ll know they run as root. What they do is making backboardd run a bash script, and the bash script launches the real root App, which bypasses iOS’ check. A simple bash script would be like this:

#!/bin/bash
root=$(dirname "$0")
exec "${root}"/iOSREbooter

The only thing you need to do for customization is changing iOSREbooter to your App’s name. Easy huh?

One last thing, we tell backboardd to run this bash script instead of our executable. You may already guessed how: Change the value of key “CFBundleExecutable” in Info.plist to our bash script, like (“bash” is our bash script):

CFBundleExecutable = "bash";

The ultimate project tree looks like this:

Compile and run. Boom! Our iPhone reboots when you hit the + button.

Part III Conclusion

User identifications are rather complicated on iOS/OSX. I strongly suggest you read some documents about uids before making your App run as root, or it could cause unexpected problems. Have fun, but the risk is on your own.

References:

http://en.wikipedia.org/wiki/User_identifier#Real_user_ID

http://www.lst.de/~okir/blackhats/node23.html

http://docs.oracle.com/cd/E19683-01/816-4883/secfile-69/index.html

12 个赞

希望有人汉化下

1 个赞

求教:修改makefile这一步怎么做?
我的工程是在 xcode 下建立的; 不能像您这样找到 makefile。

另外,如果在xcode中操作的话,按照您的文章,我的理解是:
1.修改 main();setuid(0);
2.修改info.plist : bash
3.增加 bash;
4.build for profiling.

不知道对不对呢?

大概就是这么几步:

  1. App内调用setuid(0);
  2. 给App的可执行程序赋予setuid权限;
  3. 把Info.plist的可执行文件换成一个bash script,然后用bash script启动这个App的可执行文件
2 个赞

非常感谢~~“
我在这个过程中,遇到了的困难主要就是:(已解决)“app 闪退”。 正如您所讲,应该是权限问题。
我参考了stackoverflow : http://stackoverflow.com/questions/7841344/gaining-root-permissions-on-ios-for-nsfilemanager-jailbreak/8796556#8796556
希望能够帮到遇到同样困惑的同学。

闪退的原因一般是因为SpringBoard无法启动具有root权限的App,因为SpringBoard仅有mobile权限。所以借助bash script可以绕开这个限制

我修改了文件权限 bash为0775 可执行文件为 6775 重启后 可以打开App 但是运行reboot的时候 仍旧提示没有权限问题 501, 501, 256

我哪里出错了么

501是mobile权限,说明你提权失败了。你看看可执行文件的owner和group是不是root:wheel

我按照楼主写的这个 rootapp 安装后,通过cydia 卸载。 重启手机后,不知道为啥: /Applications 目录全部被删掉了~~,所有的系统应用都没有了。但是之前安装游戏都还在。估计因为不是通过cydia安装的原因。
我不确定是什么原因导致的,在此一提有两个目的:
1.最重要的! 跪求恢复方法~~ 现在手机可以接电话,和短信,但是找不到 电话,信息,邮件,设置等等系统应用。。
2.不知道我哪里做错了,求高手建议,以防其他同学重蹈覆辙~~

当然了,楼主的demo 肯定没问题的,其他同学参考时候,不需要担心code 有问题~~

1 个赞

还有这事?恢复的话,我猜你把ipsw里的/Applications/提取一份出来复制过去就可以了吧!

不知道你解决了没?
我是通过 mobileterminal 修改成功的。
如果直接在mac上修改,然后在copy到手机里好像有问题~

我做了一个测试,当然了,前提是: 1.在xcode下写的测试代码
2.并且没有按照楼主所说的修改 makefile 文件(chmod )。。。
结果:
bash 和 rootapp必须要在 ios 中的 terminal 中修改权限,才可以启动app。
那么这就有一个问题:
写好的app安装后,不可能让用户去点击修改权限~~,应该有什么脚本之类的修改权限,(我对脚本不了解,汗~),不知道bash中是否可以增加修改属性的代码呢?不过,好像也有问题,即使bash可以修改另外一个执行文件的权限为:chmod 6775 rootapp。 可是谁来修改bash的权限呢?》好像两个的权限都必须改。

xcode里面找找看,有没有类似于debian的postinst脚本,我印象是有的,你搜搜看

1 个赞

谢谢楼主的热心啊

根据版主的提示,自己再补充一点小技巧,希望可以帮助到后面的朋友。

  1. bash文件,必须由一个执行文件修改而来,具体方法可以是 复制一份RootApp 使用Hex Fiend 打开可执行文件,删干净里面的东西
    然后粘贴
#!/bin/bash
root=$(dirname "$0")
exec "${root}"/RootApp

2.打包好的DEB安装进了/Applications/下面,实际上 里面的文件并不具备 root:wheel 这两个属性,你点开,还是会闪退
解决方案:用iFile打开你的app,修改里面所有文件为以上两个属性。

chmod +x xxx

就是干这个的;
2. 帖子里其实已经提到了,

真是不好意思哦,英语水平有限,遗漏了很多内容,自己的方法真是献丑啊。

补充一下:
需要将文件RootApp指定成root组用户的文件,否则即使Info.plist直接运行RootApp,程序也是不会崩溃的,只会看到setuid(0)返回-1
如果RootApp成为了root组用户的文件,就会在此时看到下面的crash原因:
backboardd[66] : Unable to obtain a task name port right for pid 416: (os/kern) failure (5)

给Makefile的after-stage加入一行chown完成文件所属设置。(这之后需要使用root用户来make && make package && make install)

after-stage::
$(ECHO_NOTHING)chmod +s $(THEOS_STAGING_DIR)/Applications/RootApp.app/RootApp$(ECHO_END)
$(ECHO_NOTHING)chown root:wheel $(THEOS_STAGING_DIR)/Applications/RootApp.app/RootApp$(ECHO_END)

Note:
Info.plist和bash脚本不需要成为root组文件
-rw-r–r-- 1 mobile staff 423 Oct 15 02:00 Info.plist
-rwsr-sr-x 1 root wheel 14112 Oct 15 01:57 RootApp*
-rwxr-xr-x 1 mobile staff 55 Oct 14 01:32 rootstrap.sh*

总结一下,简单说就需要4个条件:

  1. mach-o文件具有s标记,
  2. mach-o文件属于root组,
  3. mach-o文件中调用setuid(0)
  4. 修改Info.plist 让backboardd先调用bash脚本,在脚本里再启动mach-o文件

帖子里其实已经提到了,

嗯,没注意。