radare/radare2-webui

View on GitHub
www/lib/r2.js

Summary

Maintainability
F
6 days
Test Coverage
/* radare2 Copyleft 2013-2024 pancake */

var r2 = {};

// TODO: avoid globals
var backward = false;
var next_curoff = 0;
var next_lastoff = 0;
var prev_curoff = 0;
var prev_lastoff = 0;
var hascmd = false;
var r2_root = self.location.pathname.split('/').slice(0, -2).join('/');

function isFirefoxOS() {
    if (typeof locationbar !== 'undefined' && !locationbar.visible) {
        if (navigator.userAgent.indexOf('Firefox') > -1 && navigator.userAgent.indexOf('Mobile') > -1) {
            return ('mozApps' in navigator);
        }
    }
    return false;
}

// Valid options: sync, async or sasync
//r2.asyncMode = 'sasync';
r2.asyncMode = 'sync';

r2.err = null; // callback to be executed when connection fails

if (isFirefoxOS()) {
    /* Requires CORS or SystemXHR */
    r2.root = 'http://cloud.radare.org';
} else {
    r2.root = r2_root;
}

// async helper
function asyncLoop(iterations, func, callback) {
    var index = 0;
    var done = false;
    var loop = {
        next: function() {
            if (done) {
                return;
            }

            if (index < iterations) {
                index++;
                func(loop);

            } else {
                done = true;
                callback();
            }
        },

        iteration: function() {
            return index - 1;
        },

        break: function() {
            done = true;
            callback();
        }
    };
    loop.next();
    return loop;
}

if (typeof (module) !== 'undefined') {
    module.exports = function(r) {
        if (typeof (r) == 'function') {
            hascmd = r;
        } else {
            hascmd = r.cmd;
        }
        return r2;
    };
}

r2.project_name = '';

r2.plugin = function() {
    console.error('r2.plugin is not available in this environment');
};
try {
    if (r2plugin) {
        r2.plugin = r2plugin;
    }
} catch (e) {}

r2.root = r2_root; // prefix path

/* helpers */
function dump(obj) {
    var x = '';
    for (var a in obj) {
        x += a + '\n';
    }
    if (typeof ('alert') != 'undefined') {
        alert(x);
    } else {
        console.log(x);
    }
}

r2.analAll = function() {
    r2.cmd('aa', function() {});
};

r2.analOp = function(addr, cb) {
    r2.cmd('aoj 1 @ ' + addr, function(txt) {
        try {
            cb(JSON.parse(txt)[0]);
        } catch (e) {
            console.error(e);
            cb(txt);
        }
    });
};

r2.varMap = [];
r2.argMap = [];

function objtostr(obj) {
    var str = '';
    for (var a in obj) {
        str += a + ': ' + obj[a] + ',\n';
    }
    return str;
}

var ajax_in_process = false;

function Ajax(method, uri, body, fn, err) {
    if (typeof (XMLHttpRequest) == 'undefined') {
        return false;
    }
    if (r2.asyncMode == 'fake') {
        if (fn) {
            fn('{}');
        }
        return true;
    }
    if (r2.asyncMode == 'sasync') {
        console.log('async waiting');
        if (ajax_in_process) {
            setTimeout(function() {
                    Ajax(method, uri, body, fn);
                }, 100);
            return false;
        }
    }

    var x = undefined;
    if (isFirefoxOS()) {
        var x = new XMLHttpRequest({mozSystem: true});
    } else {
        var x = new XMLHttpRequest();
    }
    if (!x) {
        return false;
    }
    ajax_in_process = true;
    if (r2.asyncMode == 'sync') {
        x.open(method, uri, false);
    } else {
        x.open(method, uri, true);
    }
    x.setRequestHeader('Accept', 'text/plain');
    //x.setRequestHeader ('Accept', 'text/html');
    x.setRequestHeader('Content-Type', 'application/x-ww-form-urlencoded; charset=UTF-8');
    x.onreadystatechange = function() {
        ajax_in_process = false;
        if (x.status == 200) {
            if (x.readyState < 4) {
                // wait until request is complete
                return;
            }
            if (fn) {
                fn(x.responseText);
            } else {
                console.error('missing ajax callback');
            }
        } else {
            (err || r2.err)('connection refused');
            console.error('ajax ' + x.status);
        }
    };

    try {
        x.send(body);
    } catch (e) {
        if (e.name == 'NetworkError') {
            (err || r2.err)('connection error');
        }
    }

    return true;
}

r2.assemble = function(offset, opcode, fn) {
    var off = offset ? '@' + offset : '';
    r2.cmd('"pa ' + opcode + '"' + off, fn);
};

r2.disassemble = function(offset, bytes, fn) {
    var off = offset ? '@' + offset : '';
    var str = 'pi @b:' + bytes + off;
    r2.cmd(str, fn);
};

