從中國的國情來看,Google 的諸多產品,包括 gmail,Android 官方市場 Google Play 正處於並將長期處於訪問不了的狀態。國內幾億網民也要生活,於是牆內出現了“百家爭鳴”的場面,各家硬件廠商、三大運營商和游戲應用商城都推出了自己的Android市場,出現了豌豆莢,91助手,MIUI 應用商店、360手機助手、搜狗手機助手、酷安網等。Android市場魚龍混雜,有很多優秀的閱讀、影音、游戲、辦公軟件等,也有很多吸費、廣告推送、泄露或上傳用戶隱私信息的 APK,讓人又愛又恨。有人比喻,Android 就像當年的 Windows XP。
Android 已然成為市場占有量最大的移動智能設備平台,同時也成為了移動惡意應用最大的溫床。得到 ROOT 權限,就可以執行任意操作了。全民掀起刷機熱,但手機用戶被硬件廠商告知對 ROOT 過的手機不予保修。刷機成功能給人一種成就感,刷機不當就淪為“磚頭”。(那么問題就來了,手機變磚頭,是發生了物理變化還是化學變化?無法使用了,自然給用戶的心理留下了陰影,請問陰影部分的面積有多大?)回到正題,關於保護隱私、控制應用權限,月光博客也列舉了五種方法,可以看看。
由於Android APK 很容易做手腳后二次打包放到網上,所以下載應用的時候最好去官網下載,也可以安裝一些殺毒軟件防御着。前一陣子報道了酷派手機內置 CoolReaper 后門程序,官網也可能不靠譜,手機廠商內置很多無用的APP到手機ROM包,卸載不了就有些可惡了。Android 代碼是開源的,漏洞發現的也不少。最近出現的 launchAnywhere 和 boardAnywhere 漏洞通殺 Android 5.0 以下設備,防不勝防啊!
通常,惡意軟件的實現需要調用特定 API 來完成,如惡意計費軟件會調用發送短信 API,隱私竊取軟件會調用訪問通訊錄 API,此類 API 被稱為敏感 API。下面表格列舉了一些例子。
敏感API | 解釋 | 嚴重級別 |
sendTextMessage | 發送短信 | 高 |
sendMultipartTextMessage | 同時發送短信多條短信 | 高 |
abortBroadcast | 可以攔截短信 | 高 |
content://sms/inbox | 操作短信收件箱 | 高 |
getLine1Number | 獲取手機號碼 | 高 |
com.android.contacts | 通過ContentProviderOperation操作聯系人 | 高 |
ContactsContract.CommonDataKinds.Phone.NUMBER | 通過ContentValues操作聯系人 | 高 |
APK 中含有兩個classes.dex文件 | 檢查簽名漏洞 | 高 |
getDisplayMessageBody | 收到短信時讀取短信內容 | 高 |
Email.CONTENT_LOOKUP_URI 或 com.android.contacts/data/emails/lookup |
通過ContentValues操作郵件 | 高 |
getAccountsByType | 賬號操作 | 高 |
getInstalledPackages | 得到安裝列表 | 中 |
chmod | root操作 | 高 |
rm <filename> | 可能是刪除操作,需要分析確認 | 中 |
pm install/uninstall | 后台自動安裝或卸載 | 中 |
以上操作的反射api形式(需要詳細分析) | 通過反射方式可以隱藏api | 高 |
getSubscriberId | 得到設備IMSI | 低 |
getLastKnownLocation | 得到地理位置信息 | 高 |
TelephonyManager.CALL_STATE_RINGING | 監聽或攔截電話 | 中 |
廣告,包名待定,也可能是后台服務 | 含有廣告 | 低 |
Browser.BOOKMARKS_URI | 瀏覽器書簽操作 | 低 |
DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN | 激活設備管理器 | 中 |
android.intent.action.CALL | 撥打電話 | 中 |
getNeighboringCellInfo | 獲取基站信息 | 中 |
MediaRecorder | 錄音 | 中 |
沙盒掃描 | 發送短信,連接非法站等 | 高 |
敏感詞 | 出現和游戲不相關的暴力,色情,危害社會的詞語 | 中 |
APK 中含有兩個classes.dex文件
在BlackHat USA 2013上漏洞發現者講,原理就是解壓apk(zip壓縮包)時,若同時存在兩個classes.dex,第二個dex會覆蓋第一個,導致簽名檢驗到的是第二個dex,而執行dex時又會以第1個為准,因此只需要在apk中放置兩個classes.dex,順序依次為正常dex、惡意dex即可繞過簽名檢驗。
libcore/luni/src/main/java/java/util/zip/ZipFile.java
private void readCentralDir()
String entryName = newEntry.getName() ;
惡意APK軟件包包含兩個 entryName="classes.dex" 的文件,對應的數據分別為 malicious.data 和 org.data,且 malicious.data 在壓縮包字典中位於 org.data 之前。由於 APK解析中,當 entryName 相同時,后者會覆蓋前者信息,簽名驗證的時候,會去驗證原來的 classes.dex,但是執行的時候,是執行篡改過的 classes.dex。攻擊的時候,可以用名字 classes.dey, classes.dex 與 classes.dey 塞入APK后,然后二進制查找"classes.dey"替換成"classes.dex"(有兩處)。
APK其實是一個壓縮的ZIP文件,可以嘗試使用不同的壓縮級別0-9(0是store模式,不壓縮,9是最大壓縮,用時間換空間,會多耗一點時間),包體積會小一點,但不影響安裝。這里是ZIP文件的格式規范 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
4.3.7 Local file header:
local file header signature 4 bytes (0x04034b50)
version needed to extract 2 bytes
general purpose bit flag 2 bytes
compression method 2 bytes
last mod file time 2 bytes
last mod file date 2 bytes
crc-32 4 bytes
compressed size 4 bytes
uncompressed size 4 bytes
file name length 2 bytes
extra field length 2 bytes
file name (variable size)
extra field (variable size)
- nameLength = it.readShort(); - int extraLength = it.readShort(); - int commentByteCount = it.readShort();
+ nameLength = it.readShort() & 0xffff; + int extraLength = it.readShort() & 0xffff; + int commentByteCount = it.readShort() & 0xffff;
Android 校驗 APK 文件的時候,會調用 ZipFile 的 public InputStream getInputStream(ZipEntry entry) 函數。
int localExtraLenOrWhatever = Short.reverseBytes(is.readShort());

