Skip to content

Latest commit

 

History

History
320 lines (266 loc) · 33.8 KB

REVERSE_ENGINEERING.rst

File metadata and controls

320 lines (266 loc) · 33.8 KB

Reverse Engineering

Debugging with the rocket-r60v CLI tool

You can read any memory address by using the rocket-r60v CLI tool:

usage: rocket-r60v read [-h] address length

positional arguments:
  address     the memory address (unsigned 16-bit integer)
  length      the data length (unsigned 16-bit integer)

optional arguments:
  -h, --help  show this help message and exit

There's also an option for writing (use with caution):

usage: rocket-r60v write [-h] [-r] address length data

positional arguments:
  address     the memory address (unsigned 16-bit integer)
  length      the data length (unsigned 16-bit integer)
  data        the memory data (8-bit unsigned integers or hex value if raw)

optional arguments:
  -h, --help  show this help message and exit
  -r, --raw   send raw data, do not encode data to hex

If you want to display all implemented byte addresses and the settings, run:

./rocket-r60v addresses

TCP server

The R 60V has a TCP listener which accepts unencrypted connections to configure it:

  • IP address: 192.168.1.1
  • TCP port: 1774

Note

There's no HTTP/REST interface or alike. It's all plain TCP, therefore this Python API.

Message protocol

Fortunately, the R 60V uses a relatively simple message protocol format.

I'm not 100% sure, but I think it's simply an interface to read & write the memory of the Rocket R 60V. I also suppose that the whole logic is built into the original Rocket mobile app itself, and there's no "rich" backend in the machine itself.

Here's an example message which will set the language of the machine to English:

w000100010059
Position Data Type Usage Description Example
1 8-bit unsigned integer, encoded as uppercase hex value command The command. Uses r (0x72) for reading, or w (0x77) for writing. w
2 … 5 16-bit unsigned integer, encoded as uppercase hex value address The memory address. 0001
6 … 9 16-bit unsigned integer, encoded as uppercase hex value length The length of the data. 0001
10 … (10 + length * 2) sequence of 8-bit unsigned integers, encoded as uppercase hex values data The data itself. The length must be multiplied by 2, as an 8-bit integer uses 2 hex chars (i.e. FF). 00
(END - 2) … END 8-bit unsigned integer, encoded as uppercase hex value checksum The checksum. Modulo 256 of the sum of all bytes. 59

Note

Please note that these are not "official" terms by Rocket itself. I just reverse engineered everything and tried to use matching terms all across my code base. Next to these terms, I'll make use of the following ones:

  • Raw Message: The complete message with its checksum (i.e. w000100010059)
  • Message: The message without its checksum (i.e. w0001000100)
  • Envelope: The command, address & length (i.e. w00010001)

iOS app communication

I've installed the iOS app on my iPhone and analysed the network communication when using the app. This is achieved by:

  • Installing the iOS app on the iPhone
  • Installing Xcode on the MacBook
  • Installing Wireshark on the MacBook
  • Connecting the iPhone via USB to the MacBook and trusting it
  • Opening Xcode and adding the device under Window > Devices and Simulators
  • Copy the UDID
  • Executing the /Library/Apple/usr/bin/rvictl -x {UDID} on the shell
  • Starting Wireshark and recording on the interface rvi0
  • Using the app and doing a single action
  • Stopping Wireshark
  • Filtering the conversation (e.g. ip.addr==192.168.1.1 && ip.addr==192.168.1.11 && tcp.port==1774)
  • Analysing the data packets

There's an excellent tutorial by pentest_it available which describes How to capture network traffic from iPhone with tcpdump.

Android APK decode

Download the Rocket Espresso R 60V android app to your PC. There are several ways to do this, either by installing it on your Android phone and extracting it from there, or by downloading it directly from the Google Play store. Just google for it or use the APK Downloader by APK.Support.

Install the Apktool to decode the APK. There's a Homebrew Formulae available for Mac OS X.

When you've downloaded the Android app and installed apktool, you can decode the app by running:

apktool decode -o rocket_app {Rocket apk file}

