解密SuperWebview的一種另類方法


解密SuperWebview的一種另類方法

什么是SuperWebview

SuperWebview是APICloud官方推出的另一項重量級API生態產品,以SDK方式提供,致力於提升和改善移動設備Webview體驗差的整套解決方案。APP通過嵌入SuperWebview替代系統Webview,即可在HTML5中使用APICloud平台現有的所有端API,以及包括增量更新、版本管理、數據雲、推送雲、統計分析、積木式模塊化開發、所有已經聚合的開方平台服務等在內的雲服務能力,增強用戶體驗,解決移動設備Webview使用過程中出現的兼容能力弱、加載速度慢、功能缺陷等任何問題,幫助開發者解決使用Webview過程中的所有痛點。

SuperWebview繼承APICloud終端引擎的包括跨平台能力,模塊擴展機制,生命周期管理,窗口系統,事件模型,APP級別的用戶體驗等在內的所有優秀能力,並且全面打通html5與android和iOS之間的交互,同步提供APICloud平台最新的API技術能力和服務,APICloud團隊將保持對SuperWebview的持續更新和優化,兼容Html5的新特性,持續推出優質服務。

-----要弄別人,總得知道別人是誰。

總結來說有三點:

  • 安卓原生Webview的擴展
  • 結合native app 和 web app
  • APICloud實現安卓框架,自己開發web

上個圖,一目了然:

SuperWebview1

SuperWebview的安全措施

全包加密

  • 網頁全包加密:對網頁中全包的html,css,javascript代碼進行加密,加密后的網友代碼都是不可讀的,並且不能通過常用的格式化工具恢復。代碼在運行前都是加密的,在運行時進行動態解密。
  • 一鍵加密、運行時解密 在開發過程中無需對代碼做任何特殊處理,在雲編譯時選擇代碼加密即可。
  • 零修改、零影響 加密后不改變代碼的大小,不影響運行效率。
  • 安全盒子 定義了一個安全盒子,在盒子內的代碼按照加密和解密進行處理,其他代碼不受影響。
  • 重新定義資源標准 對保護的代碼進行統一資源管理,加速資源加載,加速代碼運行。

為什么要去解密

使用SuperWebview,安卓app基本上就是框架,一般的核心代碼都放在web中。最近遇到一個灰色的軟件,需要利用其中的一些東西。經過大半個星期的逆向,最終確定核心代碼在web里。

上點直觀的東西

加密后的文件

如圖:雲端加密,本地運行時解密

SuperWebview2

解密后的文件

如圖:對於一個web app來說,這就是核心代碼

SuperWebview3

如果要做,HOW?

准備工作

  • 目標安卓app(最好沒有加固,有加固的需要脫殼。vmp的搞不定。)
  • 一台root的手機
  • 安裝xposed框架
  • 安卓開發環境(Android Studio)

原理概述

由於SuperWebview要兼容原生的Webview(或者說由其派生而來),所以這決定了web的代碼必須要符合規范,即:必須是能讓瀏覽器引擎識別的有效代碼。很明白,在引擎中執行的都是明文。所以一下就找到一個核心點:怎么樣從Webview中dump出代碼。不論百度還是google都能找到方法。可以先看看這篇博客

簡單來說有4步:

  1. 開啟SuperWebview/Webview的JavaScript功能
  2. 自定義本地JavaScript的接口
  3. 向網頁中添加自定義接口
  4. 調用js,實現dump網頁

