Android无需权限显示悬浮窗, 兼谈逆向分析app
前言
最近UC瀏覽器中文版出了一個(gè)快速搜索的功能, 在使用其他app的時(shí)候, 如果復(fù)制了一些內(nèi)容, 屏幕頂部會(huì)彈一個(gè)窗口, 提示一些操作, 點(diǎn)擊后跳轉(zhuǎn)到UC, 顯示這個(gè)懸浮窗不需要申請(qǐng)android.permission.SYSTEM_ALERT_WINDOW權(quán)限.
如下圖, 截圖是在使用Chrome時(shí)截的, 但是屏幕頂部卻有UC的view浮在屏幕上. 我使用的是小米, 我并沒(méi)有給UC授懸浮窗權(quán)限, 所以我看到這個(gè)懸浮窗時(shí)是很震驚的.
截圖
懸浮窗原理
做過(guò)懸浮窗功能的人都知道, 要想顯示懸浮窗, 要有一個(gè)服務(wù)運(yùn)行在后臺(tái), 通過(guò)getSystemService(Context.WINDOW_SERVICE)拿到WindowManager, 然后向其中addView,?addView第二個(gè)參數(shù)是一個(gè)WindowManager.LayoutParams,?WindowManager.LayoutParams中有一個(gè)成員type, 有各種值, 一般設(shè)置成TYPE_PHONE就可以懸浮在很多view的上方了, 但是調(diào)用這個(gè)方法需要申請(qǐng)android.permission.SYSTEM_ALERT_WINDOW權(quán)限, 在很多機(jī)型上, 這個(gè)權(quán)限的名字叫懸浮窗, 比如小米手機(jī)上默認(rèn)是禁用這個(gè)權(quán)限的, 有些惡意app會(huì)用這個(gè)權(quán)限彈廣告, 而且很難追查是哪個(gè)應(yīng)用彈的. 如果這個(gè)權(quán)限被禁用, 那么結(jié)果就是懸浮窗無(wú)法展示, 比如有道詞典的復(fù)制查詞功能, 在小米手機(jī)上經(jīng)常沒(méi)用, 其實(shí)是用戶沒(méi)有授權(quán), 而且應(yīng)用也沒(méi)有引導(dǎo)用戶給它打開(kāi)授權(quán).
現(xiàn)在UC能突破這個(gè)限制, 我很好奇它是怎么做到的.
研究實(shí)現(xiàn)
Android開(kāi)發(fā)有點(diǎn)蛋疼的地方就是太容易被反編譯, 但有時(shí)這也成為我們研究別人app的一種手段.
反編譯
使用apktool可以很輕松的反編譯UC.
找代碼
逆向別人的app, 比較關(guān)鍵的地方是怎么找代碼, 因?yàn)榇a基本上都是混淆的, 直接看肯定是看不懂的, 只能去找, 突破口一般在字符資源上, 比如我們看到上圖中的快速搜索是UC的字符, 那么我們到res/values/strings.xml去找快速搜索, 就可以找到下面的內(nèi)容
<string name="dark_search_banner_search">快速搜索</string>這里我們拿到了快速搜索對(duì)應(yīng)的名字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, 我們?cè)僭诖a里面grep一下這個(gè)id, 就能知道哪幾個(gè)文件使用了這個(gè)字符資源.
之所以這么確定是在代碼里, 是因?yàn)閁C在我們復(fù)制的內(nèi)容不同時(shí), 懸浮窗標(biāo)題會(huì)不一樣, 一定是在代碼里控制的, 結(jié)果如下
./com/uc/browser/b/f.smali結(jié)果可能和大家不一樣, 但是一定會(huì)找到一個(gè)被混淆的smali文件
看代碼
這一部應(yīng)該是最惡心的. smali代碼和java代碼的關(guān)系, 就像匯編代碼和C++代碼, 但是smali比匯編代碼要容易理解的多, 不然也不會(huì)有那么多公司故意將代碼寫在C++層了.
雖然代碼都被混淆了, 而且以我們不熟悉的方式出現(xiàn), 但我們可以根據(jù)一些蛛絲馬跡來(lái)判斷代碼的執(zhí)行, 比如Framework的類和API是不能被混淆的, 這也是我們能看懂smali的原因之一, 我們可以結(jié)合這些面包屑來(lái)還原整個(gè)app代碼, 當(dāng)然這需要我們對(duì)smali很熟悉, 如果不熟悉smali, 至少要對(duì)Android的API熟悉. 因?yàn)橛袝r(shí)實(shí)在看不懂, 我們要靠猜來(lái)還原一段代碼的邏輯.
首先在代碼里面找到0x7f070049, 發(fā)現(xiàn)了如下代碼
(省略) 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出現(xiàn)之后的一部分代碼, 一路看下來(lái), 其實(shí)都是在取值賦值, 就拿0x7f070049來(lái)說(shuō):
#使v3寄存器的值為0x7f070049 const v3, 0x7f070049 #v1是Resources實(shí)例, 調(diào)用它的getString方法, 方法的參數(shù)是v3中的值 invoke-virtual {v1, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String; #將結(jié)果存入v1寄存器 move-result-object v1其實(shí)就是我們常用的getResources().getString
其實(shí)如果一直這么看下去, 會(huì)發(fā)現(xiàn)毫無(wú)頭緒, 剩下的代碼一直在干差不多的事情, 所以我只截取了這部分, 注意最后一行
也就是說(shuō), 有可能代碼轉(zhuǎn)到goto_0那兒去了, 那么看看goto_0那里又寫了些什么
: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", 我們就應(yīng)該有所警惕了, 這可能是關(guān)鍵代碼了. 為什么這么說(shuō)? 因?yàn)閼腋〈暗膶?shí)現(xiàn)里面, 需要獲取WindowManager, 從而需要調(diào)用Context.getSystemService(Context.WINDOW_SERVICE), 而官方文檔寫了Context.WINDOW_SERVICE就是常量window. 而后我們看到代碼中構(gòu)造了WindowManager.LayoutParams, 最終在addView時(shí)傳入.
看到這里, 我也覺(jué)得很奇怪, 我在懸浮窗原理中寫的是我知道的實(shí)現(xiàn)懸浮窗的方法, UC的實(shí)現(xiàn)好像跟我調(diào)用的是相同的API, 也沒(méi)看到反射之類可能展示奇技淫巧的代碼, 為什么UC就可以不需要權(quán)限直接顯示懸浮窗呢?
猜測(cè)
我認(rèn)為addView的第二個(gè)參數(shù)WindowManager.LayoutParams可能是關(guān)鍵, 所以我需要知道UC是如何構(gòu)造這個(gè)WindowManager.LayoutParams的.
由于是系統(tǒng)的類, 無(wú)法混淆, 直接搜索LayoutParams就找到了下面的代碼
iget-object v1, v10, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams;這句話就是把v10的值賦給v1,?v10是com/uc/browser/b/a的成員dpx, 那么打開(kāi)com/uc/browser/b/a.smali看看dpx到底是怎么構(gòu)造的.
(省略).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字段設(shè)成0x7d5, 官網(wǎng)上寫了0x000007d5是WindowManager.LayoutParams.TYPE_TOAST的值.
驗(yàn)證
實(shí)際測(cè)試了一下, 將type設(shè)置成TYPE_TOAST果然有奇效, 不需要android.permission.SYSTEM_ALERT_WINDOW權(quán)限就能顯示一個(gè)懸浮窗.
之前我一直以為調(diào)用了系統(tǒng)WindowManager.addView需要android.permission.SYSTEM_ALERT_WINDOW權(quán)限, 但實(shí)際上調(diào)用這個(gè)方法是不需要權(quán)限的, 在Android源碼中有這么一段
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è)方法是往系統(tǒng)的WindowManager里addView的時(shí)候做權(quán)限檢查用的, 那個(gè)type就是我們?cè)跇?gòu)造WindowManager.LayoutParams時(shí)賦值的type, 可以看到, 除了TYPE_TOAST, 其他都是要權(quán)限的, 而且非常喜感的是, 代碼中的注釋還說(shuō)他們現(xiàn)在對(duì)這種type毫無(wú)限制, 應(yīng)該引入標(biāo)記來(lái)限制開(kāi)發(fā)者.
處理兼容性
在這篇文章剛剛公布的時(shí)候, 就有同學(xué)反饋懸浮窗無(wú)法接收事件, 剛開(kāi)始我并沒(méi)有特別在意, 在廖祜秋大神做了一個(gè)demo之后, 這篇文章閱讀量又漲了不少, 隨即收到更多反饋事件的問(wèn)題, 我今天晚上借了臺(tái)MIUI V5 4.2.2實(shí)測(cè)了一下, 這臺(tái)機(jī)器上UC的快速搜索功能也無(wú)法正常使用.
在這個(gè)ROM上表現(xiàn)為:
使用TYPE_PHONE這類需要權(quán)限的type時(shí), 只有在app處于前臺(tái)時(shí)能顯示懸浮窗, 且能正常接受觸摸事件. 如果在應(yīng)用詳情里面授懸浮窗權(quán)限, 則工作完全正常.
(這里是MIUI V5對(duì)懸浮窗的特殊處理, 現(xiàn)在的ROM, 包括MIUI V6上, 如果不授權(quán), 無(wú)法顯示任何懸浮窗)
使用TYPE_TOAST這個(gè)不需要權(quán)限的type時(shí), 懸浮窗正常顯示, 但不能接受觸摸事件.
我重新檢查了一下smali代碼, 發(fā)現(xiàn)UC是有分版本處理的, 不過(guò)因?yàn)閟mali代碼的規(guī)則問(wèn)題, 很難直接看出來(lái), 我把分析過(guò)程寫出來(lái), 順便解釋一下smali的語(yǔ)法, 供大家以后逆向時(shí)拿來(lái)參考.
這次我是在OS X上反編譯的, 所以變量名可能略有區(qū)別.
接著上面com/uc/browser/b/a.smali中查看dpx的構(gòu)造過(guò)程, 代碼如下:
.field dpx:Landroid/view/WindowManager$LayoutParams;(省略)# direct methods .method public constructor <init>(Landroid/content/Context;Z)V .locals 7(省略) .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為了方便說(shuō)明, 我遵循smali的規(guī)則, 它用.line XX, 我們就說(shuō)這是第XX行的代碼.
上面是我之前分析得到UC使用的是TYPE_TOAST的地方, 證據(jù)就是第70行的const/16 v1, 0x7d5, 但是要知道, smali代碼沒(méi)有跳轉(zhuǎn)的話, 就是從上往下執(zhí)行, 我們看第69行的代碼如下:
.line 69 if-eqz p2, :cond_0這句話的意思是如果p2等于0, 控制流跳轉(zhuǎn)到cond_0, 否則就是繼續(xù)順序往下執(zhí)行. 也就是說(shuō)UC只有在p2 != 0條件滿足的時(shí)候才會(huì)使用TYPE_TOAST, 我們看看cond_0對(duì)應(yīng)的代碼.
.line 72 :cond_0 iget-object v0, p0, Lcom/uc/browser/b/a;->dpx:Landroid/view/WindowManager$LayoutParams; const/16 v1, 0x7d2 iput v1, v0, Landroid/view/WindowManager$LayoutParams;->type:I這里很簡(jiǎn)單, 就是將0x7d2賦給了type, 官網(wǎng)寫了0x000007d2是TYPE_PHONE, 也就是說(shuō)UC在某種情況下還是會(huì)用需要權(quán)限的老方法展示懸浮窗.
現(xiàn)在問(wèn)題是條件是什么, 關(guān)鍵在p2, 在smali里面, 有兩種寄存器命名規(guī)則, 一種叫v命名規(guī)則, 另一種是p命名規(guī)則, 當(dāng)然只是命名規(guī)則而已, 在使用apktool時(shí)是可以選的. 這里是p命名規(guī)則.
我剛才分析的賦值過(guò)程, 所在的方法是下面這個(gè), 我在剛才的代碼片段中也保留了這個(gè)部分.
# direct methods .method public constructor <init>(Landroid/content/Context;Z)V .locals 7這就是com/uc/browser/b/a的構(gòu)造方法,?dpx就是在構(gòu)造方法里初始化的,?.locals 7告訴我們這個(gè)方法中將出現(xiàn)7個(gè)局部寄存器(local register), 名字是v0, v1...v6, 而這個(gè)方法的參數(shù)有3個(gè), 隱式告訴我們這個(gè)方法中將出現(xiàn)3個(gè)參數(shù)寄存器(parameter register), 名字分別是p0, p1, p2.
我是怎么知道這個(gè)方法有3個(gè)參數(shù)的呢. smali中非靜態(tài)方法, 都隱含一個(gè)參數(shù)p0, 指向自身, 和Java中的this是一個(gè)意思, 而方法的參數(shù)寫在括號(hào)里, 也就是Landroid/content/Context;Z, 其中Landroid/content/Context;很明顯就是Android中的Context, 值存儲(chǔ)在p1里, 而Z對(duì)應(yīng)的是Android中的boolean, p2就是他了.
也就是說(shuō), type是用TYPE_TOAST還是用TYPE_PHONE, 取決于這個(gè)構(gòu)造方法的第二個(gè)參數(shù), 那到底誰(shuí)構(gòu)造了com/uc/browser/b/a呢? 可以去代碼里面搜形如new-instance ***, Lcom/uc/browser/b/a;的代碼. 更保險(xiǎn)的做法是搜Lcom/uc/browser/b/a然后一個(gè)一個(gè)的看.
我在com/uc/browser/b/f.smali里面找到了下面的代碼:
.prologue const/4 v0, 0x0 const/4 v1, 0x1(省略) new-instance v3, Lcom/uc/browser/b/a; iget-object v4, v9, Lcom/uc/browser/b/e;->mContext:Landroid/content/Context; sget v5, Landroid/os/Build$VERSION;->SDK_INT:I const/16 v6, 0x13 if-lt v5, v6, :cond_0 move v0, v1 :cond_0 invoke-direct {v3, v4, v0}, Lcom/uc/browser/b/a;-><init>(Landroid/content/Context;Z)V這段代碼首先是創(chuàng)建了com/uc/browser/b/a的實(shí)例, 存儲(chǔ)在v3中, 從另一處拿到了一個(gè)Context存儲(chǔ)在v4中, 然后拿到了當(dāng)前系統(tǒng)的android.os.Build.VERSION.SDK_INT存儲(chǔ)在v5中, 此時(shí)將v6的值設(shè)為0x13, 千萬(wàn)別粗心看成13了, 我好幾次都覺(jué)得這是13, 其實(shí)是十進(jìn)制的19, 接下來(lái)是一個(gè)條件分支, 如果v5的值小于v6, 也就是說(shuō)android.os.Build.VERSION.SDK_INT < 19, 直接跳轉(zhuǎn)到cond_0, 否則先將v1的值賦給v0, 再順序執(zhí)行.
這句代碼
invoke-direct {v3, v4, v0}, Lcom/uc/browser/b/a;-><init>(Landroid/content/Context;Z)V就是調(diào)用v3的構(gòu)造方法, 參數(shù)是v4和v0, 分析一下上面這段代碼的邏輯就是:
如果當(dāng)前系統(tǒng)API level小于19, 那么第二個(gè)參數(shù)就是0, 否則就是1.
而這第二個(gè)參數(shù)的值就是之前我們分析的p2的值, UC只有在p2 != 0條件滿足的時(shí)候才會(huì)使用TYPE_TOAST, 把整個(gè)邏輯串起來(lái)就是:
UC在API level >= 19的時(shí)候, 使用TYPE_TOAST, 其他情況使用TYPE_PHONE(需要權(quán)限).
可能是為了規(guī)避在低版本TYPE_TOAST不能接受事件的問(wèn)題.
關(guān)于針對(duì)源代碼的分析, 請(qǐng)看Android懸浮窗使用TYPE_TOAST的小結(jié)
實(shí)測(cè)效果
我之前寫的一個(gè)app有懸浮窗播放功能, 支持拖動(dòng)窗口和點(diǎn)擊暫停, 關(guān)閉窗口等等, 在4.4.4上實(shí)測(cè)功能正常.
無(wú)權(quán)限懸浮窗演示gif
感謝微博上關(guān)注的大神廖祜秋, 他做了個(gè)demo, 雖然交互和UC不同, 可以參考一下實(shí)現(xiàn).
廖祜秋大神的demo
關(guān)于這個(gè), 他也寫了一篇Android 懸浮窗的小結(jié)
其他補(bǔ)充
評(píng)論區(qū)的浮海大蝦同學(xué)有更多補(bǔ)充如下:
TYPE_TOAST一直都可以顯示, 但是用TYPE_TOAST顯示出來(lái)的在2.3上無(wú)法接收點(diǎn)擊事件, 因此還是無(wú)法隨意使用.
下面是我之前研究后臺(tái)線程顯示對(duì)話框的時(shí)候記得筆記, 大家可以看看我們項(xiàng)目中有需求需要在后臺(tái)任務(wù)中顯示Dialog, 項(xiàng)目最初的做法是用Activity模擬Dialog, 一個(gè)Activity已經(jīng)承載了近20種Dialog, 代碼混亂至極. 后來(lái)我發(fā)現(xiàn)Dialog可以通過(guò)改變Window Type實(shí)現(xiàn)不依賴Activity顯示, 然后就很興奮的要在使用這種方式來(lái)作為新的實(shí)現(xiàn)方式.
最初WindowType是WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 可是這是懸浮窗了, MIUI會(huì)默認(rèn)禁止(真他媽操蛋,也沒(méi)有任何提示)最終放棄. 后來(lái)試著換成了WindowManager.LayoutParams.TYPE_TOAST, 起初效果很好,MIUI也不禁止了, 哪里都能顯示, 這下開(kāi)心了. 可是后來(lái)又發(fā)現(xiàn)在2.3上不能接收點(diǎn)擊事件, 也就是說(shuō)Dialog上的按鈕不能點(diǎn)擊, 這他媽就很操蛋了, 又放棄了. 又試了試其他的Type都不能滿足需求, 結(jié)果如下:TYPE_SEARCH_BAR: 未知
TYPE_ACCESSIBILITY_OVERLAY: 拒絕使用
TYPE_APPLICATION: 只能配合Activity在當(dāng)前APP使用TYPE_APPLICATION_ATTACHED_DIALOG: 只能配合Activity在當(dāng)前APP使用
TYPE_APPLICATION_MEDIA: 無(wú)法使用(什么也不顯示)
TYPE_APPLICATION_PANEL: 只能配合Activity在當(dāng)前APP使用(PopupWindow默認(rèn)就是這個(gè)Type)
TYPE_APPLICATION_STARTING: 無(wú)法使用(什么也不顯示)
TYPE_APPLICATION_SUB_PANEL: 只能配合Activity在當(dāng)前APP使用TYPE_BASE_APPLICATION: 無(wú)法使用(什么也不顯示)
TYPE_CHANGED: 只能配合Activity在當(dāng)前APP使用
TYPE_INPUT_METHOD: 無(wú)法使用(直接崩潰)
TYPE_INPUT_METHOD_DIALOG: 無(wú)法使用(直接崩潰)
TYPE_KEYGUARD_DIALOG: 拒絕使用
TYPE_PHONE: 屬于懸浮窗(并且給一個(gè)Activity的話按下HOME鍵會(huì)出現(xiàn)看不到桌面上的圖標(biāo)異常情況)
TYPE_TOAST: 不屬于懸浮窗, 但有懸浮窗的功能, 缺點(diǎn)是在Android2.3上無(wú)法接收點(diǎn)擊事件
TYPE_SYSTEM_ALERT: 屬于懸浮窗, 但是會(huì)被禁止
尾聲
現(xiàn)在我們都知道了如何在不申請(qǐng)權(quán)限的情況下顯示懸浮窗, 我相信以中國(guó)Android開(kāi)發(fā)者的腦洞, 一定會(huì)有很多有趣或惡心的功能被開(kāi)發(fā)出來(lái), 一方面我自己覺(jué)得這個(gè)東西很有用, 可以實(shí)現(xiàn)一些很神奇的功能, 另一方面又擔(dān)心這個(gè)API被濫用, 最終不得不限制權(quán)限.
還有就是, 逆向分析僅用于學(xué)習(xí), 不要干違法的事情.
本人技術(shù)有限, 如果文中有錯(cuò)誤的歡迎指正, 以免誤導(dǎo)他人
利益聲明: 雖然我目前在UC實(shí)習(xí), 但我并沒(méi)有UC瀏覽器中文版的代碼權(quán)限, 也不會(huì)將公司的代碼分享給外人. 本文完全是靠我自己開(kāi)發(fā)經(jīng)驗(yàn)+逆向分析經(jīng)驗(yàn)+Google完成, 在此之前沒(méi)有看過(guò)UC瀏覽器的任何代碼.
原文地址:?http://www.jianshu.com/p/167fd5f47d5c
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Android无需权限显示悬浮窗, 兼谈逆向分析app的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 权威报告】WanaCrypt0r勒索蠕虫
- 下一篇: PWN2OWN 2017 Linux 内