@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.
Files changed (226) hide show
  1. package/CLAUDE.md +176 -0
  2. package/README.md +146 -0
  3. package/config.schema.json +143 -0
  4. package/dist/cli/stick.d.ts +3 -0
  5. package/dist/cli/stick.d.ts.map +1 -0
  6. package/dist/cli/stick.js +176 -0
  7. package/dist/cli/stick.js.map +1 -0
  8. package/dist/color/cct.d.ts +3 -0
  9. package/dist/color/cct.d.ts.map +1 -0
  10. package/dist/color/cct.js +33 -0
  11. package/dist/color/cct.js.map +1 -0
  12. package/dist/color/dimmer.d.ts +3 -0
  13. package/dist/color/dimmer.d.ts.map +1 -0
  14. package/dist/color/dimmer.js +16 -0
  15. package/dist/color/dimmer.js.map +1 -0
  16. package/dist/color/hsvcct.d.ts +3 -0
  17. package/dist/color/hsvcct.d.ts.map +1 -0
  18. package/dist/color/hsvcct.js +52 -0
  19. package/dist/color/hsvcct.js.map +1 -0
  20. package/dist/color/index.d.ts +7 -0
  21. package/dist/color/index.d.ts.map +1 -0
  22. package/dist/color/index.js +28 -0
  23. package/dist/color/index.js.map +1 -0
  24. package/dist/color/parsers.d.ts +20 -0
  25. package/dist/color/parsers.d.ts.map +1 -0
  26. package/dist/color/parsers.js +232 -0
  27. package/dist/color/parsers.js.map +1 -0
  28. package/dist/color/rgb.d.ts +3 -0
  29. package/dist/color/rgb.d.ts.map +1 -0
  30. package/dist/color/rgb.js +32 -0
  31. package/dist/color/rgb.js.map +1 -0
  32. package/dist/color/rgbaw.d.ts +3 -0
  33. package/dist/color/rgbaw.d.ts.map +1 -0
  34. package/dist/color/rgbaw.js +48 -0
  35. package/dist/color/rgbaw.js.map +1 -0
  36. package/dist/color/rgbw.d.ts +3 -0
  37. package/dist/color/rgbw.d.ts.map +1 -0
  38. package/dist/color/rgbw.js +37 -0
  39. package/dist/color/rgbw.js.map +1 -0
  40. package/dist/color/rgbww.d.ts +3 -0
  41. package/dist/color/rgbww.d.ts.map +1 -0
  42. package/dist/color/rgbww.js +54 -0
  43. package/dist/color/rgbww.js.map +1 -0
  44. package/dist/color/types.d.ts +64 -0
  45. package/dist/color/types.d.ts.map +1 -0
  46. package/dist/color/types.js +91 -0
  47. package/dist/color/types.js.map +1 -0
  48. package/dist/config.d.ts +60 -0
  49. package/dist/config.d.ts.map +1 -0
  50. package/dist/config.js +191 -0
  51. package/dist/config.js.map +1 -0
  52. package/dist/controller.d.ts +32 -0
  53. package/dist/controller.d.ts.map +1 -0
  54. package/dist/controller.js +128 -0
  55. package/dist/controller.js.map +1 -0
  56. package/dist/index.d.ts +4 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +7 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/platform.d.ts +17 -0
  61. package/dist/platform.d.ts.map +1 -0
  62. package/dist/platform.js +96 -0
  63. package/dist/platform.js.map +1 -0
  64. package/dist/platformAccessory.d.ts +18 -0
  65. package/dist/platformAccessory.d.ts.map +1 -0
  66. package/dist/platformAccessory.js +81 -0
  67. package/dist/platformAccessory.js.map +1 -0
  68. package/dist/settings.d.ts +14 -0
  69. package/dist/settings.d.ts.map +1 -0
  70. package/dist/settings.js +15 -0
  71. package/dist/settings.js.map +1 -0
  72. package/dist/stick/frame.d.ts +16 -0
  73. package/dist/stick/frame.d.ts.map +1 -0
  74. package/dist/stick/frame.js +51 -0
  75. package/dist/stick/frame.js.map +1 -0
  76. package/dist/stick/kdf.d.ts +23 -0
  77. package/dist/stick/kdf.d.ts.map +1 -0
  78. package/dist/stick/kdf.js +90 -0
  79. package/dist/stick/kdf.js.map +1 -0
  80. package/dist/stick/protocol.d.ts +28 -0
  81. package/dist/stick/protocol.d.ts.map +1 -0
  82. package/dist/stick/protocol.js +78 -0
  83. package/dist/stick/protocol.js.map +1 -0
  84. package/dist/stick/session.d.ts +50 -0
  85. package/dist/stick/session.d.ts.map +1 -0
  86. package/dist/stick/session.js +301 -0
  87. package/dist/stick/session.js.map +1 -0
  88. package/examples/dmx.yaml +65 -0
  89. package/package.json +51 -0
  90. package/src/cli/stick.ts +198 -0
  91. package/src/color/cct.ts +40 -0
  92. package/src/color/dimmer.ts +24 -0
  93. package/src/color/hsvcct.ts +59 -0
  94. package/src/color/index.ts +34 -0
  95. package/src/color/parsers.ts +232 -0
  96. package/src/color/rgb.ts +33 -0
  97. package/src/color/rgbaw.ts +46 -0
  98. package/src/color/rgbw.ts +36 -0
  99. package/src/color/rgbww.ts +56 -0
  100. package/src/color/types.ts +150 -0
  101. package/src/config.ts +248 -0
  102. package/src/controller.ts +136 -0
  103. package/src/index.ts +9 -0
  104. package/src/platform.ts +118 -0
  105. package/src/platformAccessory.ts +102 -0
  106. package/src/settings.ts +18 -0
  107. package/src/stick/frame.ts +70 -0
  108. package/src/stick/kdf.ts +99 -0
  109. package/src/stick/protocol.ts +90 -0
  110. package/src/stick/session.ts +348 -0
  111. package/stick-de3.json +6 -0
  112. package/tools/aes-hook.c +1404 -0
  113. package/tools/analyze-pcap.mjs +143 -0
  114. package/tools/capture-aes-key.lldb +25 -0
  115. package/tools/capture-and-verify.sh +46 -0
  116. package/tools/capture-handshake.sh +59 -0
  117. package/tools/capture-send.sh +39 -0
  118. package/tools/crack-kdf.mjs +121 -0
  119. package/tools/crack-key.mjs +170 -0
  120. package/tools/decode-frame.mjs +110 -0
  121. package/tools/decrypt-011c.mjs +123 -0
  122. package/tools/decrypt-dmx.mjs +130 -0
  123. package/tools/decrypt-ssl2.ts +154 -0
  124. package/tools/derive-dmx-key.mjs +189 -0
  125. package/tools/diff-011c.mjs +127 -0
  126. package/tools/dump-pcap-summary.mjs +54 -0
  127. package/tools/dump-tcp-stream.mjs +123 -0
  128. package/tools/extract-011c.mjs +117 -0
  129. package/tools/extract-frame.mjs +78 -0
  130. package/tools/find-011c-key.mjs +140 -0
  131. package/tools/find-iv.mjs +153 -0
  132. package/tools/ghidra/README.md +136 -0
  133. package/tools/ghidra/__pycache__/dump-011c-callers.cpython-313.pyc +0 -0
  134. package/tools/ghidra/__pycache__/dump-011c-handler.cpython-313.pyc +0 -0
  135. package/tools/ghidra/__pycache__/dump-aes-candidates.cpython-313.pyc +0 -0
  136. package/tools/ghidra/__pycache__/dump-anchors.cpython-313.pyc +0 -0
  137. package/tools/ghidra/__pycache__/dump-auth2.cpython-313.pyc +0 -0
  138. package/tools/ghidra/__pycache__/dump-authenticate.cpython-313.pyc +0 -0
  139. package/tools/ghidra/__pycache__/dump-block-cipher.cpython-313.pyc +0 -0
  140. package/tools/ghidra/__pycache__/dump-cipher.cpython-313.pyc +0 -0
  141. package/tools/ghidra/__pycache__/dump-ctor-chain.cpython-313.pyc +0 -0
  142. package/tools/ghidra/__pycache__/dump-curve.cpython-313.pyc +0 -0
  143. package/tools/ghidra/__pycache__/dump-encompassing.cpython-313.pyc +0 -0
  144. package/tools/ghidra/__pycache__/dump-handshake.cpython-313.pyc +0 -0
  145. package/tools/ghidra/__pycache__/dump-kdf-chain.cpython-313.pyc +0 -0
  146. package/tools/ghidra/__pycache__/dump-kdf-context.cpython-313.pyc +0 -0
  147. package/tools/ghidra/__pycache__/dump-profile.cpython-313.pyc +0 -0
  148. package/tools/ghidra/__pycache__/dump-real-encrypt.cpython-313.pyc +0 -0
  149. package/tools/ghidra/__pycache__/dump-secret.cpython-313.pyc +0 -0
  150. package/tools/ghidra/__pycache__/dump-setsoftware.cpython-313.pyc +0 -0
  151. package/tools/ghidra/__pycache__/dump-stick3-vtable.cpython-313.pyc +0 -0
  152. package/tools/ghidra/__pycache__/dump-swxref.cpython-313.pyc +0 -0
  153. package/tools/ghidra/__pycache__/dump-vt-key-getter.cpython-313.pyc +0 -0
  154. package/tools/ghidra/__pycache__/find-cipher.cpython-313.pyc +0 -0
  155. package/tools/ghidra/__pycache__/find-ctor.cpython-313.pyc +0 -0
  156. package/tools/ghidra/__pycache__/find-md5.cpython-313.pyc +0 -0
  157. package/tools/ghidra/__pycache__/find-secret-users.cpython-313.pyc +0 -0
  158. package/tools/ghidra/__pycache__/find-stick3-crypto.cpython-313.pyc +0 -0
  159. package/tools/ghidra/__pycache__/find-stick3-device.cpython-313.pyc +0 -0
  160. package/tools/ghidra/__pycache__/find-stick3a-ctor.cpython-313.pyc +0 -0
  161. package/tools/ghidra/__pycache__/find-stick3anet-ctor.cpython-313.pyc +0 -0
  162. package/tools/ghidra/__pycache__/find-vtables.cpython-313.pyc +0 -0
  163. package/tools/ghidra/__pycache__/string-walk.cpython-313.pyc +0 -0
  164. package/tools/ghidra/dump-011c-callers.py +140 -0
  165. package/tools/ghidra/dump-011c-handler.py +158 -0
  166. package/tools/ghidra/dump-aes-candidates.py +99 -0
  167. package/tools/ghidra/dump-anchors.py +192 -0
  168. package/tools/ghidra/dump-auth2.py +50 -0
  169. package/tools/ghidra/dump-authenticate.py +71 -0
  170. package/tools/ghidra/dump-block-cipher.py +88 -0
  171. package/tools/ghidra/dump-cipher.py +106 -0
  172. package/tools/ghidra/dump-ctor-chain.py +107 -0
  173. package/tools/ghidra/dump-curve.py +112 -0
  174. package/tools/ghidra/dump-encompassing.py +146 -0
  175. package/tools/ghidra/dump-handshake.py +76 -0
  176. package/tools/ghidra/dump-kdf-chain.py +105 -0
  177. package/tools/ghidra/dump-kdf-context.py +72 -0
  178. package/tools/ghidra/dump-profile.py +59 -0
  179. package/tools/ghidra/dump-real-encrypt.py +90 -0
  180. package/tools/ghidra/dump-secret.py +84 -0
  181. package/tools/ghidra/dump-setsoftware.py +63 -0
  182. package/tools/ghidra/dump-stick3-vtable.py +182 -0
  183. package/tools/ghidra/dump-swxref.py +27 -0
  184. package/tools/ghidra/dump-vt-key-getter.py +170 -0
  185. package/tools/ghidra/find-cipher.py +210 -0
  186. package/tools/ghidra/find-ctor.py +109 -0
  187. package/tools/ghidra/find-md5.py +97 -0
  188. package/tools/ghidra/find-secret-users.py +98 -0
  189. package/tools/ghidra/find-stick3-crypto.py +190 -0
  190. package/tools/ghidra/find-stick3-device.py +111 -0
  191. package/tools/ghidra/find-stick3a-ctor.py +87 -0
  192. package/tools/ghidra/find-stick3anet-ctor.py +76 -0
  193. package/tools/ghidra/find-vtables.py +165 -0
  194. package/tools/ghidra/out-011c-callers-run.log +51 -0
  195. package/tools/ghidra/out-011c-run.log +50 -0
  196. package/tools/ghidra/out-vt-key-run.log +55 -0
  197. package/tools/ghidra/string-walk.py +180 -0
  198. package/tools/hmac-key.sh +51 -0
  199. package/tools/hwm-entitlements.plist +18 -0
  200. package/tools/inspect-plaintext.mjs +87 -0
  201. package/tools/lldb-capture-aes.py +137 -0
  202. package/tools/lldb_hmac_key.py +93 -0
  203. package/tools/lldb_pathA_scan.py +341 -0
  204. package/tools/lldb_pathC_keytrace.py +300 -0
  205. package/tools/lldb_pathD_kdf_watch.py +281 -0
  206. package/tools/pathA-launch-hwm.sh +86 -0
  207. package/tools/pathA-scan.sh +52 -0
  208. package/tools/pathC-keytrace.sh +49 -0
  209. package/tools/pathC-run.sh +71 -0
  210. package/tools/pathD-kdf-watch.sh +51 -0
  211. package/tools/quick-trigger.mjs +86 -0
  212. package/tools/run-hooked-hwmanager.sh +51 -0
  213. package/tools/scan-aes-schedule.mjs +135 -0
  214. package/tools/send_dmx.mjs +480 -0
  215. package/tools/stick-status.mjs +57 -0
  216. package/tools/test-aes-detector.mjs +100 -0
  217. package/tools/test-tcp-write.ts +274 -0
  218. package/tools/test-udp-dmx.ts +324 -0
  219. package/tools/test-x25519.mjs +87 -0
  220. package/tools/try-decrypt.ts +144 -0
  221. package/tools/try-hardcoded-key.mjs +86 -0
  222. package/tools/try-hardcoded-key.ts +103 -0
  223. package/tools/try-key.mjs +103 -0
  224. package/tools/verify-011c-key.mjs +121 -0
  225. package/tools/verify-kdf.mjs +142 -0
  226. package/tsconfig.json +19 -0
