Note that some of the conclusions here are incorrect and have been updated in part 2
The firmware can be downloaded from Remote Central
Unzipping the download gives a single file, extfw-1.4.7-philips.bin
Despite the name, this is just another zip.
$ file extfw-1.4.7-philips.bin
extfw-1.4.7-philips.bin: Zip archive data, at least v2.0 to extract
Unzipping this gives
parts.config
zImage-1.4.7
zImage-fu-1.4.7
rootfs-1.4.7.jffs2
sndr-1.4.7.bin
So the RFX9400 runs Linux and rootfs-1.4.7.jffs2 is the filesystem image that we can mount on another Linux box (details to follow). Doing so and taking a look through the contents yields some items of interest
/opt/run.sh looks like this
#!/bin/sh
if [ -e "/opt/backdoor_open" ]
then
echo -n "Opening backdoor..."
ip link set eth0 up
ip addr add 192.168.42.22/24 dev eth0
telnetd
echo "done."
fi
So, if we can somehow create a file at /opt/backdoor_open then we can telnet to the RFX9400 and have a poke around. We could modify the jffs2 image, repack everything and flash the firmware but it turns out there is an easier and less risky way
/usr/share/httpd-philips/html/cgi-bin/open_sesame
#!/bin/sh
touch /opt/backdoor_open
echo "Content-Type: text/plain"
echo ""
echo "Backdoor is open -- remember to close it!"
So, all you need to do is go to http://<ip of rfx>/cgi-bin/open-sesame
and the backdoor is opened for anyone to telnet in! Fortunately the web server is only running on the RFX when you have the setting switch on the back set to configuration mode, but this is still mildly terrifying. Still, useful for us...
BTW, you can close the backdoor again either by telnetting in and deleting the file, or by going to http://<ip of rfx>/cgi-bin/close-sesame
Having opened the backdoor you can simply telnet in as the root user, there is no password
$ telnet <rfx ip>
Trying <rfx ip>...
Connected to <rfx ip>.
Escape character is '^]'.
(none) login: root
~ #
What can we find out? (My RFX9400 doesn't actually have the latest firmware on it, so some of these results my be different for you)
It's an ARMv5 system running a 2.4 series kernel
~ # uname -a
Linux (none) 2.4.20_mvl31-mx21ads #1 Mon May 14 10:06:57 CEST 2007 armv5EJl unknown
It's based on BusyBox
~ # ls /bin/busybox
/bin/busybox
So that means we can resort to using nc (netcat) to transfer files on or off. That may be useful if we had a suitable crosscompiler environment
In addition to the basic tools provided by the busybox binary, we also have tcpdump which is an pleasant surprise - no need to set up a socat proxy to view traffic between the RFX and the TSU
/ # ls /usr/sbin/tcpdump
/usr/sbin/tcpdump
Running processes include
/usr/local/sbin/ssdpd
which seems like it may be for the discovery process, and several instances of
/usr/local/sbin/nhcpd
which is for communication with the TSU remote. Trying to see if there is a help option for this
/ # /usr/local/sbin/nhcpd -h
/usr/local/sbin/nhcpd: invalid option -- h
Usage: nhcpd [-d] [-l address]
reveals a -d flag which turns out to be a handy debug flag.
One other useful binary is /usr/bin/dsw
/ # dsw
DSW RFX9400/9600
DSW Version 0.1
(use 'list' to see available commands)
list
ACK
Available commands :
cmd alias synopsis
--- ----- --------
BOG1 bog1 Bogus command
blnk blink_led blnk <busy|eth|wifi|serial1|serial2|serial3|serial4> <red|green|yellow|on|off>
boot reboot Soft reset
emcf cfg_emc Config emc
emof stop_emc Stop emc
emon start_emc Start emc
exit quit Exit dsw
getn get_serial Get serial number [flash]
girv get_ir_versions Get IR versions
gver get_version Get versions [component]
help ? Show help
irpi irpick Select the IR port
list list List commands
lpba rs232_loop lpba
nfbd nand_check_bad Check the nandflash for bad blocks
rinp read_input rinp [<port>]
rspi rs232_pick rspi <port>
rssp rs232_speed rssp <speed>
rsst rs232_stop rsst
rstx rs232_send rstx <data>
setn set_serial Set serial numbers
slee delay Wait a bit
snet stop_network Stop transmitting over network <device>
snfl stop_nandflash Stop the nandflash test
sram stop_ram Stop the SDRAM test
srel set_relay srel <port> <on|off>
stir irc_stop_ir Stop direct IRC IR transmission
sttr stop_ecf Stop IR transmission
tnet transmit_network Transmit data over network <device> <packet size>
tnfl test_nandflash Test nandflash
tram test_ram Test SDRAM [Background]
tsws test_dials tsws <dial> <timeout> <final position>
txec send_ecf Transmit ECF <Duration> <DeviceID> <ECF>
txir irc_send_ecf Transmit raw ECF direct via IRC <Duration> <ECF>
wfcf config_wifi Load wifi config <device> <path/to/configfile>
LIST 00
help txir
ACK
txir (irc_send_ecf) [I X] : Transmit raw ECF direct via IRC <Duration> <ECF>
Transmit the specified IR code (bypassing RIS).
Duration is in milliseconds.
ECF code must be specified as a sequence of hexadecimal digits.
HELP 00
help txec
ACK
txec (send_ecf) [I I X] : Transmit ECF <Duration> <DeviceID> <ECF>
Transmit the specified ECF IR code (using RIS).
Duration is in milliseconds, 0 means send continuously until STTR.
DeviceID is the unique number of the receiving device.
ECF code must be specified as a sequence of hexadecimal digits.
HELP 00
help irpick
ACK
irpi (irpick) [I] : Select the IR port
Pick the IR port from which to send codes. The port is a number from 1 to 5
HELP 00
quit
ACK
So dsw is a test harness that can control the front LEDs, send serial commands (on the RFX9600), send IR commands and perform other diagnostic functions. Currently I'm only interested in the IR functions which are
Transmits an IR code in ECF format (first time I've seen this mentioned anywhere). You can specify a duration in milliseconds or zero to send continuously until you request it is stopped.
Similar to txir, but takes a device id which I do not know the meaning of. References using RIS and looking in libris.so there are references to encryption so I believe this may be used for sending protected codes from the Philips database
Stops IR transmission started with txir
Selects the IR port for ftransmission
An example of using dsw to send an IR code is (the source of this ECF code will be discussed next)
/ # dsw
DSW RFX9400/9600
DSW Version 0.1
(use 'list' to see available commands)
irpick 1
ACK
IRPI 00
txir 0 0060010000080a04da61d1c040747a60747040746060747040746060e8e040748a60747040746060e8e040748a60747040746060747040746060e8e040748a607470407460607470407460607470407460607470505000400016c3000000
ACK
TXIR 00
stir
ACK
STIR 00
quit
ACK
Having this test harness available is incredibly useful as it's an easy way to test ECF codes we generate ourself or manipulated existing codes to see when they break
Find the running nhcpd processes and kill them, then re-launch in debug mode
~ # /usr/local/sbin/nhcpd -d
Starting nhcpd
Got RFX9400-111 hardware
Got IRCVersion_V7.1
Got IRCLibVersion_V0.6
Binding to 0.0.0.0
Creating receiver thread
Creating sender thread
Creating executor thread
Also, we want to see the raw traffic going to and from nhcpd (which runs on port xxx) so run tcpdump in another terminal
~ # tcpdump -s 400 -x "udp port 65442"
tcpdump: listening on eth0
A quick press on a button on the TSU remote gives the following output from nhcpd
32 bytes received from 192.168.0.27:1024
LOCK <30000> <Untitled1>
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
124 bytes received from 192.168.0.27:1024
Sending 16 bytes to 192.168.0.27:1024 [ACK]
IR_SEND <0> <1> <0> <0x00> <0x00>
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
IR_SEND evaluated to TRUE
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
28 bytes received from 192.168.0.27:1024
UNLOCK
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
and this from tcpdump
01:29:00.539787 192.168.0.27.1024 > 192.168.0.28.65442: udp 32 (DF) [tos 0xfc]
45fc 003c 0000 4000 4011 b82d c0a8 001b
c0a8 001c 0400 ffa2 0028 0088 0000 0000
0000 0000 0000 0000 3000 0012 0000 7530
556e 7469 746c 6564 3100 0000
01:29:00.558027 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7eb9 0000 0020
0000 0000 0051 7d8a 0051 7d8d
01:29:00.558763 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 794e 0000 0040
0000 0000 0000 0005 0100 0000
01:29:00.572582 192.168.0.27.1024 > 192.168.0.28.65442: udp 124 (DF) [tos 0xfc]
45fc 0098 0000 4000 4011 b7d1 c0a8 001b
c0a8 001c 0400 ffa2 0084 7786 0000 0100
0000 0000 0000 0000 4000 0070 0100 0000
0000 0000 0000 0000 ffff 0060 0100 0008
0a04 da61 d1c0 4074 7a60 7470 4074 6060
7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 e8e0 4074 8a60 7470 4074 6060 7470
4074 6060 e8e0 4074 8a60 7470 4074 6060
7470 4074 6060 7470 4074 6060 7470 5050
0040 0016 c300 0000
01:29:00.577800 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7d90 0000 0120
0000 0000 0051 7d9f 0051 7da1
01:29:00.607561 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7d87 0000 0020
0100 0000 0051 7d8a 0051 7dbf
01:29:00.608387 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 784e 0000 0040
0100 0000 0000 0005 0100 0000
01:29:00.627581 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7c5e 0000 0120
0100 0000 0051 7d9f 0051 7dd3
01:29:00.657566 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7c55 0000 0020
0200 0000 0051 7d8a 0051 7df1
01:29:00.658386 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 774e 0000 0040
0200 0000 0000 0005 0100 0000
01:29:00.677565 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7b2c 0000 0120
0200 0000 0051 7d9f 0051 7e05
01:29:00.715662 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 784e 0000 0140
0000 0000 0000 0005 0100 0000
01:29:00.722158 192.168.0.27.1024 > 192.168.0.28.65442: udp 28 (DF) [tos 0xfc]
45fc 0038 0000 4000 4011 b831 c0a8 001b
c0a8 001c 0400 ffa2 0024 72c4 0000 0200
0000 0000 0000 0000 3100 000e 556e 7469
746c 6564 3100 0000
01:29:00.726347 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7b68 0000 0220
0000 0000 0051 7e33 0051 7e35
01:29:00.726982 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 774e 0000 0240
0000 0000 0000 0005 0100 0000
01:29:00.767503 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 774e 0000 0140
0100 0000 0000 0005 0100 0000
01:29:00.775363 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7a36 0000 0220
0100 0000 0051 7e33 0051 7e67
01:29:00.776410 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 764e 0000 0240
0100 0000 0000 0005 0100 0000
01:29:00.817579 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 764e 0000 0140
0200 0000 0000 0005 0100 0000
01:29:00.825379 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 7904 0000 0220
0200 0000 0051 7e33 0051 7e99
01:29:00.826377 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 754e 0000 0240
0200 0000 0000 0005 0100 0000
A longer press gives
32 bytes received from 192.168.0.27:1024
LOCK <30000> <Untitled1>
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
124 bytes received from 192.168.0.27:1024
Sending 16 bytes to 192.168.0.27:1024 [ACK]
IR_SEND <1500> <1> <0> <0x00> <0x00>
IR started <len=96 duration=0 eqptid=0>
IR_SEND evaluated to TRUE
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
24 bytes received from 192.168.0.27:1024
Sending 16 bytes to 192.168.0.27:1024 [ACK]
IR_RESUME <0xe00> <1500>
IR_RESUME evaluated to TRUE
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
20 bytes received from 192.168.0.27:1024
Sending 16 bytes to 192.168.0.27:1024 [ACK]
IR_STOP
IR_STOP evaluated to TRUE
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
28 bytes received from 192.168.0.27:1024
UNLOCK
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
Sending 16 bytes to 192.168.0.27:1024 [ACK]
Sending 16 bytes to 192.168.0.27:1024 [REPLY]
and
04:21:28.946567 192.168.0.27.1024 > 192.168.0.28.65442: udp 32 (DF) [tos 0xfc]
45fc 003c 0000 4000 4011 b82d c0a8 001b
c0a8 001c 0400 ffa2 0028 f387 0000 0d00
0000 0000 0000 0000 3000 0012 0000 7530
556e 7469 746c 6564 3100 0000
04:21:28.950903 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 a1aa 0000 0d20
0000 0000 00ef 64f4 00ef 64f6
04:21:28.951514 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6c4e 0000 0d40
0000 0000 0000 0005 0100 0000
04:21:28.962501 192.168.0.27.1024 > 192.168.0.28.65442: udp 124 (DF) [tos 0xfc]
45fc 0098 0000 4000 4011 b7d1 c0a8 001b
c0a8 001c 0400 ffa2 0084 64aa 0000 0e00
0000 0000 0000 0000 4000 0070 0100 0000
0000 05dc 0000 0000 ffff 0060 0100 0008
0a04 da61 d1c0 4074 7a60 7470 4074 6060
7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 e8e0 4074 8a60 7470 4074 6060 7470
4074 6060 e8e0 4074 8a60 7470 4074 6060
7470 4074 6060 7470 4074 6060 7470 5050
0040 0016 c300 0000
04:21:28.967385 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 a089 0000 0e20
0000 0000 00ef 6505 00ef 6506
04:21:28.972504 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6b4e 0000 0e40
0000 0000 0000 0005 0100 0000
04:21:29.007556 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 a071 0000 0d20
0100 0000 00ef 64f4 00ef 652f
04:21:29.008185 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6b4e 0000 0d40
0100 0000 0000 0005 0100 0000
04:21:29.016378 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9f57 0000 0e20
0100 0000 00ef 6505 00ef 6538
04:21:29.022400 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6a4e 0000 0e40
0100 0000 0000 0005 0100 0000
04:21:29.057551 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9f3f 0000 0d20
0200 0000 00ef 64f4 00ef 6561
04:21:29.058185 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6a4e 0000 0d40
0200 0000 0000 0005 0100 0000
04:21:29.066356 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9e25 0000 0e20
0200 0000 00ef 6505 00ef 656a
04:21:29.072383 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 694e 0000 0e40
0200 0000 0000 0005 0100 0000
04:21:29.745470 192.168.0.27.1024 > 192.168.0.28.65442: udp 24 (DF) [tos 0xfc]
45fc 0034 0000 4000 4011 b835 c0a8 001b
c0a8 001c 0400 ffa2 0020 169b 0000 0f00
0000 0000 0000 0000 4100 000c 0000 0e00
0000 05dc
04:21:29.749272 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 996d 0000 0f20
0000 0000 00ef 6813 00ef 6814
04:21:29.753500 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 6a4e 0000 0f40
0000 0000 0000 0005 0100 0000
04:21:29.798413 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 983b 0000 0f20
0100 0000 00ef 6813 00ef 6846
04:21:29.803389 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 694e 0000 0f40
0100 0000 0000 0005 0100 0000
04:21:29.848419 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9709 0000 0f20
0200 0000 00ef 6813 00ef 6878
04:21:29.853360 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 684e 0000 0f40
0200 0000 0000 0005 0100 0000
04:21:30.047244 192.168.0.27.1024 > 192.168.0.28.65442: udp 20 (DF) [tos 0xfc]
45fc 0030 0000 4000 4011 b839 c0a8 001b
c0a8 001c 0400 ffa2 001c 1a83 0000 1000
0000 0000 0000 0000 4200 0008 0000 0e00
04:21:30.050909 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9612 0000 1020
0000 0000 00ef 6940 00ef 6942
04:21:30.087245 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 694e 0000 1040
0000 0000 0000 0005 0100 0000
04:21:30.106174 192.168.0.27.1024 > 192.168.0.28.65442: udp 28 (DF) [tos 0xfc]
45fc 0038 0000 4000 4011 b831 c0a8 001b
c0a8 001c 0400 ffa2 0024 63c4 0000 1100
0000 0000 0000 0000 3100 000e 556e 7469
746c 6564 3100 0000
04:21:30.110449 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 94d6 0000 1020
0100 0000 00ef 6940 00ef 697e
04:21:30.111044 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 949b 0000 1120
0000 0000 00ef 697b 00ef 697e
04:21:30.111649 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 684e 0000 1140
0000 0000 0000 0005 0100 0000
04:21:30.137569 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 684e 0000 1040
0100 0000 0000 0005 0100 0000
04:21:30.177549 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9393 0000 1020
0200 0000 00ef 6940 00ef 69c1
04:21:30.178170 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9358 0000 1120
0100 0000 00ef 697b 00ef 69c1
04:21:30.178776 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 674e 0000 1140
0100 0000 0000 0005 0100 0000
04:21:30.187355 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 674e 0000 1040
0200 0000 0000 0005 0100 0000
04:21:30.227556 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 9226 0000 1120
0200 0000 00ef 697b 00ef 69f3
04:21:30.228373 192.168.0.28.65442 > 192.168.0.27.1024: udp 16 (DF)
4500 002c 0000 4000 4011 b939 c0a8 001c
c0a8 001b ffa2 0400 0018 664e 0000 1140
0200 0000 0000 0005 0100 0000
For the moment we concentrate on the longest packet in each of these captures as that should contain the IR data. After stripping off the UDP headers for the short press this is
0000 0100 0000 0000 0000 0000 4000 0070 0100 0000
0000 0000 0000 0000 ffff 0060 0100 0008 0a04 da61
d1c0 4074 7a60 7470 4074 6060 7470 4074 6060 e8e0
4074 8a60 7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 7470 4074 6060 e8e0 4074 8a60 7470 4074 6060
7470 4074 6060 7470 4074 6060 7470 5050 0040 0016
c300 0000
and for the long press
0000 0100 0000 0000 0000 0000 4000 0070 0100 0000
0000 05dc 0000 0000 ffff 0060 0100 0008 0a04 da61
d1c0 4074 7a60 7470 4074 6060 7470 4074 6060 e8e0
4074 8a60 7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 7470 4074 6060 e8e0 4074 8a60 7470 4074 6060
7470 4074 6060 7470 4074 6060 7470 5050 0040 0016
c300 0000
These are identical except for the value in the 12th word, so it seems that just like for the dsw test harness a value of zero is sent to indicate the code should be sent once and a timeout should be specified if the code is to be repeated for some period of time.
Sending some different codes and further analysis gives the message structure
Unknown | Packet id | Unknown | Type | Length | Port | Unknown | Timeout | Unknown | Payload |
---|---|---|---|---|---|---|---|---|---|
0000 | 01 | 00 0000 0000 0000 0000 | 4000 | 0070 | 01 | 00 0000 0000 | 05dc | 0000 0000 | The rest |
Incremented on each packet sent by TSU, returned in status responses from RFX. RFX will ignore as duplicate packets with the same id if they are received within some period of time
4000 represents IR data. Other values are used for control messages and presumably for serial data
Zero indexed IR output port number to use
Number of milliseconds to repeat the code for, in this case 0x05dc is 1500ms
This is the IR data in ECF format
What I tried to send in Pronto HEX format fromt the TSU was
Type | Period | Initial burst length | Repeat burst length | Burst data |
---|---|---|---|---|
0000 | 0067 | 0000 | 000D | 0060 0018 0018 0018 0018 0018 0030 0018 0018 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0018 0018 0018 0018 0018 041F |
What was sent to the RFX was
ffff 0060 0100 0008 0a04 da61 d1c0 4074 7a60 7470
4074 6060 7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 e8e0 4074 8a60 7470 4074 6060 7470 4074 6060
e8e0 4074 8a60 7470 4074 6060 7470 4074 6060 7470
4074 6060 7470 5050 0040 0016 c300 0000
Which looked so different at first I thought it might be encrypted. However, by varying the input slightly and noting the different output a pattern starts to emerge
The first word, ffff, seems to indicated a learned code in the same way as 0000 does for Pronto HEX. Database codes are prefixed with eecf
There are 96 bytes in the payload and 0x60 is 96
The burst pairs, after the preamble are all either equal or one is double the other. At first there seems to be nothing like that in the ECF data, but with a little rearranging
ffff 0060 0100 0008 0a04 da6
1d1c04 0747a6
074704 074606
074704 074606
0e8e04 0748a6
074704 074606
0e8e04 0748a6
074704 074606
074704 074606
0e8e04 0748a6
074704 074606
074704 074606
074704 074606
074705 050004
0 0016 c300 0000
So the burst pairs are there, it's just that they're now much larger values stored as 3 bytes each, and they are not exactly equal/double
Looking at the first value in the ECF burst data, 0x1d1c04 = 1907716 whereas in Pronto HEX it was 0x60=96. Dividing these gives approximately 1907716/96 = 19872. This is suspiciously close to the value in the word preceeding the burst data, 0x4da6 = 19878. So let us assume that 0x4da6 represents the period and that instead of the burst pairs being length in terms of number of periods as in Pronto HEX it is instead a length in the same units as the period itself
That close match was enticing, but the numbers never quite looked right. Each of the first half pair was a slightly different multiple of the corresponding value in the Pronto HEX. 1d1c04 is not exactly 4 times 074704 as it should be and 0e8e04 is not exactly twice 074704. It's possble there could be some inaccuracy in the conversion, but there is no pattern to the conversion. Also the pairs that should have equal values are different (074704 and 074606), it could be that the pair sums are massaged to all be multiples of the same value to prevent clock drift, but there is no pattern here either.
After persisting for some time and getting nowhere I went back looking for a different pattern. After testing a number of working codes and tweaking them slightly I found that the fact the pairs end in 4, 5 or 6 was critical. So a bit of rearranging gives
ffff 0060 0100 0008 0a
04da
6 1d1c0 4 0747a
6 07470 4 07460
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 07470 4 07460
6 07470 4 07460
6 07470 5 05000
40 0016 c300 0000
So now we have a period of 0x04da = 1242
Each burst pair starts with a 6 that we will interpret as "on" or "start pulse" and then has a 4 before the second half for "off". This seems unnecessary for PWM, but maybe this is so they can also support RC5 codes. The 5 before the final half pair seems to be "end" and the value after that is not burst data at all.
Checking some values...
The period 1242 = 12.058 * 103 which is the period from the HEX format. It also happens that 50000000/1242 =~ 40257 which is the frequency. That value of 50 million also pops up later
The use of 2 and a half bytes for each burst value is odd, but that gives a maximum of just over a million which ties in well with Philips' claim of being able to support 1MHz codes. It also saves a little space which may have been deemed important
The values in the first burst look promising. 1d1c0 is exactly 4 times 07470 which is exactly half 0e8e0. They're also all multiples of the perion 1242
The second values look a little suspicious, but summing the burst pairs gives (decimal) values of 149050, 59600 and 89450 which have a common deniminator of 50. Given that the period seems to be set in terms of the inverse of 50 million and the device supports 1MHz output then synchronising each pair to units of 50 seems to be a potential requirement
The second half of the final burst pair looks to have been declared redundant and replaced. Through testing with dsw it appears this is the time to send the code for, although the rules for calculation of this value are not yet apparent
The other values are a mystery and there is nothing that appears to correspond to the initial/repeat burst length values in Pronto HEX. The lack of an initial burst in the HEX probably isn't helping, so trying again with two pairs of initial burst added to the above HEX yields
ffff 006c 0100 0008 0a
04da
6 07470 4 07460
6 07470 4 07460
6 1d1c0 4 0747a
6 07470 4 07460
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 07470 4 07460
6 0e8e0 4 0748a
6 07470 4 07460
6 07470 4 07460
6 07470 4 07460
6 07470 5 05000
40 0016 c300 000f
The only change other than the additional pairs and the length is last byte 0x0f = 15, which is the number of bytes from the a0 before the period to the start of the repeat burst, so there are no actual lengths for the burst data, just this offset
Putting everything together gives
Field | Width | Example | Description |
---|---|---|---|
Type | Word | ffff | ffff = Learned code, eecf = Database code |
Length | Word | 006c | Count of bytes in ECF |
Unknown | 5 bytes | 0100 0008 0a | Presumably more than one field, but not known how many |
Period | Word | 04da | Units of 1/50000000 s, Period = 50000000/frequency |
Burst pairs | 6 bytes per pair | See below | Final half is total duration, not a burst |
Unknown | 5 bytes | 40 0016 c300 | May also actually be more than one field |
Burst offset | Word | 000f | 0 = no initial burst, otherwise nunber of bytes in initial burst + 3 |
Field | Width | Example | Description |
---|---|---|---|
Control | 4 bits | 6 | 6 = switch on (oscillate) |
First burst | 20 bits | 07470 | Units of 1/50000000 s |
Control | 4 bits | 4 | 4 = switch off |
Second burst | 20 bits | 07460 | Units of 1/50000000 s. Adjusted such that (first + second) is a multiple of 50 |
Field | Width | Example | Description |
---|---|---|---|
Control | 4 bits | 6 | 6 = switch on (oscillate) |
First burst | 20 bits | 07470 | Units of 1/50000000 s |
Control | 4 bits | 5 | 5 = end |
Code duration | 20 bits | 05000 | Units of 1 microsecond. Sum of all burst lengths divided by 50 where instead of the final burst length the value 2000 is used (or perhaps it is limited to a maximum of 2000) |
We're now at a point where we can convert existing HEX codes to ECF
ECF field | Value |
---|---|
Type | ffff |
Length | Number of burst pairs + 24 |
Unknown | Hardcode to 0100 0008 0a0 |
Period | 12.058 * Pronto HEX period (rounded to nearest integer) |
Burst pairs | Each burst pair value in Pronto HEX, multipled by ECF period and stored as 20 bits per value. Control bits set as definition. Final burst is duration (calculated as per definition) and has a control value of 5 |
Unknown | Hard code to 00 0000 0000 |
Burst offset | 0 or number of initial burst bytes + 3 |
This Pronto HEX
0000 0067 0000 000D 0060 0018 0018 0018 0018 0018 0030 0018 0018 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0018 0018 0018 0018 0018 041F
converts to
FFFF 0060 0100 0008 0A04 DA61 D1C0 4074 7A60 7470
4074 6060 7470 4074 6060 E8E0 4074 8A60 7470 4074
6060 E8E0 4074 8A60 7470 4074 6060 7470 4074 6060
E8E0 4074 8A60 7470 4074 6060 7470 4074 6060 7470
4074 6060 7470 4050 0040 0000 0000 0000
while a real TSU/RFX gives
ffff 0060 0100 0008 0a04 da61 d1c0 4074 7a60 7470
4074 6060 7470 4074 6060 e8e0 4074 8a60 7470 4074
6060 e8e0 4074 8a60 7470 4074 6060 7470 4074 6060
e8e0 4074 8a60 7470 4074 6060 7470 4074 6060 7470
4074 6060 7470 5050 0040 0016 c300 0000
This is identical, except for the final 3 words whose derivation we have not found. Some other codes give results that don't quite match due to differences in how the rounding to nearest 50 is handled, but this is getting very close
Heading back to the message dumps/logs we see there are 2 potential scenarios for communication between the TSU and RFX when sending an IR code
When a button is pressed briefly enough to be interpretted as "send once" the following messages are sent from the TSU
LOCK
IR_SEND
UNLOCK
The LOCK message has a duration which is always 30000 milliseconds and a string to identify the owner which is the Project Name in PEP
The IR_SEND has a duration of zero to indicate "send once"
The UNLOCK is sent once the RFX has replied that the IR_SEND is complete
The messages become
LOCK
IR_SEND
IR_RESUME
IR_STOP
UNLOCK
The LOCK message again has a duration which is always 30000 milliseconds and a string to identify the owner which is the Project Name in PEP
The IR_SEND has a duration of 1500 milliseconds
If the button is held for more than around a second then roughly every second an IR_RESUME will be sent with a duration of 1500 milliseconds to indicate the IR code sending should continue
An IR_STOP is sent to stop sending
The UNLOCK is sent once the RFX has replied that the IR_STOP is complete
If a button is held past the 30 second lock timeout then the lock is expired on the RFX and the busy light goes out. Commands can still be sent after this but it is unknown if the RFX processes them
Going back from the RFX to the TSU there are ACK and REPLY messages
By analysing the tcpdump output the structure is found
Field | Width | Example | Description |
---|---|---|---|
Unknown | Word | 0000 | Appears to always be zero |
Packet id | Byte | 0d | Incremented on each message sent from TSU to RFX, included in each response from RFX to TSU |
Unknown | 9 bytes | 00 0000 0000 0000 0000 | Probably more than one field, but always seem to be zeros |
Type | Word | 3000 | 3000 = LOCK |
Length | Word | 0012 | Number of bytes in lock name + 9 |
Unknown | Word | 0000 | Appears to always be zero |
Timeout | Word | 7530 | Lock timeout in milliseconds, appears to always be 30000 |
Lock name | Variable | 556e 7469 746c 6564 31 | ASCII encoded lock name, in this case 'Untitled1'. Matches project name in PEP |
As covered above
Field | Width | Example | Description |
---|---|---|---|
Unknown | Word | 0000 | Appears to always be zero |
Packet id | Byte | 0e | Incremented on each message sent from TSU to RFX, included in each response from RFX to TSU |
Unknown | 9 bytes | 00 0000 0000 0000 0000 | Probably more than one field, but always seem to be zeros |
Type | Word | 4000 | 4000 = IR_SEND |
Length | Word | 0070 | Number of bytes in payload + 16 |
Port | Byte | 00 | Zero indexed IR port number to send on |
Unknown | 5 bytes | 00 0000 0000 | Probably more than one field, but always seem to be zeros |
Timeout | Word | 05dc | Number of milliseconds to send for, always 1500 |
Unknown | 2 words | 0000 0000 | Probably more than one field, but always seem to be zeros |
Payload | Variable | IR code in ECF format |
Field | Width | Example | Description |
---|---|---|---|
Unknown | Word | 0000 | Appears to always be zero |
Packet id | Byte | 0f | Incremented on each message sent from TSU to RFX, included in each response from RFX to TSU |
Unknown | 9 bytes | 00 0000 0000 0000 0000 | Probably more than one field, but always seem to be zeros |
Type | Word | 4100 | 4100 = IR_RESUME |
Length | Word | 000c | Always 12? |
Unknown | Word | 0000 | Appears to always be zero |
Send id | Byte | 0e | Packet id of the IR_SEND being resumed |
Unknown | 3 bytes | 00 0000 | Appears to always be zero |
Timeout | Word | 05dc | Number of milliseconds to continue for, always 1500 |
Field | Width | Example | Description |
---|---|---|---|
Unknown | Word | 0000 | Appears to always be zero |
Packet id | Byte | 10 | Incremented on each message sent from TSU to RFX, included in each response from RFX to TSU |
Unknown | 9 bytes | 00 0000 0000 0000 0000 | Probably more than one field, but always seem to be zeros |
Type | Word | 4200 | 4200 = IR_STOP |
Length | Word | 0008 | Always 8? |
Unknown | Word | 0000 | Appears to always be zero |
Send id | Byte | 0e | Packet id of the IR_SEND being stopped |
Unknown | Byte | 00 | Appears to always be zero |
Field | Width | Example | Description |
---|---|---|---|
Unknown | Word | 0000 | Appears to always be zero |
Packet id | Byte | 11 | Incremented on each message sent from TSU to RFX, included in each response from RFX to TSU |
Unknown | 9 bytes | 00 0000 0000 0000 0000 | Probably more than one field, but always seem to be zeros |
Type | Word | 4200 | 3100 = UNLOCK |
Length | Word | 000e | Number of bytes in lock name + 5 |
Lock name | Variable | 556e 7469 746c 6564 31 | ASCII encoded lock name, in this case 'Untitled1'. Matches project name in PEP |
Send id | Byte | 0e | Packet id of the IR_SEND being stopped |
Unknown | 3 bytes | 00 0000 | Appears to always be zero |
Hello! Did you tried to integrate RFX9600 into Home Assistant?