Shizuku做什么的?
Shizuku可以做到让App拥有Shell权限,不用root,来调用系统的服务,但是需要借助adb命令来执行一个脚本,并且手机不能重启。
如何启动Shizuku
https://shizuku.rikka.app/zh-hans/guide/setup.html
android 7.0+
adb shell sh /data/user_de/0/moe.shizuku.privileged.api/start.sh
Shizuku原理综述
Shizuku提供了一个权限管理器App供安装,这个App中可以控制授权哪些应用可以使用Shizuku的提权操作,需要提权的App会在AndroidManifest中声明使用Shuzuku定义的一个自定义权限。
在这个管理器App的主Activity启动时,会把一个shell脚本、一个so文件、一个dex文件从Apk资源目录拷贝到系统目录中,用户通过adb shell命令执行指定系统目录下的shell脚本,shell脚本执行so文件,so文件中会fork一个进程,因为是从shell进程fork出来的,所以fork出的子进程具有shell权限,fork出子进程后,通过app_process执行dex文件中的一个启动类,app_process是Android系统里内置的一个程序,作用就是运行一个新的Java程序,Zygote进程的启动也是用到app_process来运行的。
这个dex文件中的启动类中会创建一个Looper对象并执行loop()方法,以保证程序一直运行,作为一个服务端常驻进程。
服务端会监听App的启动,每个App启动后,会查看这个启动的App有没有申请Shizuku自定义的权限,有这个权限说明这个App需要提权。
接着服务端程序会主动调用App里的一个ContentProvider的call方法,把服务端的一个Binder对象发送给App,这个Binder对象就是用来提权用的。
然后App中就可以使用Shizuku封装的api来做需要提权的操作,也就是获取各种系统服务并调用系统服务的方法,比如静默安装apk。
具体是怎么做到提权的呢?
如果直接在普通App进程里去调用系统服务的方法,权限还是App的权限,没有权限调用系统服务的方法;需要在服务端程序中调用系统服务才行,因为服务端程序有Shell权限。所以就需要把App进程中的系统服务调用转发给服务端程序,由服务端程序去执行系统服务调用,再把结果返回给App进程。这就用到了服务端程序发送给App的Binder对象了,用这个Binder对象对系统服务的Binder对象做一层代理,对请求和响应都做转发,App这一层调用无感知。
这里调用系统服务的方法利用的是类加载的双亲委派机制,在App模块里定义framework层的类并做空实现,然后App代码就可以直接使用,framework类是由BootClassLoader加载的,所以最后调用到的方法并不是空实现的方法,而是系统的framework.jar里的类方法,在Zygote进程启动时就预加载了。系统调用一个类的时候,会去找通过BootClassLoader找到framework中的类。
Shizuk怎么实现的?
shizuku manager 分为client和server。
server是一个纯java项目,被打包成dex, 作为资源文件被打包到shizuku manager App中, server负责响应执行一些提权的命令。app安装运行后, server和libshizuku.so会被释放到sdcard上,用户使用adb 执行shell命令后,由于adb 用户具有比普通用户更高的权限, libshizuku.so会执行提权操作并启动server。
server端和client直接通信采用Android binder方式进行,实现是contentProvider,server启动后会监听应用的启动,每个app启动后,server端会主动调取对应app的moe.shizuku.api.ShizukuBinderReceiveProvider这个contentProvider,如果有权限就主动调用call方法发送binder到对方app。
app获取到这个binder后就可以根据api提供的方法调用shizuku manager提供的接口达到权限提升的目的。
server是如何启动的?
Shizuku Manager运行后,在HomeActivity的onCreate()会执行moe.shizuku.manager.starter.Starter.writeFilesAsync(this),把server.dex、libshizuku.so还有start.sh脚本写入sdcard上。
用户通过adb shell命令执行start.sh脚本,脚本中会执行,libshizuku.so中会fork()一个进程出来,然后通过/system/bin/app_process启动指定的类,也就是moe.shizuku.server.Starter类,执行Starter类的main方法,Starter.main()中执行了ShizukuService.main()方法。
app_process可以启动独立的Java程序,需要dex文件。
为什么要用app_process启动server?不用app_process不行吗?
start.sh是通过adb shell启动的,start.sh
中再执行libshizuku.so
,libshizuku.so中fork出的进程是从/system/bin/sh中fork出来,再从fork出的进程执行app_process,通过app_process启动server程序拥有shell权限,普通的app与server跨进程通信,可以完成高级权限的事。
- 利用adb shell启动的Java进程,这种情况下Java进程拥有shell级别的权限,所以Java程序对应的PID和Shell的PID一样。
- 利用app启动的Java进程,这种情况下Java进程跟当前app的权限一样,没有Shell权限。
daemon()函数是干什么的?在shizuku里起到了什么作用?
为了让app_process运行的server程序运行在后台
如果想要在后台运行,可以用系统的 nohup 命令 (Android 6.0 把 toolbox 换成 toybox 之后才开始自带这个) ,别忘了重定向 stdout 和 stderr ,并使用 –nice-name= 取个好听的进程名,以便在需要的时候能够使用 killall 结束它。
CLASSPATH=$(echo /data/app/com.package.name-*/base.apk) \
nohup app_process /system/bin –nice-name=process_name com.package.name.Main > /dev/null 2>&1 &
当然你也可以自己写一个 C 程序,在调用 daemon() 函数后启动 app_process 进程。
server干了什么?
执行流程:
libshizuku.so的start.cpp的main() -> start_server() -> moe.shizuku.server.Starter.main() -> moe.shizuku.server.ShizukuService.main() -> new ShizukuService()
ShizukuService是Binder对象,ShizukuService的构造函数里调用了BinderSender.register(this),监听所有进程,监听到Shizuku Manager进程启动和Shizuku客户端进程启动,就把ShizukuService对象发送给它们。
ShizukuManager进程判断的依据是进程有没有请求使用moe.shizuku.manager.permission.MANAGER权限
Shizuku客户端进程的判断依据是进程有没有请求使用moe.shizuku.manager.permission.API_V23权限
发送ShizukuService的Binder对象给目标进程,最终都会调用ShizukuService.sendBinderToUserApp(),再通过ActivityManagerService.getContentProviderExternal()获取目标进程中的ContentProvider,然后调用ContentProvider的call方法把ShizukuService传递给目标进程。
ShizukuServer的main方法为什么要执行Looper.prepare()和Looper.loop()?
保证当前线程不结束,如果没有消息就会阻塞线程。
如何调用系统隐藏的API?
1.通过反射调用
当应用在设备上运行时,它会加载 /system/framework/framework.jar ,framework.jar 和 android.jar的唯一的区别就是它没有移除 internal API 和 hidden API,这就说明了为什么我们可以通过反射调用,因为我们开发的SDK中不包含这些API,所以我们无法进行显式的调用,当我们利用反射,程序在设备上运行的时候,其实是可以找到对应的方法进行调用的。
2.使用Android内部资源
https://github.com/anggrayudi/android-hidden-api
Adb shell提权还有哪些应用?
在未经root的Android手机上使用adb logcat查看日志
https://github.com/Zane96/Fairy/blob/master/readme-zh.md
Sample里的addUserService(standalone process)是干什么的?
addUserService是ShizukuService的一个方法,自定义的UserService是一个IBinder对象。
ShizukuService是通过adb shell用app_process启动的,位于单独的进程中,拥有adb shell权限。
ShizukuService.addUserService()最终会调用ShizukuService.startUserServiceNewProcess(),内部会调用app_process启动ServiceStarter类的main方法,main中会创建自定义的UserService类,这个进程也是拥有adb shell权限的,相当于启动了一个自定义的系统服务。
main中再调用sendBinder把自定义的UserService传递给ShizukuManagerProvider的call方法的sendUserService逻辑分支处理,这里做了两件事:
第一件事是ShizukuManagerProvider中把自定义的UserService通过ShizukuService.sendUserService()传递给ShikuzuService所在进程,并保存在ShizukuService的userServiceRecords里。
第二件事是ShizukuManagerProvider把ShizukuService这个IBinder对象返回给ServiceStarter,当ShuzikuService死亡时(通过linkToDeath监听)自定义的UserService的进程也随机终止。
app_process用法
app_process 是 Android 上的一个原生程序,是 APP 进程的主入口点。总之就是个可以让虚拟机从 main() 方法开始执行一个 Java 程序的东西啦。
参数和用法
这个命令没有帮助程序,还好 源代码 里说得很清楚
Usage: app_process [java-options] cmd-dir start-class-name [options]
对这些参数的详细介绍也在 源代码 里,基本上是这样
java-options - 传递给 JVM 的参数
cmd-dir - 暂时没有用,学着 init.rc.* 里给传个 /system/bin 就好
start-class-name - 程序入口, main() 方法所在的类名
options - 可以是下面这些
–zygote 启动 zygote 进程用的
–start-system-server 启动系统服务(也是启动 zygote 进程的时候用的)
–application 启动应用程序
–nice-name=启动之后的进程名称
根据源代码还能得出一些关于这些参数的更加详细的结论。
- 根据 这里,传入 –zygote 会启动 com.android.internal.os.ZygoteInit,否则启动 com.android.internal.os.RuntimeInit。
- 可以发现 –start-system-server 只在启动 zygote 时有效。
- 在非 zygote 模式中,有无 –application 的选项的区别只是 是否将 stdout 和 stderr 重定向到 AndroidPrintStream 。
- 也只有在 非 zygote 的情况下,–nice-name= 选项有效。
与 Java 相似, Android 支持在环境变量 CLASSPATH 中指定类搜索路径 (CLASSPATH),此外还可以在虚拟机参数中指定 -Djava.class.path= 。但是, Android 使用 ART 环境运行 Java ,传统的 Java 字节码文件(.class) 是不能直接运行的,app_process 支持在 CLASSPATH 中指定 dex 或 apk 文件。
使用 dex
CLASSPATH=/data/local/tmp/test.dex app_process /system/bin moe.haruue.Test
app_process -Djava.class.path=/data/local/tmp/test.dex /system/bin moe.haruue.Test
使用 apk
CLASSPATH=/data/app/moe.haruue.test-1/base.apk app_process /system/bin moe.haruue.Test
app_process -Djava.class.path=/data/app/moe.haruue.test-1/base.apk /system/bin moe.haruue.Test