@ecopoesis/homebridge-dmx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +176 -0
- package/README.md +146 -0
- package/config.schema.json +143 -0
- package/dist/cli/stick.d.ts +3 -0
- package/dist/cli/stick.d.ts.map +1 -0
- package/dist/cli/stick.js +176 -0
- package/dist/cli/stick.js.map +1 -0
- package/dist/color/cct.d.ts +3 -0
- package/dist/color/cct.d.ts.map +1 -0
- package/dist/color/cct.js +33 -0
- package/dist/color/cct.js.map +1 -0
- package/dist/color/dimmer.d.ts +3 -0
- package/dist/color/dimmer.d.ts.map +1 -0
- package/dist/color/dimmer.js +16 -0
- package/dist/color/dimmer.js.map +1 -0
- package/dist/color/hsvcct.d.ts +3 -0
- package/dist/color/hsvcct.d.ts.map +1 -0
- package/dist/color/hsvcct.js +52 -0
- package/dist/color/hsvcct.js.map +1 -0
- package/dist/color/index.d.ts +7 -0
- package/dist/color/index.d.ts.map +1 -0
- package/dist/color/index.js +28 -0
- package/dist/color/index.js.map +1 -0
- package/dist/color/parsers.d.ts +20 -0
- package/dist/color/parsers.d.ts.map +1 -0
- package/dist/color/parsers.js +232 -0
- package/dist/color/parsers.js.map +1 -0
- package/dist/color/rgb.d.ts +3 -0
- package/dist/color/rgb.d.ts.map +1 -0
- package/dist/color/rgb.js +32 -0
- package/dist/color/rgb.js.map +1 -0
- package/dist/color/rgbaw.d.ts +3 -0
- package/dist/color/rgbaw.d.ts.map +1 -0
- package/dist/color/rgbaw.js +48 -0
- package/dist/color/rgbaw.js.map +1 -0
- package/dist/color/rgbw.d.ts +3 -0
- package/dist/color/rgbw.d.ts.map +1 -0
- package/dist/color/rgbw.js +37 -0
- package/dist/color/rgbw.js.map +1 -0
- package/dist/color/rgbww.d.ts +3 -0
- package/dist/color/rgbww.d.ts.map +1 -0
- package/dist/color/rgbww.js +54 -0
- package/dist/color/rgbww.js.map +1 -0
- package/dist/color/types.d.ts +64 -0
- package/dist/color/types.d.ts.map +1 -0
- package/dist/color/types.js +91 -0
- package/dist/color/types.js.map +1 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +191 -0
- package/dist/config.js.map +1 -0
- package/dist/controller.d.ts +32 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +128 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +17 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +96 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessory.d.ts +18 -0
- package/dist/platformAccessory.d.ts.map +1 -0
- package/dist/platformAccessory.js +81 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/settings.d.ts +14 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +15 -0
- package/dist/settings.js.map +1 -0
- package/dist/stick/frame.d.ts +16 -0
- package/dist/stick/frame.d.ts.map +1 -0
- package/dist/stick/frame.js +51 -0
- package/dist/stick/frame.js.map +1 -0
- package/dist/stick/kdf.d.ts +23 -0
- package/dist/stick/kdf.d.ts.map +1 -0
- package/dist/stick/kdf.js +90 -0
- package/dist/stick/kdf.js.map +1 -0
- package/dist/stick/protocol.d.ts +28 -0
- package/dist/stick/protocol.d.ts.map +1 -0
- package/dist/stick/protocol.js +78 -0
- package/dist/stick/protocol.js.map +1 -0
- package/dist/stick/session.d.ts +50 -0
- package/dist/stick/session.d.ts.map +1 -0
- package/dist/stick/session.js +301 -0
- package/dist/stick/session.js.map +1 -0
- package/examples/dmx.yaml +65 -0
- package/package.json +51 -0
- package/src/cli/stick.ts +198 -0
- package/src/color/cct.ts +40 -0
- package/src/color/dimmer.ts +24 -0
- package/src/color/hsvcct.ts +59 -0
- package/src/color/index.ts +34 -0
- package/src/color/parsers.ts +232 -0
- package/src/color/rgb.ts +33 -0
- package/src/color/rgbaw.ts +46 -0
- package/src/color/rgbw.ts +36 -0
- package/src/color/rgbww.ts +56 -0
- package/src/color/types.ts +150 -0
- package/src/config.ts +248 -0
- package/src/controller.ts +136 -0
- package/src/index.ts +9 -0
- package/src/platform.ts +118 -0
- package/src/platformAccessory.ts +102 -0
- package/src/settings.ts +18 -0
- package/src/stick/frame.ts +70 -0
- package/src/stick/kdf.ts +99 -0
- package/src/stick/protocol.ts +90 -0
- package/src/stick/session.ts +348 -0
- package/stick-de3.json +6 -0
- package/tools/aes-hook.c +1404 -0
- package/tools/analyze-pcap.mjs +143 -0
- package/tools/capture-aes-key.lldb +25 -0
- package/tools/capture-and-verify.sh +46 -0
- package/tools/capture-handshake.sh +59 -0
- package/tools/capture-send.sh +39 -0
- package/tools/crack-kdf.mjs +121 -0
- package/tools/crack-key.mjs +170 -0
- package/tools/decode-frame.mjs +110 -0
- package/tools/decrypt-011c.mjs +123 -0
- package/tools/decrypt-dmx.mjs +130 -0
- package/tools/decrypt-ssl2.ts +154 -0
- package/tools/derive-dmx-key.mjs +189 -0
- package/tools/diff-011c.mjs +127 -0
- package/tools/dump-pcap-summary.mjs +54 -0
- package/tools/dump-tcp-stream.mjs +123 -0
- package/tools/extract-011c.mjs +117 -0
- package/tools/extract-frame.mjs +78 -0
- package/tools/find-011c-key.mjs +140 -0
- package/tools/find-iv.mjs +153 -0
- package/tools/ghidra/README.md +136 -0
- package/tools/ghidra/__pycache__/dump-011c-callers.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-011c-handler.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-aes-candidates.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-anchors.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-auth2.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-authenticate.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-block-cipher.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-cipher.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-ctor-chain.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-curve.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-encompassing.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-handshake.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-kdf-chain.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-kdf-context.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-profile.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-real-encrypt.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-secret.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-setsoftware.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-stick3-vtable.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-swxref.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/dump-vt-key-getter.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-cipher.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-ctor.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-md5.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-secret-users.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-stick3-crypto.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-stick3-device.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-stick3a-ctor.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-stick3anet-ctor.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/find-vtables.cpython-313.pyc +0 -0
- package/tools/ghidra/__pycache__/string-walk.cpython-313.pyc +0 -0
- package/tools/ghidra/dump-011c-callers.py +140 -0
- package/tools/ghidra/dump-011c-handler.py +158 -0
- package/tools/ghidra/dump-aes-candidates.py +99 -0
- package/tools/ghidra/dump-anchors.py +192 -0
- package/tools/ghidra/dump-auth2.py +50 -0
- package/tools/ghidra/dump-authenticate.py +71 -0
- package/tools/ghidra/dump-block-cipher.py +88 -0
- package/tools/ghidra/dump-cipher.py +106 -0
- package/tools/ghidra/dump-ctor-chain.py +107 -0
- package/tools/ghidra/dump-curve.py +112 -0
- package/tools/ghidra/dump-encompassing.py +146 -0
- package/tools/ghidra/dump-handshake.py +76 -0
- package/tools/ghidra/dump-kdf-chain.py +105 -0
- package/tools/ghidra/dump-kdf-context.py +72 -0
- package/tools/ghidra/dump-profile.py +59 -0
- package/tools/ghidra/dump-real-encrypt.py +90 -0
- package/tools/ghidra/dump-secret.py +84 -0
- package/tools/ghidra/dump-setsoftware.py +63 -0
- package/tools/ghidra/dump-stick3-vtable.py +182 -0
- package/tools/ghidra/dump-swxref.py +27 -0
- package/tools/ghidra/dump-vt-key-getter.py +170 -0
- package/tools/ghidra/find-cipher.py +210 -0
- package/tools/ghidra/find-ctor.py +109 -0
- package/tools/ghidra/find-md5.py +97 -0
- package/tools/ghidra/find-secret-users.py +98 -0
- package/tools/ghidra/find-stick3-crypto.py +190 -0
- package/tools/ghidra/find-stick3-device.py +111 -0
- package/tools/ghidra/find-stick3a-ctor.py +87 -0
- package/tools/ghidra/find-stick3anet-ctor.py +76 -0
- package/tools/ghidra/find-vtables.py +165 -0
- package/tools/ghidra/out-011c-callers-run.log +51 -0
- package/tools/ghidra/out-011c-run.log +50 -0
- package/tools/ghidra/out-vt-key-run.log +55 -0
- package/tools/ghidra/string-walk.py +180 -0
- package/tools/hmac-key.sh +51 -0
- package/tools/hwm-entitlements.plist +18 -0
- package/tools/inspect-plaintext.mjs +87 -0
- package/tools/lldb-capture-aes.py +137 -0
- package/tools/lldb_hmac_key.py +93 -0
- package/tools/lldb_pathA_scan.py +341 -0
- package/tools/lldb_pathC_keytrace.py +300 -0
- package/tools/lldb_pathD_kdf_watch.py +281 -0
- package/tools/pathA-launch-hwm.sh +86 -0
- package/tools/pathA-scan.sh +52 -0
- package/tools/pathC-keytrace.sh +49 -0
- package/tools/pathC-run.sh +71 -0
- package/tools/pathD-kdf-watch.sh +51 -0
- package/tools/quick-trigger.mjs +86 -0
- package/tools/run-hooked-hwmanager.sh +51 -0
- package/tools/scan-aes-schedule.mjs +135 -0
- package/tools/send_dmx.mjs +480 -0
- package/tools/stick-status.mjs +57 -0
- package/tools/test-aes-detector.mjs +100 -0
- package/tools/test-tcp-write.ts +274 -0
- package/tools/test-udp-dmx.ts +324 -0
- package/tools/test-x25519.mjs +87 -0
- package/tools/try-decrypt.ts +144 -0
- package/tools/try-hardcoded-key.mjs +86 -0
- package/tools/try-hardcoded-key.ts +103 -0
- package/tools/try-key.mjs +103 -0
- package/tools/verify-011c-key.mjs +121 -0
- package/tools/verify-kdf.mjs +142 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Find crypto/cipher code via string xrefs.
|
|
2
|
+
#
|
|
3
|
+
# Ghidra didn't auto-create RTTI symbols for XHL_Stick3CryptDmxUniverse etc.,
|
|
4
|
+
# but the binary clearly contains:
|
|
5
|
+
# - typeinfo-name strings like "26XHL_DasNetCryptDmxUniverse"
|
|
6
|
+
# "N14XHL_Stick3ANet26XHL_Stick3CryptDmxUniverseE"
|
|
7
|
+
# - leaked source paths "../source/common/.../XHL_DasNetCryptDmxUniverse.cpp"
|
|
8
|
+
# - method-name strings in error logs like
|
|
9
|
+
# "[XHL_DasNetCryptDmxUniverse::setIoMode] pSubscribeUdpPort failed"
|
|
10
|
+
#
|
|
11
|
+
# Strategy: for each interesting string, locate its bytes in memory and find
|
|
12
|
+
# every function that references it. That function IS the method whose name
|
|
13
|
+
# the string mentions (since the string is its diagnostic log message), or
|
|
14
|
+
# AT LEAST a function in the crypto path.
|
|
15
|
+
|
|
16
|
+
# @category Stick
|
|
17
|
+
|
|
18
|
+
import os, re
|
|
19
|
+
from ghidra.app.decompiler import DecompInterface
|
|
20
|
+
from ghidra.util.task import ConsoleTaskMonitor
|
|
21
|
+
from ghidra.program.model.address import AddressSet
|
|
22
|
+
from ghidra.program.model.listing import CodeUnit
|
|
23
|
+
|
|
24
|
+
OUT_DIR = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out4')
|
|
25
|
+
try:
|
|
26
|
+
os.makedirs(OUT_DIR)
|
|
27
|
+
except OSError:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
prog = currentProgram
|
|
31
|
+
fm = prog.getFunctionManager()
|
|
32
|
+
mem = prog.getMemory()
|
|
33
|
+
listing = prog.getListing()
|
|
34
|
+
ref_mgr = prog.getReferenceManager()
|
|
35
|
+
monitor = ConsoleTaskMonitor()
|
|
36
|
+
decomp = DecompInterface()
|
|
37
|
+
decomp.openProgram(prog)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_string_addrs(needle):
|
|
41
|
+
"""Return all addresses in memory where the literal bytes of `needle`
|
|
42
|
+
occur (followed by a NUL byte for safety)."""
|
|
43
|
+
pattern = needle.encode('utf-8') + b'\x00'
|
|
44
|
+
hits = []
|
|
45
|
+
# Use Memory.findBytes
|
|
46
|
+
addr = mem.findBytes(prog.getMinAddress(), pattern, None, True, monitor)
|
|
47
|
+
while addr is not None and len(hits) < 50:
|
|
48
|
+
hits.append(addr)
|
|
49
|
+
nxt = addr.add(1)
|
|
50
|
+
try:
|
|
51
|
+
addr = mem.findBytes(nxt, pattern, None, True, monitor)
|
|
52
|
+
except Exception:
|
|
53
|
+
break
|
|
54
|
+
return hits
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def dump_function(func, out):
|
|
58
|
+
out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
|
|
59
|
+
out.write('signature: %s\n' % func.getPrototypeString(True, True))
|
|
60
|
+
out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
|
|
61
|
+
callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
62
|
+
for f in func.getCalledFunctions(monitor)))
|
|
63
|
+
out.write('\n-- callees (%d) --\n' % len(callees))
|
|
64
|
+
for c in callees:
|
|
65
|
+
out.write(' ' + c + '\n')
|
|
66
|
+
callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
67
|
+
for f in func.getCallingFunctions(monitor)))
|
|
68
|
+
out.write('\n-- callers (%d) --\n' % len(callers))
|
|
69
|
+
for c in callers:
|
|
70
|
+
out.write(' ' + c + '\n')
|
|
71
|
+
out.write('\n-- decompilation --\n')
|
|
72
|
+
res = decomp.decompileFunction(func, 180, monitor)
|
|
73
|
+
if res.decompileCompleted():
|
|
74
|
+
out.write(res.getDecompiledFunction().getC())
|
|
75
|
+
else:
|
|
76
|
+
out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
|
|
77
|
+
out.write('\n')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# (needle, suggested filename slug)
|
|
81
|
+
NEEDLES = [
|
|
82
|
+
# Source file paths leak class boundaries
|
|
83
|
+
('XHL_DasNetCryptDmxUniverse.cpp', 'XHL_DasNetCryptDmxUniverse_cpp'),
|
|
84
|
+
('XHL_DasEccAesCryptography.cpp', 'XHL_DasEccAesCryptography_cpp'),
|
|
85
|
+
('XHL_DasEccStreamCryptography.cpp', 'XHL_DasEccStreamCryptography_cpp'),
|
|
86
|
+
('XHL_DasDhRsaCryptography.hpp', 'XHL_DasDhRsaCryptography_hpp'),
|
|
87
|
+
|
|
88
|
+
# Error/debug strings that contain method names
|
|
89
|
+
('XHL_DasNetCryptDmxUniverse::setIoMode', 'setIoMode'),
|
|
90
|
+
|
|
91
|
+
# RTTI strings — anything that references them is the typeinfo struct
|
|
92
|
+
# for that class, and the vtable points at it.
|
|
93
|
+
('N14XHL_Stick3ANet26XHL_Stick3CryptDmxUniverseE', 'Stick3CryptDmxUniverse_rtti'),
|
|
94
|
+
('N14XHL_Stick5ANet26XHL_Stick5CryptDmxUniverseE', 'Stick5CryptDmxUniverse_rtti'),
|
|
95
|
+
('26XHL_DasNetCryptDmxUniverse', 'DasNetCryptDmxUniverse_rtti'),
|
|
96
|
+
('14XHL_AesOStream', 'AesOStream_rtti'),
|
|
97
|
+
('14XHL_AesIStream', 'AesIStream_rtti'),
|
|
98
|
+
('20XHL_AesOStreamBuffer', 'AesOStreamBuffer_rtti'),
|
|
99
|
+
('20XHL_AesIStreamBuffer', 'AesIStreamBuffer_rtti'),
|
|
100
|
+
('25XHL_DasEccAesCryptography', 'DasEccAesCryptography_rtti'),
|
|
101
|
+
('24XHL_DasDhRsaCryptographyILj1024EE', 'DasDhRsaCryptography_1024_rtti'),
|
|
102
|
+
('24XHL_DasDhRsaCryptographyILj512EE', 'DasDhRsaCryptography_512_rtti'),
|
|
103
|
+
('28XHL_DasEccStreamCryptography', 'DasEccStreamCryptography_rtti'),
|
|
104
|
+
('19XHL_DasCryptography', 'DasCryptography_rtti'),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
|
|
108
|
+
summary.write('String-walk anchors — ESA2 HardwareManager (2024-03-21)\n\n')
|
|
109
|
+
|
|
110
|
+
seen_funcs = set()
|
|
111
|
+
|
|
112
|
+
for needle, slug in NEEDLES:
|
|
113
|
+
summary.write('\n#### needle: %r\n' % needle)
|
|
114
|
+
str_addrs = find_string_addrs(needle)
|
|
115
|
+
summary.write(' found %d occurrence(s) of string\n' % len(str_addrs))
|
|
116
|
+
if not str_addrs:
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
for str_addr in str_addrs:
|
|
120
|
+
summary.write(' @ %s\n' % str_addr)
|
|
121
|
+
|
|
122
|
+
# Walk one ref-hop: who references this string?
|
|
123
|
+
L1_refs = list(ref_mgr.getReferencesTo(str_addr))
|
|
124
|
+
L1_funcs = set()
|
|
125
|
+
L1_data_addrs = set()
|
|
126
|
+
for r in L1_refs:
|
|
127
|
+
fa = r.getFromAddress()
|
|
128
|
+
f = fm.getFunctionContaining(fa)
|
|
129
|
+
if f is not None:
|
|
130
|
+
L1_funcs.add(f.getEntryPoint())
|
|
131
|
+
else:
|
|
132
|
+
L1_data_addrs.add(fa)
|
|
133
|
+
|
|
134
|
+
summary.write(' direct fn refs: %d\n' % len(L1_funcs))
|
|
135
|
+
for ep in sorted(L1_funcs):
|
|
136
|
+
f = fm.getFunctionAt(ep)
|
|
137
|
+
summary.write(' %s @ %s\n' % (f.getName(True), f.getEntryPoint()))
|
|
138
|
+
|
|
139
|
+
summary.write(' direct data refs: %d\n' % len(L1_data_addrs))
|
|
140
|
+
for da in sorted(L1_data_addrs):
|
|
141
|
+
summary.write(' data @ %s\n' % da)
|
|
142
|
+
# Walk one more hop: who references THAT data location?
|
|
143
|
+
L2_refs = list(ref_mgr.getReferencesTo(da))
|
|
144
|
+
for r2 in L2_refs:
|
|
145
|
+
fa2 = r2.getFromAddress()
|
|
146
|
+
f2 = fm.getFunctionContaining(fa2)
|
|
147
|
+
if f2 is not None:
|
|
148
|
+
L1_funcs.add(f2.getEntryPoint())
|
|
149
|
+
summary.write(' via %s -> fn %s @ %s\n' %
|
|
150
|
+
(da, f2.getName(True), f2.getEntryPoint()))
|
|
151
|
+
else:
|
|
152
|
+
# Could be the vtable structure itself
|
|
153
|
+
summary.write(' via %s -> data @ %s\n' % (da, fa2))
|
|
154
|
+
# And one more hop (vtable -> referencing fn)
|
|
155
|
+
for r3 in ref_mgr.getReferencesTo(fa2):
|
|
156
|
+
fa3 = r3.getFromAddress()
|
|
157
|
+
f3 = fm.getFunctionContaining(fa3)
|
|
158
|
+
if f3 is not None:
|
|
159
|
+
L1_funcs.add(f3.getEntryPoint())
|
|
160
|
+
summary.write(' via %s -> fn %s @ %s\n' %
|
|
161
|
+
(fa2, f3.getName(True),
|
|
162
|
+
f3.getEntryPoint()))
|
|
163
|
+
|
|
164
|
+
# Dump every collected function
|
|
165
|
+
for ep in sorted(L1_funcs):
|
|
166
|
+
if ep in seen_funcs:
|
|
167
|
+
continue
|
|
168
|
+
seen_funcs.add(ep)
|
|
169
|
+
f = fm.getFunctionAt(ep)
|
|
170
|
+
if f is None:
|
|
171
|
+
continue
|
|
172
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:140]
|
|
173
|
+
path = os.path.join(OUT_DIR, '%s_%s_%s.txt' %
|
|
174
|
+
(slug, f.getEntryPoint(), safe))
|
|
175
|
+
with open(path, 'w') as o:
|
|
176
|
+
dump_function(f, o)
|
|
177
|
+
|
|
178
|
+
summary.write('\n=== total functions dumped: %d ===\n' % len(seen_funcs))
|
|
179
|
+
summary.close()
|
|
180
|
+
print('string-walk: dumped %d functions to %s' % (len(seen_funcs), OUT_DIR))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Extract the 0x48 TCP-auth HMAC key from a running debug Hardware Manager.
|
|
3
|
+
#
|
|
4
|
+
# The 0x48 handshake is authenticated with HMAC-SHA256(key, msg[0:82]); the
|
|
5
|
+
# key is an internal HWM constant. This attaches lldb, breakpoints the HMAC
|
|
6
|
+
# routine, and dumps the key the instant HWM builds a 0x48 (one hit, then
|
|
7
|
+
# detaches — not a per-frame breakpoint, so HWM stays healthy).
|
|
8
|
+
#
|
|
9
|
+
# Steps:
|
|
10
|
+
# 1. Quit the normal Hardware Manager (it holds the Stick's single session).
|
|
11
|
+
# 2. Launch the debug copy: tools/pathA-launch-hwm.sh
|
|
12
|
+
# Leave it DISCONNECTED from the Stick for now.
|
|
13
|
+
# 3. Run this script.
|
|
14
|
+
# 4. When it prints "waiting for the 0x48 handshake", connect the debug HWM
|
|
15
|
+
# to the Stick (Connection -> Add/Connect). The key is dumped + logged.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
cd "$(dirname "$0")/.."
|
|
19
|
+
|
|
20
|
+
SCAN_PY="tools/lldb_hmac_key.py"
|
|
21
|
+
LOG="/tmp/stick-hmac-key.log"
|
|
22
|
+
|
|
23
|
+
PID="$(pgrep -f '/tmp/HM-ESA2-dbg.app/Contents/MacOS/HardwareManager' | head -1)"
|
|
24
|
+
if [ -z "$PID" ]; then
|
|
25
|
+
echo "ERROR: the debug Hardware Manager is not running."
|
|
26
|
+
echo " 1. Quit the normal Hardware Manager."
|
|
27
|
+
echo " 2. Run: tools/pathA-launch-hwm.sh"
|
|
28
|
+
echo " 3. Leave it DISCONNECTED from the Stick, then re-run this script."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo ">>> debug HWM pid = $PID"
|
|
33
|
+
echo ">>> attaching lldb and arming the HMAC breakpoint ..."
|
|
34
|
+
echo ">>> when it says 'waiting for the 0x48 handshake', CONNECT HWM to the Stick."
|
|
35
|
+
echo
|
|
36
|
+
|
|
37
|
+
timeout 600 lldb --batch \
|
|
38
|
+
-o "command script import $SCAN_PY" \
|
|
39
|
+
-o "detach" \
|
|
40
|
+
-o "quit" \
|
|
41
|
+
-p "$PID" 2>&1 | sed 's/^/ /'
|
|
42
|
+
|
|
43
|
+
echo
|
|
44
|
+
if grep -q 'auth HMAC caught' "$LOG" 2>/dev/null; then
|
|
45
|
+
echo ">>> SUCCESS — the auth key:"
|
|
46
|
+
grep -E 'key length|key \(hex\)|key \(text\)' "$LOG" | sed 's/^/ /'
|
|
47
|
+
echo ">>> full log: $LOG"
|
|
48
|
+
else
|
|
49
|
+
echo "!!! the 0x48 HMAC was not caught — did HWM connect to the Stick?"
|
|
50
|
+
echo " log: $LOG"
|
|
51
|
+
fi
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>com.apple.security.get-task-allow</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
8
|
+
<true/>
|
|
9
|
+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
10
|
+
<true/>
|
|
11
|
+
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
|
12
|
+
<true/>
|
|
13
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
14
|
+
<true/>
|
|
15
|
+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
16
|
+
<true/>
|
|
17
|
+
</dict>
|
|
18
|
+
</plist>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Decrypt every 576-byte UDP DMX frame in a pcap and dump the FULL 544-byte
|
|
2
|
+
// plaintext layout: [0:16] P0 header, [16:528] 512 DMX, [528:544] trailer.
|
|
3
|
+
// The question: is the 16-byte trailer zero (what send_dmx sends) or a
|
|
4
|
+
// per-frame checksum the Stick validates?
|
|
5
|
+
//
|
|
6
|
+
// node tools/inspect-plaintext.mjs <pcap> <key-hex>
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import crypto from 'node:crypto';
|
|
10
|
+
|
|
11
|
+
const [, , pcapPath, keyHex] = process.argv;
|
|
12
|
+
if (!pcapPath || !keyHex) { console.error('usage: inspect-plaintext.mjs <pcap> <key-hex>'); process.exit(1); }
|
|
13
|
+
const key = Buffer.from(keyHex.replace(/\s/g, ''), 'hex');
|
|
14
|
+
if (key.length !== 32) { console.error('key must be 32 bytes'); process.exit(1); }
|
|
15
|
+
|
|
16
|
+
const buf = fs.readFileSync(pcapPath);
|
|
17
|
+
const le = buf.readUInt32LE(0) === 0xa1b2c3d4;
|
|
18
|
+
const linkType = le ? buf.readUInt32LE(20) : buf.readUInt32BE(20);
|
|
19
|
+
const ethSkip = linkType === 1 ? 14 : 4;
|
|
20
|
+
|
|
21
|
+
const frames = [];
|
|
22
|
+
let off = 24;
|
|
23
|
+
while (off + 16 <= buf.length) {
|
|
24
|
+
const capLen = le ? buf.readUInt32LE(off + 8) : buf.readUInt32BE(off + 8);
|
|
25
|
+
off += 16;
|
|
26
|
+
if (off + capLen > buf.length) break;
|
|
27
|
+
const pkt = buf.subarray(off, off + capLen);
|
|
28
|
+
off += capLen;
|
|
29
|
+
if (pkt.length < ethSkip + 28) continue;
|
|
30
|
+
const ip = pkt.subarray(ethSkip);
|
|
31
|
+
if (ip[9] !== 17) continue;
|
|
32
|
+
const udp = ip.subarray((ip[0] & 0x0f) * 4);
|
|
33
|
+
const payload = udp.subarray(8, udp.readUInt16BE(4));
|
|
34
|
+
if (payload.length === 576 && payload.subarray(0, 8).toString() === 'Stick_3A')
|
|
35
|
+
frames.push(Buffer.from(payload));
|
|
36
|
+
}
|
|
37
|
+
console.log(`${frames.length} 576-byte frames in ${pcapPath}\n`);
|
|
38
|
+
|
|
39
|
+
function decrypt(frame) {
|
|
40
|
+
const h = frame.subarray(0, 32);
|
|
41
|
+
const iv = Buffer.concat([h.subarray(0x0a, 0x12), h.subarray(0x18, 0x20)]);
|
|
42
|
+
const d = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
43
|
+
d.setAutoPadding(false);
|
|
44
|
+
return Buffer.concat([d.update(frame.subarray(32)), d.final()]);
|
|
45
|
+
}
|
|
46
|
+
const zr = (b) => { let z = 0; for (const x of b) if (x === 0) z++; return z / b.length; };
|
|
47
|
+
|
|
48
|
+
let decoded = 0;
|
|
49
|
+
const p0set = new Map(), trailerSet = new Map();
|
|
50
|
+
const trailerSamples = [];
|
|
51
|
+
for (let i = 0; i < frames.length; i++) {
|
|
52
|
+
const pt = decrypt(frames[i]);
|
|
53
|
+
const dmx = pt.subarray(16, 528);
|
|
54
|
+
if (zr(dmx) < 0.5) continue; // not this session's key
|
|
55
|
+
decoded++;
|
|
56
|
+
const p0 = pt.subarray(0, 16).toString('hex');
|
|
57
|
+
const tr = pt.subarray(528, 544).toString('hex');
|
|
58
|
+
p0set.set(p0, (p0set.get(p0) || 0) + 1);
|
|
59
|
+
trailerSet.set(tr, (trailerSet.get(tr) || 0) + 1);
|
|
60
|
+
if (decoded <= 6) {
|
|
61
|
+
const h = frames[i].subarray(0, 32);
|
|
62
|
+
const lit = [];
|
|
63
|
+
for (let c = 0; c < 512; c++) if (dmx[c]) lit.push(`ch${c+1}=${dmx[c]}`);
|
|
64
|
+
trailerSamples.push({
|
|
65
|
+
fieldA: h.readUInt32LE(0x0a), seq: h[0x17],
|
|
66
|
+
p0, tr, nLit: lit.length, lit: lit.slice(0, 8).join(' '),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`frames that decrypt with this key: ${decoded}/${frames.length}\n`);
|
|
72
|
+
if (!decoded) { console.log('key does not match any frame in this pcap'); process.exit(1); }
|
|
73
|
+
|
|
74
|
+
console.log('first 6 decrypted frames:');
|
|
75
|
+
for (const s of trailerSamples) {
|
|
76
|
+
console.log(` fieldA=${String(s.fieldA).padEnd(6)} seq=${String(s.seq).padEnd(4)} lit=${s.nLit}`);
|
|
77
|
+
console.log(` P0 = ${s.p0}`);
|
|
78
|
+
console.log(` trailer = ${s.tr}`);
|
|
79
|
+
if (s.lit) console.log(` dmx = ${s.lit}`);
|
|
80
|
+
}
|
|
81
|
+
console.log(`\nP0 distinct values : ${p0set.size}`);
|
|
82
|
+
for (const [v, n] of p0set) console.log(` ${v} x${n}`);
|
|
83
|
+
console.log(`trailer distinct values: ${trailerSet.size}`);
|
|
84
|
+
for (const [v, n] of [...trailerSet].slice(0, 8)) console.log(` ${v} x${n}`);
|
|
85
|
+
console.log(trailerSet.size === 1
|
|
86
|
+
? '\n => trailer is CONSTANT — send_dmx sending zeros is wrong only if this constant != 0'
|
|
87
|
+
: '\n => trailer VARIES per frame — it is a checksum/MAC; send_dmx sending zeros is FATAL');
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
lldb script to capture AES keys from HardwareManager.
|
|
3
|
+
Attach to HardwareManager, then source this script.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
lldb -p <PID>
|
|
7
|
+
(lldb) command script import /path/to/lldb-capture-aes.py
|
|
8
|
+
(lldb) continue
|
|
9
|
+
|
|
10
|
+
Then connect to the Stick in the HardwareManager UI.
|
|
11
|
+
Keys will be printed and saved to /tmp/stick-aes-keys.log
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import lldb
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
LOG_PATH = "/tmp/stick-aes-keys.log"
|
|
18
|
+
key_count = 0
|
|
19
|
+
|
|
20
|
+
def log(msg):
|
|
21
|
+
with open(LOG_PATH, "a") as f:
|
|
22
|
+
f.write(msg + "\n")
|
|
23
|
+
print(msg)
|
|
24
|
+
|
|
25
|
+
def on_setkey_enc(frame, bp_loc, dict):
|
|
26
|
+
global key_count
|
|
27
|
+
key_count += 1
|
|
28
|
+
thread = frame.GetThread()
|
|
29
|
+
process = thread.GetProcess()
|
|
30
|
+
|
|
31
|
+
# x86_64 calling convention: rdi=ctx, rsi=key, edx=keybits
|
|
32
|
+
keybits = frame.FindRegister("edx").GetValueAsUnsigned()
|
|
33
|
+
key_ptr = frame.FindRegister("rsi").GetValueAsUnsigned()
|
|
34
|
+
key_len = keybits // 8
|
|
35
|
+
|
|
36
|
+
err = lldb.SBError()
|
|
37
|
+
key_bytes = process.ReadMemory(key_ptr, key_len, err)
|
|
38
|
+
|
|
39
|
+
if err.Success() and key_bytes:
|
|
40
|
+
hex_key = " ".join(f"{b:02x}" for b in key_bytes)
|
|
41
|
+
log(f"[{key_count}] AES_SETKEY_ENC bits={keybits} key={hex_key}")
|
|
42
|
+
|
|
43
|
+
# Save raw key to file
|
|
44
|
+
with open("/tmp/stick-aes-key.bin", "wb") as f:
|
|
45
|
+
f.write(key_bytes)
|
|
46
|
+
log(f" Raw key saved to /tmp/stick-aes-key.bin")
|
|
47
|
+
else:
|
|
48
|
+
log(f"[{key_count}] AES_SETKEY_ENC bits={keybits} (failed to read key: {err})")
|
|
49
|
+
|
|
50
|
+
# Don't stop, continue running
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def on_setkey_dec(frame, bp_loc, dict):
|
|
54
|
+
global key_count
|
|
55
|
+
key_count += 1
|
|
56
|
+
thread = frame.GetThread()
|
|
57
|
+
process = thread.GetProcess()
|
|
58
|
+
|
|
59
|
+
keybits = frame.FindRegister("edx").GetValueAsUnsigned()
|
|
60
|
+
key_ptr = frame.FindRegister("rsi").GetValueAsUnsigned()
|
|
61
|
+
key_len = keybits // 8
|
|
62
|
+
|
|
63
|
+
err = lldb.SBError()
|
|
64
|
+
key_bytes = process.ReadMemory(key_ptr, key_len, err)
|
|
65
|
+
|
|
66
|
+
if err.Success() and key_bytes:
|
|
67
|
+
hex_key = " ".join(f"{b:02x}" for b in key_bytes)
|
|
68
|
+
log(f"[{key_count}] AES_SETKEY_DEC bits={keybits} key={hex_key}")
|
|
69
|
+
else:
|
|
70
|
+
log(f"[{key_count}] AES_SETKEY_DEC bits={keybits} (failed to read key: {err})")
|
|
71
|
+
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def on_crypt_cbc(frame, bp_loc, dict):
|
|
75
|
+
thread = frame.GetThread()
|
|
76
|
+
process = thread.GetProcess()
|
|
77
|
+
|
|
78
|
+
# rdi=ctx, esi=mode, rdx=length, rcx=iv, r8=input, r9=output
|
|
79
|
+
mode = frame.FindRegister("esi").GetValueAsUnsigned()
|
|
80
|
+
length = frame.FindRegister("rdx").GetValueAsUnsigned()
|
|
81
|
+
|
|
82
|
+
mode_str = "ENC" if mode == 1 else "DEC"
|
|
83
|
+
log(f" AES_CBC {mode_str} len={length}")
|
|
84
|
+
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
def __lldb_init_module(debugger, internal_dict):
|
|
88
|
+
target = debugger.GetSelectedTarget()
|
|
89
|
+
if not target:
|
|
90
|
+
print("ERROR: No target. Attach to HardwareManager first.")
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
# Clear old log
|
|
94
|
+
with open(LOG_PATH, "w") as f:
|
|
95
|
+
f.write("=== lldb AES key capture started ===\n")
|
|
96
|
+
|
|
97
|
+
# Find the mbedcrypto module
|
|
98
|
+
found_module = None
|
|
99
|
+
for module in target.module_iter():
|
|
100
|
+
name = module.GetFileSpec().GetFilename()
|
|
101
|
+
if name and "mbedcrypto" in name:
|
|
102
|
+
found_module = module
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if not found_module:
|
|
106
|
+
print("WARNING: libmbedcrypto not found in loaded modules")
|
|
107
|
+
print("Available modules:")
|
|
108
|
+
for module in target.module_iter():
|
|
109
|
+
print(f" {module.GetFileSpec()}")
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
print(f"Found: {found_module.GetFileSpec()}")
|
|
113
|
+
|
|
114
|
+
# Set breakpoints on the symbols
|
|
115
|
+
bp_enc = target.BreakpointCreateByName("mbedtls_aes_setkey_enc", found_module.GetFileSpec().GetFilename())
|
|
116
|
+
if bp_enc.IsValid():
|
|
117
|
+
bp_enc.SetScriptCallbackFunction("lldb_capture_aes.on_setkey_enc")
|
|
118
|
+
bp_enc.SetAutoContinue(True)
|
|
119
|
+
print(f"Breakpoint on mbedtls_aes_setkey_enc: {bp_enc}")
|
|
120
|
+
else:
|
|
121
|
+
print("ERROR: Could not set breakpoint on mbedtls_aes_setkey_enc")
|
|
122
|
+
|
|
123
|
+
bp_dec = target.BreakpointCreateByName("mbedtls_aes_setkey_dec", found_module.GetFileSpec().GetFilename())
|
|
124
|
+
if bp_dec.IsValid():
|
|
125
|
+
bp_dec.SetScriptCallbackFunction("lldb_capture_aes.on_setkey_dec")
|
|
126
|
+
bp_dec.SetAutoContinue(True)
|
|
127
|
+
print(f"Breakpoint on mbedtls_aes_setkey_dec: {bp_dec}")
|
|
128
|
+
|
|
129
|
+
bp_cbc = target.BreakpointCreateByName("mbedtls_aes_crypt_cbc", found_module.GetFileSpec().GetFilename())
|
|
130
|
+
if bp_cbc.IsValid():
|
|
131
|
+
bp_cbc.SetScriptCallbackFunction("lldb_capture_aes.on_crypt_cbc")
|
|
132
|
+
bp_cbc.SetAutoContinue(True)
|
|
133
|
+
print(f"Breakpoint on mbedtls_aes_crypt_cbc: {bp_cbc}")
|
|
134
|
+
|
|
135
|
+
log("Breakpoints set. Connect to the Stick in the UI, then check this log.")
|
|
136
|
+
print(f"\nReady! Keys will be logged to {LOG_PATH}")
|
|
137
|
+
print("Now type 'continue' and connect to the Stick in the UI.")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Extract the 0x48 TCP-auth HMAC key from a running Hardware Manager.
|
|
3
|
+
|
|
4
|
+
The 0x48 handshake message ends with HMAC-SHA256(key, msg[0:82]). The key is
|
|
5
|
+
an internal HWM constant we can't derive offline. But the HMAC routine
|
|
6
|
+
FUN_1004080b0 receives it directly:
|
|
7
|
+
|
|
8
|
+
FUN_1004080b0(rdi=key, rsi=keylen, rdx=msg, rcx=msglen, r8=out, r9=32)
|
|
9
|
+
|
|
10
|
+
When HWM builds the 0x48 it calls this with rcx (msglen) == 0x52. We set one
|
|
11
|
+
breakpoint on FUN_1004080b0, and on the call where rcx==0x52 we dump rdi/rsi
|
|
12
|
+
— that's the key. One hit, then detach. Not a per-frame breakpoint, so it
|
|
13
|
+
won't starve HWM the way the old encrypt-path breakpoints did.
|
|
14
|
+
|
|
15
|
+
Usage (driven by tools/hmac-key.sh):
|
|
16
|
+
lldb -p <PID> --batch \
|
|
17
|
+
-o "command script import tools/lldb_hmac_key.py" -o "detach" -o "quit"
|
|
18
|
+
Then in HWM: disconnect from the Stick and reconnect.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import lldb
|
|
25
|
+
except ImportError:
|
|
26
|
+
lldb = None
|
|
27
|
+
|
|
28
|
+
HMAC_FN_FILEADDR = 0x1004080b0 # FUN_1004080b0 — HMAC-SHA256
|
|
29
|
+
LOG_PATH = "/tmp/stick-hmac-key.log"
|
|
30
|
+
_logf = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def log(m=""):
|
|
34
|
+
print(m)
|
|
35
|
+
sys.stdout.flush()
|
|
36
|
+
if _logf:
|
|
37
|
+
_logf.write(m + "\n")
|
|
38
|
+
_logf.flush()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def hexs(b):
|
|
42
|
+
return "".join("%02x" % x for x in bytearray(b))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def hmac_bp_callback(frame, bp_loc, internal_dict):
|
|
46
|
+
"""Fires on every FUN_1004080b0 call; reports the one for the 0x48 (msglen 0x52)."""
|
|
47
|
+
process = frame.GetThread().GetProcess()
|
|
48
|
+
reg = lambda n: frame.FindRegister(n).GetValueAsUnsigned()
|
|
49
|
+
key, keylen, msglen = reg("rdi"), reg("rsi"), reg("rcx")
|
|
50
|
+
if msglen != 0x52:
|
|
51
|
+
return False # not the 0x48 auth HMAC — keep going
|
|
52
|
+
err = lldb.SBError()
|
|
53
|
+
n = keylen if 0 < keylen <= 512 else 64
|
|
54
|
+
kd = process.ReadMemory(key, n, err)
|
|
55
|
+
md = process.ReadMemory(reg("rdx"), 0x52, err)
|
|
56
|
+
log("")
|
|
57
|
+
log(">>> 0x48 auth HMAC caught")
|
|
58
|
+
log(" key length : %d" % keylen)
|
|
59
|
+
log(" key (hex) : %s" % (hexs(kd) if kd else "(unreadable)"))
|
|
60
|
+
if kd:
|
|
61
|
+
try:
|
|
62
|
+
log(" key (text) : %r" % bytearray(kd).decode("utf-8"))
|
|
63
|
+
except Exception:
|
|
64
|
+
log(" key (text) : (not utf-8)")
|
|
65
|
+
log(" msg[0:82] : %s" % (hexs(md) if md else "(unreadable)"))
|
|
66
|
+
log("")
|
|
67
|
+
log(">>> got it — detaching.")
|
|
68
|
+
return True # stop so the batch script can detach
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def __lldb_init_module(debugger, internal_dict):
|
|
72
|
+
global _logf
|
|
73
|
+
_logf = open(LOG_PATH, "w")
|
|
74
|
+
log("=== HMAC-key extraction %s ===" % __import__("time").ctime())
|
|
75
|
+
target = debugger.GetSelectedTarget()
|
|
76
|
+
process = target.GetProcess()
|
|
77
|
+
|
|
78
|
+
saddr = target.ResolveFileAddress(HMAC_FN_FILEADDR)
|
|
79
|
+
laddr = saddr.GetLoadAddress(target)
|
|
80
|
+
log("HMAC fn file 0x%x -> load 0x%x" % (HMAC_FN_FILEADDR, laddr))
|
|
81
|
+
|
|
82
|
+
bp = target.BreakpointCreateByAddress(laddr)
|
|
83
|
+
bp.SetScriptCallbackFunction("lldb_hmac_key.hmac_bp_callback")
|
|
84
|
+
if bp.GetNumLocations() == 0:
|
|
85
|
+
log("!!! breakpoint resolved 0 locations — wrong address/module")
|
|
86
|
+
return
|
|
87
|
+
log("breakpoint armed on the HMAC function (%d location)" % bp.GetNumLocations())
|
|
88
|
+
log("")
|
|
89
|
+
log(">>> NOW: in HWM, disconnect from the Stick and reconnect.")
|
|
90
|
+
log(">>> waiting for the 0x48 handshake ...")
|
|
91
|
+
|
|
92
|
+
process.Continue() # blocks until the 0x48 HMAC call stops us
|
|
93
|
+
log(">>> done — log at %s" % LOG_PATH)
|