Greetings! I’m Emad Sohail (aka PremadeS), a 2nd-year Computer Science student at Information Technology University - Lahore. I’m passionate about contributing to open source projects mainly geared toward cybersecurity. You can find me on Github and LinkedIn.
In this project I introduced a new Mark API to support address-range annotations (marks) in Rizin, particularly for visual feedback in hexdump views. It can be thought of as an enhancement to the existing flag system. You can now mark entire ranges of addresses instead of tagging just one offset.
Below is a basic usage example for Marks
rizin /usr/bin/ls
-- Enhance your graphs by increasing the size of the block and graph.depth eval variable.
[0x00006760]> m foo 0x6800
[0x00006760]> m+ bar 0x6945 this is another mark
[0x00006760]> ml
[0x00006760 - 0x00006800] foo
[0x00006760 - 0x00006945] bar
[0x00006760]> mC bar
this is another mark
You can explore the visual implementation of Marks in Cutter here
What are Marks
A mark represents a user-defined range of addresses in the binary. Each mark can have:
From/To
: the starting and ending address (inclusive).Name
: a unique identifier (escaped internally to avoid shell issues).Real Name
: the original name of the mark as provided by the user.Comment
: an optional note attached to the mark.Color
: a color tag for visual identification in Cutter.
See the implementation of RzMarkItem
below:
typedef struct rz_mark_item_t {
ut64 from; ///< inclusive starting address of mark
ut64 to; ///< inclusive ending address of mark
char *name; ///< unique name for each mark, escaped to avoid issues with rizin shell
char *realname; ///< real name, without any escaping
char *comment; ///< item comment
char *color; ///< item color
} RzMarkItem;
How Marks Work
The idea is simple: let users mark a range of addresses instead of a single offset. Each mark has a unique name and can also include a comment for extra context.
Marks are saved and loaded with the .rzdb
project files so they are preserved just like flags.
Currently Marks are only shown in the hexdump view.
[0x00006760]> pxc
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF comment
0x00006760 31ed 4989 d15e 4889 e248 83e4 f050 5445 1.I..^H..H...PTE ; [s] foo [0x6760-0x6800]
0x00006770 31c0 31c9 488d 3d85 e4ff ffff 1527 0802 1.1.H.=......'..
0x00006780 00f4 662e 0f1f 8400 0000 0000 0f1f 4000 ..f...........@.
0x00006790 488d 3de9 0a02 0048 8d05 e20a 0200 4839 H.=....H......H9
0x000067a0 f874 1548 8b05 0608 0200 4885 c074 09ff .t.H......H..t..
0x000067b0 e00f 1f80 0000 0000 c30f 1f80 0000 0000 ................
0x000067c0 488d 3db9 0a02 0048 8d35 b20a 0200 4829 H.=....H.5....H)
0x000067d0 fe48 89f0 48c1 ee3f 48c1 f803 4801 c648 .H..H..?H...H..H
0x000067e0 d1fe 7414 488b 0505 0802 0048 85c0 7408 ..t.H......H..t.
0x000067f0 ffe0 660f 1f44 0000 c30f 1f80 0000 0000 ..f..D..........
0x00006800 f30f 1efa 803d bd0a 0200 0075 2b55 4883 .....=.....u+UH. ; [e] foo [0x6760-0x6800]
0x00006810 3de2 0702 0000 4889 e574 0c48 8b3d e607 =.....H..t.H.=..
0x00006820 0200 e8e1 deff ffe8 64ff ffff c605 950a ........d.......
0x00006830 0200 015d c30f 1f00 c30f 1f80 0000 0000 ...]............
0x00006840 f30f 1efa e977 ffff ff66 2e0f 1f84 0000 .....w...f...... ; entry.init0
0x00006850 0000 0066 2e0f 1f84 0000 0000 0066 2e0f ...f.........f..
Why This Feature Is Useful
Sometimes you want to visually mark the start and end of a function, loop, or data structure in memory. A single flag isn’t enough to represent the whole span.
When analyzing malware or packed binaries, it’s useful to mark the section of memory that was unpacked or injected for quick reference.
Having a separate abstraction (Mark) helps decouple “range annotations” from “single-address flags” and avoids overloading one system with too many concerns.
It also lays the foundation to visually show the highlighted address regions in Cutter.
Implementation
Core modules & structure
rz_mark.h
defines the public API: structures (RzMarkItem
, RzMark
, etc.) and function signatures for creating, querying, editing, removing, iterating, and serializing marks.
mark.c
is where the bulk of logic lives: implementing the functions from the header, maintaining internal data structures, and integrating with command-line printing.
serialize_mark.c
handles serialization and deserialization of marks via Sdb
(Rizin’s internal database), so marks persist across sessions.
cmd_mark.c
and cmark.c
handle the command-line functionality of marks.
db/cmd/cmd_mark
contains integration tests, while unit/test_marks.c
and unit/test_serialize_marks.c
contain unit tests.
Data structures & internal storage
At its core, a mark is represented by the RzMarkItem
structure (explained above).
The container for all marks is the RzMark
structure. It maintains:
- A hash table
(ht_name)
mapping names to items for fast lookups. - A list of items
(items)
to store all marks in memory. - A skiplist
(by_off)
to efficiently query marks by offset.
typedef struct rz_mark_t {
HtSP *ht_name; ///< name -> RzMarkItem*
RzList /*<RzMarkItem *>*/ *items; ///< All marks
RzSkipList *by_off; ///< offset -> RzMarksAtOffset*
} RzMark;
Behavior & API implementation
Creation & Deletion
rz_mark_new()
creates a new mark container.
rz_mark_free()
frees the mark container.
Adding & Querying Marks
rz_mark_set()
defines a new mark within a given address range.rz_mark_get()
looks up marks by name.rz_mark_get_start()
gets a mark starting at a specific address.rz_mark_get_end()
gets a mark ending at a specific address.rz_mark_get_at()
gets a mark covering a specific address.rz_mark_all_list()
retrieves all marks in the container.rz_mark_get_all_off()
retrieves all marks at a specific offset.
Editing
rz_mark_rename()
renames individual marks.rz_mark_item_set_comment()
annotates individual marks with comments.rz_mark_item_set_realname()
sets the real name.rz_mark_item_set_color()
visually tags with colors (for Cutter).
Removal
rz_mark_unset()
deletes individual marks.rz_mark_unset_all_off()
clears all marks at a specific offset.rz_mark_unset_glob()
removes marks in bulk using glob patterns.rz_mark_unset_all()
removes all marks.
Iteration
rz_mark_foreach()
iterates through all marks.rz_mark_foreach_regex()
iterates through marks filtered by regex patterns.
Saving/Loading
rz_serialize_mark_save()
saves marks to the Rizin database (Sdb
).rz_serialize_mark_load()
restores marks from the Rizin database (Sdb
).
By exposing this API, Rizin ensures that marks are no longer limited to a single widget but can instead serve as a general-purpose annotation mechanism. This makes them accessible to plugins, scripts, and future Cutter features.
Refer to PR #5313 for more details about the implementation of Marks.
Work Done
Here is a brief overview of the work.
Added rz_mark.h
/ rz_mark.c
with APIs for creating, querying, and serializing marks.
Implemented m
commands to allow users to create and list marks.
Below is a list of supported commands for marks that can be brought up inside Rizin with m?
:
[0x00006760]> m?
Usage: m[?] # Manage marks
│m <name> <end> [<comment>] # Adds a mark if there are no existing marks.
│m+ <name> <end> [<comment>] # Add mark.
│m- [<regex_pattern>] # Remove mark.
│m-* # Remove all marks.
│ml[j*qt] # List all marks.
│ml.[j*qt] # List all marks containing the current offset.
│mc <name> [<color>] # Set a color for the given mark / Show the color for the given mark.
│mC <name> [<comment>] # Set a comment for the given mark. If no comment is given, it shows the current one for the mark.
│mr <old> <new> # Rename mark.
│mN <name> [<realname>] # Show the realname of the mark / Set the realname of the mark.
│mm <name> <new_start> <new_end> # Move a mark to a new location.
│mf <regex_pattern> # Distance in bytes to reach the starting of mark from the current offset.
│md[jt] # Describe mark.
│mi[j*qt] [<size>] # Show marks in the current block or in the next <size> bytes.
Show start/end markers in comments section of hexdump (e.g. [s] name
, [e] name
).
Added tests under test/db/cmd/cmd_mark
to validate command behavior.
Added serialize_mark.c
to save and load marks with Rizin project files.
Challenges
Although the process was relatively straightforward, the one major decision that had to be made was how distinct marks should be relative to existing flags. Some reviewers suggested combining the new API with flags; others argued for separation. The resolution leaned toward separation in the end.
Future Work
Currently, marks are displayed in the hexdump using simple [s] name
and [e] name
tags to indicate the start and end of a range. While this works, it’s not always easy to see the full extent of a marked region at a glance. A future improvement would be to introduce visual bracket-style markers that span across lines, making ranges more obvious. (See example below)
0x00000100 ffff ffff ffff ffff ffff ffff ffff ffff ................ ┐ [s] foo [0x100-0x120]
0x00000110 ffff ffff ffff ffff ffff ffff ffff ffff ................ │
0x00000120 ffff ffff ffff ffff ffff ffff ffff ffff ................ │┐ [s] bar [0x110-0x160]
0x00000130 ffff ffff ffff ffff ffff ffff ffff ffff ................ ││
0x00000140 ffff ffff ffff ffff ffff ffff ffff ffff ................ ┘│ [e] foo [0x100-0x120]
0x00000150 ffff ffff ffff ffff ffff ffff ffff ffff ................ │
0x00000160 ffff ffff ffff ffff ffff ffff ffff ffff ................ ┘ [e] bar [0x110-0x160]
0x00000170 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x00000180 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x00000190 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001a0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001b0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001c0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001d0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001e0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x000001f0 ffff ffff ffff ffff ffff ffff ffff ffff ................
Extend the mark rendering to other views (e.g. disassembly, graph views) beyond hexdump.
Conclusion
At the end, I would like to say that this RSoC has been a great learning experience. From not knowing anything about the Rizin codebase to adding an actual useful feature was definitely a great journey.
Special thanks to @xvilka for this amazing opportunity and also @deroad, @Rot127 for the help and guidance.