198 lines
10 KiB
Markdown
198 lines
10 KiB
Markdown
# Command parsing and command handling
|
|
|
|
Rizin has moved away from the default way of parsing radare2 commands and the
|
|
way commands were handled there. It enables by default what is/was called in r2
|
|
`cfg.newshell`, which enables a generated parser that parses rizin commands and
|
|
a new way of registering and developing commands.
|
|
|
|
## A bit of history
|
|
|
|
Rizin is a fork of radare2. Radare2 did not have, until recently, a generic
|
|
parser for user inputs, but each command had to parse its arguments by itself.
|
|
Moreover, there was no global register of commands available in the radare2
|
|
shell, instead the input was chopped by looking for specific characters and then
|
|
it was analyzed char by char, using big switch-cases to recognize the right
|
|
command.
|
|
|
|
As an example, you can see
|
|
[cmd_flag.c:1163](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd_flag.c#L1163),
|
|
which identifies the `fsr` command and then parses its input to check if an
|
|
argument was available or not.
|
|
|
|
This approach, although simple at the beginning, has some drawbacks like the
|
|
inconsistency coming from having many different places in the code doing mostly
|
|
the same thing (e.g. checking if an argument is available or not), the inability
|
|
to easily register/unregister new commands at runtime (e.g. a new Core plugin
|
|
that wants to provide a new command) or the inconsistency between commands
|
|
actually available and commands shown to users in help messages.
|
|
|
|
## Rizin shell
|
|
|
|
Not long ago, radare2 introduced the variable `cfg.newshell` that, when enabled,
|
|
allows you to use new features in the code. Rizin has chosen to enable this by
|
|
default and it is going to transition most commands to the new way of writing
|
|
commands, which will make it easier/faster to write commands and make the
|
|
overall CLI experience more consistent and reliable.
|
|
|
|
Rizin uses a parser generated with
|
|
[tree-sitter](https://tree-sitter.github.io/tree-sitter/), which allows you to
|
|
write grammars in JavaScript. You can see our grammar
|
|
[here](https://github.com/rizinorg/rizin/blob/dev/subprojects/rizin-shell-parser/grammar.js).
|
|
The parser recognizes the entire syntax of the rizin/radare2 shell language,
|
|
like:
|
|
|
|
- [basic statements](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/subprojects/rizin-shell-parser/grammar.js#L238): `<command-name> <arg1> <arg2> ... <argN>`
|
|
- [temporary modifier statements](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/subprojects/rizin-shell-parser/grammar.js#L98): `<statement> @ <address`, `<statement> @a:x86:32`, etc.
|
|
- [iterator statements](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/subprojects/rizin-shell-parser/grammar.js#L118): `<statement> @@ sym.*`, `<statement> @@=<addr1> <addr2> ... <addrN>`, etc.
|
|
- [redirection and pipe statements](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/subprojects/rizin-shell-parser/grammar.js#L146): `<statement> [fd|type]> <file>`, `<statement> | <program>`, etc.
|
|
- [grep statements](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/subprojects/rizin-shell-parser/grammar.js#L148): `<statement>~<grep-pattern>`
|
|
- and many others
|
|
|
|
These patterns deal with the structure of the rizin/radare2 shell language, but
|
|
they don't parse the input of each specific command available in the rizin shell
|
|
(e.g. `af`, `pd`, etc.). The parser just splits the input statement into a
|
|
"command name" and a list of "arguments".
|
|
|
|
### Commands registry
|
|
|
|
The parser alone already provides better consistency with regards to how the
|
|
shell behaves, as all commands are split in the same way and it has a more rigid
|
|
behavior. However it was also essential to have a global commands registry,
|
|
where a command could be registered together with all the information associated
|
|
with it, like help messages, description, etc..
|
|
|
|
The module
|
|
[`RzCmd`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/include/rz_cmd.h)
|
|
is the one in charge of dealing with commands. It provides API to register a new
|
|
"command descriptor" (called
|
|
[`RzCmdDesc`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/include/rz_cmd.h#L388)),
|
|
deregister it, call the right command descriptor handler based on a list of
|
|
command name + arguments, get the help of a command and potentially do many
|
|
other things.
|
|
|
|
As rizin/radare2 commands mainly form a tree, `RzCmdDesc` are organized in a
|
|
tree, with each descriptor having references to its parent and its children.
|
|
Moreover, a descriptor has its help messages and its handler.
|
|
|
|
To make the retrieval of the right command easier, they are also stored in a
|
|
hashtable, using their names as keys.
|
|
|
|
## How to write a new command?
|
|
|
|
Let's make an example and suppose we want to add the `sky` command, which
|
|
would find all occurrences of the word "sky" in a binary. The first thing to
|
|
do is to see where `sky` command could be added by reading
|
|
[`librz/core/cmd_descs/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_descs.yaml).
|
|
`sky` is `s` command's subcommand and they are splitted and placed inside the .YAML
|
|
file specified by the descriptor `subcommands` of the respective command. Since `sky`
|
|
starts with an `s`, its subcommands would be in [`librz/core/cmd_descs/cmd_seek.yaml`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_seek.yaml).
|
|
That file respects the same tree structure used when executing rizin and seeing its help,
|
|
so it should be simple to see where to place it. If we want to place it under the
|
|
`s` sub-tree, we just need to define the descriptors for the command with at least
|
|
`name`, `cname`, `summary` and a list of `args` accepted by the command.
|
|
|
|
Now we need to choose what kind of command (`type` field in YAML) we want to
|
|
have. We can see the various types in the
|
|
[`RzCmdDescType`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/include/rz_cmd.h#L326-L380)
|
|
enum, however let's assume we want a regular command, which is the default
|
|
one, so no action is required in this regard.
|
|
|
|
If our new `sky` command accepts a numeric argument, we can specify it in the
|
|
`args` list, by using the type `RZ_CMD_ARG_TYPE_NUM`.
|
|
|
|
Then we only need to write the actual code that performs the command's job.
|
|
You have to call it according to the `cname` field you previously set for the
|
|
`sky` command, appending `_handler` to that string.
|
|
|
|
|
|
Below you can see how the code for adding the `sky` command would look like:
|
|
```YAML
|
|
- name: s
|
|
cname: cmd_seek
|
|
summary: Seek to address
|
|
type: RZ_CMD_DESC_TYPE_OLDINPUT
|
|
subcommands:
|
|
- name: sky
|
|
cname: sky
|
|
summary: Find all occurrences of the work "sky" in the opened file
|
|
args:
|
|
- name: n
|
|
type: RZ_CMD_ARG_TYPE_NUM
|
|
optional: true
|
|
```
|
|
```C
|
|
// cmd_seek.c (example, real place depends on the parent command)
|
|
RZ_IPI RzCmdStatus rz_sky_handler(RzCore *core, int argc, const char **argv) {
|
|
// argc/argv is like in main(), i.e. argv[0] always contains the command name
|
|
int limit = -1;
|
|
if (argc > 1) {
|
|
limit = rz_num_math (core->num, argv[1]);
|
|
}
|
|
// ... add the logic of your command
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
```
|
|
|
|
The YAML file is used at built-time (by meson only) to autogenerate two
|
|
files: `cmd_descs.c` and `cmd_descs.h`.
|
|
|
|
## Where is the handler of command `x`?
|
|
|
|
You can use the script `sys/rzshell_which.py` to get the name of the function
|
|
handling the specified command.
|
|
|
|
If that doesn't work, please report the problem to us! However, you can still
|
|
find the handler yourself by looking at the file
|
|
[`librz/core/cmd_descs/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_descs.yaml).
|
|
By looking at the `cname` field of the command descriptor, you can see what is
|
|
the name of the handler of the `x` command. If the `cname` is `hex` and the type
|
|
is `RZ_CMD_DESC_TYPE_OLDINPUT`, then the handler will be named `rz_hex`. In all
|
|
other cases, the handler will be named `rz_hex_handler`.
|
|
|
|
Some examples:
|
|
- command: `wv`, type: unspecified (default to `RZ_CMD_DESC_TYPE_ARGV`), handler: `rz_write_value_handler`
|
|
- command: `w6d`, type: unspecified (default to `RZ_CMD_DESC_TYPE_ARGV`), handler: `rz_write_base64_decode_handler`
|
|
- command: `s`, type: `RZ_CMD_DESC_TYPE_OLDINPUT`, handler: `rz_cmd_seek`
|
|
|
|
## How to improve the help messages of a command
|
|
|
|
Find the command in
|
|
[`librz/core/cmd_descs/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_descs.yaml),
|
|
then fix/improve the `summary` and/or `description` fields. If the command cannot be directly found in `cmd_descs.yaml`, look for the other files in `librz/core/cmd_descs`.
|
|
|
|
## How to show examples of a command or additional details
|
|
|
|
You may notice some commands like `env`, `%`, `*` and others have additional
|
|
sections when you show the extensive help with e.g. `env??` (or `%??`, etc.).
|
|
Those additional secions are called `details` and they can be specified,
|
|
again, in the file
|
|
[`librz/core/cmd_descs/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_descs.yaml).
|
|
The structure is explained at the beginning of the file and it can be seen in
|
|
existing commands (e.g.
|
|
https://github.com/rizinorg/rizin/blob/6f40dfe493f0caf9e0541e1ee83e3d8012b5750f/librz/core/cmd_descs/cmd_shell.yaml#L18
|
|
). The result, looks something like:
|
|
```
|
|
[0x00000000]> %??
|
|
Usage: %[varname[=varvalue]] # get/set environment variables
|
|
|
|
Examples:
|
|
| % # list all environment variables
|
|
| %SHELL # print value of SHELL variable
|
|
| %TMPDIR=/tmp # set TMPDIR to "/tmp"
|
|
| envSHELL # same as `%SHELL`
|
|
|
|
Environment:
|
|
| RZ_FILE # currently opened file name
|
|
| RZ_OFFSET # 10base offset 64bit value
|
|
| RZ_XOFFSET # same as above, but in 16 base
|
|
| RZ_BSIZE # block size
|
|
| RZ_ENDIAN # 'big' or 'little'
|
|
| RZ_IOVA # is io.va true? virtual addressing (1,0)
|
|
| RZ_DEBUG # debug mode enabled? (1,0)
|
|
| RZ_SIZE # file size
|
|
| RZ_ARCH # value of asm.arch
|
|
| RZ_BITS # arch reg size (8, 16, 32, 64)
|
|
| RZ_BIN_LANG # assume this lang to demangle
|
|
| RZ_BIN_DEMANGLE # demangle or not
|
|
| RZ_BIN_PDBSERVER # e pdb.server
|
|
```
|