欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Android無(wú)需權限顯示懸浮窗, 兼談逆向分析app

前言

最近UC瀏覽器中文版出了一個(gè)快速搜索的功能, 在使用其他app的時(shí)候, 如果復制了一些內容, 屏幕頂部會(huì )彈一個(gè)窗口, 提示一些操作, 點(diǎn)擊后跳轉到UC, 顯示這個(gè)懸浮窗不需要申請android.permission.SYSTEM_ALERT_WINDOW權限.

如下圖, 截圖是在使用Chrome時(shí)截的, 但是屏幕頂部卻有UC的view浮在屏幕上. 我使用的是小米, 我并沒(méi)有給UC授懸浮窗權限, 所以我看到這個(gè)懸浮窗時(shí)是很震驚的.


截圖

懸浮窗原理

做過(guò)懸浮窗功能的人都知道, 要想顯示懸浮窗, 要有一個(gè)服務(wù)運行在后臺, 通過(guò)getSystemService(Context.WINDOW_SERVICE)拿到WindowManager, 然后向其中addView, addView第二個(gè)參數是一個(gè)WindowManager.LayoutParams, WindowManager.LayoutParams中有一個(gè)成員type, 有各種值, 一般設置成TYPE_PHONE就可以懸浮在很多view的上方了, 但是調用這個(gè)方法需要申請android.permission.SYSTEM_ALERT_WINDOW權限, 在很多機型上, 這個(gè)權限的名字叫懸浮窗, 比如小米手機上默認是禁用這個(gè)權限的, 有些惡意app會(huì )用這個(gè)權限彈廣告, 而且很難追查是哪個(gè)應用彈的. 如果這個(gè)權限被禁用, 那么結果就是懸浮窗無(wú)法展示, 比如有道詞典復制查詞功能, 在小米手機上經(jīng)常沒(méi)用, 其實(shí)是用戶(hù)沒(méi)有授權, 而且應用也沒(méi)有引導用戶(hù)給它打開(kāi)授權.

現在UC能突破這個(gè)限制, 我很好奇它是怎么做到的.

研究實(shí)現

Android開(kāi)發(fā)有點(diǎn)蛋疼的地方就是太容易被反編譯, 但有時(shí)這也成為我們研究別人app的一種手段.

反編譯

使用apktool可以很輕松的反編譯UC.

找代碼

逆向別人的app, 比較關(guān)鍵的地方是怎么找代碼, 因為代碼基本上都是混淆的, 直接看肯定是看不懂的, 只能去找, 突破口一般在字符資源上, 比如我們看到上圖中的快速搜索是UC的字符, 那么我們到res/values/strings.xml去找快速搜索, 就可以找到下面的內容

<string name='dark_search_banner_search'>快速搜索</string>

這里我們拿到了快速搜索對應的名字dark_search_banner_search, Android在編譯時(shí)會(huì )給每個(gè)資源分配一個(gè)id, 我們grep一下這個(gè)字符資源的名字就能知道id是多少, 一般在R.java, res/values/public.xml中有定義, 我直接到public.xml中找到了它的id

<public type='string' name='dark_search_banner_search' id='0x7f070049' />

有了字符資源的id 0x7f070049, 我們再在代碼里面grep一下這個(gè)id, 就能知道哪幾個(gè)文件使用了這個(gè)字符資源.

之所以這么確定是在代碼里, 是因為UC在我們復制的內容不同時(shí), 懸浮窗標題會(huì )不一樣, 一定是在代碼里控制的, 結果如下

./com/uc/browser/b/f.smali

結果可能和大家不一樣, 但是一定會(huì )找到一個(gè)被混淆的smali文件

看代碼

這一部應該是最?lèi)盒牡? smali代碼和java代碼的關(guān)系, 就像匯編代碼和C++代碼, 但是smali比匯編代碼要容易理解的多, 不然也不會(huì )有那么多公司故意將代碼寫(xiě)在C++層了.

