安卓逆向

安卓逆向第七篇-hook android WebView js注

2022-02-25  本文已影响0人  萌木盖

github

https://github.com/mengmugai/webviewdome

前言

如果某app使用了webview来加载网页。而一些登录状态或者加密参数等各类问题导致不能直接在pc的浏览器打开或抓取数据
方案1:关于只能微信客户端打开链接的爬取调试
上述方案当然可以解决一部分问题。
话说技多不压身
来看方案2

hook webview 注入js获取数据

hook时机

WebView.class 里面有很多方法,主要看以下几个

    public void onProgressChanged(WebView view, int newProgress) {
     
        if (newProgress > 20) {
          注入js!
        }
        super.onProgressChanged(view, newProgress);
    }

上述内容是前人表述的 我这里依旧使用OnPageFinished

注入js

先贴几个文章,看不看都可。
https://github.com/AlienwareHe/awesome-reverse/blob/main/android/webview-js-hook.md
https://github.com/AlienwareHe/awesome-reverse/blob/main/android/crack-webview.md
大概就是java层可以跟web做交互。

主要是一下几点

1、loadUrl 这个可以加载url 也可以加载js脚本,执行的结果不能返回
2、evaluateJavascript 执行js脚本,安卓4.4之后才能用,可以直接返回结果
3、addJavascriptInterface 可以让js调用java的方法,变相能解决1返回结果的问题

实战

测试案例

https://github.com/xuexiangjys/XUI 里的1.18版本
本人开头git里面也有xuidemo.apk

java hook

image.png
XposedBridge.hookAllMethods(
        XposedHelpers.findClass("com.xuexiang.xuidemo.base.webview.XPageWebViewFragment$4",xcalssloader),
        "onPageFinished",
        new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Log.e(TAG, "call on onPageFinished  param【0】:  "+ param.args[0].getClass().getName());
                Log.e(TAG, "call on url "+ param.args[1]);
                try {
                    String jscode = "javascript: 你的代码" ;
                    XposedHelpers.callMethod(param.args[0],
                            "loadUrl",
                            jscode);
                    或者
                                // 第三个参数也是你的代码
                        XposedHelpers.callMethod(webView, "evaluateJavascript", "(function(){ return window.document.getElementsByTagName('html')[0].innerHTML})();", new ValueCallback<String>() {
                        @Override
                        public void onReceiveValue(java.lang.String value) {

                     
                            value = value.replace("\\u003C", "<");
                            value = value.replace("\\&quot;", "");

                            int maxLogSize = 1000;
                            for (int i = 0; i <= value.length() / maxLogSize; i++) {
                                int start = i * maxLogSize;
                                int end = (i + 1) * maxLogSize;
                                end = end > value.length() ? value.length() : end;

                                Log.e(TAG, "[onReceiveValue] html:" + value.substring(start, end));
                            }
                        }
                    });
                } catch (Throwable e) {
                    Log.e(TAG,"调用loadUrl error "+e.getMessage());
                }

            }
        }
);      

上面代码第二个方案可以直接看到加载的html

如果使用了loadUrl
需要通过addJavascriptInterface加入一个对象

#1
XposedHelpers.callMethod(thiz, "addJavascriptInterface", XposedJavaScriptLocalObj, "Xposed");

#2
    static final class XposedJavaScriptLocalObj {
        @JavascriptInterface
        public void getSource(String html) {
            Log.i(TAG, "WebViewHelper替换html成功  ");
            Log.i(TAG, html);
            //替换最新更新的html内容
            thizHtml = html;
        }

        @JavascriptInterface
        public void getAjax(String json) {
            Log.i(TAG, "返回json成功");
            Log.i(TAG, json);
        }

    }
#3 
就可以注入下面这个js传回来
javascript:window.Xposed.getSource(document.getElementsByTagName('html')[0].innerHTML);

为了理的更清晰 画了个图


image.png

这样就能拿到webview里的html了。
那如果是JSON的怎么办呢?

注入hook json

var realXhr = "RealXMLHttpRequest";

function hookAjax(proxy) {
    window[realXhr] = window[realXhr] || XMLHttpRequest;
    XMLHttpRequest = function () {
        var xhr = new window[realXhr];
        for (var attr in xhr) {
            var type = "";
            try {
                type = typeof xhr[attr]
            } catch (e) {
            }
            if (type === "function") {
                this[attr] = hookFunction(attr);
            } else {
                Object.defineProperty(this, attr, {
                    get: getterFactory(attr),
                    set: setterFactory(attr),
                    enumerable: true
                })
            }
        }
        this.xhr = xhr;
    };
    function getterFactory(attr) {
        return function () {
            var v = this.hasOwnProperty(attr + "_") ? this[attr + "_"] : this.xhr[attr];
            var attrGetterHook = (proxy[attr] || {})["getter"];
            return attrGetterHook && attrGetterHook(v, this) || v;
         };
    };
    function setterFactory(attr) {
        return function (v) {
            var xhr = this.xhr;
            var that = this;
            var hook = proxy[attr];
            if (typeof hook === "function") {
                xhr[attr] = function () {
                    proxy[attr](that) || v.apply(xhr, arguments);
                };
            } else {
                var attrSetterHook = (hook || {})["setter"];
                v = attrSetterHook && attrSetterHook(v, that) || v;
                try {
                    xhr[attr] = v;
                } catch (e) {
                    this[attr + "_"] = v;
                }
            }
        };
    }
    function hookFunction(fun) {
        return function () {
            var args = [].slice.call(arguments);
            if (proxy[fun] && proxy[fun].call(this, args, this.xhr)) {
                return;
            }
            return this.xhr[fun].apply(this.xhr, args);
        }
    }
    return window[realXhr];
};


function tryParseJson2(v,xhr){
    var contentType=xhr.getResponseHeader("content-type")||"";
    if(xhr.responseURL.startsWith("https://movie.douban.com/j/search_subjects")){
       window.XposedAppiumObj.getAjax(v);
    }
    return v;
}


hookAjax({
    responseText: {
        getter: tryParseJson2
    },
    response: {
        getter: tryParseJson2
    }
});

也是通过loadurl 把上述js注入进去 最后会走tryParseJson2 ,他最后走了刚才绑定的类里的第二个方法getAjax


image.png

这里选择的是豆瓣的热门电影页,原app是打开百度 我通过xposed给换了 点击继续加载日志中就会增加 详细代码可看github

实现点击

可以看珍惜的xposedappium里的自动点击里的这个文件
代码为打开百度输入1,点击百度一下。
WebViewHelper 为引入👆🏻这个链接的文件

  String jsCode = "document.getElementsByTagName('html')[0].innerHTML";
      

    WebViewHelperzx wvhzx = new WebViewHelper((WebView) param.args[0]);

    wvhzx.executeJsCode("document.getElementById(\"index-kw\").value=1");
    wvhzx.clickByXpath("//button[@id=\"index-bn\"]");
    wvhzx.executeJsCode("jsCode",new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String s) {
            thizHtml = s;

        }
    });

结束语

xuidome.app这个浏览器页在 ”扩展“-》”web浏览器“-》直接显示调用
首次打开会有注册页, 那个没啥用 是个伪界面 手机号和验证码瞎填都行

————————————————————————————————————————-
2022,4,12更新
https://github.com/WankkoRee/EnableWebViewDebugging
这个有各个主流app的webview

上一篇下一篇

猜你喜欢

热点阅读