Rizin
unix-like reverse engineering framework and cli tools
|
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.
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, 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.
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, which allows you to write grammars in JavaScript. You can see our grammar here. The parser recognizes the entire syntax of the rizin/radare2 shell language, like:
<command-name> <arg1> <arg2> ... <argN>
<statement> @ <address
, <statement> @a:x86:32
, etc.<statement> @@ sym.*
, <statement> @@=<addr1> <addr2> ... <addrN>
, etc.<statement> [fd|type]> <file>
, <statement> | <program>
, etc.<statement>~<grep-pattern>
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".
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
is the one in charge of dealing with commands. It provides API to register a new "command descriptor" (called RzCmdDesc
), 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.
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
. 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
. 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
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:
The YAML file is used at built-time (by meson only) to autogenerate two files: cmd_descs.c
and cmd_descs.h
.
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
. 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:
wv
, type: unspecified (default to RZ_CMD_DESC_TYPE_ARGV
), handler: rz_write_value_handler
w6d
, type: unspecified (default to RZ_CMD_DESC_TYPE_ARGV
), handler: rz_write_base64_decode_handler
s
, type: RZ_CMD_DESC_TYPE_OLDINPUT
, handler: rz_cmd_seek
Find the command in 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
.
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
. 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: