Android--APK 捆绑器的实现
生活随笔
收集整理的這篇文章主要介紹了
Android--APK 捆绑器的实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
利用捆綁器向正常程序捆綁病毒、木馬等惡意程序,以達到隱蔽安裝、運行的目的,這
在 Windows 平臺下是一種很常規的攻擊手段。那么,在智能終端十分流行的今天,如何實
現針對手機應用的捆綁器呢?對此,本文針對 Android 平臺的應用程序 APK 文件,給出了
類似 Windows 下捆綁器的實現方案。
原理與基礎
對任意的兩個 APK 應用程序 A 和 B 進行捆綁,并且在手機上安裝、運行捆綁生成的
APK 程序 C 后,仍然具備和 A 一樣的運行效果,要實現這一目的,捆綁過程可以從兩個思
路去實現。
1)對 Android 應用程序 A 進行反編譯,通過修改反編譯生成的 smali 代碼,控制執行
流程,使其具備安裝和執行應用程序 B 的功能,最后再打包生成捆綁后的應用 C。此方法
和早期的 Windows 環境下的 PE 病毒類似,插入額外的功能代碼,修改入口點改變程序執行
流程,最后再回到原有流程執行 A 的功能。
2)考慮到 A、B 的任意性,以及生成程序 C 的穩定性,在這里重點介紹另外一種通過
宿主程序實現釋放、安裝、運行 A 和 B 的方法。其思路亦來自 Windows 下的捆綁器,即專
門寫一個 host 程序作為宿主,其中應用程序 A、B 作為 host 的資源文件,運行 host 時可以
釋放、安裝和運行 A 和 B 的功能。此外,若考慮到安全性、免殺性,可對 A 和 B 進行加密、
編碼。
? ?? ?? ?? ?? ???因此,根據方法
?2
,可得出
?PC
?
端和
Android
?
手機端的軟件工作流程如圖
?1
?
和圖
?2
?
所示
功能實現 通過上面的介紹,可以知道總共需要實現兩個程序,即作為宿主程序的 Android 應用 host.apk,以及作為捆綁器的 Windows 應用程序“捆綁器”,下面將詳細介紹這兩個程序內 部原理和實現方法。 1.宿主應用程序 1)工作流程 對于宿主程序 host.apk,要實現上文所定義的功能需求,則其內部的工作流程可設計如 成如圖 3 所示的結構。在宿主程序初始化時,會調用MainActity 的 onCreate 函數。在 onCreate 函數中,通常用來初始化 MainActivity,但是考慮到最終呈現給用戶的界面(即 Activity) 為 A 的界面,所以宿主程序的 onCreate 函數中無需處理自身界面,只需想辦法啟動 A 的 Activity 即可。
釋放和安裝指定的應用程序通常需要較長時間(與應用程序的大小相關),若所有流程 均在 onCreate 函數流程中實現,則會導致宿主程序界面卡頓或假死,故可通過在 onCreate 函數中創建子線程的方式避免該問題,在子線程中實現相關功能,其中主要有釋放和安裝應 用程序,以及啟動主應用程序這兩部分。 2)釋放和安裝應用程序 ①釋放指定文件 對于 Android 應用程序,需要捆綁的資源文件可以直接放置到 assets 目錄下。在宿主程 序中,共計需要釋放三個文件,即配置文件 config、主程序 update.res 和捆綁進去的程序 data.res,釋放方式如下。當程序安裝后,需要釋放時可通過如下方式實現。 //dstFilePath 為釋放出來的文件路徑,resFileName 為需要釋放的文件路徑 private void dropFile(String dstFilePath, StringresFilename){ if(isFileExist(dstFilePath)) return; InputStream inputFS; try { inputFS = this.getResources().getAssets().open(resFilename); } catch (IOException e1) {return; } File outFile = new File(dstFilePath);
byte buf[] = new byte[1024];? ?int len;
try { OutputStream outputFS = new FileOutputStream(outFile);
while((len = inputFS.read(buf))>0) {??outputFS.write(buf,0,len);}
outputFS.close();??inputFS.close();
} catch (IOException e) { return ; }
return; } ②安裝應用程序 文件釋放后,接下來就要根據需要進行安裝程序。對用戶而言,已經安裝程序了,故接 下來安裝程序需要在后臺完成,即不引起用戶察覺,這就需要使用 shell 下的 pm 命令(Package Manage),而 pm 命令需要 system 權限,故執行此操作時需要確保可以獲取 root 權限。安裝 一個指定程序可通過“pm installtarget_apk”實現。而執行 shell 命令利用 runtime.exec 即可 實現,可將其簡單封裝為 runCommand(String str)。 private void runCommand(String strCommand){ Runtime runtime = Runtime.getRuntime(); try { runtime.exec(strCommand);
} catch (IOException e) {e.printStackTrace(); }
}
綜上,釋放、安裝的功能實現代碼如下。 //Install the specified apk public String installAPK(String apkFileName)
{
//獲取 assets 中文件安裝后的實際路徑
String tmpDir =this.getApplicationContext().getFilesDir()+"/"; String apk_path = tmpDir+apkFileName; //釋放文件 dropFile(apk_path, apkFileName); //安裝的 apk 文件需要讀寫權限,故賦予讀寫權限 runCommand("su -c chmod 666 "+apk_path); //使用 pm 安裝文件 runCommand("su -c pm install -t "+apk_path); return apk_path; } 3)啟動應用程序 啟動應用程序可通過切換 Activity 實現,即調用主程序 A 的 MainActivity 就可實現啟動 A 程序并切換界面為 A,該過程可通過如下幾步實現。 ①獲取需要啟動的 apk 的信息 具體包括 apk 的packagename 和MainActivity 名。對于宿主程序而言,由于捆綁器已成 功獲取并將此信息寫入了 config 文件,故只需要從 config 中分別讀取這兩個字符串即可。 ②新建 intent Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); ③切換 Activity,啟動程序 ComponentName cn = new ComponentName(main_app,main_app_Acitivty); intent.setComponent(cn); startActivity(intent); 2.捆綁器程序 運行在 Windows 下的捆綁器程序主要是為宿主程序做準備:獲取主程序 A 的 packagename 和MainActivity 信息,獲取捆綁進去的程序 B 的 packagename;修改宿主程序 的圖標,使其和程序 A 的一致;將準備好的宿主程序打包、簽名等。下面將詳述每個部分。 1)提取信息 需要提取的信息為應用程序的 Packagename 和 MainActivity 名,這些信息可以從 manifest 中解析,但更簡單的辦法就是使用現有工具 apktool。該工具可以對 apk 程序進行解包、打 包、提取信息等。具體的,提取信息僅需命令“aapt.exedump badging apk_name”即可。為 了方便應用,我們可以寫一個專門提取信息的批處理腳本 aapt_apkinfo.bat,具體如下: set PATH=%CD%;%PATH%; "%~dp0\aapt.exe" dump badging %1 > %2 該批處理以“aapt_apkinfo.bata.apk info.txt”的方式調用,其中第一個參數為 apk 路徑, 第二個參數為信息存儲文件。在程序中則可以如下調用。 ShellExecute(NULL, TEXT("open"),szAaptCommand,szParameter, NULL, SW_HIDE); 其中,szAaptCommand 指向aapt_apkinfo.bat,szParameter 為參數 1 和參數 2 構成的字 符串,如“a.apkinfo.txt”。 由于此時獲取到的信息如圖 4 所示,內容太過冗余,我們需要從中解析出packagename 和 MainActivity。易知,此時得到的信息是按行存儲的,即 info.txt 中的每行為一類或一條信 息 , 故 解 析 時 逐 行 判 斷 即 可 。 其 中packagename 通 常 以 此 種 格 式 出 現 “ package: name='com.tencent.mobileqq' ”, 可 以 認 為 package: name 之 后 的 單 引 號 內 的 內 容 為 packagename。mainactivity 的信息如圖 5 所示,故同樣易得其信息。相關文本搜索、字符串 判斷等相關代碼不再羅列。 圖 4 利用 apt 從 apk 中提取的原始信息 圖 5 info.txt 中 mainactivity 的格式 最后,通過上述方法將提取到的信息存入 assets 目錄下的 config 文件中,供宿主程序使 用。 2)替換圖標和標簽 ①獲取圖標和標簽信息 考慮到安全性,需要對 host 程序進行必要的偽裝,即重新打包后的 host 程序,安裝后 在應用列表看上去要和 A 程序的一致,故還需要替換圖標和標簽。同理,通過上述方法, 可從 info.txt 中獲得 label 和 icon 的信息,如圖 6 所示。 圖 6 info.txt 中的 label 和 icon 信息 ②替換圖標和標簽信息 在獲得 icon 路徑和 label 之后,接下來需要替換 host.apk 中的這些信息。 首先需要對主程序 A.apk 進行解包,以獲得圖標文件。Apk 解包同樣使用 apktool。直 接使用 apktool 解包的命令為“java –jar apktool.jar –d apkpathunpackeddir”,其中 apktool 支 持很多參數,實現其他功能,方便起見將其封裝為 bat 文件 unpack_apk.bat,內容如下。 set PATH=%CD%;%PATH%; java -jar "%~dp0\apktool.jar" d %1 %2 該批處理的第一個參數為 apk 路徑,第二個參數為解包后的目錄路徑。在捆綁器程序中, 可通過如下命令調用該批處理,實現相應的解包功能。 ShellExecute(NULL, TEXT("open"), szBatfilePath,szParameters, NULL, SW_HIDE); 其中,szParameters 即為批處理文件對應的兩個參數。 在成功解包之后,替換圖標就很簡單了,只需要用上述 icon 路徑對應的文件替換宿主 程序 host.apk 中的“\res\drawable-mdpi\ic_launcher.png”、 \res\drawable-hdpi\ic_launcher.png”、 “\res\drawable-ldpi\ic_launcher.png”和“\res\drawable-xhdpi\ic_launcher.png”文件。 宿主程序的標簽信息位于“\res\values\strings.xml”中,具體如圖 7 所示。 只需將其中的app_name 對應的值替換為前面獲取到的 label 的值即可。 圖 7 宿主程序的 strings 文件內容 3)打包、簽名 通過前面的步驟,我們已經成功修改好了宿主程序 host.apk,接下來需要對其進行打包、 簽名。 ①打包 對 APK 的打包,仍舊采用 apktool,將其封裝成apk_apk.bat,內容如下。
set PATH=%CD%;%PATH%; java -jar "%~dp0\apktool.jar" b %1 其所需的唯一一個參數即為需要打包成 apk 的目錄。類似的,程序中的調用方法依舊使 用“ShellExecute(NULL,TEXT("open"), szPackBatfile, m_szFinalApkDir, m_pctApkToolDir, SW_HIDE);”。 ②簽名 對 apk 的簽名,可通過使用工具 autosign 實現。其調用方式為“java -jar signapk.jar testkey.x509.pem testkey.pk8 unsigined.apk signed.apk”,需要的兩個參數為簽名的文件路徑和 成功簽名后的文件路徑。將其寫為 sign.bat,內容為:“java -jar signapk.jar testkey.x509.pem testkey.pk8 %1 %2”。在捆綁器程序中相應的調用方式為“ShellExecute(NULL, TEXT("open"), szSignBatfile, szParameters, m_pctAutoSignDir, SW_HIDE);”,最終即可獲得捆綁后的 apk 程 序。 總結 通過前面的步驟,最終生成了一套由“apktool”、“auto-sign”、“host.apk”和“捆綁器” 組成的 apk 捆綁器工具,如圖 8 所示。 圖 8 捆綁器工具組成(左)和捆綁器運行界面(右) 最終捆綁之后的 apk 應用程序具備和正常安裝的 apk 一樣的運行效果,但卻能隱蔽的安 裝所指定的任意 apk 文件,達到預期的目的和功能效果。 同時,在上述實現過程中仍存在一定的不足,如在安裝捆綁之后的 apk 應用時,所顯示 的權限和非正常安裝的程序(主程序 A.apk)的權限不一致。一種更完美的方法則可以考慮 在修改 host.apk 文件時,同時將主程序 A.apk 的 manifest 中的權限也復制到 host.apk 的 manifest 中,有興趣的讀者可以自行嘗試。
功能實現 通過上面的介紹,可以知道總共需要實現兩個程序,即作為宿主程序的 Android 應用 host.apk,以及作為捆綁器的 Windows 應用程序“捆綁器”,下面將詳細介紹這兩個程序內 部原理和實現方法。 1.宿主應用程序 1)工作流程 對于宿主程序 host.apk,要實現上文所定義的功能需求,則其內部的工作流程可設計如 成如圖 3 所示的結構。在宿主程序初始化時,會調用MainActity 的 onCreate 函數。在 onCreate 函數中,通常用來初始化 MainActivity,但是考慮到最終呈現給用戶的界面(即 Activity) 為 A 的界面,所以宿主程序的 onCreate 函數中無需處理自身界面,只需想辦法啟動 A 的 Activity 即可。
釋放和安裝指定的應用程序通常需要較長時間(與應用程序的大小相關),若所有流程 均在 onCreate 函數流程中實現,則會導致宿主程序界面卡頓或假死,故可通過在 onCreate 函數中創建子線程的方式避免該問題,在子線程中實現相關功能,其中主要有釋放和安裝應 用程序,以及啟動主應用程序這兩部分。 2)釋放和安裝應用程序 ①釋放指定文件 對于 Android 應用程序,需要捆綁的資源文件可以直接放置到 assets 目錄下。在宿主程 序中,共計需要釋放三個文件,即配置文件 config、主程序 update.res 和捆綁進去的程序 data.res,釋放方式如下。當程序安裝后,需要釋放時可通過如下方式實現。 //dstFilePath 為釋放出來的文件路徑,resFileName 為需要釋放的文件路徑 private void dropFile(String dstFilePath, StringresFilename){ if(isFileExist(dstFilePath)) return; InputStream inputFS; try { inputFS = this.getResources().getAssets().open(resFilename); } catch (IOException e1) {return; } File outFile = new File(dstFilePath);
byte buf[] = new byte[1024];? ?int len;
try { OutputStream outputFS = new FileOutputStream(outFile);
while((len = inputFS.read(buf))>0) {??outputFS.write(buf,0,len);}
outputFS.close();??inputFS.close();
} catch (IOException e) { return ; }
return; } ②安裝應用程序 文件釋放后,接下來就要根據需要進行安裝程序。對用戶而言,已經安裝程序了,故接 下來安裝程序需要在后臺完成,即不引起用戶察覺,這就需要使用 shell 下的 pm 命令(Package Manage),而 pm 命令需要 system 權限,故執行此操作時需要確保可以獲取 root 權限。安裝 一個指定程序可通過“pm installtarget_apk”實現。而執行 shell 命令利用 runtime.exec 即可 實現,可將其簡單封裝為 runCommand(String str)。 private void runCommand(String strCommand){ Runtime runtime = Runtime.getRuntime(); try { runtime.exec(strCommand);
} catch (IOException e) {e.printStackTrace(); }
}
綜上,釋放、安裝的功能實現代碼如下。 //Install the specified apk public String installAPK(String apkFileName)
{
//獲取 assets 中文件安裝后的實際路徑
String tmpDir =this.getApplicationContext().getFilesDir()+"/"; String apk_path = tmpDir+apkFileName; //釋放文件 dropFile(apk_path, apkFileName); //安裝的 apk 文件需要讀寫權限,故賦予讀寫權限 runCommand("su -c chmod 666 "+apk_path); //使用 pm 安裝文件 runCommand("su -c pm install -t "+apk_path); return apk_path; } 3)啟動應用程序 啟動應用程序可通過切換 Activity 實現,即調用主程序 A 的 MainActivity 就可實現啟動 A 程序并切換界面為 A,該過程可通過如下幾步實現。 ①獲取需要啟動的 apk 的信息 具體包括 apk 的packagename 和MainActivity 名。對于宿主程序而言,由于捆綁器已成 功獲取并將此信息寫入了 config 文件,故只需要從 config 中分別讀取這兩個字符串即可。 ②新建 intent Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); ③切換 Activity,啟動程序 ComponentName cn = new ComponentName(main_app,main_app_Acitivty); intent.setComponent(cn); startActivity(intent); 2.捆綁器程序 運行在 Windows 下的捆綁器程序主要是為宿主程序做準備:獲取主程序 A 的 packagename 和MainActivity 信息,獲取捆綁進去的程序 B 的 packagename;修改宿主程序 的圖標,使其和程序 A 的一致;將準備好的宿主程序打包、簽名等。下面將詳述每個部分。 1)提取信息 需要提取的信息為應用程序的 Packagename 和 MainActivity 名,這些信息可以從 manifest 中解析,但更簡單的辦法就是使用現有工具 apktool。該工具可以對 apk 程序進行解包、打 包、提取信息等。具體的,提取信息僅需命令“aapt.exedump badging apk_name”即可。為 了方便應用,我們可以寫一個專門提取信息的批處理腳本 aapt_apkinfo.bat,具體如下: set PATH=%CD%;%PATH%; "%~dp0\aapt.exe" dump badging %1 > %2 該批處理以“aapt_apkinfo.bata.apk info.txt”的方式調用,其中第一個參數為 apk 路徑, 第二個參數為信息存儲文件。在程序中則可以如下調用。 ShellExecute(NULL, TEXT("open"),szAaptCommand,szParameter, NULL, SW_HIDE); 其中,szAaptCommand 指向aapt_apkinfo.bat,szParameter 為參數 1 和參數 2 構成的字 符串,如“a.apkinfo.txt”。 由于此時獲取到的信息如圖 4 所示,內容太過冗余,我們需要從中解析出packagename 和 MainActivity。易知,此時得到的信息是按行存儲的,即 info.txt 中的每行為一類或一條信 息 , 故 解 析 時 逐 行 判 斷 即 可 。 其 中packagename 通 常 以 此 種 格 式 出 現 “ package: name='com.tencent.mobileqq' ”, 可 以 認 為 package: name 之 后 的 單 引 號 內 的 內 容 為 packagename。mainactivity 的信息如圖 5 所示,故同樣易得其信息。相關文本搜索、字符串 判斷等相關代碼不再羅列。 圖 4 利用 apt 從 apk 中提取的原始信息 圖 5 info.txt 中 mainactivity 的格式 最后,通過上述方法將提取到的信息存入 assets 目錄下的 config 文件中,供宿主程序使 用。 2)替換圖標和標簽 ①獲取圖標和標簽信息 考慮到安全性,需要對 host 程序進行必要的偽裝,即重新打包后的 host 程序,安裝后 在應用列表看上去要和 A 程序的一致,故還需要替換圖標和標簽。同理,通過上述方法, 可從 info.txt 中獲得 label 和 icon 的信息,如圖 6 所示。 圖 6 info.txt 中的 label 和 icon 信息 ②替換圖標和標簽信息 在獲得 icon 路徑和 label 之后,接下來需要替換 host.apk 中的這些信息。 首先需要對主程序 A.apk 進行解包,以獲得圖標文件。Apk 解包同樣使用 apktool。直 接使用 apktool 解包的命令為“java –jar apktool.jar –d apkpathunpackeddir”,其中 apktool 支 持很多參數,實現其他功能,方便起見將其封裝為 bat 文件 unpack_apk.bat,內容如下。 set PATH=%CD%;%PATH%; java -jar "%~dp0\apktool.jar" d %1 %2 該批處理的第一個參數為 apk 路徑,第二個參數為解包后的目錄路徑。在捆綁器程序中, 可通過如下命令調用該批處理,實現相應的解包功能。 ShellExecute(NULL, TEXT("open"), szBatfilePath,szParameters, NULL, SW_HIDE); 其中,szParameters 即為批處理文件對應的兩個參數。 在成功解包之后,替換圖標就很簡單了,只需要用上述 icon 路徑對應的文件替換宿主 程序 host.apk 中的“\res\drawable-mdpi\ic_launcher.png”、 \res\drawable-hdpi\ic_launcher.png”、 “\res\drawable-ldpi\ic_launcher.png”和“\res\drawable-xhdpi\ic_launcher.png”文件。 宿主程序的標簽信息位于“\res\values\strings.xml”中,具體如圖 7 所示。 只需將其中的app_name 對應的值替換為前面獲取到的 label 的值即可。 圖 7 宿主程序的 strings 文件內容 3)打包、簽名 通過前面的步驟,我們已經成功修改好了宿主程序 host.apk,接下來需要對其進行打包、 簽名。 ①打包 對 APK 的打包,仍舊采用 apktool,將其封裝成apk_apk.bat,內容如下。
set PATH=%CD%;%PATH%; java -jar "%~dp0\apktool.jar" b %1 其所需的唯一一個參數即為需要打包成 apk 的目錄。類似的,程序中的調用方法依舊使 用“ShellExecute(NULL,TEXT("open"), szPackBatfile, m_szFinalApkDir, m_pctApkToolDir, SW_HIDE);”。 ②簽名 對 apk 的簽名,可通過使用工具 autosign 實現。其調用方式為“java -jar signapk.jar testkey.x509.pem testkey.pk8 unsigined.apk signed.apk”,需要的兩個參數為簽名的文件路徑和 成功簽名后的文件路徑。將其寫為 sign.bat,內容為:“java -jar signapk.jar testkey.x509.pem testkey.pk8 %1 %2”。在捆綁器程序中相應的調用方式為“ShellExecute(NULL, TEXT("open"), szSignBatfile, szParameters, m_pctAutoSignDir, SW_HIDE);”,最終即可獲得捆綁后的 apk 程 序。 總結 通過前面的步驟,最終生成了一套由“apktool”、“auto-sign”、“host.apk”和“捆綁器” 組成的 apk 捆綁器工具,如圖 8 所示。 圖 8 捆綁器工具組成(左)和捆綁器運行界面(右) 最終捆綁之后的 apk 應用程序具備和正常安裝的 apk 一樣的運行效果,但卻能隱蔽的安 裝所指定的任意 apk 文件,達到預期的目的和功能效果。 同時,在上述實現過程中仍存在一定的不足,如在安裝捆綁之后的 apk 應用時,所顯示 的權限和非正常安裝的程序(主程序 A.apk)的權限不一致。一種更完美的方法則可以考慮 在修改 host.apk 文件時,同時將主程序 A.apk 的 manifest 中的權限也復制到 host.apk 的 manifest 中,有興趣的讀者可以自行嘗試。
總結
以上是生活随笔為你收集整理的Android--APK 捆绑器的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度Clouda的初步探索
- 下一篇: 尚硅谷韩顺平Linux教程学习笔记