具體操作

  • 開啟JavaScript支持功能

    一般來說,使用SuperWebview的app有99%的都開啟了這個功能的。可以使用jadx打開app,全包搜索setJavaScriptEnabled函數,如果不放心,可以在找到的地方通過xposed再調用一次。

    webView.getSettings().setJavaScriptEnabled(true);
    

    搜索setJavaScriptEnabled函數
    SuperWebview4

  • 自定義JavaScript接口

    注意添加@JavascriptInterface聲明

    /*
    原因:only public methods that are annotated with 
    {@link android.webkit.JavascriptInterface} can be accessed 
    from JavaScript.
    */
    import android.webkit.JavascriptInterface;
    
    final class LocalJavaScript {
    	 @JavascriptInterface
        public String dumpSource(String html) {
            return html;
        }
    }
    
  • 向網頁中添加自定義接口

    這一步很關鍵,需要額外說明一下。先看看addJavascriptInterface函數的注釋,其中有這樣一句:

    Note that injected objects will not appear in JavaScript until the page is next (re)loaded.

    意思是這次加載的JavaScript代碼要重載頁面的時候才會生效。所以調用webView.addJavascriptInterface的時機就很重要,如果太遲則會導致后面的webView.loadUrl執行實效(因為沒有添加上本地的接口):

    webView.addJavascriptInterface(new JsObject(), "injectedObject");
    webView.loadData("", "text/html", null);
    webView.loadUrl("javascript:alert(injectedObject.toString())");

    怎樣確定addJavascriptInterface函數調用的時機呢?

    好簡單。因為SuperWebview一定會調用這個接口的(框架決定的),利用jadx定位到該方法所在的函數,hook該函數再調用addJavascriptInterface就行了。

    對於SuperWebview,一般是在com.uzmap.pkg.uzcore.uzmodule.b包中的public void a(boolean r, String d)函數里調用:

     webView.addJavascriptInterface(new LocalJavaScript(),"dump_html");
    

    搜索addJavascriptInterface函數:
    SuperWebview5

  • 注入JS代碼,dump頁面

    對於SuperWebview,一般是在com.uzmap.pkg.uzcore.x包中的public void onPageFinished(WebView view, String url)函數里調用:

webView.loadUrl("javascript:alert(dump_html.dumpSource(''+document.getElementsByTagName('html')[0].innerHTML+''))");
```

代碼執行效果:

SuperWebview6

寫xposed代碼的tips

  • 如果需要hook的函數里有自定義類型怎么辦?

    加載該類,然后作為參數傳遞:

Class<?> UZWebView =null;
UZWebView =lpparam.classLoader.loadClass("com.uzmap.pkg.uzcore.UZWebView");

       
* 如何獲取對象的成員屬性?

	通過反射調用:
	
	```java
// 獲取實例對象
Class clazz = param.thisObject.getClass();
// 獲取類成員屬性
Field field = clazz.getDeclaredField("a");
field.setAccessible(true);
// 獲取指定對象的成員屬性
WebView webView = (WebView) field.get(param.thisObject);
// 調用該對象的方法
webView.addJavascriptInterface(new InJavaScriptLocalObj(), "dump_html");
	```
	
	注1:如果成員屬性是自定類型的,暫時還不知道該怎么辦。在此向大神們請教。謝謝。  
	注2:如果hook的是靜態方法,是不能獲取到對象的屬性的。
	
* 如何調用對象的方法?

	通過反射調用:只能調用對象實現的方法,如果該方法在本類中沒有實現(如調用父類的方法),則無法成功
	
	```java
// 獲取實例對象---也有可能是從參數獲取,如param.args[0]
Class clazz = param.thisObject.getClass();
// 如果類有該方法就可以用反射調用的方式
Method c = a.getClass().getDeclaredMethod("c", int.class);
c.invoke(a,12);
	```
* 如何hook函數?

	hook構造函數使用:**findAndHookConstructor**
	
	```java
	findAndHookConstructor(className,
                lpparam.classLoader,
                param,
                new XC_MethodHook() {
                 @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
							//do something
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
							//do something
                    }
                }
        );
	```
	
	hook一般函數使用:**findAndHookMethod**
	
	```java
	 findAndHookMethod(className,
                lpparam.classLoader,
                methods,
                param,
                new XC_MethodHook() {
                 @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        // do somethin
            }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        // do somethin
            }
        });

	```
	
* 如何查看調用流程?

	拋出異常,打印調用棧:
	
	```java
	String exceptionName = "whatever";
	new Exception(exceptionName).printStackTrace();
XposedBridge.log(new Exception(exceptionName));
	```

## 總結

誠然通過這種方式是無法獲取到所有的解密文件的。例如html里面加載的js腳本,css文件。我也曾想通過注入js的方法來獲取加載的js文件,但並未成功。

誠然最簡單的方法還是去逆向SuperWebview,找到核心函數,可以獲取到所有的解密文件。

誠然這個過程並沒有幫助我達到最終的目的,可它還是很有趣。

目的不是想教大家怎么去逆向別人的產品,只是在研究學習的過程中發現了一些有趣好玩的iead。特別想將這個idea分享一下。無他。

當然,對於這款灰色的軟件,最終還是達到了最初的目的。只是並非使用了如此手段。


## 后記

技術的道路看不到盡頭。或者只身一人,或者三五成群,有時狂奔,有時懶散。我們終究停不下這個腳步,逢山翻山,遇水淌水。也許那里也有一片天,一片地。保持初心,勇敢前行。


免責聲明!

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



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