前言:
首先介紹一下這個需求的背景,由於公司是涉及到金融行業的需要與銀行對接資金存管。出於保密性這裏不直接列出公司名字和銀行名字。從2018年國家對金融行業大整改以來,爲了能夠順利通過備案,我們也跟着政府的腳步一步一步走向合規。
好了,大致就是因爲要通過備案,必須把這個需求實現,否則將不會通過。
需求內容就是,當客戶端有關資金交易的時候,會通過加密數據以及祕鑰的方式把第三方銀行的頁面(充值、提現、投資...)等通過解析前面的加密數據獲取到裏面的鏈接用WebView把第三方銀行的頁面加載出來,並且此時必須對第三方頁面的文本框密碼框做攔截監聽,而且要實現公司自己的隨機鍵盤,屏蔽系統鍵盤。爲什麼要屏蔽系統鍵盤?因爲怕手機被root,鍵盤被植入的木馬監聽!那爲啥自定義鍵盤不會被監聽,因爲自定義鍵盤屬於應用。要監聽你的應用那就要破解反編譯再簽名打包等,如果做到這一點了,那說明你的應用本身就不安全了,要不要自定義鍵盤也沒啥用。
起初,覺着挺簡單,畢竟以前做過自定義的鍵盤。然後就直接開擼。
等到隨機鍵盤做出來以後,我才發現,頁面並不是我們公司自己的,而是第三方的,如果需要第三方頁面調用我們的接口,雙方公司不是必須要協商好接口協議嗎,此時才能調用我們自定義的鍵盤。
關鍵的地方就是在於,銀行不可能因爲你一家的需求改自己的公共頁面。
分析完之後,我考慮到幾個問題:
1.首先我們要能夠獲取並且設置這個框的焦點事件。
2.我們的自定義鍵盤如何綁定到這個文本框,看過android源碼的都知道EditText是如何與系統輸入法綁定的。
這個兩個問題解決了,應該基本就可以實現了。再仔細分析一下,這兩個問題其實就是一個問題,那就是如何獲取到當先密碼框的Object,對這個Object設置監聽,設置值等一系列操作只要獲取的這個Object了,那就是幾行代碼就能解決的問題了。
思路:
獲取到網頁源碼分析密碼框的特性做出篩選(Input標籤type=password就是密碼框了)。
解析出來type=password的Input標籤通過遍歷的方式得到ID(input.getElementById(id)就可以得到這個密碼框的Object了)。
回到上面1中,設置這個框的焦點事件,我們能夠通過解析出這個Object了,那麼該怎麼把監聽注入到這個Object中?爲什麼說要注入進去,因爲頁面是別人的,你總不能在別人頁面中添加js代碼吧,只能是注入js代碼。
思路分析清楚了,那流程就好辦了。
解決方案:
1.遍歷網頁input標籤並且type=password的標籤並且添加onFocus事件
webView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName(\"input\");" +
"for(var i=0;i<objs.length;i++)" +
"{"
+"var type = objs[i].getAttribute(\"type\");"
+"if(type==\"password\"){"
+"objs[i].οnfοcus=function()" +
"{"
+ "window.androidWebView.openSoftInput(this.id,this.type);" +
"}" +
"}" +
"}" +
"})()");
這段代碼邏輯應該都能看懂,就是WebView中加載js的方法loadUrl加載你自己寫的js事件,這就把js代碼注入到網頁中並且執行。主要解釋一下window.androidWebView.openSoftInput(this.id,this.type)這一句。androidWebView是
webView.addJavascriptInterface(new JavascriptInterface(),
"androidWebView");
在這一句中給你的JavascriptInterface類相當於起的一個給網頁調用的別名。後面openSoftInput(this.id,this.type)是你在JavascriptInterface這個類中定義的函數名括號裏面是參數都是String類型。這樣在黨文本框獲取到焦點的時候,會調用你在JavascriptInterface類中定義的openSoftInput函數,並且把當前密碼框的id,type傳過去(其實type後來我發現沒用着,但是留着日後萬一可以拓展)這時候就是java代碼了,你在openSoftInput函數中寫你如何調用自定義鍵盤的邏輯就可以了。
2.自定義鍵盤假裝你已經寫好了(網上很多代碼這裏也不提供了)
這個鍵盤可以通過dialog、popWindow等方式彈出,然後就是如何把輸入的值設置到當前密碼框。首先自定義的鍵盤要想跟系統鍵盤一樣,可以對此input密碼框增刪查改的話,必須拿到一個類似EditTextView中一個Editable的對象,對這個對象進行操作。但是這個是網頁,如果要實現那很繁瑣,這裏給大家提供一個簡單的思路,就是在自定義的鍵盤上面提供一個android原生的EditTextView,把你的密碼首先輸入到這個文本框,等到自定義鍵盤dismiss或者點擊上面的ok按鈕時,通過上面WebView.loadUrl(....)的方式設置給這個密碼框,這樣簡單很多。請看:
//如果輸入的是\符號會被轉義,需要輸入\\兩次才能設置進去
private void setPassword(final String id ,final String pwd){
webView.post(new Runnable() {
@Override
public void run() {
webView.loadUrl("javascript:(function(){" +
"var objs = document.getElementById(\""+id+"\");" +
"objs.value=\""+pwd +"\";"+
"objs.blur();" +
"})()");
}
});
}
這裏由於直接用WebView.loadUrl會拋出一個異常,大致意思就是線程不能執行這個操作,所以就用了異步的方式。在設置值objs.value=pwd後執行objs.blur讓此時的密碼框失去焦點,爲啥要這樣做,因爲我們注入的是onFocus事件,如果不讓它失去焦點的話,你再點擊到當前密碼框,onFocus事件不會調用,所以你的鍵盤就彈不出來。特別注意:經過測試這種設置的方式目前發現兩個特殊符號要做轉義處理,否則這個值不但設置不進去而且你的js代碼不會執行(是js報錯了,但是不會引起程序崩潰)。兩個符號是雙引號(")、反斜槓(\)。
細節處理:怎麼處理呢,就是把你的pwd拆分出來,如果有(")或者(\)就在他們前面添加一個(\)來轉義一下例如:我輸入的密碼是(" " " " " ")六個雙引號,那麼你處理過後給js設置的時候應該是(\"\"\"\"\"\")。再例如我輸入的密碼是(\\\\\\)六個反斜槓那麼你處理後給js設置的時候應該是(\\\\\\\\\\\\)沒錯十二根斜槓,因爲每一根你都要給他轉義,哈哈哈!
基本思路和核心代碼都寫出來了,相信大家應該可以自己擼起來了。
事後我感覺這種入侵代碼的方式頗有爬蟲的感覺,哈哈,大概暴力破解密碼的方式就是基於這種思想來玩的吧!
以上代碼寫的很難看,用了很多+號什麼的,因爲項目本身代碼是要保密的,這是臨時擼的,思想大家理解了就可以有千萬個實現方式。