This repository has been archived by the owner on Nov 19, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 693
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
1,510 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# FindMy Flipper - FindMy SmartTag Emulator | ||
|
||
This app extends the functionality of the FlipperZero's bluetooth capabilities, enabling it to act as an Apple AirTag or Samsung SmartTag, or even both simultaneously. It utilizes the FlipperZero's BLE beacon to broadcast a SmartTag signal to be picked up by the FindMy Network. I made this to serve as a versatile tool for tracking purposes, offering the ability to clone existing tags, generate OpenHaystack key pairs for integration with Apple's FindMy network, and tune the device's beacon broadcast settings. | ||
|
||
## Features | ||
|
||
1. Tag Emulation: Clone your existing Apple AirTag or Samsung SmartTag to the FlipperZero, or generate a key pair for use with the FindMy network without owning an actual AirTag. | ||
2. Customization: Users can adjust the interval between beacon broadcasts and modify the transmit power to suit their needs, optimizing for both visibility and battery life. | ||
3. Efficient Background Operation: The app is optimized to run in the background, ensuring that your FlipperZero can still be tracked with minimal battery usage and without stopping normal use. | ||
|
||
## Usage Guide | ||
|
||
### Step 1: Installation | ||
- **Option A:** Use the released/precompiled firmware appropriate (FAP) for your device. | ||
- **Option B:** Build the firmware yourself using `fbt/ufbt`. | ||
- Both Installation options require you to be running a dev build of firmware. When release gets access to the extra BLE beacon this will change, thank you! | ||
- All firmware should now work with main branch, including icons | ||
|
||
### Step 2: Obtaining SmartTag Data | ||
|
||
#### Option A: Cloning Existing Tag (Preferred and allows you to track without additional setup) | ||
1. **Pair a Tag:** First, pair an AirTag or Samsung SmartTag with your device. | ||
2. **Enter 'Lost' Mode:** Keep the tag away from the device it's registered to for approximately 15 minutes. | ||
3. **Download nrfConnect:** Install nrfConnect from the Google Play Store. (Apple version doesn't reveal the needed Raw data, looking for a workaround) | ||
4. **Filter and Scan:** | ||
- Open the app, click on filters, and exclude all except for the brand of your tag (Apple/Samsung). | ||
- Adjust the RSSI to the lowest setting (-40 dBm). | ||
- Initiate a scan. Wait for your SmartTag to appear as a "FindMy" device. | ||
5. **Capture Data:** Click **Raw** or **View Raw** to capture your **payload** and note your tag's **MAC Address**. Immediately remove the tag's battery to prevent key/MAC rotation. | ||
6. **Enter Data in FlipperZero App:** Input the captured **payload** and **MAC Address** into the FlipperZero app. | ||
|
||
#### Option B: Open Haystack Method | ||
1. **Generate a Tag:** Download the `generate_keys.py` file and execute it in your terminal. (You will need cryptography ```python3 -m pip install cryptography```) | ||
2. **Follow Prompts:** During execution, you'll be prompted for inputs. By the end, you'll obtain a **Private Key**, **Public Key**, **Payload**, and **MAC Address**. | ||
- **Private Key** is necessary to receive location reports from Apple. | ||
- **MAC Address** should be registered in the FlipperZero app: | ||
1. Open the app and navigate to the config menu. | ||
2. Choose "register tag" and enter the MAC Address when prompted. | ||
3. A payload dialog will appear next. Enter your **Payload** here. | ||
4. Click save. | ||
3. **Configuration Completion:** With this setup, your device is ready for Open Haystack. Proceed with the specific steps for Open Haystack or MaclessHaystack based on your setup. | ||
- Don't Own a Mac: https://github.com/dchristl/macless-haystack or https://github.com/Chapoly1305/FindMy | ||
- Own a Mac: https://github.com/seemoo-lab/openhaystack | ||
|
||
## Setting Up on Mac with OpenHayStack (OHS) App -- If you own a Mac instructions | ||
|
||
Follow these steps to get everything working on a Mac using the latest version of the OpenHayStack app. | ||
Thanks to Wr3nch for the help | ||
|
||
### Step 1: Create a New Device | ||
- Start by creating a new device in the OpenHayStack app, but **do not deploy** it immediately after creation. | ||
|
||
### Step 2: Export Configuration | ||
- Choose to **EXPORT** the configuration by selecting "all accessories as file." To simplify, ensure you only have one entry in the list before exporting. | ||
- It is crucial that the export format is in JSON. | ||
|
||
### Step 3: Modify the JSON File | ||
Open the exported JSON file in a text editor and make the following changes: | ||
- **Left OHS, Right keys from my ```generate_keys.py``` script:** | ||
- `symmetricKey` should be set to the `Hashed adv key`. | ||
- `privateKey` should be replaced with your `Private Key`. | ||
- `oldestRelevantSymmetricKey` should also use the `Hashed adv key`. | ||
- Additionally, update the following attributes to `true`: | ||
- `"isDeployed": true` | ||
- `"isActive": true` | ||
|
||
### Step 4: Re-import the Configuration | ||
- After saving your changes to the JSON file, re-import it back into OpenHayStack. | ||
|
||
### Step 5: Adjust Settings in OHS App | ||
- In the OpenHayStack Mac App, navigate to the top bar and change the time setting from `1 Day` to `30min`. | ||
- Give it some time to process and apply the new settings. | ||
|
||
By following these steps, you should have your device set up and ready to go with OpenHayStack on a Mac. | ||
**** | ||
|
||
### Step 3: Configuration on the FlipperZero | ||
- Upon launching the app, open the config menu and either click ```Import Tag From File``` or ```Register Tag Manually```. Put your generated .keys file onto the FlipperZero SD card inside the AppsData/FindMyFlipper folder to import from file. Or you can manually enter the tag information. When using the cloning method, you can export a .txt file from nrfConnect (click save button) amd place that in the same folder in order to import. | ||
|
||
### Step 4: Tracking | ||
- Once the app is configured, your FlipperZero can be tracked using the relevant platform's tracking service (FindMy app for Apple devices, SmartThings for Samsung devices, and respective web browsers). If using generated keys and OpenHaystack then you can track on the OHS app or via the Macless Haystack setup. Links to both are above | ||
|
||
|
||
Customization | ||
|
||
- Beacon Interval: Adjust how frequently your FlipperZero broadcasts its presence. | ||
- Transmit Power: Increase or decrease the signal strength to balance between tracking range and battery life. | ||
|
||
Background Use | ||
|
||
The app is designed to have a negligible impact on battery life, even when running in the background. This allows for continuous tracking without the need for frequent recharging. | ||
|
||
Compatibility | ||
|
||
- Apple devices for AirTag tracking via the FindMy network. | ||
- Any device that supports Samsung SmartTag tracking, including web browsers (previously FindMyMobile). | ||
|
||
Thanks | ||
|
||
- Huge thanks to all the people that contributed to the OpenHaystack project, supporting projects, and guides on the subject. This wouldn't be a thing without any of you! Special thanks to WillyJL for helping get the app input working and overall overhaul of the apps functions! | ||
|
||
Legal and Privacy | ||
|
||
This app is intended for personal and educational use. Users are responsible for complying with local privacy laws and regulations regarding tracking devices. The cloning and emulation of tracking tags should be done responsibly and with respect to the ownership of the original devices. | ||
|
||
Disclaimer | ||
|
||
This project is not affiliated with Apple Inc. or Samsung. All product names, logos, and brands are property of their respective owners. Use this app responsibly and ethically. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
App( | ||
appid="findmy", | ||
name="FindMy Flipper", | ||
apptype=FlipperAppType.EXTERNAL, | ||
entry_point="findmy_main", | ||
requires=["gui"], | ||
stack_size=2 * 1024, | ||
fap_icon="location_icon.png", | ||
fap_icon_assets="icons", | ||
fap_category="Bluetooth", | ||
fap_author="@MatthewKuKanich", | ||
fap_weburl="https://github.com/MatthewKuKanich/FindMyFlipper", | ||
fap_version="1.0", | ||
fap_description="BLE FindMy Location Beacon", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
#include "findmy_i.h" | ||
|
||
static bool findmy_custom_event_callback(void* context, uint32_t event) { | ||
furi_assert(context); | ||
FindMy* app = context; | ||
return scene_manager_handle_custom_event(app->scene_manager, event); | ||
} | ||
|
||
static bool findmy_back_event_callback(void* context) { | ||
furi_assert(context); | ||
FindMy* app = context; | ||
return scene_manager_handle_back_event(app->scene_manager); | ||
} | ||
|
||
static FindMy* findmy_app_alloc() { | ||
FindMy* app = malloc(sizeof(FindMy)); | ||
|
||
app->gui = furi_record_open(RECORD_GUI); | ||
app->storage = furi_record_open(RECORD_STORAGE); | ||
app->dialogs = furi_record_open(RECORD_DIALOGS); | ||
|
||
app->view_dispatcher = view_dispatcher_alloc(); | ||
view_dispatcher_enable_queue(app->view_dispatcher); | ||
|
||
app->scene_manager = scene_manager_alloc(&findmy_scene_handlers, app); | ||
|
||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||
view_dispatcher_set_custom_event_callback(app->view_dispatcher, findmy_custom_event_callback); | ||
view_dispatcher_set_navigation_event_callback( | ||
app->view_dispatcher, findmy_back_event_callback); | ||
|
||
app->findmy_main = findmy_main_alloc(app); | ||
view_dispatcher_add_view( | ||
app->view_dispatcher, FindMyViewMain, findmy_main_get_view(app->findmy_main)); | ||
|
||
app->byte_input = byte_input_alloc(); | ||
view_dispatcher_add_view( | ||
app->view_dispatcher, FindMyViewByteInput, byte_input_get_view(app->byte_input)); | ||
|
||
app->var_item_list = variable_item_list_alloc(); | ||
view_dispatcher_add_view( | ||
app->view_dispatcher, | ||
FindMyViewVarItemList, | ||
variable_item_list_get_view(app->var_item_list)); | ||
|
||
app->popup = popup_alloc(); | ||
view_dispatcher_add_view(app->view_dispatcher, FindMyViewPopup, popup_get_view(app->popup)); | ||
|
||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||
|
||
findmy_state_load(&app->state); | ||
findmy_state_apply(&app->state); | ||
|
||
findmy_main_update_active(app->findmy_main, furi_hal_bt_extra_beacon_is_active()); | ||
findmy_main_update_interval(app->findmy_main, app->state.broadcast_interval); | ||
findmy_main_update_type(app->findmy_main, findmy_data_get_type(app->state.data)); | ||
|
||
return app; | ||
} | ||
|
||
static void findmy_app_free(FindMy* app) { | ||
furi_assert(app); | ||
|
||
view_dispatcher_remove_view(app->view_dispatcher, FindMyViewPopup); | ||
popup_free(app->popup); | ||
|
||
view_dispatcher_remove_view(app->view_dispatcher, FindMyViewVarItemList); | ||
variable_item_list_free(app->var_item_list); | ||
|
||
view_dispatcher_remove_view(app->view_dispatcher, FindMyViewByteInput); | ||
byte_input_free(app->byte_input); | ||
|
||
view_dispatcher_remove_view(app->view_dispatcher, FindMyViewMain); | ||
findmy_main_free(app->findmy_main); | ||
|
||
view_dispatcher_free(app->view_dispatcher); | ||
scene_manager_free(app->scene_manager); | ||
|
||
furi_record_close(RECORD_DIALOGS); | ||
furi_record_close(RECORD_STORAGE); | ||
furi_record_close(RECORD_GUI); | ||
|
||
free(app); | ||
} | ||
|
||
int32_t findmy_main(void* p) { | ||
UNUSED(p); | ||
FindMy* app = findmy_app_alloc(); | ||
|
||
scene_manager_next_scene(app->scene_manager, FindMySceneMain); | ||
|
||
view_dispatcher_run(app->view_dispatcher); | ||
|
||
findmy_app_free(app); | ||
return 0; | ||
} | ||
|
||
void findmy_change_broadcast_interval(FindMy* app, uint8_t value) { | ||
if(value > 10 || value < 1) { | ||
return; | ||
} | ||
app->state.broadcast_interval = value; | ||
findmy_state_sync_config(&app->state); | ||
findmy_state_save(&app->state); | ||
findmy_main_update_interval(app->findmy_main, app->state.broadcast_interval); | ||
if(furi_hal_bt_extra_beacon_is_active()) { | ||
// Always check if beacon is active before changing config | ||
furi_check(furi_hal_bt_extra_beacon_stop()); | ||
} | ||
furi_check(furi_hal_bt_extra_beacon_set_config(&app->state.config)); | ||
if(app->state.beacon_active) { | ||
furi_check(furi_hal_bt_extra_beacon_start()); | ||
} | ||
} | ||
|
||
void findmy_change_transmit_power(FindMy* app, uint8_t value) { | ||
if(value > 6) { | ||
return; | ||
} | ||
app->state.transmit_power = value; | ||
findmy_state_sync_config(&app->state); | ||
findmy_state_save(&app->state); | ||
if(furi_hal_bt_extra_beacon_is_active()) { | ||
furi_check(furi_hal_bt_extra_beacon_stop()); | ||
} | ||
furi_check(furi_hal_bt_extra_beacon_set_config(&app->state.config)); | ||
if(app->state.beacon_active) { | ||
furi_check(furi_hal_bt_extra_beacon_start()); | ||
} | ||
} | ||
|
||
void findmy_toggle_beacon(FindMy* app) { | ||
app->state.beacon_active = !app->state.beacon_active; | ||
findmy_state_save(&app->state); | ||
if(furi_hal_bt_extra_beacon_is_active()) { | ||
furi_check(furi_hal_bt_extra_beacon_stop()); | ||
} | ||
if(app->state.beacon_active) { | ||
furi_check(furi_hal_bt_extra_beacon_start()); | ||
} | ||
findmy_main_update_active(app->findmy_main, furi_hal_bt_extra_beacon_is_active()); | ||
} | ||
|
||
FindMyType findmy_data_get_type(uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]) { | ||
if(data[0] == 0x1E && // Length | ||
data[1] == 0xFF && // Manufacturer Specific Data | ||
data[2] == 0x4C && // Company ID (Apple, Inc.) | ||
data[3] == 0x00 && // ... | ||
data[4] == 0x12 && // Type (FindMy) | ||
data[5] == 0x19 // Length | ||
) { | ||
return FindMyTypeApple; | ||
} else { | ||
return FindMyTypeSamsung; | ||
} | ||
} | ||
|
||
#if FW_ORIGIN_Official | ||
void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { | ||
uint8_t tmp; | ||
for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) { | ||
tmp = mac_addr[i]; | ||
mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i]; | ||
mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp; | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#pragma once | ||
|
||
typedef struct FindMy FindMy; | ||
|
||
typedef enum FindMyType FindMyType; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#pragma once | ||
|
||
#include "findmy.h" | ||
#include "findmy_state.h" | ||
#include <furi_hal_bt.h> | ||
#include <extra_beacon.h> | ||
#include "findmy_icons.h" | ||
#include <toolbox/stream/file_stream.h> | ||
#include <toolbox/hex.h> | ||
#include <toolbox/path.h> | ||
#include <gui/gui.h> | ||
#include <storage/storage.h> | ||
#include <dialogs/dialogs.h> | ||
#include <gui/scene_manager.h> | ||
#include <gui/view_dispatcher.h> | ||
#include "views/findmy_main.h" | ||
#include <gui/modules/byte_input.h> | ||
#include <gui/modules/variable_item_list.h> | ||
#include <gui/modules/popup.h> | ||
#include "scenes/findmy_scene.h" | ||
#include "helpers/base64.h" | ||
#if FW_ORIGIN_Official | ||
void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); | ||
#endif | ||
|
||
struct FindMy { | ||
Gui* gui; | ||
Storage* storage; | ||
DialogsApp* dialogs; | ||
SceneManager* scene_manager; | ||
ViewDispatcher* view_dispatcher; | ||
|
||
FindMyMain* findmy_main; | ||
ByteInput* byte_input; | ||
VariableItemList* var_item_list; | ||
Popup* popup; | ||
|
||
uint8_t mac_buf[EXTRA_BEACON_MAC_ADDR_SIZE]; | ||
uint8_t packet_buf[EXTRA_BEACON_MAX_DATA_SIZE]; | ||
|
||
FindMyState state; | ||
}; | ||
|
||
typedef enum { | ||
FindMyViewMain, | ||
FindMyViewByteInput, | ||
FindMyViewVarItemList, | ||
FindMyViewPopup, | ||
} FindMyView; | ||
|
||
enum FindMyType { | ||
FindMyTypeApple, | ||
FindMyTypeSamsung, | ||
}; | ||
|
||
void findmy_change_broadcast_interval(FindMy* app, uint8_t value); | ||
void findmy_change_transmit_power(FindMy* app, uint8_t value); | ||
void findmy_toggle_beacon(FindMy* app); | ||
FindMyType findmy_data_get_type(uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#include "findmy_state.h" | ||
#include <furi_hal.h> | ||
|
||
void findmy_startup() { | ||
if(!furi_hal_is_normal_boot()) return; | ||
|
||
FindMyState state; | ||
if(findmy_state_load(&state)) { | ||
findmy_state_apply(&state); | ||
} | ||
} |
Oops, something went wrong.