doc(sign): improve README and add examples (#5620)
Co-authored-by: Maijin <maijin21@gmail.com>
This commit is contained in:
parent
0321748674
commit
3cd0d5d640
4 changed files with 281 additions and 0 deletions
69
examples/api/sign/sign_create_example.c
Normal file
69
examples/api/sign/sign_create_example.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// SPDX-FileCopyrightText: 2025 Maijin <Maijin21@gmail.com>
|
||||
// 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 <rz_flirt.h>
|
||||
#include <rz_util.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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(" <pattern> <pattern_len> <crc16> <func_size> <symbols>\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;
|
||||
}
|
||||
71
examples/api/sign/sign_match_example.c
Normal file
71
examples/api/sign/sign_match_example.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-FileCopyrightText: 2025 Maijin <Maijin21@gmail.com>
|
||||
// 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 <rz_flirt.h>
|
||||
#include <rz_util.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
```
|
||||
<pattern> <pattern_len> <crc16> <func_size> <symbols> [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`:
|
||||
```
|
||||
<bin_format>/<arch>/<bits>/<library>/
|
||||
```
|
||||
Where:
|
||||
- `<bin_format>`: Binary format (e.g., `elf`, `pe`) - see `rz-bin -L`
|
||||
- `<arch>`: Architecture (e.g., `x86`, `arm`) - see `rz-asm -L`
|
||||
- `<bits>`: Architecture bits (e.g., `32`, `64`)
|
||||
|
||||
2. **Add required files**:
|
||||
- `<library>.pat` - The pattern file
|
||||
- `<library>.description` - Human-readable description (max 1024 chars)
|
||||
- `<library>.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).
|
||||
Loading…
Reference in a new issue