There should now be a rocket/ directory with the decoded app. When browsing through the smali files, you can find hints how to access different data of the machine.

For example, the smali/singleton/SettingsSingleton.smali contains lines which look like this:

.field private static final INGRESSO_ACQUA:I = 0x46

These are significant static fields which point to a byte address of a specific setting. Fortunately, with a bit knowledge of Italian (or a translator), you found yourself a mapping between the settings and the actual memory addresses. The addresses are 16bit unsigned integers, encoded in uppercase hex characters.

A bit of grepping like grep -R 'ADDRESS:I' or grep -R 'field public static final \w\+:I = 0x[0-9a-f]\{1,2\}$' can disclose even more addresses!

Note

Please note that the Android app is developed in Java. Fortunately, Java is an interpreted language and thus the shipped bytecode can be decoded back into "readable" source code.

Unfortunately, most iOS apps are compiled into machine code. The Rocket iOS app is no exception to this. There's only compiled machine code and no bytecode available. Decompiling machine code back into "readable" source code (e.g. Objectiv-C or Swift) is a much harder task. It would even be easier to disassembling it into assembly, but even that is a hard thing to do and hard to reverse engineer.

Therefor I'd stick with the Java bytecode / APK and decode it for reverse engineering of the app / protocol.

Memory addresses

Here's a list of memory addresses I found in the bytecode, with an optional link to the implementation of the setting:

State Address Field Name Implementation / Notes example data
implemented 0x01 LINGUA_ADDRESS Language  
implemented 0x02 TEMP_SET_CAF_ADDRESS Brew Boiler Temperature  
implemented 0x03 TEMPERATURA_VAPORE_ADDRESS Service Boiler Temperature  
? 0x04 KP_CAFFE_ADDRESS coffee [P]ID? [15, 0]
? 0x06 KP_GRUPPO_ADDRESS group [P]ID? [40, 0]
? 0x0A KI_CAFFE_ADDRESS coffee P[I]D? [1, 0]
? 0x0C KI_GRUPPO_ADDRESS group P[I]D? [1, 0]
? 0x10 KD_CAFFE_ADDRESS coffee PI[D]? [65, 0]
? 0x12 KD_GRUPPO_ADDRESS group PI[D]? [5, 0]
implemented 0x16   Pressure Profile A  
implemented 0x26   Pressure Profile B  
implemented 0x36   Pressure Profile C  
? 0x2B ENAB_PRE_INF_ADDRESS enable pre-infusion? [0]
? 0x2C T_OFF_PRE_INF_ADDRESS time off-preinfusion? [0, 0, 0, 0]
? 0x30 T_ON_PRE_INF_ADDRESS time on pre-infusion? [40, 90, 0, 0]
? 0x45 TEMP_SET_LANCIA_ADDRESS   [0]
implemented 0x46 INGRESSO_ACQUA Water Feed  
implemented 0x47 TIPO_TASTIERA_ADDRESS Active Profile  
? 0x48 T_LAV_LANCIA_ADDRESS   [15]
implemented 0x49 ENAB_CALDVAP_ADDRESS Service Boiler  
implemented 0x4A STATO_MACCHINA_ADDRESS Standby  
? 0x4B COUNT_PARZ_ADDRESS partial coffee counter? counts up with 0x4d  
? 0x4C TEMP_SET_GRUPPO_ADDRESS   [0]
implemented 0x4D COUNT_TOT_ADDRESS Total Coffee Count  
implemented 0x51 ORA_AUTO_ON_ADDRESS Auto On Hour  
implemented 0x52 MIN_AUTO_ON_ADDRESS Auto On Minute  
implemented 0x53 ORA_AUTO_OFF_ADDRESS Auto Off Hour  
implemented 0x54 MIN_AUTO_OFF_ADDRESS Auto Off Minute  
? 0x55 DAY_OFF_ADDRESS weekdays when timer isn't active? [0, 0, 0, 0]
? 0x55 ENAB_PROG_ADDRESS enable programming? same address as field above.  
? 0x59 CICLI_MANUT_ADDRESS maintenance cycle by Rocket? [0, 0, 0, 0]
? 0x5D DOSES_COUNT_K1_GR1_ADDRESS    
? 0x5F DOSES_COUNT_K2_GR1_ADDRESS    
? 0x61 DOSES_COUNT_K3_GR1_ADDRESS    
? 0x63 DOSES_COUNT_K4_GR1_ADDRESS    
? 0x65 DOSES_COUNT_K5_GR1_ADDRESS    
? 0x67 DOSES_COUNT_K1_GR2_ADDRESS    
? 0x69 DOSES_COUNT_K2_GR2_ADDRESS    
? 0x6B DOSES_COUNT_K3_GR2_ADDRESS    
? 0x6D DOSES_COUNT_K4_GR2_ADDRESS    
? 0x6F DOSES_COUNT_K5_GR2_ADDRESS    
? 0x71 DOSES_COUNT_K1_GR3_ADDRESS    
? 0x73 DOSES_COUNT_K2_GR3_ADDRESS    
? 0x75 DOSES_COUNT_K3_GR3_ADDRESS    
invalid 0x77 DOSES_COUNT_K4_GR3_ADDRESS not working, invalid response envelope (when length 2)  
invalid 0x79 DOSES_COUNT_K5_GR3_ADDRESS not working, invalid response envelope  
invalid 0x7B DOSES_COUNT_TEA1_ADDRESS not working, invalid response envelope  
invalid 0x7C DOSES_COUNT_TEA2_ADDRESS not working, invalid response envelope  
invalid 0x7D DOSES_COUNT_TEA3_ADDRESS not working, invalid response envelope  
invalid 0x97 COUNT_K1_GR1_ADDRESS not working, invalid response envelope  
invalid 0x99 COUNT_K2_GR1_ADDRESS not working, invalid response envelope  
invalid 0x9B COUNT_K3_GR1_ADDRESS not working, invalid response envelope  
invalid 0x9D COUNT_K4_GR1_ADDRESS not working, invalid response envelope  
invalid 0x9F COUNT_K5_GR1_ADDRESS not working, invalid response envelope  
invalid 0xA1 COUNT_K1_GR2_ADDRESS not working, invalid response envelope  
invalid 0xA3 COUNT_K2_GR2_ADDRESS not working, invalid response envelope  
invalid 0xA5 COUNT_K3_GR2_ADDRESS not working, invalid response envelope  
invalid 0xA7 COUNT_K4_GR2_ADDRESS not working, invalid response envelope  
invalid 0xA9 COUNT_K5_GR2_ADDRESS not working, invalid response envelope  
invalid 0xAB COUNT_K1_GR3_ADDRESS not working, invalid response envelope  
invalid 0xAD COUNT_K2_GR3_ADDRESS not working, invalid response envelope  
invalid 0xAF COUNT_K3_GR3_ADDRESS not working, invalid response envelope  
invalid 0xB1 COUNT_K4_GR3_ADDRESS not working, invalid response envelope  
invalid 0xB3 COUNT_K5_GR3_ADDRESS not working, invalid response envelope  
invalid 0xB5 COUNT_TEA1_ADDRESS not working, invalid response envelope  
invalid 0xB7 COUNT_TEA2_ADDRESS not working, invalid response envelope  
invalid 0xB9 COUNT_TEA3_ADDRESS not working, invalid response envelope  
invalid 0xC3 LITRI_FILTRO_ADDRESS not working, invalid response envelope  
invalid 0xC7 COUNT_LAVAGGIO_ADDRESS not working, invalid response envelope  
invalid 0xCB COUNT_POMPA_ADDRESS not working, invalid response envelope  
invalid 0xCF COUNT_RIEMPIMENTO_ADDRESS not working, invalid response envelope  
implemented 0xA000   Date Time  
implemented 0xB000   Current Brew Boiler Temperature  
implemented 0xB001   Current Service Boiler Temperature  
  0xB002   current pressure  
  0xB007   display content  

jffry's library

Another GitHub user called jffry already did another client API written in NodeJS for the Rocket R 60V. Kudos to his excellent reverse engineering and for publishing his findings!