一、什么是vmp?

vmp全称虚拟机保护(Virtual Machine Protection),是一种用于软件保护和防逆向工程的技术,主要通过使用虚拟机来执行代码,以保护源代码、降低代码可读性、提升逆向/抄袭难度。

二、基础vm

这里只结合代码来看代码混淆层面的vm实现,不深入讲语言应用层面的复杂虚拟机(jvm、Dalvik等)。

原代码:

var a = 15;
var b = 18;
var c = a + b;
console.log(c);

栈式虚拟机

栈式虚拟机是一种使用栈(stack)来管理操作数(operand)和中间结果(ir)的虚拟机架构。所有的操作都基于栈进行,指令通过栈来传递操作数。

栈式虚拟机的设计使其指令集非常简洁,因为所有操作都隐含地使用栈顶元素作为操作数。

  • 使用栈来存储和操作数据。

  • 指令通过栈来传递操作数。

  • 优点是指令集简洁,容易实现。

  • 缺点是指令间的数据传递需要通过栈,效率可能较低。

使用栈式虚拟机实现原代码如下:

function stack_vmp_main(op_list){
    var start_ip = 0;
    var ret;
    var stack = [];
    for (;!ret;){
        op = op_list[start_ip++];
        ret = run_zhiling(op, op_list, stack);
    }
    function run_zhiling(op, op_list, stack){
        switch(op){
            case 0:
                stack.push(op_list[start_ip++])
                break
            case 1:
                stack.push(stack.pop() + stack.pop())
                break
            case 2:
                console.log(stack.pop())
                break
            case 3:
                return true
        }
    }
}

stack_vmp_main([0,15,0,18,1,2,3]);

可以看到,栈式虚拟机的指令集简单易懂,很容易看出vm是如何实现以及各指令的作用。

寄存器式虚拟机

寄存器式虚拟机通过寄存器(register)来管理操作数和中间结果。寄存器是一组高速存储单元,操作数在这些寄存器之间传递和操作,避免了频繁的内存访问。与栈式虚拟机相比,寄存器式虚拟机的指令集更为复杂,因为每条指令都需要指定操作数所在的寄存器位置。

寄存器式虚拟机的优缺点:

  • 优点:寄存器之间的操作通常比栈操作更高效,减少了指令的数量。

  • 缺点:指令集更复杂,实现难度较大。

使用寄存器式虚拟机实现原代码如下:

function register_vmp_main(op_list){
    var start_ip = 0;
    var ret;
    var linshi;
    for (;!ret;){
        op = op_list[start_ip++];
        ret = run_zhiling(op, op_list);
    }
    function run_zhiling(op, op_list){
        switch(op){
            case 0:
                linshi = new Array(op_list[start_ip++])
                break
            case 1:
                linshi[op_list[start_ip++]] = void 0;
                break
            case 2:
                linshi[op_list[start_ip++]] = op_list[start_ip++];
                break
            case 3:
                linshi[op_list[start_ip++]] = linshi[op_list[start_ip++]] + linshi[op_list[start_ip++]];
                break;
            case 4:
                console.log(linshi[op_list[start_ip++]]);
                break;
            case 5:
                return true
        }
    }
}
register_vmp_main([0,255,1,0,1,1,1,2,2,0,15,2,1,18,3,2,0,1,4,2,5])

由于js中array长度可变以及变量即插即用的特性,指令0(初始化寄存器)和指令1(初始化变量)大部分情况下可以省去,但是通常不会简化掉这些指令,因为批量初始化可以更方便后续的寻址引用。

vm扩展

以上两种虚拟机基本为所有vm的基础,衍生出的堆式虚拟机、中间语言虚拟机、栈帧式虚拟机等复杂的虚拟机都是将它们改进/结合而来。

这里拿中间语言式虚拟机举例:

中间语言式虚拟机

顾名思义,使用一种语言运行另一种语言的code即为中间语言(Intermediate Language)式虚拟机,比如wasm。它具有平台独立性和跨语言互操作性,使得编译后的代码可以在多个平台上运行。同时,由于执行过程中加入了额外的抽象语言,性能方面也会受到较大的损耗。

我们反混淆中常用到的AST便可以理解为中间语言,babel的generate就是这门中间语言的反汇编,而parser.parse则可以理解为增加了一层中间语言vmp的混淆

使用栈式虚拟机来实现IL虚拟机实现原代码如下:

// ast版
function il_vmp_main(ast_tree) {
    function get_op_list(node, il_zhiling) {
        if (!il_zhiling) {
            il_zhiling = []
        }
        switch (node.type) {
            case 'Program':
                node.body.forEach(statement => get_op_list(statement, il_zhiling));
                break;
            case 'VariableDeclaration':
                node.declarations.forEach(declarator => get_op_list(declarator, il_zhiling));
                break;
            case 'VariableDeclarator':
                il_zhiling.push({ op: 'DECLARE', name: node.id.name });
                get_op_list(node.init, il_zhiling);
                il_zhiling.push({ op: 'STORE', name: node.id.name });
                break;
            case 'Literal':
                il_zhiling.push({ op: 'PUSH', value: node.value });
                break;
            case 'Identifier':
                il_zhiling.push({ op: 'LOAD', name: node.name });
                break;
            case 'BinaryExpression':
                get_op_list(node.left, il_zhiling);
                get_op_list(node.right, il_zhiling);
                il_zhiling.push({ op: 'ADD' });
                break;
            case 'ExpressionStatement':
                get_op_list(node.expression, il_zhiling);
                break;
            case 'CallExpression':
                node.arguments.forEach(arg => get_op_list(arg, il_zhiling));
                il_zhiling.push({ op: 'CALL', callee: node.callee.property.name });
                break;
            default:
                throw new Error(`Unsupported node type: ${node.type}`);
        }
        return il_zhiling;
    }
    function run_zhiling(il_zhiling) {
        const stack = [];
        const variables = {};
        for (let ip = 0; ip < il_zhiling.length; ip++) {
            const instr = il_zhiling[ip];
            switch (instr.op) {
                case 'DECLARE':
                    variables[instr.name] = 0;
                    break;
                case 'PUSH':
                    stack.push(instr.value);
                    break;
                case 'STORE':
                    variables[instr.name] = stack.pop();
                    break;
                case 'LOAD':
                    stack.push(variables[instr.name]);
                    break;
                case 'ADD':
                    stack.push(stack.pop() + stack.pop());
                    break;
                case 'CALL':
                    if (instr.callee === 'log') {
                        console.log(stack.pop());
                    } else {
                        throw new Error(`Unsupported function call: ${instr.callee}`);
                    }
                    break;
                default:
                    throw new Error(`Unsupported operation: ${instr.op}`);
            }
        }
    }
    il_zhiling_list = get_op_list(ast_tree);
    run_zhiling(il_zhiling_list);
}


ast = {
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": { "type": "Identifier", "name": "a" },
                    "init": { "type": "Literal", "value": 15, "raw": "15" }
                }
            ],
            "kind": "var"
        },
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": { "type": "Identifier", "name": "b" },
                    "init": { "type": "Literal", "value": 18, "raw": "18" }
                }
            ],
            "kind": "var"
        },
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": { "type": "Identifier", "name": "c" },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "+",
                        "left": { "type": "Identifier", "name": "a" },
                        "right": { "type": "Identifier", "name": "b" }
                    }
                }
            ],
            "kind": "var"
        },
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "CallExpression",
                "callee": { "type": "MemberExpression", "object": { "type": "Identifier", "name": "console" }, "property": { "type": "Identifier", "name": "log" } },
                "arguments": [{ "type": "Identifier", "name": "c" }]
            }
        }
    ]
}
il_vmp_main(ast);

将get_op_list的结果单独提取出来,便可以看做一套自己的汇编语言:

// 自定义版
il_list = [
    {
      op: "DECLARE",
      name: "a",
    },
    {
      op: "PUSH",
      value: 15,
    },
    {
      op: "STORE",
      name: "a",
    },
    {
      op: "DECLARE",
      name: "b",
    },
    {
      op: "PUSH",
      value: 18,
    },
    {
      op: "STORE",
      name: "b",
    },
    {
      op: "DECLARE",
      name: "c",
    },
    {
      op: "LOAD",
      name: "a",
    },
    {
      op: "LOAD",
      name: "b",
    },
    {
      op: "ADD",
    },
    {
      op: "STORE",
      name: "c",
    },
    {
      op: "LOAD",
      name: "c",
    },
    {
      op: "CALL",
      callee: "log",
    },
]
function il2_vmp_main(il_zhiling){
    const stack = [];
    const variables = {};
    for (let ip = 0; ip < il_zhiling.length; ip++) {
        const instr = il_zhiling[ip];
        switch (instr.op) {
            case 'DECLARE':
                variables[instr.name] = 0;
                break;
            case 'PUSH':
                stack.push(instr.value);
                break;
            case 'STORE':
                variables[instr.name] = stack.pop();
                break;
            case 'LOAD':
                stack.push(variables[instr.name]);
                break;
            case 'ADD':
                stack.push(stack.pop() + stack.pop());
                break;
            case 'CALL':
                if (instr.callee === 'log') {
                    console.log(stack.pop());
                } else {
                    throw new Error(`Unsupported function call: ${instr.callee}`);
                }
                break;
            default:
                throw new Error(`Unsupported operation: ${instr.op}`);
        }
    }
}
il2_vmp_main(il_list)

IL虚拟机实例demo:http://43.153.41.76:8602/

(需要注意的是,拿寄存器式虚拟机也可实现IL的运行,并不局限于栈式虚拟机)

三、几个vmp分析

腾讯tdc

略,栈式虚拟机的经典。

快手sig3

快手创作者平台和h5部分接口sig3签名,js如下:

https://s2-111422.kwimgs.com/kos/nlav111422/h5-share/assets/index-CkeXlguR.js

vmp核心代码

