这个瑞数五代被写死了,没有用到随机cookie和随机代码和随机变量,我简称弱智版瑞数
这个web需要用微信浏览器授权来获取userid,所以不能在谷歌调试
这里可以学习到jsvmp的内容,大家可以看下JSVMP js加密

仅供学习侵权联系我删

远程调试

这个app是webview的,在app没法断点调试,但是用的是chrome的X5内核,所以可以映射到pc端的谷歌浏览器调试.
app需要开debugger,

WebView.setWebContentsDebuggingEnabled(true);

方案

  • frida hook
  • xp模块

hook成功后,adb连上手机
谷歌浏览器打开 chrome://inspect 会看到自己的手机型号和webview的内容
点击对应页面的 pause 就能进行调试,如果一直转圈,要搞个V才行

F12无限debugger

一开始调试,直接就进入了无限debugger,在文本中搜不到debugger
模式也是vm,意思是内存
这个时候就会想到大概率eval
写个脚本hook他

var _eval=eval
eval=function(arg){
console.trace(arg);
    return _eval(arg);
}

完事后会发现有两层eval,第一层是乱码,第二层才是代码.
那就注入个代码把debugger去掉,并在第二层注入eval,hook下一层的代码(就套娃)

} else if (56 > 87 - _$G$ && _$G$ < 36) {
    if (_$G$ - 64 > -31 && 104 > 69 + _$G$) {
        var _$_r = _$vq();
        } else if (99 === _$G$ * 3) {
        //add
                _$w$=_$w$.replace("debugger", "");           
_$w$ ="var _eval=eval;eval=function(arg){console.trace(arg); return _eval(arg);}" +_$w$;
        //add
            _$aj = _$TN[_$Fk[18]](_$pJ, _$w$);
    } else if (71 > 102 - _$G$ && 6 - _$G$ > -27) {
        return Math.abs(arguments[1]) % 16;
    } else {
        _$TN = _$hT(221);
    }
} else if (_$G$ > 39 && _$G$ - 64 < -20) {

这样就绕过了debugger顺便找到了下一层的代码
实际上debugger的检测是以下两行

} else if (_$Wa === 426) {
var _$AD = _$kg[_$N3[679]](_$f0(_$N3[103]));
} else {
 var _$$4 = _$nB(_$I0(_$Xp.join(_$N3[74])));

定位加密

上面的步骤走完后我们已经有了完整的代码,把他eval hook去掉,然后把代码丢到同一个文件,让给他别在vm了,这样我们可以随便调试

这时候刷新下页面,debugger没了,除了乱其它都挺好(乱可以用ast还原下,但是硬怼比较爽)
上一步之前F12的network看堆栈还是在vm的,现在看堆栈已经在代码里面了


用正常的逆向手法追踪,一定已经追到类似xmlhttp的一个乱码变量,聪明的朋友会发现他被重写了

用dir看看
找一下作用域
就找到了实际的加密点

                    function _$63() {
                        window._$c4 = _$c4;//放到环境变量
                        _$BN();
                        console.log('url->'+arguments[1]);//自吐加密前的
                        var _$M7 = _$c4(arguments[1]);
                        arguments[1] = _$M7._$O_;
                        this._$xw = _$M7._$xw;
                        return _$jC[_$N3[14]](this, arguments);
                    }

这样搞之后就能在console随便加密了

sekiro

用的免费版,简单易用,目前发现的问题在于有时候断了不会自动重连,自己部署的话有坑,我也忘了啥坑

调用流程

    <script type="text/javascript">
        alert('hook success');
        function guid() {
            function S4() {
                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
            }

            return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
        }

        var client = new SekiroClient("wss://sekiro.virjar.com/websocket?group=ws-group-demo&clientId=" + guid());
        client.registerAction("encode", function (request, resolve, reject) {
            resolve(window._$c4(request['encode_str']));
        });

        client.registerAction("executeJs", function (request, resolve, reject) {
            var code = request['code'];
            if (!code) {
                reject("need param:{code}");
                return;
            }

            code = "return " + code;

            console.log("executeJs: " + code);

            try {
                var result = new Function(code)();
                resolve(result);
            } catch (e) {
                reject("error: " + e);
            }

        });
    </script>

以下为他开源的部分,我直接复制到源码根目录,不引用了

 <script type="text/javascript">
        function SekiroClient(wsURL) {
            this.wsURL = wsURL;
            this.handlers = {};
            this.socket = {};
            this.base64 = false;
            // check
            if (!wsURL) {
                throw new Error('wsURL can not be empty!!')
            }
            this.webSocketFactory = this.resolveWebSocketFactory();
            this.connect()
        }

        SekiroClient.prototype.resolveWebSocketFactory = function () {
            if (typeof window === 'object') {
                var theWebSocket = window.WebSocket ? window.WebSocket : window.MozWebSocket;
                return function (wsURL) {

                    function WindowWebSocketWrapper(wsURL) {
                        this.mSocket = new theWebSocket(wsURL);
                    }

                    WindowWebSocketWrapper.prototype.close = function () {
                        this.mSocket.close();
                    };

                    WindowWebSocketWrapper.prototype.onmessage = function (onMessageFunction) {
                        this.mSocket.onmessage = onMessageFunction;
                    };

                    WindowWebSocketWrapper.prototype.onopen = function (onOpenFunction) {
                        this.mSocket.onopen = onOpenFunction;
                    };
                    WindowWebSocketWrapper.prototype.onclose = function (onCloseFunction) {
                        this.mSocket.onclose = onCloseFunction;
                    };

                    WindowWebSocketWrapper.prototype.send = function (message) {
                        this.mSocket.send(message);
                    };

                    return new WindowWebSocketWrapper(wsURL);
                }
            }
            if (typeof weex === 'object') {
                // this is weex env : https://weex.apache.org/zh/docs/modules/websockets.html
                try {
                    console.log("test webSocket for weex");
                    var ws = weex.requireModule('webSocket');
                    console.log("find webSocket for weex:" + ws);
                    return function (wsURL) {
                        try {
                            ws.close();
                        } catch (e) {
                        }
                        ws.WebSocket(wsURL, '');
                        return ws;
                    }
                } catch (e) {
                    console.log(e);
                    //ignore
                }
            }
            //TODO support ReactNative
            if (typeof WebSocket === 'object') {
                return function (wsURL) {
                    return new theWebSocket(wsURL);
                }
            }
            // weex 和 PC环境的websocket API不完全一致,所以做了抽象兼容
            throw new Error("the js environment do not support websocket");
        };

        SekiroClient.prototype.connect = function () {
            console.log('sekiro: begin of connect to wsURL: ' + this.wsURL);
            var _this = this;
            // 不check close,让
            // if (this.socket && this.socket.readyState === 1) {
            //     this.socket.close();
            // }
            try {
                this.socket = this.webSocketFactory(this.wsURL);
            } catch (e) {
                console.log("sekiro: create connection failed,reconnect after 2s");
                setTimeout(function () {
                    _this.connect()
                }, 2000)
            }

            this.socket.onmessage(function (event) {
                _this.handleSekiroRequest(event.data)
            });

            this.socket.onopen(function (event) {
                console.log('sekiro: open a sekiro client connection')
            });

            this.socket.onclose(function (event) {
                console.log('sekiro: disconnected ,reconnection after 2s');
                setTimeout(function () {
                    _this.connect()
                }, 2000)
            });
        };

        SekiroClient.prototype.handleSekiroRequest = function (requestJson) {
            console.log("receive sekiro request: " + requestJson);
            var request = JSON.parse(requestJson);
            var seq = request['__sekiro_seq__'];

            if (!request['action']) {
                this.sendFailed(seq, 'need request param {action}');
                return
            }
            var action = request['action'];
            if (!this.handlers[action]) {
                this.sendFailed(seq, 'no action handler: ' + action + ' defined');
                return
            }

            var theHandler = this.handlers[action];
            var _this = this;
            try {
                theHandler(request, function (response) {
                    try {
                        _this.sendSuccess(seq, response)
                    } catch (e) {
                        _this.sendFailed(seq, "e:" + e);
                    }
                }, function (errorMessage) {
                    _this.sendFailed(seq, errorMessage)
                })
            } catch (e) {
                console.log("error: " + e);
                _this.sendFailed(seq, ":" + e);
            }
        };

        SekiroClient.prototype.sendSuccess = function (seq, response) {
            var responseJson;
            if (typeof response == 'string') {
                try {
                    responseJson = JSON.parse(response);
                } catch (e) {
                    responseJson = {};
                    responseJson['data'] = response;
                }
            } else if (typeof response == 'object') {
                responseJson = response;
            } else {
                responseJson = {};
                responseJson['data'] = response;
            }


            if (Array.isArray(responseJson)) {
                responseJson = {
                    data: responseJson,
                    code: 0
                }
            }

            if (responseJson['code']) {
                responseJson['code'] = 0;
            } else if (responseJson['status']) {
                responseJson['status'] = 0;
            } else {
                responseJson['status'] = 0;
            }
            responseJson['__sekiro_seq__'] = seq;
            var responseText = JSON.stringify(responseJson);
            console.log("response :" + responseText);


            if (responseText.length < 1024 * 6) {
                this.socket.send(responseText);
                return;
            }

            if (this.base64) {
                responseText = this.base64Encode(responseText)
            }

            //大报文要分段传输
            var segmentSize = 1024 * 5;
            var i = 0, totalFrameIndex = Math.floor(responseText.length / segmentSize) + 1;

            for (; i < totalFrameIndex; i++) {
                var frameData = JSON.stringify({
                    __sekiro_frame_total: totalFrameIndex,
                    __sekiro_index: i,
                    __sekiro_seq__: seq,
                    __sekiro_base64: this.base64,
                    __sekiro_is_frame: true,
                    __sekiro_content: responseText.substring(i * segmentSize, (i + 1) * segmentSize)
                }
                );
                console.log("frame: " + frameData);
                this.socket.send(frameData);
            }
        };

        SekiroClient.prototype.sendFailed = function (seq, errorMessage) {
            if (typeof errorMessage != 'string') {
                errorMessage = JSON.stringify(errorMessage);
            }
            var responseJson = {};
            responseJson['message'] = errorMessage;
            responseJson['status'] = -1;
            responseJson['__sekiro_seq__'] = seq;
            var responseText = JSON.stringify(responseJson);
            console.log("sekiro: response :" + responseText);
            this.socket.send(responseText)
        };

        SekiroClient.prototype.registerAction = function (action, handler) {
            if (typeof action !== 'string') {
                throw new Error("an action must be string");
            }
            if (typeof handler !== 'function') {
                throw new Error("a handler must be function");
            }
            console.log("sekiro: register action: " + action);
            this.handlers[action] = handler;
            return this;
        };

        SekiroClient.prototype.encodeWithBase64 = function () {
            this.base64 = arguments && arguments.length > 0 && arguments[0];
        };

        SekiroClient.prototype.base64Encode = function (s) {
            if (arguments.length !== 1) {
                throw "SyntaxError: exactly one argument required";
            }

            s = String(s);
            if (s.length === 0) {
                return s;
            }

            function _get_chars(ch, y) {
                if (ch < 0x80) y.push(ch);
                else if (ch < 0x800) {
                    y.push(0xc0 + ((ch >> 6) & 0x1f));
                    y.push(0x80 + (ch & 0x3f));
                } else {
                    y.push(0xe0 + ((ch >> 12) & 0xf));
                    y.push(0x80 + ((ch >> 6) & 0x3f));
                    y.push(0x80 + (ch & 0x3f));
                }
            }

            var _PADCHAR = "=",
                _ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
                _VERSION = "1.1";//Mr. Ruan fix to 1.1 to support asian char(utf8)

            //s = _encode_utf8(s);
            var i,
                b10,
                y = [],
                x = [],
                len = s.length;
            i = 0;
            while (i < len) {
                _get_chars(s.charCodeAt(i), y);
                while (y.length >= 3) {
                    var ch1 = y.shift();
                    var ch2 = y.shift();
                    var ch3 = y.shift();
                    b10 = (ch1 << 16) | (ch2 << 8) | ch3;
                    x.push(_ALPHA.charAt(b10 >> 18));
                    x.push(_ALPHA.charAt((b10 >> 12) & 0x3F));
                    x.push(_ALPHA.charAt((b10 >> 6) & 0x3f));
                    x.push(_ALPHA.charAt(b10 & 0x3f));
                }
                i++;
            }


            switch (y.length) {
                case 1:
                    var ch = y.shift();
                    b10 = ch << 16;
                    x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _PADCHAR + _PADCHAR);
                    break;

                case 2:
                    var ch1 = y.shift();
                    var ch2 = y.shift();
                    b10 = (ch1 << 16) | (ch2 << 8);
                    x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _ALPHA.charAt((b10 >> 6) & 0x3f) + _PADCHAR);
                    break;
            }

            return x.join("");
        };

    </script>
    <script type="text/javascript">
        alert('hook success');
        function guid() {
            function S4() {
                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
            }

            return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
        }

        var client = new SekiroClient("wss://sekiro.virjar.com/websocket?group=ws-group-youzheng&clientId=" + guid());
        client.registerAction("encode", function (request, resolve, reject) {
            resolve(window._$c4(request['encode_str']));
        });

        client.registerAction("executeJs", function (request, resolve, reject) {
            var code = request['code'];
            if (!code) {
                reject("need param:{code}");
                return;
            }

            code = "return " + code;

            console.log("executeJs: " + code);

            try {
                var result = new Function(code)();
                resolve(result);
            } catch (e) {
                reject("error: " + e);
            }

        });
    </script>

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