Skip to content

Latest commit

 

History

History
95 lines (81 loc) · 4.17 KB

DEVELOPERS.md

File metadata and controls

95 lines (81 loc) · 4.17 KB

Developing on r2dec

r2dec is mostly written in javascript and the engine is duktape.

Duktape APIs: https://duktape.org/api.html

Extending r2dec arch

First of all when you need to add a new architecture, you need to create a new .js file inside libdec/arch/.

For example libdec/arch/arch9999.js and it needs to follow the minimal base javascript template:

module.exports = (function() {
    const Base = require('libdec/core/base');
    const Variable = require('libdec/core/variable');
    const Extra = require('libdec/core/extra');
    return {
        instructions: {
            add: function(instr, context, instructions) {
                return Base.add(instr.parsed.opd[0], instr.parsed.opd[1], instr.parsed.opd[2]);
            },
            nop: function() {
                return Base.nop();
            },
            invalid: function(instr, context, instructions) {
                return Base.nop();
            }
        },
        parse: function(assembly) {
            var tokens = assembly.trim().split(' ');
            return { mnem: tokens.shift(), opd: tokens };
        },
        context: function() {
            return { cond: { a: '?', b: '?' } };
        },
        preanalisys: function(instructions, context) {},
        postanalisys: function(instructions, context) {},
        localvars: function(context) {
            return [];
        },
        globalvars: function(context) {
            return [];
        },
        arguments: function(context) {
            return [];
        },
        returns: function(context) {
            return 'void';
        }
    };
})();

After saving the new arch (arch9999.js in the example), you need to add this arch to the file libdec/Archs.js.

The new architecture needs to have the same name as the cmd e asm.arch, because the architecture is choosen from that input, regardless the bits, etc.. of the arch.

For example:

module.exports = (function() {
    return {
        arm: require('libdec/arch/arm'),
        arch9999: require('libdec/arch/arch9999'),
        x86: require('libdec/arch/x86')
    };
})();

Codebase:

So the codebase will use the Base object.

  • All the common instructions are under Base.*; they will follow the following input: fcn(destination, reg0, reg1, ...).
    • Base.composed allows to build a set of readable instructions that can be used to express a complex opcode/instruction (see for example rlwimi under ppc.js).
  • Variable.* will include the creators for known arguments, like
    • Variable.functionPointer(value, type_or_bits, is_signed) defines a function pointer (uint16_t (*mypointer)(...)) as argument for Base.*.
    • Variable.pointer(value, type_or_bits, is_signed) defines a pointer (uint16_t* mypointer) as argument for Base.*.
    • Variable.local(value, type_or_bits, is_signed) defines a local (int32_t mylocal) as argument for Base.*.
    • Variable.string(content) is ment to be used for strings as argument of Base.* functions; eg: return Base.assign('r0', Base.string('"wooooow"'));.
  • var Long = require('libdec/long'); can be used to support 64 bits values on javascript.

All the instructions added under arch.instructions.* will have the following data as input (instr, context, instructions), where:

  • instr is the current instruction analized.
  • context is the an object that can be used to store values tha will be used by instructions that will be analyzed later.
  • instructions is the array with all the libdec/instruction.js derived objects that can be used to recover some required infos that might be needed. One last thing:
  • context used by instructions to store/retrieve data is generated by arch.context().
  • arch.parse is used to parse the instruction to a json object:
    • an example: "add r0, r1, r2" to { "mnem": "add", "opd": ["r0", "r1", "r2"] }.
    • assembly is a string containing the r2 enriched assembly (for example: call sym.imp.__libc_start_main).
    • simplified is a string containing the standard assembly (for example: call 0x4a234).
    • the returned object is available under instr.parsed, meanwhile the original string can still be found under instr.assembly or instr.simplified.

Deroad.