Android 敏感 API 的說明


  從中國的國情來看,Google 的諸多產品,包括 gmail,Android 官方市場 Google Play 正處於並將長期處於訪問不了的狀態。國內幾億網民也要生活,於是牆內出現了“百家爭鳴”的場面,各家硬件廠商、三大運營商和游戲應用商城都推出了自己的Android市場,出現了豌豆莢,91助手,MIUI 應用商店、360手機助手、搜狗手機助手、酷安網等。Android市場魚龍混雜,有很多優秀的閱讀、影音、游戲、辦公軟件等,也有很多吸費、廣告推送、泄露或上傳用戶隱私信息的 APK,讓人又愛又恨。有人比喻,Android 就像當年的 Windows XP。

  Android 已然成為市場占有量最大的移動智能設備平台,同時也成為了移動惡意應用最大的溫床。得到 ROOT 權限,就可以執行任意操作了。全民掀起刷機熱,但手機用戶被硬件廠商告知對 ROOT 過的手機不予保修。刷機成功能給人一種成就感,刷機不當就淪為“磚頭”。(那么問題就來了,手機變磚頭,是發生了物理變化還是化學變化?無法使用了,自然給用戶的心理留下了陰影,請問陰影部分的面積有多大?)回到正題,關於保護隱私、控制應用權限,月光博客也列舉了五種方法,可以看看。

  由於Android APK 很容易做手腳后二次打包放到網上,所以下載應用的時候最好去官網下載,也可以安裝一些殺毒軟件防御着。前一陣子報道了酷派手機內置 CoolReaper  后門程序,官網也可能不靠譜,手機廠商內置很多無用的APP到手機ROM包,卸載不了就有些可惡了。Android 代碼是開源的,漏洞發現的也不少。最近出現的 launchAnywhereboardAnywhere 漏洞通殺 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()

        // Seek to the first CDE and read all entries.
        RAFStream rafs = new RAFStream(mRaf, centralDirOffset);
        BufferedInputStream bin = new BufferedInputStream(rafs, 4096);
        byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
        for (int i = 0; i < numEntries; ++i) {
            ZipEntry newEntry = new ZipEntry(hdrBuf, bin);
             mEntries.put(newEntry.getName() , newEntry);
        }

             String entryName = newEntry.getName() ;
            if (mEntries.put(entryName, newEntry) != null) {
                throw new ZipException("Duplicate entry name: " + entryName);
            }

惡意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;

Java 與 C/C++ 不同,整型都是有符號的。讀取結構體的 WORD、DWORD 字段需要考慮大於2^15、2^31情況的發生。
Android 校驗 APK 文件的時候,會調用 ZipFile 的 public InputStream getInputStream(ZipEntry entry) 函數。
int localExtraLenOrWhatever = Short.reverseBytes(is.readShort());
// Skip the name and this "extra" data or whatever it is:
rafstrm.skip(entry.nameLength + localExtraLenOrWhatever);

 
  file name 填"classes.dex",classes.dex 文件的 magic number也是"dex",可以共用這三個字節。然后從file name 的'.'后寫入classes.dex。extra field length 填 0xFFFD,然后 rafstrm.skip(entry.nameLength + localExtraLenOrWhatever); 就會去讀取寫入的 classes.dex zip格式中的extra filed length大於2^15時會整數溢出變成負數,造成字段索引錯誤,無法跳過file name 與 extra field,就可以在這兩個域放原class.dex文件,后面再跟惡意dex,從而繞過簽名檢驗。但被攻擊的Apk里的classes.dex大小必須在64K以內。否則,就無法對其進行攻擊,可利用場景比較受限。

 

  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
parsePermission

 

有時候 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;
}
getLastKnownLocation

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM