From 3cd0d5d64083a405300115badc4bc95fcb6080e4 Mon Sep 17 00:00:00 2001 From: Maijin Date: Tue, 16 Dec 2025 02:57:59 +0800 Subject: [PATCH] doc(sign): improve README and add examples (#5620) Co-authored-by: Maijin --- examples/api/sign/sign_create_example.c | 69 +++++++++++++ examples/api/sign/sign_match_example.c | 71 +++++++++++++ examples/meson.build | 10 ++ librz/sign/README.md | 131 ++++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 examples/api/sign/sign_create_example.c create mode 100644 examples/api/sign/sign_match_example.c diff --git a/examples/api/sign/sign_create_example.c b/examples/api/sign/sign_create_example.c new file mode 100644 index 0000000000..ca051d2985 --- /dev/null +++ b/examples/api/sign/sign_create_example.c @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2025 Maijin +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * Example: Creating a FLIRT signature from a pattern + * + * This example demonstrates the structure of FLIRT nodes + * and how to create a simple .pat signature string manually. + */ + +#include +#include +#include + +int main(int argc, char **argv) { + printf("=== RzSign Create Example ===\n\n"); + + // Demonstrate how a .pat signature is structured + printf("A .pat signature line has this format:\n"); + printf(" \n\n"); + + // Example: Creating a simple signature for a function + // Pattern bytes: 55 89 E5 83 EC (push ebp; mov ebp, esp; sub esp, ...) + // With variant bytes: 55 89 E5 83 EC .. (last byte is variable) + const char *example_pattern = "5589E583EC.."; + const char *func_name = "my_function"; + ut8 pattern_len = 0x00; + ut16 crc16 = 0x0000; + ut32 func_size = 0x40; // 64 bytes + + printf("Building signature for function '%s':\n", func_name); + printf(" Pattern bytes: 55 89 E5 83 EC ..\n"); + printf(" Pattern len: 0x%02X\n", pattern_len); + printf(" CRC16: 0x%04X\n", crc16); + printf(" Function size: 0x%04X (%u bytes)\n\n", func_size, func_size); + + // Generate the .pat line + printf("Generated .pat signature:\n"); + printf("--------------------------------\n"); + printf("%s %02X %04X %04X :0000 %s\n", + example_pattern, pattern_len, crc16, func_size, func_name); + printf("---\n"); + printf("--------------------------------\n\n"); + + // Parse it back to verify + const char *pat_content = "5589E583EC.. 00 0000 0040 :0000 my_function\n---\n"; + RzBuffer *buf = rz_buf_new_with_bytes((const ut8 *)pat_content, strlen(pat_content)); + if (!buf) { + fprintf(stderr, "Failed to create buffer\n"); + return 1; + } + + RzFlirtInfo info = { 0 }; + RzFlirtNode *node = rz_sign_flirt_parse_string_pattern_from_buffer(buf, RZ_FLIRT_NODE_OPTIMIZE_NONE, &info); + rz_buf_free(buf); + + if (node) { + printf("Verification: Parsed signature successfully!\n"); + printf(" File type: %s\n", info.type == RZ_FLIRT_FILE_TYPE_PAT ? "PAT" : "Unknown"); + printf(" Modules: %u\n", info.u.pat.n_modules); + rz_sign_flirt_info_fini(&info); + rz_sign_flirt_node_free(node); + } else { + fprintf(stderr, "Failed to parse signature\n"); + } + + printf("\n=== Done ===\n"); + return 0; +} diff --git a/examples/api/sign/sign_match_example.c b/examples/api/sign/sign_match_example.c new file mode 100644 index 0000000000..16eed74f67 --- /dev/null +++ b/examples/api/sign/sign_match_example.c @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2025 Maijin +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * Example: Parsing and matching a FLIRT signature + * + * This example demonstrates how to parse a .pat format + * signature string and count the number of patterns/modules. + */ + +#include +#include +#include + +// A simple .pat signature for testing +static const char *test_pat = + "5589E583EC..894DF8....................C745FC00000000 00 0000 0040 :0000 test_function\n" + "---\n"; + +int main(int argc, char **argv) { + printf("=== RzSign Match Example ===\n\n"); + + // Create a buffer from the test pattern + RzBuffer *pat_buf = rz_buf_new_with_bytes((const ut8 *)test_pat, strlen(test_pat)); + if (!pat_buf) { + fprintf(stderr, "Failed to create buffer\n"); + return 1; + } + + printf("Parsing .pat signature:\n%s\n", test_pat); + + // Parse the pattern + RzFlirtInfo info = { 0 }; + RzFlirtNode *node = rz_sign_flirt_parse_string_pattern_from_buffer(pat_buf, RZ_FLIRT_NODE_OPTIMIZE_NONE, &info); + + rz_buf_free(pat_buf); + + if (!node) { + fprintf(stderr, "Failed to parse pattern\n"); + return 1; + } + + printf("Parsing successful!\n"); + printf("File type: %s\n", info.type == RZ_FLIRT_FILE_TYPE_PAT ? "PAT" : "Unknown"); + printf("Number of modules: %u\n", info.u.pat.n_modules); + + // Count nodes + ut32 node_count = rz_sign_flirt_node_count_nodes(node); + printf("Total nodes in tree: %u\n", node_count); + + // Verify structure + if (node->child_list && rz_list_length(node->child_list) > 0) { + RzFlirtNode *child = rz_list_first(node->child_list); + if (child && child->module_list && rz_list_length(child->module_list) > 0) { + RzFlirtModule *module = rz_list_first(child->module_list); + if (module && module->public_functions && rz_list_length(module->public_functions) > 0) { + RzFlirtFunction *func = rz_list_first(module->public_functions); + printf("\nFirst function in signature:\n"); + printf(" Name: %s\n", func->name); + printf(" Offset: 0x%04x\n", func->offset); + printf(" Is local: %s\n", func->is_local ? "yes" : "no"); + } + } + } + + rz_sign_flirt_info_fini(&info); + rz_sign_flirt_node_free(node); + + printf("\n=== Done ===\n"); + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index 8982746e6b..bfd76c4fab 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -10,4 +10,14 @@ if get_option('enable_examples') dependencies: [rz_syscall_dep, rz_util_dep], install: false ) + executable('sign_create_example', 'api/sign/sign_create_example.c', + include_directories: [platform_inc], + dependencies: [rz_sign_dep, rz_util_dep], + install: false + ) + executable('sign_match_example', 'api/sign/sign_match_example.c', + include_directories: [platform_inc], + dependencies: [rz_sign_dep, rz_util_dep], + install: false + ) endif diff --git a/librz/sign/README.md b/librz/sign/README.md index e69de29bb2..e1e1dfaaa4 100644 --- a/librz/sign/README.md +++ b/librz/sign/README.md @@ -0,0 +1,131 @@ +# RzSign + +`RzSign` module provides functionality to work with signatures, primarily focusing on FLIRT (Fast Library Identification and Recognition Technology) signatures. It allows creating, loading, and applying signatures to identify functions in a binary. + +## FLIRT Signatures + +FLIRT signatures are used to identify library functions in stripped binaries. They rely on pattern matching of the function's code bytes (variant and non-variant bytes) and CRC checksums. + +### File Formats + +FLIRT signatures can be stored in two formats: + +- **.pat (Pattern file)**: Human-readable text format. Each line describes one function signature with its byte pattern, CRC, size, and symbol name. Easy to create and debug. +- **.sig (Signature file)**: Compressed binary format. More compact but not human-readable. Rizin can parse both formats. + +The `.pat` format is typically used during signature development, while `.sig` files are distributed for production use. + +### Pattern Format + +A FLIRT pattern consists of byte values in hexadecimal. Some bytes are **fixed** (must match exactly) while others are **variant** (can match any value). Variant bytes are represented with `..` (two dots). + +**Example pattern:** +``` +5589E583EC..894DF8....................C745FC00000000 +``` + +- `55 89 E5 83 EC` - Fixed bytes (must match exactly) +- `..` - Variant byte (matches any value, typically for relocations or offsets) +- `89 4D F8` - More fixed bytes +- `....................` - Multiple variant bytes (10 pairs = 10 bytes) +- `C7 45 FC 00 00 00 00` - Fixed bytes + +### .pat File Line Format + +A complete `.pat` line includes: +``` + [tail_bytes] +``` + +**Example:** +``` +5589E583EC..894DF8 00 0000 0040 :0000 my_function +``` +- Pattern: `5589E583EC..894DF8` (with variant bytes) +- Pattern length: `00` (length of pattern after the prelude) +- CRC16: `0000` +- Function size: `0040` (64 bytes in hex) +- Symbol: `:0000 my_function` (public function at offset 0) + +### Key Structures + +- `RzFlirtNode`: Represents a node in the signature tree. It contains a byte pattern and mask. +- `RzFlirtModule`: Represents a specific module (function or group of functions) associated with a pattern. It includes CRC checksums and function names. +- `RzFlirtFunction`: Represents a function within a module. + +### API Usage + +#### Creating Signatures + +To create a signature from an analyzed function: +1. Ensure the function is analyzed (`RzAnalysisFunction`). +2. Use `rz_sign_flirt_node_from_function()` to generate a `RzFlirtNode` from the function. +3. Use `rz_sign_flirt_write_string_pattern_to_buffer()` to serialize the node to a `.pat` format string. + +#### Matching Signatures + +To match signatures against an analysis context: +1. Load the signature file (either `.sig` binary or `.pat` text). +2. Use `rz_sign_flirt_apply()` to apply the signature file to the current `RzAnalysis` instance. + - This function parses the file, matches patterns against analyzed functions, and renames them if a match is found. + + +### Workflow + +```mermaid +graph TD + subgraph Creation + A[Analyzed Function] -->|rz_sign_flirt_node_from_function| B(RzFlirtNode) + B -->|rz_sign_flirt_write_...| C[Signature Buffer] + C --> D[.pat / .sig File] + end + + subgraph Matching + E[Signature File] -->|rz_sign_flirt_apply| F{Match?} + F -->|Yes| G[Rename Function] + F -->|No| H[Ignore] + end +``` + +## Rizin Signature Database + +Rizin maintains a community signature database with pre-built signatures for common libraries. + +### Repository Structure + +The signature ecosystem consists of three repositories: + +| Repository | Purpose | +|------------|---------| +| [sigdb](https://github.com/rizinorg/sigdb) | Pre-built `.sig` files, auto-pulled during Rizin build via meson | +| [sigdb-source](https://github.com/rizinorg/sigdb-source) | Source `.pat` files where contributors submit new signatures | +| [sigdb-tools](https://github.com/rizinorg/sigdb-tools) | Tools to convert `.pat` files to `.sig` format | + +### Contributing Signatures + +To contribute new signatures to the Rizin signature database: + +1. **Create the folder structure** in `sigdb-source`: + ``` + //// + ``` + Where: + - ``: Binary format (e.g., `elf`, `pe`) - see `rz-bin -L` + - ``: Architecture (e.g., `x86`, `arm`) - see `rz-asm -L` + - ``: Architecture bits (e.g., `32`, `64`) + +2. **Add required files**: + - `.pat` - The pattern file + - `.description` - Human-readable description (max 1024 chars) + - `.src.sha1` - SHA1 of original source files + +3. **Example**: + ```bash + mkdir -p sigdb-source/elf/x86/32/mylib + echo "My Library v1.0" > sigdb-source/elf/x86/32/mylib/mylib.description + sha1sum original.a > sigdb-source/elf/x86/32/mylib/mylib.src.sha1 + # Add your .pat file + cp signature.pat sigdb-source/elf/x86/32/mylib/mylib.pat + ``` + +4. Submit a pull request to [sigdb-source](https://github.com/rizinorg/sigdb-source).