Shizuku 源码解读 (自动解锁手机抢购飞天茅台)解决遗留部分问题

每天定时抢购飞天茅台

原视频链接

背景

Shizuku 源码解读上文

  • 上次预留两个问题

      - 1. 新进程中的context 如何构建创造
    
      - 2. hidden api的实现原理
    

问题一 新进程中的context 如何构建创造

  • 反射构造客户端Service,构造使用context 原代码

    • public class UserService {
      
        
          @Nullable
          public static Pair<IBinder, String> create(String[] args) {
            
              Log.i(TAG, String.format("starting service %s/%s...", pkg, cls));
      
              IBinder service;
      
              try {
                  Context systemContext = ActivityThread.systemMain().getSystemContext();
      
                  DdmHandleAppName.setAppName(name != null ? name : pkg + ":user_service", 0);
      
                  //noinspection InstantiationOfUtilityClass
                  UserHandle userHandle = Refine.unsafeCast(
                          Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
                                  ? UserHandleHidden.of(userId)
                                  : new UserHandleHidden(userId));
                  Context context = Refine.<ContextHidden>unsafeCast(systemContext).createPackageContextAsUser(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY, userHandle);
                  ClassLoader classLoader = context.getClassLoader();
                  Class<?> serviceClass = classLoader.loadClass(cls);
                  Constructor<?> constructorWithContext = null;
                  try {
                      constructorWithContext = serviceClass.getConstructor(Context.class);
                  } catch (NoSuchMethodException | SecurityException ignored) {
                  }
                  if (constructorWithContext != null) {
                      service = (IBinder) constructorWithContext.newInstance(context);
                  } else {
                      service = (IBinder) serviceClass.newInstance();
                  }
              } catch (Throwable tr) {
                  Log.w(TAG, String.format("unable to start service %s/%s...", pkg, cls), tr);
                  return null;
              }
      
              return new Pair<>(service, token);
          }
      }
      
  • Context systemContext = ActivityThread.systemMain().getSystemContext(); ActivityThread 作为隐藏API,为什么项目中可以使用到?

    • 自定义ActivityThread,包名类名和系统相同

      •   package android.app;
          
          public class ActivityThread {
          
              public static ActivityThread systemMain() {
                  throw new RuntimeException();
              }
          
              public static ActivityThread currentActivityThread() {
                  throw new RuntimeException();
              }
          
              public Application getApplication() {
                  throw new RuntimeException();
              }
          
              public ContextImpl getSystemContext() {
                  throw new RuntimeException();
              }
          }
        
    • 调用自定义的ActivityThread,这样运行不就抛出异常吗?为什么会顺利执行

    • 双亲委派机制

      •  private final ClassLoader parent;
         protected Class<?> loadClass(String name, boolean resolve)
        	        throws ClassNotFoundException {
        	            // First, check if the class has already been loaded
        	            Class<?> c = findLoadedClass(name);
        	            if (c == null) {
        	                try {
        	                    if (parent != null) {
        	                        c = parent.loadClass(name, false);
        	                    } else {
        	                        c = findBootstrapClassOrNull(name);
        	                    }
        	                } catch (ClassNotFoundException e) {
        	                    // ClassNotFoundException thrown if class not found
        	                    // from the non-null parent class loader
        	                }
        	                if (c == null) {
        	                    // If still not found, then invoke findClass in order
        	                    // to find the class.
        	                    c = findClass(name);
        	                }
        	            }
        	            return c;
        }
        
        1. findLoadedClass(name) 从缓存中获取Class对象,如果获取不到,就让parent.loadClass(name)找。
        • android 调用native方法从 class_table(哈希map)的缓存中查找

        • java中可能java层和native层都有缓存,Android只有native有缓存

        1. 递归此过程。直到顶层为BootClassLoader类型时,如果在缓存中无法获取到需要的class对象,就调用BootClassLoader.findClass(name)。
        1. 最后,如果parent获取不到class对象,就调用自己的findClass(name)寻找。
        • DexPathList -> dexElements这个数组就是一个Dex文件集合,而findClass就是遍历这个集合,然后从Dex文件中获取需要的字节码文件,然后使用字节码文件创建对应的Class对象,并将其缓存到class_table中。

        1. 如果findClass()方法仍然无法加载该类,那么ClassLoader会抛出ClassNotFoundException异常。
    • 根据双亲委派机制,loadClass时返回BootClassCloder中已经加载的系统ActivityThread类,那我们自己实验一下,

      • apk类加载器的父类是否是BootClassCloder?

        • ClassLoader classLoader = MainActivity.class.getClassLoader();
          while (classLoader != null) {
              Log.d("gzp", classLoader.getClass().getCanonicalName());
              classLoader = classLoader.getParent();
          }
          
          在这里插入图片描述
      • 自己替换一下系统类,demo验证一下,

        • package android.os;
          
          import android.util.Log;
          
          public class ServiceManager {
          
              public static IBinder getService(String name) {
                  Log.d("gzp", "custom getService");
                  throw new RuntimeException("STUB");
              }
          
              public static void addService(String name, IBinder service) {
                  Log.d("gzp", "custom addService");
                  throw new RuntimeException("STUB");
              }
          
          }
          
          public class MainActivity extends HomeActivity {
              @Override
              protected void onCreate(@Nullable Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
               
                  IBinder iBinder = ServiceManager.getService("activity");
                  Log.d("gzp", "custom" + iBinder);
              }
          }
          
          
          在这里插入图片描述
          果然没有输出custom getService日志,并且返回的是一个对象
  • 是不是所有的类都可以自定义替换呢

    • 当然不是,如果替换View类,你会发现编译报错;android jar 包含的类替换有可能编译失败;导入类时,优先导入android jar的类

问题二 hidden api的实现原理

  • hidden api的实现原理 Context context = Refine.<ContextHidden>unsafeCast(systemContext).createPackageContextAsUser();

  • HiddenApiRefinePlugin 查看源码

    • gradle 插件,在编译(compileDebugJavaWithJavac/compileRelaseJavaWithJavac)之后 将android.content.ContextHidden重新命名为 android.content.Context

      •  class RefineRemapper extends Remapper {
            
         
             @Override
             public String map(final String typeName) {
                 final ClassData data = context.loadClassData(typeName.replace('/', '.') + "$" + RefineProcessor.REFINE_METADATA_CLASS_NAME);
                 if (data == null) {
                     return typeName;
                 }
         
                 if (data.getClassAnnotations().contains(RefineProcessor.Descriptor.class.getName())) {
                     final String to = data.getClassAnnotations().stream()
                             .filter(a -> a.startsWith(RefineProcessor.REFINE_NS_PACKAGE))
                             .findFirst()
                             .orElseThrow(() -> new UnsupportedOperationException("Use deprecated refine class " + data.getClassName()));
         
                     return to.substring(RefineProcessor.REFINE_NS_PACKAGE.length() + 1).replace('.', '/');
                 }
         
                 return typeName;
             }
         
         }
        
    • 过程:

        1. 编译时调用自定义ContextHidden.createPackageContextAsUser 保证编译通过,不会因为类找不到或者方法找不到而抛出编译异常;
        1. 编译之后,重新命名(android.content.ContextHidden重新命名为 android.content.Context)
        1. 加载类时,根据双亲委派机制,findClass时,返回BootClassCloder中的系统android.content.Context
        1. 运行调用系统的android.content.Context
