Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Export your Windows Bluetooth LE keys into Linux!
#!/usr/bin/python3
"""
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')
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())
@ceres-c

This comment has been minimized.

Copy link

@ceres-c ceres-c commented May 7, 2018

I might have a dumb problem but can't fix it

python3 ./export-ble-infos.py -s <MY_PATH>
Traceback (most recent call last):
  File "./export-ble-infos.py", line 143, in <module>
    sys.exit(main())
  File "./export-ble-infos.py", line 84, in main
    reged = subprocess.Popen(["reged", "-x", options.system, '\\', options.key, out], stdout=sys.stderr)                                                     
  File "/usr/lib/python3.6/subprocess.py", line 709, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.6/subprocess.py", line 1344, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'reged': 'reged'

Do you have any suggestion?

--Update--
Nevermind, I believe there was a problem accessing the file or something like that. It has been working since I mounted my windows drive on boot with fstab on a folder accessible by all users. It might have been due to /Windows/System32/config/SYSTEM access restrictions.

@MrMoDDoM

This comment has been minimized.

Copy link

@MrMoDDoM MrMoDDoM commented Jun 13, 2018

Great code, thanks!
Please insert as dependency the package "chntpw" that provides the command "reged".
Cheers!

@mattbeau

This comment has been minimized.

Copy link

@mattbeau mattbeau commented Aug 10, 2018

This worked perfectly on Fedora 28. I created a username just to let you know how much I appreciate this code! THANK YOU!

@drekerd

This comment has been minimized.

Copy link

@drekerd drekerd commented Apr 11, 2019

Hey, this works great on Ubuntu. Thank you.
Do you have anything similar for macOS?
BR

@pocoz

This comment has been minimized.

Copy link

@pocoz pocoz commented May 2, 2019

Buddy, you made my day!
For a whole year I couldn’t connect my mouse over bluetooth. And the emergence of the second system also suffered with switching the keyboard. I'm happy!

@criess

This comment has been minimized.

Copy link

@criess criess commented May 16, 2019

Thanks you so much 🎉! Thats a very handy script. Just keep in mind you need chntpw package on your machine. I was using Centos 7 with https://centos.pkgs.org/7/nux-dextop-x86_64/chntpw-0.99.6-22.110511.el7.nux.x86_64.rpm.html

@chomamateusz

This comment has been minimized.

Copy link

@chomamateusz chomamateusz commented Jul 23, 2019

Great, it works! Thanks!

@gnysek

This comment has been minimized.

Copy link

@gnysek gnysek commented Aug 16, 2019

Replacing system. with SYSTEM helped for me (also remember to replace username, or provide whole path as argument).

@Sciroccogti

This comment has been minimized.

Copy link

@Sciroccogti Sciroccogti commented Nov 6, 2019

I really appreciate your share, but it doesn't work on my computer. Maybe that is because of the same address of mouse on my Windows and Ubuntu.

@Wahidur-Rahman

This comment has been minimized.

Copy link

@Wahidur-Rahman Wahidur-Rahman commented May 28, 2020

No CRSK key is being generated in my BTKeys.reg when running psexec so the code throws this error. Any advice?

Exporting to file '/tmp/tmpv3dr3u3e.reg'...
Exporting key 'Keys' with 2 subkeys and 0 values...
Exporting key '803253471200' with 1 subkeys and 4 values...
Exporting key 'e4def07b268e' with 0 subkeys and 9 values...
Exporting key 'd8fc930fc449' with 0 subkeys and 2 values...
Dumping 80:32:53:47:12:00/E4:DE:F0:7B:26:8E...
Traceback (most recent call last):
File "export-ble-infos.py", line 143, in
sys.exit(main())
File "export-ble-infos.py", line 133, in main
config['LocalSignatureKey']['Key'] = read_reg('CSRK', 'hex16')
File "export-ble-infos.py", line 124, in read_reg
result = read_reg_actual(key, expected_type)
File "export-ble-infos.py", line 109, in read_reg_actual
actual_type, content = dump[section]['"{}"'.format(key)].split(':', 1)
File "/home/wahid/anaconda3/lib/python3.7/configparser.py", line 1251, in getitem
raise KeyError(key)
KeyError: '"CSRK"'

@Mygod

This comment has been minimized.

Copy link
Owner Author

@Mygod Mygod commented May 29, 2020

@Wahidur-Rahman I have not tested it out but maybe @corecoding's fork could be useful to you.

@Wahidur-Rahman

This comment has been minimized.

Copy link

@Wahidur-Rahman Wahidur-Rahman commented May 29, 2020

@Wahidur-Rahman I have not tested it out but maybe @corecoding's fork could be useful to you.

Yes, that has worked, thanks!

@Mygod

This comment has been minimized.

Copy link
Owner Author

@Mygod Mygod commented May 29, 2020

@Wahidur-Rahman Thanks for confirming! I have merged the relevant changes from @corecoding's fork.

@tkscripts

This comment has been minimized.

Copy link

@tkscripts tkscripts commented Jun 1, 2020

Running into a similar error but with IRK. No CSRK is being generated in my case either.

[Manjaro ~]# python3 export-ble-infos.py
reged version 0.1 140201, (c) Petter N Hagen
Exporting to file '/tmp/tmpebcobaf_.reg'...
Exporting key 'Keys' with 1 subkeys and 0 values...
Exporting key '8878732352bb' with 1 subkeys and 3 values...
Exporting key 'c9bf519682e9' with 0 subkeys and 6 values...
Dumping bluetooth/88:78:73:23:52:BB/C9:BF:51:96:82:E9
Traceback (most recent call last):
File "export-ble-infos.py", line 140, in
sys.exit(main())
File "export-ble-infos.py", line 127, in main
config['IdentityResolvingKey']['Key'] = read_reg('IRK', 'hex16')
File "export-ble-infos.py", line 119, in read_reg
result = read_reg_actual(key, expected_type)
File "export-ble-infos.py", line 104, in read_reg_actual
actual_type, content = dump[section]['"{}"'.format(key)].split(':', 1)
File "/usr/lib/python3.8/configparser.py", line 1254, in getitem
raise KeyError(key)
KeyError: '"IRK"'

BTKeys.reg Output:

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys]

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\8878732352bb]
"2811a541d1bb"=hex:82,75,06,49,5b,52,75,c1,5a,19,fd,7a,1f,b0,79,4f
"MasterIRK"=hex:d0,03,49,6c,cf,3a,7a,9e,de,b5,8b,b7,a3,6c,43,49
"4801c570a524"=hex:14,28,5e,2a,0f,49,bf,8a,90,2b,6b,dd,20,f9,52,6b

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Keys\8878732352bb\c9bf519382e9]
"LTK"=hex:10,4a,0a,ab,fe,ae,e7,8e,1b,db,a8,ee,cb,91,2a,b2
"KeyLength"=dword:00000010
"ERand"=hex(b):
"EDIV"=dword:00000fee
"MasterIRKStatus"=dword:00000001
"AuthReq"=dword:0000002d

I did try editing the script to MasterIRK and the same error was presented KeyError being updated to MasterIRK.

@gordonrix

This comment has been minimized.

Copy link

@gordonrix gordonrix commented Jun 4, 2020

New to linux, sorry in advance.

I'm getting an error that appears to result from not being able to make the output directory for some reason. This is the error, with the output_dir variable printed out right above it

bluetooth/48:45:20:92:0F:30/F1:9F:12:F6:F0:18
Traceback (most recent call last):
File "./export-ble-infos.py", line 154, in
sys.exit(main())
File "./export-ble-infos.py", line 146, in main
os.makedirs(output_dir, exist_ok=True)
File "/usr/lib/python3.8/os.py", line 213, in makedirs
makedirs(head, exist_ok=exist_ok)
File "/usr/lib/python3.8/os.py", line 223, in makedirs
mkdir(name, mode)
OSError: [Errno 22] Invalid argument: 'bluetooth/48:45:20:92:0F:30'

Edit: Solved the issue, was trying to run the script in a directory in my Windows partition, so the colon was not allowed.

@n1vgabay

This comment has been minimized.

Copy link

@n1vgabay n1vgabay commented Jun 11, 2020

How do i run this script? on windows/linux?

I tried to run this script on visual code in windows and go this error:

Traceback (most recent call last):
File ".\export-ble-infos.py", line 153, in
sys.exit(main())
File ".\export-ble-infos.py", line 86, in main
reged = subprocess.Popen(["reged", "-x", options.system, '\', options.key, out], stdout=sys.stderr)
File "C:\Python\Python37\lib\subprocess.py", line 800, in init
restore_signals, start_new_session)
File "C:\Python\Python37\lib\subprocess.py", line 1207, in _execute_child
startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified

@n1vgabay

This comment has been minimized.

Copy link

@n1vgabay n1vgabay commented Jun 11, 2020

So, I tried to run on Linux, I guess i don't run this script properly but in any case i got error of some modules that can't be loaded.
python ./export-ble-infos.py
Traceback (most recent call last):
File "./export-ble-infos.py", line 22, in
from configparser import ConfigParser
ImportError: No module named configparser

Is it the right way to run this script?
thanks.

@Wahidur-Rahman

This comment has been minimized.

Copy link

@Wahidur-Rahman Wahidur-Rahman commented Jun 11, 2020

@n1vgabay - seems you are missing the appropriate python module. Might be that you don't have the dependency chntpw installed. Try 'sudo apt install chntpw'

if that doesn't work, try manually installing the package to python with 'pip install configparser'

@osmanbedrettin

This comment has been minimized.

Copy link

@osmanbedrettin osmanbedrettin commented Jun 20, 2020

I'm using also designer mouse but mouse cant connecting after run these script and commands. The CSRK key was blank, I added it from BTkeys. Unfortunately still not working on fedora 32.

@n1vgabay

This comment has been minimized.

Copy link

@n1vgabay n1vgabay commented Jul 13, 2020

@n1vgabay - seems you are missing the appropriate python module. Might be that you don't have the dependency chntpw installed. Try 'sudo apt install chntpw'

if that doesn't work, try manually installing the package to python with 'pip install configparser'

So i installed this package with python-pip as well, now i've got this massege:

reged version 0.1 140201, (c) Petter N Hagen
openHive(/mnt/Windows/System32/config/SYSTEM) failed: No such file or directory, trying read-only
openHive(/mnt/Windows/System32/config/SYSTEM) in fallback RO-mode failed: No such file or directory
Unable to open/read hive /mnt/Windows/System32/config/SYSTEM, exiting..

Any ideas how to solve this issue to make it work finally?

And something i wondered since i saw this script,
How should i run this script? according to this usage above.

Usage:
$ ./export-ble-infos.py # # # Do i need to add arguments when run this py script?
$ sudo bash -c 'cp -r ./bluetooth /var/lib && service bluetooth force-reload'
$ rm -r bluetooth
"""

I'm assuming that the path I see in the script isn't working.. i mout windows and linux but maybe it's not enough.
I can see my windows file through: **/media/$user/OS ... and so on ..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.