天天看點

ARTistGUI主要流程分析

通過應用層分析,ARTistGUI的主要處理函數在/ArtistGui/app/src/main/java/saarland/cispa/artist/artistgui/instrumentation/InstrumentationTask.java中,代碼如下:

@Override
    public void run() {
        Log.i(TAG, "Run() compiling and starting " + mRunConfig.app_package_name);
        Log.i(TAG, "> apkPath:     " + mRunConfig.app_apk_file_path);
        Log.i(TAG, "> codeLibName: " + mRunConfig.codeLibName);
        Log.i(TAG, "> Keystore:    " + mRunConfig.keystore);

        try {
            ArtistThread.checkThreadCancellation();
            prepareReporter();

            reportProgress(, "Preparing build environment");
            String pathDex2oat = mInstrumenationStages.prepareEnvironment();

            ArtistThread.checkThreadCancellation();
            mInstrumenationStages.probePermissionAndDeleteOatFile();

            ArtistThread.checkThreadCancellation();
            reportProgress(, "Merging CodeLib");
            mInstrumenationStages.mergeCodeLib();
            ArtistThread.checkThreadCancellation();
            mInstrumenationStages.backupMergedApk();

            ArtistThread.checkThreadCancellation();
            reportProgress(, "Compiling: " + mRunConfig.app_package_name);
            mInstrumenationStages.runDex2OatCompilation(pathDex2oat);

            ArtistThread.checkThreadCancellation();
            reportProgress(, "Compilation done, setting file permissions");
            mInstrumenationStages.setOatFilePermissions();
        } catch (InstrumentationException | ArtistInterruptedException e) {
            reportResult(false);
            return;
        }
        reportResult(true);
    }
           

其中主要的函數為:

  • mInstrumenationStages.probePermissionAndDeleteOatFile();
  • mInstrumenationStages.mergeCodeLib();
  • mInstrumenationStages.backupMergedApk();
  • mInstrumenationStages.runDex2OatCompilation(pathDex2oat);
  • mInstrumenationStages.setOatFilePermissions();

這五個函數位于/ArtistGui/app/src/main/java/saarland/cispa/artist/artistgui/instrumentation/stages/InstrumentationStagesImpl.java,主要完成了對APK檔案的處理,先看probePermissionAndDeleteOatFile:

public void probePermissionAndDeleteOatFile() {
        reportProgressDetails("Probing oat file permissions: " + mRunConfig.app_oat_file_path);
        mRunConfig.oatOwner = AndroidUtils.getFileOwnerUid(mRunConfig.app_oat_file_path);
        mRunConfig.oatGroup = AndroidUtils.getFileGroupId(mRunConfig.app_oat_file_path);
        mRunConfig.oatPermissions = AndroidUtils.getFilePermissions(mRunConfig.app_oat_file_path);
        mRunConfig.stats.oatFileSizeOriginal = new File(mRunConfig.app_oat_file_path).length();
        Log.d(TAG, String.format("base.odex UID: %s GID: %s Permissions: %s Size: %s",
                mRunConfig.oatOwner,
                mRunConfig.oatGroup,
                mRunConfig.oatPermissions,
                mRunConfig.stats.oatFileSizeOriginal));

        reportProgressDetails("Deleting existing oat file: " + mRunConfig.app_oat_file_path);
        boolean success = deleteRootFile(mRunConfig.app_oat_file_path);
        if (!success) {
            Log.d(TAG, String.format("Failed to delete old base oat: %s - Continue", mRunConfig.app_oat_file_path));
        }
    }

    private boolean deleteRootFile(final String filePath) {
        final String cmd_rm_root_file = "rm " + filePath;
        return ProcessExecutor.execute(cmd_rm_root_file, true,
                ProcessExecutor.processName(mRunConfig.app_package_name, "rm_rootfile"));
    }
           

該方法主要調用cmd指令删除原來的oat檔案。

接下來看第二個函數mergeCodeLib:

public void mergeCodeLib() throws InstrumentationException {
        Log.d(TAG, "MergeCodeLib into: " + mRunConfig.app_apk_file_path);

        String pathToApkSigned;
        // deactivate injection upon user wish or if no code lib is provided
        if (mRunConfig.codeLib != null) {
            reportProgressDetails("Injecting CodeLib");
            final File appApk = new File(mRunConfig.app_apk_file_path);
            final File codeLibApk = mRunConfig.codeLib;

            setupCodeLib();

            final MergeConfig mergeConfig = new MergeConfig(mRunConfig.codeLib.getName(),
                    mRunConfig.app_apk_merged_file_path, mRunConfig.app_apk_file_path);

            Dexterous dexterous = new Dexterous(mergeConfig);
            dexterous.init(appApk, codeLibApk);
            dexterous.mergeCodeLib();
            final String pathToApk = dexterous.buildApk();
            reportProgressDetails("Resigning APK");
            Log.d(TAG, String.format("MergeCodeLib DONE (%s)", pathToApk));

            pathToApkSigned = resignApk(pathToApk);
            Log.d(TAG, String.format("MergeCodeLib Signing DONE (%s)", pathToApkSigned));

            if (pathToApkSigned.isEmpty()) {
                throw new InstrumentationException("Codelib Merge Failed");
            }
        } else {
            reportProgressDetails("Not Injecting CodeLib");
            Log.i(TAG, "Skip CodeLib Injection");
            Log.d(TAG, "MergeCodeLib SKIPPED");
        }
    }

    private void setupCodeLib() {
        if (mRunConfig.codeLibName.startsWith(ArtistUtils.CODELIB_ASSET)) {
            Log.d(TAG, "setupCodeLib() " + mRunConfig.codeLibName);
            final String assetName = mRunConfig.codeLibName.replaceFirst(ArtistUtils.CODELIB_ASSET, "");
            AndroidUtils.copyAsset(mContext, "codelib" + File.separator + assetName,
                    mRunConfig.codeLib.getAbsolutePath());
            if (!mRunConfig.codeLib.exists()) {
                Log.e(TAG, " setupCodeLib: " + mRunConfig.codeLib + " FAILED");
            } else {
                Log.d(TAG, " setupCodeLib: " + mRunConfig.codeLib + " READY");
            }
        }
    }

    private String resignApk(final String unsignedApkPath) {
        Log.d(TAG, "resignApk() " + unsignedApkPath);

        String signedApkPath;
        final ApkSigner apkSir = new ApkZipSir(mRunConfig.app_apk_merged_signed_file_path);
        try {
            signedApkPath = apkSir.signApk(mRunConfig.keystore.getAbsolutePath(), unsignedApkPath);
        } catch (final IllegalArgumentException e) {
            Log.e(TAG, "> Signing of APK Failed", e);
            signedApkPath = "";
        }
        return signedApkPath;
    }
           

其中setupCodeLib将codelib的apk檔案複制到codelib檔案夾下,為下一步做準備。

Dexterous用來将codelib的dex合并到目标apk的dex中去,并重新簽名。

接下來進行backupMergedApk:

public void backupMergedApk() {
    if (!mRunConfig.BACKUP_APK_MERGED) {
        Log.v(TAG, "Skip: backupMergedApk()");
        return;
    }
    Log.v(TAG, "backupMergedApk()");

    final File sdcard = Environment.getExternalStorageDirectory();
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
    Date date = new Date();

    final String mergedApkBackupPath = sdcard.getAbsolutePath() + File.separator
            + mRunConfig.app_package_name + "_merged_signed_" + dateFormat.format(date) + ".apk";

    reportProgressDetails("Backing up Merged APK: " + mergedApkBackupPath);

    final String cmd_backup_merged_apk = "cp " + mRunConfig.app_apk_merged_signed_file_path +
            " " + mergedApkBackupPath;

    boolean success = ProcessExecutor.execute(cmd_backup_merged_apk, true,
            ProcessExecutor.processName(mRunConfig.app_package_name, "cp_backup_merged"));

    if (success) {
        Log.d(TAG, "backupMergedApk() Success: " + mergedApkBackupPath);
    } else {
        Log.e(TAG, "backupMergedApk() Failed:  " + mergedApkBackupPath);
    }
}
           

此方法将上一步生成的apk檔案備份到指定目錄下。

接下來進行核心功能runDex2OatCompilation來編譯生成新的oat檔案:

public void runDex2OatCompilation(String pathDex2oat) throws InstrumentationException {
        final String cmd_dex2oat_compile = setupDex2oatCommand(pathDex2oat);

        Log.d(TAG, "dex2oat command:");
        Log.d(TAG, cmd_dex2oat_compile);
        Log.d(TAG, "Starting the compilation process!");
        Log.d(TAG, "> Result will get placed at: " + mRunConfig.app_oat_file_path);

        final String divider = "########################################################";

        Log.d(TAG, divider);
        Log.d(TAG, divider);
        Log.d(TAG, divider);

        boolean success = ProcessExecutor.execute(cmd_dex2oat_compile, true,
                ProcessExecutor.processName(mRunConfig.app_package_name, "dex2artist"));

        Log.d(TAG, divider);
        Log.d(TAG, divider);
        Log.d(TAG, divider);

        if (success) {
            Log.d(TAG, "Compilation was successfull");
        } else {
            Log.d(TAG, "Compilation failed...");
            throw new InstrumentationException("Artist Injection Failed");
        }
    }
           

其中setupDex2oatCommand會生成對應指令,此指令用來将指定的apk檔案中的dex生成oat檔案,當然這裡的dex2oat使用的是ARTist生成的新的dex2oat,下面是一個示例的setupDex2oatCommand生成的指令

  • export LD_LIBRARY_PATH=/data/app/saarland.cispa.artist.artistgui-1/lib/arm:/data/user/0/saarland.cispa.artist.artistgui/files/artist/lib/;/data/user/0/saarland.cispa.artist.artistgui/files/artist/dex2oat –oat-file=/data/app/com.xidian.testapp-1/oat/arm/base.odex –compiler-backend=Optimizing –compiler-filter=everything –generate-debug-info –compile-pic –dex-file=/data/user/0/saarland.cispa.artist.artistgui/files/base_merged-signed.apk –dex-location=/data/app/com.xidian.testapp-1/base.apk –checksum-rewriting

然後通過ProcessExecutor.processName方法就把cmd指令交給系統處理了,最終生成oat檔案。

下一步通過setOatFilePermissions方法處理檔案權限,處理結束。。。