💾 Archived View for radare.org › book › plugins › python.gmi captured on 2024-05-26 at 14:52:53. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-11-04)

➡️ Next capture (2024-08-18)

-=-=-=-=-=-=-

Python plugins

At first, to be able to write a plugins in Python for radare2 you need to install r2lang plugin: `r2pm -i lang-python`.

Note - in the following examples there are missing functions of the actual decoding for the sake of readability!

For this you need to do this: 1. `import r2lang` and `from r2lang import R` (for constants) 2. Make a function with 2 subfunctions - `assemble` and `disassemble` and returning plugin structure - for RAsm plugin ```python def mycpu(a):

def assemble(s):
    return [1, 2, 3, 4]
def disassemble(memview, addr):
    try:
        opcode = get_opcode(memview) # https://docs.python.org/3/library/stdtypes.html#memoryview
        opstr = optbl[opcode][1]
        return [4, opstr]
    except:
        return [4, "unknown"]
3. This structure should contain a pointers to these 2 functions - `assemble` and `disassemble`

return {
        "name" : "mycpu",
        "arch" : "mycpu",
        "bits" : 32,
        "endian" : R.R_SYS_ENDIAN_LITTLE,
        "license" : "GPL",
        "desc" : "MYCPU disasm",
        "assemble" : assemble,
        "disassemble" : disassemble,
}
4. Make a function with 2 subfunctions - `set_reg_profile` and `op` and returning plugin structure - for RAnal plugin

def mycpu_anal(a): def set_reg_profile():

    profile = "=PC	pc\n" + \

"=SP sp\n" + \ "gpr r0 .32 0 0\n" + \ "gpr r1 .32 4 0\n" + \ "gpr r2 .32 8 0\n" + \ "gpr r3 .32 12 0\n" + \ "gpr r4 .32 16 0\n" + \ "gpr r5 .32 20 0\n" + \ "gpr sp .32 24 0\n" + \ "gpr pc .32 28 0\n" return profile

def op(memview, pc):

analop = { "type" : R.R_ANAL_OP_TYPE_NULL, "cycles" : 0, "stackop" : 0, "stackptr" : 0, "ptr" : -1, "jump" : -1, "addr" : 0, "eob" : False, "esil" : "", } try:

        opcode = get_opcode(memview) # https://docs.python.org/3/library/stdtypes.html#memoryview
        esilstr = optbl[opcode][2]
        if optbl[opcode][0] == "J": # it's jump
            analop["type"] = R.R_ANAL_OP_TYPE_JMP
            analop["jump"] = decode_jump(opcode, j_mask)
            esilstr = jump_esil(esilstr, opcode, j_mask)
    except:
        result = analop

Don't forget to return proper instruction size! return [4, result]

5. This structure should contain a pointers to these 2 functions - `set_reg_profile` and `op`

return {
        "name" : "mycpu",
        "arch" : "mycpu",
        "bits" : 32,
        "license" : "GPL",
        "desc" : "MYCPU anal",
        "esil" : 1,
        "set_reg_profile" : set_reg_profile,
        "op" : op,
}
6. (Optional) To add extra information about op sizes and alignment, add a `archinfo` subfunction and point to it in the structure

def mycpu_anal(a): def set_reg_profile():

    [...]
def archinfo(query):
    if query == R.R_ANAL_ARCHINFO_MIN_OP_SIZE:
        return 1
    if query == R.R_ANAL_ARCHINFO_MAX_OP_SIZE:
        return 8
    if query == R.R_ANAL_ARCHINFO_INV_OP_SIZE:  # invalid op size
        return 2
    return 0
def analop(memview, pc):
    [...]
return {
        "name" : "mycpu",
        "arch" : "mycpu",
        "bits" : 32,
        "license" : "GPL",
        "desc" : "MYCPU anal",
        "esil" : 1,
        "set_reg_profile" : set_reg_profile,
        "archinfo": archinfo,
        "op" : op,
}
7. Register both plugins using `r2lang.plugin("asm")` and `r2lang.plugin("anal")` respectively

print("Registering MYCPU disasm plugin...") print(r2lang.plugin("asm", mycpu)) print("Registering MYCPU analysis plugin...") print(r2lang.plugin("anal", mycpu_anal)) ```

You can combine everything in one file and load it using `-i` option: ``` r2 -I mycpu.py some_file.bin ``` Or you can load it from the r2 shell: `#!python mycpu.py`

See also:

- Python[1]

1: Python

- Javascript[1]

1: Javascript

Implementing new format plugin in Python

Note - in the following examples there are missing functions of the actual decoding for the sake of readability!

For this you need to do this: 1. `import r2lang` 2. Make a function with subfunctions:

- `load`

- `load_bytes`

- `destroy`

- `check_bytes`

- `baddr`

- `entries`

- `sections`

- `imports`

- `relocs`

- `binsym`

- `info`

and returning plugin structure - for RAsm plugin ```python def le_format(a):

def load(binf):
    return [0]
def check_bytes(buf):
    try:

if buf[0] == 77 and buf[1] == 90: lx_off, = struct.unpack("<I", buf[0x3c:0x40]) if buf[lx_off] == 76 and buf[lx_off+1] == 88:

                return [1]
        return [0]
    except:
        return [0]
and so on. Please be sure of the parameters for each function and format of returns.
Note, that functions `entries`, `sections`, `imports`, `relocs` returns a list of special
formed dictionaries - each with a different type.
Other functions return just a list of numerical values, even if single element one.
There is a special function, which returns information about the file - `info`:
def info(binf):
    return [{
            "type" : "le",
            "bclass" : "le",
            "rclass" : "le",
            "os" : "OS/2",
            "subsystem" : "CLI",
            "machine" : "IBM",
            "arch" : "x86",
            "has_va" : 0,
            "bits" : 32,
            "big_endian" : 0,
            "dbg_info" : 0,
            }]

3. This structure should contain a pointers to the most important functions like
`check_bytes`, `load` and `load_bytes`, `entries`, `relocs`, `imports`.

return {
        "name" : "le",
        "desc" : "OS/2 LE/LX format",
        "license" : "GPL",
        "load" : load,
        "load_bytes" : load_bytes,
        "destroy" : destroy,
        "check_bytes" : check_bytes,
        "baddr" : baddr,
        "entries" : entries,
        "sections" : sections,
        "imports" : imports,
        "symbols" : symbols,
        "relocs" : relocs,
        "binsym" : binsym,
        "info" : info,
}
4. Then you need to register it as a file format plugin:

print("Registering OS/2 LE/LX plugin...") print(r2lang.plugin("bin", le_format)) ```