!function(e, n) {
    "object" === ("undefined" == typeof exports ? "undefined" : Rr(exports)) && "object" === ("undefined" == typeof module ? "undefined" : Rr(module)) ? module.exports = n() : "function" == typeof define && define.amd ? define([], n) : "object" === ("undefined" == typeof exports ? "undefined" : Rr(exports)) ? exports.Jose = n() : e.Jose = n()
}(window, (function() {
    return t = {},
    e.m = n = [function(e, n) {
        (function() {
            var e = Object.create
              , t = Array.isArray;
            n.prototypeOf = function(e) {
                return e.constructor.prototype
            }
            ,
            n.create = e,
            n.hasProp = function(e, n) {
                return Object.prototype.hasOwnProperty.call(e, n)
            }
            ,
            n.isArray = t,
            n.defProp = function(e, n, t) {
                return Object.defineProperty(e, n, t)
            }
        }
        ).call(this)
    }
    , function(e, n) {
        (function() {
            function e(e) {
                this.elements = e,
                this.index = 0
            }
            e.prototype.next = function() {
                if (this.index >= this.elements.length)
                    throw new Error("array over");
                return this.elements[this.index++]
            }
            ,
            n.ArrayIterator = e
        }
        ).call(this)
    }
    , function(e, n, t) {
        function o(e) {
            return (o = "function" == typeof Symbol && "symbol" === Rr(Symbol.iterator) ? function(e) {
                return void 0 === e ? "undefined" : Rr(e)
            }
            : function(e) {
                return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : void 0 === e ? "undefined" : Rr(e)
            }
            )(e)
        }
        (function() {
            var e = {}.hasOwnProperty
              , a = t(0).isArray
              , r = (i.prototype.run = function() {
                for (var e = this.callStack[this.depth], n = e.error; 0 <= this.depth && e && !this.paused; )
                    if ((e = n ? this.unwind(n) : e).run(),
                    (n = e.error)instanceof Error && this.injectStackTrace(n),
                    e.done()) {
                        if (e.guards.length) {
                            var t = e.guards.pop();
                            if (t.finalizer) {
                                e.ip = t.finalizer,
                                e.exitIp = t.end,
                                e.paused = !1;
                                continue
                            }
                        }
                        !e.construct || "object" !== (t = o(this.rv)) && "function" !== t && (this.rv = e.scope.get(0)),
                        (e = this.popFrame()) && !n && (e.evalStack.push(this.rv),
                        this.rv = void 0)
                    } else
                        n = (e = this.callStack[this.depth]).error;
                if (this.timedOut() && (n = new Error(this),
                this.injectStackTrace(n)),
                n)
                    throw n
            }
            ,
            i.prototype.unwind = function(e) {
                for (var n = this.callStack[this.depth]; n; ) {
                    n.error = e;
                    var t = n.ip - 1
                      , o = n.guards.length;
                    if (o && (o = n.guards[o - 1]).start <= t && t <= o.end) {
                        if (null !== o.handler)
                            if (t <= o.handler)
                                n.evalStack.push(e),
                                n.error = null,
                                n.ip = o.handler;
                            else {
                                if (!(o.finalizer && n.ip <= o.finalizer)) {
                                    n = this.popFrame();
                                    continue
                                }
                                n.ip = o.finalizer
                            }
                        else
                            n.ip = o.finalizer;
                        return n.paused = !1,
                        n
                    }
                    n = this.popFrame()
                }
                throw e
            }
            ,
            i.prototype.injectStackTrace = function(e) {
                var n, t, o = void 0, r = void 0, i = void 0, s = void 0, l = void 0, c = [], u = 0;
                for (this.depth > this.maxTraceDepth && (u = this.depth - this.maxTraceDepth),
                r = i = n = this.depth,
                t = u; n <= t ? i <= t : t <= i; r = n <= t ? ++i : --i)
                    "<anonymous>" === (s = (o = this.callStack[r]).script.name) && o.fname && (s = o.fname),
                    c.push({
                        at: {
                            name: s,
                            filename: o.script.filename
                        },
                        line: o.line,
                        column: o.column
                    });
                if (e.trace) {
                    for (l = e.trace; a(l[l.length - 1]); )
                        l = l[l.length - 1];
                    l.push(c)
                } else
                    e.trace = c;
                return e.stack = e.toString()
            }
            ,
            i.prototype.pushFrame = function(e, n, t, o, a, r, i) {
                if (null == r && (r = "<anonymous>"),
                null == i && (i = !1),
                this.checkCallStack())
                    return (t = new p(t,e.localNames,e.localLength)).set(0, n),
                    i = new s(this,e,t,this.realm,r,i),
                    a && i.evalStack.push(a),
                    o && i.evalStack.push(o),
                    this.callStack[++this.depth] = i
            }
            ,
            i.prototype.checkCallStack = function() {
                return this.depth !== this.maxDepth || (this.callStack[this.depth].error = new Error("maximum call stack size exceeded"),
                this.pause(),
                !1)
            }
            ,
            i.prototype.popFrame = function() {
                var e = this.callStack[--this.depth];
                return e && (e.paused = !1),
                e
            }
            ,
            i.prototype.pause = function() {
                return this.paused = this.callStack[this.depth].paused = !0
            }
            ,
            i.prototype.resume = function(e) {
                if (this.timeout = null != e ? e : -1,
                this.paused = !1,
                this.callStack[this.depth].paused = !1,
                this.run(),
                !this.paused)
                    return this.rexp
            }
            ,
            i.prototype.timedOut = function() {
                return 0 === this.timeout
            }
            ,
            i.prototype.send = function(e) {
                return this.callStack[this.depth].evalStack.push(e)
            }
            ,
            i.prototype.done = function() {
                return -1 === this.depth
            }
            ,
            i);
            function i(e, n) {
                this.realm = e,
                this.timeout = null != n ? n : -1,
                this.maxDepth = 1e3,
                this.maxTraceDepth = 50,
                this.callStack = [],
                this.evalStack = null,
                this.depth = -1,
                this.yielded = this.rv = void 0,
                this.paused = !1,
                this.r1 = this.r2 = this.r3 = null,
                this.rexp = null
            }
            var s = (l.prototype.run = function() {
                for (var e = this.script.instructions; this.ip !== this.exitIp && !this.paused && 0 !== this.fiber.timeout; )
                    this.fiber.timeout--,
                    e[this.ip++].exec(this, this.evalStack, this.scope, this.realm);
                0 === this.fiber.timeout && (this.paused = this.fiber.paused = !0);
                var n = this.evalStack.len();
                if (!this.paused && !this.error && 0 !== n)
                    throw new Error("Evaluation stack has " + n + " items after execution")
            }
            ,
            l.prototype.done = function() {
                return this.ip === this.exitIp
            }
            ,
            l.prototype.setLine = function(e) {
                this.line = e
            }
            ,
            l.prototype.setColumn = function(e) {
                this.column = e
            }
            ,
            l);
            function l(e, n, t, o, a, r) {
                this.fiber = e,
                this.script = n,
                this.scope = t,
                this.realm = o,
                this.fname = a,
                this.construct = null != r && r,
                this.evalStack = new c(this.script.stackSize,this.fiber),
                this.ip = 0,
                this.exitIp = this.script.instructions.length,
                this.paused = !1,
                this.finalizer = null,
                this.guards = [],
                this.rv = void 0,
                this.line = this.column = -1
            }
            var c = (u.prototype.push = function(e) {
                if (this.idx === this.array.length)
                    throw new Error("maximum evaluation stack size exceeded");
                return this.array[this.idx++] = e
            }
            ,
            u.prototype.pop = function() {
                return this.array[--this.idx]
            }
            ,
            u.prototype.top = function() {
                return this.array[this.idx - 1]
            }
            ,
            u.prototype.len = function() {
                return this.idx
            }
            ,
            u.prototype.clear = function() {
                return this.idx = 0
            }
            ,
            u);
            function u(e, n) {
                this.fiber = n,
                this.array = new Array(e),
                this.idx = 0
            }
            var p = (d.prototype.get = function(e) {
                return this.data[e]
            }
            ,
            d.prototype.set = function(e, n) {
                return this.data[e] = n
            }
            ,
            d.prototype.name = function(n) {
                var t = void 0
                  , o = this.names;
                for (t in o)
                    if (e.call(o, t) && o[t] === n)
                        return parseInt(t);
                return -1
            }
            ,
            d);
            function d(e, n, t) {
                this.parent = e,
                this.names = n,
                this.data = new Array(t)
            }
            var h = (f.prototype.get = function(e) {
                return this.object[e]
            }
            ,
            f.prototype.set = function(e, n) {
                return this.object[e] = n
            }
            ,
            f.prototype.has = function(e) {
                return e in this.object
            }
            ,
            f);
            function f(e, n) {
                this.parent = e,
                this.object = n
            }
            n.Fiber = r,
            n.Scope = p,
            n.WithScope = h
        }
        ).call(this)
    }
    , function(e, n, t) {
        (t = new (t(4))).eval('["<script>",0,[[76,1]č77,5572Ĕ[2ēĕėĠď5ċ,falseĝ24Ē,nullĝ16]ĝĀĂĄĆĈĊāanonymousĉčĤ,28ħĩīĝ3ČČ4Ĝčģ7ĥ2ėĨĪĬčŕĦ4ģśŝđŠœţŖ,40ĶĝŜŎĥūŢ[ŤČ39ŲĕŝįŷŔŮ38žō23őšƃĦ37ƇŴ22ƋŬŹƄ5Ƒŝ1ƕŸź,3ŧųŝČƂŭƎűŚƣĒ9ƝƍČ2ŽƪſĒŐƦƗĦŏƚĒşŒƞŮŞƻķƮƧưĸƳō1ŶƾƯŵǂƁnjDžŎ4ǂƊƷƟƉǂƔǗǀƢƴ1ƜǜƹƩ[ŨĒƥǑƸČ1ƲǥƫƭǢǫƆLjŴƶǩƟ1ƐǴĥƽƌǒķƻŪǷŮNJƻNjǾǪĒǔǻůDŽȉ13ƻǖȃĦ1řǮƴǛȔǫǞljȎǸǤǦǨȈƟǭģ2ȣƖ7ŖĭƭǫĭLJȉȑŚȱĐĒųėǙčƱĦǭȫ,"$encode"ȸĒȳĞȮƠĶŧȡɌ1ȋĞǺǥȵė3ȱȼŻƙĕČɁɃĄyćɉġǎȻɎɓɑĶɓɕǁȴɊ4ǭɝůǤȿɁɇɤɦɊƅȭƎɐĶɒĶɰɗȀɨĚųƜǤƟɕʈďėȫʋȷǒɟɘɊ8ɸƜȗƟȱʐĖǧɗȿǬ5609ǹ7ɌʡǠdzďČ-NJɴ54Ű5śĢŕųʱɛ6ʩ335ȾĢŦʼ,9ŏ89ęĐɊNJśŻ26ʿˌ9ɸĢ6ˑ,ʲŦǹ6ȫʨˏʫˇȨ4ʵĚƏȗʭ8ɕȿʲ99ˋŞɴśįʯʭ˲ˇ10ʶ0Ȗ˔ʬȹˢɠĒƓŕǠ0ʵɊƓɸČ96ˋʧ88˫ȹʻ̅ɔ˿̑9ˁʣȹˆ̙ŕ85ʚ˃˄ŵ˫ǫ3ƱƏ8˂̖Ŏ6ʬǫʿŘʥɱʑŎ7ˮǫ06Ʊ64˭ˮ˷ųįʝŮʇəŎ̝˻ȱƟʯʠȹƓ̽ĒǠ̳5Į0˸͔˂˻ˋ9ʶ̡̼̺͕ȵŻ˽̝Ɖ47ʹŎ2ʊ̅żğ0ʨ˩̌˓˸̏˚˲̳ͮ͹7ͻĒ5͜9ȫ˱̂Ͱˋ˥˧ɛ˂̼͝Ŏŕ̎Ŏ8ȫĐ̬ğ̌ơ˛ɛĮƅ˼ʚΜƓˇơ̊˲ŰͯƉ̬ˇʧěͱŞΒ̪ͬȊͺ8ǬΗΜ5̴Ƭͱ0΂ƅγɛ͖ğ̀π̿̕ΜŜ˥˚̓̔˼ΊƉ8ͨƠĮ̕Ɠ̥˘Γ·Φ̯μ˗Ϊ̌Ű΃ŰƓ͕Ėˮ̠Ǭύ43ͭψϣƱή˽ʚĐƺɨΠȾČĖ0ͱ˃̯ϣɴ˻Įʩ΅Ȗ̱Įʧ˻ę΅θʿЉͭ˛Ǭ̐̿ɴϔϣ̕˥ęŘȨ̔Б͎̅ŏğ̯ϮЃϹ΅υˌƱ̐ʚγʸʗȿ̬ϑȐˠΫ͚ϕˁɛĚ1ˋзƏΦ̥˧ŏͮз̓˻Ȑˉ˕ϒę΃ˁ̕Ŧˋбȹʦ΃˔̳πȨѓŵ΂ˇḚ̇˱Ȗ̥̌΃Ȗ͢Ŝʫěѣ˱˻ż˚Ϙʩ̌˚ΕķˌнƏͶѲΛ̙˲ї˼ʸѲέТʪϾ͙ϛ˓Ϯ΍ʦˌʧрϹ6̋Тʫϵ˃ѻͦ˔бưωŕĮǠѲˎͳͱ·̣ѹҏκͳϯĚ͂ĖΫʿюʸʧҐθ̟̻̄ʰƠʧ0̰ʚϪ̻ȨˇŰ͢м˗̨Şơ˥ŜŘˁʶ̼̌˛ϮϾŞʫҽŞҀ̅΅̶ϔ͓̻˚ή˃̿̊ʦӍͮˇ̔ż·ΗҴβ˛ˌθżмӍѱ̢ƓϘФ̌ʚεNJѸϾ̿ϒӮ˻̼΂ѩ7Ӆѐ˻ˌę̀ϧӷ͟ТϾŦϵȅϹ˭ϕϧџ˽͚ӷͲҷƉѯ͚ŘӔϔ΃ĮŏϮҫԡԄӈʦӓнӪˋΕ̬˽ҺɔԩϹ˗Ξ̬͚ͫ˺ĕį͂ǍΥɲʉнƆĺāăąćŋ"rĊĎȨ,truŸȿǤɶȧ͐ǀĶϕȧɎՙǍɓȵđģŐϛԱʀưɾнʀʜɌĮİ"jɧȉȗӛˁ՞ƹɾϳɪİȐĭįƜ"dմʎ՜ɾءէŰխ,ɗ˓ʃɮˇ"slicɈĭĥɡ֕֗֙ʖևɨɛչŗɾ΀սČћ˂ɵƭƜɔրŎɀpօŮʟ͌˪թ,ַʓսʜɰցɀDմʡɛʯȼ֮կ׀փִť̺֡ƅѴɾѝֽ֎Ȱ֑Śν֖֔֘մͱĦך֞׌֩׎ׄˌֹҽż֏Ɏ֪Сɍվֱֿ"ֳǍֶɳϊĕʌǍ͋ɳԒҷϛŦկɎɜ͌ёˇՒՔ׼չʜ׆͈ɀbעוՂ̺ӐʔʍŮ͒؄ɛʔ֤,ʯɌ҉ǒؘɳ4ɜ֭ůֹ؞ĝؠȉآɨ˧ȵʜɳґ׮Ɯկɜĝˠ̆ȝʡ͖ͭ˼ؖȽפė̓ذɏخ˭֍ضĭظʺɓɳ̐˻ҥȏȡ͌ʸӹɊ΅˫صׇْ٘نǥɭטʅזčثտŚʆقĘϬ֨ˈ٘ҖȿƷʡʸתİϸĞ؍"؏بŔتɌ͑٪μ؁ȕϛμҙȎٳӇהٷհƔٺםɎة٥ٿؗځ˧ֹٵČڒ[ثڀؒٳˠŔƊ"DatɈċĭƊڅԙţڣڥڧĊȡʄ̅ײarīםĥւpڷڹ٘ˤ̙ͶǤؤۀʣȩՕėę՗ئڌٹٻ٭ڛڝڕڟۊҧďƜɰڑٽړǍح̺ʧśƜʗɶےڔĦ۟ʡѯټɨ˚إվȧ׊gؐҞەֺĮֹ۵׮ښۜڜۧۼ۶ј۬۠ڌئ˼ְւsؐ۩ė͂ȗۥɊ̳ڃŻחǥΊ͙ʔɜڻıijĵؒ˔̲ܗӖȏն͌́۹̱ҐۚʁŚڴ۠Ӣ׸ؑĞܛIJĴ׎ȮĹčĝܵlܜĴܾܽ"nŀaŀtŀڨ"oŀvŀfŀcŀuŀՍĺȐŵʱʜ΀Ĕˋč"ŀʵ˧ŀstڷtupRłdomݐňn݇ɀ˿ۃؿŀۃۃݣuݑɀerݕܺکݞļՉĿɀ܊ŌՐ؇ՕȬ٭ՠǒբųդܑ̼ۗނȉʏܦԼۻِٟ۪ה̘ٸձkؐʗӛΈ֓lɃgthڐĦۤ͌ͮѥتҽΘֹޱ״٪ʫ˛ـϼ޻ˁژɊʫڇ"ުnެޮ޸٤̺ͤ˻޵ĝ̥ظŐΣɨŜ֬ĦҴѨߋȻ޲ֹ̀ȱμ߃Կ٭ɗߢߖڡТęߑ̱ŜܓܲʡŜ؜ʏ١ܖ٣Ȼ׻ߖۅߥĶնʗͥƟʇĶަظđϯࠅֺ̨ĖۯČ޲ہޜߚӤ٭ߴܕ٧ǥ٩۶Ԃק߃̤ى۱ձxմǔĝܻܝ݀ࠧՌŀ݃ɀ݅ɀݲ"֟[Ĕϯށ࠲ޗՇĽՊŀ۳މČދ֊۹ߌƟޑĕޓɨθ܏ɫʙ܇ޠ՗׊ޤǍޙʙٰލȉަ̯͌ν޾ֺ٪ϟߠʙҎҷ߆ޫޭޯ֥ߝߑʯ̑Ęҽ̤ࠌ࡜ࡇسɶɰࡘ˃Лʗڭޜʗ؟ۿĘ࡝߻ޜʟ߾تģʞƇ׽ģܠՁ[ܠثʡϔ࡯ћԢࡷظǤعӝčܠࠏ࢏Ҡࡺت࡛ࡗʙθߏ٭ࡻپǍࢡࡇࠝࢥࢢۇŵࡸ͉ް٪˗یࢲࡇޛٞѢȻ׊ݏتܹ࠶[ܼࠧࣂࠩɀࠫ"࠭"݇ࠤ̻࠵ĔǤ࠸ޅՋݏ࠽ՑՓތޗ՘ࡂƄפࡆ̺ˉʬ׹ǒࡒɨˉߘܔࣥ߁̅ǤܰࢌكϛѶֹΊˌνʐߒ࢙ȹ͌ЕؕࠈٷʡҌ؛Ɋ·ڇӛ̐ܫࣨǥܮɹtoSՒi߈ںձऌऎąऑࢧࣣ׎ࢍҽ̝˸رङ۶ѽࠛ٥ࢿࠥܿࣂࣄՍࣆմĔǹݞݷݷ0ब࠷ބľՋքࣕ࠿࣫ࡁࡨސࣝʕ̺˼ͱЄΒؿࡻڣकऐ࠼ڳ֒ڵfrݭChڷCɆ࠱מւॏ॑॓rॕɇࡦٟࡼա٪ϑϕࡣ߈ࡥࣲߌʭԘӀت͌˼ͭ˥घޘ।̿ۤɎҽ˼ߩش؝९ˏӡڢɀMڦޮޗࡌ߶ڵ֘ilऒւঋ঍ࢨॶ࢝ޖ࡛׵ɨ˼׭Ֆ׺ƑĢͷ߂ঘʨڙࡾčࣷࢌŐњˏঙ͇ޗʭॻঠुॱ࡟Śߵࠖ֯߸।ʮࡀءঝ͗ҿۑপࢋٞʯদ࡫ǬॺNJࢵ޿ঘNJεߤڜћǠ߫ীৌԛȿɕڗ۝ৌࡡɶɓऊɡcड़फ़eA࣋Ȼܛ"ৡ॔ॖ৥׃Ģķ˛۸٥ॅʪরমԵҷ৏৙঱К৔ৼ͖৘߿Ίʮ՝ॴম৏ঢ়ࠕי৪ढ़৬০ܳձ਌ৣ৭প˗͏تৄͯȖ׷ࠐˏОήਁਞۺܐঘğνਡ৚ुğڇȗࢎ৯׭ਉܭ्ࡢ਒਎঎ɀਵɇਔਥ٬ҷʯчথਚͬ৶Ģԝਠت৑Ɖࣧॾਥͥৗੈਆ੅ࢎईਮ̆Җ਱उਲ਼ɹਹ৤ਏक़ਸৢਸ਼ߑਚȐइੌ঱ڊਝৌռࢁߌۺঈশ࠘ʘ੫੦͒ࢃ٥ࢅ۔Șǥࠄ࢙đό੾Ƶਃʨੋʇ੷ڜ੹ߙࢇ੽ࢌđߧઍƬॺͱי߇߉ॠيਥߟהॅ҈८٥ਂਞ˔ࢤڜɕȥ।ŏ৊ٮਥ৽ਾ੐ਥচժયڇ҂ਪوڮঃঅڲਊ֓powऒڎઽિॿਥ઀ॽજĖॳ٥઒ּ࢞ૅٗોડ׆؂੮঵ɯĭ߹ुԹ֍ॅո૊તˏ̬੦৑̬ਅ૑ঘΚ঳Ğ؃ૡۺˁસࡥইয়ɀflooހਐख़૶૸৮ɋਜৃૡ͜૟ӌ૨˿ߜ૚ҺણଅਪѬ٭૬঴઻٨૘।ϯণ̨੥ϻ੧৻ʭ˂ӏƱɴˠƱࢰૡ։છଥؾߐ૨ড়׫ૡżଋॺˁॸĦଐ٠଒ࠗଔ۶еੋȱࠣ࠵नࠦऩ݄݆݈ࠪŀ݋ɀݍ࣌ʮ࣎ऴՈशŀֳहࣗ঻ޜޏȲפȯގࣛ׍ڟࣞଝচįࢠ।Ř०ક३٭ઘȏগुʶ޽Ǎষ੻ʭʶ߯ୱ୳৻՘ߡࡪŐЎˏ˧΃࡛࢈୾Ћގ৑ؤࡉİৄ୻֎ਚͭੋ׽୳࢝՘ॺؽ४ऽ૫୾ࠒୖஆʿଈ੯ܮୱ̹ӛɔਈ՟ަઇࠀȄઌஂઁؚઁ஄୭̔૜୰ઌ୳ਖஅ୾ˉੇথ஋NJଘ΅ஏ৑ʸৱପु΅ণ޹ঘ΅ଳ֮ɵ׊ݮˏٴ۹ூ਽ȿɗଌʭаଈ֐਩௙௜஛௑ࣾǫ߿ॺԟ௛॰͚ஈ֮،ձૃ௝Ģچ௓௑ࡡʤଽ௥࣪૎஘௯΄Ͼ௲ொٝଭ଑ਲ٢ଓস଺ʵࢮŘ̐͢ěצொث՘ூͥɶަ૖߷௺[ࡹੱହੳ୭ఎ௠Śநઉ௣ઋƇ࢚ʗܠɔॅʦεϛNJரࡳଡ଼ࠍ।ҋણ௙ॼఱ஗ࠁఴঔ઱ெ̼णఘڞఝ௙Ёͳ௑ࢣࠓలપౄ௰ஷҷ௥லొ఺Ȅٔ௑ϝӗ౗ଳࡧూȄŧ॰ʧ͖ౠ௽౓ౝȕܥˏ˚˛ҴķҶహ౦̫।χ୕Ǹவ˙ূ஦౩৲৹ɕ৅Ő૛ঘ̳૓ȕਃ܎۹మ͂ࢵ֮֏׊AࠢಅॲގಈЧୖਚҐ؜ࢺु͂ௌŎࡎƠঃಏ౩࣭఑ಢܫʜ܏ଽಅ࢘ఠಚࡶࢬ஗ۓȕ૙ʭଢ૪಴્ࢹɜ׊y૾ķૉଏ౩৸ɶ౬̑؜ॅˠಜ੗įैಡǿ܌Ē̑࡯ಙǸࡄಚ౏՘ణ̆౲͢ಇ୰౟౩ࢸɎଘˡ۹ௗਃȫεఊଡ఍ତǿౖঘͮ౛੧દ଺ͮઅ۞।ě߯՚ઊ೵̹՘౬఍۹ਿ೧੩ৗଠఌଣ߬޺଺̀ণ࢈ఢழథLjࢊ਽ܠơॅ̼ଚమԁు௟ʭŜஈళ௻ǹسഇఋଢటࡹഠ௘ࡕബഢاంਖ਼ఄସఆ్ƼѪ̙ˣಸॹ५ĢĖೲ೦഻ఉഈഩ೭ुĐࢮਚĐೲࠔఃউവɖ೹టȿ̱ǹਰഽ஗ഠೡۨੑƵْധ೫ഊথൖԄΕ൥౒ޜൎള൐஡ಳĢθюˏЏഞ॰̕؜ǭ੕нȺ౐൅೬ഋঘӿేඁਣ൙ଶ൏ఛശண˭ӏ൳ଯ౥ട൰ଌ՘ۄਃԔӀൾൣఙൖ̯ϕॺХుடਲ਼൮।ϔ͖৑ϔരࡴ൳స՟ģൺϔଚ೪ഉപගಮҷඨԛ౯ජഴඤ଺̔ࢮ൶ࢫ൪ാƵ೗՟ਭඖ஺̅ඳെ඀ुПή൳ߕහലੰ૗ඊ॰ʩΕ౬˗ಃ౜එƬࠖ՘ॢঘІ඘നൿ൤ˏ˗ڇෝଊ౥඼൭ੲணͣ۹ʏ഑ǿʏࠃదđ௟ࢍౘु̝߯৘थčୀधࠦࣅ݂ୄ࠮୆ɀୈ"ݍ૴ݐݒŀiŀআ߆ݣ࠻ŀքୋޖࣀ࣏ĝ࣑୐फ୓؈ऻ୛౔ܬౄ୚ୖో౨ࡅी˹দɶൖ̐രɌ΅෬஥պ෦ॼв഼ุอଌੀఙදฆࣁୁ์ܿࠩୋƜย୎࠹ކ"಼ศࣘ઩ୗࡃिਚ˱ਬ෬ก੘˹൘վۤٹ݃̌ͶӹϒͶೲ๭ఀ๦ְڎ๩থ๭ঈൕЉ˿߯๰ಃ֮๧ױ๵ఙ؟๊จܽୂୋƊ๒࣐व࠺ɀಎ๘୕ࣚฬईʠୟ̃ඏ৹๪ఓࡊϹҺٵ๿๳फປ՝Ϋͪ஖ͦ˽ದ΄ຢฌߑ๭ദưћњণະ௪ຬࢻກմએାࣀຆࠨૹभຊ࣌ຌ୏ຎ"િຑสะດٟຖิ̃ెҷӪ͜๯๪౉ॽ׽ڍຣພ౏ʤγʨ๼໗๾ຸޢ๴ົ๭ޟޜϒؿΦߑ࣬ຄ๋࣌ฉ໴݁ແ୍̆໅๔Ջݮ໊׾़౰໎գ໐ŎȖऄӪȖෟޝȹȖڃມູ໧̌ȐࣵӅеຨʡਦ؊໥໛ຮ๶༒ນ๣༌੓ǧ໯ຯࢾ໲຿ຈށȖ໹ฤຍ๕MՎĞ࠾୔໋ຓ༁ೕʤༀ௟ֆڟా༷༼Ůࢩďທ༅ச༡ཆคා༘˟ຨ֌Ϲǹ༊േຼ຅ํງชपฌࠬळ༬຋༮ໆ๕S༲ފ༵໿ห༸୙ीཀ೼౜ʠ༿՟ోགྷȶΫ঩୩͹πుഫ͔౮ຝͦğ؜ནϒਦు༘ਫଈࡹཔ༨བເୃཛୋįཞރའՋճ໾ছཧཁอ໏եভȹఋ়ࣼ۶ଣͅޗ೴ഷ࡚́ࡑঽ́ࢮ१ખຨྙണ୲ྞஒආ༘Ө૪౾ͰҴΔ઩Ӕ;ຨ෗֓ੜ਻ૺ੠৫਺ਏƟིɚͶ࢑ɾͶѥٷ৅̃ྼԳଈׄҺν˦ஊٌં࿐ࠏఔ࿠ڇę́ࡩֺًࣸࠂ֢͜ര֪໕ˇ˩ଌಫعపիம෕ǥ෹ॵLj࢈ַɔৎę࿸ׄ৉࿘ɚ্ّർེƠாّਘի૆౯ငચ໫ଷਜ਼੡࿉਷৩ဘ੝૾ơדҷू฻ȗܣಱף۶ơӲԜཏ׏ʥဆဌޛໟتǭܣׄȨГࣾɚཱུ௹ံଃ̙ਭ̨г˛֧ඛ੣ջ୶ǫ္ϖլಯ׏Ɠ၃ဳջർȿ၄ྈͯΚ՝၊̬ޡ࿢֢Ɖৎဣ၆֢н੦Ȧ૳ײʼnߊ১ȍၨsၪఝ఩બ࿙଎Ԝ౻င෰ြဇਜʤ၀ၢိҶၼΒΝ֪߯͘ි٥၁঺̅ၕဤၗȖးႂ༆ုơ̹ၔၡੁ࿐෋ޜၦਗ਼ɡpၩऒڣ႟ၮմʐࢊၻ࿍ࢀʤႚ࿙฼၍ႭνଽဴၗঁဿႂΨ႓̿၃႗่࿐༠ೢဖ႞Ⴀ֚ױႣၯႦ෾շӼႹ෣Ⴠඈי჆ယბ٪Ʊුර࿻༽੻஬ઐ෿჉ַΠ౴Ů෻ၞ಑ࡢ୧ၯɶɵཬܲၗͱ࡯רͱ༊ߦ஋ŞէƱ྾ջ൩ჩළ૥؞ैएऑ૲ႝ૴ॐm॒࿈ॗ২ग़ᄆလॠۦ೸ဨ˓ჸడ෼੸ྪ࿽ઌࢊࡡࢊභׄ̈܃ɕࢊ໘ᄞҶȠ٪ˁࢋ૯"Eݿ૽ࣙګųĝܸྊ໵ྌวࣈ୅ݽେ݌ݔմ[নޖʲΖēĔ˲यໄཟ໻ŀׂྖࡕཫॣ୞༄ӋȾ࣢࿼ഷˁ̑სࢶླƠָڵყગో࿫׏Ρ႓ƅ߯ჳߓˈၗѮᅦ೜ྲྀ൚ɚʹ֍௟հւݓᄐᅗŰϕখঽϯ๸ɡᅠ܃ྱౌၗŦ଼ֈ੗჎ఙ஋ුׄΎಶɚ˧ೆჼϮࢅჿखोჁᄄज़ᄈယᄋᄇ਍य़ు෥ᅖշӊ૪ಙ׽ன଴ഓ੻ࢍɗܠ஝֢ƅೲ஧ᄕઈ঒ᄘ෽༅ࠈᆎᆒҐుຽदབྷᄵซࣇࣉ࠯݉ฑ୊ށơᅇށ໺࣒ตᅍࣙ཰ໍ༹ཅϯޕୢᄗᅗ΅Εୣဨٙ႓ோણභ࡫એׄ௒ᅰ׏ྀ͚ɾ͚νਿࡸէ͚ਬ৓၅֢ʸૣᇱಙරఖఅ൒ᇣླྀ၍ᆪ߿೙஫ᄙդనđଦᇮ࿸౯ᇃงྋୂᄷ݅ୋƭྑĻྒྷถᇕ๚ోᇙᅒэব࡛ࣤᇮᆳҷࡽ౶ƠӰႯɚĚ͖࢖ࢉ୼ַѤ༗ሰႚၝᇮᅯભટ༥ᇻ໪ൕᇹဋ˂໒вᇱසሻ௘ሰ໘ሀଷ඾ᇟྵᅛሇتࢱဧლላ໥ࢍ֪ίᇂཕᄵሖཚᄸ࣌Րማล໇Tལ༴ษས໌ཨᅑၗ̀Ⱦᇝठᅗಆ໋࿋ᄧ͂ᇽ֢ҫ໮থǭᇩΊлੋኄܧሯҸᇁ̅ᇴቀ׏˔ࢮϿཋɚҐ࿏ቾࡲආෲඉሃቷಔሻቕᄖ֠ᆻഔሌࠈሏׄˠ༊ฅᄴᇅክɀཙᇈཛྷሚᅈྒᅊ؎ቫࣖቭྗቯᆄ༹ฯᅏቶྛג౤ۘྥགᄧхሥഌᅗͮণᄡဇגሑཽׄӓᅴಌձቪዋ჈Ơႂě੦ኄΰᅴ಺ձᅸǒ୬ዔಔኻ఻ဨ߲੄Ơ߮ީࡤშ૔஗ྺీ֢ϩዮƏഹ੭ᅱዯไ֮܏ࢼॠࢦ਩ቺዬနೃ߽ᆸᆬো቙ᆼሏᆲዠቊఁڜበኮቢ኱ฎᄺብྐݟኴሜ኶࣊ኸऺቮዀ୘ቱɾ൴ᇭׄ൱ਗ֢϶ংڶڸeIݱ઺༐ֲھጵጷᇞշ̯ኑጪນɹᆂᇭ෡ƅ็ൕૄ׏̤Εኍጰٷᄪ঄૱ौഴɹૂીֲાޥᄧ࡮྄ɚ̤དྷጪரፊ٥ַඩର࢕࿞ಔܠ࣭ጭතଢ଼ፌሩቋથტ፝ဠሒ጗ࣃམྍቤށƓ༭ኵᇓɀޤሟ༻ძሢၗࢴዊ዁ַˀᅚቘշ෧኉żǹዱ२ዳอ௟ྺቅˌ઩׬ഫాץຜྚၧsubݤૹמڎᎧᎩՒသ̝ߘʜ௎ओᎱ઴රၧდၫڼჃဨࣦ૟ሻᆥጇᆯእઁ੕Ѯ੦ካିሕ፼ሗळቦጠቨ๕ࠡᎆ྘ᎈ๝ɾढܱሦᄧ˱ഛᆺഷŰଇ኉Ꮳઔዲᅡድ஋ദ࣯೿ጕؼϾ໖ዾŰޡቃേᏰർࢹጂձࢽዦ٪Ᏼ༊ჯ۽ဦറᏢ˿࿮ጋŚለᏡŧቚ׽ࢍΊŰქ፹ኬ፻๎ኰญጣሙᇐ࠶ᇒฦᄸᏖኼᏘ༾ཪᇗ༁ሧ፥ᐢՀᏙčզ׶๢ዓ࣯ၴٞ؄টהఓٹB፜۶Ԑ˸ትȉዧكȖ͖Ə֫ჷᏬůЈዖ܈ɀዙخǠ೻ᆛ৩ńcڦယɅɄᑖዚ؄စҡ׼็ٞृູএ૾؀ᆈ჏࿃ᑔᑚᎼਸᑪ࿊ֵ᏿ķञྜྷᑉઢࡍᑌ"ࡐᑏයዴ൬ࠖਜ਼ᑮᑗᒁᑛɳȨඞঢᒅ˽ᅴ຀ᑤ׼టɶʟၧᑘᑕਫ਼২ᒓᑫᑀ᏿ϙႇďցᒅ໚ᑽ࿂ঊᒃᑬᑓᑙᑯᆭᐼĮᏊᒅሏٞ༠׊qᑥ͛ᑋᑣᑍᒳ੩ᒐᑒᒗᒕ਑ᒤᒙᒪಫٞбᒞخͱ؜ᒑᄃᒦᒔᒂᒧؐᑁůનုŘ፸ئᒰձᒲᒅቓʜ዗ւᑎؓ˓ণᓉፖৠᒿ੟ᓋᒘࢆᒪေᆁᏨ௧ᒅ໪ᑡᓝᒷᒅۗᒺᑨᒣᓎჄএᒿᓪᏢըᏥΝᆩᒒᓦᒖᓽᑰᐼᄨᆑůၛֹնऊك̬б֮ᓻ݀चդИخᅿজᔉએᓷؓϯ੦ᔏਗ਼ᔑႪट࿆ܼܷکᐕ຿ᇇᄹ࠰ᄽݖŐǹƊᅂнĸĔȿĀݸᔼᔽݸ3ݺݼ࠰ݿळᐞ໇Rጤཥኻጧ๜ጩᔚቇᓭ᎘Ꮹᐧ᏿ᐒ፟ᔌޛᄪAݿa๗՗Ɗሉᕕ᎖ቹᔈᏢᆇᔋʶᆊ፱᎜̨ʶࢀᔟؼϮ໢ᓸ਴လ࿅ᓧ࿄ਏᕱၜᑽᕱ܅ቔሁ൑஢؄ᆨೀᄔᐊࢄዚᆸࢉ෾ᄡሎΒʶಣጕሓ໳ጘᏎባᇉᏐໃᇑᅉᎃ"UᕉኺཅʶᕐŵᖄಿŹڣᕛrᕝጸĞᕠƍصᓐʶᓬ዆ྚ؄ᇠሸůᇤߪ୺ᅪ৸ؼᇬပᎍزᑢሾࡖ᏿ڂஞந቗তጏ࢙ስزာဒكݡᇧ׿ఈᕗ˧ዩฯࡹعႮؓబ୕፭̱˧ဵŻ˲࣠̕શ຃ጾȱᄢزไ౜ᗁ፰ईᗴಜ࿸ኚෘኜӛ˧଀ཽአᆹाኣᏅɋࠈᅙᔬྋᔮྎ፿đቧ༯Ջᒲᐡᕌࣜᕎؓҹຨ৏ؼౡྠఙᗯിɳ۫ᇭࢊᗥ˚ᏲᘗჍİড়׊ᕈᗮᘡࢋ˯ᕡᐼഎ᎗ྯඐძᖳಂ᎐წ؄͂༎Ҹᘡქᄪunɇfऐeस࣬ᘟᖻኦᕭҲى௬ɡᖠ᏿ವᕤ൝ᘳዷޜᖐˠ࡯ᖾᗐˈᘽᓶۛخ്̑ᅢᙕᒏᏯكʿণߦᐑ೤ߪ᏷ᙬሺᓖଵؼȫᆶɳިኌ኎એᏄᗽኩ܃ᘁግ੧ᐍᆼએܠසᙸ၌௹ာᙸᘰࡕᗠᖔ༩ᖗጚጣᄺฐळ̬ݞᎪॊᕅᖝᐟڨՏቬ๙ᎇᅐฮᐥȕోᐨ཯ᚫໍིࣞᐮخϷ˥ᙰኘᎥᓊ֝לᔕנᚼᚴᑶॽ؄ഡ֍ᐸױᐺᒄᚴᓕᒩؓĐੋަᔐůҬᘝ੟ᔩܟ઎ҴͭۗያᏢԄᑧᛍ෇ቔᛑஎٚཪܴܝᔪűᘉᇆᐘཛጛ๏ŌнůįᅂʊĔƉ᚝ݻ݈ᕄᏒᘐŀᐺᘓᐦኽཀྵಘᚬᐬؓ൷ΦĮЎ̳ǭŤصาᜈ௸ၔ΢ש࢝ᜏᅝ᏿˭ڇŘԫˋʚᜎǫƠᐨك፡ഞ࿞੩ͯلۢᖶ཭؄өᘵ୨࿹ᆄ࿌ůʩးُ஡ᙫ᜶ዄፒહᄂᓤɀmaࠡჄڎᝃᝅه໪ᕚᕜᕞڪीᓾᗽˉ஀ᔜᏢ̝઩ᛙͫ྇஋ቂك໋ࣻؼ͢࡯᝙ඓආ௟ᒢᕵᄈᕷᒖᕶᕺ᝞ᎤጎᝡႿᛌᗺగᖃُࠇ߼ᖇ੼ᖉǒᅣმ፭ࡪđᚋ᝞࢛ࡕᛜᝒထ᜻ɴᖨጊ௻ឌଚಙྺ༖خ˱ᝣُሽᔟ෡Зᛏម؜ൂផᙣ᝴ᖁቑᝒબណ࿺ጌ೙ᅣ఩ْ఩႕ۊͶᏠ᝾ځ˿សٜᑿ౜ྺျٳӑى̱ٙڇਿΒٙ೷ʉ࿗෌൞ٙᓢߌܣ᜘઴ӛ΅ዒᘪΊ្ଚៃٜఐɎҴЩ᜜៊˽ᓈ៍ઉص༹ឱᗠᒯҽோᇳဏះᑼ೽̺ோ඲៟ᇹរߡ៣ᜣ឵ᒠ៯ᚄឫᆮᖌȊ࢖դሽួኋቮ᠄Ꮃϖߑᙌğȗകٕπᑄᜋ͜ᜍᖱ᠉۶Ўࢮڅʨ༚௸࣭ع̇ࣽឰĘʨϕѳ̯·᜗ᜢ௸៑ফᝠۊᑐى౽᠋᝹ნɿՃᚍഇ᜞˲ᄥᠪᜤ٫ԍᒝᜭᚆٕ࿶ᙗጎᠬ៧៛٘ဈᅟᓮፆᙟধᅃᡋដ៯ٳᔴྰᡄཇئᒮ׊F૾Ұჲᡋᛁਤඇᑾᗻ᝷Ճᓂᡊ᝻ᆫ៽ኤǓᗯ෾ዼᡖᔙᙛ༧Ꮜቡᚖᐙᇊᄻ࣌ˉݞks-īᙉᚠᎂᐟᡞᜁᚯተౄཅ͚ှԜԴ̓Əࢀკ៑ᒛۂѩё࣭ ٕԤύ˭˂୿ᛉ៰ჱ˥ᢒඝᙂ͊ځӕّᢚൻࡽ೏ԻεͺϮ˨ࡻŮȦٕ̣ᢧؽᢩ؞ౕځᔒᜊ᠌ͱͭᗍ੯ᢻኧŗ҅μၤ஁ᜑډᄝជȄ៥Ęɛຠ᜙ʉɛទාǸᠾμឍᓖ៩Əᣝ෫ǿ᜵Ϡ୯ǿᓐϠ୵ᢻൔ܀ᆌᅪਟʉʷ୕ٳԐឿ٘Ŧૐᇺఙᆱʺៀ̓ᗸᤃΌഺথڅ˵রࡹ៏ᣑۊଡ᣸ۊЇᠱ្٘ᇧဤᤁ͗៩μಊ៸ʉμ଩ᤉᤖ៓̏ᣞᠽځʵᇡഒᛗɋͯۋ᠈ᠴٳ௃ᔋęஐᗝ໰ᤖᕣୖႍ᠕ᠴ៑ᇯણ౵ᔗƵٕ؅ޠᤃি࿹᣹ၶᤸ༦᣶ᅵ՟࣬៷័᥆ആമᤐ៓៤᣼ᠶအාԛ࿵ሩ᣹᠅ᒯ៕ؤᣦఙᤊᡚᙘ᣿៏ᣖఈڇ஁ሧၱ᥆ᝥ។᣼ᕯ᜻˨בᤣ༞᣶ࡹ೘ၾ᥮ږ፮ฯᣟᤩ୸՟៕ʵ޴৳ᤖᡉȕᤜᦀ౹᥾Ⴝǿ᥯ש۹ࢂឪ୰ʟᘇልƬ̨Ѝ࡯ᦋึᦊ᥏ጨഷԉີᡷ຾Ꮝᐗ፽ᖙᚙ݊ᄼดਸሞ"ธɀบ঍އळ͂༭ᇄᐖ݁ᦺ"บᇌŀᦽ"Cᄽᦾɀ࠼"ࣔ׋ֲ፽๗"ຐ່ŀ໽"༱ɀརɀ$ŀྕڤᇔ"ݓᒷ኷ᛯᎅ"Ꮥ"zŀEŀIᐙLŀᕈɀᖠɀᘒᔰɀᜀ"ᡞຉƔᔻƉलɀ2ŀTgurdLTmvjUyjhm᧯ᅈ໲Ĕྐᅈ'),
        e.exports = t
    }
    , function(e, n, t) {
        (function(n) {
            var o = t(5)
              , a = t(6)
              , r = t(2).Fiber;
            function i(e) {
                this.realm = new o(e),
                this.realm.global.startupRandom = Date.parse(new Date) / 1e3,
                this.realm.global.count = 100
            }
            i.prototype.eval = function(e, n) {
                return e = function(e) {
                    var n = void 0
                      , t = {}
                      , o = e.split("")
                      , a = o[0]
                      , r = o[0]
                      , i = [a]
                      , s = 256;
                    for (e = 1; e < o.length; e++)
                        n = (n = o[e].charCodeAt(0)) < 256 ? o[e] : t[n] || r + a,
                        i.push(n),
                        a = n.charAt(0),
                        t[s] = r + a,
                        s++,
                        r = n;
                    return i.join("")
                }(e),
                this.run(i.fromJSON(JSON.parse(e)), n)
            }
            ,
            i.prototype.run = function(e, n) {
                if ((n = this.createFiber(e, n)).run(),
                !n.paused)
                    return n.rexp
            }
            ,
            i.prototype.call = function(e, n) {
                return this.realm.global[e].apply(this, n)
            }
            ,
            i.prototype.createFiber = function(e, n) {
                return (n = new r(this.realm,n)).pushFrame(e, this.realm.global),
                n
            }
            ,
            i.fromJSON = a.fromJSON,
            e.exports = i
        }
        ).call(this)
    }
    , function(e, n, t) {
        function o(e) {
            return (o = "function" == typeof Symbol && "symbol" === Rr(Symbol.iterator) ? function(e) {
                return void 0 === e ? "undefined" : Rr(e)
            }
            : function(e) {
                return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : void 0 === e ? "undefined" : Rr(e)
            }
            )(e)
        }
        (function() {
            var n = {}.hasOwnProperty
              , a = (l = t(0)).prototypeOf
              , r = l.hasProp
              , i = (l = t(1)).ArrayIterator
              , s = l.StopIteration
              , l = (c.prototype.inv = function(e) {
                return -e
            }
            ,
            c.prototype.lnot = function(e) {
                return !e
            }
            ,
            c.prototype.not = function(e) {
                return ~e
            }
            ,
            c.prototype.inc = function(e) {
                return e + 1
            }
            ,
            c.prototype.dec = function(e) {
                return e - 1
            }
            ,
            c.prototype.add = function(e, n) {
                return n + e
            }
            ,
            c.prototype.sub = function(e, n) {
                return n - e
            }
            ,
            c.prototype.mul = function(e, n) {
                return n * e
            }
            ,
            c.prototype.div = function(e, n) {
                return n / e
            }
            ,
            c.prototype.mod = function(e, n) {
                return n % e
            }
            ,
            c.prototype.shl = function(e, n) {
                return n << e
            }
            ,
            c.prototype.sar = function(e, n) {
                return n >> e
            }
            ,
            c.prototype.shr = function(e, n) {
                return n >>> e
            }
            ,
            c.prototype.or = function(e, n) {
                return n | e
            }
            ,
            c.prototype.and = function(e, n) {
                return n & e
            }
            ,
            c.prototype.xor = function(e, n) {
                return n ^ e
            }
            ,
            c.prototype.ceq = function(e, n) {
                return n == e
            }
            ,
            c.prototype.cneq = function(e, n) {
                return n != e
            }
            ,
            c.prototype.cid = function(e, n) {
                return n === e
            }
            ,
            c.prototype.cnid = function(e, n) {
                return n !== e
            }
            ,
            c.prototype.lt = function(e, n) {
                return n < e
            }
            ,
            c.prototype.lte = function(e, n) {
                return n <= e
            }
            ,
            c.prototype.gt = function(e, n) {
                return e < n
            }
            ,
            c.prototype.gte = function(e, n) {
                return e <= n
            }
            ,
            c);
            function c(e) {
                var t = void 0
                  , l = void 0
                  , c = {
                    window: "undefined" == typeof window ? {} : window,
                    undefined: void 0,
                    Object: Object,
                    Function: Function,
                    Number: Number,
                    Boolean: Boolean,
                    String: String,
                    Array: Array,
                    Date: Date,
                    RegExp: RegExp,
                    Error: Error,
                    StopIteration: s,
                    Math: Math,
                    JSON: JSON,
                    console: console,
                    encodeURIComponent: encodeURIComponent,
                    unescape: unescape,
                    Uint8Array: Uint8Array,
                    parseInt: parseInt,
                    escape: escape,
                    decodeURIComponent: decodeURIComponent
                };
                for (t in c.global = c,
                this.has = function(e, n) {
                    return null != e && (!!r(e, n) || this.has(a(e), n))
                }
                ,
                this.get = function(e, n) {
                    if (null != e)
                        return r(e, n) || "string" == typeof e && "number" == typeof n || "length" === n ? e[n] : this.get(a(e), n)
                }
                ,
                this.set = function(e, n, t) {
                    var a = o(e);
                    return ("object" === a || "function" === a) && (e[n] = t),
                    t
                }
                ,
                this.del = function(e, n) {
                    var t = o(e);
                    return "object" !== t && "function" !== t || delete e[n]
                }
                ,
                this.instanceOf = function(e, n) {
                    var t = void 0;
                    return null != n && ("object" === (t = o(n)) || "function" === t) && n instanceof e
                }
                ,
                this.enumerateKeys = function(e) {
                    var n = void 0
                      , t = [];
                    for (n in e)
                        "__mdid__" !== n && t.push(n);
                    return new i(t)
                }
                ,
                e)
                    n.call(e, t) && (l = e[t],
                    c[t] = l);
                this.global = c
            }
            e.exports = l
        }
        ).call(this)
    }
    , function(e, n, t) {
        (function() {
            var n = t(7)
              , o = function(e) {
                for (var t = [], o = 0; o < e.length; o++) {
                    for (var a = e[o], r = n[a[0]], i = [], s = 1, l = 1, c = a.length; 1 <= c ? l < c : c < l; s = 1 <= c ? ++l : --l)
                        i.push(a[s]);
                    r = new r(i.length ? i : null),
                    t.push(r)
                }
                return t
            }
              , a = function(e) {
                var n = e.lastIndexOf("/")
                  , t = e.slice(0, n);
                n = e.slice(n + 1);
                return new RegExp(t,n)
            }
              , r = (i.fromJSON = function e(n) {
                for (var t = o(n[2]), i = [], s = n[3], l = 0; l < s.length; l++) {
                    var c = s[l];
                    i.push(e(c))
                }
                for (var u = n[4], p = u.length, d = [], h = n[5], f = 0; f < h.length; f++) {
                    var g = h[f];
                    d.push({
                        start: -1 !== g[0] ? g[0] : null,
                        handler: -1 !== g[1] ? g[1] : null,
                        finalizer: -1 !== g[2] ? g[2] : null,
                        end: -1 !== g[3] ? g[3] : null
                    })
                }
                for (var m = n[6], v = n[7], w = [], _ = n[8], y = 0; y < _.length; y++) {
                    var I = _[y];
                    w.push(a(I))
                }
                return new r(null,null,t,i,u,p,d,m,v,w,null)
            }
            ,
            i);
            function i(e, n, t, o, a, r, i, s, l, c, u) {
                this.filename = e,
                this.name = n,
                this.instructions = t,
                this.scripts = o,
                this.localNames = a,
                this.localLength = r,
                this.guards = i,
                this.stackSize = s,
                this.strings = l,
                this.regexps = c,
                this.source = u
            }
            e.exports = r
        }
        ).call(this)
    }
    , function(e, n, t) {
        function o(e) {
            return (o = "function" == typeof Symbol && "symbol" === Rr(Symbol.iterator) ? function(e) {
                return void 0 === e ? "undefined" : Rr(e)
            }
            : function(e) {
                return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : void 0 === e ? "undefined" : Rr(e)
            }
            )(e)
        }
        (function() {
            var n, a = void 0, r = t(1).StopIteration, i = ((p = t(0)).defProp,
            p.hasProp), s = (p = t(2)).Fiber, l = p.Scope, c = p.WithScope, u = (a = 0,
            function(e, n, t) {
                var o;
                return o = function(e) {
                    e && (this.args = e)
                }
                ,
                Object.defineProperty(o, "name", {
                    writable: !0,
                    value: e
                }),
                o.prototype.id = a++,
                o.prototype.name = e,
                o.prototype.exec = n,
                o.prototype.calculateFactor = t || function() {
                    return 2
                }
                ,
                o
            }
            ), p = [new (n = function(e, n, t) {
                return u(e, n, t)
            }
            )("",(function(e, n, t) {
                return m(e)
            }
            )), new n("",(function(e, n, t) {
                return n.pop()
            }
            )), new n("",(function(e, n, t) {
                return n.push(n.top())
            }
            )), new n("",(function(e, n, t) {
                var o = n.pop()
                  , a = n.pop();
                return n.push(o),
                n.push(a)
            }
            )), new n("",(function(e, n, t) {
                return e.fiber.rv = n.pop(),
                m(e)
            }
            )), new n("",(function(e, n) {
                return e.paused = !0
            }
            )), new n("",(function(e, n) {
                return e.fiber.yielded = n.pop(),
                e.fiber.pause()
            }
            )), new n("",(function(e, n, t) {
                return v(e, n.pop())
            }
            )), new n("",(function(e) {
                return e.guards.push(e.script.guards[this.args[0]])
            }
            )), new n("",(function(e) {
                var n = e.guards[e.guards.length - 1];
                if (e.script.guards[this.args[0]] === n)
                    return e.guards.pop()
            }
            )), new n("",(function(e, n, t) {
                return e.fiber.r1 = n.pop()
            }
            )), new n("",(function(e, n, t) {
                return e.fiber.r2 = n.pop()
            }
            )), new n("",(function(e, n, t) {
                return e.fiber.r3 = n.pop()
            }
            )), new n("",(function(e, n, t) {
                return n.push(e.fiber.r1)
            }
            )), new n("",(function(e, n, t) {
                return n.push(e.fiber.r2)
            }
            )), new n("",(function(e, n, t) {
                return n.push(e.fiber.r3)
            }
            )), new n("",(function(e, n, t) {
                return n.fiber.rexp = n.pop()
            }
            )), new n("",(function(e, n, t) {
                return d(e, 0, "iterator", n.pop())
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.enumerateKeys(n.pop()))
            }
            )), new n("",(function(e, n, t) {
                if (d(e, 0, "next", n.pop()),
                e.error instanceof r)
                    return e.error = null,
                    e.paused = !1,
                    e.ip = this.args[0]
            }
            )), new n("",(function(e, n, t) {
                if (t.set(1, n.pop()),
                n = n.pop(),
                this.args[0])
                    return t.set(2, n)
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.global)
            }
            )), new n("",(function(e, n, t, o) {
                var a = this.args[0]
                  , r = this.args[1]
                  , i = t.get(1);
                if (a < i.length)
                    return t.set(r, Array.prototype.slice.call(i, a))
            }
            )), new n("",(function(e, n, t) {
                return h(e, this.args[0], n.pop(), null, null, !0)
            }
            )), new n("",(function(e, n, t) {
                return h(e, this.args[0], n.pop(), null, this.args[1])
            }
            )), new n("",(function(e, n, t) {
                return d(e, this.args[0], n.pop(), n.pop(), this.args[1])
            }
            )), new n("",(function(e, n, t, o) {
                var a = n.pop()
                  , r = n.pop();
                return null == a ? v(e, new Error("Cannot read property '" + r + "' of " + a)) : n.push(o.get(a, r))
            }
            )), new n("",(function(e, n, t, o) {
                var a = n.pop()
                  , r = n.pop()
                  , i = n.pop();
                return null == a ? v(e, new Error("Cannot set property '" + r + "' of " + a)) : n.push(o.set(a, r, i))
            }
            )), new n("",(function(e, n, t, o) {
                var a = n.pop()
                  , r = n.pop();
                return null == a ? v(e, new Error("Cannot convert null to object")) : n.push(o.del(a, r))
            }
            )), new n("",(function(e, n, t) {
                for (var o = this.args[0], a = this.args[1], r = t; o--; )
                    r = r.parent;
                return n.push(r.get(a))
            }
            )), new n("",(function(e, n, t) {
                for (var o = this.args[0], a = this.args[1], r = t; o--; )
                    r = r.parent;
                return n.push(r.set(a, n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                for (var a, r = this.args[0]; t instanceof c; ) {
                    if (t.has(r))
                        return n.push(t.get(r));
                    t = t.parent
                }
                for (; t instanceof l; ) {
                    if (0 <= (a = t.name(r)))
                        return n.push(t.get(a));
                    t = t.parent
                }
                return i(o.global, r) || this.args[1] ? n.push(o.global[r]) : v(e, new Error(r + " is not defined"))
            }
            )), new n("",(function(e, n, t, o) {
                for (var a, r = this.args[0], i = n.pop(); t instanceof c; ) {
                    if (t.has(r))
                        return n.push(t.set(r, i));
                    t = t.parent
                }
                for (; t instanceof l; ) {
                    if (0 <= (a = t.name(r)))
                        return n.push(t.set(a, i));
                    t = t.parent
                }
                return n.push(o.global[r] = i)
            }
            )), new n("",(function(e, n, t, o) {
                return i(o.global, this.args[0]) || this.args[1] ? n.push(o.global[this.args[0]]) : v(e, new Error(this.args[0] + " is not defined"))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.global[this.args[0]] = n.pop())
            }
            )), new n("",(function(e) {
                return e.scope = new l(e.scope,e.script.localNames,e.script.localLength)
            }
            )), new n("",(function(e) {
                return e.scope = e.scope.parent
            }
            )), new n("",(function(e, n) {
                return e.scope = new c(e.scope,n.pop())
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.inv(n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.lnot(n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.not(n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.inc(n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.dec(n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.add(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.sub(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.mul(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.div(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.mod(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.shl(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.sar(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.shr(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.or(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.and(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.xor(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.ceq(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.cneq(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.cid(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.cnid(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.lt(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.lte(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.gt(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.gte(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.has(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(o.instanceOf(n.pop(), n.pop()))
            }
            )), new n("",(function(e, n, t, a) {
                return n.push(o(n.pop()))
            }
            )), new n("",(function(e, n) {
                return n.pop(),
                n.push(void 0)
            }
            )), new n("",(function(e, n, t) {
                return e.ip = this.args[0]
            }
            )), new n("",(function(e, n, t) {
                if (n.pop())
                    return e.ip = this.args[0]
            }
            )), new n("",(function(e, n, t) {
                if (!n.pop())
                    return e.ip = this.args[0]
            }
            )), new n("",(function(e, n) {
                return n.push(void 0)
            }
            )), new n("",(function(e, n, t) {
                return n.push(this.args[0])
            }
            )), new n("",(function(e, n, t) {
                return n.push(e.script.strings[this.args[0]])
            }
            )), new n("",(function(e, n, t, o) {
                return n.push(new RegExpProxy(e.script.regexps[this.args[0]],o))
            }
            )), new n("",(function(e, n, t, o) {
                for (var a = this.args[0], r = {}; a--; )
                    o.set(r, n.pop(), n.pop());
                return n.push(r)
            }
            )), new n("",(function(e, n, t, o) {
                for (var a = this.args[0], r = new Array(a); a--; )
                    r[a] = n.pop();
                return n.push(r)
            }
            )), new n("",(function(e, n, t, o) {
                var a = this.args[0];
                return n.push(f(e.script.scripts[a], t, o, this.args[1]))
            }
            )), new n("",(function(e) {
                return e.setLine(this.args[0])
            }
            )), new n("",(function(e) {
                return e.setColumn(this.args[0])
            }
            )), new n("",(function(e, n, t) {
                return w()
            }
            ))], d = function(e, n, t, o, a) {
                var r = e.evalStack
                  , i = e.realm;
                if (null == o)
                    return v(e, new Error("Cannot call method '" + t + "' of " + (void 0 === o ? "undefined" : "null")));
                var s = o.constructor.name || "Object";
                return (i = i.get(o, t))instanceof Function ? h(e, n, i, o) : null == i ? (r.pop(),
                v(e, new Error("Object #<" + s + "> has no method '" + t + "'"))) : (r.pop(),
                v(e, new Error("Property '" + t + "' of object #<" + s + "> is not a function")))
            }, h = function(e, n, t, o, a, r) {
                if ("function" != typeof t)
                    return v(e, new Error("object is not a function"));
                for (var i = e.evalStack, s = e.fiber, l = e.realm, c = {
                    length: n,
                    callee: t
                }; n; )
                    c[--n] = i.pop();
                o = o || l.global,
                c = Array.prototype.slice.call(c);
                try {
                    var u = r ? g(t, c) : t.apply(o, c);
                    if (!s.paused)
                        return i.push(u)
                } catch (p) {
                    v(e, p)
                }
            }, f = function(e, n, t, o) {
                return function o() {
                    var a, r = void 0, i = void 0, l = !1;
                    if ((i = o.__fiber__) ? (i.callStack[i.depth].paused = !0,
                    o.__fiber__ = null,
                    r = o.__construct__,
                    o.__construct__ = null) : (i = new s(t),
                    l = !0),
                    a = o.__callname__ || e.name,
                    o.__callname__ = null,
                    i.pushFrame(e, this, n, arguments, o, a, r),
                    l)
                        return i.run(),
                        i.rv
                }
            }, g = function(e, n) {
                var t = void 0;
                return e === Array ? function(e) {
                    return 1 === e.length && (0 | e[0]) === e[0] ? new Array(e[0]) : e.slice()
                }(n) : e === Date ? new Date : e === RegExp ? function(e) {
                    return 1 === e.length ? new RegExp(e[0]) : new RegExp(e[0],e[1])
                }(n) : e === Number ? new Number(n[0]) : e === Boolean ? new Boolean(n[0]) : e === Uint8Array ? new Uint8Array(n[0]) : ((t = function() {
                    return e.apply(this, n)
                }
                ).prototype = e.prototype,
                new t)
            }, m = function(e) {
                return e.evalStack.clear(),
                e.exitIp = e.ip
            }, v = function(e, n) {
                return e.error = n,
                e.paused = !0
            }, w = function() {};
            e.exports = p
        }
        ).call(this)
    }
    ],
    e.c = t,
    e.d = function(n, t, o) {
        e.o(n, t) || Object.defineProperty(n, t, {
            enumerable: !0,
            get: o
        })
    }
    ,
    e.r = function(e) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
            value: "Module"
        }),
        Object.defineProperty(e, "__esModule", {
            value: !0
        })
    }
    ,
    e.t = function(n, t) {
        if (1 & t && (n = e(n)),
        8 & t)
            return n;
        if (4 & t && "object" === (void 0 === n ? "undefined" : Rr(n)) && n && n.__esModule)
            return n;
        var o = Object.create(null);
        if (e.r(o),
        Object.defineProperty(o, "default", {
            enumerable: !0,
            value: n
        }),
        2 & t && "string" != typeof n)
            for (var a in n)
                e.d(o, a, function(e) {
                    return n[e]
                }
                .bind(null, a));
        return o
    }
    ,
    e.n = function(n) {
        var t = n && n.__esModule ? function() {
            return n.default
        }
        : function() {
            return n
        }
        ;
        return e.d(t, "a", t),
        t
    }
    ,
    e.o = function(e, n) {
        return Object.prototype.hasOwnProperty.call(e, n)
    }
    ,
    e.p = "",
    e(e.s = 3);
    function e(o) {
        if (t[o])
            return t[o].exports;
        var a = t[o] = {
            i: o,
            l: !1,
            exports: {}
        };
        return n[o].call(a.exports, a, a.exports, e),
        a.l = !0,
        a.exports
    }
    var n, t
}
));

关键模块

(function() {
    var e = {}.hasOwnProperty
      , a = t(0).isArray;
    ......
    i.prototype.done = function() {
        return -1 === this.depth
    }
    ......
}).call(this)

这个模块实现了虚拟机的核心运行机制,包括堆栈帧管理、错误处理、执行指令等功能。

var s = (l.prototype.run = function() {
    ......
}),
i.prototype.pushFrame = function(e, n, t, o, a, r, i) {
    ......
},
i.prototype.popFrame = function() {
    ......
},
i.prototype.pause = function() {
    ......
}

这部分用于控制虚拟机的执行,包括纤程的创建、执行、暂停、恢复以及栈帧的切换等。

var p = [new (n = function(e, n, t) {
    return u(e, n, t)
})("", (function(e, n, t) {
    return m(e)
})),
......
]

指令集,不再多说。

接着看一下一些关键功能的实现。

全局环境

function Realm() {
    // 初始化全局对象和内置函数
    this.global = {
        window: window,
        undefined: void 0,
        Object: Object,
        Function: Function,
        Number: Number,
        Boolean: Boolean,
        String: String,
        Array: Array,
        Date: Date,
        RegExp: RegExp,
        Error: Error,
        Math: Math,
        JSON: JSON,
        console: console,
        parseInt: parseInt,
        parseFloat: parseFloat,
        isNaN: isNaN,
        isFinite: isFinite,
        decodeURI: decodeURI,
        decodeURIComponent: decodeURIComponent,
        encodeURI: encodeURI,
        encodeURIComponent: encodeURIComponent,
        escape: escape,
        unescape: unescape
    };
    // 内置方法
    ......
}

纤程

可以理解为轻量级的协程:

function Fiber(realm, timeout) {
    this.realm = realm; // 虚拟机的执行环境
    this.timeout = timeout != null ? timeout : -1; // 超时时间
    this.maxDepth = 1000; // 最大栈深度
    this.maxTraceDepth = 50; // 最大追踪深度
    this.callStack = []; // 调用栈
    this.evalStack = null; // 评估栈
    this.depth = -1; // 当前栈深度
    this.yielded = this.rv = undefined; // 保存纤程的返回值
    this.paused = false; // 纤程是否暂停
    this.r1 = this.r2 = this.r3 = null; // 寄存器
    this.rexp = null; // 保存执行结果
}

接着看下纤程的执行,其它的这里不再细说:

Fiber.prototype.run = function() {
    // 执行指令的主循环
    for (var frame = this.callStack[this.depth], error = frame.error; this.depth >= 0 && frame && !this.paused; ) {
        frame.run();
        error = frame.error;
        if (error) {
            // 处理异常
            frame = this.unwind(error);
        }
        if (frame.done()) {
            // 如果栈帧完成,弹出栈帧
            frame = this.popFrame();
        }
    }
    if (this.timedOut()) {
        // 如果超时,抛出异常
        error = new Error(this);
        this.injectStackTrace(error);
        throw error;
    }
};

栈帧

栈帧代表了一个函数调用的执行上下文,包含了局部变量、指令指针、评估栈、环境信息等。

function StackFrame(fiber, script, scope, realm, fname, construct) {
    this.fiber = fiber; // 所属的纤程
    this.script = script; // 当前执行的脚本
    this.scope = scope; // 作用域
    this.realm = realm; // 执行环境
    this.fname = fname; // 函数名
    this.construct = construct != null ? construct : false; // 是否为构造函数
    this.evalStack = new EvalStack(this.script.stackSize, this.fiber); // 评估栈
    this.ip = 0; // 指令指针
    this.exitIp = this.script.instructions.length; // 退出指令指针
    this.paused = false; // 是否暂停
    this.finalizer = null; // 终止器
    this.guards = []; // 异常处理
    this.rv = undefined; // 返回值
    this.line = this.column = -1; // 当前行列号
}

接着看栈帧的运行,除运行外的其它方法也不再多说:

StackFrame.prototype.run = function() {
    // 获取当前脚本的指令数组
    var instructions = this.script.instructions;
    // 主循环,直到指令指针达到结束位置或栈帧被暂停或超时再退出
    while (this.ip !== this.exitIp && !this.paused && this.fiber.timeout !== 0) {
        // 每次执行一条指令时减少超时时间
        this.fiber.timeout--;
        // 执行当前指令,指令指针自增
        instructions[this.ip++].exec(this, this.evalStack, this.scope, this.realm);
    }
    // 如果超时,将栈帧和纤程标记为暂停状态
    if (this.fiber.timeout === 0) this.paused = this.fiber.paused = true;
    // 如果执行完毕后评估栈中仍有数据且没有暂停或错误,抛出异常
    if (!this.paused && !this.error && this.evalStack.len() !== 0) {
        throw new Error("Evaluation stack has " + this.evalStack.len() + " items after execution");
    }
};

作用域

变量/对象的生命周期。

function Scope(parent, localNames, localLength) {
    this.parent = parent; // 父作用域,形成作用域链
    this.names = localNames; // 局部变量名数组,存储局部变量的名称
    this.data = new Array(localLength); // 局部变量值数组,存储局部变量的值
}
Scope.prototype.get = function(index) {
    return this.data[index]; // 根据索引获取局部变量的值
};
Scope.prototype.set = function(index, value) {
    this.data[index] = value; // 根据索引设置局部变量的值
    return value;
};
Scope.prototype.name = function(name) {
    for (var i in this.names) { // 遍历局部变量名数组,查找变量名对应的索引
        if (this.names[i] === name) {
            return parseInt(i);
        }
    }
    return -1; // 如果没有找到变量名,返回-1
};

这里重点学习一下此vmp对作用域的管理,主要在以下几个指令:

new n("", function(e) {
    // 创建新作用域
    return e.scope = new Scope(e.scope, e.script.localNames, e.script.localLength);
});
new n("", function(e) {
    // 切回父作用域
    return e.scope = e.scope.parent;
});
// 查找变量
new n("", function(e, n, t, o) {
    for (var a, r = this.args[0]; t instanceof WithScope; ) {
        if (t.has(r))
            return n.push(t.get(r));
        t = t.parent;
    }
    for (; t instanceof Scope; ) {
        if (0 <= (a = t.name(r)))
            return n.push(t.get(a));
        t = t.parent;
    }
    return i(o.global, r) || this.args[1] ? n.push(o.global[r]) : v(e, new Error(r + " is not defined"));
});
// 赋值变量
new n("", function(e, n, t, o) {
    for (var a, r = this.args[0], i = n.pop(); t instanceof WithScope; ) {
        if (t.has(r))
            return n.push(t.set(r, i));
        t = t.parent;
    }
    for (; t instanceof Scope; ) {
        if (0 <= (a = t.name(r)))
            return n.push(t.set(a, i));
        t = t.parent;
    }
    return n.push(o.global[r] = i);
});

综上可以看出,相较于腾讯mvp的简单栈式虚拟机,快手定义了一套更为完整复杂的虚拟机结构,细微到每段脚本的行号、列号都会储存,即使原代码经过编译后出现预想以外的错误,通过完整的异常处理模块,也可以快速定位到混淆前的代码错误位置。

kasada

nike、始祖鸟等平台风控,vmp相关js地址如下:

https://api.nike.com/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/ips.js

核心部分如下:

var C = _(); 
{
    s.a = function(r, t) {
        return s.r.slice(r, r + t)
    };
    var A = v[N + d.indexOf(".")] ^ N,
    B = v.splice(A, v[A + C.W[0]] + 2);
    s.r = R(B, C.W[1].W(), L)
}
function z(r) {
    return r.W[v[r.W[0]++] >> 5]
}
function G(r) {
    return v[r.W[0]++] >> 5
}
function y(r) {
    return R(v, r.W, L, s)
}
var U = [function(n, e, a, _, u, r) {
    // 指令集
}];
function V(r, t) {
    r.W[G(r)] = t
}
function m(r) {
    return r.W[1]
}
function O(r, t) {
    for (;;) {
        var e = r.W[1];
        if (!e) throw t;
        if (e.S) {
            r.B = {
                u: t
            },
            r.W[0] = e.S;
            return
        }
        r.W = e.W()
    }
}
function Y(r, t) {
    var e = m(r);
    e.Q = {
        u: t
    },
    e.z ? r.W[0] = e.z: (r.W = e.W(), r.R = g(), r.W[2] = t)
}
function D(r) {
    for (var t = [T, [w, b], v], e = [Y, O, _, D, d, M, x, g, z];;) {
        op_index = r.W[0]++
        if (op_index == 41){
            debugger
        }
        var a = U[v[op_index]];
        try {
            var o = a(r, y, V, m, t, e);
            if (o === null) break
        } catch(f) {
            O(r, f)
        }
    }
}
D(C)

运行原理及各函数的作用之前文章有写过,不再细说。这里主要说下r.W[1]中各属性的作用:

// r.W[1]初始化
{
    F: this, // 当前函数对象的引用
    W: function() {
		// 上下文切换
        return [0]
    },
    R: function() {
		// 返回值
        return [0]
    },
    T: [], // 局部变量存储
    q: p(f, b), // 执行顺序导航
    P: d // 原型对象
}
// r.W[1].S属性, 虚拟机的运行状态
{
	var e = r.W[1];
	if (!e) throw t;
	if (e.S) {
    	r.B = {
        	u: t
    	},
    	r.W[0] = e.S;
    	return
	}
	r.W = e.W()
}