-
Star
(134)
You must be signed in to star a gist -
Fork
(33)
You must be signed in to fork a gist
-
-
Save Mygod/f390aabf53cf1406fc71166a47236ebf to your computer and use it in GitHub Desktop.
| #!/usr/bin/python3 | |
| """ | |
| Copyright 2021 Mygod | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| What is this: Export your Windows Bluetooth LE keys into Linux! | |
| Thanks to: | |
| * http://console.systems/2014/09/how-to-pair-low-energy-le-bluetooth.html | |
| * https://gist.github.com/corecoding/eac76d3da20c7e427a1848b8aed8e334/revisions#diff-6eeb0d27c24cc10680e8574f75648585 | |
| Usage: | |
| $ ./export-ble-infos.py <args> | |
| $ sudo bash -c 'cp -r ./bluetooth /var/lib && service bluetooth force-reload' | |
| $ rm -r bluetooth | |
| """ | |
| import os | |
| import shutil | |
| import subprocess | |
| import sys | |
| import tempfile | |
| from configparser import ConfigParser | |
| from optparse import OptionParser | |
| default_template = """ | |
| [General] | |
| Name=Designer Mouse | |
| Appearance=0x03c2 | |
| AddressType=static | |
| SupportedTechnologies=LE; | |
| Trusted=true | |
| Blocked=false | |
| Services=00001800-0000-1000-8000-00805f9b34fb;00001801-0000-1000-8000-00805f9b34fb;0000180a-0000-1000-8000-00805f9b34fb;0000180f-0000-1000-8000-00805f9b34fb;00001812-0000-1000-8000-00805f9b34fb; | |
| [IdentityResolvingKey] | |
| Key= | |
| [LocalSignatureKey] | |
| Key= | |
| Counter=0 | |
| Authenticated=false | |
| [LongTermKey] | |
| Key= | |
| Authenticated=0 | |
| EncSize=16 | |
| EDiv= | |
| Rand= | |
| [DeviceID] | |
| Source=2 | |
| Vendor=1118 | |
| Product=2053 | |
| Version=272 | |
| [ConnectionParameters] | |
| MinInterval=6 | |
| MaxInterval=6 | |
| Latency=60 | |
| Timeout=300 | |
| """ | |
| def main(): | |
| parser = OptionParser() | |
| parser.add_option("-v", "--verbose", action='store_true', dest='verbose') | |
| parser.add_option("-s", "--system", dest="system", metavar="FILE", | |
| default="/mnt/Windows/System32/config/SYSTEM", | |
| help="SYSTEM file in Windows. Usually at /Windows/System32/config/system.") | |
| parser.add_option("-k", "--key", dest="key", metavar="KEY", | |
| default=r"ControlSet001\Services\BTHPORT\Parameters\Keys", | |
| help="Registry key for BT. [default: %default]") | |
| parser.add_option("-o", "--output", dest="output", metavar="DIR", default="bluetooth", | |
| help="Output directory. [default: %default]") | |
| parser.add_option("-t", "--template", dest="template", metavar="FILE", help="Template file.") | |
| parser.add_option("-a", "--attributes", dest='attributes', help="Additional attributes file to be copied.") | |
| options, args = parser.parse_args() | |
| if options.template: | |
| with open(options.template) as file: | |
| template = file.read() | |
| else: | |
| template = default_template | |
| out = tempfile.mktemp(".reg") | |
| reged = subprocess.Popen(["reged", "-x", options.system, '\\', options.key, out], stdout=sys.stderr) | |
| reged.wait() | |
| if reged.returncode: | |
| return reged.returncode | |
| dump = ConfigParser() | |
| with open(out) as file: | |
| reged_out = file.read() | |
| if options.verbose: | |
| print(reged_out) | |
| dump.read_string(reged_out.split('\n', 1)[1]) | |
| os.unlink(out) | |
| for section in dump: | |
| path = section[len(options.key) + 2:].split('\\') | |
| assert not path[0] | |
| if len(path) == 3: | |
| path[1] = ':'.join([path[1][i:i + 2] for i in range(0, len(path[1]), 2)]).upper() | |
| path[2] = ':'.join([path[2][i:i + 2] for i in range(0, len(path[2]), 2)]).upper() | |
| print("Dumping {}/{}...".format(path[1], path[2])) | |
| config = ConfigParser() | |
| config.optionxform = str | |
| # See if device has been paired in Linux before | |
| existing_template = '/var/lib/bluetooth/{}/{}/info'.format(path[1], path[2]) | |
| if (os.path.exists(existing_template)): | |
| with open(existing_template) as file: | |
| config.read_string(file.read()) | |
| else: | |
| config.read_string(template) | |
| def read_reg(key, expected_type): | |
| def read_reg_actual(key, expected_type): | |
| actual_type, content = dump[section]['"{}"'.format(key)].split(':', 1) | |
| if expected_type == 'hex16': | |
| assert actual_type == 'hex' | |
| content = content.split(',') | |
| assert len(content) == 16 | |
| return ''.join(content).upper() | |
| if expected_type == 'qword': | |
| assert actual_type == 'hex(b)' | |
| content = content.split(',') | |
| assert len(content) == 8 | |
| return str(int(''.join(content[::-1]), 16)) | |
| if expected_type == 'dword': | |
| assert actual_type == expected_type | |
| return str(int(content, 16)) | |
| assert False | |
| result = read_reg_actual(key, expected_type) | |
| if options.verbose: | |
| print("{} of type {}: {}".format(key, expected_type, result)) | |
| return result | |
| config['LongTermKey']['Key'] = read_reg('LTK', 'hex16') | |
| # KeyLength ignored for now | |
| config['LongTermKey']['Rand'] = read_reg('ERand', 'qword') | |
| config['LongTermKey']['EDiv'] = read_reg('EDIV', 'dword') | |
| if '"IRK"' in dump[section]: | |
| config['IdentityResolvingKey']['Key'] = read_reg('IRK', 'hex16') | |
| if '"CSRK"' in dump[section]: | |
| config['LocalSignatureKey']['Key'] = read_reg('CSRK', 'hex16') | |
| output_dir = os.path.join(options.output, path[1], path[2]) | |
| os.makedirs(output_dir, exist_ok=True) | |
| with open(os.path.join(output_dir, 'info'), 'w') as file: | |
| config.write(file, False) | |
| if options.attributes: | |
| shutil.copyfile(options.attributes, os.path.join(output_dir, 'attributes')) | |
| if __name__ == "__main__": | |
| sys.exit(main()) |
I'm trying this and no output directory is created. Any idea what is wrong?
This is the command and console output:
steve@aragorn:~$ sudo python3 ./export-ble-infos.py -s /media/steve/BOOTCAMP/Windows/System32/config/SYSTEM
reged version 0.1 140201, (c) Petter N Hagen
Exporting to file '/tmp/tmpfkmkggfk.reg'...
Exporting key 'Keys' with 1 subkeys and 0 values...
Exporting key '5cf370809d45' with 0 subkeys and 3 values...
Should the output directory be in my current directory or somewhere else?
This used to work for me, but after upgrading to Fedora 37 (not sure if its related), the following fields appears on my
infofile:
- [RemoteSignatureKey]
- [PeripheralLongTermKey]
- [SlaveLongTermKey]
Both
[PeripheralLongTermKey]and[SlaveLongTermKey]with their own EDiv and ERand, but there's no correspondingKeyon the Windows registry. Have you guys ever had this happen?
In case someone ends up here as I did - I think PeripheralLongTermKey / SlaveLongTermKey can be left as is at least in some cases. That seemed to be the case for me (Keychron V6 Max keyboard, dualboot Windows 10 + Ubuntu 24), where switching the keys for LTK (along with Rand and EDiv) and IRK sufficed.
I've been fighting with getting this to work, but cannot for the life of me get this to run without errors. Anyone get this working with a smartphone between Win/Linux, and for a BLE Audio device between Win/Linux?
Entire steps for modern Linux/Win systems?
Arch + Wayland + KDE Plasma: I got my BLE keyboard to work:
sudo pacman -S chntpw python
bluetoothctl list
MOUNT windows partition and remmeber the /mnt/ location for it.
curl -L https://gist.githubusercontent.com/Mygod/f390aabf53cf1406fc71166a47236ebf/raw/export-ble-infos.py -o export-ble-infos.py
chmod +x export-ble-infos.py
sudo ./export-ble-infos.py -s /mnt/<YOURwindowsMNTlocation>/Windows/System32/config/SYSTEM
You should get output like this:
reged version 0.1 140201, (c) Petter N Hagen
Exporting to file '/tmp/tmpqgt8rldx.reg'...
Exporting key 'Keys' with 1 subkeys and 0 values...
Exporting key 'aabbccddee' with 2 subkeys and 4 values...
If you want you can browse this file. I had to confirm the keyboard MAC address in windows, because whenever it gets re-paired it changes the MAC. So first I copied
sudo mv /var/lib/bluetooth/<YOURbluetoothCHIPmacADDR>/<the linux address for the BLE keyboard> /var/lib/bluetooth/<YOURbluetoothCHIPmacADDR>/<whatWINDOWSwroteASthe keyboard MAC addr>
Once that was prepped, the dumped keys had somewhere to go.
sudo cp -r ./bluetooth/* /var/lib/bluetooth/
I had to turn off the keyboard, turn off my bluetooth, then
sudo systemctl restart bluetooth
Turn on bluetooth. Turn on keyboard.
Worked!
The phone and the LE Audio True Wireless IEMs are way more challenging, and I'm not sure I'll ever be able to tackle them properly. I will follow up later.
Thanks o: