use alloc::{format, string::String, vec};
use obfstr::obfstr as s;
use crate::error::Result;
use crate::com::_Assembly;
use crate::string::ComString;
use crate::{Invocation, RustClrEnv};
use crate::variant::{Variant, create_safe_args};
pub struct PowerShell {
automation: _Assembly,
#[allow(dead_code)]
clr: RustClrEnv,
}
impl PowerShell {
pub fn new() -> Result<Self> {
let clr = RustClrEnv::new(None)?;
let mscorlib = clr.app_domain.get_assembly(s!("mscorlib"))?;
let reflection_assembly = mscorlib.resolve_type(s!("System.Reflection.Assembly"))?;
let load_partial_name = reflection_assembly.method_signature(s!(
"System.Reflection.Assembly LoadWithPartialName(System.String)"
))?;
let param = create_safe_args(vec![s!("System.Management.Automation").to_variant()])?;
let result = load_partial_name.invoke(None, Some(param))?;
let automation =
_Assembly::from_raw(unsafe { result.Anonymous.Anonymous.Anonymous.byref })?;
Ok(Self { automation, clr })
}
pub fn execute(&self, command: &str) -> Result<String> {
let runspace_factory = self.automation.resolve_type(
s!("System.Management.Automation.Runspaces.RunspaceFactory")
)?;
let create_runspace = runspace_factory.method_signature(s!(
"System.Management.Automation.Runspaces.Runspace CreateRunspace()"
))?;
let runspace = create_runspace.invoke(None, None)?;
let assembly_runspace = self.automation.resolve_type(
s!("System.Management.Automation.Runspaces.Runspace"),
)?;
assembly_runspace.invoke(
s!("Open"),
Some(runspace),
None,
Invocation::Instance,
)?;
let create_pipeline = assembly_runspace.method_signature(s!(
"System.Management.Automation.Runspaces.Pipeline CreatePipeline()"
))?;
let pipe = create_pipeline.invoke(Some(runspace), None)?;
let pipeline = self.automation.resolve_type(
s!("System.Management.Automation.Runspaces.Pipeline"),
)?;
let get_command = pipeline.invoke(
s!("get_Commands"),
Some(pipe),
None,
Invocation::Instance,
)?;
let cmd = vec![format!("{} | {}", command, s!("Out-String")).to_variant()];
let command_collection = self.automation.resolve_type(
s!("System.Management.Automation.Runspaces.CommandCollection"),
)?;
let add_script = command_collection.method_signature(s!(
"Void AddScript(System.String)"
))?;
add_script.invoke(Some(get_command), Some(create_safe_args(cmd)?))?;
pipeline.invoke(
s!("InvokeAsync"),
Some(pipe),
None,
Invocation::Instance,
)?;
let get_output = pipeline.invoke(
s!("get_Output"),
Some(pipe),
None,
Invocation::Instance,
)?;
let pipeline_reader = self.automation.resolve_type(s!(
"System.Management.Automation.Runspaces.PipelineReader`1[System.Management.Automation.PSObject]"
))?;
let read = pipeline_reader.method_signature(s!(
"System.Management.Automation.PSObject Read()"
))?;
let ps_object_instance = read.invoke(Some(get_output), None)?;
let ps_object = self.automation.resolve_type(s!("System.Management.Automation.PSObject"))?;
let to_string = ps_object.method_signature(s!("System.String ToString()"))?;
let output = to_string.invoke(Some(ps_object_instance), None)?;
assembly_runspace.invoke(
s!("Close"),
Some(runspace),
None,
Invocation::Instance,
)?;
Ok(unsafe {
output
.Anonymous
.Anonymous
.Anonymous
.bstrVal
.to_string()
})
}
}
#[cfg(test)]
mod tests {
use crate::error::Result;
use super::PowerShell;
#[test]
fn test_powershell() -> Result<()> {
let pwsh = PowerShell::new()?;
let output = pwsh.execute("whoami /all")?;
assert!(
output.contains("\\")
|| output.contains("User")
|| output.contains("Account")
|| output.contains("Authority"),
"whoami output does not look valid: {output}"
);
Ok(())
}
}