import frida
import os
import sys
import time
def main() -> int:
try:
sys.stdout.reconfigure(encoding="utf-8", errors="backslashreplace")
except Exception:
pass
if len(sys.argv) != 2:
print(f"Usage: {os.path.basename(sys.argv[0])} <username>")
return 2
username = sys.argv[1]
dummy_key = "0000-0000-0000-0000"
exe = os.path.join(os.getcwd(), "main.exe")
if not os.path.exists(exe):
print("main.exe not found in current directory")
return 2
js = r"""
var depths = {};
var out = [];
function setup() {
var py = Process.findModuleByName('python312.dll'), main = Process.findModuleByName('main.dll');
if (!py || !main) { setTimeout(setup, 10); return; }
var Repr = new NativeFunction(py.getExportByName('PyObject_Repr'), 'pointer', ['pointer']);
var AsUtf8 = new NativeFunction(py.getExportByName('PyUnicode_AsUTF8'), 'pointer', ['pointer']);
var Clear = new NativeFunction(py.getExportByName('PyErr_Clear'), 'void', []);
var BytesSize = new NativeFunction(py.getExportByName('PyBytes_Size'), 'long', ['pointer']);
var BytesPtr = new NativeFunction(py.getExportByName('PyBytes_AsString'), 'pointer', ['pointer']);
function repr(p) {
if (p.isNull()) return 'NULL';
try {
var r = Repr(p);
if (r.isNull()) { Clear(); return '<reprNULL>'; }
var c = AsUtf8(r);
if (c.isNull()) { Clear(); return '<utf8NULL>'; }
return c.readUtf8String();
} catch (e) {
Clear();
return '<err>';
}
}
function bytesHex(p) {
try {
var n = BytesSize(p);
if (n !== 2) return null;
var b = new Uint8Array(BytesPtr(p).readByteArray(n));
return Array.prototype.map.call(b, x => x.toString(16).padStart(2, '0')).join('').toUpperCase();
} catch (e) {
Clear();
return null;
}
}
function inDepth(tid) { return (depths[tid] || 0) > 0; }
Interceptor.attach(main.base.add(0x5d6c0), {
onEnter() { depths[this.threadId] = (depths[this.threadId] || 0) + 1; },
onLeave() { depths[this.threadId] = (depths[this.threadId] || 1) - 1; }
});
Interceptor.attach(main.base.add(0x73b50), {
onEnter(args) {
if (!inDepth(this.threadId)) return;
var callable = repr(args[1]);
if (callable.indexOf('to_bytes') < 0) return;
this.want = true;
},
onLeave(ret) {
if (!this.want) return;
var h = bytesHex(ret);
if (h === null) return;
out.push(h);
send('TOBYTES ' + h);
if (out.length >= 7) {
send('KEY ' + [out[0], out[1], out[3], out[6]].join('-'));
}
}
});
}
setup();
"""
current = {"key": None}
dev = frida.get_local_device()
sessions = []
def on_msg(message, data):
if message["type"] != "send":
return
payload = message["payload"]
if payload.startswith("KEY "):
current["key"] = payload[4:]
def attach(pid):
session = frida.attach(pid)
session.enable_child_gating()
script = session.create_script(js)
script.on("message", on_msg)
script.load()
sessions.append(session)
def child_added(child):
try:
attach(child.pid)
finally:
dev.resume(child.pid)
dev.on("child-added", child_added)
env = os.environ.copy()
env["PYTHONIOENCODING"] = "utf-8"
keys = []
for _ in range(8):
current["key"] = None
with open("input.txt", "w", encoding="ascii") as f:
f.write(username + "\n" + dummy_key + "\n")
pid = dev.spawn(
["C:\\Windows\\System32\\cmd.exe", "/d", "/s", "/c", "main.exe < input.txt > NUL 2> NUL"],
cwd=os.getcwd(),
env=env,
)
attach(pid)
dev.resume(pid)
for _ in range(120):
if current["key"]:
break
time.sleep(0.1)
if current["key"] and current["key"] not in keys:
keys.append(current["key"])
if len(keys) >= 2:
break
if not keys:
print("failed to extract key material")
return 1
print("\n".join(keys))
return 0
if __name__ == "__main__":
raise SystemExit(main())