通過應用層分析,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方法處理檔案權限,處理結束。。。