r2.get_hexdump = function(offset, length, cb) {
    r2.cmd('px ' + length + '@' + offset, cb);
};

r2.get_disasm = function(offset, length, cb) {
    // TODO: honor offset and length
    r2.cmd('pD ' + length + '@' + offset, cb);
};

r2.get_disasm_before = function(offset, start, cb) {
    var before = [];
    r2.cmd('pdj -' + start + '@' + offset + '|', function(x) {
        before = JSON.parse(x);
    });
    cb(before);
};

r2.get_disasm_after = function(offset, end, cb) {
    var after = [];
    r2.cmd('pdj ' + end + '@' + offset + '|', function(x) {
        after = JSON.parse(x);
    });
    cb(after);
};

r2.get_disasm_before_after = function(offset, start, end, cb) {
    var before = [];
    var after = [];
    r2.cmd('pdj ' + start + ' @' + offset + '|', function(x) {
        before = JSON.parse(x);
    });
    r2.cmd('pdj ' + end + '@' + offset + '|', function(x) {
        after = JSON.parse(x);
    });
    var opcodes = before.concat(after);
    cb(opcodes);
};

r2.Config = function(k, v, fn) {
    if (typeof v == 'function' || !v) { // get
        r2.cmd('e ' + k + '|', fn || v);
    } else { // set
        r2.cmd('e ' + k + '=' + v, fn);
    }
    return r2;
};

r2.sections = {};

r2.load_mmap = function() {
    r2.cmdj('iSj|', function(x) {
        if (x !== undefined && x !== null) {
            r2.sections = x;
        }
    });
};

r2.get_address_type = function(address) {
    var offset = parseInt(address, 16);
    for (var i in r2.sections) {
        if (offset >= r2.sections[i].addr && offset < r2.sections[i].addr + r2.sections[i].size) {
            if (r2.sections[i].flags.indexOf('x') > -1) {
                return 'instruction';
            } else {
                return 'memory';
            }
        }
    }
    return '';
};

r2.settings = {};

r2.load_settings = function() {
    r2.cmd('e asm.arch', function(x) {r2.settings['asm.arch'] = x.trim();});
    r2.cmd('e asm.bits', function(x) {r2.settings['asm.bits'] = x.trim();});
    r2.cmd('e asm.bytes', function(x) {r2.settings['asm.bytes'] = toBoolean(x.trim());});
    r2.cmd('e asm.flags', function(x) {r2.settings['asm.flags'] = toBoolean(x.trim());});
    r2.cmd('e asm.offset', function(x) {r2.settings['asm.offset'] = toBoolean(x.trim());});
    r2.cmd('e asm.lines', function(x) {r2.settings['asm.lines'] = toBoolean(x.trim());});
    r2.cmd('e asm.xrefs', function(x) {r2.settings['asm.xrefs'] = toBoolean(x.trim());});
    r2.cmd('e asm.cmtright', function(x) {r2.settings['asm.cmtright'] = toBoolean(x.trim());});
    r2.cmd('e asm.pseudo', function(x) {r2.settings['asm.pseudo'] = toBoolean(x.trim());});
    // console.log("Loading settings from r2");
    // console.log(r2.settings);
};

r2.flags = {};

r2.update_flags = function() {
    r2.cmd('fs *;fj|', function(x) {

        var fs = JSON.parse(x);
        if (fs !== undefined && fs !== null) {
            r2.flags = {};
            for (var f in fs) {
                var addr = '0x' + fs[f].offset.toString(16);
                addr = address_canonicalize(addr);
                if (addr in r2.flags) {
                    var fl = r2.flags[addr];
                    fl[fl.length] = { name: fs[f].name, size: fs[f].size};
                    r2.flags[addr] = fl;
                } else {
                    r2.flags[addr] = [{ name: fs[f].name, size: fs[f].size}];
                }
            }
        }
    });
};

r2.get_flag_address = function(name) {
    for (var f in r2.flags) {
        for (var v in r2.flags[f]) {
            if (name == r2.flags[f][v].name) return f;
        }
    }
    return null;
};

r2.get_flag_names = function(offset) {
    var names = [];
    for (let i in r2.flags[offset]) {
        names[names.length] = r2.flags[offset][i].name;
    }
    return names;
};

r2.set_flag_space = function(ns, fn) {
    r2.cmd('fs ' + ns, fn);
};

r2.get_flags = function(fn) {
    r2.cmd('fj|', function(x) {
        fn(x ? JSON.parse(x) : []);
    });
};

r2.get_opcodes = function(off, n, cb) {
    r2.cmd('pdj @' + off + '!' + n + '|', function(json) {
        cb(JSON.parse(json));
    });
};

r2.get_bytes = function(off, n, cb) {
    r2.cmd('pcj @' + off + '!' + n +'|', function(json) {
        cb(JSON.parse(json));
    });
};

r2.asm_config = {};

r2.store_asm_config = function() {
    config = {};
    r2.cmd('e', function(x) {
        conf = x.split('\n');
        for (let prop in conf) {
            var fields = conf[prop].split(' ');
            if (fields.length == 3) {
                // TODO: Dont know why byt e~asm. is not working so filtering here
                if (fields[0].trim().indexOf('asm.') == 0) config[fields[0].trim()] = fields[2].trim();
            }
        }
        r2.asm_config = config;
    });
};

r2.restore_asm_config = function() {
    cmd = '';
    for (var prop in r2.asm_config) {
        cmd += 'e ' + prop + '=' + r2.asm_config[prop] + ';';
    }
    r2.cmd(cmd, function() {});
};

r2.get_info = function(cb) {
    r2.cmd('ij|', function(json) {
        cb(JSON.parse(json));
    });
};
r2.bin_relocs = function(cb) {
    r2.cmd('irj|', function(json) {
        cb(JSON.parse(json));
    });
};
r2.bin_imports = function(cb) {
    r2.cmd('iij|', function(json) {
        cb(JSON.parse(json));
    });
};

r2.bin_symbols = function(cb) {
    r2.cmd('isj|', function(json) {
        cb(JSON.parse(json));
    });
};

r2.bin_sections = function(cb) {
    r2.cmd('iSj|', function(json) {
        cb(JSON.parse(json));
    });
};

r2.cmds = function(cmds, cb) {
    if (cmds.length == 0) return;
    var cmd = cmds[0];
    cmds = cmds.splice(1);
    function lala() {
        if (cmd == undefined || cmds.length == 0) {
            return;
        }
        cmd = cmds[0];
        cmds = cmds.splice(1);
        r2.cmd(cmd, lala);
        if (cb) {
            cb();
        }
        return;
    }
    r2.cmd(cmd, lala);
};

function _internal_cmd(c, cb, err) {
    if (typeof (r2cmd) != 'undefined') {
        hascmd = r2cmd;
    }
    if (hascmd) {
        // TODO: use setTimeout for async?
        if (typeof (r2plugin) != 'undefined') {
            return cb(r2cmd(c)); // duktape
        }
        return hascmd(c, cb); // node
    } else {
        Ajax('GET', r2.root + '/cmd/' + encodeURI(c), '', function(x) {
                if (cb) {
                    cb(x);
                }
            }, err);
    }
}

r2.cmd = function(c, cb, err) {
    if (Array.isArray(c)) {
        var res = [];
        var idx = 0;
        asyncLoop(c.length, function(loop) {
            _internal_cmd(c[idx], function(result) {
                idx = loop.iteration();
                res[idx] = result.replace(/\n$/, '');
                idx++;
                loop.next();
            }, err);
        }, function() {
                // all iterations done
                cb(res);
            });
    } else {
        _internal_cmd(c, cb, err);
    }
};

r2.cmdj = function(c, cb) {
    r2.cmd(c, function(x) {
        try {
            cb(JSON.parse(x));
        } catch (e) {
            cb(null);
        }
    });
};

r2.alive = function(cb) {
    r2.cmd('b', function(o) {
        var ret = false;
        if (o && o.length() > 0) {
            ret = true;
        }
        if (cb) {
            cb(o);
        }
    });
};

r2.getTextLogger = function(obj) {
    if (typeof (obj) != 'object') {
        obj = {};
    }
    obj.last = 0;
    obj.events = {};
    obj.interval = null;
    r2.cmd('Tl', function(x) {
        obj.last = +x;
    });
    obj.load = function(cb) {
        r2.cmd('"Tj ' + (obj.last + 1) + '"', function(ret) {
            if (cb) {
                cb(JSON.parse(ret));
            }
        });
    };
    obj.clear = function(cb) {
        // XXX: fix l-N
        r2.cmd('T-', cb); //+obj.last, cb);
    };
    obj.send = function(msg, cb) {
        r2.cmd('"T ' + msg + '"', cb);
    };
    obj.refresh = function(cb) {
        obj.load(function(ret) {
            //obj.last = 0;
            for (let i = 0; i < ret.length; i++) {
                var message = ret[i];
                obj.events['message']({
                    'id': message[0],
                    'text': message[1]
                });
                if (message[0] > obj.last) {
                    obj.last = message[0];
                }
            }
            if (cb) {
                cb();
            }
        });
    };
    obj.autorefresh = function(n) {
        if (!n) {
            if (obj.interval) {
                obj.interval.stop();
            }
            return;
        }
        function to() {
            obj.refresh(function() {
                //obj.clear ();
            });
            if (r2ui.selected_panel === 'Logs') {
                setTimeout(to, n * 1000);
            } else {
                console.log('Not in logs :(');
            }
            return true;
        }
        obj.interval = setTimeout(to, n * 1000);
    };
    obj.on = function(ev, cb) {
        obj.events[ev] = cb;
        return obj;
    };
    return obj;
};

