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

github

https://github.com/mengmugai/webviewdome

前言

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

hook webview 注入js获取数据

hook时机

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

  • OnPageStarted 这个是页面开始加载时,不太适合。
  • OnPageFinished中注入,这个是页面加载完毕时,这个可以。
  • onProgressChanged中注入,在进度变化时注入,这个也不错。
    为什么是进度大于25%才进行注入,因为从测试看来只有进度大于这个数字页面才真正得到框架刷新加载,保证100%注入成功
    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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273

推荐阅读更多精彩内容