雖然代碼都被混淆了, 而且以我們不熟悉的方式出現, 但我們可以根據一些蛛絲馬跡來(lái)判斷代碼的執行, 比如Framework的類(lèi)和API是不能被混淆的, 這也是我們能看懂smali的原因之一, 我們可以結合這些面包屑來(lái)還原整個(gè)app代碼, 當然這需要我們對smali很熟悉, 如果不熟悉smali, 至少要對Android的API熟悉. 因為有時(shí)實(shí)在看不懂, 我們要靠猜來(lái)還原一段代碼的邏輯.

首先在代碼里面找到0x7f070049, 發(fā)現了如下代碼

    (省略)    const v3, 0x7f070049    invoke-virtual {v1, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String;    move-result-object v1    iput-object v1, v0, Lcom/uc/browser/b/a;->dpC:Ljava/lang/String;    :cond_9    (省略)    invoke-virtual {v0, v1}, Lcom/uc/browser/b/a;->o(Landroid/graphics/drawable/Drawable;)V    :try_end_2    .catch Ljava/lang/Exception; {:try_start_2 .. :try_end_2} :catch_0    goto/16 :goto_0    (省略)

這是0x7f070049出現之后的一部分代碼, 一路看下來(lái), 其實(shí)都是在取值賦值, 就拿0x7f070049來(lái)說(shuō):

#使v3寄存器的值為0x7f070049    const v3, 0x7f070049#v1是Resources實(shí)例, 調用它的getString方法, 方法的參數是v3中的值    invoke-virtual {v1, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String;#將結果存入v1寄存器    move-result-object v1

其實(shí)就是我們常用的getResources().getString
其實(shí)如果一直這么看下去, 會(huì )發(fā)現毫無(wú)頭緒, 剩下的代碼一直在干差不多的事情, 所以我只截取了這部分, 注意最后一行

goto/16 :goto_0

也就是說(shuō), 有可能代碼轉到goto_0那兒去了, 那么看看goto_0那里又寫(xiě)了些什么

    :goto_0    (省略)    const-string v1, 'window'    invoke-virtual {v0, v1}, Landroid/content/Context;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;    move-result-object v0    check-cast v0, Landroid/view/WindowManager;    invoke-interface {v0}, Landroid/view/WindowManager;->getDefaultDisplay()Landroid/view/Display;    move-result-object v0    invoke-virtual {v0}, Landroid/view/Display;->getWidth()I    move-result v0    iget-object v1, v10, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    iput v0, v1, Landroid/view/WindowManager$LayoutParams;->width:I    iget-object v0, v10, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    invoke-virtual {v10}, Lcom/uc/browser/b/a;->getContext()Landroid/content/Context;    move-result-object v1    invoke-virtual {v1}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;    move-result-object v1    const v2, 0x7f0d0022    invoke-virtual {v1, v2}, Landroid/content/res/Resources;->getDimension(I)F    move-result v1    float-to-int v1, v1    iput v1, v0, Landroid/view/WindowManager$LayoutParams;->height:I    iget-object v0, v10, Lcom/uc/browser/b/a;->mWindowManager:Landroid/view/WindowManager;    iget-object v1, v10, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    invoke-interface {v0, v10, v1}, Landroid/view/WindowManager;->addView(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V

其實(shí)看到const-string v1, 'window', 我們就應該有所警惕了, 這可能是關(guān)鍵代碼了. 為什么這么說(shuō)? 因為懸浮窗的實(shí)現里面, 需要獲取WindowManager, 從而需要調用Context.getSystemService(Context.WINDOW_SERVICE), 而官方文檔寫(xiě)了Context.WINDOW_SERVICE就是常量window. 而后我們看到代碼中構造了WindowManager.LayoutParams, 最終在addView時(shí)傳入.

看到這里, 我也覺(jué)得很奇怪, 我在懸浮窗原理中寫(xiě)的是我知道的實(shí)現懸浮窗的方法, UC的實(shí)現好像跟我調用的是相同的API, 也沒(méi)看到反射之類(lèi)可能展示奇技淫巧的代碼, 為什么UC就可以不需要權限直接顯示懸浮窗呢?

猜測

我認為addView的第二個(gè)參數WindowManager.LayoutParams可能是關(guān)鍵, 所以我需要知道UC是如何構造這個(gè)WindowManager.LayoutParams的.

由于是系統的類(lèi), 無(wú)法混淆, 直接搜索LayoutParams就找到了下面的代碼

iget-object v1, v10, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;

這句話(huà)就是把v10的值付給v1, v10com/uc/browser/b/a的成員dpx, 那么打開(kāi)com/uc/browser/b/a.smali看看dpx到底是怎么構造的.

    (省略).field dpx:Landroid/view/WindowManager$LayoutParams;    (省略)    .line 68    new-instance v0, Landroid/view/WindowManager$LayoutParams;    invoke-direct {v0}, Landroid/view/WindowManager$LayoutParams;-><init>()V    iput-object v0, p0, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    .line 69    if-eqz p2, :cond_0    .line 70    iget-object v0, p0, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    const/16 v1, 0x7d5    iput v1, v0, Landroid/view/WindowManager$LayoutParams;->type:I    .line 74    :goto_0    iget-object v0, p0, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;    const/4 v1, 0x1    iput v1, v0, Landroid/view/WindowManager$LayoutParams;->format:I    (省略)

這里的代碼就很簡(jiǎn)單的, 我最先看的是下面這段

    const/16 v1, 0x7d5    iput v1, v0, Landroid/view/WindowManager$LayoutParams;->type:I

這兩句代碼就是把WindowManager.LayoutParams.type字段設成0x7d5, 官網(wǎng)上寫(xiě)了0x000007d5是WindowManager.LayoutParams.TYPE_TOAST的值.

驗證

實(shí)際測試了一下, 將type設置成TYPE_TOAST果然有奇效, 不需要android.permission.SYSTEM_ALERT_WINDOW權限就能顯示一個(gè)懸浮窗.

之前我一直以為調用了系統WindowManager.addView需要android.permission.SYSTEM_ALERT_WINDOW權限, 但實(shí)際上調用這個(gè)方法是不需要權限的, 在A(yíng)ndroid源碼中有這么一段

public int checkAddPermission(WindowManager.LayoutParams attrs) {    int type = attrs.type;    if (type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW            || type > WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {        return WindowManagerImpl.ADD_OKAY;    }    String permission = null;    switch (type) {        case TYPE_TOAST:            // XXX right now the app process has complete control over            // this...  should introduce a token to let the system            // monitor/control what they are doing.            break;        case TYPE_INPUT_METHOD:        case TYPE_WALLPAPER:            // The window manager will check these.            break;        case TYPE_PHONE:        case TYPE_PRIORITY_PHONE:        case TYPE_SYSTEM_ALERT:        case TYPE_SYSTEM_ERROR:        case TYPE_SYSTEM_OVERLAY:            permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;            break;        default:            permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;    }    if (permission != null) {        if (mContext.checkCallingOrSelfPermission(permission)                != PackageManager.PERMISSION_GRANTED) {            return WindowManagerImpl.ADD_PERMISSION_DENIED;        }    }    return WindowManagerImpl.ADD_OKAY;}

可以猜到這個(gè)方法是往系統的WindowManageraddView的時(shí)候做權限檢查用的, 那個(gè)type就是我們在構造WindowManager.LayoutParams時(shí)賦值的type, 可以看到, 除了TYPE_TOAST, 其他都是要權限的, 而且非常喜感的是, 代碼中的注釋還說(shuō)他們現在對這種type毫無(wú)限制, 應該引入標記來(lái)限制開(kāi)發(fā)者.

實(shí)測效果

看到有評論說(shuō)這樣的是不支持點(diǎn)擊的. 我之前寫(xiě)的一個(gè)app有懸浮窗播放功能, 支持拖動(dòng)窗口和點(diǎn)擊暫停, 關(guān)閉窗口等等, 實(shí)測功能正常, 今天下班匆匆忙忙寫(xiě)的這篇文章, 沒(méi)有錄制演示視頻.

但是在2.3上不能接收點(diǎn)擊事件.

評論區的浮海大蝦同學(xué)有更多補充如下:

TYPE_TOAST一直都可以顯示, 但是用TYPE_TOAST顯示出來(lái)的在2.3上無(wú)法接收點(diǎn)擊事件, 因此還是無(wú)法隨意使用.
下面是我之前研究后臺線(xiàn)程顯示對話(huà)框的時(shí)候記得筆記, 大家可以看看我們項目中有需求需要在后臺任務(wù)中顯示Dialog, 項目最初的做法是用Activity模擬Dialog, 一個(gè)Activity已經(jīng)承載了近20種Dialog, 代碼混亂至極. 后來(lái)我發(fā)現Dialog可以通過(guò)改變Window Type實(shí)現不依賴(lài)Activity顯示, 然后就很興奮的要在使用這種方式來(lái)作為新的實(shí)現方式.
最初WindowType是WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 可是這是懸浮窗了, MIUI會(huì )默認禁止(真他媽操蛋,也沒(méi)有任何提示)最終放棄. 后來(lái)試著(zhù)換成了WindowManager.LayoutParams.TYPE_TOAST, 起初效果很好,MIUI也不禁止了, 哪里都能顯示, 這下開(kāi)心了. 可是后來(lái)又發(fā)現在2.3上不能接收點(diǎn)擊事件, 也就是說(shuō)Dialog上的按鈕不能點(diǎn)擊, 這他媽就很操蛋了, 又放棄了. 又試了試其他的Type都不能滿(mǎn)足需求, 結果如下:TYPE_SEARCH_BAR: 未知
TYPE_ACCESSIBILITY_OVERLAY: 拒絕使用
TYPE_APPLICATION: 只能配合Activity在當前APP使用TYPE_APPLICATION_ATTACHED_DIALOG: 只能配合Activity在當前APP使用
TYPE_APPLICATION_MEDIA: 無(wú)法使用(什么也不顯示)
TYPE_APPLICATION_PANEL: 只能配合Activity在當前APP使用(PopupWindow默認就是這個(gè)Type)
TYPE_APPLICATION_STARTING: 無(wú)法使用(什么也不顯示)
TYPE_APPLICATION_SUB_PANEL: 只能配合Activity在當前APP使用TYPE_BASE_APPLICATION: 無(wú)法使用(什么也不顯示)
TYPE_CHANGED: 只能配合Activity在當前APP使用
TYPE_INPUT_METHOD: 無(wú)法使用(直接崩潰)
TYPE_INPUT_METHOD_DIALOG: 無(wú)法使用(直接崩潰)
TYPE_KEYGUARD_DIALOG: 拒絕使用
TYPE_PHONE: 屬于懸浮窗(并且給一個(gè)Activity的話(huà)按下HOME鍵會(huì )出現看不到桌面上的圖標異常情況)
TYPE_TOAST: 不屬于懸浮窗, 但有懸浮窗的功能, 缺點(diǎn)是在A(yíng)ndroid2.3上無(wú)法接收點(diǎn)擊事件
TYPE_SYSTEM_ALERT: 屬于懸浮窗, 但是會(huì )被禁止

(TODO: 補充演示GIF)

尾聲

現在我們都知道了如何在不申請權限的情況下顯示懸浮窗, 我相信以中國Android開(kāi)發(fā)者的腦洞, 一定會(huì )有很多有趣或惡心的功能被開(kāi)發(fā)出來(lái), 一方面我自己覺(jué)得這個(gè)東西很有用, 可以實(shí)現一些很神奇的功能, 另一方面又擔心這個(gè)API被濫用, 最終不得不限制權限.

還有就是, 逆向分析僅用于學(xué)習, 不要干違法的事情.

本人技術(shù)有限, 如果文中有錯誤的歡迎指正, 以免誤導他人

圖片來(lái)源于網(wǎng)絡(luò )

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
關(guān)于安卓手機的硬件加速問(wèn)題
不懂記,在A(yíng)ndroid 開(kāi)發(fā)中怎么全屏顯示——day1
Android設備防止休眠
Android 防止手機休眠
android:screenOrientation屬性
android全屏
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久