예제 #1
0
 private static File ensureExists(final String filePath) {
   File f = new File(filePath);
   if (!f.exists()) {
     Console.die(f.getPath() + " does not exist.");
   }
   return f;
 }
예제 #2
0
 public static void enforceOverwrite(final String filePath, final String whatIsIt) {
   if (!AllowOverwrites) {
     if (new File(filePath).exists()) {
       Console.die(
           "The "
               + whatIsIt
               + " "
               + filePath
               + " already exists.\n"
               + "Delete and try again or use --force option.");
     }
   }
 }
예제 #3
0
  // returns false if options are not valid
  public static void parseArgs(String[] args) {
    // https://www.karlin.mff.cuni.cz/network/prirucky/javatut/java/cmdLineArgs/parsing.html
    String arg;
    int argPos = 0;

    while ((argPos < args.length) && args[argPos].startsWith("-")) {
      arg = args[argPos++];

      if (arg.startsWith("-v") || arg.startsWith("--verbose")) {
        String level = arg.startsWith("-v") ? arg.replace("-v", "") : arg.replace("--verbose", "");
        VerboseLevel = level.isEmpty() ? 1 : Integer.parseInt(level);
        if (VerboseLevel > MaxVerboseLevel) {
          VerboseLevel = MaxVerboseLevel;
        } else if (VerboseLevel < 1) {
          VerboseLevel = 1;
        }

        Console.VerboseLevel = VerboseLevel;
        Console.debug("Option: Verbose level set to " + VerboseLevel);
      } else if (arg.equals("-s") || arg.equals("--skip-assembly")) {
        Console.debug("Option: Skipping assembly.");
        SkipAssembly = true;
      } else if (arg.equals("--skip-cleanup")) {
        Console.debug("Option: Skipping cleanup.");
        SkipCleanup = true;
      } else if (arg.equals("--decode-res")) {
        Console.debug("Option: Decoding resources.");
        DecodeResources = true;
      } else if (arg.equals("-d") || arg.equals("--detect-only")) {
        Console.debug("Option: Determining protection information only.");
        DetectOnly = true;
      } else if (arg.equals("-f") || arg.equals("--force")) {
        Console.debug("Option: Allowing file overwrites.");
        AllowOverwrites = true;
      } else if (arg.equals("--sign-only")) {
        Console.debug("Option: Signing only.");
        SignOnly = true;
        AllowOverwrites = true; // disable any checking, wont be needed
        SkipCleanup = true;
      } else if (arg.equals("--sign-key")) {
        arg = args[argPos++];
        SignKey = new File(arg);
        if (!SignKey.exists()) {
          Console.die("Signing key does not exist: " + arg);
        }
      } else if (arg.equals("--sign-cert")) {
        arg = args[argPos++];
        SignCert = new File(arg);
        if (!SignCert.exists()) {
          Console.die("Signing certificate does not exist: " + arg);
        }
      } else if (arg.equals("--sign-pass")) {
        SignPass = args[argPos++];
      } else if (arg.equals("--info-only")) {
        Console.debug("Option: Getting info only.");
        InfoOnly = true;
        AllowOverwrites = true; // disable any checking, wont be needed
      } else if (arg.equals("--assemble-only")) {
        Console.debug("Option: Assembling only.");
        AssembleOnly = true;
      } else if (arg.equals("--fplist")) {
        ListFPsOnly = true;
      } else if (arg.equals("--fpexclude")) {
        arg = args[argPos++];
        String fps[] = arg.split(",");

        for (String fp : fps) {
          ExcludedFPs.add(fp.trim());
        }

        Console.debug("Option: Excluding fingerprints: " + arg);
      } else if (arg.equals("--fpinclude")) {
        arg = args[argPos++];
        String fps[] = arg.split(",");

        for (String fp : fps) {
          IncludedFPs.add(fp.trim());
        }

        Console.debug("Option: Including fingerprints: " + arg);
      } else if (arg.equals("--chksigs")) {
        arg = args[argPos++];
        CheckSigsBehavior = Integer.parseInt(arg);
      } else if (arg.equals("--getpi")) {
        arg = args[argPos++];
        GetPIBehavior = Integer.parseInt(arg);
      } else if (arg.equals("--sigvfy")) {
        arg = args[argPos++];
        SigVerifyBehavior = Integer.parseInt(arg);
      } else if (arg.equals("--spoof-id")) {
        arg = args[argPos++];
        DeviceIDSpoofType = Integer.parseInt(arg);

        // These are enabled=false by default
        IncludedFPs.add("Hook Get Secure Setting");
        IncludedFPs.add("Hook Device ID");

        if (DeviceIDSpoofType == 5) {
          DeviceIDSpoof = args[argPos++];

          // device id must be 15 characters, numeric only
          if (!DeviceIDSpoof.matches("\\d{15}")) {
            Console.die("Spoofed device ID is must be 15 digits.", -1);
          }
        }
      } else if (arg.equals("--spoof-account")) {
        arg = args[argPos++];
        AccountNameSpoofType = Integer.parseInt(arg);

        // These are enabled=false by default
        IncludedFPs.add("Hook Get Account Name");

        if (AccountNameSpoofType == 3) {
          AccountNameSpoof = args[argPos++].toUpperCase();

          if (!AccountNameSpoof.matches("[a-zA-Z0-9\\.]+")) {
            Console.die("Spoofed Account Name must be alpha-numeric!", -1);
          }
        }
      } else if (arg.equals("--spoof-wifimac")) {
        arg = args[argPos++];
        WifiMacSpoofType = Integer.parseInt(arg);

        // These are enabled=false by default
        IncludedFPs.add("Hook Get Wifi Mac");

        if (WifiMacSpoofType == 3) {
          WifiMacSpoof = args[argPos++].toUpperCase();

          if (!WifiMacSpoof.matches("(?m)^([0-9A-F]{2}([:-]|$)){6}$")) {
            Console.die("Spoofed Wifi MAC must be of the form 11:22:33:AA:BB:CC !", -1);
          }
        }
      } else if (arg.equals("--spoof-btmac")) {
        arg = args[argPos++];
        BTMacSpoofType = Integer.parseInt(arg);

        // These are enabled=false by default
        IncludedFPs.add("Hook Bluetooth MAC");

        if (BTMacSpoofType == 3) {
          BTMacSpoof = args[argPos++].toUpperCase();

          if (!BTMacSpoof.matches("(?m)^([0-9A-F]{2}([:-]|$)){6}$")) {
            Console.die("Spoofed BT MAC must be of the form 11:22:33:AA:BB:CC !", -1);
          }
        }
      } else if (arg.equals("--spoof-model")) {
        arg = args[argPos++];

        // This is enabled=false by default
        IncludedFPs.add("Hook Device Model");

        Options.DeviceModelSpoof = arg;
        Console.debug("Spoofing model as " + Options.DeviceModelSpoof);
      } else if (arg.equals("--spoof-network")) {
        arg = args[argPos++];

        // This is enabled=false by default
        IncludedFPs.add("Hook Network Operator");

        Options.NetworkOperatorSpoof = arg;
        Console.debug("Spoofing network operator as " + Options.NetworkOperatorSpoof);
      } else if (arg.equals("--spoof-manufacturer")) {
        arg = args[argPos++];

        // This is enabled=false by default
        IncludedFPs.add("Hook Device Manufacturer");

        Options.DeviceManufacturerSpoof = arg;
        Console.debug("Spoofing manufacturer as " + Options.DeviceManufacturerSpoof);
      } else if (arg.equals("--key-apk")) {
        KeyApkPath = args[argPos++];
        File f = new File(KeyApkPath);
        if (!f.exists()) {
          Console.die("The key apk " + KeyApkPath + " does not exist!");
        }
      } else if (arg.equals("--trace")) {
        Console.debug("Option: Enabling method trace and debug hooks.");
        IncludedFPs.add("Method Trace");
        IncludedFPs.add("Method Trace FixLocals");
        DebugHooks = true;
      } else if (arg.equals("--translate")) {
        Console.debug("Option: Enabling Smali string language translation.");
        SmaliHinter.enableTranslations();
      } else if (arg.equals("-h") || arg.equals("--help")) {
        showUsage();
        System.exit(0);
      } else if (arg.equals("--dbghooks")) {
        Console.debug("Option: Using debugging hooks.");
        DebugHooks = true;
      } else if (arg.equals("--skip-hints")) {
        Console.debug("Option: Skipping smali hint generation.");
        SkipHints = true;
      } else {
        Console.die("Unknown option: " + arg + ".", -1);
      }
    }

    // No bother processing the rest of the logic, we just want FP listing
    if (ListFPsOnly) {
      return;
    }

    // If either is alone, no bueno!
    if ((SignKey != null) ^ (SignKey != null)) {
      Console.die("Options --sign-key and --sign-cert must be used together.");
    }

    if (argPos == args.length) {
      Console.error("Oopsy! Dump path / Apk file missing. Please review:");
      showUsage();
      System.exit(-1);
    }

    // Make sure smali dump / apk exists
    File input = ensureExists(args[argPos]);

    if (input.isDirectory()) {
      if (SignOnly) {
        Console.die("Sign only option enabled but input is not an APK file.");
      }

      SmaliDir = input.getPath();

      if (!SkipAssembly) {
        if ((argPos + 1) >= args.length) {
          Console.die("Output APK required.");
        }

        OutputApk = args[argPos + 1];
        ensureExists(OutputApk);
      }
    } else {
      if (AssembleOnly) {
        Console.die("Option --assemble-only only works with smali dump directories.");
      }

      ApkPath = input.getPath();
      OutputApk = ApkPath.replace(".apk", "");

      if (args.length <= (argPos + 1)) {
        // overwrite original by default
        if (SignOnly || DetectOnly || SkipAssembly || InfoOnly) {
          OutputApk = ApkPath;
        } else {
          OutputApk += "_sequenced.apk";
        }
      } else {
        OutputApk = args[argPos + 1];
      }
    }

    Console.debug("Output apk: " + OutputApk);
    Console.debug("Apk path: " + ApkPath);

    if (!DetectOnly && !SkipAssembly) {
      enforceOverwrite(OutputApk, "output file");
    }
  }