Skip to content

Commit 491b847

Browse files
committed
feat: add raw command for protocol exploration
Add a new 'raw' command that sends arbitrary bootloader commands and displays detailed TX/RX packet analysis with field interpretation. Useful for exploring undocumented commands, debugging protocol issues, and security research. Hardware tests extended in test-hw.sh. Example usage: radfu raw 0x3A # Signature request radfu raw 0x3B 0x00 # Area info for area 0 radfu raw 0x2C # DLM state request radfu raw 0x99 # Test unknown command (returns error) Signed-off-by: Vincent Jardin <vjardin@free.fr>
1 parent 14f9dd9 commit 491b847

File tree

5 files changed

+415
-0
lines changed

5 files changed

+415
-0
lines changed

radfu.h2m

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,43 @@ blocks are protected from programming/erasure. A value of 0 means protected,
119119
1 means unprotected. PBPS (Permanent Block Protection) cannot be reversed
120120
once set to 0.
121121

122+
.TP
123+
.B raw <cmd> [data...]
124+
Send a raw bootloader command for protocol exploration and debugging. The command
125+
byte and optional data bytes are specified as hexadecimal values (with or without
126+
0x prefix). Displays detailed TX/RX packet analysis including field-by-field
127+
breakdown, checksum verification, and interpretation of known response fields.
128+
129+
This command is useful for:
130+
.IP \(bu 4
131+
Exploring undocumented bootloader commands
132+
.IP \(bu
133+
Debugging protocol issues
134+
.IP \(bu
135+
Security research and reverse engineering
136+
.IP \(bu
137+
Testing device behavior with specific command sequences
138+
139+
.SS Output Format
140+
The command displays:
141+
.IP \(bu 4
142+
Raw hexdump of transmitted and received packets
143+
.IP \(bu
144+
Field analysis: SOH/SOD, length, command/response code, data, checksum, ETX
145+
.IP \(bu
146+
Interpretation of known fields (addresses, DLM states, error codes, etc.)
147+
.IP \(bu
148+
Checksum validation status
149+
150+
.SS Examples
151+
.nf
152+
radfu raw 0x3A # Signature request (device info)
153+
radfu raw 0x3B 0x00 # Area information for area 0
154+
radfu raw 0x2C # DLM state request
155+
radfu raw 0x15 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF # Read 256 bytes from 0x0
156+
radfu raw 0x99 # Test unknown command (returns error)
157+
.fi
158+
122159
[id authentication]
123160
Some RA devices have ID code protection enabled. To access protected devices,
124161
you must provide the correct 16-byte ID code using the \fB-i\fR option.
@@ -397,6 +434,8 @@ radfu boundary-set --cfs1 0 --cfs2 0 --dfs 0 --srs1 0 --srs2 0
397434
radfu boundary-set --file zephyr.rpd # Load from .rpd file
398435
radfu -u -p /dev/ttyUSB0 info # UART via USB-UART adapter
399436
radfu write -q firmware.bin # Write without progress bar
437+
radfu raw 0x3A # Raw protocol exploration
438+
radfu raw 0x3B 0x00 # Area info with data byte
400439
.fi
401440

402441
[progress display]

scripts/test-hw.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,74 @@ test_multi_file_write() {
579579
rm -f "${TEST_FILE}_1.bin" "${TEST_FILE}_2.bin"
580580
}
581581

582+
test_raw() {
583+
echo
584+
log_info "=== Test: raw command (protocol exploration) ==="
585+
586+
# Test 1: Signature request (0x3A) - should return device info
587+
local output
588+
if output=$(run_radfu raw 0x3A 2>&1); then
589+
if echo "$output" | grep -q "TX Packet" && \
590+
echo "$output" | grep -q "RX Packet" && \
591+
echo "$output" | grep -q "SIG (Signature request)"; then
592+
log_ok "raw: signature request (0x3A)"
593+
if [ "$VERBOSE" -eq 1 ]; then
594+
echo "$output" | head -30
595+
fi
596+
else
597+
log_fail "raw: signature request missing expected output"
598+
return 1
599+
fi
600+
else
601+
log_fail "raw: signature request command failed"
602+
return 1
603+
fi
604+
605+
# Test 2: Area info for area 0 (0x3B with data 0x00)
606+
if output=$(run_radfu raw 0x3B 0x00 2>&1); then
607+
if echo "$output" | grep -q "ARE (Area information)" && \
608+
echo "$output" | grep -q "KOA:"; then
609+
log_ok "raw: area info request (0x3B 0x00)"
610+
else
611+
log_fail "raw: area info missing expected output"
612+
return 1
613+
fi
614+
else
615+
log_fail "raw: area info command failed"
616+
return 1
617+
fi
618+
619+
# Test 3: DLM state request (0x2C)
620+
if output=$(run_radfu raw 0x2C 2>&1); then
621+
if echo "$output" | grep -q "DLM (DLM state request)" && \
622+
echo "$output" | grep -q "DLM state:"; then
623+
log_ok "raw: DLM state request (0x2C)"
624+
else
625+
log_fail "raw: DLM state missing expected output"
626+
return 1
627+
fi
628+
else
629+
log_fail "raw: DLM state command failed"
630+
return 1
631+
fi
632+
633+
# Test 4: Unknown command (0x99) - should return error 0xC0
634+
if output=$(run_radfu raw 0x99 2>&1); then
635+
# Command should "succeed" (sends and receives)
636+
log_fail "raw: unknown command should have failed"
637+
return 1
638+
else
639+
# Expected to fail with error response
640+
if echo "$output" | grep -q "UNKNOWN" && \
641+
echo "$output" | grep -q "ERROR"; then
642+
log_ok "raw: unknown command (0x99) returns error as expected"
643+
else
644+
log_fail "raw: unknown command unexpected response"
645+
return 1
646+
fi
647+
fi
648+
}
649+
582650
test_input_formats() {
583651
echo
584652
log_info "=== Test: input formats (write from ihex/srec) ==="
@@ -722,6 +790,7 @@ Tests (default: all safe tests):
722790
area Memory area selection (--area)
723791
reconnect Multiple command sessions
724792
baudrate Baud rate switching
793+
raw Raw command protocol analysis
725794
write Write/erase/verify tests (requires -w)
726795
multiwrite Multi-file write test (requires -w)
727796
informat Input format tests (requires -w)
@@ -806,6 +875,7 @@ for test in $TESTS; do
806875
area) test_area_selection ;;
807876
reconnect) test_reconnection ;;
808877
baudrate) test_baudrate ;;
878+
raw) test_raw ;;
809879
write) test_write_data_flash; test_write_with_verify_flag ;;
810880
multiwrite) test_multi_file_write ;;
811881
informat) test_input_formats ;;
@@ -827,6 +897,7 @@ for test in $TESTS; do
827897
test_area_selection
828898
test_reconnection
829899
test_baudrate
900+
test_raw
830901
# Write tests (require -w flag)
831902
test_write_data_flash
832903
test_write_with_verify_flag

src/main.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ usage(int status) {
5252
" key-verify <type> Verify DLM key (secdbg|nonsecdbg|rma)\n"
5353
" ukey-set <idx> <file> Inject user wrapped key from file at index\n"
5454
" ukey-verify <idx> Verify user key at index\n"
55+
" raw <cmd> [data...] Send raw command (hex bytes) for protocol analysis\n"
5556
"\n"
5657
"Options:\n"
5758
" -p, --port <dev> Serial port (auto-detect if omitted)\n"
@@ -203,6 +204,7 @@ enum command {
203204
CMD_KEY_VERIFY,
204205
CMD_UKEY_SET,
205206
CMD_UKEY_VERIFY,
207+
CMD_RAW,
206208
};
207209

208210
/* DLM key types (KYTY) per R01AN5562 */
@@ -726,6 +728,11 @@ main(int argc, char *argv[]) {
726728
if (optind >= argc)
727729
errx(EXIT_FAILURE, "ukey-verify requires index argument");
728730
key_index = (uint8_t)strtoul(argv[optind], NULL, 10);
731+
} else if (strcmp(command, "raw") == 0) {
732+
cmd = CMD_RAW;
733+
if (optind >= argc)
734+
errx(EXIT_FAILURE, "raw command requires at least a command byte (hex)");
735+
/* Arguments parsed later after device connection */
729736
} else {
730737
errx(EXIT_FAILURE, "unknown command: %s", command);
731738
}
@@ -951,6 +958,37 @@ main(int argc, char *argv[]) {
951958
case CMD_UKEY_VERIFY:
952959
ret = ra_ukey_verify(&dev, key_index, NULL);
953960
break;
961+
case CMD_RAW: {
962+
/* Parse command byte and optional data from remaining args */
963+
uint8_t raw_cmd;
964+
uint8_t raw_data[256];
965+
size_t raw_len = 0;
966+
967+
/* Parse command byte */
968+
char *endptr;
969+
unsigned long val = strtoul(argv[optind], &endptr, 16);
970+
if (*endptr != '\0' || val > 0xFF) {
971+
warnx("invalid command byte: %s (use hex 0x00-0xFF)", argv[optind]);
972+
ret = -1;
973+
break;
974+
}
975+
raw_cmd = (uint8_t)val;
976+
977+
/* Parse optional data bytes */
978+
for (int i = optind + 1; i < argc && raw_len < sizeof(raw_data); i++) {
979+
val = strtoul(argv[i], &endptr, 16);
980+
if (*endptr != '\0' || val > 0xFF) {
981+
warnx("invalid data byte: %s (use hex 0x00-0xFF)", argv[i]);
982+
ret = -1;
983+
break;
984+
}
985+
raw_data[raw_len++] = (uint8_t)val;
986+
}
987+
if (ret < 0)
988+
break;
989+
990+
ret = ra_raw_cmd(&dev, raw_cmd, raw_data, raw_len);
991+
} break;
954992
default:
955993
break;
956994
}

0 commit comments

Comments
 (0)