💾 Archived View for radare.org › book › plugins › python.gmi captured on 2024-08-18 at 17:18:00. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-11-04)
-=-=-=-=-=-=-
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
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]
- Javascript[1]
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)) ```