منبع منتشرشده
دانلود ZIP منبع
CH-J Tools
پوشهها و فایلهای یک نسخهٔ مشخص از برنامه را مرور کنید.
CH-J / Server Manager
872 فایل
core-20261030T220507Z-61e4558d889f
افزودهشده: · ساختهشده:
1
//! Fixed PowerShell helpers over Windows OpenSSH (port of `sessions/windowsPrivilegedExec.js`).2
//! The command line is a short `-EncodedCommand` bootstrap; the script and one JSON argument3
//! object follow on stdin as base64 lines, so user input never reaches a command line.4
//! Windows has no sudo: scripts refuse administrative work without an elevated token and5
//! never try to bypass UAC.7
use crate::core::error::{CoreError, CoreResult};8
use crate::core::ssh::session::{SessionManager, SessionRecord};9
use base64::engine::general_purpose::STANDARD as B64;10
use base64::Engine;11
use once_cell::sync::Lazy;12
use regex::Regex;13
use serde_json::Value;14
use std::sync::Arc;15
use std::time::Duration;17
pub const BOOTSTRAP: &str = "$ErrorActionPreference='Stop';$ProgressPreference='SilentlyContinue';$i=[Console]::In;$c=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($i.ReadLine()));$a=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($i.ReadLine()));& ([ScriptBlock]::Create($c)) $a";18
static ERR_LINE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^CHJ_ERR:([A-Z0-9_]{2,64}):([A-Za-z0-9+/=]*)\s*$").unwrap());20
pub fn encoded_command(source: &str) -> String {21
let utf16: Vec<u8> = source.encode_utf16().flat_map(|u| u.to_le_bytes()).collect();22
format!("powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand {}", B64.encode(utf16))23
}25
pub fn bootstrap_input(script: &str, args: &Value) -> String {26
format!("{}\n{}\n", B64.encode(script.as_bytes()), B64.encode(serde_json::to_string(args).unwrap_or_else(|_| "{}".into()).as_bytes()))27
}29
fn clean(text: &str, limit: usize) -> String {30
let replaced: String = text.chars().map(|c| if (c as u32) < 0x20 || c as u32 == 0x7f { ' ' } else { c }).collect();31
replaced.split_whitespace().collect::<Vec<_>>().join(" ").chars().take(limit).collect()32
}34
pub fn parse_windows_output(stdout: &str, stderr: &str, code: Option<i64>, prefix: &str) -> CoreResult<Value> {35
let lines: Vec<&str> = stdout.split('\n').map(|l| l.trim_end_matches('\r')).collect();36
if let Some(line) = lines.iter().find(|l| l.starts_with("CHJ_RESULT:")) {37
return B6438
.decode(line[11..].trim())39
.ok()40
.and_then(|bytes| serde_json::from_slice(&bytes).ok())41
.ok_or_else(|| CoreError::new(format!("{prefix}PROTOCOL_ERROR"), "The Windows helper returned unreadable data."));42
}43
if let Some(line) = lines.iter().find(|l| l.starts_with("CHJ_ERR:")) {44
if let Some(caps) = ERR_LINE.captures(line) {45
let message = B64.decode(&caps[2]).ok().map(|b| clean(&String::from_utf8_lossy(&b), 600)).unwrap_or_default();46
return Err(CoreError::new(format!("{prefix}{}", &caps[1]), if message.is_empty() { caps[1].to_string() } else { message }));47
}48
}49
let text = format!("{stdout}\n{stderr}");50
let not_found = Regex::new(r"(?i)is not recognized as (an internal or external command|the name of a cmdlet)|powershell(\.exe)?: (command )?not found").unwrap();51
if code == Some(9009) || not_found.is_match(&text) {52
return Err(CoreError::new(format!("{prefix}POWERSHELL_UNAVAILABLE"), "Windows PowerShell (powershell.exe) could not be started over this SSH session."));53
}54
if Regex::new(r"(?i)language mode|ConstrainedLanguage|cannot be loaded because running scripts is disabled").unwrap().is_match(&text) {55
return Err(CoreError::new(56
format!("{prefix}POWERSHELL_RESTRICTED"),57
"PowerShell is restricted by policy (Constrained Language Mode or script blocking); CH-J does not bypass it.",58
));59
}60
let tags = Regex::new(r"<[^>]+>").unwrap();61
Err(CoreError::new(62
format!("{prefix}PROTOCOL_ERROR"),63
format!("The Windows helper did not return a result (exit {}). {}", code.map(|c| c.to_string()).unwrap_or_else(|| "null".into()), clean(&tags.replace_all(stderr, " "), 300)),64
))65
}67
/// Runs a fixed PowerShell script on a Windows session, serialized per session.68
pub async fn run_windows_script(manager: &SessionManager, record: &Arc<SessionRecord>, script: &str, args: &Value, timeout: Duration, output_limit: usize, prefix: &str) -> CoreResult<Value> {69
let input = bootstrap_input(script, args);70
let result = manager71
.windows_serial(record, async {72
let mut channel = manager.exec_channel(record, &encoded_command(BOOTSTRAP), timeout, output_limit).await?;73
channel.write(input.as_bytes()).await;74
channel.eof().await;75
channel.finish().await76
})77
.await?;78
parse_windows_output(&result.stdout, &result.stderr, result.code, prefix)79
}81
pub struct WindowsExecutor {82
pub sessions: Arc<SessionManager>,83
}85
impl WindowsExecutor {86
pub async fn run(&self, session_id: &str, script: &str, args: &Value, timeout: Duration, purpose: &str, prefix: &str) -> CoreResult<Value> {87
let record = self.sessions.require_connected(session_id)?;88
let result = run_windows_script(&self.sessions, &record, script, args, timeout, 16 * 1024 * 1024, prefix).await;89
if let Err(error) = &result {90
self.sessions.logger.warn("Windows remote operation failed.", serde_json::json!({ "sessionId": session_id, "purpose": purpose, "code": error.code }));91
}92
result93
}94
}96
#[cfg(test)]97
mod tests {98
use super::*;99
use serde_json::json;101
#[test]102
fn encoded_command_is_utf16le_base64() {103
let command = encoded_command("a");104
assert!(command.ends_with("-EncodedCommand YQA="));105
assert!(encoded_command(BOOTSTRAP).len() < 8191);106
}108
#[test]109
fn output_parsing() {110
let ok = format!("noise\r\nCHJ_RESULT:{}\r\n", B64.encode(br#"{"a":1}"#));111
assert_eq!(parse_windows_output(&ok, "", Some(0), "USERS_").unwrap(), json!({"a": 1}));112
let err = format!("CHJ_ERR:NOT_ELEVATED:{}\n", B64.encode("Run as admin"));113
let error = parse_windows_output(&err, "", Some(1), "USERS_").unwrap_err();114
assert_eq!(error.code, "USERS_NOT_ELEVATED");115
assert_eq!(error.message, "Run as admin");116
assert_eq!(parse_windows_output("", "'powershell.exe' is not recognized as an internal or external command", Some(1), "X_").unwrap_err().code, "X_POWERSHELL_UNAVAILABLE");117
}118
}SHA-256
1825ef1082d8898256406c23968b05f3ed0a95bc8ba104b8fa195162617af0f7CH-J Proprietary Software License 1.14 · کد منبع تحت CH-J Proprietary Software License 1.14 ارائه میشود. در دسترس بودن آن شرایط مجوز را تغییر نمیدهد و حق اضافهای اعطا نمیکند.
SHA-256 بایگانی
77380d4805485ffa4027fbe7858b5836f197a4aa9d52358a43e2d12d018744df