<think>好的,我现在需要帮助用户了解如何通过Shizuku连接电脑调试,使得手机的USB共享网络自动开启。首先,我得回忆一下Shizuku的作用和原理。Shizuku是一个帮助用户在Android设备上通过ADB授权运行特权命令的工具,它可以让普通应用以ADB权限执行一些操作,比如系统级别的设置。 用户的需求是让手机在连接电脑调试时自动开启USB网络共享。通常,手动开启USB网络共享需要在连接USB后进入设置,找到网络共享选项并手动打开。用户希望自动化这一过程,可能涉及到检测USB连接状态,并自动触发相应的设置开关。 接下来,我需要思考如何通过Shizuku来实现这一点。Shizuku本身并不直接提供自动化脚本的功能,但它可以授权其他应用执行ADB命令或系统级API。因此,用户可能需要结合其他工具,比如Tasker或自动化脚本应用,配合Shizuku的权限来实现自动开启。 首先,用户需要在手机上安装Shizuku,并通过USB调试或无线调试激活它。激活Shizuku通常需要通过ADB命令,比如`adb shell sh /sdcard/Android/data/moe.shizuku.privileged.api/start.sh`,具体步骤可能因手机型号和Android版本而异。 然后,用户需要找到控制USB网络共享的ADB命令或系统设置。通常,可以通过`svc usb setFunctions`命令来管理USB功能,但不同设备可能有不同的实现。例如,启用USB网络共享可能需要执行类似`svc usb setFunctions rndis`的命令。不过,这可能因设备而异,需要用户查询自己设备的特定命令或设置项。 另外,Android系统设置中的某些选项可以通过`settings`命令来修改。例如,USB网络共享对应的设置项可能是`tether_dun_required`或其他类似名称。用户可能需要使用Shizuku授权的权限来执行这些命令,例如通过一个脚本或Tasker任务,在检测到USB连接时自动执行相应的ADB命令。 接下来,用户需要设置自动化工具,如Tasker,来检测USB连接事件。当手机连接到电脑时,Tasker可以触发一个任务,该任务使用Shizuku的权限运行必要的命令来启用USB网络共享。这可能需要使用插件如“Shizuku Runner”或者通过ADB Shell命令直接调用。 需要注意的是,不同手机厂商可能对USB网络共享的实现有所不同,有些可能需要额外的步骤或Root权限。如果Shizuku无法直接通过ADB权限修改相关设置,用户可能需要Root手机,或者寻找其他变通方法。 此外,用户还需要确保USB调试模式已启用,并且在连接电脑时授权了正确的权限。如果电脑之前未配对,可能需要先完成ADB设备的配对流程。 最后,测试整个流程是否正常工作。用户连接手机到电脑,Shizuku应已激活,Tasker检测到USB连接后触发脚本,执行对应的命令开启USB网络共享。如果遇到问题,可能需要检查命令的正确性,或者查看Logcat日志来调试错误。 总结步骤大致如下:安装并激活Shizuku,确定开启USB网络共享的正确命令,设置自动化工具检测USB连接,使用Shizuku权限执行命令,测试并调试。过程中可能会遇到设备兼容性问题,需要用户根据具体情况调整。</think>以下是通过Shizuku实现连接电脑时自动开启USB网络共享的分步指南: --- ### **步骤1:准备条件** 1. **安装Shizuku** - 从 [Google Play](https://play.google.com/store/apps/details?id=moe.shizuku.privileged.api) 或 [GitHub](https://github.com/RikkaApps/Shizuku) 下载安装Shizuku。 - 启用手机**开发者选项**(设置 → 关于手机 → 多次点击“版本号”)。 - 在开发者选项中开启**USB调试**。 2. **激活Shizuku** - 通过USB连接电脑,执行以下命令: ```bash adb shell sh /sdcard/Android/data/moe.shizuku.privileged.api/files/start.sh ``` - 若无法连接电脑,可选择**无线调试**模式(需Shizuku v12+)。 --- ### **步骤2:获取USB网络共享的命令** 不同设备命令可能不同,需自行测试: 1. **通过ADB命令尝试** - 手动启用USB网络共享后,执行以下命令查看状态: ```bash adb shell settings get global tether_dun_required ``` - 修改设置的命令可能类似: ```bash adb shell svc usb setFunctions rndis ``` 2. **查找设备专用命令** - 部分厂商需特定命令,例如华为: ```bash adb shell am start -n com.android.settings/.TetherSettings ``` --- ### **步骤3:自动化工具配置(以Tasker为例)** 1. **安装Tasker** 从Play商店安装 [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm)。 2. **创建USB连接触发事件** - 新建任务 → 事件 → **系统** → **Intent Received** - 填写Intent:`android.hardware.usb.action.USB_STATE` 3. **添加执行命令的Action** - 安装插件 [**Shizuku Runner**](https://github.com/RikkaApps/Shizuku-API) - 在Tasker中添加Action → **Plugin** → **Shizuku Runner** - 输入需要执行的命令(如`svc usb setFunctions rndis`) --- ### **步骤4:测试与调试** 1. 连接手机到电脑,观察是否自动开启USB网络共享。 2. 若失败,检查以下可能原因: - Shizuku权限未正确授予。 - 命令不适用于当前设备(需Root或厂商适配)。 - Tasker未在后台保持运行。 --- ### **常见问题** 1. **需要Root吗?** 部分设备可能需Root才能修改系统级设置,Shizuku本身无需Root但依赖ADB授权。 2. **命令执行失败** 尝试使用完整路径调用命令: ```bash /system/bin/svc usb setFunctions rndis ``` 3. **如何验证命令有效性?** 手动在ADB Shell中执行,确认是否生效。 --- 通过上述步骤,可实现手机连接电脑时自动开启USB网络共享。具体命令需根据设备型号调整,建议先在ADB中手动验证命令可行性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值