-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathdecoder.py
More file actions
273 lines (229 loc) · 8.52 KB
/
Copy pathdecoder.py
File metadata and controls
273 lines (229 loc) · 8.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
from Crypto.Cipher import AES
import struct
import json
inEnc = False
inSize = 0
inCmd = 0
inSet = False
outEnc = False
outCmd = 0
outSize = 0
outSet = False
encKey = 0
###### PROTOCOL
def printKey(bytes, len):
if len == 4:
return "Key "+printInts(bytes, len)
if len == 0:
return "<Request>"
return printInts(bytes, len)
def printInts(bytes, len, max = 10, start=0):
max = 10
count = min((len-start) // 4, max)
ints = [
str(int.from_bytes(bytes[start+i*4:start+(i+1)*4], byteorder='little', signed=False))
for i in range(count)
]
result = ' '.join(ints)
if (len-start) // 4 > max:
result += ' ...'
return result
def printBytes(bytes, len, max=20, start=0):
max = 20
count = min(len-start, max)
if count<1:
return "_"
result = f"{' '.join(f'{b:02x}' for b in bytes[start:start+count])}"
if count>max:
result+= " ..."
return result
def stringOrFunc(bytes, len, func, start=0):
result = []
for b in bytes[start:start + len]:
if 32 <= b <= 126:
result.append(chr(b))
else:
return func(bytes, len, start=start)
return ''.join(result)
def printStr0(bytes, len, start=0):
return ''.join((chr(b) if (32 <= b <= 126 or b == 0) else '.') for b in bytes[start:start + len])
def printFile(bytes, len):
len1 = int.from_bytes(bytes[0:4], 'little')
result = printStr0(bytes, len1, 4)
len2 = int.from_bytes(bytes[4+len1:8+len1], 'little')
if len2 % 4 == 0:
result = result+":"+stringOrFunc(bytes, len, printInts, start=len1+8)
else:
result = result+":"+stringOrFunc(bytes, len, printBytes, start=len1+8)
return result
def cmd8(bytes, len):
if len != 4:
return printBytes(bytes, len)
cmd = int.from_bytes(bytes[0:4], 'little')
res = f"[{cmd}] "
match cmd:
case 1: return res+"Start audio recording"
case 2: return res+"Stop audio recording"
case 7: return res+"Mic type car"
case 15: return res+"Mic type dongle"
case 21: return res+"Mic type phone"
case 24: return res+"Wifi 2.4Ghz"
case 25: return res+"Wifi 5Ghz"
case 22: return res+"Audio transfer bluetooth"
case 23: return res+"Audio transfer dongle"
case 500: return res+"Request video focus"
case 501: return res+"Release video focus"
case 1003: return res+"Scanning devices"
case 1004: return res+"Device found"
case 1005: return res+"Device not found"
case 1006: return res+"Connect device failed"
case 1007: return res+"Dongle bluetooth connected"
case 1008: return res+"Dongle bluetooth disconnected"
case 1009: return res+"Dongle wifi connected"
case 1010: return res+"Dongle wifi disconnected"
case 1011: return res+"Bluetooth pairing started"
return res+""
def getCmd(id):
match id:
case 1: return "Open", printInts
case 2: return "Plugged", printInts
case 3: return "State", None
case 4: return "Unplugged", printInts
case 5: return "Touch", None
case 6: return "Video", printInts
case 7: return "Audio", printInts
case 8: return "Control", cmd8
case 10: return "App info", None
case 13: return "Bt info", printStr0
case 14: return "Wifi info", printStr0
case 15: return "Disconnect", None
case 18: return "Devices", printStr0
case 20: return "Mfr name", None
case 22: return "Camera", None
case 25: return "Json Ctl", None
case 42: return "Media inf", None
case 136: return "App log", None
case 153: return "Send file", printFile
case 162: return "Daynight", None
case 170: return "Heartbeat", None
case 204: return "Version", printStr0
case 206: return "Reboot", None
case 240: return "Encrypt", printInts
return "", None
###### ENCRYPTION
def build_iv_le(f5604m):
iv = bytearray(16)
iv[1] = (f5604m >> 0) & 0xFF
iv[4] = (f5604m >> 8) & 0xFF
iv[9] = (f5604m >> 16) & 0xFF
iv[12] = (f5604m >> 24) & 0xFF
return bytes(iv)
def generate_key(f5604m):
base = "SkBRDy3gmrw1ieH0"
key_bytes = bytearray(16)
for i in range(16):
key_bytes[i] = ord(base[(f5604m + i) % 16])
return bytes(key_bytes)
def decrypt_hex_ciphertext(hex_ciphertext, intkey, iv_builder):
key = generate_key(intkey)
iv = iv_builder(intkey)
cipher = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
ciphertext = bytes.fromhex(hex_ciphertext)
plaintext = cipher.decrypt(ciphertext)
return plaintext
def decrypt_hex_cipher(ciphertext, intkey, iv_builder):
key = generate_key(intkey)
iv = iv_builder(intkey)
cipher = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
plaintext = cipher.decrypt(ciphertext)
return plaintext
###### HELPERS
def bytes_to_printable_ascii(byte_data):
return ''.join((chr(b) if 32 <= b <= 126 else '.') for b in byte_data)
def bytes_to_int_list_le(byte_data):
# Pad if length is not a multiple of 4
length = len(byte_data)
padded_length = (length + 3) // 4 * 4
padded = byte_data.ljust(padded_length, b'\x00')
# Unpack as little-endian unsigned ints
return list(struct.unpack('<' + 'I' * (padded_length // 4), padded))
###### DECODING
def check_types(capdata: bytes) -> tuple[bool, bool]:
is_aa = capdata.startswith(bytes.fromhex('aa55aa55'))
is_bb = capdata.startswith(bytes.fromhex('bb55bb55'))
if is_aa or is_bb:
if len(capdata) >= 12: # 4 bytes prefix + 8 bytes data
size = int.from_bytes(capdata[4:8], 'little')
cmd = int.from_bytes(capdata[8:12], 'little')
return True, is_bb, size, cmd
return False, False, None, None
def processPacket(frame, out, capdata, cmd, size, enc):
global encKey
if enc and encKey != 0 and size > 0:
capdata = decrypt_hex_cipher(capdata, encKey, build_iv_le)
if out and cmd == 240 and size == 4 and len(capdata) >= 4:
encKey = int.from_bytes(capdata[0:4], 'little')
#if cmd == 170 or cmd == 7 or cmd == 5 or cmd == 6:
# return
cmdName, cmdProc = getCmd(cmd)
cmdstr = f"{cmdName} [{cmd}]"
res = f"{frame:>5} {cmdstr:>15} {'>' if not out else '<'}{' ' if not enc else '*'} "
str = ""
if cmdProc:
str = cmdProc(capdata, size)
else:
if len(capdata)<1000:
str = stringOrFunc(capdata, size, printBytes)
else:
str = f"{' '.join(f'{b:02x}' for b in capdata[:40])}"
if size == 0:
str = "_"
print(f"{'\033[92m' if not out else '\033[93m'}{res}{str}\033[0m")
def processInc(frame, capdata):
global inEnc, inSize, inCmd, inSet
header, enc, size, cmd = check_types(capdata)
if header:
inEnc = enc
inSize = size
inCmd = cmd
inSet = header
if inSize == 0:
processPacket(frame, False, bytearray(), inCmd, inSize, inEnc)
return
if inSet:
processPacket(frame, False, capdata, inCmd, inSize, inEnc)
inSet = header
def processOut(frame, capdata):
global outEnc, outSize, outCmd, outSet
header, enc, size, cmd = check_types(capdata)
if header:
outEnc = enc
outSize = size
outCmd = cmd
outSet = header
if outSize == 0:
processPacket(frame, True, bytearray(), outCmd, outSize, outEnc)
return
if outSet:
processPacket(frame, True, capdata, outCmd, outSize, outEnc)
outSet = header
def processFile(file_path):
with open(file_path) as f:
data = json.load(f)
for entry in data:
layers = entry['_source']['layers']
usb = layers.get('usb', {})
frame_number = layers.get('frame', {}).get('frame.number')
direction = usb.get('usb.endpoint_address_tree', {}).get('usb.endpoint_address.direction')
urb_type = usb.get('usb.urb_type')
capdata = entry['_source']['layers'].get('usb.capdata')
if capdata:
byte_array = bytes(int(b, 16) for b in capdata.split(':'))
if (direction == "0" and urb_type == "'S'"):
processOut(frame_number, byte_array)
if(direction == "1" and urb_type == "'C'"):
processInc(frame_number, byte_array)
print("This tool decode carlinkit protocol")
print("It requred JSON export of USB events filtered by device")
file_path = input("Path to JSON export from PCAPNG: ").strip().strip("'")
processFile(file_path)