@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,76 @@
|
|
|
1
|
+
# Find + decompile HWM's LSAG_ALL / 0x47 / 0x48 TCP-auth handshake builder.
|
|
2
|
+
# Goal: learn what HWM puts in the 0x48 message — is the X25519 key a fresh
|
|
3
|
+
# ephemeral, or derived from a stored credential? That decides whether the
|
|
4
|
+
# auth is RE-able or needs "Security for Cloud Access" disabled.
|
|
5
|
+
# @category Stick
|
|
6
|
+
|
|
7
|
+
import os, re
|
|
8
|
+
from ghidra.app.decompiler import DecompInterface
|
|
9
|
+
from ghidra.util.task import ConsoleTaskMonitor
|
|
10
|
+
|
|
11
|
+
OUT = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out-handshake')
|
|
12
|
+
try: os.makedirs(OUT)
|
|
13
|
+
except OSError: pass
|
|
14
|
+
|
|
15
|
+
prog = currentProgram
|
|
16
|
+
fm = prog.getFunctionManager()
|
|
17
|
+
af = prog.getAddressFactory()
|
|
18
|
+
mem = prog.getMemory()
|
|
19
|
+
monitor = ConsoleTaskMonitor()
|
|
20
|
+
decomp = DecompInterface(); decomp.openProgram(prog)
|
|
21
|
+
listing = prog.getListing()
|
|
22
|
+
|
|
23
|
+
def decompile(f):
|
|
24
|
+
r = decomp.decompileFunction(f, 240, monitor)
|
|
25
|
+
return r.getDecompiledFunction().getC() if r.decompileCompleted() else '(fail)\n'
|
|
26
|
+
|
|
27
|
+
summary = open(os.path.join(OUT, 'SUMMARY.txt'), 'w')
|
|
28
|
+
|
|
29
|
+
# 1. find interesting strings
|
|
30
|
+
needles = ['LSAG_ALL', 'EccAesCryptography', 'DasNetEcc', 'Cloud', 'Security',
|
|
31
|
+
'curve25519', 'Curve25519', '25519', 'softwareName', 'authenticat']
|
|
32
|
+
hits = {}
|
|
33
|
+
for d in listing.getDefinedData(True):
|
|
34
|
+
v = d.getValue()
|
|
35
|
+
s = None
|
|
36
|
+
try:
|
|
37
|
+
if d.hasStringValue(): s = str(v)
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
if not s: continue
|
|
41
|
+
for n in needles:
|
|
42
|
+
if n in s:
|
|
43
|
+
hits.setdefault(n, []).append((d.getAddress(), s[:90]))
|
|
44
|
+
|
|
45
|
+
summary.write('=== string hits ===\n')
|
|
46
|
+
for n, lst in hits.items():
|
|
47
|
+
for a, s in lst[:12]:
|
|
48
|
+
summary.write(' [%s] %s : %r\n' % (n, a, s))
|
|
49
|
+
|
|
50
|
+
# 2. xref the LSAG_ALL / Ecc strings -> referencing functions -> decompile
|
|
51
|
+
seen = set()
|
|
52
|
+
def dump_fn(f, tag):
|
|
53
|
+
ep = str(f.getEntryPoint())
|
|
54
|
+
if ep in seen: return
|
|
55
|
+
seen.add(ep)
|
|
56
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:70]
|
|
57
|
+
with open(os.path.join(OUT, '%s_%s_%s.txt' % (tag, ep, safe)), 'w') as o:
|
|
58
|
+
o.write('==== %s @ %s (%d bytes) ====\n' %
|
|
59
|
+
(f.getName(True), ep, f.getBody().getNumAddresses()))
|
|
60
|
+
callees = sorted(set('%s @ %s' % (c.getName(True), c.getEntryPoint())
|
|
61
|
+
for c in f.getCalledFunctions(monitor)))
|
|
62
|
+
o.write('callees:\n ' + '\n '.join(callees) + '\n\n')
|
|
63
|
+
o.write(decompile(f))
|
|
64
|
+
summary.write(' dumped %s : %s @ %s\n' % (tag, f.getName(True), ep))
|
|
65
|
+
|
|
66
|
+
rm = prog.getReferenceManager()
|
|
67
|
+
summary.write('\n=== functions referencing handshake strings ===\n')
|
|
68
|
+
for n in ['LSAG_ALL', 'EccAesCryptography', 'DasNetEcc']:
|
|
69
|
+
for a, s in hits.get(n, []):
|
|
70
|
+
for r in rm.getReferencesTo(a):
|
|
71
|
+
f = fm.getFunctionContaining(r.getFromAddress())
|
|
72
|
+
if f: dump_fn(f, 'ref_' + n[:8])
|
|
73
|
+
|
|
74
|
+
summary.write('\n=== total dumped: %d ===\n' % len(seen))
|
|
75
|
+
summary.close()
|
|
76
|
+
print('dump-handshake: %d functions -> %s' % (len(seen), OUT))
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Decompile the X25519 -> AES-256-key KDF call chain.
|
|
2
|
+
# Chain (lldb Path D, ESA2 file addrs):
|
|
3
|
+
# on_listWidgetDevice_itemSelectionChanged
|
|
4
|
+
# -> 0x1005e1bd6 -> 0x1005e267b -> 0x1001780d1
|
|
5
|
+
# -> 0x10010780e -> 0x1003f57c6 (key installer)
|
|
6
|
+
# @category Stick
|
|
7
|
+
|
|
8
|
+
import os, re
|
|
9
|
+
from ghidra.app.decompiler import DecompInterface
|
|
10
|
+
from ghidra.util.task import ConsoleTaskMonitor
|
|
11
|
+
|
|
12
|
+
OUT_DIR = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out-kdf')
|
|
13
|
+
try: os.makedirs(OUT_DIR)
|
|
14
|
+
except OSError: pass
|
|
15
|
+
|
|
16
|
+
prog = currentProgram
|
|
17
|
+
fm = prog.getFunctionManager()
|
|
18
|
+
af = prog.getAddressFactory()
|
|
19
|
+
monitor = ConsoleTaskMonitor()
|
|
20
|
+
decomp = DecompInterface()
|
|
21
|
+
decomp.openProgram(prog)
|
|
22
|
+
def addr(a): return af.getDefaultAddressSpace().getAddress(a)
|
|
23
|
+
|
|
24
|
+
def func_for(a):
|
|
25
|
+
ad = addr(a)
|
|
26
|
+
return fm.getFunctionAt(ad) or fm.getFunctionContaining(ad)
|
|
27
|
+
|
|
28
|
+
def decompile(func):
|
|
29
|
+
res = decomp.decompileFunction(func, 240, monitor)
|
|
30
|
+
if res.decompileCompleted():
|
|
31
|
+
return res.getDecompiledFunction().getC()
|
|
32
|
+
return '(decompilation failed: %s)\n' % res.getErrorMessage()
|
|
33
|
+
|
|
34
|
+
def dump(func, out, with_decomp=True):
|
|
35
|
+
out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
|
|
36
|
+
out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
|
|
37
|
+
callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
38
|
+
for f in func.getCalledFunctions(monitor)))
|
|
39
|
+
out.write('\n-- callees (%d) --\n' % len(callees))
|
|
40
|
+
for c in callees: out.write(' ' + c + '\n')
|
|
41
|
+
callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
42
|
+
for f in func.getCallingFunctions(monitor)))
|
|
43
|
+
out.write('\n-- callers (%d) --\n' % len(callers))
|
|
44
|
+
for c in callers: out.write(' ' + c + '\n')
|
|
45
|
+
if with_decomp:
|
|
46
|
+
out.write('\n-- decompilation --\n')
|
|
47
|
+
out.write(decompile(func))
|
|
48
|
+
out.write('\n')
|
|
49
|
+
|
|
50
|
+
# Chain call sites -> the function each address sits inside.
|
|
51
|
+
CHAIN = [
|
|
52
|
+
('callsite_1005e1bd6', 0x1005e1bd6),
|
|
53
|
+
('callsite_1005e267b', 0x1005e267b),
|
|
54
|
+
('kdf_outer_1001780d1', 0x1001780d1),
|
|
55
|
+
('kdf_1001780d1_fn', 0x1001780d1),
|
|
56
|
+
('kdf_inner_10010780e', 0x10010780e),
|
|
57
|
+
('key_installer_1003f57c6', 0x1003f57c6),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
|
|
61
|
+
seen = set()
|
|
62
|
+
|
|
63
|
+
# 1. Dump every function in the chain.
|
|
64
|
+
chain_funcs = []
|
|
65
|
+
for label, a in CHAIN:
|
|
66
|
+
f = func_for(a)
|
|
67
|
+
if f is None:
|
|
68
|
+
summary.write(' %-26s 0x%x (NO FUNCTION)\n' % (label, a))
|
|
69
|
+
continue
|
|
70
|
+
ep = f.getEntryPoint()
|
|
71
|
+
summary.write(' %-26s 0x%x -> %s @ %s size=%d callees=%d\n' %
|
|
72
|
+
(label, a, f.getName(True), ep,
|
|
73
|
+
f.getBody().getNumAddresses(),
|
|
74
|
+
len(set(f.getCalledFunctions(monitor)))))
|
|
75
|
+
if str(ep) in seen:
|
|
76
|
+
continue
|
|
77
|
+
seen.add(str(ep))
|
|
78
|
+
chain_funcs.append((label, f))
|
|
79
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:80]
|
|
80
|
+
with open(os.path.join(OUT_DIR, '1_%s_%s.txt' % (label, safe)), 'w') as o:
|
|
81
|
+
dump(f, o)
|
|
82
|
+
|
|
83
|
+
# 2. Decompile every direct callee of the two KDF-critical functions, so the
|
|
84
|
+
# actual hash/curve arithmetic is visible.
|
|
85
|
+
KDF_CRITICAL = [0x1001780d1, 0x10010780e]
|
|
86
|
+
callee_seen = set(seen)
|
|
87
|
+
for a in KDF_CRITICAL:
|
|
88
|
+
f = func_for(a)
|
|
89
|
+
if f is None: continue
|
|
90
|
+
for callee in sorted(f.getCalledFunctions(monitor), key=lambda x: str(x.getEntryPoint())):
|
|
91
|
+
ep = str(callee.getEntryPoint())
|
|
92
|
+
if ep in callee_seen: continue
|
|
93
|
+
callee_seen.add(ep)
|
|
94
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', callee.getName(True))[:80]
|
|
95
|
+
with open(os.path.join(OUT_DIR, '2_callee_%s_%s.txt' %
|
|
96
|
+
(callee.getEntryPoint(), safe)), 'w') as o:
|
|
97
|
+
dump(callee, o)
|
|
98
|
+
summary.write(' callee %s @ %s size=%d\n' %
|
|
99
|
+
(callee.getName(True), callee.getEntryPoint(),
|
|
100
|
+
callee.getBody().getNumAddresses()))
|
|
101
|
+
|
|
102
|
+
summary.write('\n=== chain funcs: %d, callees dumped: %d ===\n' %
|
|
103
|
+
(len(chain_funcs), len(callee_seen) - len(seen)))
|
|
104
|
+
summary.close()
|
|
105
|
+
print('dump-kdf-chain: done -> %s' % OUT_DIR)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Dump everything around the SHA-1 KDF candidate to confirm role.
|
|
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()), 'out19')
|
|
10
|
+
try: os.makedirs(OUT_DIR)
|
|
11
|
+
except OSError: pass
|
|
12
|
+
|
|
13
|
+
prog = currentProgram
|
|
14
|
+
fm = prog.getFunctionManager()
|
|
15
|
+
af = prog.getAddressFactory()
|
|
16
|
+
monitor = ConsoleTaskMonitor()
|
|
17
|
+
decomp = DecompInterface()
|
|
18
|
+
decomp.openProgram(prog)
|
|
19
|
+
def addr(a): return af.getDefaultAddressSpace().getAddress(a)
|
|
20
|
+
|
|
21
|
+
def dump(func, out):
|
|
22
|
+
out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
|
|
23
|
+
out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
|
|
24
|
+
callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
25
|
+
for f in func.getCalledFunctions(monitor)))
|
|
26
|
+
out.write('\n-- callees (%d) --\n' % len(callees))
|
|
27
|
+
for c in callees: out.write(' ' + c + '\n')
|
|
28
|
+
callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
29
|
+
for f in func.getCallingFunctions(monitor)))
|
|
30
|
+
out.write('\n-- callers (%d) --\n' % len(callers))
|
|
31
|
+
for c in callers: out.write(' ' + c + '\n')
|
|
32
|
+
out.write('\n-- decompilation --\n')
|
|
33
|
+
res = decomp.decompileFunction(func, 240, monitor)
|
|
34
|
+
if res.decompileCompleted():
|
|
35
|
+
out.write(res.getDecompiledFunction().getC())
|
|
36
|
+
else:
|
|
37
|
+
out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
|
|
38
|
+
out.write('\n')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
ROOTS = {
|
|
42
|
+
'sha1_init_FUN_100407940': 0x100407940, # 39B — should be a tiny init
|
|
43
|
+
'sha1_update_FUN_100407970': 0x100407970, # update
|
|
44
|
+
'sha1_final_FUN_100407a60': 0x100407a60, # 615B
|
|
45
|
+
'kdf_candidate_FUN_10065aad0': 0x10065aad0, # KDF candidate using sha1 + salt
|
|
46
|
+
'dest_FUN_1006420d0': 0x1006420d0, # what kdf writes via
|
|
47
|
+
'getter_FUN_1006aebb0': 0x1006aebb0, # what supplies KDF input
|
|
48
|
+
'kdf_caller_FUN_10065ac20': 0x10065ac20,
|
|
49
|
+
'kdf_caller_FUN_10065d160': 0x10065d160,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
seen = set()
|
|
53
|
+
summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
|
|
54
|
+
|
|
55
|
+
for label, fp in ROOTS.items():
|
|
56
|
+
f = fm.getFunctionAt(addr(fp)) or fm.getFunctionContaining(addr(fp))
|
|
57
|
+
if f is None:
|
|
58
|
+
summary.write(' %s 0x%x (no fn)\n' % (label, fp)); continue
|
|
59
|
+
summary.write(' %-32s = %s @ %s size=%d callees=%d\n' %
|
|
60
|
+
(label, f.getName(True), f.getEntryPoint(),
|
|
61
|
+
f.getBody().getNumAddresses(),
|
|
62
|
+
len(set(f.getCalledFunctions(monitor)))))
|
|
63
|
+
if f.getEntryPoint() in seen: continue
|
|
64
|
+
seen.add(f.getEntryPoint())
|
|
65
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:120]
|
|
66
|
+
with open(os.path.join(OUT_DIR, '%s_%s_%s.txt' %
|
|
67
|
+
(label, f.getEntryPoint(), safe)), 'w') as o:
|
|
68
|
+
dump(f, o)
|
|
69
|
+
|
|
70
|
+
summary.write('\n=== total: %d ===\n' % len(seen))
|
|
71
|
+
summary.close()
|
|
72
|
+
print('dump-kdf-context: %d functions dumped to %s' % (len(seen), OUT_DIR))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Decompile HmSoftwareProfile::HmSoftwareProfile (+ its callees) — the
|
|
2
|
+
# constructor that hardcodes the software name + the auth key used as the
|
|
3
|
+
# HMAC-SHA256 key for the 0x48 handshake.
|
|
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 = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out-profile')
|
|
11
|
+
try: os.makedirs(OUT)
|
|
12
|
+
except OSError: pass
|
|
13
|
+
|
|
14
|
+
prog = currentProgram
|
|
15
|
+
fm = prog.getFunctionManager()
|
|
16
|
+
st = prog.getSymbolTable()
|
|
17
|
+
monitor = ConsoleTaskMonitor()
|
|
18
|
+
decomp = DecompInterface(); decomp.openProgram(prog)
|
|
19
|
+
|
|
20
|
+
def decompile(f):
|
|
21
|
+
r = decomp.decompileFunction(f, 240, monitor)
|
|
22
|
+
return r.getDecompiledFunction().getC() if r.decompileCompleted() else '(fail)\n'
|
|
23
|
+
|
|
24
|
+
seen = set()
|
|
25
|
+
def dump(f, tag, depth=0):
|
|
26
|
+
if f is None or str(f.getEntryPoint()) in seen: return
|
|
27
|
+
seen.add(str(f.getEntryPoint()))
|
|
28
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:70]
|
|
29
|
+
cs = sorted(set(f.getCalledFunctions(monitor)), key=lambda x: str(x.getEntryPoint()))
|
|
30
|
+
with open(os.path.join(OUT, '%s_%s_%s.txt' % (tag, f.getEntryPoint(), safe)), 'w') as o:
|
|
31
|
+
o.write('==== %s @ %s (%d bytes) ====\n' %
|
|
32
|
+
(f.getName(True), f.getEntryPoint(), f.getBody().getNumAddresses()))
|
|
33
|
+
o.write('callees: ' + ', '.join(str(c.getEntryPoint()) for c in cs) + '\n\n')
|
|
34
|
+
o.write(decompile(f))
|
|
35
|
+
if depth > 0:
|
|
36
|
+
for c in cs:
|
|
37
|
+
nm = c.getName(True)
|
|
38
|
+
if nm.startswith('_') or 'std::' in nm or 'operator' in nm or 'QArray' in nm \
|
|
39
|
+
or 'QString' in nm or 'QDir' in nm: continue
|
|
40
|
+
dump(c, tag + '_c', depth - 1)
|
|
41
|
+
|
|
42
|
+
summary = open(os.path.join(OUT, 'SUMMARY.txt'), 'w')
|
|
43
|
+
|
|
44
|
+
# find any symbol mentioning HmSoftwareProfile
|
|
45
|
+
hits = []
|
|
46
|
+
for s in st.getAllSymbols(True):
|
|
47
|
+
n = s.getName(True)
|
|
48
|
+
if 'HmSoftwareProfile' in n or 'SoftwareProfile' in n:
|
|
49
|
+
hits.append((n, s.getAddress()))
|
|
50
|
+
summary.write('=== HmSoftwareProfile symbols ===\n')
|
|
51
|
+
for n, a in hits[:40]:
|
|
52
|
+
summary.write(' %s @ %s\n' % (n, a))
|
|
53
|
+
f = fm.getFunctionContaining(a)
|
|
54
|
+
if f and ('HmSoftwareProfile' in f.getName(True)):
|
|
55
|
+
dump(f, 'profile', depth=2)
|
|
56
|
+
|
|
57
|
+
summary.write('\n=== dumped %d ===\n' % len(seen))
|
|
58
|
+
summary.close()
|
|
59
|
+
print('dump-profile: %d functions, %d symbol hits -> %s' % (len(seen), len(hits), OUT))
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Dump the ACTUAL encrypt function at FUN_100107950 (Stick3 cipher vt[3]).
|
|
2
|
+
# Plus walk callees to find the keystream generator.
|
|
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()), 'out15')
|
|
11
|
+
try: os.makedirs(OUT_DIR)
|
|
12
|
+
except OSError: pass
|
|
13
|
+
|
|
14
|
+
prog = currentProgram
|
|
15
|
+
fm = prog.getFunctionManager()
|
|
16
|
+
af = prog.getAddressFactory()
|
|
17
|
+
monitor = ConsoleTaskMonitor()
|
|
18
|
+
decomp = DecompInterface()
|
|
19
|
+
decomp.openProgram(prog)
|
|
20
|
+
|
|
21
|
+
def addr(a): return af.getDefaultAddressSpace().getAddress(a)
|
|
22
|
+
|
|
23
|
+
def dump(func, out):
|
|
24
|
+
out.write('==== %s @ %s ====\n' % (func.getName(True), func.getEntryPoint()))
|
|
25
|
+
out.write('size: %d body bytes\n' % func.getBody().getNumAddresses())
|
|
26
|
+
callees = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
27
|
+
for f in func.getCalledFunctions(monitor)))
|
|
28
|
+
out.write('\n-- callees (%d) --\n' % len(callees))
|
|
29
|
+
for c in callees: out.write(' ' + c + '\n')
|
|
30
|
+
callers = sorted(set('%s @ %s' % (f.getName(True), f.getEntryPoint())
|
|
31
|
+
for f in func.getCallingFunctions(monitor)))
|
|
32
|
+
out.write('\n-- callers (%d) --\n' % len(callers))
|
|
33
|
+
for c in callers: out.write(' ' + c + '\n')
|
|
34
|
+
out.write('\n-- decompilation --\n')
|
|
35
|
+
res = decomp.decompileFunction(func, 240, monitor)
|
|
36
|
+
if res.decompileCompleted():
|
|
37
|
+
out.write(res.getDecompiledFunction().getC())
|
|
38
|
+
else:
|
|
39
|
+
out.write('(decompilation failed: %s)\n' % res.getErrorMessage())
|
|
40
|
+
out.write('\n')
|
|
41
|
+
|
|
42
|
+
# Full Stick3 cipher vtable @ 0x10095F830 (typeinfo_ptr):
|
|
43
|
+
ROOTS = {
|
|
44
|
+
'vt0_FUN_100177990': 0x100177990,
|
|
45
|
+
'vt1_FUN_100177CB0': 0x100177CB0,
|
|
46
|
+
'vt2_FUN_100106290': 0x100106290,
|
|
47
|
+
'vt3_REAL_ENCRYPT_FUN_100107950': 0x100107950,
|
|
48
|
+
'vt4_FUN_1001C0650': 0x1001C0650,
|
|
49
|
+
'vt5_FUN_1001C0A50': 0x1001C0A50,
|
|
50
|
+
'vt6_FUN_1001C0CF0': 0x1001C0CF0,
|
|
51
|
+
'vt7_FUN_100180E90': 0x100180E90,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
seen = set()
|
|
55
|
+
summary = open(os.path.join(OUT_DIR, 'SUMMARY.txt'), 'w')
|
|
56
|
+
|
|
57
|
+
for label, fp in ROOTS.items():
|
|
58
|
+
f = fm.getFunctionAt(addr(fp)) or fm.getFunctionContaining(addr(fp))
|
|
59
|
+
if f is None:
|
|
60
|
+
summary.write(' %s 0x%x (no fn)\n' % (label, fp))
|
|
61
|
+
continue
|
|
62
|
+
summary.write(' %-40s = %s @ %s size=%d callees=%d\n' %
|
|
63
|
+
(label, f.getName(True), f.getEntryPoint(),
|
|
64
|
+
f.getBody().getNumAddresses(),
|
|
65
|
+
len(set(f.getCalledFunctions(monitor)))))
|
|
66
|
+
if f.getEntryPoint() in seen: continue
|
|
67
|
+
seen.add(f.getEntryPoint())
|
|
68
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:120]
|
|
69
|
+
with open(os.path.join(OUT_DIR, '%s_%s_%s.txt' %
|
|
70
|
+
(label, f.getEntryPoint(), safe)), 'w') as o:
|
|
71
|
+
dump(f, o)
|
|
72
|
+
|
|
73
|
+
# Walk into real-encrypt's callees
|
|
74
|
+
enc = fm.getFunctionAt(addr(0x100107950))
|
|
75
|
+
if enc:
|
|
76
|
+
summary.write('\n=== depth-1 callees of real encrypt fn ===\n')
|
|
77
|
+
for c in sorted(enc.getCalledFunctions(monitor), key=lambda f: f.getEntryPoint()):
|
|
78
|
+
summary.write(' callee: %s @ %s (size %d)\n' %
|
|
79
|
+
(c.getName(True), c.getEntryPoint(),
|
|
80
|
+
c.getBody().getNumAddresses()))
|
|
81
|
+
if c.getEntryPoint() in seen: continue
|
|
82
|
+
seen.add(c.getEntryPoint())
|
|
83
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', c.getName(True))[:120]
|
|
84
|
+
with open(os.path.join(OUT_DIR, 'depth1_%s_%s.txt' %
|
|
85
|
+
(c.getEntryPoint(), safe)), 'w') as o:
|
|
86
|
+
dump(c, o)
|
|
87
|
+
|
|
88
|
+
summary.write('\n=== total: %d ===\n' % len(seen))
|
|
89
|
+
summary.close()
|
|
90
|
+
print('dump-real-encrypt: %d functions dumped to %s' % (len(seen), OUT_DIR))
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Decompile the 33-byte-secret deobfuscator + locate its obfuscated source,
|
|
2
|
+
# and find the initializer that populates the P-256 curve constants.
|
|
3
|
+
# @category Stick
|
|
4
|
+
|
|
5
|
+
import os, struct
|
|
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()), 'out-secret')
|
|
10
|
+
try: os.makedirs(OUT_DIR)
|
|
11
|
+
except OSError: pass
|
|
12
|
+
|
|
13
|
+
prog = currentProgram
|
|
14
|
+
fm = prog.getFunctionManager()
|
|
15
|
+
af = prog.getAddressFactory()
|
|
16
|
+
mem = prog.getMemory()
|
|
17
|
+
rm = prog.getReferenceManager()
|
|
18
|
+
monitor = ConsoleTaskMonitor()
|
|
19
|
+
decomp = DecompInterface()
|
|
20
|
+
decomp.openProgram(prog)
|
|
21
|
+
def addr(a): return af.getDefaultAddressSpace().getAddress(a)
|
|
22
|
+
|
|
23
|
+
def rd(a, n):
|
|
24
|
+
buf = bytearray(n)
|
|
25
|
+
try:
|
|
26
|
+
mem.getBytes(addr(a), buf); return bytes(buf)
|
|
27
|
+
except Exception:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def decompile(func):
|
|
31
|
+
res = decomp.decompileFunction(func, 240, monitor)
|
|
32
|
+
if res.decompileCompleted():
|
|
33
|
+
return res.getDecompiledFunction().getC()
|
|
34
|
+
return '(decompile failed: %s)\n' % res.getErrorMessage()
|
|
35
|
+
|
|
36
|
+
out = open(os.path.join(OUT_DIR, 'REPORT.txt'), 'w')
|
|
37
|
+
|
|
38
|
+
# memory blocks
|
|
39
|
+
out.write('=== MEMORY BLOCKS ===\n')
|
|
40
|
+
for b in mem.getBlocks():
|
|
41
|
+
out.write(' %-20s %s - %s init=%s %s\n' %
|
|
42
|
+
(b.getName(), b.getStart(), b.getEnd(),
|
|
43
|
+
b.isInitialized(), 'rwx'[0] if b.isRead() else '-'))
|
|
44
|
+
|
|
45
|
+
# deobfuscators + network exchange + state getter
|
|
46
|
+
out.write('\n=== DEOBFUSCATORS / NETWORK / STATE ===\n')
|
|
47
|
+
for fp, label in [(0x100180E90, 'vt38_deobf_180E90'),
|
|
48
|
+
(0x100180D40, 'deobf_180D40'),
|
|
49
|
+
(0x1001C0650, 'vt20_net_exchange_1C0650'),
|
|
50
|
+
(0x1001C0A50, 'vt28_state_getter_1C0A50'),
|
|
51
|
+
(0x1001C0CF0, 'vt30_1C0CF0'),
|
|
52
|
+
(0x1004029b0, 'EC_decomp_rhs_4029b0')]:
|
|
53
|
+
f = fm.getFunctionAt(addr(fp)) or fm.getFunctionContaining(addr(fp))
|
|
54
|
+
if f is None:
|
|
55
|
+
out.write('%s: no fn\n' % label); continue
|
|
56
|
+
out.write('\n---- %s : %s @ %s ----\n' % (label, f.getName(True), f.getEntryPoint()))
|
|
57
|
+
out.write(decompile(f))
|
|
58
|
+
|
|
59
|
+
# hunt the obfuscated 33-byte source: scan __const for plausible region,
|
|
60
|
+
# and read candidate addresses
|
|
61
|
+
out.write('\n=== CANDIDATE OBFUSCATED-SECRET BYTES ===\n')
|
|
62
|
+
for a in [0x1007b0490, 0x1007b04a0, 0x1007af9c0, 0x1007c0240, 0x1007c0250]:
|
|
63
|
+
b = rd(a, 0x21)
|
|
64
|
+
out.write(' 0x%x : %s\n' % (a, b.hex() if b else '(unreadable)'))
|
|
65
|
+
|
|
66
|
+
# references to curve-constant addresses -> find the initializer
|
|
67
|
+
out.write('\n=== XREFS TO CURVE CONSTANTS ===\n')
|
|
68
|
+
for ca, label in [(0x100cf5bf0, 'prime_p'), (0x100cf5c10, 'order_n'),
|
|
69
|
+
(0x100cf5c30, 'generator_G'), (0x100cf8d11, 'decoded_secret')]:
|
|
70
|
+
out.write('\n-- %s @ 0x%x --\n' % (label, ca))
|
|
71
|
+
refs = rm.getReferencesTo(addr(ca))
|
|
72
|
+
cnt = 0
|
|
73
|
+
for r in refs:
|
|
74
|
+
cnt += 1
|
|
75
|
+
if cnt > 30:
|
|
76
|
+
out.write(' ... (more)\n'); break
|
|
77
|
+
fa = r.getFromAddress()
|
|
78
|
+
f = fm.getFunctionContaining(fa)
|
|
79
|
+
out.write(' %s %s from %s\n' %
|
|
80
|
+
(r.getReferenceType(), fa,
|
|
81
|
+
(f.getName(True) + ' @ ' + str(f.getEntryPoint())) if f else '(no fn)'))
|
|
82
|
+
|
|
83
|
+
out.close()
|
|
84
|
+
print('dump-secret: done -> %s' % OUT_DIR)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Find XHL_XHardwareLibrary::setSoftware() and its caller — the caller passes
|
|
2
|
+
# the hardcoded software-code / software-name / software-KEY. That key is the
|
|
3
|
+
# HMAC-SHA256 key for the 0x48 auth handshake. Also decompile the hash init
|
|
4
|
+
# (FUN_100408420) to confirm the hash algorithm.
|
|
5
|
+
# @category Stick
|
|
6
|
+
|
|
7
|
+
import os, re
|
|
8
|
+
from ghidra.app.decompiler import DecompInterface
|
|
9
|
+
from ghidra.util.task import ConsoleTaskMonitor
|
|
10
|
+
|
|
11
|
+
OUT = os.path.join(os.path.dirname(getSourceFile().getAbsolutePath()), 'out-sw')
|
|
12
|
+
try: os.makedirs(OUT)
|
|
13
|
+
except OSError: pass
|
|
14
|
+
|
|
15
|
+
prog = currentProgram
|
|
16
|
+
fm = prog.getFunctionManager()
|
|
17
|
+
af = prog.getAddressFactory()
|
|
18
|
+
rm = prog.getReferenceManager()
|
|
19
|
+
mem = prog.getMemory()
|
|
20
|
+
monitor = ConsoleTaskMonitor()
|
|
21
|
+
decomp = DecompInterface(); decomp.openProgram(prog)
|
|
22
|
+
def addr(a): return af.getDefaultAddressSpace().getAddress(a)
|
|
23
|
+
|
|
24
|
+
def decompile(f):
|
|
25
|
+
r = decomp.decompileFunction(f, 240, monitor)
|
|
26
|
+
return r.getDecompiledFunction().getC() if r.decompileCompleted() else '(fail)\n'
|
|
27
|
+
|
|
28
|
+
seen = set()
|
|
29
|
+
def dump(f, tag, callers=False):
|
|
30
|
+
if f is None or str(f.getEntryPoint()) in seen: return
|
|
31
|
+
seen.add(str(f.getEntryPoint()))
|
|
32
|
+
safe = re.sub(r'[^A-Za-z0-9._-]', '_', f.getName(True))[:60]
|
|
33
|
+
with open(os.path.join(OUT, '%s_%s_%s.txt' % (tag, f.getEntryPoint(), safe)), 'w') as o:
|
|
34
|
+
o.write('==== %s @ %s (%d bytes) ====\n' %
|
|
35
|
+
(f.getName(True), f.getEntryPoint(), f.getBody().getNumAddresses()))
|
|
36
|
+
cs = sorted(set(f.getCalledFunctions(monitor)), key=lambda x: str(x.getEntryPoint()))
|
|
37
|
+
o.write('callees: ' + ', '.join(str(c.getEntryPoint()) for c in cs) + '\n\n')
|
|
38
|
+
o.write(decompile(f))
|
|
39
|
+
if callers:
|
|
40
|
+
for c in sorted(set(f.getCallingFunctions(monitor)), key=lambda x: str(x.getEntryPoint())):
|
|
41
|
+
dump(c, tag + '_caller', False)
|
|
42
|
+
|
|
43
|
+
summary = open(os.path.join(OUT, 'SUMMARY.txt'), 'w')
|
|
44
|
+
|
|
45
|
+
# 1. setSoftware — xref the __PRETTY_FUNCTION__ string at 0x10081a947
|
|
46
|
+
summary.write('=== setSoftware (xref 0x10081a947) ===\n')
|
|
47
|
+
for r in rm.getReferencesTo(addr(0x10081a947)):
|
|
48
|
+
f = fm.getFunctionContaining(r.getFromAddress())
|
|
49
|
+
if f:
|
|
50
|
+
summary.write(' setSoftware = %s @ %s\n' % (f.getName(True), f.getEntryPoint()))
|
|
51
|
+
dump(f, '1_setSoftware', callers=True)
|
|
52
|
+
|
|
53
|
+
# 2. the hash init/update/final used by the HMAC
|
|
54
|
+
summary.write('\n=== HMAC hash primitives ===\n')
|
|
55
|
+
for fp, t in [(0x100408420, '2_hash_init'), (0x10040aee0, '2_hash_update'),
|
|
56
|
+
(0x10040afb0, '2_hash_final'), (0x10040b290, '2_hash_oneshot')]:
|
|
57
|
+
f = fm.getFunctionAt(addr(fp))
|
|
58
|
+
if f: dump(f, t, False)
|
|
59
|
+
|
|
60
|
+
# 3. also dump bytes around the "software" cstring region for nearby key data
|
|
61
|
+
summary.write('\n=== done, %d fns ===\n' % len(seen))
|
|
62
|
+
summary.close()
|
|
63
|
+
print('dump-setsoftware: %d functions -> %s' % (len(seen), OUT))
|