@@ -0,0 +1,87 @@
1
+ # Locate the Stick3A_DmxUniverse constructor and trace where the cipher
2
+ # state at this+0x1618+offsets gets initialized.
3
+
4
+ # @category Stick
5
+
6
+ import os, re
7
+ from ghidra.app.decompiler import DecompInterface
8
+ from ghidra.util.task import ConsoleTaskMonitor
9
+
10
+ OUT_DIR = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out13')
11
+ try: os.makedirs(OUT_DIR)
12
+ except OSError: pass
13
+
14
+ prog = currentProgram
15
+ fm = prog.getFunctionManager()
16
+ af = prog.getAddressFactory()
17
+ ref_mgr = prog.getReferenceManager()
18
+ mem = prog.getMemory()
19
+ monitor = ConsoleTaskMonitor()
20
+ decomp = DecompInterface()
21
+ decomp.openProgram(prog)
22
+
23
+
24
+ def addr(a): return af.getDefaultAddressSpace().getAddress(a)
25
+
26
+
27
+ def dump(func, out):
28
+ out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
29
+ out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
30
+ callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
31
+ for f in func.getCalledFunctions(monitor)))
32
+ out.write('\n-- callees (%d) --\n' % len(callees))
33
+ for c in callees: out.write(' ' + c + '\n')
34
+ callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
35
+ for f in func.getCallingFunctions(monitor)))
36
+ out.write('\n-- callers (%d) --\n' % len(callers))
37
+ for c in callers: out.write(' ' + c + '\n')
38
+ out.write('\n-- decompilation --\n')
39
+ res = decomp.decompileFunction(func, 240, monitor)
40
+ if res.decompileCompleted():
41
+ out.write(res.getDecompiledFunction().getC())
42
+ else:
43
+ out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
44
+ out.write('\n')
45
+
46
+
47
+ # Each of these is the typeinfo_ptr field; slot[0] (vptr value used by
48
+ # instances) = field + 8.
49
+ VTABLES = {
50
+ 'Stick3A_DmxUniverse_primary': 0x100962968,
51
+ 'Stick3A_DmxUniverse_sub1': 0x100962A48,
52
+ 'Stick3A_DmxUniverse_sub2': 0x100962A70,
53
+ 'Stick3A_DmxUniverse_sub3': 0x100962AB0,
54
+ 'Stick3A_DmxUniverse_sub4': 0x100962AE8,
55
+ 'Stick3A_DmxUniverse_sub5': 0x100962B28,
56
+ }
57
+
58
+ seen = set()
59
+ summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
60
+ summary.write('Stick3A_DmxUniverse ctor hunt\n\n')
61
+
62
+ for label, ti in VTABLES.items():
63
+ vptr = ti + 8
64
+ summary.write('\n#### %s vptr=0x%x\n' % (label, vptr))
65
+ code_refs = set()
66
+ for r in ref_mgr.getReferencesTo(addr(vptr)):
67
+ f = fm.getFunctionContaining(r.getFromAddress())
68
+ if f is not None:
69
+ code_refs.add(f.getEntryPoint())
70
+ else:
71
+ summary.write(' data ref @ %s (no fn)\n' % r.getFromAddress())
72
+ summary.write(' code refs from %d fn(s):\n' % len(code_refs))
73
+ for ep in sorted(code_refs):
74
+ f = fm.getFunctionAt(ep)
75
+ summary.write(' %s @ %s (size %d)\n' %
76
+ (f.getName(True), f.getEntryPoint(),
77
+ f.getBody().getNumAddresses()))
78
+ if ep in seen: continue
79
+ seen.add(ep)
80
+ safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:120]
81
+ with open(os.path.join(OUT_DIR, '%s_%s_%s.txt' %
82
+ (label, f.getEntryPoint(), safe)), 'w') as o:
83
+ dump(f, o)
84
+
85
+ summary.write('\n=== total: %d ===\n' % len(seen))
86
+ summary.close()
87
+ print('find-stick3a-ctor: %d functions dumped to %s' % (len(seen), OUT_DIR))
@@ -0,0 +1,76 @@
1
+ # Find XHL_Stick3ANet device constructor + trace cipher init at this+0x1618.
2
+
3
+ # @category Stick
4
+
5
+ import os, re
6
+ from ghidra.app.decompiler import DecompInterface
7
+ from ghidra.util.task import ConsoleTaskMonitor
8
+
9
+ OUT_DIR = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out14')
10
+ try: os.makedirs(OUT_DIR)
11
+ except OSError: pass
12
+
13
+ prog = currentProgram
14
+ fm = prog.getFunctionManager()
15
+ af = prog.getAddressFactory()
16
+ ref_mgr = prog.getReferenceManager()
17
+ monitor = ConsoleTaskMonitor()
18
+ decomp = DecompInterface()
19
+ decomp.openProgram(prog)
20
+
21
+
22
+ def addr(a): return af.getDefaultAddressSpace().getAddress(a)
23
+
24
+
25
+ def dump(func, out):
26
+ out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
27
+ out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
28
+ callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
29
+ for f in func.getCalledFunctions(monitor)))
30
+ out.write('\n-- callees (%d) --\n' % len(callees))
31
+ for c in callees: out.write(' ' + c + '\n')
32
+ callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
33
+ for f in func.getCallingFunctions(monitor)))
34
+ out.write('\n-- callers (%d) --\n' % len(callers))
35
+ for c in callers: out.write(' ' + c + '\n')
36
+ out.write('\n-- decompilation --\n')
37
+ res = decomp.decompileFunction(func, 240, monitor)
38
+ if res.decompileCompleted():
39
+ out.write(res.getDecompiledFunction().getC())
40
+ else:
41
+ out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
42
+ out.write('\n')
43
+
44
+
45
+ # Primary Stick3ANet vptr = slot 0 of vtable @ 0x10095EF70 (typeinfo_ptr field)
46
+ # So vptr stored in instances = 0x10095EF70 + 8 = 0x10095EF78
47
+ VPTRS = {
48
+ 'Stick3ANet_primary': 0x10095EF78,
49
+ }
50
+
51
+ seen = set()
52
+ summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
53
+
54
+ for label, vptr in VPTRS.items():
55
+ summary.write('\n#### %s vptr=0x%x\n' % (label, vptr))
56
+ code_refs = set()
57
+ for r in ref_mgr.getReferencesTo(addr(vptr)):
58
+ f = fm.getFunctionContaining(r.getFromAddress())
59
+ if f is not None:
60
+ code_refs.add(f.getEntryPoint())
61
+ summary.write(' code refs from %d fn(s):\n' % len(code_refs))
62
+ for ep in sorted(code_refs):
63
+ f = fm.getFunctionAt(ep)
64
+ summary.write(' %s @ %s (size %d)\n' %
65
+ (f.getName(True), f.getEntryPoint(),
66
+ f.getBody().getNumAddresses()))
67
+ if ep in seen: continue
68
+ seen.add(ep)
69
+ safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:120]
70
+ with open(os.path.join(OUT_DIR, '%s_%s_%s.txt' %
71
+ (label, f.getEntryPoint(), safe)), 'w') as o:
72
+ dump(f, o)
73
+
74
+ summary.write('\n=== total: %d ===\n' % len(seen))
75
+ summary.close()
76
+ print('find-stick3anet-ctor: %d functions dumped to %s' % (len(seen), OUT_DIR))
@@ -0,0 +1,165 @@
1
+ # Locate vtables by scanning memory for the typeinfo pointers we found.
2
+ #
3
+ # From string-walk we have typeinfo struct addresses for each class:
4
+ # Stick3CryptDmxUniverse @ 0x100970dc8
5
+ # Stick5CryptDmxUniverse @ 0x1009843c0
6
+ # DasNetCryptDmxUniverse @ 0x100986688
7
+ # AesOStream @ 0x100851c50
8
+ # AesIStream @ 0x100851ae0
9
+ # AesOStreamBuffer @ 0x100851c68
10
+ # AesIStreamBuffer @ 0x100851af8
11
+ # DasEccAesCryptography @ 0x100851ee0
12
+ # DasDhRsaCryptography<1024> @ 0x100ae7e20
13
+ # DasDhRsaCryptography<512> @ 0x100b12398
14
+ # DasEccStreamCryptography @ 0x100852088
15
+ # DasCryptography @ 0x100851d50
16
+ #
17
+ # An Itanium-ABI vtable is laid out:
18
+ # -0x10 offset_to_top
19
+ # -0x08 typeinfo_ptr ← this is the typeinfo struct address
20
+ # +0x00 vt_slot[0] ← what objects' vptr points at
21
+ # +0x08 vt_slot[1]
22
+ # ...
23
+ #
24
+ # So scanning memory for an 8-byte little-endian value equal to a typeinfo
25
+ # address finds the typeinfo_ptr field of every vtable for that class
26
+ # (primary + secondary base vtables). Vtable[0] = that_address + 8.
27
+ #
28
+ # Then dump every fn pointer at vt_slot[N] for N in [0..32) until 0/non-code.
29
+
30
+ # @category Stick
31
+
32
+ import os, re, struct
33
+ from ghidra.app.decompiler import DecompInterface
34
+ from ghidra.util.task import ConsoleTaskMonitor
35
+ from java.lang import Long
36
+
37
+ OUT_DIR = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out5')
38
+ try:
39
+ os.makedirs(OUT_DIR)
40
+ except OSError:
41
+ pass
42
+
43
+ prog = currentProgram
44
+ fm = prog.getFunctionManager()
45
+ mem = prog.getMemory()
46
+ af = prog.getAddressFactory()
47
+ monitor = ConsoleTaskMonitor()
48
+ decomp = DecompInterface()
49
+ decomp.openProgram(prog)
50
+
51
+
52
+ def addr(a):
53
+ return af.getDefaultAddressSpace().getAddress(a)
54
+
55
+
56
+ def get_ptr(a):
57
+ try:
58
+ return mem.getLong(a) & 0xFFFFFFFFFFFFFFFF
59
+ except Exception:
60
+ return None
61
+
62
+
63
+ def find_le_qword(value):
64
+ """Return all addresses where the 8-byte LE encoding of `value` occurs."""
65
+ pat = struct.pack('<Q', value)
66
+ hits = []
67
+ a = mem.findBytes(prog.getMinAddress(), pat, None, True, monitor)
68
+ while a is not None and len(hits) < 200:
69
+ hits.append(a)
70
+ try:
71
+ a = mem.findBytes(a.add(1), pat, None, True, monitor)
72
+ except Exception:
73
+ break
74
+ return hits
75
+
76
+
77
+ def dump_function(func, out):
78
+ out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
79
+ out.write('signature: %s\n' % func.getPrototypeString(True, True))
80
+ out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
81
+ callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
82
+ for f in func.getCalledFunctions(monitor)))
83
+ out.write('\n-- callees (%d) --\n' % len(callees))
84
+ for c in callees:
85
+ out.write(' ' + c + '\n')
86
+ callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
87
+ for f in func.getCallingFunctions(monitor)))
88
+ out.write('\n-- callers (%d) --\n' % len(callers))
89
+ for c in callers:
90
+ out.write(' ' + c + '\n')
91
+ out.write('\n-- decompilation --\n')
92
+ res = decomp.decompileFunction(func, 180, monitor)
93
+ if res.decompileCompleted():
94
+ out.write(res.getDecompiledFunction().getC())
95
+ else:
96
+ out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
97
+ out.write('\n')
98
+
99
+
100
+ # (class_label, typeinfo_addr)
101
+ TARGETS = [
102
+ ('Stick3CryptDmxUniverse', 0x100970dc8),
103
+ ('Stick5CryptDmxUniverse', 0x1009843c0),
104
+ ('DasNetCryptDmxUniverse', 0x100986688),
105
+ ('AesOStream', 0x100851c50),
106
+ ('AesIStream', 0x100851ae0),
107
+ ('AesOStreamBuffer', 0x100851c68),
108
+ ('AesIStreamBuffer', 0x100851af8),
109
+ ('DasEccAesCryptography', 0x100851ee0),
110
+ ('DasDhRsaCryptography_1024', 0x100ae7e20),
111
+ ('DasDhRsaCryptography_512', 0x100b12398),
112
+ ('DasEccStreamCryptography', 0x100852088),
113
+ ('DasCryptography', 0x100851d50),
114
+ ]
115
+
116
+ summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
117
+ summary.write('Vtable hunt by typeinfo-ptr search — ESA2 HardwareManager\n\n')
118
+
119
+ seen_funcs = set()
120
+
121
+ for label, ti_addr in TARGETS:
122
+ summary.write('\n#### %s typeinfo @ 0x%x\n' % (label, ti_addr))
123
+ occurrences = find_le_qword(ti_addr)
124
+ summary.write(' typeinfo_ptr field occurs %d time(s)\n' % len(occurrences))
125
+ for occ in occurrences:
126
+ # occ = address of the typeinfo_ptr field in the vtable.
127
+ # vt_slot[0] is at occ + 8.
128
+ summary.write(' typeinfo_ptr @ %s -> vtable slot[0] @ 0x%x\n' %
129
+ (occ, int(occ.getOffset()) + 8))
130
+ base = int(occ.getOffset()) + 8
131
+ # Walk up to 32 slots
132
+ for slot in range(0, 32):
133
+ sa = addr(base + slot * 8)
134
+ fp = get_ptr(sa)
135
+ if fp is None:
136
+ summary.write(' vt[%2d] @ 0x%x <unmapped>\n' %
137
+ (slot, base + slot * 8))
138
+ break
139
+ # End-of-vtable: many ABIs put zeros or a new offset-to-top
140
+ if fp == 0:
141
+ summary.write(' vt[%2d] @ 0x%x = 0 (end?)\n' %
142
+ (slot, base + slot * 8))
143
+ break
144
+ fa = addr(fp)
145
+ f = fm.getFunctionAt(fa) or fm.getFunctionContaining(fa)
146
+ if f is None:
147
+ # Could be a pure-virtual stub or non-code; stop
148
+ summary.write(' vt[%2d] @ 0x%x -> 0x%x (no fn)\n' %
149
+ (slot, base + slot * 8, fp))
150
+ break
151
+ summary.write(' vt[%2d] @ 0x%x -> %s @ %s (size %d)\n' %
152
+ (slot, base + slot * 8, f.getName(True),
153
+ f.getEntryPoint(), f.getBody().getNumAddresses()))
154
+ if f.getEntryPoint() in seen_funcs:
155
+ continue
156
+ seen_funcs.add(f.getEntryPoint())
157
+ safe_fn = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:120]
158
+ path = os.path.join(OUT_DIR, '%s_vt%02d_%s_%s.txt' %
159
+ (label, slot, f.getEntryPoint(), safe_fn))
160
+ with open(path, 'w') as o:
161
+ dump_function(f, o)
162
+
163
+ summary.write('\n=== total functions dumped: %d ===\n' % len(seen_funcs))
164
+ summary.close()
165
+ print('find-vtables: dumped %d functions to %s' % (len(seen_funcs), OUT_DIR))
@@ -0,0 +1,51 @@
1
+ INFO Using log config file: jar:file:/opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Framework/Generic/lib/Generic.jar!/generic.log4j.xml (LoggingInitialization)
2
+ INFO Using log file: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/application.log (LoggingInitialization)
3
+ INFO Loading user preferences: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/preferences (Preferences)
4
+ INFO Searching for classes... (ClassSearcher)
5
+ INFO Class search complete (631 ms) (ClassSearcher)
6
+ INFO Initializing SSL Context (DefaultSSLContextInitializer)
7
+ INFO Initializing Random Number Generator... (SecureRandomFactory)
8
+ INFO Random Number Generator initialization complete: NativePRNGNonBlocking (SecureRandomFactory)
9
+ INFO Trust manager disabled, cacerts have not been set (DefaultTrustManagerFactory)
10
+ INFO Headless startup complete (1495 ms) (AnalyzeHeadless)
11
+ INFO Class searcher loaded 59 extension points (20 false positives) (ClassSearcher)
12
+ INFO HEADLESS Script Paths:
13
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SwiftDemangler/ghidra_scripts
14
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/8051/ghidra_scripts
15
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BSim/ghidra_scripts
16
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/VersionTracking/ghidra_scripts
17
+ /Users/miker/code/homebridge-stick-de3/tools/ghidra
18
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/GnuDemangler/ghidra_scripts
19
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/DecompilerDependent/ghidra_scripts
20
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FunctionID/ghidra_scripts
21
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SystemEmulation/ghidra_scripts
22
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/Atmel/ghidra_scripts
23
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/PIC/ghidra_scripts
24
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger/ghidra_scripts
25
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PyGhidra/ghidra_scripts
26
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Decompiler/ghidra_scripts
27
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger-rmi-trace/ghidra_scripts
28
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/WildcardAssembler/ghidra_scripts
29
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FileFormats/ghidra_scripts
30
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/JVM/ghidra_scripts
31
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Base/ghidra_scripts
32
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BytePatterns/ghidra_scripts
33
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PDB/ghidra_scripts
34
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/DATA/ghidra_scripts
35
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts (HeadlessAnalyzer)
36
+ INFO HEADLESS: execution starts (HeadlessAnalyzer)
37
+ INFO Opening existing project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessAnalyzer)
38
+ INFO Opening project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessProject)
39
+ INFO REPORT: Processing project file: /HardwareManager (HeadlessAnalyzer)
40
+ INFO REPORT: Execute script: dump-011c-callers.py (HeadlessAnalyzer)
41
+ INFO SCRIPT: /Users/miker/code/homebridge-stick-de3/tools/ghidra/dump-011c-callers.py (HeadlessAnalyzer)
42
+ dumping handler-related functions ...
43
+ finding vtable[+0xe8] load sites ...
44
+ e8 displacement sites: 494
45
+ searching for byte pattern FF 90 E8 00 00 00 (call [reg+0xe8]) ...
46
+ found 0 call-via-[reg+0xe8] sites
47
+ dump-011c-callers: done -> /Users/miker/code/homebridge-stick-de3/tools/ghidra/out-011c-callers
48
+ INFO REPORT: Save succeeded for processed file: /HardwareManager (HeadlessAnalyzer)
49
+ Last used Python executable: ['/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3']
50
+ Using Python command: "/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3"
51
+ Switching to Ghidra virtual environment: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv
@@ -0,0 +1,50 @@
1
+ INFO Using log config file: jar:file:/opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Framework/Generic/lib/Generic.jar!/generic.log4j.xml (LoggingInitialization)
2
+ INFO Using log file: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/application.log (LoggingInitialization)
3
+ INFO Loading user preferences: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/preferences (Preferences)
4
+ INFO Searching for classes... (ClassSearcher)
5
+ INFO Class search complete (588 ms) (ClassSearcher)
6
+ INFO Initializing SSL Context (DefaultSSLContextInitializer)
7
+ INFO Initializing Random Number Generator... (SecureRandomFactory)
8
+ INFO Random Number Generator initialization complete: NativePRNGNonBlocking (SecureRandomFactory)
9
+ INFO Trust manager disabled, cacerts have not been set (DefaultTrustManagerFactory)
10
+ INFO Starting cache cleanup: /var/tmp/miker-ghidra/fscache2 (FileCacheMaintenanceDaemon)
11
+ INFO Finished cache cleanup, estimated storage used: 0 (FileCacheMaintenanceDaemon)
12
+ INFO Headless startup complete (1705 ms) (AnalyzeHeadless)
13
+ INFO Class searcher loaded 59 extension points (20 false positives) (ClassSearcher)
14
+ INFO HEADLESS Script Paths:
15
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SwiftDemangler/ghidra_scripts
16
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/8051/ghidra_scripts
17
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BSim/ghidra_scripts
18
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/VersionTracking/ghidra_scripts
19
+ /Users/miker/code/homebridge-stick-de3/tools/ghidra
20
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/GnuDemangler/ghidra_scripts
21
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/DecompilerDependent/ghidra_scripts
22
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FunctionID/ghidra_scripts
23
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SystemEmulation/ghidra_scripts
24
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/Atmel/ghidra_scripts
25
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/PIC/ghidra_scripts
26
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger/ghidra_scripts
27
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PyGhidra/ghidra_scripts
28
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Decompiler/ghidra_scripts
29
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger-rmi-trace/ghidra_scripts
30
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/WildcardAssembler/ghidra_scripts
31
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FileFormats/ghidra_scripts
32
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/JVM/ghidra_scripts
33
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Base/ghidra_scripts
34
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BytePatterns/ghidra_scripts
35
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PDB/ghidra_scripts
36
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/DATA/ghidra_scripts
37
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts (HeadlessAnalyzer)
38
+ INFO HEADLESS: execution starts (HeadlessAnalyzer)
39
+ INFO Opening existing project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessAnalyzer)
40
+ INFO Opening project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessProject)
41
+ INFO REPORT: Processing project file: /HardwareManager (HeadlessAnalyzer)
42
+ INFO REPORT: Execute script: dump-011c-handler.py (HeadlessAnalyzer)
43
+ INFO SCRIPT: /Users/miker/code/homebridge-stick-de3/tools/ghidra/dump-011c-handler.py (HeadlessAnalyzer)
44
+ scanning instructions ...
45
+ scanned 1537813 instructions
46
+ dump-011c-handler: 93 functions -> /Users/miker/code/homebridge-stick-de3/tools/ghidra/out-011c
47
+ INFO REPORT: Save succeeded for processed file: /HardwareManager (HeadlessAnalyzer)
48
+ Last used Python executable: ['/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3']
49
+ Using Python command: "/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3"
50
+ Switching to Ghidra virtual environment: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv
@@ -0,0 +1,55 @@
1
+ INFO Using log config file: jar:file:/opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Framework/Generic/lib/Generic.jar!/generic.log4j.xml (LoggingInitialization)
2
+ INFO Using log file: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/application.log (LoggingInitialization)
3
+ INFO Loading user preferences: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/preferences (Preferences)
4
+ INFO Searching for classes... (ClassSearcher)
5
+ INFO Class search complete (574 ms) (ClassSearcher)
6
+ INFO Initializing SSL Context (DefaultSSLContextInitializer)
7
+ INFO Initializing Random Number Generator... (SecureRandomFactory)
8
+ INFO Random Number Generator initialization complete: NativePRNGNonBlocking (SecureRandomFactory)
9
+ INFO Trust manager disabled, cacerts have not been set (DefaultTrustManagerFactory)
10
+ INFO Headless startup complete (1631 ms) (AnalyzeHeadless)
11
+ INFO Class searcher loaded 59 extension points (20 false positives) (ClassSearcher)
12
+ INFO HEADLESS Script Paths:
13
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SwiftDemangler/ghidra_scripts
14
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/8051/ghidra_scripts
15
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BSim/ghidra_scripts
16
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/VersionTracking/ghidra_scripts
17
+ /Users/miker/code/homebridge-stick-de3/tools/ghidra
18
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/GnuDemangler/ghidra_scripts
19
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/DecompilerDependent/ghidra_scripts
20
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FunctionID/ghidra_scripts
21
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/SystemEmulation/ghidra_scripts
22
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/Atmel/ghidra_scripts
23
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/PIC/ghidra_scripts
24
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger/ghidra_scripts
25
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PyGhidra/ghidra_scripts
26
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Decompiler/ghidra_scripts
27
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Debug/Debugger-rmi-trace/ghidra_scripts
28
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/WildcardAssembler/ghidra_scripts
29
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/FileFormats/ghidra_scripts
30
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/JVM/ghidra_scripts
31
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/Base/ghidra_scripts
32
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/BytePatterns/ghidra_scripts
33
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/PDB/ghidra_scripts
34
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Processors/DATA/ghidra_scripts
35
+ /opt/homebrew/Cellar/ghidra/12.1/libexec/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts (HeadlessAnalyzer)
36
+ INFO HEADLESS: execution starts (HeadlessAnalyzer)
37
+ INFO Opening existing project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessAnalyzer)
38
+ INFO Opening project: /Users/miker/code/homebridge-stick-de3/tools/ghidra/HwmESA2 (HeadlessProject)
39
+ INFO REPORT: Processing project file: /HardwareManager (HeadlessAnalyzer)
40
+ INFO REPORT: Execute script: dump-vt-key-getter.py (HeadlessAnalyzer)
41
+ INFO SCRIPT: /Users/miker/code/homebridge-stick-de3/tools/ghidra/dump-vt-key-getter.py (HeadlessAnalyzer)
42
+ --- references at 0x1001bbee5 ---
43
+
44
+ --- references TO Stick3ANet typeinfo @ 0x100970160 ---
45
+
46
+ --- bytes near typeinfo 0x100970160 (vtable + typeinfo) ---
47
+
48
+ --- searching for pointer 0x100970160 in __DATA ---
49
+
50
+ --- searching for pointer 0x100C820F0 (Stick3A typeinfo) ---
51
+ dump-vt-key-getter: done -> /Users/miker/code/homebridge-stick-de3/tools/ghidra/out-vt-key
52
+ INFO REPORT: Save succeeded for processed file: /HardwareManager (HeadlessAnalyzer)
53
+ Last used Python executable: ['/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3']
54
+ Using Python command: "/Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv/bin/python3"
55
+ Switching to Ghidra virtual environment: /Users/miker/Library/ghidra/ghidra_12.1_PUBLIC/venv