├── DS4-usb-grab.zip ├── README.md ├── ds4sig.bin ├── jdm-001_soc.jpg ├── jedi_crypto-mod.py ├── jedi_crypto.py ├── jedi_tool.py └── ps4nonce.bin /DS4-usb-grab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrepl/ds4/8e4478094b49f51d14bde9b0434ad141156d1a10/DS4-usb-grab.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ds4 2 | Tools for working with DualShock 4 3 | 4 | With fw of controller, it is possible to do interesting things like: 5 | * flash custom fw to controller 6 | * learn how all aspects of controller works 7 | * implement native pairing on other host devices 8 | * present custom hardware as "official" DS4 to PS4 9 | 10 | 11 | - GodzIvan - 12 | 13 | Working ???? 14 | -------------------------------------------------------------------------------- /ds4sig.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrepl/ds4/8e4478094b49f51d14bde9b0434ad141156d1a10/ds4sig.bin -------------------------------------------------------------------------------- /jdm-001_soc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrepl/ds4/8e4478094b49f51d14bde9b0434ad141156d1a10/jdm-001_soc.jpg -------------------------------------------------------------------------------- /jedi_crypto-mod.py: -------------------------------------------------------------------------------- 1 | ''' 2 | fw file: 3 | With fw of controller, it is possible to do interesting things like: 4 | * flash custom fw to controller 5 | * learn how all aspects of controller works 6 | * implement native pairing on other host devices 7 | * present custom hardware as "official" DS4 to PS4 8 | The following code shows how to do the first stage of auth - authenticating 9 | over USB in order to have console send the bluetooth link key and host address. 10 | (C) HAXX 11 | ''' 12 | 13 | import struct 14 | import binascii 15 | from Crypto.Cipher import AES 16 | from Crypto.Hash import SHA256, CMAC 17 | from Crypto.PublicKey import RSA 18 | from Crypto.Signature import pss 19 | from Crypto.Util.number import bytes_to_long 20 | from Crypto.Random import get_random_bytes 21 | from Crypto.Math.Numbers import Integer 22 | 23 | hw_bindings = [ 24 | (0x40015fe0, 0x00000004), 25 | (0x40015fe4, 0x00000018), 26 | (0x40015fe8, 0x00000014), 27 | (0x40015fec, 0x00000000), 28 | (0x40015ff0, 0x0000000d), 29 | (0x40015ff4, 0x000000f0), 30 | (0x40015ff8, 0x00000005), 31 | (0x40015ffc, 0x000000b1), 32 | (0x4002f000, 0xf3b002c0), 33 | (0xe00fffe0, 0x00000034), 34 | (0xe00fffe4, 0x0000004a), 35 | (0xe00fffe8, 0x00000008), 36 | (0xe00fffec, 0x00000000), 37 | (0xe00ffff0, 0x0000000d), 38 | (0xe00ffff4, 0x00000010), 39 | (0xe00ffff8, 0x00000005), 40 | (0xe00ffffc, 0x000000b1), 41 | ] 42 | 43 | bldr_key_blob = bytes([0x39, 0xFF, 0x1A, 0x67, 0x2B, 0x4F, 0x99, 0xA6, 0xA1, 0xCA, 44 | 0x65, 0xC2, 0x99, 0xD6, 0x27, 0x0C, 0x7D, 0x4E, 0x1A, 0xF9, 45 | 0x10, 0x36, 0xAD, 0x6C, 0x8D, 0x20, 0xEA, 0xD1, 0xFF, 0x33, 46 | 0xD9, 0x03, 0x94, 0xFD, 0x44, 0x15, 0xB5, 0x40, 0x72, 0xD9, 47 | 0xC8, 0x3B, 0x94, 0x99, 0x43, 0x04, 0xFD, 0x49]) 48 | app_key0_blob = bytes([0x3E, 0x5C, 0x05, 0xC6, 0xAF, 0xAF, 0xAB, 0x02, 0x20, 0x3B, 49 | 0x3D, 0x18, 0x17, 0x33, 0xDD, 0xCB, 0xA9, 0x65, 0x40, 0x0F, 50 | 0xD5, 0x3A, 0x6F, 0x50, 0x17, 0x31, 0xF3, 0x86, 0x55, 0xB2, 51 | 0x08, 0x08, 0xCF, 0xB8, 0xE6, 0x18, 0x1C, 0xC9, 0x1D, 0x64, 52 | 0xC4, 0x99, 0x3B, 0x04, 0x0B, 0xEC, 0xC7, 0xB5, 0xED, 0x18, 53 | 0xA5, 0x68, 0x3A, 0x95, 0xA3, 0x38, 0xF3, 0xCA, 0x32, 0x55, 54 | 0x28, 0xA9, 0x6F, 0xCB]) 55 | app_key1_blob = bytes([0x7F, 0x81, 0x48, 0x8F, 0x32, 0x02, 0x4C, 0x6B, 0xF5, 0xD9, 56 | 0x99, 0x92, 0x87, 0x98, 0xAE, 0xC0, 0x78, 0x5F, 0xC3, 0xE6, 57 | 0x1B, 0xAF, 0x32, 0xDF, 0xA5, 0x83, 0x3F, 0x43, 0x49, 0x64, 58 | 0xCD, 0x53, 0x37, 0x52, 0x52, 0x39, 0xB1, 0x0B, 0xF8, 0x38, 59 | 0xEF, 0x29, 0xB3, 0x7E, 0xBD, 0x73, 0xD9, 0x51, 0x1E, 0xC4, 60 | 0xDF, 0xFB, 0x97, 0x25, 0xA1, 0xE9, 0xD2, 0x67, 0x89, 0x90, 61 | 0xA0, 0x3C, 0x28, 0x32]) 62 | 63 | # pubkey of the CA which signs controller keys 64 | jedi_CA_pubkey = RSA.construct((bytes_to_long(bytes([ 65 | 0x8E, 0xD7, 0xF9, 0xE4, 0xAA, 0x5C, 0xC5, 0xD2, 0x31, 0x96, 66 | 0xF0, 0xDE, 0x79, 0x7D, 0xFE, 0xAC, 0xF6, 0x3E, 0xDE, 0x7B, 67 | 0xC9, 0x67, 0x16, 0xF1, 0x3C, 0xF5, 0x2A, 0xDE, 0xF8, 0xDA, 68 | 0xCF, 0xA8, 0xE2, 0x33, 0xDC, 0x65, 0x57, 0x17, 0x34, 0x7D, 69 | 0x4C, 0x8C, 0x82, 0x6E, 0xAB, 0x90, 0x36, 0x16, 0xFF, 0x9F, 70 | 0xB8, 0xF9, 0x73, 0x36, 0x17, 0xFB, 0xD4, 0x4E, 0xC8, 0x10, 71 | 0x78, 0xAD, 0x6E, 0x24, 0xB0, 0x62, 0x61, 0x9F, 0x5A, 0x17, 72 | 0xEE, 0x2F, 0x55, 0x72, 0xB4, 0x27, 0xC0, 0x34, 0xA9, 0x49, 73 | 0x36, 0x3E, 0x86, 0xD3, 0xB2, 0x13, 0x35, 0x1F, 0x89, 0x04, 74 | 0xA4, 0x99, 0xF8, 0x62, 0x40, 0x1F, 0x4E, 0x60, 0xAC, 0x21, 75 | 0x31, 0xCD, 0x4B, 0xB9, 0xFD, 0xDF, 0xD5, 0x90, 0xC8, 0xE2, 76 | 0x2B, 0x7D, 0xF9, 0x6D, 0x01, 0x5A, 0x41, 0xC5, 0x49, 0xF3, 77 | 0xEA, 0x0D, 0xED, 0xFC, 0x32, 0xCE, 0xC3, 0x2D, 0x72, 0xC5, 78 | 0x34, 0x93, 0x4A, 0xEF, 0x3D, 0xD1, 0x2B, 0x58, 0xDB, 0x35, 79 | 0x7D, 0xD0, 0x4D, 0x9A, 0x93, 0x11, 0xA3, 0x83, 0x3F, 0xF8, 80 | 0x55, 0x7A, 0x0B, 0x85, 0xB4, 0x54, 0xCD, 0x21, 0xDA, 0xB9, 81 | 0x0D, 0x71, 0x4A, 0xEA, 0x2D, 0xEC, 0x42, 0xE6, 0xF4, 0xEF, 82 | 0x20, 0x45, 0x3C, 0xF6, 0xDB, 0xF3, 0x95, 0x4E, 0x73, 0xA8, 83 | 0x76, 0x91, 0xCF, 0xA0, 0x3F, 0x47, 0x59, 0x45, 0x5C, 0x8B, 84 | 0x96, 0xF1, 0xD0, 0xB6, 0x9D, 0xD3, 0xDD, 0x62, 0x62, 0xE9, 85 | 0x43, 0x8D, 0xCC, 0x26, 0x96, 0xCF, 0xE6, 0x4B, 0x93, 0x0C, 86 | 0x6E, 0x7D, 0x4E, 0x01, 0x51, 0xF6, 0xD1, 0xB1, 0x5D, 0x1A, 87 | 0x4B, 0xE2, 0xE6, 0x0F, 0x0B, 0x36, 0x11, 0x8C, 0x60, 0xF2, 88 | 0x53, 0xFD, 0xBC, 0xE2, 0x27, 0xA8, 0xA4, 0xC9, 0xCD, 0xF2, 89 | 0x26, 0x08, 0x58, 0x58, 0x4A, 0xB8, 0xD7, 0x1C, 0x62, 0x9C, 90 | 0xD4, 0x21, 0xEC, 0x66, 0x60, 0x59])), 0x10001)) 91 | 92 | 93 | def get_hw_binding(): 94 | binding = [] 95 | for addr, val in hw_bindings: 96 | binding.append(struct.pack(' 0: 219 | buf.append(f.read(4)) 220 | f.seek(4, 1) 221 | size -= 4 222 | return b''.join(buf) 223 | 224 | 225 | if __name__ == '__main__': 226 | flash = JediFlash('./jedi_flash-Aug_3_2013.bin') 227 | cert = flash.cert 228 | 229 | # example of what usb auth does 230 | 231 | # console gens and sends the nonce.. 232 | nonce = get_random_bytes(0x100) 233 | # controller signs it and sends back pubkey and sig 234 | jedi_sig = cert.sign(nonce) 235 | # console checks sig... 236 | pss.new(cert.key.publickey()).verify(SHA256.new(nonce), jedi_sig) 237 | 238 | print(r'\o/') 239 | 240 | # now, console will send controller bt link key + host addr 241 | # then controller must respond to bt challenges, but they are much more 242 | # simple 243 | 244 | #GodzIvan 245 | open('./jedi_PrivateKey.pem', 'wb').write(cert.key.exportKey()) 246 | open('./jedi_PrivateKey.der', 'wb').write(cert.key.exportKey("DER")) 247 | open('./jedi_PublicKey.pem', 'wb').write(cert.key.publickey().exportKey()) 248 | open('./jedi_PublicKey.der', 'wb').write(cert.key.publickey().exportKey("DER")) 249 | 250 | print(r'Test nonce') 251 | 252 | # console gens and sends the nonce.. 253 | ps4nonce = open('./ps4nonce.bin', 'rb').read() 254 | # controller signs it and sends back pubkey and sig 255 | ds4_sig = open('./ds4sig.bin', 'rb').read() 256 | # console checks sig... 257 | pss.new(cert.key.publickey()).verify(SHA256.new(ps4nonce), ds4_sig) 258 | 259 | -------------------------------------------------------------------------------- /jedi_crypto.py: -------------------------------------------------------------------------------- 1 | ''' 2 | fw file: 3 | With fw of controller, it is possible to do interesting things like: 4 | * flash custom fw to controller 5 | * learn how all aspects of controller works 6 | * implement native pairing on other host devices 7 | * present custom hardware as "official" DS4 to PS4 8 | The following code shows how to do the first stage of auth - authenticating 9 | over USB in order to have console send the bluetooth link key and host address. 10 | (C) HAXX 11 | ''' 12 | 13 | import struct 14 | import binascii 15 | from Crypto.Cipher import AES 16 | from Crypto.Hash import SHA256, CMAC 17 | from Crypto.PublicKey import RSA 18 | from Crypto.Signature import pss 19 | from Crypto.Util.number import bytes_to_long 20 | from Crypto.Random import get_random_bytes 21 | from Crypto.Math.Numbers import Integer 22 | 23 | hw_bindings = [ 24 | (0x40015fe0, 0x00000004), 25 | (0x40015fe4, 0x00000018), 26 | (0x40015fe8, 0x00000014), 27 | (0x40015fec, 0x00000000), 28 | (0x40015ff0, 0x0000000d), 29 | (0x40015ff4, 0x000000f0), 30 | (0x40015ff8, 0x00000005), 31 | (0x40015ffc, 0x000000b1), 32 | (0x4002f000, 0xf3b002c0), 33 | (0xe00fffe0, 0x00000034), 34 | (0xe00fffe4, 0x0000004a), 35 | (0xe00fffe8, 0x00000008), 36 | (0xe00fffec, 0x00000000), 37 | (0xe00ffff0, 0x0000000d), 38 | (0xe00ffff4, 0x00000010), 39 | (0xe00ffff8, 0x00000005), 40 | (0xe00ffffc, 0x000000b1), 41 | ] 42 | 43 | bldr_key_blob = bytes([0x39, 0xFF, 0x1A, 0x67, 0x2B, 0x4F, 0x99, 0xA6, 0xA1, 0xCA, 44 | 0x65, 0xC2, 0x99, 0xD6, 0x27, 0x0C, 0x7D, 0x4E, 0x1A, 0xF9, 45 | 0x10, 0x36, 0xAD, 0x6C, 0x8D, 0x20, 0xEA, 0xD1, 0xFF, 0x33, 46 | 0xD9, 0x03, 0x94, 0xFD, 0x44, 0x15, 0xB5, 0x40, 0x72, 0xD9, 47 | 0xC8, 0x3B, 0x94, 0x99, 0x43, 0x04, 0xFD, 0x49]) 48 | app_key0_blob = bytes([0x3E, 0x5C, 0x05, 0xC6, 0xAF, 0xAF, 0xAB, 0x02, 0x20, 0x3B, 49 | 0x3D, 0x18, 0x17, 0x33, 0xDD, 0xCB, 0xA9, 0x65, 0x40, 0x0F, 50 | 0xD5, 0x3A, 0x6F, 0x50, 0x17, 0x31, 0xF3, 0x86, 0x55, 0xB2, 51 | 0x08, 0x08, 0xCF, 0xB8, 0xE6, 0x18, 0x1C, 0xC9, 0x1D, 0x64, 52 | 0xC4, 0x99, 0x3B, 0x04, 0x0B, 0xEC, 0xC7, 0xB5, 0xED, 0x18, 53 | 0xA5, 0x68, 0x3A, 0x95, 0xA3, 0x38, 0xF3, 0xCA, 0x32, 0x55, 54 | 0x28, 0xA9, 0x6F, 0xCB]) 55 | app_key1_blob = bytes([0x7F, 0x81, 0x48, 0x8F, 0x32, 0x02, 0x4C, 0x6B, 0xF5, 0xD9, 56 | 0x99, 0x92, 0x87, 0x98, 0xAE, 0xC0, 0x78, 0x5F, 0xC3, 0xE6, 57 | 0x1B, 0xAF, 0x32, 0xDF, 0xA5, 0x83, 0x3F, 0x43, 0x49, 0x64, 58 | 0xCD, 0x53, 0x37, 0x52, 0x52, 0x39, 0xB1, 0x0B, 0xF8, 0x38, 59 | 0xEF, 0x29, 0xB3, 0x7E, 0xBD, 0x73, 0xD9, 0x51, 0x1E, 0xC4, 60 | 0xDF, 0xFB, 0x97, 0x25, 0xA1, 0xE9, 0xD2, 0x67, 0x89, 0x90, 61 | 0xA0, 0x3C, 0x28, 0x32]) 62 | 63 | # pubkey of the CA which signs controller keys 64 | jedi_CA_pubkey = RSA.construct((bytes_to_long(bytes([ 65 | 0x8E, 0xD7, 0xF9, 0xE4, 0xAA, 0x5C, 0xC5, 0xD2, 0x31, 0x96, 66 | 0xF0, 0xDE, 0x79, 0x7D, 0xFE, 0xAC, 0xF6, 0x3E, 0xDE, 0x7B, 67 | 0xC9, 0x67, 0x16, 0xF1, 0x3C, 0xF5, 0x2A, 0xDE, 0xF8, 0xDA, 68 | 0xCF, 0xA8, 0xE2, 0x33, 0xDC, 0x65, 0x57, 0x17, 0x34, 0x7D, 69 | 0x4C, 0x8C, 0x82, 0x6E, 0xAB, 0x90, 0x36, 0x16, 0xFF, 0x9F, 70 | 0xB8, 0xF9, 0x73, 0x36, 0x17, 0xFB, 0xD4, 0x4E, 0xC8, 0x10, 71 | 0x78, 0xAD, 0x6E, 0x24, 0xB0, 0x62, 0x61, 0x9F, 0x5A, 0x17, 72 | 0xEE, 0x2F, 0x55, 0x72, 0xB4, 0x27, 0xC0, 0x34, 0xA9, 0x49, 73 | 0x36, 0x3E, 0x86, 0xD3, 0xB2, 0x13, 0x35, 0x1F, 0x89, 0x04, 74 | 0xA4, 0x99, 0xF8, 0x62, 0x40, 0x1F, 0x4E, 0x60, 0xAC, 0x21, 75 | 0x31, 0xCD, 0x4B, 0xB9, 0xFD, 0xDF, 0xD5, 0x90, 0xC8, 0xE2, 76 | 0x2B, 0x7D, 0xF9, 0x6D, 0x01, 0x5A, 0x41, 0xC5, 0x49, 0xF3, 77 | 0xEA, 0x0D, 0xED, 0xFC, 0x32, 0xCE, 0xC3, 0x2D, 0x72, 0xC5, 78 | 0x34, 0x93, 0x4A, 0xEF, 0x3D, 0xD1, 0x2B, 0x58, 0xDB, 0x35, 79 | 0x7D, 0xD0, 0x4D, 0x9A, 0x93, 0x11, 0xA3, 0x83, 0x3F, 0xF8, 80 | 0x55, 0x7A, 0x0B, 0x85, 0xB4, 0x54, 0xCD, 0x21, 0xDA, 0xB9, 81 | 0x0D, 0x71, 0x4A, 0xEA, 0x2D, 0xEC, 0x42, 0xE6, 0xF4, 0xEF, 82 | 0x20, 0x45, 0x3C, 0xF6, 0xDB, 0xF3, 0x95, 0x4E, 0x73, 0xA8, 83 | 0x76, 0x91, 0xCF, 0xA0, 0x3F, 0x47, 0x59, 0x45, 0x5C, 0x8B, 84 | 0x96, 0xF1, 0xD0, 0xB6, 0x9D, 0xD3, 0xDD, 0x62, 0x62, 0xE9, 85 | 0x43, 0x8D, 0xCC, 0x26, 0x96, 0xCF, 0xE6, 0x4B, 0x93, 0x0C, 86 | 0x6E, 0x7D, 0x4E, 0x01, 0x51, 0xF6, 0xD1, 0xB1, 0x5D, 0x1A, 87 | 0x4B, 0xE2, 0xE6, 0x0F, 0x0B, 0x36, 0x11, 0x8C, 0x60, 0xF2, 88 | 0x53, 0xFD, 0xBC, 0xE2, 0x27, 0xA8, 0xA4, 0xC9, 0xCD, 0xF2, 89 | 0x26, 0x08, 0x58, 0x58, 0x4A, 0xB8, 0xD7, 0x1C, 0x62, 0x9C, 90 | 0xD4, 0x21, 0xEC, 0x66, 0x60, 0x59])), 0x10001)) 91 | 92 | 93 | def get_hw_binding(): 94 | binding = [] 95 | for addr, val in hw_bindings: 96 | binding.append(struct.pack(' 0: 219 | buf.append(f.read(4)) 220 | f.seek(4, 1) 221 | size -= 4 222 | return b''.join(buf) 223 | 224 | 225 | if __name__ == '__main__': 226 | flash = JediFlash('./jedi_flash-Aug_3_2013.bin') 227 | cert = flash.cert 228 | 229 | # example of what usb auth does 230 | 231 | # console gens and sends the nonce.. 232 | nonce = get_random_bytes(0x100) 233 | # controller signs it and sends back pubkey and sig 234 | jedi_sig = cert.sign(nonce) 235 | # console checks sig... 236 | pss.new(cert.key.publickey()).verify(SHA256.new(nonce), jedi_sig) 237 | 238 | print(r'\o/') 239 | 240 | # now, console will send controller bt link key + host addr 241 | # then controller must respond to bt challenges, but they are much more 242 | # simple 243 | -------------------------------------------------------------------------------- /jedi_tool.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is old script from before fw was dumped. 3 | So just use it as reference...for better info reverse fw dump. 4 | ''' 5 | 6 | import usb.core 7 | import usb.util 8 | import array 9 | import struct 10 | import sys 11 | import binascii 12 | import time 13 | from construct import * 14 | 15 | dev = None 16 | 17 | DEV_ID_JEDI = (0x054c, 0x05c4) 18 | DEV_ID_UJEDI = (0x054c, 0x0856) 19 | 20 | 21 | def wait_for_device(dev_id): 22 | global dev 23 | dev = usb.core.find(idVendor=dev_id[0], idProduct=dev_id[1]) 24 | while dev is None: 25 | time.sleep(1) 26 | dev = usb.core.find(idVendor=dev_id[0], idProduct=dev_id[1]) 27 | 28 | #ctrl_transfer(bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None) 29 | 30 | 31 | class HID_REQ: 32 | DEV_TO_HOST = usb.util.build_request_type( 33 | usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE) 34 | HOST_TO_DEV = usb.util.build_request_type( 35 | usb.util.CTRL_OUT, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE) 36 | GET_REPORT = 0x01 37 | SET_REPORT = 0x09 38 | 39 | 40 | def hid_get_report(dev, report_id, size): 41 | assert isinstance(size, int), 'get_report size must be integer' 42 | assert report_id <= 0xff, 'only support report_type == 0' 43 | return dev.ctrl_transfer(HID_REQ.DEV_TO_HOST, HID_REQ.GET_REPORT, report_id, 0, size + 1)[1:].tobytes() 44 | 45 | 46 | def hid_set_report(dev, report_id, buf): 47 | assert isinstance(buf, (bytes, array.array) 48 | ), 'set_report buf must be buffer' 49 | assert report_id <= 0xff, 'only support report_type == 0' 50 | buf = struct.pack('B', report_id) + buf 51 | return dev.ctrl_transfer(HID_REQ.HOST_TO_DEV, HID_REQ.SET_REPORT, (3 << 8) | report_id, 0, buf) 52 | 53 | # firmware copies from 0x4000 into a mirror in sram 54 | # we can then read this copy 16bits at a time 55 | 56 | 57 | def set_flash_mirror_read_pos(offset): 58 | assert offset < 0x800, 'flash mirror offset out of bounds' 59 | return hid_set_report(dev, 0x08, struct.pack('>BH', 0xff, offset)) 60 | 61 | 62 | def flash_mirror_read_word(): 63 | return hid_get_report(dev, 0x11, 2) 64 | 65 | 66 | def flash_mirror_read(offset): 67 | set_flash_mirror_read_pos(offset) 68 | return flash_mirror_read_word() 69 | 70 | 71 | def dump_flash_mirror(path): 72 | # TODO can't correctly calc checksum for some reason 73 | print('dumping flash mirror to %s...' % (path)) 74 | with open(path, 'wb') as f: 75 | for i in range(0, 0x800, 2): 76 | word = flash_mirror_read(i) 77 | #print('%03x : %s' % (i, binascii.hexlify(word))) 78 | f.write(word) 79 | print('done') 80 | 81 | 82 | def set_bt_link_info(host_addr, link_key): 83 | assert len(host_addr) == 6 84 | assert len(link_key) == 16 85 | hid_set_report(dev, 0x13, host_addr + link_key) 86 | 87 | 88 | def get_bt_mac_addrs(): 89 | buf = hid_get_report(dev, 0x12, 6 + 3 + 6) 90 | ds4_mac, unk, host_mac = buf[0:6], buf[6:9], buf[9:15] 91 | assert unk == b'\x08\x25\x00' 92 | # TODO they are BT addrs; "proper MAC format" is byte-reversed 93 | return (ds4_mac, host_mac) 94 | 95 | 96 | def bt_enable(enable): 97 | return hid_set_report(dev, 0xa1, struct.pack('B', 1 if enable else 0)) 98 | 99 | 100 | def dfu_enable(enable): 101 | return hid_set_report(dev, 0xa2, struct.pack('B', 1 if enable else 0)) 102 | 103 | 104 | class VersionInfo: 105 | version_info_t = Struct( 106 | 'compile_date' / String(0x10, encoding='ascii'), 107 | 'compile_time' / String(0x10, encoding='ascii'), 108 | 'hw_ver_major' / Int16ul, 109 | 'hw_ver_minor' / Int16ul, 110 | 'sw_ver_major' / Int32ul, 111 | 'sw_ver_minor' / Int16ul, 112 | 'sw_series' / Int16ul, 113 | 'code_size' / Int32ul, 114 | ) 115 | 116 | def __init__(s, buf): 117 | s.info = s.version_info_t.parse(buf) 118 | 119 | def __repr__(s): 120 | l = 'Compiled at: %s %s\n'\ 121 | 'hw_ver:%04x.%04x\n'\ 122 | 'sw_ver:%08x.%04x sw_series:%04x\n'\ 123 | 'code size:%08x' % ( 124 | s.info.compile_date, s.info.compile_time, 125 | s.info.hw_ver_major, s.info.hw_ver_minor, 126 | s.info.sw_ver_major, s.info.sw_ver_minor, s.info.sw_series, 127 | s.info.code_size 128 | ) 129 | return l 130 | 131 | 132 | def get_version_info(): 133 | return VersionInfo(hid_get_report(dev, 0xa3, 0x30)) 134 | 135 | 136 | def test_cmd(arg0=0xff, arg1=0xff, arg2=0xff): 137 | return hid_set_report(dev, 0xa0, struct.pack('BBB', arg0, arg1, arg2)) 138 | 139 | 140 | def test_reset(): 141 | # swallow the timeout exception 142 | try: 143 | test_cmd(4, 1, 0) 144 | except: 145 | pass 146 | 147 | 148 | def test_play_sin(enable): 149 | test_cmd(1, 1 if enable else 0) 150 | 151 | 152 | def beep(): 153 | for i in range(3): 154 | test_play_sin(True) 155 | time.sleep(.1) 156 | test_play_sin(False) 157 | time.sleep(.1) 158 | 159 | 160 | def dfu_send_fw_block(is_last, offset, data): 161 | ujedi_fw_block_t = Struct( 162 | 'is_last' / Int8ul, 163 | 'offset' / Int32ul, 164 | 'data' / PrefixedArray(Int8ul, Int8ul) 165 | ) 166 | return hid_set_report(dev, 0xf0, ujedi_fw_block_t.build(Container(is_last=is_last, offset=offset, data=data))) 167 | 168 | wait_for_device(DEV_ID_JEDI) 169 | # this is needed to tell usbhid on nix to go fuck itself 170 | #''' 171 | if dev.is_kernel_driver_active(0): 172 | try: 173 | dev.detach_kernel_driver(0) 174 | except usb.core.USBError as e: 175 | sys.exit('Could not detatch kernel driver: %s' % str(e)) 176 | #''' 177 | # dev.set_configuration() 178 | #dev.set_interface_altsetting(0, 0) 179 | #''' 180 | bt_addrs = get_bt_mac_addrs() 181 | print('ds4 bt mac: %s host bt mac: %s' % 182 | (binascii.hexlify(bt_addrs[0]), binascii.hexlify(bt_addrs[1]))) 183 | print(get_version_info()) 184 | exit() 185 | #''' 186 | 187 | #set_bt_link_info(b'\0' * 6, b'\0' * 16) 188 | 189 | ''' 190 | dfu_enable(True) 191 | test_reset() 192 | wait_for_device(DEV_ID_UJEDI) 193 | print(dev) 194 | exit() 195 | #''' 196 | ''' 197 | print('sending fw...') 198 | for i in range(0, 0x38000, 0x38): 199 | while True: 200 | try: 201 | dfu_send_fw_block(0, i, b'\xff' * 0x38) 202 | print(dev.read(0x84, 0x40)) 203 | break 204 | except: 205 | time.sleep(.1) 206 | # also exits dfu 207 | print('exit dfu...') 208 | time.sleep(1) 209 | dfu_send_fw_block(1, 0, b'\0' * 0x38) 210 | #''' 211 | -------------------------------------------------------------------------------- /ps4nonce.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrepl/ds4/8e4478094b49f51d14bde9b0434ad141156d1a10/ps4nonce.bin --------------------------------------------------------------------------------