pm 是PakcageManger的縮寫,用pm命令可以在控制台操作安裝或卸載應用程序。Android基於Linux內核,也充分利用了Linux的用戶權限管理方法。應用程序需要使用的權限都列在AndroidManifest.xml文件里,解析權限的代碼在 frameworks/base/core/java/android/content/pm/PackageParser.java

private Package parsePackage( Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { ... else if (tagName.equals("permission")) { if (parsePermission(pkg, res, parser, attrs, outError) == null) { return null; } ... } private Permission parsePermission(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, String[] outError) throws XmlPullParserException, IOException
有時候 getLastKnownLocation 返回為 null,需要注意一下。
添加權限到 AndroidManifest.xml,並在設置里需要打開 GPS;如果是模擬器,請執行 geo fix <longitude value> <latitude value>命令。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
因為這個方法是非阻塞的,不會等到有值才返回。可以使用 LocationListener 這個類,每隔多少時間刷新一下。如:locationManager.requestLocationUpdates(provider, 1000, 15/* minDistance */, locationListener);

private Location getLastKnownLocation() { mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); List<String> providers = mLocationManager.getProviders(true); Location bestLocation = null; for (String provider : providers) { Location l = mLocationManager.getLastKnownLocation(provider); if (l == null) { continue; } if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) { // Found best last known location: %s", l); bestLocation = l; } } return bestLocation; }