Skip to content

Latest commit

 

History

History
180 lines (153 loc) · 9.27 KB

rzshell.md

File metadata and controls

180 lines (153 loc) · 9.27 KB

Command parsing and command handling

The Rizin shell language is originally derived from the Radare2 shell language. There is 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 shell language, like:

These patterns deal with the structure of the rizin 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 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 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. 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:

  - name: sky
    cname: sky
    summary: Print current address / Seek to address
    args:
      - name: limit
        type: RZ_CMD_ARG_TYPE_NUM
        optional: true
// 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. By looking at the cname field of the command descriptor, you can see what is the name of the handler of the x command. It is always of the form rz_<cname>_handler. So for the command x with cname = hex the handler is 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

How to improve the help messages of a command

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.

How to show examples of a command or additional details

You may notice some commands like e, w and others have additional sections where you can show more extensive help with ?? e.g. e?? (or w?? etc.). Those additional sections are called details and they can be seen in existing commands e.g.:

details:
- name: Examples
entries:
- text: e
arg_str: " asm.bytes"
comment: "Show current value of config variable `asm.bytes`"
- text: e
arg_str: " asm.bytes=true"
comment: "Set config variable `asm.bytes` to `true`"
- text: e
arg_str: " search.in=?"
comment: "Show all possible values for config variable `search.in`"
- text: e
arg_str: " search.in=??"
comment: "Show all possible values for config variable `search.in` together with description"
- text: e
arg_str: " asm.bytes=true asm.offset=false"
comment: "Set asm.bytes to true and asm.offset to false"
Their structure is explained at the beginning of librz/core/cmd_descs/cmd_descs.yaml. A possible difference between ? and ?? is shown below:

[0x00000000]> w?
Usage: w[?]   # Write commands
| w <string>        # Write string
| wB[-]             # Set or unset bits with given value
| wv[1248]          # Write value of given size
| w0 <len>          # Write <len> bytes with value 0x00
| w<1248><+-> [<n>] # Increment/decrement byte, word, ...
| w6<de>            # Write base64 [d]ecoded or [e]ncoded string
| we<nsx>           # Extend write operations (insert bytes instead of replacing)
| wu <file>         # Apply unified hex patch (see output of cu)
| wr <len>          # Write <len> random bytes
| wc[j*-+ip?]       # Write cache commands
| wz <string>       # Write zero-terminated string
| wf[xfs]           # Write data from file, socket, offset
| ww <string>       # Write wide (16-bit) little-endian string
| wx[f]             # Write hexadecimal data
| wa[ifo]           # Write opcodes
| wb <hex>          # Write in current block a hexstring cyclically
| wm[-]             # Set binary mask hexpair to be used as cyclic write mask
| wo<?>             # Write a block with a special operation
| wD[/]             # Write de Bruijn pattern
| wd <src> <len>    # Duplicate <len> bytes from <src> offset to current seek
| ws <string>       # Write 1 byte for length and then the string

Detailed help for w <string> is provided by w??.
[0x00000000]> w??
Usage: w <string>   # Write string

Examples:
| w 123\n    # Write the chars '1', '2', '3' and a newline
| w ab\0cd\0 # Write the chars 'a', 'b', a NUL, 'c', 'd' and another NUL

Escape sequences:
| \0   # NUL (0x0)
| \a   # Bell (0x7)
| \b   # Backspace (0x8)
| \e   # Escape (0x1b)
| \f   # Form feed (0xc)
| \n   # Newline (0xa)
| \r   # Carriage return (0xd)
| \t   # Tab (0x9)
| \v   # Vertical tab (0xb)
| \\   # Backslash ('\')
| \xhh # Byte in hexadecimal
| \nnn # Byte in octal (eg. \033 for the escape char)