r2.filter_asm = function(x, display) {
    var curoff = backward ? prev_curoff : next_curoff;
    var lastoff = backward ? prev_lastoff : next_lastoff;
    var lines = x.split(/\n/g);
    r2.cmd('s', function(x) {
        curoff = x;
    });
    for (let i = lines.length - 1; i > 0; i--) {
        var a = lines[i].match(/0x([a-fA-F0-9]+)/);
        if (a && a.length > 0) {
            lastoff = a[0].replace(/:/g, '');
            break;
        }
    }
    if (display == 'afl') {
        // hasmore (false);
        var z = '';
        for (let i = 0; i < lines.length; i++) {
            const row = lines[i].replace(/\ +/g, ' ').split(/ /g);
            z += row[0] + '  ' + row[3] + '\n';
        }
        x = z;
    } else if (display === '') {
        // nothing
    } else if (display[0] === 'f') {
        // hasmore (false);
        if (display[1] == 's') {
            var z = '';
            for (var i = 0; i < lines.length; i++) {
                var row = lines[i].replace(/\ +/g, ' ').split(/ /g);
                var mark = row[1] == '*' ? '*' : ' ';
                var space = row[2] ? row[2] : row[1];
                if (!space) {
                    continue;
                }
                z += row[0] + ' ' + mark + ' <a href="javascript:runcmd(\'fs ' +
                space + '\')">' + space + '</a>\n';
            }
            x = z;
        } else {
        }
    } else if (display[0] == 'i') {
        // hasmore (false);
        if (display[1]) {
            var z = '';
            for (var i = 0; i < lines.length; i++) {
                var elems = lines[i].split(/ /g);
                var name = '';
                var addr = '';
                for (var j = 0; j < elems.length; j++) {
                    var kv = elems[j].split(/=/);
                    if (kv.length !== 2) {
                        continue;
                    }
                    switch (kv[0]) {
                    case 'addr':
                        addr = kv[1];
                        break;
                    case 'name':
                    case 'string':
                        name = kv[1];
                        break;
                    }
                }
                z += addr + '  ' + name + '\n';
            }
            x = z;
        }
    } // else hasmore (true);

    function haveDisasm(x) {
        if (x[0] == 'p' && x[1] == 'd') {
            return true;
        }
        if (x.indexOf(';pd') != -1) {
            return true;
        }
        return false;
    }
    if (haveDisasm(display)) {
    //    x = x.replace(/function:/g, '<span style=color:green>function:</span>');
/*
        x = x.replace(/;(\s+)/g, ';');
        x = x.replace(/;(.*)/g, '// <span style=\'color:#209020\'>$1</span>');
        x = x.replace(/(bl|goto|call)/g, '<b style=\'color:green\'>call</b>');
        x = x.replace(/(jmp|bne|beq|js|jnz|jae|jge|jbe|jg|je|jl|jz|jb|ja|jne)/g, '<b style=\'color:green\'>$1</b>');
        x = x.replace(/(dword|qword|word|byte|movzx|movsxd|cmovz|mov\ |lea\ )/g, '<b style=\'color:#1070d0\'>$1</b>');
        x = x.replace(/(hlt|leave|iretd|retn|ret)/g, '<b style=\'color:red\'>$1</b>');
        x = x.replace(/(add|sbb|sub|mul|div|shl|shr|and|not|xor|inc|dec|sar|sal)/g, '<b style=\'color:#d06010\'>$1</b>');
        x = x.replace(/(push|pop)/g, '<b style=\'color:#40a010\'>$1</b>');
        x = x.replace(/(test|cmp)/g, '<b style=\'color:#c04080\'>$1</b>');
        x = x.replace(/(outsd|out|string|invalid|int |int3|trap|main|in)/g, '<b style=\'color:red\'>$1</b>');
        x = x.replace(/nop/g, '<b style=\'color:blue\'>nop</b>');
*/
        x = x.replace(/(reloc|class|method|var|sym|fcn|str|imp|loc)\.([^:<(\\\/ \|\])\->]+)/g, '<a href=\'javascript:r2ui.seek("$1.$2")\'>$1.$2</a>');
    }
    x = x.replace(/0x([a-zA-Z0-9]+)/g, '<a href=\'javascript:r2ui.seek("0x$1")\'>0x$1</a>');
    // registers
    if (backward) {
        prev_curoff = curoff;
        prev_lastoff = lastoff;
    } else {
        next_curoff = curoff;
        next_lastoff = lastoff;
        if (!prev_curoff) {
            prev_curoff = next_curoff;
        }
    }
    return x;
};