www/lib/disasm.js
var BBGraph = function() {
this.vertices = {};
this.edges = [];
this.elements = [];
this.links = [];
this.fcn_offset = 0;
joint.shapes.html = {};
joint.shapes.html.Element = joint.shapes.basic.Rect.extend({
defaults: joint.util.deepSupplement({
type: 'html.Element',
attrs: {
rect: { stroke: r2ui.colors['.ec_gui_border'], fill: r2ui.colors['.ec_gui_alt_background'] }
}
}, joint.shapes.basic.Rect.prototype.defaults)
});
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
initialize: function() {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.model.get('html'))());
this.$box.find('input').on('mousedown click', function(evt) { evt.stopPropagation(); });
this.model.on('change', this.updateBox, this);
this.updateBox();
},
render: function() {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
this.updateBox();
return this;
},
updateBox: function() {
// move the html mask when moving the svg rect
var bbox = this.model.getBBox();
this.$box.css({ width: bbox.width + 2, height: bbox.height - 6, left: bbox.x - 1, top: bbox.y + 7});
}
});
};
BBGraph.prototype.addVertex = function(addr, vlen, dom) {
// if vertex is not yet created, do it
if (this.vertices[addr] === undefined) {
this.vertices[addr] = {};
this.vertices[addr].parents = [];
this.vertices[addr].children = [];
if (vlen === undefined && dom === undefined) {
this.vertices[addr].len = 1;
dom = document.createElement('div');
dom.id = 'bb_' + addr;
dom.className = 'basicblock enyo-selectable ec_gui_background ec_gui_border';
dom.innerHTML = '<div class=\'instruction enyo-selectable\'><span class=\'insaddr datainstruction ec_offset addr addr_0x' + addr.toString(16) + '\' >0x' + addr.toString(16) + '</span></div>';
this.vertices[addr].rendered = dom;
}
}
// function is called with vlen and dom arguments, set them
if (vlen !== undefined && dom !== undefined) {
this.vertices[addr].len = vlen;
this.vertices[addr].rendered = dom;
}
};
BBGraph.prototype.addEdge = function(v1, v2, color) {
this.addVertex(v1);
this.addVertex(v2);
this.edges.push({'from': v1, 'to': v2, 'color': color});
this.vertices[v1].children.push(v2);
this.vertices[v2].parents.push(v1);
};
BBGraph.prototype.makeElement = function(addr, width, height, html) {
this.elements.push(new joint.shapes.html.Element({
id: String(addr),
size: { width: width, height: height },
html: html
}));
};
BBGraph.prototype.makeLink = function(v1, v2, color) {
this.links.push(new joint.dia.Link({
source: { id: String(v1) },
target: { id: String(v2) },
attrs: {
'.marker-target': {
d: 'M 6 0 L 0 3 L 6 6 z',
fill: color,
stroke: color
},
'.connection': {
'stroke-width': 1,
stroke: color
},
'.link-tools': {
display: 'none'
}
},
smooth: true
}));
};
adjustVertices = function(graph, cell) {
// If the cell is a view, find its model.
cell = cell.model || cell;
if (cell instanceof joint.dia.Element) {
_.chain(graph.getConnectedLinks(cell)).groupBy(function(link) {
// the key of the group is the model id of the link's source or target, but not our cell id.
return _.omit([link.get('source').id, link.get('target').id], cell.id)[0];
}).each(function(group, key) {
// If the member of the group has both source and target model adjust vertices.
if (key !== 'undefined') adjustVertices(graph, _.first(group));
});
return;
}
var srcId = cell.get('source').id || cell.previous('source').id;
var trgId = cell.get('target').id || cell.previous('target').id;
var siblings = _.filter(graph.getLinks(), function(sibling) {
var _srcId = sibling.get('source').id;
var _trgId = sibling.get('target').id;
return (_srcId === srcId && _trgId === trgId) ||
(_srcId === trgId && _trgId === srcId);
});
// more than one link between two blocks
if (siblings.length > 1) {
var srcbox = r2ui.graph.getCell(srcId).getBBox();
var dstbox = r2ui.graph.getCell(trgId).getBBox();
src = srcbox.intersectionWithLineFromCenterToPoint(dstbox.center());
dst = dstbox.intersectionWithLineFromCenterToPoint(srcbox.center());
var midPoint = g.line(src, dst).midpoint();
var theta = src.theta(dst);
var gap = 10;
// if the vertex is in the rect : bug
// vertex doesn't seem to go to the right place
_.each(siblings, function(sibling, index) {
var offset = gap;
var sign = index % 2 ? 1 : -1;
var angle = g.toRad(theta + sign * 90);
var vertex = g.point.fromPolar(offset, angle, midPoint);
// we tell the link deviate to the right or to the left
// from its path depending on sign
// ^ ^
// | / \
// | => x x
// | \ /
// v v
// if the vertex is inside one of the box, don't do anything
// they are very close and this will result in a rendering bug
if (!srcbox.containsPoint(vertex) && !dstbox.containsPoint(vertex)) {
sibling.set('vertices', [{ x: vertex.x, y: vertex.y }]);
} else {
sibling.unset('vertices');
}
});
}
};
BBGraph.prototype.render = function() {
var name = Object.keys(this.vertices).toString();
var outergbox = document.createElement('div');
outergbox.id = 'outergbox';
var bbcanvas = document.getElementById('canvas');
var gbox = document.createElement('div');
gbox.id = 'gbox';
gbox.className = name;
outergbox.appendChild(gbox);
bbcanvas.appendChild(outergbox);
for (var addr in this.vertices) {
var r = this.vertices[addr].rendered;
if (r !== undefined) {
gbox.appendChild(r);
this.makeElement(addr, r.offsetWidth, r.offsetHeight, r.outerHTML);
}
}
for (var j = 0; j < this.edges.length; j++) {
this.makeLink(this.edges[j].from, this.edges[j].to, this.edges[j].color);
}
$('#outergbox').remove();
this.makeElement('minimap_area', 1, 1, '<div id=\'minimap_area\'>');
var items = this.elements.concat(this.links);
var width = $('#center_panel').width();
var graph = new joint.dia.Graph();
var paper = new joint.dia.Paper({
el: $('#canvas'),
gridSize: 1,
width: 2000,
height: 6000,
model: graph
});
var minimap_width = 200;
var minimap_heigh = 200;
$('#minimap').html('');
$('#minimap').html('');
var minimap = new joint.dia.Paper({
el: $('#minimap'),
gridSize: 1,
width: minimap_width,
height: minimap_heigh,
model: graph
});
graph.resetCells(items);
// render graph
joint.layout.DirectedGraph.layout(graph);
r2ui.graph = graph;
// reposition graph
reposition_graph();
// remove html mask in minimap since its not scaled
$('#minimap .basicblock').remove();
// make minimap rect transparent
graph.getCell('minimap_area').attr({rect: { stroke: 'transparent'}});
var svg_width = $('#canvas svg')[0].getBBox().width;
var svg_height = $('#canvas svg')[0].getBBox().height;
// update paper size with these values
paper.setDimensions(svg_width + 500, svg_height + 500);
var ws = Math.ceil(svg_width / minimap_width);
var hs = Math.ceil(svg_height / minimap_heigh);
var scale = 1 / Math.max(ws, hs);
var delta = 0;
if (hs > ws) delta = (minimap_width / 2) - svg_width * scale / 2;
minimap.scale(scale);
minimap.setOrigin(delta, 0);
// minimap.$el.css('pointer-events', 'none');
// enyo layout
if ($('#radareApp_mp').length) {
$('#minimap').css('left', $('#main_panel').width() - minimap_width - $('#main_panel').position().left);
$('#minimap').css('top', $('#center_panel').position().top);
$('#main_panel').bind('scroll', update_minimap);
// panel layout
} else if ($('#main_panel').length) {
$('#minimap').css('left', $('#main_panel').width() - minimap_width);
$('#minimap').css('top', $('#center_panel').position().top - 40);
$('#center_panel').bind('scroll', update_minimap);
}
paper.on('cell:pointerup', function(cellview) {
var model = cellview.model;
var bbox = model.attributes.position;
var id = String(model.prop('id'));
if (model !== undefined && id !== 'minimap_area') {
var color = null;
var bb = r2ui.get_fcn_BB(r2ui.current_fcn_offset, id);
if (bb !== undefined && bb !== null) {
if (bb.x != String(bbox.x) || bb.y != String(bbox.y)) {
bb.x = bbox.x;
bb.y = bbox.y;
r2ui.update_fcn_BB(r2ui.current_fcn_offset, id, bb);
}
} else if (bb !== undefined && bb !== null) {
r2ui.update_fcn_BB(r2ui.current_fcn_offset, id, {x: bbox.x, y: bbox.y});
}
}
});
var myAdjustVertices = _.partial(adjustVertices, graph);
_.each(graph.getLinks(), myAdjustVertices);
paper.on('cell:pointerup', myAdjustVertices);
if (r2ui._dis.minimap) {
update_minimap();
$('#minimap_area').draggable({
containment: 'parent',
stop: function(event, ui) {
var delta_x = ui.position.left / scale;
var delta_y = ui.position.top / scale;
if (delta_x < 0) delta_x = 0;
if (delta_y < 0) delta_y = 0;
if ($('#radareApp_mp').length) $('#main_panel').scrollTo({ top: delta_y, left: delta_x - delta / scale });
else $('#center_panel').scrollTo({ top: delta_y, left: delta_x - delta / scale });
}
});
} else {
$('#minimap').hide();
}
};
function toggle_minimap() {
if (r2ui._dis.minimap) {
r2ui._dis.minimap = false;
r2ui.seek(r2ui._dis.selected_offset, false);
$('#minimap').hide();
} else {
r2ui._dis.minimap = true;
r2ui.seek(r2ui._dis.selected_offset, false);
$('#minimap').show();
}
}
function update_minimap() {
if (r2ui._dis.minimap && $('#canvas svg').length) {
var minimap_width = 200;
var minimap_height = 200;
var svg_width = $('#canvas svg')[0].getBBox().width;
var svg_height = $('#canvas svg')[0].getBBox().height;
var ws = Math.ceil(svg_width / minimap_width);
var hs = Math.ceil(svg_height / minimap_height);
var scale = 1 / Math.max(ws, hs);
var delta = 0;
if (hs > ws) delta = (minimap_width / 2) - svg_width * scale / 2;
var el = null;
// enyo layout
if ($('#radareApp_mp').length) {
el = $('#main_panel');
// panel layout
} else if ($('#main_panel').length) {
el = $('#center_panel');
}
if (el.scrollTop() < svg_height) {
$('#minimap_area').width(el.width() * scale);
$('#minimap_area').height(el.height() * scale);
if (el.scrollTop() * scale <= minimap_height - el.height() * scale)
$('#minimap_area').css('top', el.scrollTop() * scale);
$('#minimap_area').css('left', delta + el.scrollLeft() * scale);
}
el = $('#center_panel');
// enyo layout
if ($('#radareApp_mp').length) {
$('#minimap').css('display', 'none');
$('#minimap').css('left', el.scrollLeft() + el.width() - minimap_width - $('#radareApp_mp').position().left + 2 * el.css('padding').replace('px', ''));
$('#minimap').css('top', el.scrollTop());
$('#minimap').css('display', 'block');
// panel layout
} else if ($('#main_panel').length) {
$('#minimap').css('left', el.scrollLeft() + $('#main_panel').width() - minimap_width);
$('#minimap').css('top', el.scrollTop());
}
$('#minimap').css('border', '1px solid ' + r2ui.colors['.ec_gui_background']);
$('#minimap_area').css('background', r2ui.colors['.ec_gui_background']);
}
}
function reposition_graph() {
var bbs = r2ui.graph.getElements();
var blocks = r2ui.get_fcn_BBs(r2ui.current_fcn_offset);
var bb_offsets = Object.keys(blocks);
for (var i in bbs) {
found = false;
for (var j in bb_offsets) {
var offset = String(bb_offsets[j]);
var bb = blocks[offset];
if (bbs[i].prop('id') === offset) {
found = true;
if (bb.x !== 'null' && bb.y !== 'null') {
bbs[i].translate(bb.x - bbs[i].prop('position').x, bb.y - bbs[i].prop('position').y);
}
var color = bb.color;
if (color !== null && color !== undefined) bbs[i].attr('rect/fill', color);
}
}
// if (!found) {
// r2ui.update_fcn_BB(r2ui.current_fcn_offset, bbs[i].prop("id"), {x:bbs[i].prop("position").x, y:bbs[i].prop("position").y, color:r2ui.colors['.ec_gui_alt_background']});
// }
}
}
var flag = 0;
function render_graph(x) {
var obj;
try {
obj = JSON.parse(x.replace(/\\l/g, '\\n'));
} catch (e) {
console.log('Cannot parse JSON data');
}
if (obj[0] === undefined) return false;
if (obj[0].blocks === undefined) return false;
var graph = new BBGraph();
r2ui.current_fcn_offset = obj[0].blocks[0].ops[0].offset;
for (var bn = 0; bn < obj[0].blocks.length; bn++) {
var bb = obj[0].blocks[bn];
var addr = bb.offset;
if (bb['trace'] !== undefined) {
var bbinfo = r2ui.get_fcn_BB(r2ui.current_fcn_offset, addr);
if (bbinfo !== undefined) {
if (bbinfo.color !== 'red')
bbinfo.color = '#7592DF';
} else {
bbinfo = {x: null, y: null, color: '#7592DF'};
}
r2ui.update_fcn_BB(r2ui.current_fcn_offset, addr, bbinfo);
}
if (bb.length === 0) continue;
var cnt = bb.ops.length;
var idump = '';
for (var i in bb.ops) {
var ins = bb.ops[i];
// ins.offset = "0x" + ins.offset.toString(16);
if (ins.comment === undefined || ins.comment === null) ins.comment = '';
else {
ins.comment = atob(ins.comment);
}
idump += html_for_instruction(ins);
}
var dom = document.createElement('div');
dom.id = 'bb_' + addr;
dom.className = 'basicblock enyo-selectable ec_gui_background ec_gui_border';
dom.innerHTML = idump;
graph.addVertex(addr, cnt, dom);
if (bb.fail > 0) {
graph.addEdge(addr, bb.fail, 'red');
if (bb.jump > 0) {
graph.addEdge(addr, bb.jump, 'green');
}
} else if (bb.jump > 0) {
graph.addEdge(addr, bb.jump, 'blue');
}
}
graph.render();
var element = $('#canvas svg g .element');
element.on('mousedown', function() {
flag = 0;
});
element.on('mousemove', function() {
flag = 1;
});
element.on('mouseup', function(event) {
if (flag === 0) {
var id = event.target.parentNode.parentNode.parentNode.getAttribute('model-id');
if (id !== 'minimap_area') {
var color = null;
var bb = r2ui.get_fcn_BB(r2ui.current_fcn_offset, id);
if (bb !== undefined && bb !== null) {
if (bb.color === 'red') bb.color = r2ui.colors['.ec_gui_alt_background'];
else bb.color = 'red';
} else {
bb = {x: 'null', y: 'null', color: 'red'};
}
r2ui.update_fcn_BB(r2ui.current_fcn_offset, id, bb);
reposition_graph();
}
}
});
$('.addr').css('-webkit-user-select', 'text');
return true;
}
function render_instructions(instructions) {
var outergbox = document.createElement('div');
outergbox.id = 'outergbox';
var flatcanvas = document.getElementById('canvas');
flatcanvas.innerHTML = '';
var gbox = document.createElement('div');
gbox.id = 'gbox';
gbox.className = 'ec_gui_background';
outergbox.appendChild(gbox);
flatcanvas.appendChild(outergbox);
var flatcanvas_rect = getOffsetRect(flatcanvas);
var asm_lines = (r2.settings['asm.lines']);
var asm_offset = (r2.settings['asm.offset']);
var accumulated_heigth = flatcanvas_rect.top;
var lines = [];
var targets = {};
var first_address = instructions[0].offset;
var last_address = instructions[instructions.length - 1].offset;
for (var i in instructions) {
var ins = instructions[i];
if ((ins.type == 'jmp' || ins.type == 'cjmp') && ins.jump !== undefined && ins.jump !== null) {
var line = {};
line.from = ins.offset;
if (last_address < ins.jump) {
line.to_end = false;
line.to = last_address;
} else if (first_address > ins.jump) {
line.to_end = false;
line.to = first_address;
} else {
line.to_end = true;
line.to = ins.jump;
}
if (ins.type == 'jmp') {
line.color = r2ui.colors['.ec_flow'];
line.dashed = false;
} else if (ins.type == 'cjmp') {
line.color = r2ui.colors['.ec_gui_cflow'];
line.dashed = true;
}
line.to_start = true;
lines[lines.length] = line;
if (targets[line.to] === undefined) {
targets[line.to] = 0;
}
}
// ins.offset = "0x" + ins.offset.toString(16);
if (ins.comment === undefined || ins.comment === null) ins.comment = '';
else {
try {
ins.comment = atob(ins.comment);
} catch (e) {
console.log(ins.comment);
}
}
var dom = document.createElement('div');
if (asm_lines) dom.className = 'instructionbox enyo-selectable lines';
else dom.className = 'instructionbox';
dom.style.top = accumulated_heigth + 'px';
dom.innerHTML = html_for_instruction(ins);
gbox.appendChild(dom);
var instruction_rect = getOffsetRect(dom);
var instruction_heigth = instruction_rect.bottom - instruction_rect.top;
accumulated_heigth += instruction_heigth;
}
if (asm_lines) {
var canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = accumulated_heigth;
canvas.id = 'linecanvas';
canvas.setAttribute('tabindex', '1');
canvas.setAttribute('style', 'outline: none;');
gbox.appendChild(canvas);
var ctx = canvas.getContext('2d');
if (!ctx.setLineDash) {
// For browsers that dont support dashed lines
ctx.setLineDash = function() {};
}
var num_targets = countProperties(targets);
var num_assigned_paths = 0;
var lines_width = 100;
for (var l in lines) {
var line = lines[l];
var from = '0x' + line.from.toString(16);
var to = '0x' + line.to.toString(16);
if (targets[line.to] === 0) {
// No path assigned for target, assigning a new one
targets[line.to] = (num_targets - num_assigned_paths - 1) * (90 / (num_targets + 1));
num_assigned_paths += 1;
}
var from_element = get_element_by_address(from);
var to_element = get_element_by_address(to);
if (from_element !== null && from_element !== undefined && to_element !== undefined && to_element !== null) {
var x = targets[line.to];
var from_rect = getOffsetRect(from_element);
var y0 = (from_rect.top + from_rect.bottom) / 2;
var to_rect = getOffsetRect(to_element);
var y1 = (to_rect.top + to_rect.bottom) / 2;
if (line.to == instructions[0].offset) {
y1 = 0;
}
// main line
ctx.beginPath();
ctx.moveTo(x, y0);
ctx.lineTo(x, y1);
ctx.strokeStyle = line.color;
if (line.dashed) ctx.setLineDash([2,3]);
ctx.stroke();
if (line.to_start) {
// horizontal line at start
ctx.beginPath();
ctx.moveTo(x, y0);
ctx.lineTo(lines_width - 5, y0);
ctx.strokeStyle = line.color;
if (line.dashed) ctx.setLineDash([2,3]);
ctx.stroke();
// circle
ctx.beginPath();
ctx.arc(lines_width - 5 - 2, y0, 2, 0, 2 * Math.PI, false);
ctx.fillStyle = line.color;
ctx.fill();
}
if (line.to_end) {
// horizontal line at end
ctx.beginPath();
ctx.moveTo(x, y1);
ctx.lineTo(lines_width - 5, y1);
ctx.strokeStyle = line.color;
if (line.dashed) ctx.setLineDash([2,3]);
ctx.stroke();
// arrow
ctx.beginPath();
ctx.moveTo(lines_width - 5, y1);
ctx.lineTo(lines_width - 10, y1 - 5);
ctx.lineTo(lines_width - 10, y1 + 5);
ctx.lineWidth = 1;
ctx.fillStyle = line.color;
ctx.fill();
}
}
}
}
if (!asm_offset) {
var elements = document.getElementsByClassName('insaddr');
for (var j in elements) {
if (elements[j].style) elements[j].style.display = 'none';
}
}
$('.addr').css('-moz-user-select', 'text');
$('.addr').css('-webkit-user-select', 'text');
}
function getOffsetRect(elem) {
var box = elem.getBoundingClientRect();
var offset = $('#gbox').offset().top;
var top = box.top - offset;
var bottom = box.bottom - offset;
return {top: Math.round(top), bottom: Math.round(bottom)};
}
function countProperties(obj) {
var count = 0;
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
++count;
}
}
return count;
}
function toBoolean(str) {
if (str === 'true') return true;
else if (str === 'false') return false;
else return undefined;
}
function html_for_instruction(ins) {
var idump = '<div class="instruction enyo-selectable">';
var offset = '0x' + ins.offset.toString(16);
var address = offset;
var asm_flags = (r2.settings['asm.flags']);
var asm_bytes = (r2.settings['asm.bytes']);
var asm_xrefs = (r2.settings['asm.xrefs']);
var asm_cmtright = (r2.settings['asm.cmtright']);
if (ins.fcn_addr > 0 && offset === "0x"+ins.fcn_addr.toString(16)) {
if (r2ui._dis.display == "flat") idump += '<div class="ec_flow">; -----------------------------------------------------------</div>';
// Get Instruction info
var results;
var cmd = "afij " + offset;
r2.cmd(cmd, function(x){
results = x.split("\n");
});
try {
var info = JSON.parse(results[0]);
if (info !== null && info !== undefined && info.length > 0)
idump += '<div class="ec_fname">(fcn) ' + info[0].name + '</div>';
} catch (err) {
console.log("Error getting instruction information from afij command");
}
// Get function variables
var vars_reg_based = [];
cmd = "afvrj " + offset;
r2.cmd(cmd, function(x){
vars_reg_based = x.split("\n");
});
try {
var vars = JSON.parse(vars_reg_based[0]);
var fvars = [];
for (var i in vars) {
idump += '<div class="ec_flag">; ' + vars[i].kind + " " + vars[i].type + " <span class='fvar id_" + address_canonicalize(offset) + "_" + vars[i].ref + " ec_prompt faddr faddr_" + address_canonicalize(offset) + "'>" + escapeHTML(vars[i].name) + "</span> @ " + vars[i].ref + '</div>';
fvars[fvars.length] = {name: vars[i].name, id: address_canonicalize(offset) + "_" + vars[i].ref};
}
r2.varMap[ins.fcn_addr] = fvars;
} catch (err) {
console.log("Error getting variable information from afvj command");
}
// Get function arguments
var args_merged = [];
var args_bp_based = [];
var args_sp_based = [];
cmd = "afvbj " + offset;
r2.cmd(cmd, function(x){
args_bp_based = x.split("\n");
});
cmd = "afvsj " + offset;
r2.cmd(cmd, function(x){
args_sp_based = x.split("\n");
});
args_merged = args_bp_based.concat(args_sp_based);
try {
var args = JSON.parse(args_merged[0]);
var fargs = [];
for (var i in args) {
var ref = ""
if (args[i].ref !== null && args[i].ref !== undefined) {
var ref_base = args[i].ref.base;
var ref_offset = args[i].ref.offset;
var ref_type = args[i].ref.type;
var ref_sign = "+";
if (ref_offset - 0) ref_sig = "-";
ref = ref_base + ref_sign + ref_offset;
}
idump += '<div class="ec_flag">; ' + args[i].kind + " " + args[i].type + " <span class='farg id_" + address_canonicalize(offset) + "_" + ref + " ec_prompt faddr faddr_" + address_canonicalize(offset) + "'>" + escapeHTML(args[i].name) + "</span> @ " + ref + '</div>';
fargs[fargs.length] = {name: args[i].name, id: address_canonicalize(offset) + "_" + args[i].ref};
}
r2.argMap[ins.fcn_addr] = fargs;
} catch (err) {
console.log("Error getting argument information from afaj command");
}
}
if (asm_flags) {
var flags;
if (ins.flags !== undefined && ins.flags !== null) {
flags = ins.flags.join(';');
} else {
flags = r2.get_flag_names(address_canonicalize(offset)).join(';');
}
if (flags !== '' && flags !== undefined && flags !== null) idump += '<div class="ec_flag flags_' + address_canonicalize(offset) + '">;-- ' + escapeHTML(flags) + ':</div> ';
}
if (ins.comment && !asm_cmtright) {
idump += '<div class="comment ec_comment comment_' + address_canonicalize(offset) + '">; ' + escapeHTML(ins.comment) + '</div>';
}
if (asm_xrefs) {
if (ins.xrefs !== undefined && ins.xrefs !== null && ins.xrefs.length > 0) {
var xrefs = '';
for (var i in ins.xrefs) {
var xref = ins.xrefs[i];
var name = '';
var xrefoffset = '0x' + xref.addr.toString(16);
if (r2.get_flag_names(address_canonicalize(xrefoffset)).length > 0) name = ' (' + r2.get_flag_names(address_canonicalize(xrefoffset)).join(';') + ')';
idump += '<div class="ec_flag xrefs">; ' + xref.type.toUpperCase() + ' XREF from ' +
'<span class="offset addr addr_' + address_canonicalize(xrefoffset) + '">' + xrefoffset + '</span> ' + name + '</div> ';
}
}
}
idump += '<span class="insaddr datainstruction ec_offset addr addr_' + address_canonicalize(offset) + '">' + address + '</span> ';
if (asm_bytes) {
if (ins.bytes !== undefined && ins.bytes !== null && ins.bytes !== '') {
var dorep = function(a) {
if (a == '00') return '<span class="ec_b0x00">00</span>';
if (a == 'ff') return '<span class="ec_b0x00">ff</span>';
if (a == '7f') return '<span class="ec_b0x00">7f</span>';
};
var bytes = ins.bytes.replace(new RegExp('(00)|(ff)|(7f)', 'g'), dorep);
idump += '<span class="bytes ec_other">' + bytes + '</span> ';
}
}
var opcode = highlight_instruction(ins.opcode, true);
if ((r2.varMap[ins.fcn_addr] !== null && r2.varMap[ins.fcn_addr] !== undefined && r2.varMap[ins.fcn_addr].length > 0) ||
(r2.argMap[ins.fcn_addr] !== null && r2.argMap[ins.fcn_addr] !== undefined && r2.argMap[ins.fcn_addr].length > 0)) {
for (var i in r2.varMap[ins.fcn_addr]) {
var var_name = r2.varMap[ins.fcn_addr][i].name;
var var_id = r2.varMap[ins.fcn_addr][i].id;
opcode = opcode.replace(' ' + var_name + ' ', ' <span class=\'fvar id_' + var_id + ' ec_prompt faddr faddr_' + address_canonicalize(offset) + '\'>' + escapeHTML(var_name) + '</span> ');
}
for (var i in r2.argMap[ins.fcn_addr]) {
var arg_name = r2.argMap[ins.fcn_addr][i];
var arg_id = r2.argMap[ins.fcn_addr][i].id;
opcode = opcode.replace(' ' + arg_name + ' ', ' <span id=\'fvar id_' + var_id + ' ec_prompt faddr faddr_' + address_canonicalize(offset) + '\'>' + escapeHTML(var_name) + '</span> ');
}
}
if (ins.type !== undefined && ins.type !== null) {
if (contains(math, ins.type)) ins.type = 'math';
if (contains(bin, ins.type)) ins.type = 'bin';
if (ins.type == 'ill') ins.type = 'invalid';
if (ins.type == 'null') ins.type = 'invalid';
if (ins.type == 'undefined') ins.type = 'invalid';
if (ins.type == 'ujmp') ins.type = 'jmp';
if (ins.type == 'upush') ins.type = 'push';
if (ins.type == 'upop') ins.type = 'pop';
if (ins.type == 'ucall') ins.type = 'call';
if (ins.type == 'lea') ins.type = 'mov';
// Add default color if we failed to identify op type
if (!contains(known_types, ins.type)) ins.type = 'other';
idump += '<div class="instructiondesc ec_' + ins.type + '">' + opcode + '</div> ';
} else {
idump += '<div class="instructiondesc">' + opcode + '</div> ';
}
if (ins.ptr_info) {
idump += '<span class="comment ec_comment comment_' + address_canonicalize(offset) + '">' + escapeHTML(ins.ptr_info) + '</span>';
}
if (ins.comment && asm_cmtright) {
idump += '<span class="comment ec_comment comment_' + address_canonicalize(offset) + '"> ; ' + escapeHTML(ins.comment) + '</span>';
}
if (ins.type == 'ret') {
idump += '<div> </div>';
}
idump += '</div>';
return idump;
}
var math = ['add', 'sub', 'mul', 'imul', 'div', 'idiv', 'neg', 'adc', 'sbb', 'inc', 'dec', '.byte'];
var bin = ['xor', 'and', 'or', 'not'];
var regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI', 'EIP', 'RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI', 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'RIP'];
var known_types = ['fline','help','args','label','flow','prompt','input','btext','swi','comment','fname','flag','offset','other','b0x00','b0x7f','b0xff','math','bin','push','pop','jmp','cjmp','call','nop','ret','trap','invalid','cmp','reg','creg','mov','num'];
var escapeHTML = (function() {
'use strict';
var chr = { '"': '"', '&': '&', '<': '<', '>': '>' };
return function(text) {
return text ? text.replace(/[\"&<>]/g, function(a) { return chr[a]; }) : '';
};
}());
function highlight_instruction(line, instruction) {
if (line === undefined) return 'undefined';
if (instruction === undefined) instruction = true;
var ret = escapeHTML(line);
// highlight registers and addresses
var re = '(0x[0123456789abcdef]+)';
// Map with register names as keys and HTML span representing the register as value
var reps = {};
if (instruction) {
for (var i in regs) {
// Loop all the registers
var rep = '<span class="ec_reg">' + regs[i] + '</span>';
reps[regs[i]] = rep;
rep = '<span class="ec_reg">' + regs[i].toLowerCase() + '</span>';
reps[regs[i].toLowerCase()] = rep;
}
for (i in reps) {
// Complete regexp with registers
re += '|(' + i + ')';
}
}
function dorep(a) {
// If address found
if (a.substr(0, 2) == '0x') {
// Look for imports or functions
var syms = r2.get_flag_names(address_canonicalize(a));
for (var i in syms) {
if (syms[i].indexOf('sym.imp.')) {
return '<span class=\'ec_offset addr addr_' + address_canonicalize(a) + '\'>' + syms[i] + '</span>';
}
if (syms[i].indexOf('fcn.')) {
return '<span class=\'ec_offset addr addr_' + address_canonicalize(a) + '\'>' + syms[i] + '</span>';
}
}
// Is it data address or code address
var cl = get_data_type(a);
if (cl === '') {
// Just an hex value, not an address
return '<span class=\'ec_num\'>' + a + '</span>';
} else if (cl === 'datainstruction') {
// An address representing data (memory) or code (instruction)
return '<span class=\'ec_offset addr addr_' + address_canonicalize(a) + '\'>' + a + '</span>';
} else if (cl === 'datamemory') {
// return "<span class='ec_dataoffset addr addr_" + address_canonicalize(a) + "'>" + a + "</span>";
return '<span class=\'ec_dataoffset\'>' + a + '</span>';
}
} else {
// Not an hex value, so a register
return reps[a];
}
}
return ret.replace(new RegExp(re, 'g'), dorep);
}
function hex2(a) {
if (a === undefined) return '__';
var ret = a.toString(16);
if (ret.length == 1) return '0' + ret;
return ret;
}
function hex(a) {
if (a === undefined) {
return '';
} else {
if (a < 0) a += 0x100000000;
return '0x' + a.toString(16);
}
}
function get_data_type(v, more) {
var a = r2.get_address_type(v);
if (a === '') return '';
else {
if (more !== undefined) {
return 'data' + a + ' addr addr_' + v;
} else {
return 'data' + a;
}
}
}
function fnum(a) {
return parseInt(a, 10);
}
function get_address_from_class(t, type) {
if (type === undefined) type = 'addr';
var prefix = type + '_';
if (!t) return undefined;
var l = t.className.split(' ').filter(function(x) { return x.substr(0, prefix.length) == type + '_'; });
if (l.length != 1) return undefined;
return l[0].split('_')[1].split(' ')[0];
}
function rehighlight_iaddress(address, prefix) {
if (prefix === undefined) prefix = 'addr';
$('.autohighlighti').removeClass('autohighlighti');
$('.' + prefix + '_' + address).addClass('autohighlighti');
if (prefix === 'addr') r2.cmd('s ' + address, function() {});
}
function rehighlight_id(eid) {
$('.autohighlighti').removeClass('autohighlighti');
$('#' + eid).addClass('autohighlighti');
}
function get_element_by_address(address) {
var elements = $('.insaddr.addr_' + address);
if (elements.length === 1) return elements[0];
else return null;
}
Element.prototype.documentOffsetTop = function() {
return this.offsetTop + (this.offsetParent ? this.offsetParent.documentOffsetTop() : 0);
};
function scroll_to_address(address) {
var elements = $('.insaddr.addr_' + address);
if (elements[0] !== null && elements[0] !== undefined) {
var top = elements[0].documentOffsetTop() - window.innerHeight / 2;
top = Math.max(0, top);
$('#main_panel').scrollTo({'top': top, 'left': 0});
}
}
function has_scrollbar(divnode) {
if (divnode.scrollHeight > divnode.clientHeight) return true;
return false;
}
function on_scroll(event) {
// console.log($(event.target).scrollTop());
if (!r2ui._dis.scrolling) {
var enyo = $('#radareApp').length ? true : false;
var panel_disas = false;
if (!enyo) panel_disas = $('#main_panel').tabs('option', 'active') === 0 ? true : false;
r2ui._dis.scrolling = true;
if (r2ui._dis.display == 'flat' && (enyo || panel_disas)) {
var scroll_offset = null;
var top_offset = null;
var addr = null;
if (enyo) {
scroll_offset = $('#main_panel').scrollTop();
top_offset = $('#gbox').height() - $('#main_panel').height() - 10;
container_element = $('#center_panel');
} else {
scroll_offset = $('#center_panel').scrollTop();
top_offset = $('#gbox').height() - $('#center_panel').height() - 10;
container_element = $('#disasm_tab');
}
if (has_scrollbar($('#center_panel')[0])) {
if (scroll_offset === 0) {
addr = '0x' + r2ui._dis.instructions[0].offset.toString(16);
// console.log("Scroll en top", scroll_offset, top_offset, addr);
r2.get_disasm_before(addr, 50, function(x) {
// console.log(x.length);
r2ui._dis.instructions = x.concat(r2ui._dis.instructions);
});
container_element.html('<div id=\'canvas\' class=\'canvas enyo-selectable ec_gui_background\'></div>');
render_instructions(r2ui._dis.instructions);
scroll_to_address(addr);
rehighlight_iaddress(r2ui._dis.selected_offset);
} else if (scroll_offset > top_offset) {
// console.log("Scroll en top", scroll_offset, top_offset)
addr = '0x' + r2ui._dis.instructions[r2ui._dis.instructions.length - 1].offset.toString(16);
r2.get_disasm_after(addr, 100, function(x) {
r2ui._dis.instructions = r2ui._dis.instructions.slice(0, -1).concat(x);
});
container_element.html('<div id=\'canvas\' class=\'canvas enyo-selectable ec_gui_background\'></div>');
render_instructions(r2ui._dis.instructions);
scroll_to_address(addr);
rehighlight_iaddress(r2ui._dis.selected_offset);
}
}
}
r2ui._dis.scrolling = false;
event.preventDefault();
}
}
function scroll_to_element(element) {
var top = element.documentOffsetTop() - (window.innerHeight / 2);
top = Math.max(0, top);
$('#main_panel').scrollTo({'top': top, 'left': 0});
// r2ui._dis.scrollTo(0,top);
}
function rename(offset, old_value, new_value, space) {
if (space === undefined) space = 'functions';
if (space == 'functions') {
// If current offset is the beginning of a function, rename it with afr
r2.cmdj('pdfj @ ' + offset + '|', function(x) {
if (x !== null && x !== undefined) {
if ('0x' + x.addr.toString(16) === offset) {
r2.cmd('afn ' + new_value + ' ' + offset, function() {
r2.update_flags();
return;
});
}
}
});
}
// Otherwise just add a flag
if (new_value !== '' && old_value !== '') {
var cmd = 'fs ' + space + ';fr ' + old_value + ' ' + new_value;
r2.cmd(cmd, function() {});
} else if (new_value === '' && old_value !== '') {
var cmd = 'fs ' + space + ';f-@' + offset;
r2.cmd(cmd, function() {});
} else if (new_value !== '' && old_value === '') {
var cmd = 'fs ' + space + ';f ' + new_value + ' @ ' + offset;
r2.cmd(cmd, function() {});
}
r2.update_flags();
}
function address_canonicalize(s) {
s = s.substr(2);
while (s.substr(0, 1) == '0') s = s.substr(1);
s = '0x' + s;
s = s.toLowerCase();
return s;
}
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
function handleInputTextChange() {
r2ui._dis.handleInputTextChange();
}
function show_contextMenu(x, y) {
r2ui._dis.showContextMenu(x, y);
}
function get_offset_flag(offset) {
var old_value = '';
r2.cmdj('fs offsets;fj', function(x) {
for (var i in x) {
if ('0x' + x[i].offset.toString(16) == offset) {
old_value = x[i].name;
break;
}
}
});
return old_value;
}
function get_symbol_flag(symbol) {
var full_name = symbol;
var found = false;
r2.cmdj('fs symbols;fj', function(x) {
for (var i in x) {
if (x[i].name == symbol) {
found = true;
break;
}
}
if (!found) {
for (var i in x) {
if (x[i].name == 'sym.' + symbol) {
full_name = 'sym.' + symbol;
break;
}
}
}
});
return full_name;
}
function get_reloc_flag(reloc) {
var full_name = reloc;
var found = false;
r2.cmdj('fs relocs;fj', function(x) {
for (var i in x) {
if (x[i].name == reloc) {
found = true;
break;
}
}
if (!found) {
for (var i in x) {
if (x[i].name == 'reloc.' + reloc) {
full_name = 'reloc.' + reloc;
break;
}
}
}
});
return full_name;
}
// Cookies
function createCookie(name, value, days) {
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = '; expires=' + date.toGMTString();
} else var expires = '';
document.cookie = name + '=' + value + expires + '; path=/';
}
function readCookie(name) {
var nameEQ = name + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name, '', -1);
}
function do_randomcolors() {
r2.cmd('ecr;ec gui.background rgb:000', function() {
r2ui.load_colors();
});
}
function inColor(x) {
return 'e scr.color=true;' + x + ';e scr.color=false';
}