Rolling-Flaws (KeeLoq rolling code teacher)
925
subghz/apps/rolling-flaws/README.md
Normal file
@ -0,0 +1,925 @@
|
|||||||
|
# Rolling Flaws
|
||||||
|
|
||||||
|
Rolling Flaws by @CodeAllNight.
|
||||||
|
Discord invite: https://discord.com/invite/NsjCvqwPAd
|
||||||
|
|
||||||
|
This application is intended to help you learn about rolling code flaws.
|
||||||
|
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Helpful hints](#helpful-hints)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Menu Options](#menu-options)
|
||||||
|
- [Settings](#settings)
|
||||||
|
- [Tutorial](#tutorial)
|
||||||
|
- [Scenario 1: clone RAW signal, replay attack on](#scenario-1-clone-raw-signal-replay-attack-on)
|
||||||
|
- [Scenario 2: clone RAW signal, replay attack off](#scenario-2-clone-raw-signal-replay-attack-off)
|
||||||
|
- [Scenario 3: pair remote, send next code](#scenario-3-pair-remote-send-next-code)
|
||||||
|
- [Scenario 4: clone remote, send next code](#scenario-4-clone-remote-send-next-code)
|
||||||
|
- [Scenario 5: skip ahead, within window-next](#scenario-5-skip-ahead-within-window-next)
|
||||||
|
- [Scenario 6: future attack](#scenario-6-future-attack)
|
||||||
|
- [Scenario 7: rollback attack](#scenario-7-rollback-attack)
|
||||||
|
- [Scenario 8: rolljam attack](#scenario-8-rolljam-attack)
|
||||||
|
- [Scenario 9: KGB/Subaru MF attack](#scenario-9-kgbsubaru-mf-attack)
|
||||||
|
- [Scenario 10: unknown MF attack](#scenario-10-unknown-mf-attack)
|
||||||
|
- [Scenario 11: enc00 attack](#scenario-11-enc00-attack)
|
||||||
|
- [Scenario 12: test transmitter](#scenario-12-test-transmitter)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
**Educational use only.** This application is intended to be used for educational purposes only. It is intended to help you learn about rolling code flaws. IIf you use this information to attack devices, you are responsible for any damage you cause.
|
||||||
|
|
||||||
|
Sending signals to a real receiver has the potential to desync the remote and can even cause the remote to no longer be valid. The reason this application was built was so that you DO NOT mess with equipment, unless you are pen testing it with permission. Even then, you can still mess things up & require service or replacement (for example, HCS300 overflow bits get cleared and you reach 0xFFFF count then bad things may happen). Please use this application instead of an actual device.
|
||||||
|
|
||||||
|
This application is intended to simulate various KeeLoq receivers that you may encounter. You can configure the receiver to simulate the device you want to practice on. Use a second Flipper Zero or HackRF or whatever to try to get the "Opened!" message.
|
||||||
|
|
||||||
|
In the future, I hope to offload this application to an ESP32+CC1101 so that you can use a single Flipper to practice rolling codes.
|
||||||
|
|
||||||
|
## Helpful hints
|
||||||
|
You can rename the file ``SD Card\subghz\assets\keeloq_mfcodes``, so that a .sub file with KeeLoq protocol will be sent **without incrementing** counts. This will also cause all signals to be decoded as "KL Unknown". Be sure to rename it back when you are done.
|
||||||
|
|
||||||
|
Firmware other than Official does support encoding rolling code signals. This makes it easier to save and transmit rolling codes. This can be a good thing when you are pen testing, but a bad thing when you are just clicking around and don't understand the potential risks. In official firmware, you can still decode signals, write the keys down and then use the "Add Manually" option to create a signal, and then edit the keys to match what was decoded. It's more work, but then you won't accidently be sending signals that you don't understand. Unofficial firmware may also unlock the TX frequency list, which may be illegal in your region. Only broadcast on frequencies that are legal in your region. If you choose to install Unofficial firmware, it is not supported by the Flipper Zero team, so you will need to get support from the community. If you are using unofficial firmware, you should be aware of the risks.
|
||||||
|
|
||||||
|
This rest of this section assumes you are familiar with building your own firmware. If you are not familiar, [this video](https://youtu.be/gqovwRkn2xw) will walk you through the process of being able to build and deplay the firmware. Note: Due to recent firmware changes, you need to do "[Debug] Flash (USB, with Resources)" [instead of *without* Resources] since some of the subghz code has moved into Resources.
|
||||||
|
|
||||||
|
If you want a Bin_RAW file, you can build a custom firmware without knowledge of keeloq. In ``.\lib\subghz\protocols\protocol_items.c`` replace the line ``&subghz_protocol_keeloq,`` with ``// &subghz_protocol_keeloq,``. Then build the firmware. Now you can use the "Read" option with Bin_RAW set to "On" to get a Bin_RAW file. Be sure to edit the file back when you are done.
|
||||||
|
|
||||||
|
If you want to generate a custom SUB file for a specific key and count, you can replace the ``case SubmenuIndexDoorHan_433_92`` code in the ``.\applications\main\subghz\scenes\subghz_scene_set_type.c`` file. You will need to build and deploy the firmware with resources. This will replace the implmentation of "Add Manually/DoorHan_433". For example, if you want to generate a SUB file for a DoorHan remote with key=0x084EE9D5, button=0x2, and count of 0xEC00, at 433.920MHz; you would use the following...
|
||||||
|
```c
|
||||||
|
case SubmenuIndexDoorHan_433_92:
|
||||||
|
generated_protocol = subghz_txrx_gen_keeloq_protocol(
|
||||||
|
subghz->txrx, "AM650", 433920000, "DoorHan", 0x084EE9D5, 0x2, 0xEC00);
|
||||||
|
if(generated_protocol != SubGhzProtocolStatusOk) {
|
||||||
|
furi_string_set(
|
||||||
|
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
|
||||||
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want the Flipper Zero to be able to decode the same signal multiple times, in ``.\lib\subghz\protocols\protocol_items.c`` after the two occurances of ``instance->decoder.decode_count_bit = 0;`` add the line ``instance->generic.data = 0;``. This will cause the Flipper Zero to forget the previous data, so it will decode the same signal multiple times. Be sure to edit the file back when you are done.
|
||||||
|
|
||||||
|
To scan for more interesting sequences, make this breaking change to keeloq.c file that will keep incrementing the key until it finds a DoorHan code (but it leaves the FIX value the same). This is one technique to search for ENC00 sequences. Be sure to edit the file back when you are done.
|
||||||
|
```c
|
||||||
|
void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubGhzProtocolDecoderKeeloq* instance = context;
|
||||||
|
|
||||||
|
// Uncomment the next line if you want to ALWAYS jump ahead.
|
||||||
|
// instance->generic.data += 0x100000000L;
|
||||||
|
while(true) {
|
||||||
|
subghz_protocol_keeloq_check_remote_controller(
|
||||||
|
&instance->generic, instance->keystore, &instance->manufacture_name);
|
||||||
|
if(strcmp(instance->manufacture_name, "Unknown") == 0) {
|
||||||
|
// UNKNOWN
|
||||||
|
} else if(strcmp(instance->manufacture_name, "DoorHan") != 0) {
|
||||||
|
FURI_LOG_E(
|
||||||
|
TAG,
|
||||||
|
"Wrong manufacturer name: %s high-bytes:%08lX cnt:%08lX",
|
||||||
|
instance->manufacture_name,
|
||||||
|
(uint32_t)(instance->generic.data >> 32),
|
||||||
|
instance->generic.cnt);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Found manufacturer name: %s %08lX",
|
||||||
|
instance->manufacture_name,
|
||||||
|
(uint32_t)(instance->generic.data >> 32));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->generic.data += 0x100000000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue with the original code.
|
||||||
|
```
|
||||||
|
|
||||||
|
Two common hardware implementations of KeeLoq are the HCS300, which uses 10 bits in discriminator & the HCS200, which uses 8 bits. The Flipper Zero software implementation decodes using 8 bits. If you make a custom change to the ``.\lib\subghz\protocols\keeloq.c`` file you can return the encoded data, which will be used by the Rolling Flaws application. For "SN00/cfw*" set to "No" to work properly, you will need these changes. For "SN bits/cfw*" set to "10 (dec)", you will also need this changes. These changes allow the application to see the encrypted data, which is needed for the "SN00/cfw*" and "SN bits/cfw*" features to work properly.
|
||||||
|
|
||||||
|
Step 1. Change the two occurances of ``decrypt & 0x0000FFFF`` to read ``decrypt``.
|
||||||
|
Step 2. Change the printf at the bottom of the file...
|
||||||
|
- In particular, we use ``instance->generic.cnt & 0xFFFF`` instead of ``instance->generic.cnt``.
|
||||||
|
- We added ``"Enc:%04lX\r\n"`` to the end of the printf string.
|
||||||
|
- We added a final parameter ``instance->generic.cnt >> 16`` to the end of the printf.
|
||||||
|
```c
|
||||||
|
furi_string_cat_printf(
|
||||||
|
output,
|
||||||
|
"%s %dbit\r\n"
|
||||||
|
"Key:%08lX%08lX\r\n"
|
||||||
|
"Fix:0x%08lX Cnt:%04lX\r\n"
|
||||||
|
"Hop:0x%08lX Btn:%01X\r\n"
|
||||||
|
"MF:%s\r\n"
|
||||||
|
"Sn:0x%07lX \r\n"
|
||||||
|
"Enc:%04lX\r\n",
|
||||||
|
instance->generic.protocol_name,
|
||||||
|
instance->generic.data_count_bit,
|
||||||
|
code_found_hi,
|
||||||
|
code_found_lo,
|
||||||
|
code_found_reverse_hi,
|
||||||
|
instance->generic.cnt & 0xFFFF,
|
||||||
|
code_found_reverse_lo,
|
||||||
|
instance->generic.btn,
|
||||||
|
instance->manufacture_name,
|
||||||
|
instance->generic.serial,
|
||||||
|
instance->generic.cnt >> 16);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
- Connect your Flipper to your computer.
|
||||||
|
- Close any programs that may be using your Flipper (putty, lab.flipper.net, qFlipper, etc.)
|
||||||
|
|
||||||
|
Method 1: (easiest)
|
||||||
|
- Load [flipc.org](https://flipc.org) using a web browser such as Chrome or Edge; which will show an "Install" button.
|
||||||
|
- Select the channel and firmware that you are running on your Flipper Zero.
|
||||||
|
- Click the Install button.
|
||||||
|
- The application will appear under "Apps/subghz/rolling_flaws".
|
||||||
|
|
||||||
|
Method 2: (command-line + allows for "SN00/cfw*" and "SN bits/cfw*" features + allows for replay feature)
|
||||||
|
- Clone the firmware repository (make sure you use ``git clone --recursive``).
|
||||||
|
- Copy the ``rolling_flaws`` folder into your firmware's ``applications_user`` folder.
|
||||||
|
- Build the firmware using ``fbt updater_package``.
|
||||||
|
|
||||||
|
Method 3: (VS Code + allows for "SN00/cfw*" and "SN bits/cfw*" features + allows for replay feature)
|
||||||
|
- Install VS Code
|
||||||
|
- Clone the firmware repository (make sure you use ``git clone --recursive``).
|
||||||
|
- Make sure you have run ``fbt vscode_dist`` at least once, so VSCode works properly.
|
||||||
|
- Copy the ``rolling_flaws`` folder into your firmware's ``applications_user`` folder.
|
||||||
|
- Ctrl+Shift+B
|
||||||
|
- Select "[Debug] Launch App on Flipper"
|
||||||
|
|
||||||
|
Method 4: (command-line)
|
||||||
|
- Install [ufbt](https://github.com/flipperdevices/flipperzero-ufbt)
|
||||||
|
- Switch into the ``rolling_flaws`` folder.
|
||||||
|
- Install and launch the app using ``ufbt launch``.
|
||||||
|
|
||||||
|
## Menu Options
|
||||||
|
### Config
|
||||||
|
This is where you can configure the settings. The settings are reset whenever the application restarts.
|
||||||
|
|
||||||
|
### Reset count to 0
|
||||||
|
This will reset the count to 0. This is useful when you want to start over or want to test some rollback scenarios.
|
||||||
|
|
||||||
|
### Transmit Signal
|
||||||
|
This will transmit the signal. This is useful to capture the next signal.
|
||||||
|
|
||||||
|
### Receive Signals
|
||||||
|
This will receive signals. This is the primary purpose of the application.
|
||||||
|
|
||||||
|
### Sync Remote
|
||||||
|
This will sync the configuration using a remote signal. This is useful when you want to pair the Flipper Zero to your remote.
|
||||||
|
|
||||||
|
### About
|
||||||
|
This will show information about the application.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
### Frequency
|
||||||
|
Frequency is the frequency that the receiver and transmitter will use. This should typically be set to 433.92 unless prohibited in your region.
|
||||||
|
|
||||||
|
### Protocol
|
||||||
|
- "KL (DH)" is the KeeLoq protocol with the manufacturer key from DoorHan.
|
||||||
|
- "KL (All)" is the KeeLoq protocol with any manufacturer key. So the Flipper will try all known keys.
|
||||||
|
- "KL(Custom)" is set when doing a 'Sync Remote' operation. The manufacture used during the sync will be used for comparisons.
|
||||||
|
|
||||||
|
### Fix [SN+Btn]
|
||||||
|
This is the button and serial number to decode.
|
||||||
|
- 0x20000000 is considered a special test transmitter (right after a reset).
|
||||||
|
- 0x284EE9D5 is used by many of the sample files from this project.
|
||||||
|
- Custom is set when doing a 'Sync Remote' operation.
|
||||||
|
|
||||||
|
### Replay attack
|
||||||
|
If this is set to "yes" then it is possible to do a replay attack. NOTE: The flipper has built in code that prevents it from receiving a duplicate signal, so you will need custom firmware if you want to receive the same code twice.
|
||||||
|
|
||||||
|
### Window [next]
|
||||||
|
This is how many counts forward from the existing count are considered acceptable. For example, if the current count is 0x0001 and the window is 16, then the next count can be 0x0011.
|
||||||
|
|
||||||
|
### Window [future]
|
||||||
|
This is how many counts forward from the existing count are considered future. For example, if the current count is 0x0001 and the window is 32768, then 0x5011 would be considered a future count, but 0xEC00 would be considered a past count.
|
||||||
|
|
||||||
|
### Window [gap]
|
||||||
|
This is how close two future counts need to be from each other for them to be considered within the gap. When a second count is received and is within the gap, the next count will be advanced to the last count sent.
|
||||||
|
|
||||||
|
### SN00/cfw*
|
||||||
|
If this is set to "yes" then if the decoded data serial number bytes match 0x00, then any serial number will be considered a match. If this is set to "no" then the serial number must match exactly. For this feature to validate the serial number, you will need custom firmware.
|
||||||
|
|
||||||
|
### SN bits/cfw*
|
||||||
|
By default the firmware only checks 8 bits of the serial number. If this is set to "10 (dec)" and you have custom firmware, then 10 bits from the decoded data will need to match the serial number.
|
||||||
|
|
||||||
|
### Count 0 opens
|
||||||
|
This will cause the receiver to open when it receives a count of 0. This is a very bad idea, but I have multiple devices that implement this strategy. They also implement "KL (All)", which means an Unknown MF with matching FIX can open the gate. This is a very bad idea, but it is a real world example.
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
This tutorial assumes you have two Flipper Zeros. The first Flipper Zero will run this application and the second Flipper Zero will be used to send Sub-GHz signals.
|
||||||
|
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
Flipper #1:
|
||||||
|
- [Install](#installation) the "Rolling Flaws" application.
|
||||||
|
- NOTE: If you want to use the "SN00/cfw*" or "SN bits/cfw*" features, or enable receiving the same code twice, you will need to make custom changes to your firmware so that the encrypted data can be accessed. See the [Helpful hints](#helpful-hints) section above.
|
||||||
|
|
||||||
|
Flipper #2:
|
||||||
|
- By default, this project uses 433.92 MHz. If 433.92 MHz is not supported in your country, you can use 315000000 or 390000000 instead. In the .sub files replace "Frequency: 433920000" with the frequency you want to use.
|
||||||
|
- Copy all of the .SUB files from this project onto your second Flipper Zero (in the ``SD Card\subghz`` folder).
|
||||||
|
- NOTE: Official firmware will display "Error in protocol parameters description" when trying to send "Unknown" or "KGB/Subaru" signals. For this Flipper Zero, it may be helpful to run an unofficial firmware that allows sending these signals. Running unofficial firmware may not be legal in your region & may prevent you from getting support in the Official Flipper Zero forum. I've also included Bin_RAW signals for these two MF, if you would like to stay on official firmware.
|
||||||
|
|
||||||
|
|
||||||
|
### Matching Signals
|
||||||
|
You need to make sure your Flipper Zero is using the same **frequency** as the remote. You can use the "Frequency Analyzer" option in the "Sub-GHz" application to determine the frequency of the remote. In some cases, you can also go to https://fccid.io and enter the FCC ID number and it will tell you the frequency being used. In some cases you may need to add custom frequencies to the firmware. For this tutorial "433.92MHz" is the frequency we will be using (but you can change it to "315MHz" or "390MHz", as long as you also edit the .SUB files to have the matching frequency).
|
||||||
|
|
||||||
|
You need to make sure your Flipper Zero is using the same **modulation** as the remote. In some cases, you can go to https://fccid.io and enter the FCC ID number and it will tell you the modulation being used. Otherwise, you can try each of the modulations until you find the one that works. For this tutorial "AM650" is the modulation we will be using.
|
||||||
|
|
||||||
|
### Scenario 1: clone RAW signal, replay attack on
|
||||||
|
<img src="./docs/replay-attack-diagram.png" width="50%" />
|
||||||
|
|
||||||
|
The first attack we will try is called a "Replay attack". This is a very common attack to do with the Flipper Zero. For static codes (codes that don't change every time they are sent) this approach works really well. For dynamic codes, where the code changes each time it is sent, this attack will only work if the device has a replay attack flaw.
|
||||||
|
|
||||||
|
WARNING: With some receivers, when receiver detects a Replay attack (the count didn't increment) it can stop responding to the remote. It may be necessary to take the receiver to an authorized dealer to get it reset. This is rare, but it is something to be aware of, since it could be an expensive mistake.
|
||||||
|
|
||||||
|
Flipper #1: **Enable Replay attack**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set "Replay attack" to "Yes".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Freq Analyzer**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Frequency Analyzer" option.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show the frequency (if not: try again).
|
||||||
|
|
||||||
|
Flipper #2: **RX signal, determine frequency**
|
||||||
|
- Notice the frequency is the same as the one you set in the "Rolling Flaws" application.
|
||||||
|
- We use this technique to determine the frequency of the remote.
|
||||||
|
|
||||||
|
Flipper #2: **Read RAW record**
|
||||||
|
- Press BACK button to return to Sub-GHz menu.
|
||||||
|
- Select "Read RAW" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Set "RSSI Threshold" to "-75".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
- Press OK button to start recording.
|
||||||
|
|
||||||
|
Flipper #1: **Reset count, TX signal once**
|
||||||
|
- Select "Reset count to 0". This resets the rolling code.
|
||||||
|
- Select "Transmit Signal" option once.
|
||||||
|
- Flipper #1 will increment the count (to 1).
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Stop recording**
|
||||||
|
- Press OK button to stop recording.
|
||||||
|
- You should see options for "Erase" (LEFT), "Send" (OK), and "Save" (RIGHT).
|
||||||
|
|
||||||
|
Flipper #1: **Receive signal**
|
||||||
|
- Select "Receive Signals" option.
|
||||||
|
- Flipper #1 has "Count: 0001".
|
||||||
|
- Flipper #1 has "Fix: 284EE9D5" (which means button=2 and serial number=0x84EE9D5).
|
||||||
|
- Flipper #1 has "CLOSED" message.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to replay the signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has "Count: 0001".
|
||||||
|
- Flipper #1 (bottom right) has reason as "REPLAY".
|
||||||
|
|
||||||
|
Congratulations! You have successfully cloned and replayed a signal.
|
||||||
|
|
||||||
|
### Scenario 2: clone RAW signal, replay attack off
|
||||||
|
<img src="./docs/replay-attack-failed-diagram.png" width="50%" />
|
||||||
|
|
||||||
|
In the previous attack we successfully performed a "Replay attack". This is because our dynamic codes receiver had a replay attack flaw. Let's try the above steps again, but without the flaw.
|
||||||
|
|
||||||
|
WARNING: With some receivers, when receiver detects a Replay attack (the count didn't increment) it can stop responding to the remote. It may be necessary to take the receiver to an authorized dealer to get it reset. This is rare, but it is something to be aware of, since it could be an expensive mistake.
|
||||||
|
|
||||||
|
Flipper #1: **Disable Replay attack**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Confirm "Replay attack" is set to "No".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Freq Analyzer**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Frequency Analyzer" option.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show the frequency (if not: try again).
|
||||||
|
|
||||||
|
Flipper #2: **RX signal, determine frequency**
|
||||||
|
- Notice the frequency is the same as the one you set in the "Rolling Flaws" application.
|
||||||
|
- We use this technique to determine the frequency of the remote.
|
||||||
|
|
||||||
|
Flipper #2: **Read RAW record**
|
||||||
|
- Press BACK button to return to Sub-GHz menu.
|
||||||
|
- Select "Read RAW" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Set "RSSI Threshold" to "-75".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
- Press OK button to start recording.
|
||||||
|
|
||||||
|
Flipper #1: **Reset count, TX signal once**
|
||||||
|
- Select "Reset count to 0". This resets the rolling code.
|
||||||
|
- Select "Transmit Signal" option once.
|
||||||
|
- Flipper #1 will increment the count (to 1).
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Stop recording**
|
||||||
|
- Press OK button to stop recording.
|
||||||
|
- You should see options for "Erase" (LEFT), "Send" (OK), and "Save" (RIGHT).
|
||||||
|
|
||||||
|
Flipper #1: **Receive signal**
|
||||||
|
- Select "Receive Signals" option.
|
||||||
|
- Flipper #1 has "Count: 0001".
|
||||||
|
- Flipper #1 has "Fix: 284EE9D5" (which means button=2 and serial number=0x84EE9D5).
|
||||||
|
- Flipper #1 has "CLOSED" message.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to replay the signal.
|
||||||
|
|
||||||
|
Sadly, this time the last step will be...
|
||||||
|
Flipper #1: **Closed**
|
||||||
|
- Flipper #1 has "CLOSED" message.
|
||||||
|
- Flipper #1 has "Count: 0001".
|
||||||
|
- Flipper #1 (bottom right) has reason as "PAST".
|
||||||
|
|
||||||
|
### Scenario 3: pair remote, send next code
|
||||||
|
<img src="./docs/pair-fz-remote.png" width="50%" />
|
||||||
|
Sometimes you aren't trying to attack a device, you just want to use your Flipper Zero as a Universal Remote. For this you will need to know the protocol that the remote is using. You can use the "Sub-GHz -> Read" application to determine the protocol. In this example, we will use a "DoorHan" remote. Since you will pair the Flipper Zero as a new remote to your receiver, it should not impact the existing remotes and you don't have to worry about getting things out of sync.
|
||||||
|
|
||||||
|
NOTE: Some receivers have a limited number of remotes that can be paired, so you may want to check the manual to see if this is a concern for you.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Read**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Read" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Determine protocol**
|
||||||
|
- Notice the protocol is "Keeloq DoorHan". We can click on this entry to see even more information. This is how we determine what protocol the remote is using. We can use this information to determine what protocol to use when we add the signal to our Flipper Zero.
|
||||||
|
- Press BACK button to return to Sub-GHz menu.
|
||||||
|
|
||||||
|
Flipper #2: **Add Manually**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Add Manually" option.
|
||||||
|
- Select "DoorHan_433" option (or 315 if you are using 315MHz).
|
||||||
|
- Enter a name for your signal (We will use "Dh433_man") and choose "Save".
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
(Same Flipper Zero as previous steps)
|
||||||
|
- Select the file you just created (in the future, you can access this list under "Saved" option).
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Sync Remote**
|
||||||
|
- Select "Sync Remote" option.
|
||||||
|
- Flipper #1 has "WAITING FOR SIGNAL"
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the signal.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Flipper #1 has "Fix" matching the remote.
|
||||||
|
- Notice the current "Count" from the remote.
|
||||||
|
- Flipper #1 (bottom right) has reason as "SYNCED"
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "NEXT".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by pairing the receiver to your Flipper and sending the next code in the sequence. You can continue to press OK on Flipper #2 and the gate will continue to open and the "Count" will continue to increase!
|
||||||
|
|
||||||
|
### Scenario 4: clone remote, send next code
|
||||||
|
<img src="./docs/clone-fz-remote.png" width="50%" />
|
||||||
|
|
||||||
|
In this example, we will clone an existing remote. This is a **"bad idea"** as you are likely to get the receiver out of sync with the original remote. There is a high probability that the original remote will no longer work & using the original remote could cause the Flipper Zero remote to no longer work. This is why it is better to use the "Pair Remote" option instead of the "Clone Remote" option.
|
||||||
|
|
||||||
|
This scenario WILL most likely cause problems for you. **You should only do this if you are pen-testing a device and you are willing to take the risk of getting the receiver out of sync.**
|
||||||
|
|
||||||
|
WARNING: Your original remote will be considered as performing a "Replay attack" since it's codes will be in the past. With some receivers, when receiver detects a Replay attack (the count didn't increment on your original remote) it can stop responding to the remote. It may be necessary to take the receiver to an authorized dealer to get it reset. This is rare, but it is something to be aware of, since it could be an expensive mistake. More commonly, you will have to pair the originial remote one or more times (which may also require an authorized dealer).
|
||||||
|
|
||||||
|
**The "Rolling Flaws" application has no consequences for getting the receiver out of sync, it was made to help you learn. Let me know if you think the application should punish you for getting the receiver out of sync -- for example, we could store a list of banned FIX values & no longer allow them to work until you enter a special code.**
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Read**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Read" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Determine protocol**
|
||||||
|
- Notice the protocol is "Keeloq DoorHan".
|
||||||
|
- Click on the entry to see more information.
|
||||||
|
- Write down the KEY value (16-character code, put spaces every two characters).
|
||||||
|
- For example "Key:AD045814AB977214" should be written as "AD 04 58 14 AB 97 72 14".
|
||||||
|
|
||||||
|
Flipper #2: **Create a SUB file & emulate it**
|
||||||
|
- If your firmware has a "Send" button, skip ahead to the next step.
|
||||||
|
- **WARNING:** There is a reason why your firmware does not have a "Send" button. This WILL most likely cause problems for you. You should only do this if you are pen-testing a device and you are willing to take the risk of getting the receiver out of sync.
|
||||||
|
- If your firmware does not have a "Send" button, you will need to create a .SUB file.
|
||||||
|
- Copy the "k-unknown-sn84EE9D5-hop6A2C4803.sub" file to a new file "dh433_clone.sub".
|
||||||
|
- Edit the "dh433_clone.sub" file and change the "Key" value to match the key you wrote down.
|
||||||
|
- Copy the "dh433_clone.sub" file to your Flipper Zero (in the ``SD Card\subghz`` folder).
|
||||||
|
- Press the BACK button as needed to return to the Sub-GHz menu.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the "dh433_clone.sub" file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
|
||||||
|
Flipper #2: **Add Manually**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Add Manually" option.
|
||||||
|
- Select "DoorHan_433" option (or 315 if you are using 315MHz).
|
||||||
|
- Enter a name for your signal (We will use "Dh433_man") and choose "Save".
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
(Same Flipper Zero as previous steps)
|
||||||
|
- Select the file you just created (in the future, you can access this list under "Saved" option).
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Sync Remote**
|
||||||
|
- Select "Sync Remote" option.
|
||||||
|
- Flipper #1 has "WAITING FOR SIGNAL"
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the signal.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Flipper #1 has "Fix" matching the remote.
|
||||||
|
- Notice the current "Count" from the remote.
|
||||||
|
- Flipper #1 (bottom right) has reason as "SYNCED"
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "NEXT".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by pairing the receiver to your Flipper and sending the next code in the sequence. You can continue to press OK on Flipper #2 and the gate will continue to open and the "Count" will continue to increase! This cloning worked because the protocol was known by the Flipper and it contained the manufacturer keys. If the protocol was unknown, the Flipper would not have been able to clone the remote.
|
||||||
|
|
||||||
|
### Scenario 5: skip ahead, within window-next
|
||||||
|
<img src="./docs/window-next-attack.png" width="50%" />
|
||||||
|
|
||||||
|
In this example, we somehow have a file with a matching FIX to our remote. This file also happens to have a count that is only a little bit larger than the current count. This is a very unlikely scenario, but it is possible. This is why it is important to use a receiver that has a small "window-next" value.
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Notice the "Window [next]" setting is set to 16.
|
||||||
|
- This means the next code must be within 16 codes of the current one.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the file "k-dh-sn84EE9D5-cnt000B"
|
||||||
|
- This is KeeLoq with MF=DoorHan
|
||||||
|
- The Fix matches our remote.
|
||||||
|
- The current count should be 000B.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" from the remote, is "0000".
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
- This should be a count of "000C".
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count" ("000C").
|
||||||
|
- Flipper #1 (bottom right) has reason as "NEXT".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by skipping ahead to another code that is in the expected range. We jumped ahead and now the count is "000C". You can continue to press OK on Flipper #2 and the gate will continue to open and the "Count" will continue to increase!
|
||||||
|
|
||||||
|
### Scenario 6: future attack
|
||||||
|
<img src="./docs/window-future-attack.png" width="50%" />
|
||||||
|
|
||||||
|
In this example, we somehow have a file with a matching FIX to our remote. This file also happens to have a count that is quite a ways in the future (but less than the receiver's window-future value). For this attack to work, we need to also have a signal containing a count right after it (or a small gap). When we send those signals in sequence and if the count is less than the receivers window-future, when the receiver detects the second signal it will resyncronize the current count to the second signal. In some cases it will open the door (and some will require a third signal to be sent, within the window-next range). Most only require two signals to be sent, because to reduce the number of times the user has to press the button.
|
||||||
|
|
||||||
|
If your receiver becomes out of sync with your remote (because you were pressing the remote too many times, so your count is too far in the future) then pressing the button 2-3 times may resync things; depending on the receiver firmware. If your reciever becomes out of sync with your remote (because a cloned remote sent a future signal) then you would want to press the button many times on the remote (out of range of the receiver, so as not to do a replay/past attack) until the remote was in the window-next or window-future range.
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Notice the "Window [future]" setting is set to 32768 (0x8000 in hex)
|
||||||
|
- This means the future code must be within 32768 codes of the current one.
|
||||||
|
- Notice the "Window [gap]" setting is set to 2.
|
||||||
|
- This means after a future code, the next code must be within 2 codes of the future one.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the file "k-dh-sn84EE9D5-cnt3E90"
|
||||||
|
- This is KeeLoq with MF=DoorHan
|
||||||
|
- The Fix matches our remote.
|
||||||
|
- The current count should be 3E90.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" from the remote, is "0000".
|
||||||
|
|
||||||
|
Flipper #2: **Send future signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
- This should be a count of "3E91".
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "Future" set to "3E91".
|
||||||
|
- Flipper #1 has "Count" still set to "0000".
|
||||||
|
- Flipper #1 has "CLOSED" message.
|
||||||
|
- Flipper #1 (bottom right) has reason as "FUTURE".
|
||||||
|
|
||||||
|
Flipper #2: **Send next future signal**
|
||||||
|
- Press OK button to send the NEXT signal (gap is 1 from previous).
|
||||||
|
- This should be a count of "3E92".
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count" ("3E92").
|
||||||
|
- Flipper #1 (bottom right) has reason as "GAP".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by skipping ahead to future code sequence. You can continue to press OK on Flipper #2 and the gate will continue to open and the "Count" will continue to increase! At this point, your old remote is way in the past and is no longer useful.
|
||||||
|
|
||||||
|
### Scenario 7: rollback attack
|
||||||
|
<img src="./docs/rollback-attack.png" width="50%" />
|
||||||
|
|
||||||
|
This is very similar to a future attack, but instead of using codes from the future, we record and use codes from the past. Typically this attack will only work if the Window [future] is set to "all". What is most likely happening is the previous codes are considered as part of the far future, and when you play back two sequencial codes, it resyncs the remote to the second code. This is a very rare attack, but it is possible on some receivers.
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency, future all**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set the "Window [future]" setting to "All".
|
||||||
|
- Effectively we are saying past codes are considered as part of the future.
|
||||||
|
- Notice the "Window [gap]" setting is set to 2.
|
||||||
|
- This means after a future code, the next code must be within 2 codes of the future one.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Read RAW record**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Read RAW" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Set "RSSI Threshold" to "-75".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
- Press OK button to start recording.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Stop & save recording**
|
||||||
|
- Press OK button to stop recording.
|
||||||
|
- You should see options for "Erase" (LEFT), "Send" (OK), and "Save" (RIGHT).
|
||||||
|
- Select "Save" option.
|
||||||
|
- Give signal a name (we will use "signal-1").
|
||||||
|
|
||||||
|
Flipper #2: **Read RAW record**
|
||||||
|
(Same Flipper Zero as previous step)
|
||||||
|
- Select "New" option (press LEFT button).
|
||||||
|
- Press OK button to start recording.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Stop & save recording**
|
||||||
|
- Press OK button to stop recording.
|
||||||
|
- You should see options for "Erase" (LEFT), "Send" (OK), and "Save" (RIGHT).
|
||||||
|
- Select "Save" option.
|
||||||
|
- Give signal a name (we will use "signal-2").
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- NOTE: This is an optional step, we are doing this just to increase the count further & show normal usage.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
- Press BACK button, until you are at Sub-GHz menu.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the file "signal-1"
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" from the remote.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
- Notice: Flipper #1 should show "FUTURE" with some count in the past.
|
||||||
|
|
||||||
|
Flipper #2: **Emulate and send next signal**
|
||||||
|
- Press BACK button, until you are at Sub-GHz menu.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the file "signal-2"
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "GAP".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by replaying signals from back in time (that were considered future codes). This attack worked using RAW signals, where we don't need to ability to decode the data, we just needed to record two open commands to play back later!
|
||||||
|
|
||||||
|
### Scenario 8: rolljam attack
|
||||||
|
<img src="./docs/rolljam-attack.png" width="50%" />
|
||||||
|
|
||||||
|
The concept is when the first signal is sent from the remote, you somehow record the signal (narrow bandwidth) while preventing the receiver from getting the signal (perhaps interference near the receiver). The user of the remote then sends the second signal, which you again somehow record the signal (narrow bandwidth) while preventing the receiver from getting the signal. You then send the first signal without interference and the device will open, so user doesn't realize anything strange. You still have a second signal that you can use to open the device. Depending on other flaws, you may need to use the second signal before the next open signal (or before any signal, including "close/lock" signals). Samy Kamkar released videos on this attack years ago.
|
||||||
|
|
||||||
|
https://www.fcc.gov/general/jammer-enforcement (illegal to jam signals in the US) so you should not practice this technique in the US.
|
||||||
|
|
||||||
|
### Scenario 9: KGB/Subaru MF attack
|
||||||
|
<img src="./docs/kgb-attack.png" width="50%" />
|
||||||
|
|
||||||
|
The concept here is that perhaps the receiver knows how to decode many manufacturers, instead of just one. If we send a FIX value that matches the expected value, but encode the count using a different manufacturer key, then perhaps it will open (you will typically combine this attack with a future attack, because you don't know what the count should be). For universal receivers, there is a chance they forget to actually store the manufacturer associated with the remote but instead loop through all of the keys they know about. For this demo, we use KGB/Subaru as the manufacturer, but you can use any manufacturer that the receiver knows about.
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency, KeeLoq all**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set the "Protocol" setting to "KL (All)".
|
||||||
|
- Effectively we are saying any MF Flipper Zero knows about is treated the same.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Freq Analyzer**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the "k-dh-sn84EE9D5-cndEC01" file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
|
||||||
|
Flipper #1: **Sync Remote**
|
||||||
|
- Select "Sync Remote" option.
|
||||||
|
- Flipper #1 has "WAITING FOR SIGNAL"
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the signal.
|
||||||
|
- Flipper #1 should go back to the menu.
|
||||||
|
|
||||||
|
Flipper #2: **Prepare to Read signal**
|
||||||
|
(same Flipper as previous step)
|
||||||
|
- Press BACK button to return to Sub-GHz menu.
|
||||||
|
- Select "Read" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #1: **Send signal**
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **View signal details**
|
||||||
|
- Press OK button to see details of the signal.
|
||||||
|
- Notice the "Fix" value is "284EE9D5".
|
||||||
|
- Also the end of the Key is "AB977214" (Fix with bits from right to left)
|
||||||
|
- This technique works even when the receiver doesn't know the manufacturer. We now know what data "AB 97 72 14" is required to be at the end of our key. We can then encrypt a count using a different manufacturer key. This is how we can use a KGB key to open a DoorHan gate.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" from the remote, is "EC03".
|
||||||
|
|
||||||
|
Flipper #2: **Emulate and send KGB signal**
|
||||||
|
- Press BACK button, until you are at Sub-GHz menu.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the file "k-subaru-sn84E9D5-cntEC0D". On some firmware, where non-DoorHan signals cannot be sent, you may be required to use "b-subaru-sn84E9D5-cntEC0D" instead; which is a Bin-RAW file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "NEXT".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by using a different manufacturer key to encrypt the count. This attack worked because the receiver didn't save the manufacturer of the remote, so it tried all of the keys it knew about.
|
||||||
|
|
||||||
|
### Scenario 10: unknown MF attack
|
||||||
|
<img src="./docs/unknown-mf-attack.png" width="50%" />
|
||||||
|
|
||||||
|
The concept here is that when the receiver can't decode the manufacturer, it uses a count of 0. For universal receivers, there is a chance they open when the count is 0. For this demo, we use a "KeeLoq Unknown" as the manufacturer, but you can use any manufacturer that the receiver doesn't know about.
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency, KeeLoq (All)**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set the "Protocol" setting to "KL (All)".
|
||||||
|
- Effectively we are saying any MF Flipper Zero knows about is treated the same.
|
||||||
|
- This also will all the receiver to get a "KL Unknown" as the MF.
|
||||||
|
- Set the "Count 0 opens" to "Yes".
|
||||||
|
- Effectively we are saying that a count of 0 will open the door.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Emulate signal**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select the "k-unknown-sn84EE9D5-hop6A2C4803" file. On some firmware, where non-DoorHan signals cannot be sent, you may be required to use "b-unknown-sn84EE9D5-hop6A2C4803" instead; which is a Bin-RAW file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Transmit signal**
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- NOTE: We are just doing this step, so the count is not 0.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
(On the same Flipper Zero as the previous step.)
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" from the remote, is "0001".
|
||||||
|
|
||||||
|
Flipper #2: **Emulate and send Unknown signal**
|
||||||
|
- Press OK button to send the NEXT signal.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "COUNT0".
|
||||||
|
|
||||||
|
|
||||||
|
### Scenario 11: enc00 attack
|
||||||
|
<img src="./docs/enc00-attack.png" width="50%" />
|
||||||
|
|
||||||
|
The software implementation of KeeLoq used by the Flipper Zero treats a decoded serial number of "00" as a special case that matches ANY serial number. This is interesting, because once we find a HOP code for a manufacturer that decodes to "00" for the SN and a Button value we want, we can replace the FIX code with any other SN. The FIX code (Button+SN) is sent in the clear for KeeLoq.
|
||||||
|
|
||||||
|
For example, DoorHan with Key of "C0 00 0B D4 AB 97 72 14" decrypts to a count 3DC9 with a "00" SN. The last digit of the key is 4 (0100) which written backwards is "0010" so button 2. Now if someone transmits a button 2 DoorHan signal, say "AD 04 58 14 AB 97 72 14" we can replace their "AD 04 58 14" with "C0 00 0B D4", getting "C0 00 0B D4 AB 97 72 14" which will decrypt to a count of 3DC9 but using their serial number! We can then use Key "C0 02 8A 33" prefix to get a count of 3DCA with a "00" SN, which is the next code in the sequence. Likely this would resync the receiver to our remote! (NOTE: if "3DC9" was in the past, we would need to use a different KEY to jump to a different count, like count of A247 or FD75.)
|
||||||
|
|
||||||
|
At this point the original remote would no longer work, since it was in the PAST. If we jumped even further into the future, at some point it _might_ work when we press the remote button twice?
|
||||||
|
|
||||||
|
**WARNING: Some receivers have a limit as to how many times you can cycle!** The Overflow bits make it so that you can only cycle 0, 1 or 2 times. "This can be done by programming OVR0 and OVR1 to 1s at the time of production. The encoder will automatically clear OVR0 the first time that the synchronization value wraps from 0xFFFF to 0x0000 and clear OVR1 the second time the counter wraps. Once cleared, OVR0 and OVR1 cannot be set again, thereby creating a permanent record of the counter overflow. This prevents fast cycling of 64K
|
||||||
|
counter. If the decoder system is programmed to track the overflow bits, then the effective number of unique synchronization values can be extended to 196,608." -- [HCS300 datasheet](https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/21137G.pdf)
|
||||||
|
|
||||||
|
Previous warnings still apply.
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency, SN00/cfw**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set "SN00/cfw*" to "Yes" to allow for 00 to match any. (If you don't have custom FW this is the behavior regardless of the setting.)
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Read**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Read" option.
|
||||||
|
- Select "Config" option (LEFT button).
|
||||||
|
- Set "Frequency" to match Flipper #1.
|
||||||
|
- Set "Modulation" to "AM650".
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #1: **TX signal**
|
||||||
|
- Make sure the two Flipper Zeros are at least 6" apart.
|
||||||
|
- Select "Transmit Signal" option.
|
||||||
|
- Flipper #1 will send the signal.
|
||||||
|
- Flipper #1 should vibrate (but it will stay on same menu option).
|
||||||
|
- Flipper #2 should show some signal get received.
|
||||||
|
|
||||||
|
Flipper #2: **Determine key (FIX:BTN+SN)**
|
||||||
|
- Press OK to view the key. In particular we are interested in the last 8 digits.
|
||||||
|
- Press BACK button to return to Sub-GHz menu.
|
||||||
|
|
||||||
|
Flipper #2: **Load ENC00 file**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- If needed edit the "k-dh-enc00-cnt3DC9" on your PC, replacing the last 8 digits of the Key with the last 8 digits from the previous step. (The provided file Key ends with "AB 97 72 14" which matches the tutorials "sn84EE9D5" and "button2".)
|
||||||
|
- Select "k-dh-enc00-cnt3DC9" file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Flipper #1 has "Fix" matching the remote.
|
||||||
|
- Notice the current "Count" is some small number.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the 3DC9 signal with a "00SN".
|
||||||
|
|
||||||
|
Flipper #1: **Future**
|
||||||
|
- Flipper #1 has "Future" set to "3DCA".
|
||||||
|
- Flipper #1 has "Count" still set to the small number.
|
||||||
|
|
||||||
|
Flipper #2: **Load next ENC00 file**
|
||||||
|
- Press BACK to "Sub-GHz" menu.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- If needed edit the "k-dh-enc00-cnt3DCA" on your PC, replacing the last 8 digits of the Key with the last 8 digits from the previous step. (The provided file Key ends with "AB 97 72 14" which matches the tutorials "sn84EE9D5" and "button2".)
|
||||||
|
- Select "k-dh-enc00-cnt3DCA" file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the 3DCA signal with a "00SN".
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "GAP".
|
||||||
|
|
||||||
|
Congratulations! You have successfully opened a gate by using a different serial number. This attack worked because the receiver didn't check the serial number ending of the remote when the decoded serial number was "00".
|
||||||
|
|
||||||
|
### Scenario 12: test transmitter
|
||||||
|
"The HCS512 decoder will automatically add a test transmitter each time an Erase All Function is done. A test transmitter is defined as a transmitter with a serial number of zero. After an Erase All, the test transmitter will always work without learning and will not check the
|
||||||
|
synchronization counter of the transmitter. Learning of any new transmitters will erase the test transmitter." -- [HCS512 datasheet](https://www.microchip.com/content/dam/mchp/documents/MCU08/ProductDocuments/DataSheets/40151E.pdf)
|
||||||
|
|
||||||
|
Flipper #1: **Set Frequency, SN0000000**
|
||||||
|
- Exit and relaunch the "Rolling Flaws" application (resets all settings).
|
||||||
|
- Select "Config" option.
|
||||||
|
- Set "Frequency" to the frequency you want to use.
|
||||||
|
- Set "Fix [Btn+SN]" to "0x20000000" to allow for test transmitter.
|
||||||
|
- Leave all the rest of the settings default & click the BACK button.
|
||||||
|
|
||||||
|
Flipper #2: **Load sn0000000 file**
|
||||||
|
- Launch "Sub-GHz" application.
|
||||||
|
- Select "Saved" option.
|
||||||
|
- Select "k-dh-sn0000000" file.
|
||||||
|
- Select "Emulate" option.
|
||||||
|
- Flipper #2 should show "Send" (OK) option.
|
||||||
|
|
||||||
|
Flipper #1: **Receive signals**
|
||||||
|
- Select "Receive signals"
|
||||||
|
- Notice the current "Count" is some small number.
|
||||||
|
|
||||||
|
Flipper #2: **Send signal**
|
||||||
|
- Press OK button to send the test signal with a SN of all zeros.
|
||||||
|
|
||||||
|
Flipper #1: **Opened!**
|
||||||
|
- Flipper #1 has "OPENED!" message.
|
||||||
|
- Flipper #1 has new "Count".
|
||||||
|
- Flipper #1 (bottom right) has reason as "TEST".
|
||||||
|
|
11
subghz/apps/rolling-flaws/application.fam
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
App(
|
||||||
|
appid="rolling_flaws",
|
||||||
|
name="Subghz Rolling Receiver",
|
||||||
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
|
entry_point="rolling_flaws_app",
|
||||||
|
requires=["gui", "subghz"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
fap_icon="rolling_flaws.png",
|
||||||
|
fap_category="Sub-GHz",
|
||||||
|
fap_icon_assets="assets",
|
||||||
|
)
|
BIN
subghz/apps/rolling-flaws/assets/Lock_10x8.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
subghz/apps/rolling-flaws/assets/Unlock_10x8.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
subghz/apps/rolling-flaws/docs/clone-fz-remote.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
subghz/apps/rolling-flaws/docs/enc00-attack.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
subghz/apps/rolling-flaws/docs/kgb-attack.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
subghz/apps/rolling-flaws/docs/pair-fz-remote.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
subghz/apps/rolling-flaws/docs/replay-attack-diagram.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
subghz/apps/rolling-flaws/docs/replay-attack-failed-diagram.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
subghz/apps/rolling-flaws/docs/rollback-attack.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
subghz/apps/rolling-flaws/docs/rolljam-attack.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
subghz/apps/rolling-flaws/docs/unknown-mf-attack.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
subghz/apps/rolling-flaws/docs/window-future-attack.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
subghz/apps/rolling-flaws/docs/window-next-attack.png
Normal file
After Width: | Height: | Size: 69 KiB |
421
subghz/apps/rolling-flaws/rolling_flaws.c
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Wish list:
|
||||||
|
|
||||||
|
1. variable_item_set_current_value_text allows for large text, but
|
||||||
|
it trucates it to X characters. It would be nice it it scrolled.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
#include <notification/notification.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include "rolling_flaws_icons.h"
|
||||||
|
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||||
|
#include <lib/subghz/devices/devices.h>
|
||||||
|
|
||||||
|
#include "rolling_flaws_subghz_receive.h"
|
||||||
|
#include "rolling_flaws_settings.h"
|
||||||
|
#include "rolling_flaws_about.h"
|
||||||
|
#include "rolling_flaws_keeloq.h"
|
||||||
|
#include "rolling_flaws_send_keeloq.h"
|
||||||
|
|
||||||
|
#ifdef TAG
|
||||||
|
#undef TAG
|
||||||
|
#endif
|
||||||
|
#define TAG "RollingFlawsSubGHzApp"
|
||||||
|
|
||||||
|
// Comment this line if you don't want the backlight to be continuously on.
|
||||||
|
#define BACKLIGHT_ALWAYS_ON yes
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RollingFlawsSubmenuIndexConfigure,
|
||||||
|
RollingFlawsSubmenuIndexResetCountToZero,
|
||||||
|
RollingFlawsSubmenuIndexTransmit,
|
||||||
|
RollingFlawsSubmenuIndexReceive,
|
||||||
|
RollingFlawsSubmenuIndexSyncRemote,
|
||||||
|
RollingFlawsSubmenuIndexAbout,
|
||||||
|
} RollingFlawsSubmenuIndex;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RollingFlawsViewSubmenu,
|
||||||
|
RollingFlawsViewConfigure,
|
||||||
|
RollingFlawsViewReceiveSignals,
|
||||||
|
RollingFlawsViewReceiveSync,
|
||||||
|
RollingFlawsViewAbout,
|
||||||
|
} RollingFlawsView;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RollingFlawsEventIdReceivedSignal,
|
||||||
|
} RollingFlawsEventId;
|
||||||
|
|
||||||
|
static bool decode_packet(FuriString* buffer, void* ctx) {
|
||||||
|
RollingFlaws* context = ctx;
|
||||||
|
if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
|
||||||
|
if(!furi_string_start_with_str(
|
||||||
|
buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
|
||||||
|
FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
decode_keeloq(context->model, buffer, false);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(TAG, "Unknown protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sync_packet(FuriString* buffer, void* ctx) {
|
||||||
|
RollingFlaws* context = ctx;
|
||||||
|
if(furi_string_start_with_str(buffer, "KeeLoq 64bit")) {
|
||||||
|
if(!furi_string_start_with_str(
|
||||||
|
buffer, rolling_flaws_setting_protocol_base_name_get(context->model))) {
|
||||||
|
FURI_LOG_I(TAG, "KeeLoq 64bit protocol is not enabled");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
decode_keeloq(context->model, buffer, true);
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
context->view_dispatcher, RollingFlawsEventIdReceivedSignal);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(TAG, "Unknown protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for navigation events
|
||||||
|
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||||
|
* indicate that we want to exit the application.
|
||||||
|
* @param context The context
|
||||||
|
* @return next view id
|
||||||
|
*/
|
||||||
|
uint32_t rolling_flaws_navigation_exit_callback(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
return VIEW_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for navigation events
|
||||||
|
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||||
|
* indicate that we want to exit the application.
|
||||||
|
* @param context The context
|
||||||
|
* @return next view id
|
||||||
|
*/
|
||||||
|
uint32_t rolling_flaws_navigation_submenu_callback(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
return RollingFlawsViewSubmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for navigation events
|
||||||
|
* @details This function is called when user press back button. We return VIEW_NONE to
|
||||||
|
* indicate that we want to exit the application.
|
||||||
|
* @param context The context
|
||||||
|
* @return next view id
|
||||||
|
*/
|
||||||
|
uint32_t rolling_flaws_navigation_submenu_stop_receiving_callback(void* context) {
|
||||||
|
RollingFlaws* app = (RollingFlaws*)context;
|
||||||
|
stop_listening(app->subghz);
|
||||||
|
|
||||||
|
return RollingFlawsViewSubmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rolling_flaws_navigation_submenu_stop_sync_callback(void* context) {
|
||||||
|
RollingFlaws* app = (RollingFlaws*)context;
|
||||||
|
stop_listening(app->subghz);
|
||||||
|
|
||||||
|
return RollingFlawsViewSubmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rolling_flaws_view_dispatcher_custom_event_callback(void* context, uint32_t event) {
|
||||||
|
FURI_LOG_I(TAG, "Custom event received: %ld", event);
|
||||||
|
if(event == RollingFlawsEventIdReceivedSignal) {
|
||||||
|
RollingFlaws* app = (RollingFlaws*)context;
|
||||||
|
stop_listening(app->subghz);
|
||||||
|
|
||||||
|
furi_hal_vibro_on(true);
|
||||||
|
furi_delay_ms(200);
|
||||||
|
furi_hal_vibro_on(false);
|
||||||
|
furi_delay_ms(100);
|
||||||
|
|
||||||
|
furi_hal_vibro_on(true);
|
||||||
|
furi_delay_ms(100);
|
||||||
|
furi_hal_vibro_on(false);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaws_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RollingFlaws* app = (RollingFlaws*)context;
|
||||||
|
|
||||||
|
switch(index) {
|
||||||
|
case RollingFlawsSubmenuIndexConfigure:
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewConfigure);
|
||||||
|
break;
|
||||||
|
case RollingFlawsSubmenuIndexResetCountToZero:
|
||||||
|
app->model->count = 0x0;
|
||||||
|
app->model->future_count = 0xFFFFFFFF;
|
||||||
|
furi_hal_vibro_on(true);
|
||||||
|
furi_delay_ms(200);
|
||||||
|
furi_hal_vibro_on(false);
|
||||||
|
break;
|
||||||
|
case RollingFlawsSubmenuIndexTransmit:
|
||||||
|
app->model->count++;
|
||||||
|
app->model->future_count = 0xFFFFFFFF;
|
||||||
|
send_keeloq_count(
|
||||||
|
rolling_flaws_setting_fix_get(app->model),
|
||||||
|
app->model->count - 2,
|
||||||
|
rolling_flaws_setting_protocol_mf_name_get(app->model),
|
||||||
|
rolling_flaws_setting_frequency_get(app->model));
|
||||||
|
furi_hal_vibro_on(true);
|
||||||
|
furi_delay_ms(100);
|
||||||
|
furi_hal_vibro_on(false);
|
||||||
|
furi_delay_ms(100);
|
||||||
|
furi_hal_vibro_on(true);
|
||||||
|
furi_delay_ms(200);
|
||||||
|
furi_hal_vibro_on(false);
|
||||||
|
break;
|
||||||
|
case RollingFlawsSubmenuIndexReceive: {
|
||||||
|
uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
|
||||||
|
app->model->opened = false;
|
||||||
|
start_listening(app->subghz, frequency, decode_packet, app);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
|
||||||
|
} break;
|
||||||
|
case RollingFlawsSubmenuIndexSyncRemote: {
|
||||||
|
uint32_t frequency = rolling_flaws_setting_frequency_get(app->model);
|
||||||
|
start_listening(app->subghz, frequency, sync_packet, app);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
|
||||||
|
} break;
|
||||||
|
case RollingFlawsSubmenuIndexAbout:
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewAbout);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaws_receive_sync_draw_callback(Canvas* canvas, void* model) {
|
||||||
|
UNUSED(model);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 13, 30, "Syncing rolling code:");
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 13, 45, "Press remote button now.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaws_receive_signal_draw_callback(Canvas* canvas, void* model) {
|
||||||
|
RollingFlawsModel* my_model = ((RollingFlawsRefModel*)model)->model;
|
||||||
|
|
||||||
|
FuriString* str = furi_string_alloc(32);
|
||||||
|
|
||||||
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 13, 8, "Rolling code receiver");
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
furi_string_printf(str, "Count: %04X", (uint16_t)my_model->count);
|
||||||
|
canvas_draw_str(canvas, 2, 34, furi_string_get_cstr(str));
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
if(my_model->future_count > 0xFFFF) {
|
||||||
|
canvas_draw_str(canvas, 2, 44, "Future: none");
|
||||||
|
} else {
|
||||||
|
furi_string_printf(str, "Future: %04X", (uint16_t)my_model->future_count);
|
||||||
|
canvas_draw_str(canvas, 2, 44, furi_string_get_cstr(str));
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 3, 20, rolling_flaws_setting_protocol_display_name_get(my_model));
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
furi_string_printf(str, "Fix: %08lX", rolling_flaws_setting_fix_get(my_model));
|
||||||
|
canvas_draw_str(canvas, 2, 54, furi_string_get_cstr(str));
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
furi_string_printf(str, "RX: %s", furi_string_get_cstr(my_model->key));
|
||||||
|
canvas_draw_str(canvas, 2, 64, furi_string_get_cstr(str));
|
||||||
|
if(my_model->opened) {
|
||||||
|
canvas_draw_icon(canvas, 100, 15, &I_Unlock_10x8);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 82, 33, "OPENED!");
|
||||||
|
} else {
|
||||||
|
canvas_draw_icon(canvas, 100, 15, &I_Lock_10x8);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 85, 33, "CLOSED");
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
furi_string_printf(str, "%sMHz", rolling_flaws_setting_frequency_name_get(my_model));
|
||||||
|
canvas_draw_str(canvas, 75, 43, furi_string_get_cstr(str));
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 82, 54, furi_string_get_cstr(my_model->status));
|
||||||
|
|
||||||
|
furi_string_free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rolling_flaws_view_input_callback(InputEvent* event, void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
UNUSED(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RollingFlaws* rolling_flaws_alloc() {
|
||||||
|
RollingFlaws* app = (RollingFlaws*)malloc(sizeof(RollingFlaws));
|
||||||
|
|
||||||
|
Gui* gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
|
app->subghz = rolling_flaws_subghz_alloc();
|
||||||
|
|
||||||
|
app->model = malloc(sizeof(RollingFlawsModel));
|
||||||
|
app->model->key = furi_string_alloc();
|
||||||
|
app->model->custom_mf = furi_string_alloc();
|
||||||
|
app->model->status = furi_string_alloc();
|
||||||
|
app->model->custom_fix = 0x24321234;
|
||||||
|
app->model->count = 0x0;
|
||||||
|
app->model->future_count = 0xFFFFFFFF;
|
||||||
|
app->model->opened = false;
|
||||||
|
|
||||||
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||||
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
|
view_dispatcher_set_custom_event_callback(
|
||||||
|
app->view_dispatcher, rolling_flaws_view_dispatcher_custom_event_callback);
|
||||||
|
|
||||||
|
app->submenu = submenu_alloc();
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu,
|
||||||
|
"Config",
|
||||||
|
RollingFlawsSubmenuIndexConfigure,
|
||||||
|
rolling_flaws_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu,
|
||||||
|
"Reset count to 0",
|
||||||
|
RollingFlawsSubmenuIndexResetCountToZero,
|
||||||
|
rolling_flaws_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu,
|
||||||
|
"Transmit Signal",
|
||||||
|
RollingFlawsSubmenuIndexTransmit,
|
||||||
|
rolling_flaws_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu,
|
||||||
|
"Receive Signals",
|
||||||
|
RollingFlawsSubmenuIndexReceive,
|
||||||
|
rolling_flaws_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu,
|
||||||
|
"Sync Remote",
|
||||||
|
RollingFlawsSubmenuIndexSyncRemote,
|
||||||
|
rolling_flaws_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu, "About", RollingFlawsSubmenuIndexAbout, rolling_flaws_submenu_callback, app);
|
||||||
|
view_set_previous_callback(
|
||||||
|
submenu_get_view(app->submenu), rolling_flaws_navigation_exit_callback);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RollingFlawsViewSubmenu, submenu_get_view(app->submenu));
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RollingFlawsViewSubmenu);
|
||||||
|
|
||||||
|
rolling_flaw_populate_variable_item_list(app);
|
||||||
|
view_set_previous_callback(
|
||||||
|
variable_item_list_get_view(app->variable_item_list_config),
|
||||||
|
rolling_flaws_navigation_submenu_callback);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher,
|
||||||
|
RollingFlawsViewConfigure,
|
||||||
|
variable_item_list_get_view(app->variable_item_list_config));
|
||||||
|
|
||||||
|
app->view_receive_signals = view_alloc();
|
||||||
|
view_set_context(app->view_receive_signals, app);
|
||||||
|
view_set_draw_callback(app->view_receive_signals, rolling_flaws_receive_signal_draw_callback);
|
||||||
|
view_set_input_callback(app->view_receive_signals, rolling_flaws_view_input_callback);
|
||||||
|
view_set_previous_callback(
|
||||||
|
app->view_receive_signals, rolling_flaws_navigation_submenu_stop_receiving_callback);
|
||||||
|
view_allocate_model(
|
||||||
|
app->view_receive_signals, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
|
||||||
|
RollingFlawsRefModel* refmodel = view_get_model(app->view_receive_signals);
|
||||||
|
refmodel->model = app->model;
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RollingFlawsViewReceiveSignals, app->view_receive_signals);
|
||||||
|
|
||||||
|
app->view_receive_sync = view_alloc();
|
||||||
|
view_set_context(app->view_receive_sync, app);
|
||||||
|
view_set_draw_callback(app->view_receive_sync, rolling_flaws_receive_sync_draw_callback);
|
||||||
|
view_set_input_callback(app->view_receive_sync, rolling_flaws_view_input_callback);
|
||||||
|
view_set_previous_callback(
|
||||||
|
app->view_receive_sync, rolling_flaws_navigation_submenu_stop_sync_callback);
|
||||||
|
view_allocate_model(
|
||||||
|
app->view_receive_sync, ViewModelTypeLockFree, sizeof(RollingFlawsRefModel));
|
||||||
|
refmodel = view_get_model(app->view_receive_sync);
|
||||||
|
refmodel->model = app->model;
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RollingFlawsViewReceiveSync, app->view_receive_sync);
|
||||||
|
|
||||||
|
app->widget_about = widget_alloc();
|
||||||
|
widget_add_text_scroll_element(app->widget_about, 0, 0, 128, 64, ROLLING_FLAWS_ABOUT_TEXT);
|
||||||
|
view_set_previous_callback(
|
||||||
|
widget_get_view(app->widget_about), rolling_flaws_navigation_submenu_callback);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RollingFlawsViewAbout, widget_get_view(app->widget_about));
|
||||||
|
|
||||||
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
|
||||||
|
#ifdef BACKLIGHT_ALWAYS_ON
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaws_free(RollingFlaws* app) {
|
||||||
|
#ifdef BACKLIGHT_ALWAYS_ON
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
|
||||||
|
#endif
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewAbout);
|
||||||
|
widget_free(app->widget_about);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSignals);
|
||||||
|
view_free(app->view_receive_signals);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewReceiveSync);
|
||||||
|
view_free(app->view_receive_sync);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewConfigure);
|
||||||
|
variable_item_list_free(app->variable_item_list_config);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RollingFlawsViewSubmenu);
|
||||||
|
submenu_free(app->submenu);
|
||||||
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
|
||||||
|
rolling_flaws_subghz_free(app->subghz);
|
||||||
|
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rolling_flaws_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
|
||||||
|
RollingFlaws* app = rolling_flaws_alloc();
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
|
rolling_flaws_free(app);
|
||||||
|
return 0;
|
||||||
|
}
|
BIN
subghz/apps/rolling-flaws/rolling_flaws.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
34
subghz/apps/rolling-flaws/rolling_flaws_about.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define ROLLING_FLAWS_ABOUT_TEXT \
|
||||||
|
"Rolling code receiver\n" \
|
||||||
|
"---\n" \
|
||||||
|
"Practice rolling code attacks without risking a desync!\n" \
|
||||||
|
"This app is for educational\n" \
|
||||||
|
"purposes only.\n---\n" \
|
||||||
|
"Protocol KeeLoq (DoorHan) is\n" \
|
||||||
|
"currently supported. More\n" \
|
||||||
|
"protocols added in future.\n\n" \
|
||||||
|
":::Config supported:::\n" \
|
||||||
|
"Frequency: The\n frequency to TX/RX.\n" \
|
||||||
|
"Protocol: KL(DH) = KeeLoq\n MF=DoorHan.\n" \
|
||||||
|
" KL(All) = KeeLoq (any MF)\n" \
|
||||||
|
" KL(Custom) = KeeLoq\n" \
|
||||||
|
" (specific MF) set when doing\n a 'Sync Remote'.\n" \
|
||||||
|
"Fix [SN+Btn]: The SN+button to decode. (20000000 is\n" \
|
||||||
|
" test decode).\n" \
|
||||||
|
"Replay attack: Allow replay\n attacks.\n" \
|
||||||
|
"Window [next]: How many\n" \
|
||||||
|
" counts forward are\n acceptable?\n" \
|
||||||
|
"Window [future]: How many\n" \
|
||||||
|
" counts forward are\n considered future?\n" \
|
||||||
|
"Window [gap]: How far can\n" \
|
||||||
|
" two sequential future counts\n be apart?\n" \
|
||||||
|
"SN00 attack: Allow decoded\n 00 to wildcard SN.\n" \
|
||||||
|
"SN bits [cfw*]: Number of\n" \
|
||||||
|
" bits to compare. (custom fw\n only?)\n" \
|
||||||
|
"Count 0 opens: Count of 0 is\n treated as a match.\n" \
|
||||||
|
"=========\n" \
|
||||||
|
"author: @codeallnight\n" \
|
||||||
|
"https://discord.com/invite/NsjCvqwPAd\n" \
|
||||||
|
"https://youtube.com/@MrDerekJamison"
|
214
subghz/apps/rolling-flaws/rolling_flaws_keeloq.c
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
#include "rolling_flaws_keeloq.h"
|
||||||
|
|
||||||
|
#include "rolling_flaws_utils.h"
|
||||||
|
#include "rolling_flaws_settings.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t fix;
|
||||||
|
uint32_t hop;
|
||||||
|
uint32_t sn;
|
||||||
|
uint32_t btn;
|
||||||
|
uint32_t cnt;
|
||||||
|
uint32_t enc;
|
||||||
|
FuriString* mf;
|
||||||
|
} KeeLoqData;
|
||||||
|
|
||||||
|
KeeLoqData* keeloq_data_alloc() {
|
||||||
|
KeeLoqData* data = malloc(sizeof(KeeLoqData));
|
||||||
|
data->mf = furi_string_alloc();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keeloq_data_free(KeeLoqData* data) {
|
||||||
|
furi_string_free(data->mf);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_forward_distance(uint32_t current_count, uint32_t new_count) {
|
||||||
|
uint32_t distance = 0;
|
||||||
|
if(new_count >= current_count) {
|
||||||
|
distance = new_count - current_count;
|
||||||
|
} else {
|
||||||
|
distance = (0xFFFFFFFF - current_count) + new_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_open(RollingFlawsModel* model, KeeLoqData* data) {
|
||||||
|
bool any_mf = rolling_flaws_setting_protocol_mf_name_get(model)[0] == '*';
|
||||||
|
if(!any_mf &&
|
||||||
|
furi_string_cmp(data->mf, rolling_flaws_setting_protocol_mf_name_get(model)) != 0) {
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Wrong MF. Expected >%s< but got >%s<",
|
||||||
|
rolling_flaws_setting_protocol_mf_name_get(model),
|
||||||
|
furi_string_get_cstr(data->mf));
|
||||||
|
furi_string_set(model->status, "BAD MF");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->fix != rolling_flaws_setting_fix_get(model)) {
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Wrong fix. Expected >%08lX< but got >%08lX<",
|
||||||
|
rolling_flaws_setting_fix_get(model),
|
||||||
|
data->fix);
|
||||||
|
furi_string_set(model->status, "BAD FIX");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rolling_flaws_setting_fix_get(model) & 0xFFFFFFF) == 0) {
|
||||||
|
FURI_LOG_I(TAG, "Fix is test. Not checking data.");
|
||||||
|
furi_string_set(model->status, "TEST");
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
model->count = data->cnt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->enc != FAILED_TO_PARSE) {
|
||||||
|
FURI_LOG_I(TAG, "Encrypted payload is %08lX", data->enc);
|
||||||
|
|
||||||
|
if(!rolling_flaws_setting_sn_zero_get(model)) {
|
||||||
|
FURI_LOG_I(TAG, "SN wildcard by 00 disabled.");
|
||||||
|
if((data->fix & 0xFF) != 0) {
|
||||||
|
FURI_LOG_I(TAG, "SN does not end in 00, validating enc %08lX.", data->enc);
|
||||||
|
|
||||||
|
if((data->enc & 0xFF) == 0) {
|
||||||
|
FURI_LOG_I(TAG, "Encrypted payload SN is zero.");
|
||||||
|
furi_string_set(model->status, "SN 00");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t match_bits = rolling_flaws_setting_sn_bits_get(model);
|
||||||
|
if(match_bits != 0) {
|
||||||
|
uint32_t mask = 0xFFFFFFFF;
|
||||||
|
mask = mask >> (32 - match_bits);
|
||||||
|
uint32_t fix_sn = data->fix & mask;
|
||||||
|
uint32_t enc_sn = data->enc & mask;
|
||||||
|
if(fix_sn != enc_sn) {
|
||||||
|
FURI_LOG_I(TAG, "SN does not match. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
|
||||||
|
furi_string_set(model->status, "BAD SN");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(TAG, "SN matches. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t distance = get_forward_distance(model->count, data->cnt);
|
||||||
|
FURI_LOG_I(TAG, "Distance: %08lX", distance);
|
||||||
|
if(distance == 0 && rolling_flaws_setting_replay_get(model)) {
|
||||||
|
FURI_LOG_I(TAG, "Replay attack detected");
|
||||||
|
furi_string_set(model->status, "REPLAY");
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
model->count = data->cnt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rolling_flaws_setting_count_zero_get(model) && data->cnt == 0) {
|
||||||
|
FURI_LOG_I(TAG, "Count zero allowed.");
|
||||||
|
furi_string_set(model->status, "COUNT0");
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
// We don't reset count in this case.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(distance == 0) {
|
||||||
|
distance = 0x10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(distance <= rolling_flaws_setting_window_next_get(model)) {
|
||||||
|
FURI_LOG_I(TAG, "Within next window");
|
||||||
|
furi_string_set(model->status, "NEXT");
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
model->count = data->cnt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(distance <= rolling_flaws_setting_window_future_get(model)) {
|
||||||
|
FURI_LOG_I(TAG, "Within future window");
|
||||||
|
|
||||||
|
if(model->future_count > 0xFFFF) {
|
||||||
|
FURI_LOG_I(TAG, "Set future value to %08lX.", data->cnt);
|
||||||
|
furi_string_set(model->status, "FUTURE");
|
||||||
|
model->future_count = data->cnt;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t future_gap = get_forward_distance(model->future_count, data->cnt);
|
||||||
|
if(future_gap > 0 && future_gap <= rolling_flaws_setting_window_future_gap_get(model)) {
|
||||||
|
FURI_LOG_I(TAG, "Future gap accepted. Gap is %08lX", future_gap);
|
||||||
|
furi_string_set(model->status, "GAP");
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
model->count = data->cnt;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(future_gap == 0) {
|
||||||
|
FURI_LOG_I(TAG, "Future gap is zero. Set future value to %08lX.", data->cnt);
|
||||||
|
furi_string_set(model->status, "FUTURE");
|
||||||
|
model->future_count = data->cnt;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Future gap too large. %08lX > %08lX",
|
||||||
|
future_gap,
|
||||||
|
rolling_flaws_setting_window_future_gap_get(model));
|
||||||
|
furi_string_set(model->status, "BAD GAP");
|
||||||
|
model->future_count = data->cnt;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Signal must be from the past (non-future).");
|
||||||
|
furi_string_set(model->status, "PAST");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decode_keeloq(RollingFlawsModel* model, FuriString* buffer, bool sync) {
|
||||||
|
FURI_LOG_T(TAG, "Decoding KeeLoq 64bit");
|
||||||
|
|
||||||
|
KeeLoqData* data = keeloq_data_alloc();
|
||||||
|
__furi_string_extract_string(buffer, 0, "MF:", '\r', data->mf);
|
||||||
|
__furi_string_extract_string(buffer, 0, "Key:", '\r', model->key);
|
||||||
|
|
||||||
|
data->fix = __furi_string_extract_int(buffer, "Fix:0x", ' ', FAILED_TO_PARSE);
|
||||||
|
data->hop = __furi_string_extract_int(buffer, "Hop:0x", ' ', FAILED_TO_PARSE);
|
||||||
|
data->sn = __furi_string_extract_int(buffer, "Sn:0x", ' ', FAILED_TO_PARSE);
|
||||||
|
data->btn = __furi_string_extract_int(buffer, "Btn:", '\r', FAILED_TO_PARSE);
|
||||||
|
data->cnt = __furi_string_extract_int(buffer, "Cnt:", '\r', FAILED_TO_PARSE);
|
||||||
|
// NOTE: "Enc:" needs to be added to "keeloq.c" subghz_protocol_decoder_keeloq_get_string() method.
|
||||||
|
data->enc = __furi_string_extract_int(buffer, "Enc:", '\r', FAILED_TO_PARSE);
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"fix: %08lX hop: %08lX sn: %08lX btn: %08lX cnt: %08lX enc:%08lX key:%s mf:%s",
|
||||||
|
data->fix,
|
||||||
|
data->hop,
|
||||||
|
data->sn,
|
||||||
|
data->btn,
|
||||||
|
data->cnt,
|
||||||
|
data->enc,
|
||||||
|
furi_string_get_cstr(model->key),
|
||||||
|
furi_string_get_cstr(data->mf));
|
||||||
|
|
||||||
|
if(!sync) {
|
||||||
|
model->opened = is_open(model, data);
|
||||||
|
if(model->opened) {
|
||||||
|
model->count = data->cnt;
|
||||||
|
}
|
||||||
|
__gui_redraw();
|
||||||
|
} else {
|
||||||
|
model->custom_fix = data->fix;
|
||||||
|
model->count = data->cnt;
|
||||||
|
model->future_count = 0xFFFFFFFF;
|
||||||
|
model->opened = false;
|
||||||
|
rolling_flaws_setting_protocol_custom_mf_set(model, data->mf);
|
||||||
|
furi_string_set(model->status, "SYNCED");
|
||||||
|
}
|
||||||
|
|
||||||
|
keeloq_data_free(data);
|
||||||
|
}
|
7
subghz/apps/rolling-flaws/rolling_flaws_keeloq.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "rolling_flaws_structs.h"
|
||||||
|
|
||||||
|
#define FAILED_TO_PARSE 0x0BADC0DE
|
||||||
|
|
||||||
|
void decode_keeloq(RollingFlawsModel* model, FuriString* buffer, bool sync);
|
126
subghz/apps/rolling-flaws/rolling_flaws_send_keeloq.c
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#include "rolling_flaws_send_keeloq.h"
|
||||||
|
|
||||||
|
#include <lib/subghz/transmitter.h>
|
||||||
|
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
#include <lib/subghz/devices/devices.h>
|
||||||
|
|
||||||
|
#ifdef TAG
|
||||||
|
#undef TAG
|
||||||
|
#endif
|
||||||
|
#define TAG "RollingFlawsSendKeeloq"
|
||||||
|
|
||||||
|
static SubGhzEnvironment* load_environment() {
|
||||||
|
SubGhzEnvironment* environment = subghz_environment_alloc();
|
||||||
|
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME);
|
||||||
|
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
|
||||||
|
subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
|
||||||
|
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
|
||||||
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_keeloq(
|
||||||
|
uint32_t frequency,
|
||||||
|
uint32_t serial,
|
||||||
|
uint8_t btn,
|
||||||
|
uint16_t cnt,
|
||||||
|
const char* name_sysmem) {
|
||||||
|
if(!furi_hal_region_is_frequency_allowed(frequency)) {
|
||||||
|
// TODO: Show friendly UI message if frequency is not allowed.
|
||||||
|
FURI_LOG_E(TAG, "Frequency %lu is not allowed in this region.", frequency);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Sending signal on frequency %lu", frequency);
|
||||||
|
|
||||||
|
// Populate the CC101 device list.
|
||||||
|
subghz_devices_init();
|
||||||
|
|
||||||
|
// Get the internal radio device.
|
||||||
|
const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||||
|
|
||||||
|
// Get the Princeton SubGhzTransmitter (for decoding our file format).
|
||||||
|
SubGhzEnvironment* environment = load_environment();
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
SubGhzTransmitter* transmitter =
|
||||||
|
subghz_transmitter_alloc_init(environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
|
||||||
|
|
||||||
|
// Load the payload we want to send into flipper_format.
|
||||||
|
FlipperFormat* flipper_format = flipper_format_string_alloc();
|
||||||
|
|
||||||
|
SubGhzRadioPreset* preset = malloc(sizeof(SubGhzRadioPreset));
|
||||||
|
preset->frequency = frequency;
|
||||||
|
preset->name = furi_string_alloc();
|
||||||
|
furi_string_set(preset->name, "AM650");
|
||||||
|
preset->data = NULL;
|
||||||
|
preset->data_size = 0;
|
||||||
|
|
||||||
|
subghz_protocol_keeloq_create_data(
|
||||||
|
subghz_transmitter_get_protocol_instance(transmitter),
|
||||||
|
flipper_format,
|
||||||
|
serial,
|
||||||
|
btn,
|
||||||
|
cnt,
|
||||||
|
name_sysmem,
|
||||||
|
preset);
|
||||||
|
|
||||||
|
// Fill out the SubGhzProtocolDecoderPrinceton (which includes SubGhzBlockGeneric data) in our transmitter based on parsing flipper_format.
|
||||||
|
// initance->encoder.upload[] gets filled out with duration and level information (You can think of this as the RAW data).
|
||||||
|
SubGhzProtocolStatus status = subghz_transmitter_deserialize(transmitter, flipper_format);
|
||||||
|
furi_assert(status == SubGhzProtocolStatusOk);
|
||||||
|
|
||||||
|
// Currently unused for internal radio, but good idea to still invoke it.
|
||||||
|
subghz_devices_begin(device);
|
||||||
|
|
||||||
|
// Initializes the CC1101 SPI bus
|
||||||
|
subghz_devices_reset(device);
|
||||||
|
|
||||||
|
// Use one of the presets in subghz_device_cc1101_int_interconnect_load_preset. If the first argument is FuriHalSubGhzPresetCustom, then the second argument is
|
||||||
|
// a custom register table (Reg, value, reg, value, ...,0, 0, PATable [0..7] entries).
|
||||||
|
subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL);
|
||||||
|
|
||||||
|
// Set the frequency, RF switch path (band), calibrates the oscillator on the CC1101.
|
||||||
|
frequency = subghz_devices_set_frequency(device, frequency);
|
||||||
|
|
||||||
|
// Stop charging the battery while transmitting.
|
||||||
|
furi_hal_power_suppress_charge_enter();
|
||||||
|
|
||||||
|
// Start transmitting (keeps the DMA buffer filled with the encoder.upload[] data)
|
||||||
|
if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {
|
||||||
|
// Wait for the transmission to complete.
|
||||||
|
while(!(subghz_devices_is_async_complete_tx(device))) {
|
||||||
|
furi_delay_ms(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop transmitting, debug log (tag="FuriHalSubGhz") the duty cycle information.
|
||||||
|
subghz_devices_stop_async_tx(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up and shutdown cc1101
|
||||||
|
subghz_devices_sleep(device);
|
||||||
|
|
||||||
|
// also does a shutdown of cc1101
|
||||||
|
subghz_devices_end(device);
|
||||||
|
|
||||||
|
// remove the devices from the registry
|
||||||
|
subghz_devices_deinit();
|
||||||
|
|
||||||
|
// Allow the battery to charge again.
|
||||||
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
|
// Free resources we allocated.
|
||||||
|
flipper_format_free(flipper_format);
|
||||||
|
subghz_transmitter_free(transmitter);
|
||||||
|
subghz_environment_free(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_keeloq_count(uint32_t fix, uint32_t count, const char* name, uint32_t frequency) {
|
||||||
|
uint32_t serial = fix & 0x0FFFFFFF;
|
||||||
|
uint8_t btn = fix >> 28;
|
||||||
|
send_keeloq(frequency, serial, btn, count, name);
|
||||||
|
}
|
5
subghz/apps/rolling-flaws/rolling_flaws_send_keeloq.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
void send_keeloq_count(uint32_t fix, uint32_t count, const char* name, uint32_t frequency);
|
256
subghz/apps/rolling-flaws/rolling_flaws_settings.c
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
#include "rolling_flaws_settings.h"
|
||||||
|
|
||||||
|
void rolling_flaws_setting_change(VariableItem* item, char** names, uint8_t* new_index) {
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
variable_item_set_current_value_text(item, names[index]);
|
||||||
|
*new_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t setting_frequency_values[] = {315000000, 390000000, 433920000};
|
||||||
|
char* setting_frequency_names[] = {"315.00", "390.00", "433.92"};
|
||||||
|
void rolling_flaws_setting_frequency_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_frequency_names, &app->model->frequency_index);
|
||||||
|
}
|
||||||
|
char* rolling_flaws_setting_frequency_name_get(RollingFlawsModel* model) {
|
||||||
|
return setting_frequency_names[model->frequency_index];
|
||||||
|
}
|
||||||
|
uint32_t rolling_flaws_setting_frequency_get(RollingFlawsModel* model) {
|
||||||
|
return setting_frequency_values[model->frequency_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t setting_fix_values[] = {0x20000000, 0x201EA8D8, 0x284EE9D5, 0xCAFECAFE};
|
||||||
|
char* setting_fix_names[] = {"0x20000000", "0x201EA8D8", "0x284EE9D5", "Custom"};
|
||||||
|
void rolling_flaws_setting_fix_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_fix_names, &app->model->fix_index);
|
||||||
|
}
|
||||||
|
uint32_t rolling_flaws_setting_fix_get(RollingFlawsModel* model) {
|
||||||
|
if(model->fix_index == COUNT_OF(setting_fix_values) - 1) {
|
||||||
|
return model->custom_fix;
|
||||||
|
}
|
||||||
|
return setting_fix_values[model->fix_index];
|
||||||
|
}
|
||||||
|
char* rolling_flaws_setting_fix_display_name_get(RollingFlawsModel* model) {
|
||||||
|
return setting_fix_names[model->fix_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
char* setting_protocol_values_mf_name[] = {"DoorHan", "*", "Custom"};
|
||||||
|
char* setting_protocol_values_base_name[] = {"KeeLoq 64bit", "KeeLoq 64bit", "KeeLoq 64bit"};
|
||||||
|
char* setting_protocol_names[] = {"KL (DH)", "KL (All)", "KL (Custom)"};
|
||||||
|
void rolling_flaws_setting_protocol_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_protocol_names, &app->model->protocol_index);
|
||||||
|
}
|
||||||
|
char* rolling_flaws_setting_protocol_base_name_get(RollingFlawsModel* model) {
|
||||||
|
return setting_protocol_values_base_name[model->protocol_index];
|
||||||
|
}
|
||||||
|
char* rolling_flaws_setting_protocol_display_name_get(RollingFlawsModel* model) {
|
||||||
|
return setting_protocol_names[model->protocol_index];
|
||||||
|
}
|
||||||
|
const char* rolling_flaws_setting_protocol_mf_name_get(RollingFlawsModel* model) {
|
||||||
|
if(model->protocol_index == COUNT_OF(setting_protocol_values_mf_name) - 1) {
|
||||||
|
return furi_string_get_cstr(model->custom_mf);
|
||||||
|
}
|
||||||
|
return setting_protocol_values_mf_name[model->protocol_index];
|
||||||
|
}
|
||||||
|
void rolling_flaws_setting_protocol_custom_mf_set(RollingFlawsModel* model, FuriString* mf) {
|
||||||
|
model->protocol_index = COUNT_OF(setting_protocol_values_mf_name) - 1;
|
||||||
|
variable_item_set_current_value_index(model->variable_item_protocol, model->protocol_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
model->variable_item_protocol, rolling_flaws_setting_protocol_display_name_get(model));
|
||||||
|
|
||||||
|
model->fix_index = COUNT_OF(setting_fix_values) - 1;
|
||||||
|
variable_item_set_current_value_index(model->variable_item_fix, model->fix_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
model->variable_item_fix, rolling_flaws_setting_fix_display_name_get(model));
|
||||||
|
|
||||||
|
furi_string_set(model->custom_mf, furi_string_get_cstr(mf));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setting_replay_values[] = {false, true};
|
||||||
|
char* setting_replay_names[] = {"No", "Yes"};
|
||||||
|
void rolling_flaws_setting_replay_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_replay_names, &app->model->replay_index);
|
||||||
|
}
|
||||||
|
bool rolling_flaws_setting_replay_get(RollingFlawsModel* model) {
|
||||||
|
return setting_replay_values[model->replay_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief The window_next_values have precedence over past/future values.
|
||||||
|
uint32_t setting_window_next_values[] = {4, 8, 16, 256, 16384, 32768, 65536};
|
||||||
|
char* setting_window_next_names[] = {"4", "8", "16", "256", "16384", "32768", "All"};
|
||||||
|
void rolling_flaws_setting_window_next_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_window_next_names, &app->model->window_next_index);
|
||||||
|
}
|
||||||
|
uint32_t rolling_flaws_setting_window_next_get(RollingFlawsModel* model) {
|
||||||
|
return setting_window_next_values[model->window_next_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t setting_window_future_values[] = {8, 16, 256, 16384, 32768, 65536};
|
||||||
|
char* setting_window_future_names[] = {"1", "8", "16", "256", "16384", "32768", "All"};
|
||||||
|
void rolling_flaws_setting_window_future_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(
|
||||||
|
item, setting_window_future_names, &app->model->window_future_index);
|
||||||
|
}
|
||||||
|
uint32_t rolling_flaws_setting_window_future_get(RollingFlawsModel* model) {
|
||||||
|
return setting_window_future_values[model->window_future_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t setting_window_future_gap_values[] = {1, 2, 3, 4};
|
||||||
|
char* setting_window_future_gap_names[] = {"1", "2", "3", "4"};
|
||||||
|
void rolling_flaws_setting_window_future_gap_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(
|
||||||
|
item, setting_window_future_gap_names, &app->model->window_future_gap_index);
|
||||||
|
}
|
||||||
|
uint32_t rolling_flaws_setting_window_future_gap_get(RollingFlawsModel* model) {
|
||||||
|
return setting_window_future_gap_values[model->window_future_gap_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setting_sn_zero_values[] = {false, true};
|
||||||
|
char* setting_sn_zero_names[] = {"No", "Yes"};
|
||||||
|
void rolling_flaws_setting_sn_zero_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_sn_zero_names, &app->model->sn_zero_index);
|
||||||
|
}
|
||||||
|
bool rolling_flaws_setting_sn_zero_get(RollingFlawsModel* model) {
|
||||||
|
return setting_sn_zero_values[model->sn_zero_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCS300 uses 10 bits in discriminator, HCS200 uses 8 bits
|
||||||
|
uint8_t setting_sn_bits_values[] = {8, 10};
|
||||||
|
char* setting_sn_bits_names[] = {"8", "10 (dec)"};
|
||||||
|
void rolling_flaws_setting_sn_bits_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_sn_bits_names, &app->model->sn_bits_index);
|
||||||
|
}
|
||||||
|
uint8_t rolling_flaws_setting_sn_bits_get(RollingFlawsModel* model) {
|
||||||
|
return setting_sn_bits_values[model->sn_bits_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setting_count_zero_values[] = {false, true};
|
||||||
|
char* setting_count_zero_names[] = {"No", "Yes"};
|
||||||
|
void rolling_flaws_setting_count_zero_change(VariableItem* item) {
|
||||||
|
RollingFlaws* app = variable_item_get_context(item);
|
||||||
|
rolling_flaws_setting_change(item, setting_sn_zero_names, &app->model->count_zero_index);
|
||||||
|
}
|
||||||
|
bool rolling_flaws_setting_count_zero_get(RollingFlawsModel* model) {
|
||||||
|
return setting_count_zero_values[model->count_zero_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaw_populate_variable_item_list(RollingFlaws* app) {
|
||||||
|
app->variable_item_list_config = variable_item_list_alloc();
|
||||||
|
variable_item_list_reset(app->variable_item_list_config);
|
||||||
|
|
||||||
|
VariableItem* item;
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Frequency",
|
||||||
|
COUNT_OF(setting_frequency_names),
|
||||||
|
rolling_flaws_setting_frequency_change,
|
||||||
|
app);
|
||||||
|
app->model->frequency_index = 2; // 433.92
|
||||||
|
variable_item_set_current_value_index(item, app->model->frequency_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, setting_frequency_names[app->model->frequency_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Protocol",
|
||||||
|
COUNT_OF(setting_protocol_names),
|
||||||
|
rolling_flaws_setting_protocol_change,
|
||||||
|
app);
|
||||||
|
app->model->protocol_index = 0; // KeeLoq (DoorHan)
|
||||||
|
variable_item_set_current_value_index(item, app->model->protocol_index);
|
||||||
|
variable_item_set_current_value_text(item, setting_protocol_names[app->model->protocol_index]);
|
||||||
|
app->model->variable_item_protocol = item;
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Fix [Btn+SN]",
|
||||||
|
COUNT_OF(setting_fix_names),
|
||||||
|
rolling_flaws_setting_fix_change,
|
||||||
|
app);
|
||||||
|
app->model->fix_index = 2; // 0x284EE9D5
|
||||||
|
variable_item_set_current_value_index(item, app->model->fix_index);
|
||||||
|
variable_item_set_current_value_text(item, setting_fix_names[app->model->fix_index]);
|
||||||
|
app->model->variable_item_fix = item;
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Replay attack",
|
||||||
|
COUNT_OF(setting_replay_names),
|
||||||
|
rolling_flaws_setting_replay_change,
|
||||||
|
app);
|
||||||
|
app->model->replay_index = 0; // Disabled
|
||||||
|
variable_item_set_current_value_index(item, app->model->replay_index);
|
||||||
|
variable_item_set_current_value_text(item, setting_replay_names[app->model->replay_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Window [next]",
|
||||||
|
COUNT_OF(setting_window_next_names),
|
||||||
|
rolling_flaws_setting_window_next_change,
|
||||||
|
app);
|
||||||
|
app->model->window_next_index = 2; // 16 codes.
|
||||||
|
variable_item_set_current_value_index(item, app->model->window_next_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, setting_window_next_names[app->model->window_next_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Window [future]",
|
||||||
|
COUNT_OF(setting_window_future_names),
|
||||||
|
rolling_flaws_setting_window_future_change,
|
||||||
|
app);
|
||||||
|
app->model->window_future_index = 5; // 32768 codes.
|
||||||
|
variable_item_set_current_value_index(item, app->model->window_future_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, setting_window_future_names[app->model->window_future_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Window [gap]",
|
||||||
|
COUNT_OF(setting_window_future_gap_names),
|
||||||
|
rolling_flaws_setting_window_future_gap_change,
|
||||||
|
app);
|
||||||
|
app->model->window_future_gap_index = 1; // 2 codes.
|
||||||
|
variable_item_set_current_value_index(item, app->model->window_future_gap_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, setting_window_future_gap_names[app->model->window_future_gap_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"SN00/cfw*",
|
||||||
|
COUNT_OF(setting_sn_zero_names),
|
||||||
|
rolling_flaws_setting_sn_zero_change,
|
||||||
|
app);
|
||||||
|
app->model->sn_zero_index = 0; // Disabled
|
||||||
|
variable_item_set_current_value_index(item, app->model->sn_zero_index);
|
||||||
|
variable_item_set_current_value_text(item, setting_sn_zero_names[app->model->sn_zero_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"SN bits/cfw*",
|
||||||
|
COUNT_OF(setting_sn_bits_names),
|
||||||
|
rolling_flaws_setting_sn_bits_change,
|
||||||
|
app);
|
||||||
|
app->model->sn_bits_index = 0; // 8-bits
|
||||||
|
variable_item_set_current_value_index(item, app->model->sn_bits_index);
|
||||||
|
variable_item_set_current_value_text(item, setting_sn_bits_names[app->model->sn_bits_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
app->variable_item_list_config,
|
||||||
|
"Count 0 opens",
|
||||||
|
COUNT_OF(setting_count_zero_names),
|
||||||
|
rolling_flaws_setting_count_zero_change,
|
||||||
|
app);
|
||||||
|
app->model->count_zero_index = 0; // No - count of 0 is not an open.
|
||||||
|
variable_item_set_current_value_index(item, app->model->count_zero_index);
|
||||||
|
variable_item_set_current_value_text(
|
||||||
|
item, setting_count_zero_names[app->model->count_zero_index]);
|
||||||
|
}
|
21
subghz/apps/rolling-flaws/rolling_flaws_settings.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include "rolling_flaws_structs.h"
|
||||||
|
|
||||||
|
uint32_t rolling_flaws_setting_frequency_get(RollingFlawsModel* model);
|
||||||
|
char* rolling_flaws_setting_frequency_name_get(RollingFlawsModel* model);
|
||||||
|
char* rolling_flaws_setting_protocol_base_name_get(RollingFlawsModel* model);
|
||||||
|
char* rolling_flaws_setting_protocol_display_name_get(RollingFlawsModel* model);
|
||||||
|
const char* rolling_flaws_setting_protocol_mf_name_get(RollingFlawsModel* model);
|
||||||
|
void rolling_flaws_setting_protocol_custom_mf_set(RollingFlawsModel* model, FuriString* mf);
|
||||||
|
bool rolling_flaws_setting_replay_get(RollingFlawsModel* model);
|
||||||
|
uint32_t rolling_flaws_setting_window_next_get(RollingFlawsModel* model);
|
||||||
|
uint32_t rolling_flaws_setting_window_future_get(RollingFlawsModel* model);
|
||||||
|
uint32_t rolling_flaws_setting_window_future_gap_get(RollingFlawsModel* model);
|
||||||
|
uint32_t rolling_flaws_setting_fix_get(RollingFlawsModel* model);
|
||||||
|
bool rolling_flaws_setting_sn_zero_get(RollingFlawsModel* model);
|
||||||
|
uint8_t rolling_flaws_setting_sn_bits_get(RollingFlawsModel* model);
|
||||||
|
bool rolling_flaws_setting_count_zero_get(RollingFlawsModel* model);
|
||||||
|
|
||||||
|
void rolling_flaw_populate_variable_item_list(RollingFlaws* app);
|
53
subghz/apps/rolling-flaws/rolling_flaws_structs.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/variable_item_list.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <notification/notification.h>
|
||||||
|
|
||||||
|
#include "rolling_flaws_subghz_receive.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t protocol_index;
|
||||||
|
FuriString* custom_mf;
|
||||||
|
uint8_t frequency_index;
|
||||||
|
uint8_t replay_index; // allow replay attack?
|
||||||
|
uint8_t window_next_index; // how many codes forward are acceptable?
|
||||||
|
uint8_t window_future_index; // how many codes forward are considered future?
|
||||||
|
uint8_t window_future_gap_index; // how far can two sequential future codes be?
|
||||||
|
uint8_t fix_index; // SN+button
|
||||||
|
uint32_t custom_fix; // Fix value if custom is selected.
|
||||||
|
uint8_t sn_zero_index; // allow decoded 00 to wildcard SN.
|
||||||
|
uint8_t sn_bits_index; // number of bits to compare. (custom fw only?)
|
||||||
|
uint8_t count_zero_index; // allow count of 0 to be considered an open.
|
||||||
|
|
||||||
|
uint32_t count; // 0 to 0xFFFF
|
||||||
|
uint32_t future_count; // 0 to 0xFFFF, use 0xFFFFFFFF if cleared.
|
||||||
|
bool opened;
|
||||||
|
|
||||||
|
FuriString* key;
|
||||||
|
FuriString* status;
|
||||||
|
VariableItem* variable_item_protocol;
|
||||||
|
VariableItem* variable_item_fix;
|
||||||
|
} RollingFlawsModel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
RollingFlawsModel* model;
|
||||||
|
} RollingFlawsRefModel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NotificationApp* notifications;
|
||||||
|
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
|
||||||
|
Submenu* submenu;
|
||||||
|
VariableItemList* variable_item_list_config;
|
||||||
|
View* view_receive_signals;
|
||||||
|
View* view_receive_sync;
|
||||||
|
Widget* widget_about;
|
||||||
|
|
||||||
|
RollingFlawsModel* model;
|
||||||
|
RollingFlawsSubGhz* subghz;
|
||||||
|
} RollingFlaws;
|
140
subghz/apps/rolling-flaws/rolling_flaws_subghz_receive.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "rolling_flaws_subghz_receive.h"
|
||||||
|
|
||||||
|
static SubGhzEnvironment* load_environment() {
|
||||||
|
SubGhzEnvironment* environment = subghz_environment_alloc();
|
||||||
|
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME);
|
||||||
|
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
|
||||||
|
subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
|
||||||
|
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
|
||||||
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
|
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
|
||||||
|
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
RollingFlawsSubGhz* rolling_flaws_subghz_alloc() {
|
||||||
|
RollingFlawsSubGhz* subghz = malloc(sizeof(RollingFlawsSubGhz));
|
||||||
|
subghz->status = SUBGHZ_RECEIVER_UNINITIALIZED;
|
||||||
|
subghz->environment = load_environment();
|
||||||
|
subghz->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
|
||||||
|
furi_check(subghz->stream);
|
||||||
|
subghz->overrun = false;
|
||||||
|
return subghz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rolling_flaws_subghz_free(RollingFlawsSubGhz* subghz) {
|
||||||
|
subghz_environment_free(subghz->environment);
|
||||||
|
furi_stream_buffer_free(subghz->stream);
|
||||||
|
free(subghz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rx_callback(SubGhzReceiver* receiver, SubGhzProtocolDecoderBase* decoder_base, void* cxt) {
|
||||||
|
RollingFlawsSubGhz* context = (RollingFlawsSubGhz*)cxt;
|
||||||
|
FuriString* buffer = furi_string_alloc();
|
||||||
|
subghz_protocol_decoder_base_get_string(decoder_base, buffer);
|
||||||
|
subghz_receiver_reset(receiver);
|
||||||
|
FURI_LOG_I(TAG, "PACKET:\r\n%s", furi_string_get_cstr(buffer));
|
||||||
|
if(context->callback) {
|
||||||
|
if(!context->callback(buffer, context->callback_context)) {
|
||||||
|
context->status = SUBGHZ_RECEIVER_SYNCHRONIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_string_free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rx_capture_callback(bool level, uint32_t duration, void* context) {
|
||||||
|
RollingFlawsSubGhz* instance = context;
|
||||||
|
|
||||||
|
LevelDuration level_duration = level_duration_make(level, duration);
|
||||||
|
if(instance->overrun) {
|
||||||
|
instance->overrun = false;
|
||||||
|
level_duration = level_duration_reset();
|
||||||
|
}
|
||||||
|
size_t ret =
|
||||||
|
furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0);
|
||||||
|
if(sizeof(LevelDuration) != ret) {
|
||||||
|
instance->overrun = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t listen_rx(void* ctx) {
|
||||||
|
RollingFlawsSubGhz* context = (RollingFlawsSubGhz*)ctx;
|
||||||
|
context->status = SUBGHZ_RECEIVER_LISTENING;
|
||||||
|
LevelDuration level_duration;
|
||||||
|
FURI_LOG_I(TAG, "listen_rx started...");
|
||||||
|
while(context->status == SUBGHZ_RECEIVER_LISTENING) {
|
||||||
|
int ret = furi_stream_buffer_receive(
|
||||||
|
context->stream, &level_duration, sizeof(LevelDuration), 10);
|
||||||
|
|
||||||
|
if(ret == sizeof(LevelDuration)) {
|
||||||
|
if(level_duration_is_reset(level_duration)) {
|
||||||
|
subghz_receiver_reset(context->receiver);
|
||||||
|
} else {
|
||||||
|
bool level = level_duration_get_level(level_duration);
|
||||||
|
uint32_t duration = level_duration_get_duration(level_duration);
|
||||||
|
subghz_receiver_decode(context->receiver, level, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FURI_LOG_I(TAG, "listen_rx exiting...");
|
||||||
|
context->status = SUBGHZ_RECEIVER_NOTLISTENING;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_listening(
|
||||||
|
RollingFlawsSubGhz* context,
|
||||||
|
uint32_t frequency,
|
||||||
|
SubghzPacketCallback callback,
|
||||||
|
void* callback_context) {
|
||||||
|
context->status = SUBGHZ_RECEIVER_INITIALIZING;
|
||||||
|
|
||||||
|
context->callback = callback;
|
||||||
|
context->callback_context = callback_context;
|
||||||
|
subghz_devices_init();
|
||||||
|
const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||||
|
if(!subghz_devices_is_frequency_valid(device, frequency)) {
|
||||||
|
FURI_LOG_E(TAG, "Frequency not in range. %lu\r\n", frequency);
|
||||||
|
subghz_devices_deinit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->receiver = subghz_receiver_alloc_init(context->environment);
|
||||||
|
subghz_receiver_set_filter(context->receiver, SubGhzProtocolFlag_Decodable);
|
||||||
|
subghz_receiver_set_rx_callback(context->receiver, rx_callback, context);
|
||||||
|
|
||||||
|
subghz_devices_begin(device);
|
||||||
|
subghz_devices_reset(device);
|
||||||
|
subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL);
|
||||||
|
frequency = subghz_devices_set_frequency(device, frequency);
|
||||||
|
|
||||||
|
furi_hal_power_suppress_charge_enter();
|
||||||
|
|
||||||
|
subghz_devices_start_async_rx(device, rx_capture_callback, context);
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "Listening at frequency: %lu\r\n", frequency);
|
||||||
|
|
||||||
|
context->thread = furi_thread_alloc_ex("RX", 1024, listen_rx, context);
|
||||||
|
furi_thread_start(context->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_listening(RollingFlawsSubGhz* context) {
|
||||||
|
if(context->status == SUBGHZ_RECEIVER_UNINITIALIZED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->status = SUBGHZ_RECEIVER_UNINITIALING;
|
||||||
|
FURI_LOG_D(TAG, "Stopping listening...");
|
||||||
|
furi_thread_join(context->thread);
|
||||||
|
furi_thread_free(context->thread);
|
||||||
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
|
const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||||
|
subghz_devices_stop_async_rx(device);
|
||||||
|
|
||||||
|
subghz_receiver_free(context->receiver);
|
||||||
|
subghz_devices_deinit();
|
||||||
|
context->status = SUBGHZ_RECEIVER_UNINITIALIZED;
|
||||||
|
}
|
48
subghz/apps/rolling-flaws/rolling_flaws_subghz_receive.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
|
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||||
|
#include <lib/subghz/devices/devices.h>
|
||||||
|
|
||||||
|
#include "./rolling_flaws_utils.h"
|
||||||
|
|
||||||
|
#ifdef TAG
|
||||||
|
#undef TAG
|
||||||
|
#endif
|
||||||
|
#define TAG "RollingFlawsSubGHzReceive"
|
||||||
|
|
||||||
|
typedef bool (*SubghzPacketCallback)(FuriString* buffer, void* context);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SUBGHZ_RECEIVER_INITIALIZING,
|
||||||
|
SUBGHZ_RECEIVER_LISTENING,
|
||||||
|
SUBGHZ_RECEIVER_SYNCHRONIZED,
|
||||||
|
SUBGHZ_RECEIVER_NOTLISTENING,
|
||||||
|
SUBGHZ_RECEIVER_UNINITIALING,
|
||||||
|
SUBGHZ_RECEIVER_UNINITIALIZED,
|
||||||
|
} SubghzReceiverState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SubGhzEnvironment* environment;
|
||||||
|
FuriStreamBuffer* stream;
|
||||||
|
FuriThread* thread;
|
||||||
|
SubGhzReceiver* receiver;
|
||||||
|
bool overrun;
|
||||||
|
SubghzReceiverState status;
|
||||||
|
SubghzPacketCallback callback;
|
||||||
|
void* callback_context;
|
||||||
|
} RollingFlawsSubGhz;
|
||||||
|
|
||||||
|
RollingFlawsSubGhz* rolling_flaws_subghz_alloc();
|
||||||
|
void rolling_flaws_subghz_free(RollingFlawsSubGhz* subghz);
|
||||||
|
|
||||||
|
void start_listening(
|
||||||
|
RollingFlawsSubGhz* context,
|
||||||
|
uint32_t frequency,
|
||||||
|
SubghzPacketCallback callback,
|
||||||
|
void* callback_context);
|
||||||
|
void stop_listening(RollingFlawsSubGhz* context);
|
80
subghz/apps/rolling-flaws/rolling_flaws_utils.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include "rolling_flaws_utils.h"
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
|
size_t __furi_string_extract_string(
|
||||||
|
FuriString* buffer,
|
||||||
|
size_t start_index,
|
||||||
|
char* text,
|
||||||
|
char delim,
|
||||||
|
FuriString* result) {
|
||||||
|
size_t len = strlen(text);
|
||||||
|
size_t valid_index = furi_string_size(buffer) - 1;
|
||||||
|
size_t field = furi_string_search_str(buffer, text, start_index) + len;
|
||||||
|
size_t term = -1;
|
||||||
|
if(field < valid_index) {
|
||||||
|
term = furi_string_search_char(buffer, delim, field);
|
||||||
|
if(term < valid_index) {
|
||||||
|
furi_string_reset(result);
|
||||||
|
furi_string_set_n(result, buffer, field, term - field);
|
||||||
|
FURI_LOG_I(TAG, "%s data is >>%s<<", text, furi_string_get_cstr(result));
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Failed to find terminator for >>%s<<", text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Failed to find >>%s<<", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return term;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
__furi_string_extract_int(FuriString* buffer, char* text, char delim, uint32_t default_value) {
|
||||||
|
uint32_t value = default_value;
|
||||||
|
size_t len = strlen(text);
|
||||||
|
size_t valid_index = furi_string_size(buffer) - 1;
|
||||||
|
size_t field = furi_string_search_str(buffer, text, 0) + len;
|
||||||
|
size_t term = -1;
|
||||||
|
FURI_LOG_I(TAG, "Extracting %s from field %d len is %d", text, field, len);
|
||||||
|
if(field < valid_index && len <= field) {
|
||||||
|
term = furi_string_search_char(buffer, delim, field);
|
||||||
|
if(term < valid_index) {
|
||||||
|
FuriString* result = furi_string_alloc();
|
||||||
|
furi_string_set_n(result, buffer, field, term - field);
|
||||||
|
value = __furi_string_hex_to_uint32(result);
|
||||||
|
FURI_LOG_D(TAG, "%s data is >>%s<<", text, furi_string_get_cstr(result));
|
||||||
|
furi_string_free(result);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Failed to find terminator for >>%s<<", text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Failed to find >>%s<<", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t __furi_string_hex_to_uint32(FuriString* str) {
|
||||||
|
uint32_t result = 0;
|
||||||
|
for(size_t i = 0; i < furi_string_size(str); i++) {
|
||||||
|
char ch = furi_string_get_char(str, i);
|
||||||
|
result *= 16;
|
||||||
|
if(ch >= '0' && ch <= '9') {
|
||||||
|
result += ch - '0';
|
||||||
|
} else if(ch >= 'A' && ch <= 'F') {
|
||||||
|
result += ch - 'A' + 10;
|
||||||
|
} else if(ch >= 'a' && ch <= 'f') {
|
||||||
|
result += ch - 'a' + 10;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_E(TAG, "Invalid hex character %c", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __gui_redraw() {
|
||||||
|
// Redraw screen
|
||||||
|
Gui* gui = furi_record_open(RECORD_GUI);
|
||||||
|
gui_direct_draw_acquire(gui);
|
||||||
|
gui_direct_draw_release(gui);
|
||||||
|
}
|
22
subghz/apps/rolling-flaws/rolling_flaws_utils.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#ifdef TAG
|
||||||
|
#undef TAG
|
||||||
|
#endif
|
||||||
|
#define TAG "RollingFlawsUtils"
|
||||||
|
|
||||||
|
size_t __furi_string_extract_string(
|
||||||
|
FuriString* buffer,
|
||||||
|
size_t start_index,
|
||||||
|
char* text,
|
||||||
|
char delim,
|
||||||
|
FuriString* result);
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
__furi_string_extract_int(FuriString* buffer, char* text, char delim, uint32_t default_value);
|
||||||
|
|
||||||
|
uint32_t __furi_string_hex_to_uint32(FuriString* str);
|
||||||
|
|
||||||
|
void __gui_redraw();
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cnt3DC9.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 00 0B D4 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cnt3DCA.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 02 8A 33 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cntA247.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 04 6A 34 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cntA248.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 03 89 EC AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cntFD75.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 03 E4 C9 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-enc00-cntFD76.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 0E BE 3B AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn0000000.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: BE 2B 2D F9 00 00 00 04
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn01EA8D8-cnt0004.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: BD 92 92 F3 1B 15 78 04
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cnt0004.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: AD 04 58 14 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cnt000B.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: 9F 5E C1 A4 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cnt0042.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: 17 6A ED 5F AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cnt0043.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: 82 70 AD 05 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cnt3E90.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: D7 60 F6 78 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cntEC01.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: A4 F2 F4 F7 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cntEC02.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: B9 26 02 C7 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cntEC03.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: 19 A1 2A D9 AB 97 72 14
|
7
subghz/apps/rolling-flaws/sub/k-dh-sn84EE9D5-cntXXXX.sub
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 02 8A 33 AB 97 72 14
|
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: AD 04 58 7A AB 97 72 14
|
@ -0,0 +1,7 @@
|
|||||||
|
Filetype: Flipper SubGhz Key File
|
||||||
|
Version: 1
|
||||||
|
Frequency: 433920000
|
||||||
|
Preset: FuriHalSubGhzPresetOok650Async
|
||||||
|
Protocol: KeeLoq
|
||||||
|
Bit: 64
|
||||||
|
Key: C0 12 34 56 AB 97 72 14
|