├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── laas │ │ │ └── rcayre │ │ │ └── radiosploit │ │ │ ├── HciInterface.java │ │ │ ├── MainActivity.java │ │ │ ├── PacketItemData.java │ │ │ ├── PacketListAdapter.java │ │ │ ├── PatchFragment.java │ │ │ ├── dissectors │ │ │ ├── Dissector.java │ │ │ ├── EsbDissector.java │ │ │ ├── MosartDissector.java │ │ │ └── ZigbeeDissector.java │ │ │ └── ui │ │ │ ├── esb │ │ │ ├── EsbBus.java │ │ │ ├── EsbDevice.java │ │ │ ├── EsbDeviceBus.java │ │ │ ├── EsbDevicePacketListAdapter.java │ │ │ ├── EsbDevicesList.java │ │ │ ├── EsbEditDialogFragment.java │ │ │ ├── EsbFragment.java │ │ │ ├── EsbRxFragment.java │ │ │ ├── EsbRxPacketListAdapter.java │ │ │ ├── EsbRxThread.java │ │ │ ├── EsbScanFragment.java │ │ │ ├── EsbScanThread.java │ │ │ ├── EsbTabAdapter.java │ │ │ ├── EsbTxFragment.java │ │ │ ├── EsbTxPacketListAdapter.java │ │ │ ├── EsbTxThread.java │ │ │ └── EsbVisualizeDialogFragment.java │ │ │ ├── mosart │ │ │ ├── MosartBus.java │ │ │ ├── MosartDevice.java │ │ │ ├── MosartDeviceBus.java │ │ │ ├── MosartDevicePacketListAdapter.java │ │ │ ├── MosartDevicesList.java │ │ │ ├── MosartEditDialogFragment.java │ │ │ ├── MosartFragment.java │ │ │ ├── MosartKeyloggerFragment.java │ │ │ ├── MosartKeyloggerThread.java │ │ │ ├── MosartRxFragment.java │ │ │ ├── MosartRxPacketListAdapter.java │ │ │ ├── MosartRxThread.java │ │ │ ├── MosartScanFragment.java │ │ │ ├── MosartScanThread.java │ │ │ ├── MosartTabAdapter.java │ │ │ ├── MosartTxFragment.java │ │ │ ├── MosartTxPacketListAdapter.java │ │ │ ├── MosartTxThread.java │ │ │ └── MosartVisualizeDialogFragment.java │ │ │ └── zigbee │ │ │ ├── ZigbeeBus.java │ │ │ ├── ZigbeeEditDialogFragment.java │ │ │ ├── ZigbeeFragment.java │ │ │ ├── ZigbeeRxFragment.java │ │ │ ├── ZigbeeRxPacketListAdapter.java │ │ │ ├── ZigbeeRxThread.java │ │ │ ├── ZigbeeTabAdapter.java │ │ │ ├── ZigbeeTxFragment.java │ │ │ ├── ZigbeeTxPacketListAdapter.java │ │ │ ├── ZigbeeTxThread.java │ │ │ └── ZigbeeVisualizeDialogFragment.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── border.xml │ │ ├── ic_add.xml │ │ ├── ic_dashboard_black_24dp.xml │ │ ├── ic_esb.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_keyboard_dev.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_mosart.xml │ │ ├── ic_mosart2.xml │ │ ├── ic_mouse_24.xml │ │ ├── ic_mouse_dev.xml │ │ ├── ic_notifications_black_24dp.xml │ │ ├── ic_packet.xml │ │ ├── ic_radiosploit.xml │ │ ├── ic_unknown_dev.xml │ │ └── ic_zigbee.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── chip_field_entry.xml │ │ ├── fragment_edit_packet.xml │ │ ├── fragment_esb.xml │ │ ├── fragment_esb_rx.xml │ │ ├── fragment_esb_scan.xml │ │ ├── fragment_esb_tx.xml │ │ ├── fragment_mosart.xml │ │ ├── fragment_mosart_keylogger.xml │ │ ├── fragment_mosart_rx.xml │ │ ├── fragment_mosart_scan.xml │ │ ├── fragment_mosart_tx.xml │ │ ├── fragment_patch.xml │ │ ├── fragment_visualize_packet.xml │ │ ├── fragment_zigbee.xml │ │ ├── fragment_zigbee_rx.xml │ │ ├── fragment_zigbee_tx.xml │ │ └── packet_view_item.xml │ │ ├── menu │ │ └── bottom_nav_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── mobile_navigation.xml │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── laas │ └── rcayre │ └── radiosploit │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── radiosploit.apk ├── screenshots ├── esbrx.jpg ├── esbrx_small.jpg ├── esbtx.jpg ├── esbtx_small.jpg ├── mosartkeylogger.jpg ├── mosartkeylogger_small.jpg ├── mosartrx.jpg ├── mosartrx_small.jpg ├── mosartscan.jpg ├── mosartscan_small.jpg ├── zigbeedissector.jpg ├── zigbeedissector_small.jpg ├── zigbeerx.jpg ├── zigbeerx_small.jpg ├── zigbeetx.jpg └── zigbeetx_small.jpg └── settings.gradle /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Romain Cayre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RadioSploit 1.0 2 | ================ 3 | 4 | This Android application allows to sniff and inject Zigbee, Mosart and Enhanced ShockBurst packets from a Samsung Galaxy S20 smartphone. 5 | It interacts with a set of patches installed on the phone Bluetooth controller, allowing to add new capabilities to communicate using the previously mentioned protocols. 6 | 7 | This project is a Proof of Concept developed in the context of a research work aiming at exploring the feasibility of cross-protocol pivoting attacks. If you need additional details, we have published multiple papers about it: 8 | 9 | * Romain Cayre, Florent Galtier, Guillaume Auriol, Vincent Nicomette, Mohamed Kaâniche, Géraldine Marconato. POSTER: Cross-protocol attacks : weaponizing a smartphone by diverting its Bluetooth controller. *14th ACM Conference on Security and Privacy in Wireless and Mobile Networks (WiSec 2021)*, Jun 2021, Abu Dhabi (virtual), UAE. **\[en\]** 10 | 11 | * Romain Cayre, Florent Galtier. [Attaques inter-protocolaires par détournement du contrôleur Bluetooth d'un téléphone mobile](https://hal.laas.fr/hal-03221148). *GT Sécurité des Systèmes, Logiciels et Réseaux*, May 2021, En ligne, France. **\[fr\]** 12 | 13 | * Romain Cayre, Florent Galtier, Guillaume Auriol, Vincent Nicomette, Mohamed Kaâniche, et al. [WazaBee: attacking Zigbee networks by diverting Bluetooth Low Energy chips](https://hal.laas.fr/hal-03193299). *IEEE/IFIP International Conference on Dependable Systems and Networks (DSN)*, Jun 2021, Taipei (virtual), Taiwan. **\[en\]** 14 | 15 | * Romain Cayre, Florent Galtier, Guillaume Auriol, Vincent Nicomette, Geraldine Marconato. [WazaBee : attaque de réseaux Zigbee par détournement de puces Bluetooth Low Energy](https://hal.laas.fr/hal-02778262). *Symposium sur la Sécurité des Technologies de l'Information et des Communications (SSTIC 2020)*, Jun 2020, Rennes, France. pp.381-418.**\[fr\]** 16 | 17 | 18 | This application is released as an opensource software using the MIT License. 19 | 20 | Screenshots 21 | ------------ 22 | ![](screenshots/zigbeerx_small.jpg) 23 | ![](screenshots/zigbeetx_small.jpg) 24 | ![](screenshots/zigbeedissector_small.jpg) 25 | ![](screenshots/mosartscan_small.jpg) 26 | ![](screenshots/mosartkeylogger_small.jpg) 27 | ![](screenshots/mosartrx_small.jpg) 28 | ![](screenshots/esbrx_small.jpg) 29 | ![](screenshots/esbtx_small.jpg) 30 | 31 | How to use this application ? 32 | ------------------------------ 33 | * First, you need to root your Samsung Galaxy S20: it should also work on Samsung Galaxy S10, but it has not been tested yet. Multiple tutorials can be found online to enable root. 34 | * Enable the Bluetooth Debugging (BTsnoop logs) in your smartphone settings. 35 | * Then, you have to install the Radiosploit patches on your Bluetooth controller. The patches are uploaded on a different repository, follow [these instructions](https://github.com/RCayre/radiosploit_patches) ! 36 | * Finally, you can install the application using adb: 37 | ``` 38 | $ adb install radiosploit.apk 39 | ``` 40 | * Launch the app and allow it to use root permissions. 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion 30 7 | buildToolsVersion "30.0.3" 8 | 9 | defaultConfig { 10 | applicationId "laas.rcayre.radiosploit" 11 | minSdkVersion 16 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | } 30 | 31 | dependencies { 32 | 33 | implementation 'androidx.appcompat:appcompat:1.2.0' 34 | implementation 'com.google.android.material:material:1.3.0' 35 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 36 | implementation 'androidx.vectordrawable:vectordrawable:1.1.0' 37 | implementation 'androidx.navigation:navigation-fragment:2.3.5' 38 | implementation 'androidx.navigation:navigation-ui:2.3.5' 39 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' 40 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' 41 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 42 | implementation 'io.reactivex.rxjava2:rxjava:2.2.8' 43 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 44 | testImplementation 'junit:junit:4.+' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 47 | 48 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/MainActivity.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.example.radiosploit.R; 6 | 7 | import laas.rcayre.radiosploit.dissectors.Dissector; 8 | 9 | import com.google.android.material.bottomnavigation.BottomNavigationView; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | import androidx.fragment.app.FragmentManager; 13 | import androidx.navigation.NavController; 14 | import androidx.navigation.Navigation; 15 | import androidx.navigation.ui.AppBarConfiguration; 16 | import androidx.navigation.ui.NavigationUI; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | /* This is the main activity. It instantiates the HCI interface and setup the views. */ 20 | private HciInterface hciInterface = new HciInterface(); 21 | 22 | public HciInterface getHciInterface() { 23 | /* Getter allowing to easily get HCI Interface instance */ 24 | return hciInterface; 25 | } 26 | 27 | private void showPatchFragment() { 28 | /* This method shows a pop up indicating that the patches have not been found */ 29 | FragmentManager fm = getSupportFragmentManager(); 30 | PatchFragment patchFragment = PatchFragment.newInstance("Patches not found"); 31 | patchFragment.show(fm, "PatchFragment"); 32 | 33 | } 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | 39 | // We configure the views / UI 40 | setContentView(R.layout.activity_main); 41 | BottomNavigationView navView = findViewById(R.id.nav_view); 42 | AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( 43 | R.id.navigation_zigbee, R.id.navigation_mosart, R.id.navigation_esb) 44 | .build(); 45 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); 46 | NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); 47 | NavigationUI.setupWithNavController(navView, navController); 48 | 49 | // We runs the HCI Interface thread (to extract HCI event) 50 | Thread hciInterfaceThread = new Thread(hciInterface); 51 | hciInterfaceThread.start(); 52 | 53 | /* 54 | Here, we have to check that our patches are installed on the controller. 55 | We check if the string "RadioSploit" (in hex: 526164696F53706C6F6974) is present at address 0x2106cc in controller's RAM. 56 | If it is not, the patches are not installed, and we show a dialog to inform the user. 57 | */ 58 | byte[] patchString = hciInterface.readRam(0x2106cc,11); 59 | if (!Dissector.bytesToHex(patchString).equals("526164696F53706C6F6974")) { 60 | this.showPatchFragment(); 61 | } 62 | 63 | 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/PacketItemData.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit; 2 | 3 | import com.example.radiosploit.R; 4 | import laas.rcayre.radiosploit.dissectors.Dissector; 5 | 6 | public class PacketItemData { 7 | /* This crappy class is used to represent a Packet or a Device */ 8 | private byte[] content; 9 | private int imgId; // icon 10 | private int type; 11 | private String description; 12 | private String status; 13 | 14 | public PacketItemData(byte[] content, int type, String description,String status) { 15 | this.content = content; 16 | this.description = description; 17 | this.type = type; 18 | this.status = status; 19 | if (type == 0x01) { 20 | this.imgId = R.drawable.ic_zigbee; 21 | } 22 | else if (type == 0x02) { 23 | if (description.equals("Mouse")) { 24 | this.imgId = R.drawable.ic_mouse_dev; 25 | } 26 | else if (description.equals("Keyboard")) { 27 | this.imgId = R.drawable.ic_keyboard_dev; 28 | } 29 | else { 30 | this.imgId = R.drawable.ic_unknown_dev; 31 | } 32 | } 33 | else if (type == 0x04) { 34 | this.imgId = R.drawable.ic_packet; 35 | } 36 | else if (type == 0x05) { 37 | this.imgId = R.drawable.ic_unknown_dev; 38 | } 39 | else if (type == 0x06) { 40 | this.imgId = R.drawable.ic_packet; 41 | } 42 | 43 | else { 44 | this.imgId = R.drawable.ic_unknown_dev; 45 | } 46 | } 47 | public String getFormattedContent() { 48 | /* Getter allowing to get a Human readable version of the content */ 49 | if (this.type != 0x02) { 50 | return Dissector.bytesToHex(content); 51 | } 52 | else return Dissector.bytesToAddress(content); 53 | } 54 | 55 | public String getDescription() { 56 | /* Getter allowing to get the description */ 57 | return description; 58 | } 59 | 60 | public String getStatus() { 61 | /* Getter allowing to get the status */ 62 | return status; 63 | } 64 | 65 | public int getType() { 66 | /* Getter allowing to get the type */ 67 | return this.type; 68 | } 69 | 70 | public void setContent(byte[] content) { 71 | /* Setter allowing to modify the content */ 72 | this.content = content; 73 | } 74 | public byte[] getContent() { 75 | /* Getter allowing to get the content (byte array) */ 76 | return this.content; 77 | } 78 | public int getImgId() { 79 | /* Getter allowing to get the image identifier (used to choose the icon)*/ 80 | return imgId; 81 | } 82 | public void update(PacketItemData newContent) { 83 | /* Method allowing to update the content of the current instance */ 84 | this.content = newContent.getContent(); 85 | this.description = newContent.getDescription(); 86 | this.type = newContent.getType(); 87 | this.imgId = newContent.getImgId(); 88 | this.status = newContent.getStatus(); 89 | 90 | } 91 | public void setStatus(String status) { 92 | /* Setter allowing to modify the status */ 93 | this.status = status; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/PacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.RelativeLayout; 8 | import android.widget.TextView; 9 | 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import com.example.radiosploit.R; 13 | 14 | import java.util.ArrayList; 15 | 16 | public abstract class PacketListAdapter extends RecyclerView.Adapter{ 17 | /* Adapter allowing to manage a list of PacketItemData */ 18 | private ArrayList packetList; 19 | public PacketListAdapter(ArrayList list) { 20 | this.packetList = list; 21 | } 22 | 23 | public abstract void onItemClick(View view,PacketItemData itemData, int position); 24 | public abstract void onItemLongClick(View view,PacketItemData itemData, int position); 25 | 26 | @Override 27 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 28 | LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); 29 | View listItem= layoutInflater.inflate(R.layout.packet_view_item, parent, false); 30 | ViewHolder viewHolder = new ViewHolder(listItem); 31 | return viewHolder; 32 | } 33 | 34 | @Override 35 | public void onBindViewHolder(ViewHolder holder, int position) { 36 | final PacketItemData myListData = packetList.get(position); 37 | holder.descriptionTextView.setText("#"+String.valueOf(position)+" "+packetList.get(position).getDescription()); 38 | holder.statusTextView.setText(packetList.get(position).getStatus()); 39 | holder.contentTextView.setText(packetList.get(position).getFormattedContent()); 40 | holder.imageView.setImageResource(packetList.get(position).getImgId()); 41 | 42 | holder.relativeLayout.setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View view) { 45 | /* If there is a short click on a PacketItemData, propagate the event to the corresponding callback */ 46 | PacketListAdapter.this.onItemClick(view,myListData, position); 47 | } 48 | }); 49 | holder.relativeLayout.setOnLongClickListener(new View.OnLongClickListener() { 50 | @Override 51 | public boolean onLongClick (View view){ 52 | /* If there is a long click on a PacketItemData, propagate the event to the corresponding callback */ 53 | PacketListAdapter.this.onItemLongClick(view,myListData, position); 54 | return true; 55 | } 56 | } 57 | ); 58 | } 59 | 60 | 61 | @Override 62 | public int getItemCount() { 63 | /* Returns the number of items in the list */ 64 | return packetList.size(); 65 | } 66 | 67 | public static class ViewHolder extends RecyclerView.ViewHolder { 68 | public ImageView imageView; 69 | public TextView descriptionTextView; 70 | public TextView statusTextView; 71 | public TextView contentTextView; 72 | public RelativeLayout relativeLayout; 73 | public ViewHolder(View itemView) { 74 | super(itemView); 75 | this.imageView = (ImageView) itemView.findViewById(R.id.packet_icon); 76 | this.descriptionTextView = (TextView) itemView.findViewById(R.id.packet_description); 77 | this.contentTextView = (TextView) itemView.findViewById(R.id.packet_content); 78 | this.statusTextView = (TextView) itemView.findViewById(R.id.packet_status); 79 | relativeLayout = (RelativeLayout)itemView.findViewById(R.id.packet_view_item_layout); 80 | } 81 | } 82 | 83 | /* Methods allowing to interact with the list items */ 84 | public void addNewData(PacketItemData data){ 85 | packetList.add(data); 86 | notifyDataSetChanged(); 87 | } 88 | 89 | public void updateStatus(int packetId,String status) { 90 | packetList.get(packetId).setStatus(status); 91 | notifyItemChanged(packetId); 92 | } 93 | public void updateData(int packetId, PacketItemData data) { 94 | packetList.get(packetId).update(data); 95 | notifyItemChanged(packetId); 96 | } 97 | 98 | public void resetData() { 99 | packetList.clear(); 100 | notifyDataSetChanged(); 101 | } 102 | 103 | public PacketItemData getData(int position) { 104 | return packetList.get(position); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/PatchFragment.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | 10 | import androidx.fragment.app.DialogFragment; 11 | 12 | import com.example.radiosploit.R; 13 | 14 | public class PatchFragment extends DialogFragment { 15 | /* This Fragment is used to inform the user that he should install controller patches, nothing to see here */ 16 | public static PatchFragment newInstance(String title) { 17 | 18 | PatchFragment frag = new PatchFragment(); 19 | Bundle args = new Bundle(); 20 | args.putString("title", title); 21 | frag.setArguments(args); 22 | 23 | return frag; 24 | 25 | } 26 | 27 | @Override 28 | public void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | } 31 | 32 | @Override 33 | public void onResume() { 34 | super.onResume(); 35 | ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes(); 36 | params.width = android.view.WindowManager.LayoutParams.MATCH_PARENT; 37 | params.height = android.view.WindowManager.LayoutParams.WRAP_CONTENT; 38 | 39 | getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 40 | 41 | } 42 | 43 | @Override 44 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 45 | View v = inflater.inflate(R.layout.fragment_patch, container, false); 46 | Button okButton = v.findViewById(R.id.patch_ok_button); 47 | 48 | // If we detect a click on the button, close the window 49 | okButton.setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View v) { 52 | dismiss(); 53 | } 54 | }); 55 | return v; 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/dissectors/Dissector.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.dissectors; 2 | 3 | import java.util.ArrayList; 4 | 5 | public abstract class Dissector { 6 | /* This class implements a Protocol Dissector */ 7 | 8 | public static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 9 | protected static final byte[] STOP_DISSECTION = {}; 10 | 11 | public static String bytesToHex(byte[] bytes) { 12 | /* Converts bytes to an Hex string */ 13 | char[] hexChars = new char[bytes.length * 2]; 14 | for (int j = 0; j < bytes.length; j++) { 15 | int v = bytes[j] & 0xFF; 16 | hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 17 | hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 18 | } 19 | return new String(hexChars); 20 | } 21 | public static byte[] addressToBytes(String address) { 22 | /* Converts an address string ("11:22:33:44") to the correspond bytes */ 23 | String addr = address.replace(":",""); 24 | return hexToBytes(addr); 25 | } 26 | 27 | public static String bytesToAddress(byte[] bytes) { 28 | /* Converts bytes to an address string ("11:22:33:44") */ 29 | String hex = bytesToHex(bytes); 30 | StringBuilder builder = new StringBuilder(); 31 | 32 | int index = 0; 33 | while (index < hex.length()) 34 | { 35 | builder.append(hex.substring(index,index+2)); 36 | 37 | index += 2; 38 | if (index < hex.length()) builder.append(":"); 39 | } 40 | return builder.toString(); 41 | } 42 | 43 | public static byte[] hexToBytes(String s) { 44 | /*Converts an hex string to the corresponding bytes */ 45 | int len = s.length(); 46 | 47 | byte[] data = new byte[len / 2]; 48 | for (int i = 0; i < len; i += 2) { 49 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 50 | + Character.digit(s.charAt(i+1), 16)); 51 | } 52 | return data; 53 | } 54 | protected ArrayList fieldList; 55 | protected byte[] content; 56 | 57 | public Dissector(byte[] content) { 58 | this.content = content; 59 | this.fieldList = new ArrayList(); 60 | } 61 | public Dissector(String content) { 62 | this.content = hexToBytes(content); 63 | this.fieldList = new ArrayList(); 64 | } 65 | public void update(byte[] content) { 66 | this.content = content; 67 | } 68 | 69 | public void update(String content) { 70 | this.content = hexToBytes(content); 71 | } 72 | public ArrayList getFields() { 73 | return this.fieldList; 74 | } 75 | public abstract void dissect(); 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbBus.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import laas.rcayre.radiosploit.PacketItemData; 4 | 5 | import io.reactivex.subjects.PublishSubject; 6 | import io.reactivex.Observable; 7 | 8 | public class EsbBus { 9 | /* This class is used to exchange information between two ESB fragments (RX > TX, used to add a received packet to the TX list) */ 10 | 11 | /* Singleton implementation */ 12 | private static EsbBus mInstance; 13 | public static EsbBus getInstance() { 14 | if (mInstance == null) { 15 | mInstance = new EsbBus(); 16 | } 17 | return mInstance; 18 | } 19 | private EsbBus() { 20 | } 21 | private PublishSubject publisher = PublishSubject.create(); 22 | void publish(PacketItemData packet) { 23 | publisher.onNext(packet); 24 | } 25 | Observable listen() { 26 | return publisher; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbDevice.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import laas.rcayre.radiosploit.dissectors.Dissector; 4 | 5 | public class EsbDevice { 6 | /* This class represents an Enhanced ShockBurst Device */ 7 | private int channel; 8 | private String address; 9 | 10 | public EsbDevice(int channel, String address) { 11 | this.channel = channel; 12 | this.address = address; 13 | } 14 | 15 | public EsbDevice(int channel, byte[] address) { 16 | this.channel = channel; 17 | this.address = Dissector.bytesToAddress(address); 18 | } 19 | 20 | public int getChannel() { 21 | return this.channel; 22 | } 23 | public String getAddress() { 24 | return this.address; 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbDeviceBus.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import laas.rcayre.radiosploit.PacketItemData; 4 | 5 | import io.reactivex.subjects.PublishSubject; 6 | import io.reactivex.Observable; 7 | 8 | public class EsbDeviceBus { 9 | /* This class is used to exchange information between two ESB fragments (Scan > RX, used to automatically configure RX parameters based on the transmitted device) */ 10 | 11 | /* Singleton implementation */ 12 | private static EsbDeviceBus mInstance; 13 | public static EsbDeviceBus getInstance() { 14 | if (mInstance == null) { 15 | mInstance = new EsbDeviceBus(); 16 | } 17 | return mInstance; 18 | } 19 | private EsbDeviceBus() { 20 | } 21 | private PublishSubject publisher = PublishSubject.create(); 22 | void publish(PacketItemData packet) { 23 | publisher.onNext(packet); 24 | } 25 | // Listen should return an Observable 26 | Observable listen() { 27 | return publisher; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbDevicePacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import android.view.View; 4 | import android.widget.Toast; 5 | 6 | import laas.rcayre.radiosploit.PacketItemData; 7 | import laas.rcayre.radiosploit.PacketListAdapter; 8 | 9 | import java.util.ArrayList; 10 | 11 | public class EsbDevicePacketListAdapter extends PacketListAdapter { 12 | /* Enhanced ShockBurst Device Packet List Adapter, represents a device PacketListData*/ 13 | public EsbDevicePacketListAdapter(ArrayList list) {super(list);} 14 | @Override 15 | public void onItemClick(View view, PacketItemData itemData, int position) { 16 | 17 | } 18 | 19 | @Override 20 | public void onItemLongClick(View view, PacketItemData itemData, int position) { 21 | /* If a long click is detected on the device, automatically selects it in RX fragment */ 22 | EsbDeviceBus.getInstance().publish(itemData); 23 | Toast.makeText(view.getContext(),"Address selected", Toast.LENGTH_LONG).show(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbDevicesList.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import laas.rcayre.radiosploit.MainActivity; 4 | import laas.rcayre.radiosploit.PacketItemData; 5 | import laas.rcayre.radiosploit.PacketListAdapter; 6 | import laas.rcayre.radiosploit.dissectors.Dissector; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class EsbDevicesList { 11 | /* This class is a representation of a Enhanced ShockBurst Devices List*/ 12 | 13 | private PacketListAdapter esbDeviceListAdapter ; 14 | private MainActivity activity; 15 | ArrayList deviceList = new ArrayList(); 16 | public EsbDevicesList(MainActivity activity, PacketListAdapter adapter) { 17 | this.esbDeviceListAdapter = adapter; 18 | this.activity = activity; 19 | } 20 | public void clear() { 21 | /* This method clears the list */ 22 | this.deviceList.clear(); 23 | this.esbDeviceListAdapter.resetData(); 24 | } 25 | public void addDevice(EsbDevice device) { 26 | /* This method allows to add a device if it is not already known */ 27 | boolean found = false; 28 | for (int i=0;i packetList = new ArrayList(); 63 | PacketListAdapter adapter = new EsbRxPacketListAdapter(packetList,this); 64 | esbRxPacketView.setHasFixedSize(true); 65 | esbRxPacketView.setLayoutManager(new LinearLayoutManager(root.getContext())); 66 | esbRxPacketView.setAdapter(adapter); 67 | 68 | EsbDeviceBus.getInstance().listen().subscribe(getInputObserver()); 69 | esbRxThread = new EsbRxThread((MainActivity)getActivity(),adapter,hciInterface,esbRxPacketView); 70 | 71 | esbRxResetButton.setOnClickListener(new View.OnClickListener() { 72 | @Override 73 | public void onClick(View v) { 74 | adapter.resetData(); 75 | } 76 | }); 77 | esbRxChannelSlider.addOnChangeListener(new Slider.OnChangeListener() { 78 | @Override 79 | public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { 80 | esbRxChannelLabel.setText("CH:" + String.valueOf((int) (value))); 81 | if (startRxToggleButton.isChecked() && esbRxAddress.getText().toString().length() == 14) { 82 | hciInterface.configureEsbRx(true,(int)value, Dissector.addressToBytes(esbRxAddress.getText().toString())); 83 | } 84 | } 85 | }); 86 | startRxToggleButton.setOnClickListener(new View.OnClickListener() { 87 | @Override 88 | public void onClick(View arg0) { 89 | int channel = (int)(esbRxChannelSlider.getValue()); 90 | if(startRxToggleButton.isChecked()){ 91 | if (esbRxAddress.getText().toString().length() == 14) { 92 | hciInterface.configureEsbRx(true, channel, Dissector.addressToBytes(esbRxAddress.getText().toString())); 93 | } 94 | else { 95 | startRxToggleButton.setChecked(false); 96 | } 97 | Thread esbThread = new Thread(esbRxThread); 98 | esbThread.start(); 99 | } 100 | else { 101 | byte[] address = {0x00,0x00,0x00,0x00,0x00}; 102 | hciInterface.configureEsbRx(false,channel, address); 103 | esbRxThread.stop(); 104 | } 105 | }}); 106 | 107 | return root; 108 | } 109 | // Get input observer instance 110 | private Observer getInputObserver() { 111 | return new Observer() { 112 | @Override public void onSubscribe(Disposable d) { 113 | } 114 | @Override public void onNext(PacketItemData s) { 115 | int channel = Integer.parseInt(s.getStatus().substring(8,10)); 116 | esbRxAddress.setText(s.getFormattedContent()); 117 | esbRxChannelSlider.setValue(channel); 118 | esbRxChannelLabel.setText("CH: "+channel); 119 | } 120 | @Override public void onError(Throwable e) { 121 | } 122 | @Override public void onComplete() { 123 | } 124 | }; 125 | } 126 | 127 | public void setUserVisibleHint(boolean visible) { 128 | super.setUserVisibleHint(visible); 129 | if (!visible) { 130 | /* If the fragment is hidden, stops the thread */ 131 | if (esbRxThread != null && esbRxThread.isRunning()) { 132 | startRxToggleButton.setChecked(false); 133 | esbRxThread.stop(); 134 | } 135 | } 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbRxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import android.view.View; 4 | import android.widget.Toast; 5 | 6 | import androidx.fragment.app.DialogFragment; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentTransaction; 9 | 10 | import laas.rcayre.radiosploit.PacketItemData; 11 | import laas.rcayre.radiosploit.PacketListAdapter; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class EsbRxPacketListAdapter extends PacketListAdapter { 16 | /* This class represents the packet list adapter for Enhanced Shockburst RX packets*/ 17 | private Fragment rxFragment; 18 | public EsbRxPacketListAdapter(ArrayList list, Fragment rxFragment) { 19 | super(list); 20 | this.rxFragment = rxFragment; 21 | } 22 | 23 | public void onItemClick(View view, PacketItemData item, int position) { 24 | /* Short click : visualize the packet using EsbVisualizeDialogFragment */ 25 | FragmentTransaction ft = this.rxFragment.getParentFragmentManager().beginTransaction(); 26 | Fragment prev = this.rxFragment.getParentFragmentManager().findFragmentByTag("EsbVisualizeDialog"); 27 | if (prev != null) { 28 | ft.remove(prev); 29 | } 30 | ft.addToBackStack(null); 31 | DialogFragment dialogFragment = EsbVisualizeDialogFragment.newInstance(item.getFormattedContent()); 32 | dialogFragment.setTargetFragment(this.rxFragment,0); 33 | dialogFragment.show(ft, "EsbVisualizeDialog"); 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | /* Long click: transmit the packet to TX fragment using EsbBus */ 37 | EsbBus.getInstance().publish(new PacketItemData(item.getContent(),0x06, item.getDescription(),"")); 38 | Toast.makeText(view.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbRxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketListAdapter; 8 | import laas.rcayre.radiosploit.PacketItemData; 9 | 10 | public class EsbRxThread implements Runnable { 11 | /* This class implements the Enhanced ShockBurst RX Thread, allowing to populates the RX packet list */ 12 | private PacketListAdapter esbPacketListAdapter; 13 | private HciInterface hciInterface; 14 | private MainActivity activity; 15 | private RecyclerView packetView; 16 | private boolean running; 17 | 18 | public EsbRxThread(MainActivity activity, PacketListAdapter esbPacketListAdapter, HciInterface hciInterface,RecyclerView packetView) { 19 | this.esbPacketListAdapter = esbPacketListAdapter; 20 | this.hciInterface = hciInterface; 21 | this.activity = activity; 22 | this.packetView = packetView; 23 | } 24 | 25 | public boolean isRunning() { 26 | return running; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | running = true; 32 | while (running) { 33 | PacketItemData packet = this.hciInterface.nextPacket(); 34 | 35 | if (packet != null) { 36 | /* if we receive a packet ...*/ 37 | activity.runOnUiThread(new Runnable() { 38 | @Override 39 | public void run() { 40 | // adds it to the list 41 | esbPacketListAdapter.addNewData(packet); 42 | // scrolls to the end of the list 43 | packetView.scrollToPosition(esbPacketListAdapter.getItemCount()-1); 44 | } 45 | }); 46 | 47 | 48 | } 49 | } 50 | } 51 | 52 | public void stop() { 53 | running = false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbScanFragment.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.ProgressBar; 10 | import android.widget.ToggleButton; 11 | 12 | import androidx.fragment.app.Fragment; 13 | import androidx.recyclerview.widget.LinearLayoutManager; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import laas.rcayre.radiosploit.HciInterface; 17 | import laas.rcayre.radiosploit.MainActivity; 18 | import laas.rcayre.radiosploit.PacketItemData; 19 | import laas.rcayre.radiosploit.PacketListAdapter; 20 | import com.example.radiosploit.R; 21 | 22 | import java.util.ArrayList; 23 | 24 | public class EsbScanFragment extends Fragment { 25 | /* This is the Enhanced Shockburst Scan fragment, allowing to detect ESB devices */ 26 | private EsbScanThread esbScanThread; 27 | private ToggleButton esbScanToggleButton; 28 | public EsbScanFragment() { 29 | } 30 | 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | } 35 | 36 | @Override 37 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 38 | Bundle savedInstanceState) { 39 | 40 | View root = inflater.inflate(R.layout.fragment_esb_scan, container, false); 41 | esbScanToggleButton = root.findViewById(R.id.esb_scan_toggle_button); 42 | RecyclerView deviceView = root.findViewById(R.id.esb_scan_device_view); 43 | ProgressBar progressBar = root.findViewById(R.id.esb_scan_progressbar); 44 | ArrayList packetList = new ArrayList(); 45 | PacketListAdapter adapter = new EsbDevicePacketListAdapter(packetList); 46 | deviceView.setHasFixedSize(true); 47 | deviceView.setLayoutManager(new LinearLayoutManager(root.getContext())); 48 | deviceView.setAdapter(adapter); 49 | 50 | HciInterface hciInterface = ((MainActivity)getActivity()).getHciInterface(); 51 | esbScanThread = new EsbScanThread((MainActivity)getActivity(),hciInterface,adapter, progressBar); 52 | Button esbScanResetButton = root.findViewById(R.id.esb_scan_reset_button); 53 | esbScanResetButton.setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | esbScanThread.getDevicesList().clear(); 57 | } 58 | }); 59 | 60 | esbScanToggleButton.setOnClickListener(new View.OnClickListener() { 61 | @Override 62 | public void onClick(View arg0) { 63 | if(esbScanToggleButton.isChecked()){ 64 | Thread esbThread = new Thread(esbScanThread); 65 | esbThread.start(); 66 | } 67 | else { 68 | esbScanThread.stop(); 69 | } 70 | }}); 71 | 72 | return root; 73 | } 74 | public void setUserVisibleHint(boolean visible) { 75 | super.setUserVisibleHint(visible); 76 | if (!visible) { 77 | /* If the fragment is hidden, stops the thread */ 78 | if (esbScanThread != null && esbScanThread.isRunning()) { 79 | esbScanToggleButton.setChecked(false); 80 | esbScanThread.stop(); 81 | } 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbScanThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import android.widget.ProgressBar; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketItemData; 8 | import laas.rcayre.radiosploit.PacketListAdapter; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class EsbScanThread implements Runnable { 16 | /* This is the thread allowing to scan Enhanced ShockBurst devices */ 17 | private HciInterface hciInterface; 18 | private MainActivity activity; 19 | private ArrayList candidateAddressesList; 20 | private EsbDevicesList devicesList; 21 | private ProgressBar progressBar; 22 | 23 | private boolean running; 24 | 25 | public EsbScanThread(MainActivity activity, HciInterface hciInterface, PacketListAdapter adapter, ProgressBar progressBar) { 26 | this.hciInterface = hciInterface; 27 | this.activity = activity; 28 | this.devicesList = new EsbDevicesList(activity,adapter); 29 | this.candidateAddressesList = new ArrayList(); 30 | this.progressBar = progressBar; 31 | } 32 | 33 | public EsbDevicesList getDevicesList() { 34 | return this.devicesList; 35 | } 36 | public boolean isRunning() { 37 | return running; 38 | } 39 | 40 | public static String mostCommon(List list) { 41 | /* This method returns the most common element in a list */ 42 | Map map = new HashMap<>(); 43 | 44 | for (String t : list) { 45 | Integer val = map.get(t); 46 | map.put(t, val == null ? 1 : val + 1); 47 | } 48 | 49 | Map.Entry max = null; 50 | 51 | for (Map.Entry e : map.entrySet()) { 52 | if (max == null || e.getValue() > max.getValue()) 53 | max = e; 54 | } 55 | 56 | return max.getKey(); 57 | } 58 | @Override 59 | public void run() { 60 | running = true; 61 | try { 62 | /* We explore every channel during 750 ms*/ 63 | int channel = 2; 64 | this.progressBar.setMax(82); 65 | while (running) { 66 | this.hciInterface.configureEsbScan(true, channel); 67 | Thread.sleep(750); 68 | PacketItemData currentPacket; 69 | this.progressBar.setProgress(channel-2); 70 | candidateAddressesList.clear(); 71 | do { 72 | currentPacket = this.hciInterface.nextPacket(); 73 | if (currentPacket != null && !currentPacket.getDescription().equals("00:00:00:00:00")) { 74 | candidateAddressesList.add(currentPacket.getDescription()); 75 | } 76 | } while(currentPacket != null); 77 | // If we received something, we wait 10 secs 78 | if (candidateAddressesList.size() != 0) { 79 | Thread.sleep(10000); 80 | do { 81 | currentPacket = this.hciInterface.nextPacket(); 82 | if (currentPacket != null && !currentPacket.getDescription().equals("00:00:00:00:00")) { 83 | candidateAddressesList.add(currentPacket.getDescription()); 84 | } 85 | } while(currentPacket != null); 86 | 87 | // We only keep the most common device because we may have false positives 88 | String winner = mostCommon(candidateAddressesList); 89 | devicesList.addDevice(channel,winner); 90 | 91 | 92 | } 93 | if (channel < 80) channel++; 94 | else channel = 2; 95 | } 96 | } 97 | catch (InterruptedException e) { 98 | running = false; 99 | } 100 | } 101 | 102 | public void stop() { 103 | running = false; 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbTabAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.fragment.app.Fragment; 5 | import androidx.fragment.app.FragmentManager; 6 | import androidx.fragment.app.FragmentPagerAdapter; 7 | 8 | 9 | public class EsbTabAdapter extends FragmentPagerAdapter { 10 | /* This class implements the EsbTabAdapter. We have three tabs: Scan (EsbScanFragment), RX (EsbRxFragment) and TX (EsbTxFragment)*/ 11 | 12 | private String[] tabs_name = { "Scan", "RX", "TX" }; 13 | public EsbTabAdapter(FragmentManager fm) { 14 | super(fm); 15 | } 16 | @Override 17 | public Fragment getItem(int index) { 18 | 19 | switch (index) { 20 | case 0: 21 | return new EsbScanFragment(); 22 | case 1: 23 | return new EsbRxFragment(); 24 | case 2: 25 | return new EsbTxFragment(); 26 | } 27 | 28 | return null; 29 | } 30 | @Nullable 31 | @Override 32 | public CharSequence getPageTitle(int position) { 33 | return tabs_name[position]; 34 | } 35 | 36 | @Override 37 | public int getCount() { 38 | return 3; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbTxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | import android.view.View; 4 | 5 | import androidx.fragment.app.DialogFragment; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.fragment.app.FragmentTransaction; 8 | 9 | import laas.rcayre.radiosploit.PacketItemData; 10 | import laas.rcayre.radiosploit.PacketListAdapter; 11 | 12 | import java.util.ArrayList; 13 | 14 | public class EsbTxPacketListAdapter extends PacketListAdapter { 15 | /* This is the Enhanced Shockburst packet List Adapter, allowing to react to click events*/ 16 | private EsbTxFragment txFragment; 17 | public EsbTxPacketListAdapter(ArrayList list, EsbTxFragment txFragment) { 18 | super(list); 19 | this.txFragment = txFragment; 20 | } 21 | 22 | public void onItemClick(View view, PacketItemData item, int position) { 23 | /* Short click, opens the edit dialog with the corresponding packet */ 24 | FragmentTransaction ft = this.txFragment.getParentFragmentManager().beginTransaction(); 25 | Fragment prev = this.txFragment.getParentFragmentManager().findFragmentByTag("EsbEditDialog"); 26 | if (prev != null) { 27 | ft.remove(prev); 28 | } 29 | ft.addToBackStack(null); 30 | DialogFragment dialogFragment = EsbEditDialogFragment.newInstance(position,item.getFormattedContent()); 31 | dialogFragment.setTargetFragment( this.txFragment,0); 32 | dialogFragment.show(ft, "EsbEditDialog"); 33 | 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | /* Long click, do nothing */ 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/esb/EsbTxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.esb; 2 | 3 | 4 | import android.widget.ProgressBar; 5 | import android.widget.TextView; 6 | import android.widget.ToggleButton; 7 | 8 | import laas.rcayre.radiosploit.HciInterface; 9 | import laas.rcayre.radiosploit.MainActivity; 10 | import laas.rcayre.radiosploit.PacketListAdapter; 11 | import laas.rcayre.radiosploit.dissectors.Dissector; 12 | 13 | public class EsbTxThread implements Runnable { 14 | /* This class implements the Enhanced ShockBurst TX Thread, allowing to transmit packets */ 15 | private PacketListAdapter esbPacketListAdapter; 16 | private HciInterface hciInterface; 17 | private ProgressBar txProgressBar; 18 | private ToggleButton txToggleButton; 19 | private MainActivity mainActivity; 20 | private TextView addressEntry; 21 | private int channel; 22 | 23 | private boolean running; 24 | 25 | public EsbTxThread(MainActivity mainActivity,PacketListAdapter esbPacketListAdapter, HciInterface hciInterface, ProgressBar txProgressBar, ToggleButton txToggleButton, TextView addressEntry) { 26 | this.mainActivity = mainActivity; 27 | this.esbPacketListAdapter = esbPacketListAdapter; 28 | this.hciInterface = hciInterface; 29 | this.txProgressBar = txProgressBar; 30 | this.addressEntry = addressEntry; 31 | this.txToggleButton = txToggleButton; 32 | this.channel = 11; 33 | } 34 | 35 | public boolean isRunning() { 36 | return running; 37 | } 38 | 39 | public void updateChannel(int channel) { 40 | this.channel = channel; 41 | } 42 | @Override 43 | public void run() { 44 | /* For every packet in the list, we transmit it, then the thread is automatically stopped */ 45 | running = true; 46 | for (int i=0;i fields = esbDissector.getFields(); 58 | for (int i = 0; i < fields.size(); i++) { 59 | Chip newChip = (Chip) inflater.inflate(R.layout.chip_field_entry, chipGroup, false); 60 | newChip.setText(fields.get(i)); 61 | newChip.setCloseIconVisible(false); 62 | newChip.setCheckedIconVisible(false); 63 | newChip.setCheckable(false); 64 | chipGroup.addView(newChip); 65 | scrollView.post(new Runnable() { 66 | public void run() { 67 | scrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 68 | } 69 | }); 70 | } 71 | } 72 | @Override 73 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 74 | View v = inflater.inflate(R.layout.fragment_visualize_packet, container, false); 75 | packetContent = getArguments().getString("PacketData"); 76 | 77 | ChipGroup chipGroup = v.findViewById(R.id.packet_visualizer_chipgroup); 78 | TextView packetData = v.findViewById(R.id.packet_visualizer_textentry); 79 | HorizontalScrollView scrollView = v.findViewById(R.id.packet_visualizer_fields_scrollview); 80 | Button closeButton = v.findViewById(R.id.packet_visualizer_close_button); 81 | EsbVisualizeDialogFragment currentFragment = this; 82 | 83 | packetData.setText(packetContent); 84 | closeButton.setOnClickListener(new View.OnClickListener() { 85 | @Override 86 | public void onClick(View view) { 87 | currentFragment.dismiss(); 88 | } 89 | }); 90 | 91 | Button addButton = v.findViewById(R.id.packet_visualizer_add_to_tx_button); 92 | addButton.setOnClickListener(new View.OnClickListener() { 93 | @Override 94 | public void onClick(View v) { 95 | String currentText = packetData.getText().toString(); 96 | if (currentText.length() % 2 == 0) { 97 | /* Adds the packet to the TX list */ 98 | byte[] esbFrame = Dissector.hexToBytes(currentText); 99 | String description = EsbDissector.extractPacketTypeFromFrame(esbFrame); 100 | if (description.equals("")) description = "Unknown packet"; 101 | EsbBus.getInstance().publish(new PacketItemData(esbFrame,0x06, description,"")); 102 | Toast.makeText(v.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 103 | currentFragment.dismiss(); 104 | } 105 | } 106 | }); 107 | EsbDissector esbDissector = new EsbDissector(packetData.getText().toString()); 108 | updateDissectorView(chipGroup,esbDissector, inflater, scrollView, packetData.getText()); 109 | 110 | return v; 111 | } 112 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartBus.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import laas.rcayre.radiosploit.PacketItemData; 4 | 5 | import io.reactivex.subjects.PublishSubject; 6 | import io.reactivex.Observable; 7 | 8 | public class MosartBus { 9 | /* This class is used to exchange information between two Mosart fragments (RX > TX, used to add a received packet to the TX list) */ 10 | 11 | /* Singleton implementation */ 12 | private static MosartBus mInstance; 13 | public static MosartBus getInstance() { 14 | if (mInstance == null) { 15 | mInstance = new MosartBus(); 16 | } 17 | return mInstance; 18 | } 19 | private MosartBus() { 20 | } 21 | private PublishSubject publisher = PublishSubject.create(); 22 | 23 | void publish(PacketItemData packet) { 24 | publisher.onNext(packet); 25 | } 26 | Observable listen() { 27 | return publisher; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartDevice.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import laas.rcayre.radiosploit.dissectors.MosartDissector; 4 | 5 | public class MosartDevice { 6 | /* This class represents a Mosart Device */ 7 | private int channel; 8 | private String address; 9 | private String description; 10 | public MosartDevice(int channel, String address, String description) { 11 | this.channel = channel; 12 | this.address = address; 13 | this.description = description; 14 | } 15 | 16 | public MosartDevice(int channel, byte[] mosartFrame) { 17 | this.channel = channel; 18 | this.address = MosartDissector.extractAddressFromFrame(mosartFrame); 19 | this.description = MosartDissector.extractDeviceTypeFromFrame(mosartFrame); 20 | } 21 | 22 | public int getChannel() { 23 | return this.channel; 24 | } 25 | public String getDescription() { 26 | return this.description; 27 | } 28 | 29 | public String getAddress() { 30 | return this.address; 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartDeviceBus.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import laas.rcayre.radiosploit.PacketItemData; 4 | 5 | import io.reactivex.subjects.PublishSubject; 6 | import io.reactivex.Observable; 7 | 8 | public class MosartDeviceBus { 9 | /* This class is used to exchange information between two Zigbee fragments (Scan > RX / Keylogger, used to select a scanned device) */ 10 | 11 | /* Singleton implementation */ 12 | private static MosartDeviceBus mInstance; 13 | public static MosartDeviceBus getInstance() { 14 | if (mInstance == null) { 15 | mInstance = new MosartDeviceBus(); 16 | } 17 | return mInstance; 18 | } 19 | private MosartDeviceBus() { 20 | } 21 | private PublishSubject publisher = PublishSubject.create(); 22 | 23 | void publish(PacketItemData packet) { 24 | publisher.onNext(packet); 25 | } 26 | Observable listen() { 27 | return publisher; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartDevicePacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import android.view.View; 4 | import android.widget.Toast; 5 | 6 | import laas.rcayre.radiosploit.PacketItemData; 7 | import laas.rcayre.radiosploit.PacketListAdapter; 8 | 9 | import java.util.ArrayList; 10 | 11 | public class MosartDevicePacketListAdapter extends PacketListAdapter { 12 | /* Mosart Device Packet List Adapter, represents a device PacketListData*/ 13 | public MosartDevicePacketListAdapter(ArrayList list) {super(list);} 14 | @Override 15 | public void onItemClick(View view, PacketItemData itemData, int position) { 16 | } 17 | 18 | @Override 19 | public void onItemLongClick(View view, PacketItemData itemData, int position) { 20 | /* If a long click is detected on the device, automatically selects it in Keylogger / RX fragment */ 21 | MosartDeviceBus.getInstance().publish(itemData); 22 | Toast.makeText(view.getContext(),"Address selected", Toast.LENGTH_LONG).show(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartDevicesList.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import laas.rcayre.radiosploit.MainActivity; 4 | import laas.rcayre.radiosploit.PacketItemData; 5 | import laas.rcayre.radiosploit.PacketListAdapter; 6 | import laas.rcayre.radiosploit.dissectors.Dissector; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class MosartDevicesList { 11 | /* This class is a representation of a Mosart Devices List*/ 12 | private PacketListAdapter mosartDeviceListAdapter; 13 | private MainActivity activity; 14 | ArrayList deviceList = new ArrayList(); 15 | public MosartDevicesList(MainActivity activity, PacketListAdapter adapter) { 16 | this.mosartDeviceListAdapter = adapter; 17 | this.activity = activity; 18 | } 19 | public void clear() { 20 | /* This method clears the list */ 21 | this.deviceList.clear(); 22 | this.mosartDeviceListAdapter.resetData(); 23 | } 24 | public void addDevice(MosartDevice device) { 25 | /* This method allows to add a device if it is not already known */ 26 | boolean found = false; 27 | for (int i=0;i getInputObserver() { 104 | return new Observer() { 105 | @Override public void onSubscribe(Disposable d) { 106 | } 107 | @Override public void onNext(PacketItemData s) { 108 | int channel = Integer.parseInt(s.getStatus().substring(8,10)); 109 | mosartKeyloggerAddress.setText(s.getFormattedContent()); 110 | mosartKeyloggerChannelSlider.setValue(channel); 111 | mosartKeyloggerChannelLabel.setText("CH: "+channel); 112 | } 113 | @Override public void onError(Throwable e) { 114 | } 115 | @Override public void onComplete() { 116 | } 117 | }; 118 | } 119 | 120 | 121 | public void setUserVisibleHint(boolean visible) { 122 | super.setUserVisibleHint(visible); 123 | if (!visible) { 124 | /* If the fragment is hidden, we have to stop the thread */ 125 | if (mosartKeyloggerThread != null && mosartKeyloggerThread.isRunning()) { 126 | mosartKeyloggerThread.stop(); 127 | byte[] address = {0x00, 0x00, 0x00, 0x00}; 128 | hciInterface.configureMosartKeylogger(false, (int) (mosartKeyloggerChannelSlider.getValue()), address); 129 | startKeyloggerToggleButton.setChecked(false); 130 | } 131 | } 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartKeyloggerThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import android.widget.EditText; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketItemData; 8 | 9 | public class MosartKeyloggerThread implements Runnable { 10 | /* This thread handles the keylogger packets and modify the Keystroke Visualizer */ 11 | private HciInterface hciInterface; 12 | private MainActivity activity; 13 | private EditText visualizer; 14 | private boolean running; 15 | 16 | public MosartKeyloggerThread(MainActivity activity, HciInterface hciInterface,EditText visualizer) { 17 | this.hciInterface = hciInterface; 18 | this.activity = activity; 19 | this.visualizer = visualizer; 20 | } 21 | 22 | public boolean isRunning() { 23 | return running; 24 | } 25 | @Override 26 | public void run() { 27 | running = true; 28 | PacketItemData currentPacket; 29 | 30 | while (running) { 31 | currentPacket = this.hciInterface.nextPacket(); 32 | if (currentPacket != null) { 33 | PacketItemData finalCurrentPacket = currentPacket; 34 | // When a keylogger packet is received, adds the corresponding key to the Keystroke Visualizer 35 | activity.runOnUiThread( 36 | new Runnable() { 37 | @Override 38 | public void run() { 39 | if (finalCurrentPacket.getDescription() != "*") { // Ignore key release 40 | visualizer.setText(visualizer.getText() + finalCurrentPacket.getDescription()); 41 | } 42 | } 43 | } 44 | ); 45 | 46 | } 47 | } 48 | } 49 | 50 | public void stop() { 51 | running = false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartRxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import android.view.View; 4 | import android.widget.Toast; 5 | 6 | import androidx.fragment.app.DialogFragment; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentTransaction; 9 | 10 | import laas.rcayre.radiosploit.PacketItemData; 11 | import laas.rcayre.radiosploit.PacketListAdapter; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class MosartRxPacketListAdapter extends PacketListAdapter { 16 | /* Mosart Rx Packet List adapter, allowing to react to click events */ 17 | private Fragment rxFragment; 18 | public MosartRxPacketListAdapter(ArrayList list, Fragment rxFragment) { 19 | super(list); 20 | this.rxFragment = rxFragment; 21 | } 22 | 23 | public void onItemClick(View view, PacketItemData item, int position) { 24 | /* When a short click is detected, opens the visualizer with the corresponding packet */ 25 | FragmentTransaction ft = this.rxFragment.getParentFragmentManager().beginTransaction(); 26 | Fragment prev = this.rxFragment.getParentFragmentManager().findFragmentByTag("MosartVisualizeDialog"); 27 | if (prev != null) { 28 | ft.remove(prev); 29 | } 30 | ft.addToBackStack(null); 31 | DialogFragment dialogFragment = MosartVisualizeDialogFragment.newInstance(item.getFormattedContent()); 32 | dialogFragment.setTargetFragment(this.rxFragment,0); 33 | dialogFragment.show(ft, "MosartVisualizeDialog"); 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | /* When a long click is detected, transmits the packet to the TX fragment */ 37 | MosartBus.getInstance().publish(new PacketItemData(item.getContent(),0x04, item.getDescription(),"")); 38 | Toast.makeText(view.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartRxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketListAdapter; 8 | import laas.rcayre.radiosploit.PacketItemData; 9 | 10 | public class MosartRxThread implements Runnable { 11 | /* Mosart RX Thread, receives Mosart packets and populates the packet's list */ 12 | private PacketListAdapter mosartPacketListAdapter; 13 | private HciInterface hciInterface; 14 | private MainActivity activity; 15 | private RecyclerView packetView; 16 | private boolean running; 17 | 18 | public MosartRxThread(MainActivity activity, PacketListAdapter mosartPacketListAdapter, HciInterface hciInterface,RecyclerView packetView) { 19 | this.mosartPacketListAdapter = mosartPacketListAdapter; 20 | this.hciInterface = hciInterface; 21 | this.activity = activity; 22 | this.packetView = packetView; 23 | } 24 | 25 | public boolean isRunning() { 26 | return running; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | running = true; 32 | while (running) { 33 | PacketItemData packet = this.hciInterface.nextPacket(); 34 | /* When we receive a packet ... */ 35 | if (packet != null) { 36 | activity.runOnUiThread(new Runnable() { 37 | @Override 38 | public void run() { 39 | // Adds it to the list 40 | mosartPacketListAdapter.addNewData(packet); 41 | // Scrolls to the last packet 42 | packetView.scrollToPosition(mosartPacketListAdapter.getItemCount()-1); 43 | } 44 | }); 45 | 46 | 47 | } 48 | } 49 | } 50 | 51 | public void stop() { 52 | running = false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartScanFragment.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.ProgressBar; 10 | import android.widget.ToggleButton; 11 | 12 | import androidx.fragment.app.Fragment; 13 | import androidx.recyclerview.widget.LinearLayoutManager; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import laas.rcayre.radiosploit.HciInterface; 17 | import laas.rcayre.radiosploit.MainActivity; 18 | import laas.rcayre.radiosploit.PacketItemData; 19 | import laas.rcayre.radiosploit.PacketListAdapter; 20 | import com.example.radiosploit.R; 21 | 22 | import java.util.ArrayList; 23 | 24 | public class MosartScanFragment extends Fragment { 25 | /* This fragment allows to scan Mosart devices */ 26 | private MosartScanThread mosartScanThread; 27 | private ToggleButton mosartScanToggleButton; 28 | public MosartScanFragment() { 29 | } 30 | 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | } 35 | 36 | 37 | @Override 38 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 39 | Bundle savedInstanceState) { 40 | /* Gets the views */ 41 | View root = inflater.inflate(R.layout.fragment_mosart_scan, container, false); 42 | mosartScanToggleButton = root.findViewById(R.id.mosart_scan_toggle_button); 43 | RecyclerView deviceView = root.findViewById(R.id.mosart_scan_device_view); 44 | ProgressBar progressBar = root.findViewById(R.id.mosart_scan_progressbar); 45 | ArrayList packetList = new ArrayList(); 46 | PacketListAdapter adapter = new MosartDevicePacketListAdapter(packetList); 47 | deviceView.setHasFixedSize(true); 48 | deviceView.setLayoutManager(new LinearLayoutManager(root.getContext())); 49 | deviceView.setAdapter(adapter); 50 | 51 | HciInterface hciInterface = ((MainActivity)getActivity()).getHciInterface(); 52 | mosartScanThread = new MosartScanThread((MainActivity)getActivity(),hciInterface,adapter, progressBar); 53 | Button mosartScanResetButton = root.findViewById(R.id.mosart_scan_reset_button); 54 | 55 | /* Configures the listeners */ 56 | mosartScanResetButton.setOnClickListener(new View.OnClickListener() { 57 | @Override 58 | public void onClick(View v) { 59 | mosartScanThread.getDevicesList().clear(); 60 | } 61 | }); 62 | 63 | mosartScanToggleButton.setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View arg0) { 66 | //int channel = (int)(zigbeeeChannelSlider.getValue()); 67 | if(mosartScanToggleButton.isChecked()){ 68 | Thread mosartThread = new Thread(mosartScanThread); 69 | mosartThread.start(); 70 | } 71 | else { 72 | mosartScanThread.stop(); 73 | } 74 | }}); 75 | 76 | return root; 77 | } 78 | public void setUserVisibleHint(boolean visible) { 79 | super.setUserVisibleHint(visible); 80 | if (!visible) { 81 | /* When the fragment is hidden, stops the thread ! */ 82 | if (mosartScanThread != null && mosartScanThread.isRunning()) { 83 | mosartScanToggleButton.setChecked(false); 84 | mosartScanThread.stop(); 85 | } 86 | } 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartScanThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import android.widget.ProgressBar; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketItemData; 8 | import laas.rcayre.radiosploit.PacketListAdapter; 9 | import laas.rcayre.radiosploit.dissectors.MosartDissector; 10 | 11 | public class MosartScanThread implements Runnable { 12 | /* This class implements the Mosart Scan thread */ 13 | private HciInterface hciInterface; 14 | private MainActivity activity; 15 | private MosartDevicesList devicesList; 16 | private ProgressBar progressBar; 17 | 18 | private boolean running; 19 | 20 | public MosartScanThread(MainActivity activity, HciInterface hciInterface, PacketListAdapter adapter, ProgressBar progressBar) { 21 | this.hciInterface = hciInterface; 22 | this.activity = activity; 23 | this.devicesList = new MosartDevicesList(activity, adapter); 24 | this.progressBar = progressBar; 25 | } 26 | 27 | public boolean isRunning() { 28 | return running; 29 | } 30 | 31 | public MosartDevicesList getDevicesList() { 32 | return this.devicesList; 33 | } 34 | @Override 35 | public void run() { 36 | running = true; 37 | try { 38 | // Ok, ugly algorithm incoming. 39 | 40 | int channel = 2; 41 | this.progressBar.setMax(82); 42 | while (running) { 43 | // We explore every channel during 250ms, and we allow dongle frames (they are frequently transmitted) 44 | this.hciInterface.configureMosartScan(true, channel, true); 45 | Thread.sleep(250); 46 | PacketItemData currentPacket; 47 | this.progressBar.setProgress(channel-2); 48 | boolean dongleFound = false; 49 | do { 50 | currentPacket = this.hciInterface.nextPacket(); 51 | if (currentPacket != null && MosartDissector.extractDeviceTypeFromFrame(currentPacket.getContent()).equals("Dongle")) { 52 | dongleFound = true; 53 | devicesList.addDevice(channel,currentPacket.getContent()); 54 | } 55 | } while(currentPacket != null); 56 | 57 | // If we found dongle frames, it's probably an used channel : 58 | if (dongleFound) { 59 | // We stay on the same channel during 5 more seconds, but we ignore dongle frames 60 | this.hciInterface.configureMosartScan(true, channel, false); 61 | Thread.sleep(5000); 62 | do { 63 | currentPacket = this.hciInterface.nextPacket(); 64 | if (currentPacket != null) { 65 | // If we found other devices here, we add them to the list 66 | devicesList.addDevice(channel,currentPacket.getContent()); 67 | } 68 | } while(currentPacket != null); 69 | 70 | } 71 | 72 | if (channel < 80) channel++; 73 | else channel = 2; 74 | } 75 | } 76 | catch (InterruptedException e) { 77 | running = false; 78 | } 79 | } 80 | 81 | public void stop() { 82 | running = false; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartTabAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.fragment.app.Fragment; 5 | import androidx.fragment.app.FragmentManager; 6 | import androidx.fragment.app.FragmentPagerAdapter; 7 | 8 | 9 | public class MosartTabAdapter extends FragmentPagerAdapter { 10 | /* This class implements the MosartTabAdapter. We have four tabs: Scan (MosartScanFragment), KeyLogger (MosartKeyloggerFragment), RX (MosartRxFragment) and TX (MosartTxFragment)*/ 11 | private String[] tabs_name = { "Scan", "Key logger", "RX", "TX" }; 12 | public MosartTabAdapter(FragmentManager fm) { 13 | super(fm); 14 | } 15 | @Override 16 | public Fragment getItem(int index) { 17 | 18 | switch (index) { 19 | case 0: 20 | return new MosartScanFragment(); 21 | case 1: 22 | return new MosartKeyloggerFragment(); 23 | case 2: 24 | return new MosartRxFragment(); 25 | case 3: 26 | return new MosartTxFragment(); 27 | } 28 | 29 | return null; 30 | } 31 | @Nullable 32 | @Override 33 | public CharSequence getPageTitle(int position) { 34 | return tabs_name[position]; 35 | } 36 | 37 | @Override 38 | public int getCount() { 39 | return 4; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartTxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | import android.view.View; 4 | 5 | import androidx.fragment.app.DialogFragment; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.fragment.app.FragmentTransaction; 8 | 9 | import laas.rcayre.radiosploit.PacketItemData; 10 | import laas.rcayre.radiosploit.PacketListAdapter; 11 | 12 | import java.util.ArrayList; 13 | 14 | public class MosartTxPacketListAdapter extends PacketListAdapter { 15 | /* This class implements Mosart Tx Packet List Adapter, allowing to react to click events */ 16 | private MosartTxFragment txFragment; 17 | public MosartTxPacketListAdapter(ArrayList list, MosartTxFragment txFragment) { 18 | super(list); 19 | this.txFragment = txFragment; 20 | } 21 | 22 | public void onItemClick(View view, PacketItemData item, int position) { 23 | /* A short click is detected, opens an editor dialog */ 24 | FragmentTransaction ft = this.txFragment.getParentFragmentManager().beginTransaction(); 25 | Fragment prev = this.txFragment.getParentFragmentManager().findFragmentByTag("MosartEditDialog"); 26 | if (prev != null) { 27 | ft.remove(prev); 28 | } 29 | ft.addToBackStack(null); 30 | DialogFragment dialogFragment = MosartEditDialogFragment.newInstance(position,item.getFormattedContent()); 31 | dialogFragment.setTargetFragment( this.txFragment,0); 32 | dialogFragment.show(ft, "MosartEditDialog"); 33 | 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/mosart/MosartTxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.mosart; 2 | 3 | 4 | import android.widget.ProgressBar; 5 | import android.widget.ToggleButton; 6 | 7 | import laas.rcayre.radiosploit.HciInterface; 8 | import laas.rcayre.radiosploit.MainActivity; 9 | import laas.rcayre.radiosploit.PacketListAdapter; 10 | 11 | public class MosartTxThread implements Runnable { 12 | /* This class implements the Mosart TX Thread*/ 13 | private PacketListAdapter mosartPacketListAdapter; 14 | private HciInterface hciInterface; 15 | private ProgressBar txProgressBar; 16 | private ToggleButton txToggleButton; 17 | private MainActivity mainActivity; 18 | private int channel; 19 | 20 | private boolean running; 21 | 22 | public MosartTxThread(MainActivity mainActivity,PacketListAdapter mosartPacketListAdapter, HciInterface hciInterface, ProgressBar txProgressBar, ToggleButton txToggleButton) { 23 | this.mainActivity = mainActivity; 24 | this.mosartPacketListAdapter = mosartPacketListAdapter; 25 | this.hciInterface = hciInterface; 26 | this.txProgressBar = txProgressBar; 27 | this.txToggleButton = txToggleButton; 28 | this.channel = 11; 29 | } 30 | 31 | public boolean isRunning() { 32 | return running; 33 | } 34 | 35 | public void updateChannel(int channel) { 36 | this.channel = channel; 37 | } 38 | @Override 39 | public void run() { 40 | /* Transmits every packets present in the list, then stops the thread */ 41 | running = true; 42 | for (int i=0;i fields = mosartDissector.getFields(); 59 | for (int i = 0; i < fields.size(); i++) { 60 | Chip newChip = (Chip) inflater.inflate(R.layout.chip_field_entry, chipGroup, false); 61 | newChip.setText(fields.get(i)); 62 | newChip.setCloseIconVisible(false); 63 | newChip.setCheckedIconVisible(false); 64 | newChip.setCheckable(false); 65 | chipGroup.addView(newChip); 66 | scrollView.post(new Runnable() { 67 | public void run() { 68 | scrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 69 | } 70 | }); 71 | } 72 | } 73 | @Override 74 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 75 | View v = inflater.inflate(R.layout.fragment_visualize_packet, container, false); 76 | packetContent = getArguments().getString("PacketData"); 77 | 78 | ChipGroup chipGroup = v.findViewById(R.id.packet_visualizer_chipgroup); 79 | TextView packetData = v.findViewById(R.id.packet_visualizer_textentry); 80 | HorizontalScrollView scrollView = v.findViewById(R.id.packet_visualizer_fields_scrollview); 81 | Button closeButton = v.findViewById(R.id.packet_visualizer_close_button); 82 | MosartVisualizeDialogFragment currentFragment = this; 83 | 84 | packetData.setText(packetContent); 85 | closeButton.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View view) { 88 | currentFragment.dismiss(); 89 | } 90 | }); 91 | 92 | Button addButton = v.findViewById(R.id.packet_visualizer_add_to_tx_button); 93 | addButton.setOnClickListener(new View.OnClickListener() { 94 | @Override 95 | public void onClick(View v) { 96 | String currentText = packetData.getText().toString(); 97 | if (currentText.length() % 2 == 0) { 98 | byte[] mosartFrame = Dissector.hexToBytes(currentText); 99 | MosartBus.getInstance().publish(new PacketItemData(mosartFrame,0x04, (mosartFrame.length > 2 ? MosartDissector.extractPacketTypeFromFrame(Arrays.copyOfRange(mosartFrame,2,mosartFrame.length)) : "Unknown packet"),"")); 100 | Toast.makeText(v.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 101 | currentFragment.dismiss(); 102 | } 103 | } 104 | }); 105 | MosartDissector mosartDissector = new MosartDissector(packetData.getText().toString()); 106 | updateDissectorView(chipGroup,mosartDissector, inflater, scrollView, packetData.getText()); 107 | 108 | return v; 109 | } 110 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeBus.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import laas.rcayre.radiosploit.PacketItemData; 4 | 5 | import io.reactivex.subjects.PublishSubject; 6 | import io.reactivex.Observable; 7 | 8 | public class ZigbeeBus { 9 | /* This class is used to exchange information between two Zigbee fragments (RX > TX, used to add a received packet to the TX list) */ 10 | 11 | /* Singleton implementation */ 12 | private static ZigbeeBus mInstance; 13 | public static ZigbeeBus getInstance() { 14 | if (mInstance == null) { 15 | mInstance = new ZigbeeBus(); 16 | } 17 | return mInstance; 18 | } 19 | private ZigbeeBus() { 20 | } 21 | 22 | 23 | private PublishSubject publisher = PublishSubject.create(); 24 | 25 | void publish(PacketItemData packet) { 26 | /* Publish a packet */ 27 | publisher.onNext(packet); 28 | } 29 | 30 | 31 | Observable listen() { 32 | /* Returns an Observable */ 33 | return publisher; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeFragment.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.fragment.app.Fragment; 10 | import androidx.viewpager.widget.ViewPager; 11 | 12 | import com.example.radiosploit.R; 13 | import com.google.android.material.tabs.TabLayout; 14 | 15 | public class ZigbeeFragment extends Fragment { 16 | /* Zigbee Fragment, it mainly instantiates the tabs */ 17 | public View onCreateView(@NonNull LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) { 18 | View root = inflater.inflate(R.layout.fragment_zigbee, container, false); 19 | ZigbeeTabAdapter tabAdapter = new ZigbeeTabAdapter(getChildFragmentManager()); 20 | ViewPager viewPager = root.findViewById(R.id.zigbee_view_pager); 21 | viewPager.setAdapter(tabAdapter); 22 | TabLayout tabs = root.findViewById(R.id.zigbee_tabs); 23 | tabs.setupWithViewPager(viewPager); 24 | return root; 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeRxFragment.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.recyclerview.widget.LinearLayoutManager; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Button; 14 | import android.widget.CompoundButton; 15 | import android.widget.Switch; 16 | import android.widget.TextView; 17 | import android.widget.ToggleButton; 18 | 19 | import laas.rcayre.radiosploit.HciInterface; 20 | import laas.rcayre.radiosploit.MainActivity; 21 | import laas.rcayre.radiosploit.PacketListAdapter; 22 | import com.example.radiosploit.R; 23 | import laas.rcayre.radiosploit.PacketItemData; 24 | import com.google.android.material.slider.Slider; 25 | 26 | import java.util.ArrayList; 27 | 28 | public class ZigbeeRxFragment extends Fragment { 29 | /* Zigbee RX Fragment, used to receive Zigbee packets */ 30 | private ZigbeeRxThread zigbeeRxThread; 31 | private Slider zigbeeChannelSlider; 32 | private HciInterface hciInterface; 33 | private ToggleButton startRxToggleButton; 34 | public ZigbeeRxFragment() { 35 | } 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | } 41 | 42 | 43 | @Override 44 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 45 | Bundle savedInstanceState) { 46 | 47 | View root = inflater.inflate(R.layout.fragment_zigbee_rx, container, false); 48 | zigbeeChannelSlider = (Slider)(root.findViewById(R.id.zigbee_channel_slider)); 49 | TextView zigbeeChannelLabel = (TextView) (root.findViewById(R.id.zigbee_channel_label)); 50 | startRxToggleButton = (ToggleButton) root.findViewById(R.id.zigbee_rx_toggle_button); 51 | Button zigbeeResetButton = root.findViewById(R.id.zigbee_reset_button); 52 | Switch zigbeeFcsSwitch = root.findViewById(R.id.zigbee_fcs_check); 53 | RecyclerView packetView = root.findViewById(R.id.zigbee_rx_packet_view); 54 | 55 | hciInterface = ((MainActivity)getActivity()).getHciInterface(); 56 | 57 | zigbeeChannelSlider.addOnChangeListener(new Slider.OnChangeListener() { 58 | @Override 59 | public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { 60 | zigbeeChannelLabel.setText("CH:" + String.valueOf((int) (value))); 61 | if (startRxToggleButton.isChecked()) { 62 | hciInterface.configureZigbeeRx(true,(int)value); 63 | } 64 | } 65 | 66 | }); 67 | ArrayList packetList = new ArrayList(); 68 | PacketListAdapter adapter = new ZigbeeRxPacketListAdapter(packetList,this); 69 | packetView.setHasFixedSize(true); 70 | packetView.setLayoutManager(new LinearLayoutManager(root.getContext())); 71 | packetView.setAdapter(adapter); 72 | 73 | 74 | zigbeeRxThread = new ZigbeeRxThread((MainActivity)getActivity(),adapter,hciInterface,zigbeeFcsSwitch.isChecked(),packetView); 75 | startRxToggleButton.setOnClickListener(new View.OnClickListener() { 76 | @Override 77 | public void onClick(View arg0) { 78 | int channel = (int)(zigbeeChannelSlider.getValue()); 79 | if(startRxToggleButton.isChecked()){ 80 | hciInterface.configureZigbeeRx(true,channel); 81 | Thread zigbeeThread = new Thread(zigbeeRxThread); 82 | zigbeeThread.start(); 83 | } 84 | else { 85 | hciInterface.configureZigbeeRx(false,channel); 86 | zigbeeRxThread.stop(); 87 | } 88 | }}); 89 | zigbeeResetButton.setOnClickListener(new View.OnClickListener() { 90 | @Override 91 | public void onClick(View arg0) { 92 | adapter.resetData(); 93 | } 94 | }); 95 | 96 | zigbeeFcsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 97 | @Override 98 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 99 | zigbeeRxThread.updateCheckFCS(isChecked); 100 | } 101 | }); 102 | 103 | return root; 104 | 105 | } 106 | public void setUserVisibleHint(boolean visible) { 107 | super.setUserVisibleHint(visible); 108 | if (!visible) { 109 | /* If the fragment is hidden, we have to stop the thread if it is running */ 110 | if (zigbeeRxThread != null && zigbeeRxThread.isRunning()) { 111 | int channel = (int)(zigbeeChannelSlider.getValue()); 112 | hciInterface.configureZigbeeRx(false,channel); 113 | startRxToggleButton.setChecked(false); 114 | zigbeeRxThread.stop(); 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeRxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import android.view.View; 4 | import android.widget.Toast; 5 | 6 | import androidx.fragment.app.DialogFragment; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentTransaction; 9 | 10 | import laas.rcayre.radiosploit.PacketItemData; 11 | import laas.rcayre.radiosploit.PacketListAdapter; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class ZigbeeRxPacketListAdapter extends PacketListAdapter { 16 | /* Zigbee RX packet list adapter */ 17 | private Fragment rxFragment; 18 | public ZigbeeRxPacketListAdapter(ArrayList list, Fragment rxFragment) { 19 | super(list); 20 | this.rxFragment = rxFragment; 21 | } 22 | 23 | public void onItemClick(View view, PacketItemData item, int position) { 24 | /* If a short click is detected, opens the packet in the Visualizer */ 25 | FragmentTransaction ft = this.rxFragment.getParentFragmentManager().beginTransaction(); 26 | Fragment prev = this.rxFragment.getParentFragmentManager().findFragmentByTag("ZigbeeVisualizeDialog"); 27 | if (prev != null) { 28 | ft.remove(prev); 29 | } 30 | ft.addToBackStack(null); 31 | DialogFragment dialogFragment = ZigbeeVisualizeDialogFragment.newInstance(item.getFormattedContent()); 32 | dialogFragment.setTargetFragment(this.rxFragment,0); 33 | dialogFragment.show(ft, "ZigbeeVisualizeDialog"); 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | /* If a long click is detected, add the packet to the TX list */ 37 | ZigbeeBus.getInstance().publish(new PacketItemData(item.getContent(),0x01, item.getDescription(),"")); 38 | Toast.makeText(view.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeRxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | 5 | import laas.rcayre.radiosploit.HciInterface; 6 | import laas.rcayre.radiosploit.MainActivity; 7 | import laas.rcayre.radiosploit.PacketListAdapter; 8 | import laas.rcayre.radiosploit.PacketItemData; 9 | import laas.rcayre.radiosploit.dissectors.ZigbeeDissector; 10 | 11 | public class ZigbeeRxThread implements Runnable { 12 | /* Zigbee RX thread, allowing to get the packets from the HCI Interface and add it to the list */ 13 | private PacketListAdapter zigbeePacketListAdapter; 14 | private HciInterface hciInterface; 15 | private MainActivity activity; 16 | private RecyclerView packetView; 17 | private boolean checkFCS; 18 | private boolean running; 19 | 20 | public ZigbeeRxThread(MainActivity activity, PacketListAdapter zigbeePacketListAdapter, HciInterface hciInterface,boolean checkFCS,RecyclerView packetView) { 21 | this.zigbeePacketListAdapter = zigbeePacketListAdapter; 22 | this.hciInterface = hciInterface; 23 | this.activity = activity; 24 | this.checkFCS = checkFCS; 25 | this.packetView = packetView; 26 | } 27 | 28 | public boolean isRunning() { 29 | return running; 30 | } 31 | 32 | public void updateCheckFCS(boolean checkFCS) { 33 | this.checkFCS = checkFCS; 34 | } 35 | @Override 36 | public void run() { 37 | running = true; 38 | while (running) { 39 | /* While the thread is running, get packets, check the FCS if needed and add it to the packet list */ 40 | PacketItemData packet = this.hciInterface.nextPacket(); 41 | 42 | if (packet != null && (!this.checkFCS || (this.checkFCS && ZigbeeDissector.checkCrc(packet.getContent())))) { 43 | activity.runOnUiThread(new Runnable() { 44 | @Override 45 | public void run() { 46 | // Add the packet 47 | zigbeePacketListAdapter.addNewData(packet); 48 | // Scroll to the last packet 49 | packetView.scrollToPosition(zigbeePacketListAdapter.getItemCount()-1); 50 | } 51 | }); 52 | 53 | 54 | } 55 | } 56 | } 57 | 58 | public void stop() { 59 | running = false; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeTabAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.fragment.app.Fragment; 5 | import androidx.fragment.app.FragmentManager; 6 | import androidx.fragment.app.FragmentPagerAdapter; 7 | 8 | public class ZigbeeTabAdapter extends FragmentPagerAdapter { 9 | /* Zigbee Tab Adapter, it exposes two tabs, Receive (ZigbeeRxFragment) and Transmit (ZigbeeTxFragment) */ 10 | private String[] tabs_name = { "Receive", "Transmit" }; 11 | public ZigbeeTabAdapter(FragmentManager fm) { 12 | super(fm); 13 | } 14 | @Override 15 | public Fragment getItem(int index) { 16 | switch (index) { 17 | case 0: 18 | return new ZigbeeRxFragment(); 19 | case 1: 20 | return new ZigbeeTxFragment(); 21 | } 22 | 23 | return null; 24 | } 25 | 26 | @Nullable 27 | @Override 28 | public CharSequence getPageTitle(int position) { 29 | return tabs_name[position]; 30 | } 31 | 32 | @Override 33 | public int getCount() { 34 | return 2; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeTxPacketListAdapter.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | import android.view.View; 4 | 5 | import androidx.fragment.app.DialogFragment; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.fragment.app.FragmentTransaction; 8 | 9 | import laas.rcayre.radiosploit.PacketItemData; 10 | import laas.rcayre.radiosploit.PacketListAdapter; 11 | 12 | import java.util.ArrayList; 13 | 14 | public class ZigbeeTxPacketListAdapter extends PacketListAdapter { 15 | /* Zigbee TX Packet List Adapter */ 16 | private ZigbeeTxFragment txFragment; 17 | public ZigbeeTxPacketListAdapter(ArrayList list, ZigbeeTxFragment txFragment) { 18 | super(list); 19 | this.txFragment = txFragment; 20 | } 21 | 22 | public void onItemClick(View view, PacketItemData item, int position) { 23 | /* If there's a short click, open the packet in the editor view */ 24 | FragmentTransaction ft = this.txFragment.getParentFragmentManager().beginTransaction(); 25 | Fragment prev = this.txFragment.getParentFragmentManager().findFragmentByTag("ZigbeeEditDialog"); 26 | if (prev != null) { 27 | ft.remove(prev); 28 | } 29 | ft.addToBackStack(null); 30 | DialogFragment dialogFragment = ZigbeeEditDialogFragment.newInstance(position,item.getFormattedContent()); 31 | dialogFragment.setTargetFragment( this.txFragment,0); 32 | dialogFragment.show(ft, "ZigbeeEditDialog"); 33 | 34 | } 35 | public void onItemLongClick(View view,PacketItemData item, int position) { 36 | 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/laas/rcayre/radiosploit/ui/zigbee/ZigbeeTxThread.java: -------------------------------------------------------------------------------- 1 | package laas.rcayre.radiosploit.ui.zigbee; 2 | 3 | 4 | import android.widget.ProgressBar; 5 | import android.widget.ToggleButton; 6 | 7 | import laas.rcayre.radiosploit.HciInterface; 8 | import laas.rcayre.radiosploit.MainActivity; 9 | import laas.rcayre.radiosploit.PacketListAdapter; 10 | 11 | public class ZigbeeTxThread implements Runnable { 12 | /* Zigbee TX thread, used to transmit Zigbee packets */ 13 | private PacketListAdapter zigbeePacketListAdapter; 14 | private HciInterface hciInterface; 15 | private ProgressBar txProgressBar; 16 | private ToggleButton txToggleButton; 17 | private MainActivity mainActivity; 18 | private int channel; 19 | 20 | private boolean running; 21 | 22 | public ZigbeeTxThread(MainActivity mainActivity,PacketListAdapter zigbeePacketListAdapter, HciInterface hciInterface, ProgressBar txProgressBar, ToggleButton txToggleButton) { 23 | this.mainActivity = mainActivity; 24 | this.zigbeePacketListAdapter = zigbeePacketListAdapter; 25 | this.hciInterface = hciInterface; 26 | this.txProgressBar = txProgressBar; 27 | this.txToggleButton = txToggleButton; 28 | this.channel = 11; 29 | } 30 | 31 | public boolean isRunning() { 32 | return running; 33 | } 34 | 35 | public void updateChannel(int channel) { 36 | this.channel = channel; 37 | } 38 | @Override 39 | public void run() { 40 | running = true; 41 | for (int i=0;i fields = zigbeeDissector.getFields(); 57 | for (int i = 0; i < fields.size(); i++) { 58 | Chip newChip = (Chip) inflater.inflate(R.layout.chip_field_entry, chipGroup, false); 59 | newChip.setText(fields.get(i)); 60 | newChip.setCloseIconVisible(false); 61 | newChip.setCheckedIconVisible(false); 62 | newChip.setCheckable(false); 63 | chipGroup.addView(newChip); 64 | scrollView.post(new Runnable() { 65 | public void run() { 66 | scrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 67 | } 68 | }); 69 | } 70 | } 71 | @Override 72 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 73 | View v = inflater.inflate(R.layout.fragment_visualize_packet, container, false); 74 | 75 | /* We get the parameters transmitted to the fragment */ 76 | packetContent = getArguments().getString("PacketData"); 77 | 78 | /* Views initialization */ 79 | ChipGroup chipGroup = v.findViewById(R.id.packet_visualizer_chipgroup); 80 | TextView packetData = v.findViewById(R.id.packet_visualizer_textentry); 81 | HorizontalScrollView scrollView = v.findViewById(R.id.packet_visualizer_fields_scrollview); 82 | Button closeButton = v.findViewById(R.id.packet_visualizer_close_button); 83 | ZigbeeVisualizeDialogFragment currentFragment = this; 84 | 85 | /* Updates packet content */ 86 | packetData.setText(packetContent); 87 | 88 | /* Manage events */ 89 | closeButton.setOnClickListener(new View.OnClickListener() { 90 | @Override 91 | public void onClick(View view) { 92 | currentFragment.dismiss(); 93 | } 94 | }); 95 | 96 | Button addButton = v.findViewById(R.id.packet_visualizer_add_to_tx_button); 97 | addButton.setOnClickListener(new View.OnClickListener() { 98 | @Override 99 | public void onClick(View v) { 100 | String currentText = packetData.getText().toString(); 101 | if (currentText.length() % 2 == 0) { 102 | ZigbeeBus.getInstance().publish(new PacketItemData(Dissector.hexToBytes(currentText),0x01, ZigbeeDissector.getZigbeePacketDescription(Dissector.hexToBytes(currentText)),"")); 103 | Toast.makeText(v.getContext(),"Packet added to TX list", Toast.LENGTH_LONG).show(); 104 | currentFragment.dismiss(); 105 | } 106 | } 107 | }); 108 | ZigbeeDissector zigbeeDissector = new ZigbeeDissector(packetData.getText().toString()); 109 | updateDissectorView(chipGroup,zigbeeDissector, inflater, scrollView, packetData.getText()); 110 | 111 | return v; 112 | } 113 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_esb.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 51 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_keyboard_dev.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mosart.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mosart2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mouse_24.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mouse_dev.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_packet.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_radiosploit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_unknown_dev.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_zigbee.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/chip_field_entry.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_edit_packet.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 20 | 21 | 28 | 29 |