From 4046076b1c38b35ae7c90a8a34a55f57c2991f3d Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 7 Oct 2022 14:48:58 +0800 Subject: [PATCH 01/38] ebpfdriver: add rename & link --- agent/deploy/nfpm.yaml | 2 +- agent/deploy/scripts/postinstall.sh | 3 +- plugin/ebpfdriver/kern/include/define.h | 2 + plugin/ebpfdriver/kern/include/hades_file.h | 54 ++++++++++++++++++++ plugin/ebpfdriver/kern/include/utils.h | 2 +- plugin/ebpfdriver/user/event/anti_rkt_sdt.go | 9 ++-- plugin/ebpfdriver/user/event/createfile.go | 2 +- plugin/ebpfdriver/user/event/inode_link.go | 54 ++++++++++++++++++++ plugin/ebpfdriver/user/event/inode_rename.go | 54 ++++++++++++++++++++ 9 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 plugin/ebpfdriver/user/event/inode_link.go create mode 100644 plugin/ebpfdriver/user/event/inode_rename.go diff --git a/agent/deploy/nfpm.yaml b/agent/deploy/nfpm.yaml index 21088b75..76e46175 100644 --- a/agent/deploy/nfpm.yaml +++ b/agent/deploy/nfpm.yaml @@ -6,7 +6,7 @@ name: "hades-agent" arch: "amd64" platform: "linux" epoch: 3 -version: v1.0.0 +version: v1.0.1 release: 1 section: "default" priority: "extra" diff --git a/agent/deploy/scripts/postinstall.sh b/agent/deploy/scripts/postinstall.sh index 63c97985..9ded79ee 100644 --- a/agent/deploy/scripts/postinstall.sh +++ b/agent/deploy/scripts/postinstall.sh @@ -45,7 +45,7 @@ enable_service() { succ "service enabled successfully" } -# 重启失效的问题 +# 重启失效的问题, 在 sysvinit 中, 将该处迁移至 ctl 中, 注意关闭时的 mount 关闭 create_cgroups(){ cat /proc/self/mountinfo|grep -q 'cgroup .* rw,.*\bmemory\b' if [ $? -ne 0 ];then @@ -60,6 +60,7 @@ create_cgroups(){ expect "mount -t cgroup -o cpu cgroup ${root_dir}/cgroup/cpu" fi } + start_agent(){ ${root_dir}/${agent_ctl} start } diff --git a/plugin/ebpfdriver/kern/include/define.h b/plugin/ebpfdriver/kern/include/define.h index f62f6ff3..849ccc72 100644 --- a/plugin/ebpfdriver/kern/include/define.h +++ b/plugin/ebpfdriver/kern/include/define.h @@ -279,6 +279,8 @@ static __always_inline int get_config(__u32 key) #define SECURITY_INODE_CREATE 1028 #define SECURITY_SB_MOUNT 1029 #define CALL_USERMODEHELPER 1030 +#define SECURITY_INODE_RENAME 1031 +#define SECURITY_INODE_LINK 1032 // uprobe #define BASH_READLINE 2000 // rootkit field diff --git a/plugin/ebpfdriver/kern/include/hades_file.h b/plugin/ebpfdriver/kern/include/hades_file.h index e2a0e285..8f10b8f2 100644 --- a/plugin/ebpfdriver/kern/include/hades_file.h +++ b/plugin/ebpfdriver/kern/include/hades_file.h @@ -49,3 +49,57 @@ int BPF_KPROBE(kprobe_security_sb_mount) save_pid_tree_to_buf(&data, 8, 5); return events_perf_submit(&data); } + +SEC("kprobe/security_inode_rename") +int BPF_KPROBE(kprobe_security_inode_rename) +{ + event_data_t data = {}; + if (!init_event_data(&data, ctx)) + return 0; + if (context_filter(&data.context)) + return 0; + data.context.type = SECURITY_INODE_RENAME; + + struct dentry *from = (struct dentry *) PT_REGS_PARM2(ctx); + struct dentry *to = (struct dentry *) PT_REGS_PARM4(ctx); + + void *from_ptr = get_dentry_path_str(from); + if (from_ptr == NULL) + return 0; + save_str_to_buf(&data, from_ptr, 0); + void *to_ptr = get_dentry_path_str(to); + if (to_ptr == NULL) + return 0; + save_str_to_buf(&data, to_ptr, 1); + return events_perf_submit(&data); +} + +SEC("kprobe/security_inode_link") +int BPF_KPROBE(kprobe_security_inode_link) +{ + event_data_t data = {}; + if (!init_event_data(&data, ctx)) + return 0; + if (context_filter(&data.context)) + return 0; + data.context.type = SECURITY_INODE_LINK; + + struct dentry *from = (struct dentry *) PT_REGS_PARM2(ctx); + struct dentry *to = (struct dentry *) PT_REGS_PARM4(ctx); + + void *from_ptr = get_dentry_path_str(from); + if (from_ptr == NULL) + return 0; + save_str_to_buf(&data, from_ptr, 0); + void *to_ptr = get_dentry_path_str(to); + if (to_ptr == NULL) + return 0; + save_str_to_buf(&data, to_ptr, 1); + return events_perf_submit(&data); +} + +// SEC("kprobe/security_file_open") +// int BPF_KPROBE(kprobe_security_file_open) +// { + +// } \ No newline at end of file diff --git a/plugin/ebpfdriver/kern/include/utils.h b/plugin/ebpfdriver/kern/include/utils.h index dfc08708..15bd1167 100644 --- a/plugin/ebpfdriver/kern/include/utils.h +++ b/plugin/ebpfdriver/kern/include/utils.h @@ -619,7 +619,7 @@ static __always_inline int get_socket_info_sub(event_data_t *data, // remote we need to send struct sockaddr_in remote; get_remote_sockaddr_in_from_network_details( - &remote, &net_details, family); + &remote, (void *)&net_details, family); save_to_submit_buf(data, &remote, sizeof(struct sockaddr_in), index); return 1; diff --git a/plugin/ebpfdriver/user/event/anti_rkt_sdt.go b/plugin/ebpfdriver/user/event/anti_rkt_sdt.go index 20d1665d..818a39ad 100644 --- a/plugin/ebpfdriver/user/event/anti_rkt_sdt.go +++ b/plugin/ebpfdriver/user/event/anti_rkt_sdt.go @@ -27,14 +27,13 @@ func (SCTScan) ID() uint32 { func (s *SCTScan) DecodeEvent(e *decoder.EbpfDecoder) (err error) { var ( - addr uint64 - call_index uint64 - index uint8 + addr uint64 + index uint8 ) if err = e.DecodeUint8(&index); err != nil { return } - if err = e.DecodeUint64(&call_index); err != nil { + if err = e.DecodeUint64(&s.Index); err != nil { return } if err = e.DecodeUint8(&index); err != nil { @@ -62,7 +61,7 @@ func (s *SCTScan) Trigger(m *manager.Manager) error { return err } - for i := 0; i < 302; i++ { + for i := 0; i < 2000; i++ { s.trigger(sct.Address, uint64(i)) } diff --git a/plugin/ebpfdriver/user/event/createfile.go b/plugin/ebpfdriver/user/event/createfile.go index 090d73e4..c4db5e61 100644 --- a/plugin/ebpfdriver/user/event/createfile.go +++ b/plugin/ebpfdriver/user/event/createfile.go @@ -11,7 +11,7 @@ var _ decoder.Event = (*InodeCreate)(nil) // Sha256 maybe, and others type InodeCreate struct { decoder.BasicEvent `json:"-"` - Exe string `json:"exe"` + Exe string `json:"-"` Filename string `json:"filename"` Dport uint16 `json:"dport"` Dip string `json:"dip"` diff --git a/plugin/ebpfdriver/user/event/inode_link.go b/plugin/ebpfdriver/user/event/inode_link.go new file mode 100644 index 00000000..a818eb61 --- /dev/null +++ b/plugin/ebpfdriver/user/event/inode_link.go @@ -0,0 +1,54 @@ +package event + +import ( + "hades-ebpf/user/decoder" + + manager "github.com/ehids/ebpfmanager" +) + +var _ decoder.Event = (*InodeLink)(nil) + +// Sha256 maybe, and others +type InodeLink struct { + decoder.BasicEvent `json:"-"` + Exe string `json:"-"` + Old string `json:"old"` + New string `json:"new"` +} + +func (InodeLink) ID() uint32 { + return 1031 +} + +func (InodeLink) Name() string { + return "security_inode_link" +} + +func (i *InodeLink) GetExe() string { + return i.Exe +} + +func (i *InodeLink) DecodeEvent(e *decoder.EbpfDecoder) (err error) { + if i.Old, err = e.DecodeString(); err != nil { + return + } + if i.New, err = e.DecodeString(); err != nil { + return + } + return +} + +func (InodeLink) GetProbes() []*manager.Probe { + return []*manager.Probe{ + { + UID: "KprobeSecurityInodeLink", + Section: "kprobe/security_inode_link", + EbpfFuncName: "kprobe_security_inode_link", + AttachToFuncName: "security_inode_link", + }, + } +} + +func init() { + decoder.RegistEvent(&InodeLink{}) +} diff --git a/plugin/ebpfdriver/user/event/inode_rename.go b/plugin/ebpfdriver/user/event/inode_rename.go new file mode 100644 index 00000000..da40e066 --- /dev/null +++ b/plugin/ebpfdriver/user/event/inode_rename.go @@ -0,0 +1,54 @@ +package event + +import ( + "hades-ebpf/user/decoder" + + manager "github.com/ehids/ebpfmanager" +) + +var _ decoder.Event = (*InodeRename)(nil) + +// Sha256 maybe, and others +type InodeRename struct { + decoder.BasicEvent `json:"-"` + Exe string `json:"-"` + Old string `json:"old"` + New string `json:"new"` +} + +func (InodeRename) ID() uint32 { + return 1032 +} + +func (InodeRename) Name() string { + return "security_inode_rename" +} + +func (i *InodeRename) GetExe() string { + return i.Exe +} + +func (i *InodeRename) DecodeEvent(e *decoder.EbpfDecoder) (err error) { + if i.Old, err = e.DecodeString(); err != nil { + return + } + if i.New, err = e.DecodeString(); err != nil { + return + } + return +} + +func (InodeRename) GetProbes() []*manager.Probe { + return []*manager.Probe{ + { + UID: "KprobeSecurityInodeRename", + Section: "kprobe/security_inode_rename", + EbpfFuncName: "kprobe_security_inode_rename", + AttachToFuncName: "security_inode_rename", + }, + } +} + +func init() { + decoder.RegistEvent(&InodeRename{}) +} From bc75405f3ffc37810785578313966a9873bf1c96 Mon Sep 17 00:00:00 2001 From: czy Date: Sat, 30 Nov 2024 21:36:11 +0800 Subject: [PATCH 02/38] add wdriver rust --- plugins/eguard/Cargo.toml | 2 +- plugins/eguard/src/bpf/rules/l7_acl.h | 2 +- plugins/wdriver-rust/Cargo.toml | 29 +++++++ plugins/wdriver-rust/README.md | 62 +++++++++++++++ plugins/wdriver-rust/build.rs | 3 + .../config/directoryRuleConfig.json | 13 ++++ .../config/networkRuleConfig.yaml | 21 +++++ .../config/processRuleConfig.json | 4 + .../config/registerRuleConfig.json | 8 ++ .../config/registerRuleConfig_.json | 22 ++++++ .../wdriver-rust/config/threadRuleConfig.json | 3 + plugins/wdriver-rust/src/drvmg.rs | 60 +++++++++++++++ plugins/wdriver-rust/src/handle.rs | 17 +++++ plugins/wdriver-rust/src/lib.rs | 6 ++ plugins/wdriver-rust/src/mod.rs | 0 plugins/wdriver-rust/src/rule.rs | 76 +++++++++++++++++++ plugins/wdriver-rust/tests/tsDrvmg.rs | 20 +++++ plugins/wdriver-rust/tests/tsRule.rs | 20 +++++ 18 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 plugins/wdriver-rust/Cargo.toml create mode 100644 plugins/wdriver-rust/README.md create mode 100644 plugins/wdriver-rust/build.rs create mode 100644 plugins/wdriver-rust/config/directoryRuleConfig.json create mode 100644 plugins/wdriver-rust/config/networkRuleConfig.yaml create mode 100644 plugins/wdriver-rust/config/processRuleConfig.json create mode 100644 plugins/wdriver-rust/config/registerRuleConfig.json create mode 100644 plugins/wdriver-rust/config/registerRuleConfig_.json create mode 100644 plugins/wdriver-rust/config/threadRuleConfig.json create mode 100644 plugins/wdriver-rust/src/drvmg.rs create mode 100644 plugins/wdriver-rust/src/handle.rs create mode 100644 plugins/wdriver-rust/src/lib.rs create mode 100644 plugins/wdriver-rust/src/mod.rs create mode 100644 plugins/wdriver-rust/src/rule.rs create mode 100644 plugins/wdriver-rust/tests/tsDrvmg.rs create mode 100644 plugins/wdriver-rust/tests/tsRule.rs diff --git a/plugins/eguard/Cargo.toml b/plugins/eguard/Cargo.toml index c1a843e0..315dac63 100644 --- a/plugins/eguard/Cargo.toml +++ b/plugins/eguard/Cargo.toml @@ -32,4 +32,4 @@ sdk = { path = "../../SDK/rust" } tokio = { version = "1", features = ["full"] } [build-dependencies] -libbpf-cargo = "0.21.2" +libbpf-cargo = "0.21.2" \ No newline at end of file diff --git a/plugins/eguard/src/bpf/rules/l7_acl.h b/plugins/eguard/src/bpf/rules/l7_acl.h index da708c5a..8f997f43 100644 --- a/plugins/eguard/src/bpf/rules/l7_acl.h +++ b/plugins/eguard/src/bpf/rules/l7_acl.h @@ -84,7 +84,7 @@ static __always_inline int l7_acl_rule(net_packet_t pkt, struct __sk_buff *skb) // trim key struct dns_policy_key key = {0}; key.prefixlen = DNS_MAX_PRELEN; - + bpf_skb_load_bytes(skb, DNS_OFFSET, (void *)&pkt.buf_p->buf[SIZE_CONTEXT], SIZE_DNSHDR); int offset = load_dns(pkt, &key, skb); void *output_data = pkt.buf_p->buf; diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml new file mode 100644 index 00000000..e7b7acaa --- /dev/null +++ b/plugins/wdriver-rust/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "wdriver_rs" +version = "23.11.30" +edition = "2021" + +# unit test rule +[[bin]] +name = "unitRule" +path = "tests\\tsRule.rs" + +# unit test driven +[[bin]] +name = "unitDrivern" +path = "tests\\tsDrvmg.rs" + +[dependencies] +log = "0.4.17" +yaml-rust = "0.4" +fast_log = "1.5.51" + +[dependencies.windows] +version = "0.52" +features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_Threading", + "Win32_System_IO", +] \ No newline at end of file diff --git a/plugins/wdriver-rust/README.md b/plugins/wdriver-rust/README.md new file mode 100644 index 00000000..99cb2a33 --- /dev/null +++ b/plugins/wdriver-rust/README.md @@ -0,0 +1,62 @@ +# Hades Windows Driver + +wdriver-rsut优先提供Windows插件模块管理,管理组件核心dll +``` +NetDrvlib64.dll - 内核网络 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib +``` + +``` +RuleEnginelib64.dll - 规则引擎 +https://github.com/theSecHunter/Hades-Windows/tree/main/RuleEngineSvc +``` + +``` +SysMonDrvlib64.dll - 内核探针 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrvlib +``` + +``` +SysMonUserlib64.dll - 用户态探针 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmonuserlib +``` + +依赖于Driver驱动能力 +``` +SysMonDrvlib64 -> sysmondriver.sys - 内核驱动 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrv +``` + +``` +NetDrvlib64 -> hadesndr.sys - 网络驱动 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib +``` + +wdriver-rsut目标替换c++组件库,整合NetDrvlib64,RuleEnginelib64,SysMonDrvlib64,SysMonUserlib64动态库能力,重构插件。 + +``` +rule.rs -> RuleEnginelib64.dll - 规则引擎 +``` + +``` +knetfilter.rs -> NetDrvlib64.dll - 内核网络 +``` + +``` +kmonfilter.rs -> SysMonDrvlib64.dll - 内核探针 +``` + +``` +umonfilter.rs -> SysMonUserlib64.dll - 用户态探针 +``` + +driver sys不变 +``` +kmonfilter -> sysmondriver.sys - 内核驱动 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrv +``` + +``` +knetfilter -> hadesndr.sys - 网络驱动 +https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib +``` \ No newline at end of file diff --git a/plugins/wdriver-rust/build.rs b/plugins/wdriver-rust/build.rs new file mode 100644 index 00000000..96850e36 --- /dev/null +++ b/plugins/wdriver-rust/build.rs @@ -0,0 +1,3 @@ +pub fn main() { + +} \ No newline at end of file diff --git a/plugins/wdriver-rust/config/directoryRuleConfig.json b/plugins/wdriver-rust/config/directoryRuleConfig.json new file mode 100644 index 00000000..949c42eb --- /dev/null +++ b/plugins/wdriver-rust/config/directoryRuleConfig.json @@ -0,0 +1,13 @@ +[ + { + "FileIORuleMod": 1, + "processName": "CreateFile.exe|powershell.exe", + "Directory": "D:\\Document\\|C:\\System\\AppData\\|C:\\testIPS\\" + }, + + { + "FileIORuleMod": 2, + "processName": "explorer.exe|cmd.exe", + "Directory": "D:\\Document1\\|C:\\System\\AppData1\\|C:\\testIPS\\" + } +] \ No newline at end of file diff --git a/plugins/wdriver-rust/config/networkRuleConfig.yaml b/plugins/wdriver-rust/config/networkRuleConfig.yaml new file mode 100644 index 00000000..260cba59 --- /dev/null +++ b/plugins/wdriver-rust/config/networkRuleConfig.yaml @@ -0,0 +1,21 @@ +egress: + - name: "eguard_egress_test_project" + address: "192.168.1.1/24" + protocol: TCP # ALL/TCP/UDP + ports: # empty means all ports. 32(single port like 80), 16(range like 8079-8080) + - 80 + - 8079-8080 + action: DENY # DENY/LOG + level: INFO + + - name: "test_tcp_redirect" + #address: "" + protocol: TCP # TCP + #ports: # empty means all ports. + # - 80 + # - 8079-8080 + processname: "1.exe|2.exe" # empty means all process. + redirectip: "192.168.188.188" # redirect to ipaddrss + redirectport: "88" # redirect to port + action: REDIRECT + level: INFO \ No newline at end of file diff --git a/plugins/wdriver-rust/config/processRuleConfig.json b/plugins/wdriver-rust/config/processRuleConfig.json new file mode 100644 index 00000000..cf87cfd3 --- /dev/null +++ b/plugins/wdriver-rust/config/processRuleConfig.json @@ -0,0 +1,4 @@ +{ + "processRuleMod": 2, + "processName": "cmd.exe|powershell.exe" +} \ No newline at end of file diff --git a/plugins/wdriver-rust/config/registerRuleConfig.json b/plugins/wdriver-rust/config/registerRuleConfig.json new file mode 100644 index 00000000..f619e1d5 --- /dev/null +++ b/plugins/wdriver-rust/config/registerRuleConfig.json @@ -0,0 +1,8 @@ +[ + { + "registerRuleMod": 1, + "processName": "AddRegister.exe|cmd.exe", + "registerValuse": "\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run|\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", + "permissions": "1111111" + } +] \ No newline at end of file diff --git a/plugins/wdriver-rust/config/registerRuleConfig_.json b/plugins/wdriver-rust/config/registerRuleConfig_.json new file mode 100644 index 00000000..dca3e86f --- /dev/null +++ b/plugins/wdriver-rust/config/registerRuleConfig_.json @@ -0,0 +1,22 @@ +[ + { + "registerRuleMod": 2, + "processName": "cmd.exe|powershell.exe", + "registerValuse": "\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run|\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOne", + "permissions": "0000000" + }, + + { + "registerRuleMod": 2, + "processName": "AddRegister.exe|cmd.exe", + "registerValuse": "\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run|\\REGISTRY\\MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOne", + "permissions": "1111111" + }, + + { + "registerRuleMod": 1, + "processName": "cmd.exe|svhost.exe.exe", + "registerValuse": "\\Registry\\Machine\\Software\\WOW6432Node\\Policies\\Microsoft\\MUI\\Settings", + "permissions": "1000110" + } +] \ No newline at end of file diff --git a/plugins/wdriver-rust/config/threadRuleConfig.json b/plugins/wdriver-rust/config/threadRuleConfig.json new file mode 100644 index 00000000..cfb8f00b --- /dev/null +++ b/plugins/wdriver-rust/config/threadRuleConfig.json @@ -0,0 +1,3 @@ +{ + "InjectIpsProcessNameArray": "WaitInject.exe|explorer.exe" +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/drvmg.rs b/plugins/wdriver-rust/src/drvmg.rs new file mode 100644 index 00000000..e8385e4b --- /dev/null +++ b/plugins/wdriver-rust/src/drvmg.rs @@ -0,0 +1,60 @@ +use windows::{ + core::*, Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Threading::*, + Win32::System::IO::*, +}; +use std::{fs, ptr::null, io::Read, path::PathBuf}; + +pub struct DrivenManageImpl { + pub m_hDriver: HANDLE, +} + +impl DrivenManageImpl { + pub fn new() -> Self { + Self { m_hDriver: HANDLE(0) } + } + + // Chekcout Driver Status + pub fn GetStuFormDriver(strDrivenName:String) -> bool { + + return true; + } + + // Open DriverHandle + pub fn OpenDriverHandle(&mut self, strDrivenName:String) -> bool { + unsafe { + let dwAttribute:u32 = 2147483648u32 | 1073741824u32; + let hResult: std::prelude::v1::Result = CreateFileA( + PCSTR(strDrivenName.as_ptr()), + dwAttribute, + FILE_SHARE_MODE(0), + None, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + None, + ); + if hResult.is_ok() { + let hDriver: HANDLE = hResult.unwrap(); + self.m_hDriver = hDriver; + return true; + } + else { + let hResultError: std::prelude::v1::Result<(), Error> = GetLastError(); + println!("{}",hResultError.unwrap_err().code()); + return false; + } + } + } + + // Send Data Pop Data + pub fn SendDataToDriver(iCode:u32, cData:String) -> bool { + unsafe { + + } + return true; + } + + // Read Data Push Queue + pub fn ReadDataFromDriver() { + + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/handle.rs b/plugins/wdriver-rust/src/handle.rs new file mode 100644 index 00000000..91067ff3 --- /dev/null +++ b/plugins/wdriver-rust/src/handle.rs @@ -0,0 +1,17 @@ +// network packet filter handle +pub struct HandleMSG; + +impl HandleMSG { + + + // Handle Message + fn HandleMSGNotify() -> bool { + return true; + } + + // When Waiting For Queue Data, The Data Pop is Dispatch. + pub fn WaitiQueueDataDispatch() -> bool { + return true; + } + +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs new file mode 100644 index 00000000..0b359617 --- /dev/null +++ b/plugins/wdriver-rust/src/lib.rs @@ -0,0 +1,6 @@ +// @exterm lib mod +mod rule; +pub use rule::*; + +mod drvmg; +pub use drvmg::*; diff --git a/plugins/wdriver-rust/src/mod.rs b/plugins/wdriver-rust/src/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/rule.rs b/plugins/wdriver-rust/src/rule.rs new file mode 100644 index 00000000..2fb39a6a --- /dev/null +++ b/plugins/wdriver-rust/src/rule.rs @@ -0,0 +1,76 @@ +extern crate yaml_rust; + +use std::{fs, ptr::null, io::Read, path::PathBuf}; +use yaml_rust::{YamlLoader, YamlEmitter}; + +pub struct RuleImpl; + +impl RuleImpl { + // Read json, yaml Data + fn ReadFileRule(sRulePath: String, sYamData: &mut String) -> bool { + if sRulePath.is_empty() || null() == sYamData { + return false; + } + if !PathBuf::from(sRulePath.to_string()).exists() { + log::error!("Checkout Yaml File exists failuer. {}", sRulePath); + return false; + } + *sYamData = fs::read_to_string(sRulePath.to_string()).unwrap(); + return true; + } + + // Analyze dns rule + pub fn GetDnsRule(sRulePath: String, sYamData: &mut String) -> bool { + if sRulePath.is_empty() { + return false; + } + + RuleImpl::ReadFileRule(sRulePath, sYamData); + if sYamData.is_empty() { + return false; + } + + let docs: Vec = YamlLoader::load_from_str(sYamData).unwrap(); + + // Multi document support, doc is a yaml::Yaml + let doc: &yaml_rust::Yaml = &docs[0]; + + // Debug support + println!("{:?}", doc); + + // Index access for map & array + // assert_eq!(doc["foo"][0].as_str().unwrap(), "list1"); + // assert_eq!(doc["bar"][1].as_f64().unwrap(), 2.0); + + // // Chained key/array access is checked and won't panic, + // // return BadValue if they are not exist. + // assert!(doc["INVALID_KEY"][100].is_badvalue()); + + // // Dump the YAML object + // let mut out_str = String::new(); + // { + // let mut emitter = YamlEmitter::new(&mut out_str); + // emitter.dump(doc).unwrap(); // dump the YAML object to a String + // } + // println!("{}", out_str); + return true; + } + + // Analyze Redirect rule + pub fn GetRedirectRule(sRulePath:String) { + + } + + // Analyze transport layer rule + pub fn GetTransportNetRule(sRulePath:String) { + + } + +} + +// Unit test +#[cfg(test)] +mod test{ + +} + diff --git a/plugins/wdriver-rust/tests/tsDrvmg.rs b/plugins/wdriver-rust/tests/tsDrvmg.rs new file mode 100644 index 00000000..567353c7 --- /dev/null +++ b/plugins/wdriver-rust/tests/tsDrvmg.rs @@ -0,0 +1,20 @@ +use netCom::DrivenManageImpl; + +#[test] +// 驱动启动状态 +pub fn UnitGetDrivenStu() { + let strDrivenName :String= String::from("\\Hades\\HadesNet"); + let mut hObjDrivenMang: DrivenManageImpl = DrivenManageImpl::new(); + let bSuc: bool = hObjDrivenMang.OpenDriverHandle(strDrivenName); + loop { + if bSuc == false { + break; + } + // checkout handle invalid + if hObjDrivenMang.m_hDriver.is_invalid() { + break; + } + + break; + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/tests/tsRule.rs b/plugins/wdriver-rust/tests/tsRule.rs new file mode 100644 index 00000000..017cdb43 --- /dev/null +++ b/plugins/wdriver-rust/tests/tsRule.rs @@ -0,0 +1,20 @@ +use fast_log::{consts::LogSize, plugin::{file_split::RollingType, packer::LogPacker}}; +use netCom::RuleImpl; + + +#[test] +pub fn UnitGetDnsRule() { + let mut sYamData: String = String::from(""); + let mut sCurrentPath = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + if sCurrentPath.is_empty() { + log::error!("Get Rule DirPath Failuer."); + return; + } + let sNetRulePath: String = sCurrentPath + "\\config\\networkRuleConfig.yaml"; + let bRet: bool = RuleImpl::GetDnsRule(sNetRulePath, &mut sYamData); + if false == bRet { + log::error!("Get Rule DirPath Failuer."); + return; + } + println!("Analyze Rule Success. {}",sYamData); +} \ No newline at end of file From f803b76d61dcd44e9538b6563c7b6512269acecf Mon Sep 17 00:00:00 2001 From: czy Date: Sun, 1 Dec 2024 21:19:57 +0800 Subject: [PATCH 03/38] cargo test --- plugins/wdriver-rust/Cargo.toml | 10 ---------- plugins/wdriver-rust/src/drvmg.rs | 16 ++++++++-------- plugins/wdriver-rust/src/handle.rs | 4 ++-- plugins/wdriver-rust/src/lib.rs | 7 ++----- plugins/wdriver-rust/src/rule.rs | 25 +++++++++++++------------ plugins/wdriver-rust/tests/tsDrvmg.rs | 14 +++++++------- plugins/wdriver-rust/tests/tsRule.rs | 19 +++++++++---------- 7 files changed, 41 insertions(+), 54 deletions(-) diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index e7b7acaa..c7a114ec 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -3,16 +3,6 @@ name = "wdriver_rs" version = "23.11.30" edition = "2021" -# unit test rule -[[bin]] -name = "unitRule" -path = "tests\\tsRule.rs" - -# unit test driven -[[bin]] -name = "unitDrivern" -path = "tests\\tsDrvmg.rs" - [dependencies] log = "0.4.17" yaml-rust = "0.4" diff --git a/plugins/wdriver-rust/src/drvmg.rs b/plugins/wdriver-rust/src/drvmg.rs index e8385e4b..638f3943 100644 --- a/plugins/wdriver-rust/src/drvmg.rs +++ b/plugins/wdriver-rust/src/drvmg.rs @@ -5,26 +5,26 @@ use windows::{ use std::{fs, ptr::null, io::Read, path::PathBuf}; pub struct DrivenManageImpl { - pub m_hDriver: HANDLE, + pub handle: HANDLE, } impl DrivenManageImpl { pub fn new() -> Self { - Self { m_hDriver: HANDLE(0) } + Self { handle: HANDLE(0) } } // Chekcout Driver Status - pub fn GetStuFormDriver(strDrivenName:String) -> bool { + pub fn get_driver_stu(driver_name:String) -> bool { return true; } // Open DriverHandle - pub fn OpenDriverHandle(&mut self, strDrivenName:String) -> bool { + pub fn open_driver_handle(&mut self, driver_name:String) -> bool { unsafe { let dwAttribute:u32 = 2147483648u32 | 1073741824u32; let hResult: std::prelude::v1::Result = CreateFileA( - PCSTR(strDrivenName.as_ptr()), + PCSTR(driver_name.as_ptr()), dwAttribute, FILE_SHARE_MODE(0), None, @@ -34,7 +34,7 @@ impl DrivenManageImpl { ); if hResult.is_ok() { let hDriver: HANDLE = hResult.unwrap(); - self.m_hDriver = hDriver; + self.handle = hDriver; return true; } else { @@ -46,7 +46,7 @@ impl DrivenManageImpl { } // Send Data Pop Data - pub fn SendDataToDriver(iCode:u32, cData:String) -> bool { + pub fn send_driver_data(code:u32, data:String) -> bool { unsafe { } @@ -54,7 +54,7 @@ impl DrivenManageImpl { } // Read Data Push Queue - pub fn ReadDataFromDriver() { + pub fn read_driver_data() { } } \ No newline at end of file diff --git a/plugins/wdriver-rust/src/handle.rs b/plugins/wdriver-rust/src/handle.rs index 91067ff3..a30b6334 100644 --- a/plugins/wdriver-rust/src/handle.rs +++ b/plugins/wdriver-rust/src/handle.rs @@ -5,12 +5,12 @@ impl HandleMSG { // Handle Message - fn HandleMSGNotify() -> bool { + fn handle_msg_notify() -> bool { return true; } // When Waiting For Queue Data, The Data Pop is Dispatch. - pub fn WaitiQueueDataDispatch() -> bool { + pub fn waiti_queue_data_dispatch() -> bool { return true; } diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs index 0b359617..2410f681 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver-rust/src/lib.rs @@ -1,6 +1,3 @@ // @exterm lib mod -mod rule; -pub use rule::*; - -mod drvmg; -pub use drvmg::*; +pub mod rule; +pub mod drvmg; diff --git a/plugins/wdriver-rust/src/rule.rs b/plugins/wdriver-rust/src/rule.rs index 2fb39a6a..24764e27 100644 --- a/plugins/wdriver-rust/src/rule.rs +++ b/plugins/wdriver-rust/src/rule.rs @@ -6,31 +6,32 @@ use yaml_rust::{YamlLoader, YamlEmitter}; pub struct RuleImpl; impl RuleImpl { + // Read json, yaml Data - fn ReadFileRule(sRulePath: String, sYamData: &mut String) -> bool { - if sRulePath.is_empty() || null() == sYamData { + pub fn read_file_rule(rule_path: String, yaml_data: &mut String) -> bool { + if rule_path.is_empty() || null() == yaml_data { return false; } - if !PathBuf::from(sRulePath.to_string()).exists() { - log::error!("Checkout Yaml File exists failuer. {}", sRulePath); + if !PathBuf::from(rule_path.to_string()).exists() { + log::error!("Checkout Yaml File exists failuer. {}", rule_path); return false; } - *sYamData = fs::read_to_string(sRulePath.to_string()).unwrap(); + *yaml_data = fs::read_to_string(rule_path.to_string()).unwrap(); return true; } // Analyze dns rule - pub fn GetDnsRule(sRulePath: String, sYamData: &mut String) -> bool { - if sRulePath.is_empty() { + pub fn get_dns_rule(rule_path: String, yaml_data: &mut String) -> bool { + if rule_path.is_empty() { return false; } - RuleImpl::ReadFileRule(sRulePath, sYamData); - if sYamData.is_empty() { + RuleImpl::read_file_rule(rule_path, yaml_data); + if yaml_data.is_empty() { return false; } - let docs: Vec = YamlLoader::load_from_str(sYamData).unwrap(); + let docs: Vec = YamlLoader::load_from_str(yaml_data).unwrap(); // Multi document support, doc is a yaml::Yaml let doc: &yaml_rust::Yaml = &docs[0]; @@ -57,12 +58,12 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn GetRedirectRule(sRulePath:String) { + pub fn get_redirect_rule(rule_path: String) { } // Analyze transport layer rule - pub fn GetTransportNetRule(sRulePath:String) { + pub fn get_transport_rule(rule_path:String) { } diff --git a/plugins/wdriver-rust/tests/tsDrvmg.rs b/plugins/wdriver-rust/tests/tsDrvmg.rs index 567353c7..d62dd31a 100644 --- a/plugins/wdriver-rust/tests/tsDrvmg.rs +++ b/plugins/wdriver-rust/tests/tsDrvmg.rs @@ -1,17 +1,17 @@ -use netCom::DrivenManageImpl; +use wdriver_rs::drvmg::DrivenManageImpl; #[test] // 驱动启动状态 -pub fn UnitGetDrivenStu() { - let strDrivenName :String= String::from("\\Hades\\HadesNet"); - let mut hObjDrivenMang: DrivenManageImpl = DrivenManageImpl::new(); - let bSuc: bool = hObjDrivenMang.OpenDriverHandle(strDrivenName); +pub fn unit_get_driven_stu() { + let drivename :String= String::from("\\Hades\\HadesNet"); + let mut driverobj: DrivenManageImpl = DrivenManageImpl::new(); + let b : bool = driverobj.open_driver_handle(drivename); loop { - if bSuc == false { + if b == false { break; } // checkout handle invalid - if hObjDrivenMang.m_hDriver.is_invalid() { + if driverobj.handle.is_invalid() { break; } diff --git a/plugins/wdriver-rust/tests/tsRule.rs b/plugins/wdriver-rust/tests/tsRule.rs index 017cdb43..ca7dda40 100644 --- a/plugins/wdriver-rust/tests/tsRule.rs +++ b/plugins/wdriver-rust/tests/tsRule.rs @@ -1,20 +1,19 @@ use fast_log::{consts::LogSize, plugin::{file_split::RollingType, packer::LogPacker}}; -use netCom::RuleImpl; - +use wdriver_rs::rule::RuleImpl; #[test] -pub fn UnitGetDnsRule() { - let mut sYamData: String = String::from(""); - let mut sCurrentPath = std::env::current_dir().unwrap().to_str().unwrap().to_string(); - if sCurrentPath.is_empty() { +pub fn unit_get_dns_rule() { + let mut yaml_data: String = String::from(""); + let mut current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); + if current_path.is_empty() { log::error!("Get Rule DirPath Failuer."); return; } - let sNetRulePath: String = sCurrentPath + "\\config\\networkRuleConfig.yaml"; - let bRet: bool = RuleImpl::GetDnsRule(sNetRulePath, &mut sYamData); - if false == bRet { + let rule_path: String = current_path + "\\config\\networkRuleConfig.yaml"; + let b: bool = RuleImpl::get_dns_rule(rule_path, &mut yaml_data); + if false == b { log::error!("Get Rule DirPath Failuer."); return; } - println!("Analyze Rule Success. {}",sYamData); + println!("Analyze Rule Success. {}", yaml_data); } \ No newline at end of file From 68f4fec212307d650f84dab06e3c45b2043bdc15 Mon Sep 17 00:00:00 2001 From: czy Date: Mon, 2 Dec 2024 18:14:10 +0800 Subject: [PATCH 04/38] rule.rs rule engine init. --- plugins/wdriver-rust/Cargo.toml | 9 +- plugins/wdriver-rust/build.rs | 4 +- .../config/networkRuleConfig.yaml | 13 +- plugins/wdriver-rust/src/drvmg.rs | 27 +- plugins/wdriver-rust/src/lib.rs | 2 +- plugins/wdriver-rust/src/rule.rs | 379 ++++++++++++++++-- plugins/wdriver-rust/tests/tsDrvmg.rs | 20 - plugins/wdriver-rust/tests/tsRule.rs | 19 - 8 files changed, 385 insertions(+), 88 deletions(-) delete mode 100644 plugins/wdriver-rust/tests/tsDrvmg.rs delete mode 100644 plugins/wdriver-rust/tests/tsRule.rs diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index c7a114ec..e6c6c097 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -7,6 +7,10 @@ edition = "2021" log = "0.4.17" yaml-rust = "0.4" fast_log = "1.5.51" +serde = { version = "1.0.211", features = ["derive"] } +serde_json = "1.0.132" +napi = { version = "2.16.12", default-features = false, features = ["napi4", "serde-json", "tokio_rt"] } +napi-derive = "2.16.12" [dependencies.windows] version = "0.52" @@ -16,4 +20,7 @@ features = [ "Win32_Storage_FileSystem", "Win32_System_Threading", "Win32_System_IO", -] \ No newline at end of file +] + +[profile.test] +debug-assertions = false \ No newline at end of file diff --git a/plugins/wdriver-rust/build.rs b/plugins/wdriver-rust/build.rs index 96850e36..da0f5d92 100644 --- a/plugins/wdriver-rust/build.rs +++ b/plugins/wdriver-rust/build.rs @@ -1,3 +1 @@ -pub fn main() { - -} \ No newline at end of file +pub fn main() {} diff --git a/plugins/wdriver-rust/config/networkRuleConfig.yaml b/plugins/wdriver-rust/config/networkRuleConfig.yaml index 260cba59..873686f4 100644 --- a/plugins/wdriver-rust/config/networkRuleConfig.yaml +++ b/plugins/wdriver-rust/config/networkRuleConfig.yaml @@ -1,4 +1,4 @@ -egress: +tc: - name: "eguard_egress_test_project" address: "192.168.1.1/24" protocol: TCP # ALL/TCP/UDP @@ -10,7 +10,7 @@ egress: - name: "test_tcp_redirect" #address: "" - protocol: TCP # TCP + protocol: "TCP" # TCP #ports: # empty means all ports. # - 80 # - 8079-8080 @@ -18,4 +18,11 @@ egress: redirectip: "192.168.188.188" # redirect to ipaddrss redirectport: "88" # redirect to port action: REDIRECT - level: INFO \ No newline at end of file + level: INFO +dns: + - name: "eguard_egress_test_dns" + action: DENY + domain: "grpc.hades.store" + - name: "eguard_egress_test_dns_1" + action: DENY + domain: "*.baidu.com" \ No newline at end of file diff --git a/plugins/wdriver-rust/src/drvmg.rs b/plugins/wdriver-rust/src/drvmg.rs index 638f3943..345c30c6 100644 --- a/plugins/wdriver-rust/src/drvmg.rs +++ b/plugins/wdriver-rust/src/drvmg.rs @@ -1,8 +1,8 @@ +use std::{fs, io::Read, path::PathBuf, ptr::null}; use windows::{ core::*, Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Threading::*, Win32::System::IO::*, }; -use std::{fs, ptr::null, io::Read, path::PathBuf}; pub struct DrivenManageImpl { pub handle: HANDLE, @@ -12,17 +12,16 @@ impl DrivenManageImpl { pub fn new() -> Self { Self { handle: HANDLE(0) } } - + // Chekcout Driver Status - pub fn get_driver_stu(driver_name:String) -> bool { - + pub fn get_driver_stu(driver_name: String) -> bool { return true; } // Open DriverHandle - pub fn open_driver_handle(&mut self, driver_name:String) -> bool { + pub fn open_driver_handle(&mut self, driver_name: String) -> bool { unsafe { - let dwAttribute:u32 = 2147483648u32 | 1073741824u32; + let dwAttribute: u32 = 2147483648u32 | 1073741824u32; let hResult: std::prelude::v1::Result = CreateFileA( PCSTR(driver_name.as_ptr()), dwAttribute, @@ -36,25 +35,19 @@ impl DrivenManageImpl { let hDriver: HANDLE = hResult.unwrap(); self.handle = hDriver; return true; - } - else { + } else { let hResultError: std::prelude::v1::Result<(), Error> = GetLastError(); - println!("{}",hResultError.unwrap_err().code()); + println!("{}", hResultError.unwrap_err().code()); return false; } } } // Send Data Pop Data - pub fn send_driver_data(code:u32, data:String) -> bool { - unsafe { - - } + pub fn send_driver_data(code: u32, data: String) -> bool { return true; } // Read Data Push Queue - pub fn read_driver_data() { - - } -} \ No newline at end of file + pub fn read_driver_data() {} +} diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs index 2410f681..6573dc29 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver-rust/src/lib.rs @@ -1,3 +1,3 @@ // @exterm lib mod -pub mod rule; pub mod drvmg; +pub mod rule; diff --git a/plugins/wdriver-rust/src/rule.rs b/plugins/wdriver-rust/src/rule.rs index 24764e27..41e2d3ec 100644 --- a/plugins/wdriver-rust/src/rule.rs +++ b/plugins/wdriver-rust/src/rule.rs @@ -1,52 +1,247 @@ extern crate yaml_rust; +use napi::threadsafe_function::ThreadsafeFunction; +use napi_derive::napi; +use serde::{Deserialize, Serialize}; +use std::{ + fs, + io::Read, + path::PathBuf, + ptr::null, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; +use yaml_rust::{YamlEmitter, YamlLoader}; -use std::{fs, ptr::null, io::Read, path::PathBuf}; -use yaml_rust::{YamlLoader, YamlEmitter}; +pub struct RuleImpl { + // // dns + // rule_dns: Arc, -pub struct RuleImpl; + // // redirect + // rule_redirect: Arc, + + // // transport + // rule_transport: Arc, + + // // directory + // rule_directory: Arc, + + // // process + // rule_process: Arc, + + // // thread + // rule_thread: Arc, + + // // register + // rule_register: Arc, + // dns + rule_dns: RuleDns, + + // redirect + rule_redirect: RuleRediRect, + + // transport + rule_transport: RuleTranSport, + + // directory + rule_directory: RuleDirectoryArray, + + // process + rule_process: RuleProcess, + + // thread + rule_thread: RuleThread, + + // register + rule_register: RuleResgiter, +} impl RuleImpl { + pub async fn init() -> bool { + // get cuurent exec path + let current_path = std::env::current_dir() + .unwrap() + .to_str() + .unwrap() + .to_string(); + if current_path.is_empty() { + log::error!("Get Rule DirPath Failuer."); + return false; + } + + // init dns rule + let mut rule_dns = RuleDns { + name: "".to_string(), + address: "".to_string(), + protocol: "".to_string(), + action: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; + let b: bool = RuleImpl::get_dns_rule(rule_path, &mut _data); + if true == b { + println!("analyze dns rule success. {}", _data); + } else { + log::error!("get dns rule fails."); + } + } + + // init redirect rule + let mut rule_redirect = RuleRediRect { + name: "".to_string(), + address: "".to_string(), + protocol: "".to_string(), + action: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; + let b: bool = RuleImpl::get_redirect_rule(rule_path, &mut _data); + if true == b { + println!("analyze redirect rule success. {}", _data); + } else { + log::error!("get redirect rule fails."); + } + } + + // init transport rule + let mut rule_transport = RuleTranSport { + name: "".to_string(), + address: "".to_string(), + protocol: "".to_string(), + action: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; + let b: bool = RuleImpl::get_transport_rule(rule_path, &mut _data); + if true == b { + println!("analyze transport rule success. {}", _data); + } else { + log::error!("get transport rule fails."); + } + } + + // init directory rule + let mut vec_directory: Vec = Vec::new(); + let mut rule_directory = RuleDirectoryArray { + rule_array: vec_directory, + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\directoryRuleConfig.json"; + let _ = RuleImpl::get_dirtecory_rule(rule_path, &mut _data); + // if true == b { + // println!("analyze directory success. {}", _data); + // } else { + // log::error!("get directory rule fails."); + // } + } + + // init process rule + let mut rule_process = RuleProcess { + rule_type: "".to_string(), + process_name: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\processRuleConfig.json"; + let b: bool = RuleImpl::get_process_rule(rule_path, &mut _data); + if true == b { + println!("analyze transport success. {}", _data); + } else { + log::error!("get transport rule fails."); + } + } + + // init thread rule + let mut rule_thread = RuleThread { + process_name: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\threadRuleConfig.json"; + let b: bool = RuleImpl::get_thread_rule(rule_path, &mut _data); + if true == b { + println!("analyze thread success. {}", _data); + } else { + log::error!("get thread rule fails."); + } + } + + // init register rule + let mut rule_register = RuleResgiter { + rule_type: "".to_string(), + process_name: "".to_string(), + register_key: "".to_string(), + register_permiss: "".to_string(), + }; + { + let mut _data: String = String::from(""); + let rule_path: String = current_path.clone() + "\\config\\registerRuleConfig_.json"; + let b: bool = RuleImpl::get_register_rule(rule_path, &mut _data); + if true == b { + println!("analyze register success. {}", _data); + } else { + log::error!("get register rule fails."); + } + } + + Self { + rule_dns, + rule_redirect, + rule_transport, + rule_directory, + rule_process, + rule_thread, + rule_register, + }; + + return true; + } // Read json, yaml Data - pub fn read_file_rule(rule_path: String, yaml_data: &mut String) -> bool { - if rule_path.is_empty() || null() == yaml_data { + pub fn read_file_data(file_path: String, _data: &mut String) -> bool { + if file_path.is_empty() || null() == _data { return false; } - if !PathBuf::from(rule_path.to_string()).exists() { - log::error!("Checkout Yaml File exists failuer. {}", rule_path); + if !PathBuf::from(file_path.to_string()).exists() { + log::error!("Checkout Yaml File exists failuer. {}", file_path); return false; } - *yaml_data = fs::read_to_string(rule_path.to_string()).unwrap(); + *_data = fs::read_to_string(file_path.to_string()).unwrap(); return true; } // Analyze dns rule - pub fn get_dns_rule(rule_path: String, yaml_data: &mut String) -> bool { - if rule_path.is_empty() { + pub fn get_dns_rule(file_path: String, _data: &mut String) -> bool { + if file_path.is_empty() { return false; } - - RuleImpl::read_file_rule(rule_path, yaml_data); - if yaml_data.is_empty() { + + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { return false; } - let docs: Vec = YamlLoader::load_from_str(yaml_data).unwrap(); + let docs: Vec = YamlLoader::load_from_str(_data).unwrap(); // Multi document support, doc is a yaml::Yaml let doc: &yaml_rust::Yaml = &docs[0]; - + // Debug support println!("{:?}", doc); - + // Index access for map & array // assert_eq!(doc["foo"][0].as_str().unwrap(), "list1"); // assert_eq!(doc["bar"][1].as_f64().unwrap(), 2.0); - + // // Chained key/array access is checked and won't panic, // // return BadValue if they are not exist. // assert!(doc["INVALID_KEY"][100].is_badvalue()); - + // // Dump the YAML object // let mut out_str = String::new(); // { @@ -58,20 +253,156 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn get_redirect_rule(rule_path: String) { + pub fn get_redirect_rule(file_path: String, _data: &mut String) -> bool { + if file_path.is_empty() { + return false; + } + + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + return false; + } + + let docs: Vec = YamlLoader::load_from_str(_data).unwrap(); + + // Multi document support, doc is a yaml::Yaml + let doc: &yaml_rust::Yaml = &docs[0]; + // Debug support + println!("{:?}", doc); + return true; } // Analyze transport layer rule - pub fn get_transport_rule(rule_path:String) { + pub fn get_transport_rule(file_path: String, _data: &mut String) -> bool { + if file_path.is_empty() { + return false; + } + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + return false; + } + + let docs: Vec = YamlLoader::load_from_str(_data).unwrap(); + + // Multi document support, doc is a yaml::Yaml + let doc: &yaml_rust::Yaml = &docs[0]; + + // Debug support + println!("{:?}", doc); + return true; + } + + // Analyze directory rule + pub fn get_dirtecory_rule(file_path: String, _data: &mut String) -> Result<(), napi::Error> { + loop { + if file_path.is_empty() { + break; + } + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + break; + } + let rule_array: RuleDirectoryArray = serde_json::from_str(&_data)?; + break; + } + + Ok(()) + } + + // Analyze process rule + pub fn get_process_rule(file_path: String, _data: &mut String) -> bool { + return true; } + // Analyze thread rule + pub fn get_thread_rule(file_path: String, _data: &mut String) -> bool { + return true; + } + + // Analyze register rule + pub fn get_register_rule(file_path: String, _data: &mut String) -> bool { + return true; + } } -// Unit test -#[cfg(test)] -mod test{ - +#[derive(Serialize, Deserialize)] +pub struct RuleDns { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "address")] + pub address: String, + #[serde(rename = "protocol")] + pub protocol: String, + #[serde(rename = "action")] + pub action: String, +} + +#[derive(Serialize, Deserialize)] +pub struct RuleRediRect { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "address")] + pub address: String, + #[serde(rename = "protocol")] + pub protocol: String, + #[serde(rename = "action")] + pub action: String, +} + +#[derive(Serialize, Deserialize)] +pub struct RuleTranSport { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "address")] + pub address: String, + #[serde(rename = "protocol")] + pub protocol: String, + #[serde(rename = "action")] + pub action: String, } +#[derive(Serialize, Deserialize)] +pub struct RuleDirectory { + #[serde(rename = "FileIORuleMod")] + pub rule_type: String, + #[serde(rename = "processName")] + pub process_name: String, + #[serde(rename = "Directory")] + pub directory_path: String, +} +#[derive(Serialize, Deserialize)] +pub struct RuleDirectoryArray { + rule_array: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct RuleProcess { + #[serde(rename = "processRuleMod")] + pub rule_type: String, + #[serde(rename = "processName")] + pub process_name: String, +} + +#[derive(Serialize, Deserialize)] +pub struct RuleResgiter { + #[serde(rename = "registerRuleMod")] + pub rule_type: String, + #[serde(rename = "processName")] + pub process_name: String, + #[serde(rename = "registerValuse")] + pub register_key: String, + #[serde(rename = "permissions")] + pub register_permiss: String, +} + +#[derive(Serialize, Deserialize)] +pub struct RuleThread { + #[serde(rename = "InjectIpsProcessNameArray")] + pub process_name: String, +} + +// Unit test +#[cfg(test)] +mod test {} diff --git a/plugins/wdriver-rust/tests/tsDrvmg.rs b/plugins/wdriver-rust/tests/tsDrvmg.rs deleted file mode 100644 index d62dd31a..00000000 --- a/plugins/wdriver-rust/tests/tsDrvmg.rs +++ /dev/null @@ -1,20 +0,0 @@ -use wdriver_rs::drvmg::DrivenManageImpl; - -#[test] -// 驱动启动状态 -pub fn unit_get_driven_stu() { - let drivename :String= String::from("\\Hades\\HadesNet"); - let mut driverobj: DrivenManageImpl = DrivenManageImpl::new(); - let b : bool = driverobj.open_driver_handle(drivename); - loop { - if b == false { - break; - } - // checkout handle invalid - if driverobj.handle.is_invalid() { - break; - } - - break; - } -} \ No newline at end of file diff --git a/plugins/wdriver-rust/tests/tsRule.rs b/plugins/wdriver-rust/tests/tsRule.rs deleted file mode 100644 index ca7dda40..00000000 --- a/plugins/wdriver-rust/tests/tsRule.rs +++ /dev/null @@ -1,19 +0,0 @@ -use fast_log::{consts::LogSize, plugin::{file_split::RollingType, packer::LogPacker}}; -use wdriver_rs::rule::RuleImpl; - -#[test] -pub fn unit_get_dns_rule() { - let mut yaml_data: String = String::from(""); - let mut current_path = std::env::current_dir().unwrap().to_str().unwrap().to_string(); - if current_path.is_empty() { - log::error!("Get Rule DirPath Failuer."); - return; - } - let rule_path: String = current_path + "\\config\\networkRuleConfig.yaml"; - let b: bool = RuleImpl::get_dns_rule(rule_path, &mut yaml_data); - if false == b { - log::error!("Get Rule DirPath Failuer."); - return; - } - println!("Analyze Rule Success. {}", yaml_data); -} \ No newline at end of file From eec318a266692bcb43b58a15c632137ed19193f2 Mon Sep 17 00:00:00 2001 From: czy Date: Tue, 3 Dec 2024 18:41:39 +0800 Subject: [PATCH 05/38] rule unit test --- plugins/wdriver-rust/Cargo.toml | 10 +- .../config/directoryRuleConfig.json | 4 +- plugins/wdriver-rust/src/drvmg.rs | 41 +++- plugins/wdriver-rust/src/lib.rs | 1 + plugins/wdriver-rust/src/log.rs | 56 +++++ plugins/wdriver-rust/src/rule.rs | 212 ++++++++++-------- plugins/wdriver-rust/tests/ts_drvmg.rs | 20 ++ plugins/wdriver-rust/tests/ts_rule.rs | 21 ++ 8 files changed, 253 insertions(+), 112 deletions(-) create mode 100644 plugins/wdriver-rust/src/log.rs create mode 100644 plugins/wdriver-rust/tests/ts_drvmg.rs create mode 100644 plugins/wdriver-rust/tests/ts_rule.rs diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index e6c6c097..9d7b0720 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -4,13 +4,21 @@ version = "23.11.30" edition = "2021" [dependencies] -log = "0.4.17" +log = "0.4.22" +fern = "0.7.0" +chrono = "0.4.38" +version = "3.0.0" +process_path = "0.1" yaml-rust = "0.4" fast_log = "1.5.51" serde = { version = "1.0.211", features = ["derive"] } serde_json = "1.0.132" napi = { version = "2.16.12", default-features = false, features = ["napi4", "serde-json", "tokio_rt"] } napi-derive = "2.16.12" +tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } +tokio-util = "0.7.12" +win_etw_macros = "0.1.*" +win_etw_provider = "0.1.*" [dependencies.windows] version = "0.52" diff --git a/plugins/wdriver-rust/config/directoryRuleConfig.json b/plugins/wdriver-rust/config/directoryRuleConfig.json index 949c42eb..ac799a1f 100644 --- a/plugins/wdriver-rust/config/directoryRuleConfig.json +++ b/plugins/wdriver-rust/config/directoryRuleConfig.json @@ -2,12 +2,12 @@ { "FileIORuleMod": 1, "processName": "CreateFile.exe|powershell.exe", - "Directory": "D:\\Document\\|C:\\System\\AppData\\|C:\\testIPS\\" + "Directory": "D:\\Document\\|C:\\System\\AppData\\|C:\\testIPS" }, { "FileIORuleMod": 2, "processName": "explorer.exe|cmd.exe", - "Directory": "D:\\Document1\\|C:\\System\\AppData1\\|C:\\testIPS\\" + "Directory": "D:\\Document1\\|C:\\System\\AppData1\\|C:\\testIPS" } ] \ No newline at end of file diff --git a/plugins/wdriver-rust/src/drvmg.rs b/plugins/wdriver-rust/src/drvmg.rs index 345c30c6..92674214 100644 --- a/plugins/wdriver-rust/src/drvmg.rs +++ b/plugins/wdriver-rust/src/drvmg.rs @@ -1,6 +1,9 @@ -use std::{fs, io::Read, path::PathBuf, ptr::null}; +use std::{fs, io::Read, path::PathBuf, ptr::null, result}; use windows::{ - core::*, Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Threading::*, + core::*, + Win32::Foundation::*, + Win32::Storage::FileSystem::*, + Win32::System::Threading::*, Win32::System::IO::*, }; @@ -21,33 +24,47 @@ impl DrivenManageImpl { // Open DriverHandle pub fn open_driver_handle(&mut self, driver_name: String) -> bool { unsafe { - let dwAttribute: u32 = 2147483648u32 | 1073741824u32; - let hResult: std::prelude::v1::Result = CreateFileA( + let attribute: u32 = 2147483648u32 | 1073741824u32; + let result: std::prelude::v1::Result = CreateFileA( PCSTR(driver_name.as_ptr()), - dwAttribute, + attribute, FILE_SHARE_MODE(0), None, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None, ); - if hResult.is_ok() { - let hDriver: HANDLE = hResult.unwrap(); - self.handle = hDriver; + if result.is_ok() { + let driver_handle: HANDLE = result.unwrap(); + self.handle = driver_handle; return true; } else { - let hResultError: std::prelude::v1::Result<(), Error> = GetLastError(); - println!("{}", hResultError.unwrap_err().code()); + let err: std::prelude::v1::Result<(), Error> = GetLastError(); + println!("{}", err.unwrap_err().code()); return false; } } } // Send Data Pop Data - pub fn send_driver_data(code: u32, data: String) -> bool { + pub async fn send_driver_data(&self, _code: u32, _data: String) -> bool { + return true; } // Read Data Push Queue - pub fn read_driver_data() {} + pub async fn read_driver_data(&self) { + + } + + pub fn close_driver_handle(&mut self) { + if self.handle.is_invalid(){ + return; + } + unsafe { + CloseHandle(self.handle); + } + self.handle = HANDLE(0); + } + } diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs index 6573dc29..4e69bc34 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver-rust/src/lib.rs @@ -1,3 +1,4 @@ // @exterm lib mod pub mod drvmg; pub mod rule; +pub mod log; diff --git a/plugins/wdriver-rust/src/log.rs b/plugins/wdriver-rust/src/log.rs new file mode 100644 index 00000000..c888cff4 --- /dev/null +++ b/plugins/wdriver-rust/src/log.rs @@ -0,0 +1,56 @@ +use std::path::PathBuf; +use log::LevelFilter; + +pub fn init_log(curr_path: &PathBuf) -> bool { + let debug_mode = { + let test_meta = curr_path.join("test"); + test_meta.exists() + }; + + { + let _ = std::fs::create_dir_all(curr_path.join("log")); + let log_file = curr_path.join("log/wdriver.log"); + //clean + if let Ok(mt) = std::fs::metadata(&log_file) { + if mt.len() > 10 * 1024 * 1024 { + //rename + let log_file_old = curr_path.join("log/wdriver.log.old"); + let _ = std::fs::remove_file(&log_file_old); + let _ = std::fs::rename(&log_file, &log_file_old); + } + } + + let fern_log_file = fern::log_file(log_file); + if fern_log_file.is_err() { + eprintln!("log file error:{:?}", fern_log_file.unwrap_err()); + return debug_mode; + } + let log_res = fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "{} [{}] {}:{} {}", + chrono::Local::now().format("[%Y-%m-%d %H:%M:%S.%6f]"), + record.level(), + record.target(), + record.line().unwrap_or_default(), + message + )) + }) + // .level(if debug_mode { + // LevelFilter::Debug + // } else { + // LevelFilter::Info + // }) + .level(LevelFilter::Debug) + .chain(fern_log_file.unwrap()) + .apply(); + if log_res.is_err() { + eprintln!("fern log error:{:?}", log_res.unwrap_err()); + } + } + + log::info!("------------------------------------------------------------------"); + log::info!("process path:{:?} client version:{}", curr_path, version::version!()); + + debug_mode +} diff --git a/plugins/wdriver-rust/src/rule.rs b/plugins/wdriver-rust/src/rule.rs index 41e2d3ec..bc09c926 100644 --- a/plugins/wdriver-rust/src/rule.rs +++ b/plugins/wdriver-rust/src/rule.rs @@ -1,4 +1,6 @@ extern crate yaml_rust; +use crate::{log::init_log}; + use napi::threadsafe_function::ThreadsafeFunction; use napi_derive::napi; use serde::{Deserialize, Serialize}; @@ -15,37 +17,17 @@ use std::{ use yaml_rust::{YamlEmitter, YamlLoader}; pub struct RuleImpl { - // // dns - // rule_dns: Arc, - - // // redirect - // rule_redirect: Arc, - - // // transport - // rule_transport: Arc, - - // // directory - // rule_directory: Arc, - - // // process - // rule_process: Arc, - - // // thread - // rule_thread: Arc, - - // // register - // rule_register: Arc, // dns - rule_dns: RuleDns, + rule_dns: Vec, // redirect - rule_redirect: RuleRediRect, + rule_redirect: Vec, // transport - rule_transport: RuleTranSport, + rule_transport: Vec, // directory - rule_directory: RuleDirectoryArray, + rule_directory: Vec, // process rule_process: RuleProcess, @@ -54,11 +36,16 @@ pub struct RuleImpl { rule_thread: RuleThread, // register - rule_register: RuleResgiter, + rule_register: Vec, } impl RuleImpl { pub async fn init() -> bool { + let path = get_path().unwrap(); + let debug = init_log(&path); + if !debug { + } + // get cuurent exec path let current_path = std::env::current_dir() .unwrap() @@ -71,86 +58,68 @@ impl RuleImpl { } // init dns rule - let mut rule_dns = RuleDns { - name: "".to_string(), - address: "".to_string(), - protocol: "".to_string(), - action: "".to_string(), - }; + let mut rule_dns : Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; - let b: bool = RuleImpl::get_dns_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_dns_rule(rule_path, &mut _data, &mut rule_dns); if true == b { - println!("analyze dns rule success. {}", _data); + log::debug!("analyze dns rule success. {}", _data); } else { log::error!("get dns rule fails."); } } // init redirect rule - let mut rule_redirect = RuleRediRect { - name: "".to_string(), - address: "".to_string(), - protocol: "".to_string(), - action: "".to_string(), - }; + let mut rule_redirect :Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; - let b: bool = RuleImpl::get_redirect_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_redirect_rule(rule_path, &mut _data, &mut rule_redirect); if true == b { - println!("analyze redirect rule success. {}", _data); + log::debug!("analyze redirect rule success. {}", _data); } else { log::error!("get redirect rule fails."); } } // init transport rule - let mut rule_transport = RuleTranSport { - name: "".to_string(), - address: "".to_string(), - protocol: "".to_string(), - action: "".to_string(), - }; + let mut rule_transport:Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; - let b: bool = RuleImpl::get_transport_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_transport_rule(rule_path, &mut _data, &mut rule_transport); if true == b { - println!("analyze transport rule success. {}", _data); + log::debug!("analyze transport rule success. {}", _data); } else { log::error!("get transport rule fails."); } } // init directory rule - let mut vec_directory: Vec = Vec::new(); - let mut rule_directory = RuleDirectoryArray { - rule_array: vec_directory, - }; + let mut rule_directory: Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\directoryRuleConfig.json"; - let _ = RuleImpl::get_dirtecory_rule(rule_path, &mut _data); - // if true == b { - // println!("analyze directory success. {}", _data); - // } else { - // log::error!("get directory rule fails."); - // } + let b = RuleImpl::get_dirtecory_rule(rule_path, &mut _data, &mut rule_directory).is_ok(); + if true == b { + log::debug!("analyze directory success. {}", _data); + } else { + log::error!("get directory rule fails."); + } } // init process rule let mut rule_process = RuleProcess { - rule_type: "".to_string(), + rule_type: 0, process_name: "".to_string(), }; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\processRuleConfig.json"; - let b: bool = RuleImpl::get_process_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_process_rule(rule_path, &mut _data, &mut rule_process).is_ok(); if true == b { - println!("analyze transport success. {}", _data); + log::debug!("analyze transport success. {}", _data); } else { log::error!("get transport rule fails."); } @@ -163,27 +132,22 @@ impl RuleImpl { { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\threadRuleConfig.json"; - let b: bool = RuleImpl::get_thread_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_thread_rule(rule_path, &mut _data, &mut rule_thread).is_ok(); if true == b { - println!("analyze thread success. {}", _data); + log::debug!("analyze thread success. {}", _data); } else { log::error!("get thread rule fails."); } } // init register rule - let mut rule_register = RuleResgiter { - rule_type: "".to_string(), - process_name: "".to_string(), - register_key: "".to_string(), - register_permiss: "".to_string(), - }; + let mut rule_register: Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\registerRuleConfig_.json"; - let b: bool = RuleImpl::get_register_rule(rule_path, &mut _data); + let b: bool = RuleImpl::get_register_rule(rule_path, &mut _data, &mut rule_register).is_ok(); if true == b { - println!("analyze register success. {}", _data); + log::debug!("analyze register success. {}", _data); } else { log::error!("get register rule fails."); } @@ -216,7 +180,7 @@ impl RuleImpl { } // Analyze dns rule - pub fn get_dns_rule(file_path: String, _data: &mut String) -> bool { + pub fn get_dns_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -253,7 +217,7 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn get_redirect_rule(file_path: String, _data: &mut String) -> bool { + pub fn get_redirect_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -274,7 +238,7 @@ impl RuleImpl { } // Analyze transport layer rule - pub fn get_transport_rule(file_path: String, _data: &mut String) -> bool { + pub fn get_transport_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -295,7 +259,7 @@ impl RuleImpl { } // Analyze directory rule - pub fn get_dirtecory_rule(file_path: String, _data: &mut String) -> Result<(), napi::Error> { + pub fn get_dirtecory_rule(file_path: String, _data: &mut String, rule_diretcory: &mut Vec) -> Result<(), napi::Error> { loop { if file_path.is_empty() { break; @@ -304,7 +268,9 @@ impl RuleImpl { if _data.is_empty() { break; } - let rule_array: RuleDirectoryArray = serde_json::from_str(&_data)?; + + *rule_diretcory = serde_json::from_str(&_data)?; + break; } @@ -312,19 +278,72 @@ impl RuleImpl { } // Analyze process rule - pub fn get_process_rule(file_path: String, _data: &mut String) -> bool { - return true; + pub fn get_process_rule(file_path: String, _data: &mut String, rule_process: &mut RuleProcess) -> Result<(), napi::Error> { + loop { + if file_path.is_empty() { + break; + } + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + break; + } + + *rule_process = serde_json::from_str(&_data)?; + + break; + } + + Ok(()) } // Analyze thread rule - pub fn get_thread_rule(file_path: String, _data: &mut String) -> bool { - return true; + pub fn get_thread_rule(file_path: String, _data: &mut String, rule_thread: &mut RuleThread) -> Result<(), napi::Error> { + loop { + if file_path.is_empty() { + break; + } + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + break; + } + + *rule_thread = serde_json::from_str(&_data).unwrap(); + + break; + } + + Ok(()) } // Analyze register rule - pub fn get_register_rule(file_path: String, _data: &mut String) -> bool { - return true; + pub fn get_register_rule(file_path: String, _data: &mut String, rule_register: &mut Vec) -> Result<(), napi::Error> { + loop { + if file_path.is_empty() { + break; + } + RuleImpl::read_file_data(file_path, _data); + if _data.is_empty() { + break; + } + + *rule_register = serde_json::from_str(&_data).unwrap(); + + break; + } + + Ok(()) } + +} + +pub fn get_path() -> Result { + let path = process_path::get_dylib_path(); + if let Some(p) = path { + if let Some(parent) = p.parent() { + return Ok(parent.to_path_buf()); + } + } + Err(napi::Error::from_reason("get dylib path error".to_string())) } #[derive(Serialize, Deserialize)] @@ -366,29 +385,31 @@ pub struct RuleTranSport { #[derive(Serialize, Deserialize)] pub struct RuleDirectory { #[serde(rename = "FileIORuleMod")] - pub rule_type: String, + pub rule_type: u32, #[serde(rename = "processName")] pub process_name: String, #[serde(rename = "Directory")] pub directory_path: String, } -#[derive(Serialize, Deserialize)] -pub struct RuleDirectoryArray { - rule_array: Vec, -} #[derive(Serialize, Deserialize)] pub struct RuleProcess { #[serde(rename = "processRuleMod")] - pub rule_type: String, + pub rule_type: u32, #[serde(rename = "processName")] pub process_name: String, } +#[derive(Serialize, Deserialize)] +pub struct RuleThread { + #[serde(rename = "InjectIpsProcessNameArray")] + pub process_name: String, +} + #[derive(Serialize, Deserialize)] pub struct RuleResgiter { #[serde(rename = "registerRuleMod")] - pub rule_type: String, + pub rule_type: u32, #[serde(rename = "processName")] pub process_name: String, #[serde(rename = "registerValuse")] @@ -397,12 +418,9 @@ pub struct RuleResgiter { pub register_permiss: String, } -#[derive(Serialize, Deserialize)] -pub struct RuleThread { - #[serde(rename = "InjectIpsProcessNameArray")] - pub process_name: String, -} - // Unit test #[cfg(test)] -mod test {} +mod test { + + +} diff --git a/plugins/wdriver-rust/tests/ts_drvmg.rs b/plugins/wdriver-rust/tests/ts_drvmg.rs new file mode 100644 index 00000000..4241312f --- /dev/null +++ b/plugins/wdriver-rust/tests/ts_drvmg.rs @@ -0,0 +1,20 @@ +use wdriver_rs::drvmg::DrivenManageImpl; + +#[test] +// 驱动启动状态 +pub fn unit_get_driven_stu() { + let drivename: String = String::from("\\Hades\\HadesNet"); + let mut driverobj: DrivenManageImpl = DrivenManageImpl::new(); + let b: bool = driverobj.open_driver_handle(drivename); + loop { + if b == false { + break; + } + // checkout handle invalid + if driverobj.handle.is_invalid() { + break; + } + + break; + } +} diff --git a/plugins/wdriver-rust/tests/ts_rule.rs b/plugins/wdriver-rust/tests/ts_rule.rs new file mode 100644 index 00000000..24c1d478 --- /dev/null +++ b/plugins/wdriver-rust/tests/ts_rule.rs @@ -0,0 +1,21 @@ +use fast_log::{ + consts::LogSize, + plugin::{file_split::RollingType, packer::LogPacker}, +}; +use wdriver_rs::rule::RuleImpl; +use tokio::{io::AsyncWriteExt, sync::{mpsc::Sender, RwLock}, task::JoinHandle, runtime::Runtime}; +use std::{thread::{self, current}, time}; + +#[test] +pub fn unit_get_dns_rule() { + let runtime = tokio::runtime::Runtime::new().ok().unwrap(); + runtime.spawn(async move { + let reply = RuleImpl::init().await; + if false == reply { + println!("rule init."); + } + }); + + let ten_millis = time::Duration::from_millis(100000); + thread::sleep(ten_millis); +} From a10e48971a9585c8f28f376bad1c007990abeefb Mon Sep 17 00:00:00 2001 From: czy Date: Wed, 4 Dec 2024 19:49:46 +0800 Subject: [PATCH 06/38] appcore & kernelcore & unit test --- plugins/wdriver-rust/Cargo.toml | 7 +- .../wdriver-rust/src/appcore/app_account.rs | 39 ++++++++ .../wdriver-rust/src/appcore/app_autostart.rs | 5 ++ plugins/wdriver-rust/src/appcore/app_file.rs | 5 ++ .../wdriver-rust/src/appcore/app_include.rs | 88 +++++++++++++++++++ plugins/wdriver-rust/src/appcore/app_net.rs | 79 +++++++++++++++++ .../wdriver-rust/src/appcore/app_process.rs | 5 ++ .../src/appcore/app_service_software.rs | 5 ++ .../wdriver-rust/src/appcore/app_sysinfo.rs | 5 ++ plugins/wdriver-rust/src/appcore/etw/etw.rs | 6 ++ plugins/wdriver-rust/src/appcore/etw/mod.rs | 1 + plugins/wdriver-rust/src/appcore/mod.rs | 8 ++ .../src/{rule.rs => config/config.rs} | 30 +++---- plugins/wdriver-rust/src/config/mod.rs | 1 + .../wdriver-rust/src/{ => driver}/drvmg.rs | 15 ++-- plugins/wdriver-rust/src/driver/mod.rs | 1 + .../src/{handle.rs => events/event.rs} | 5 +- plugins/wdriver-rust/src/events/mod.rs | 1 + .../{mod.rs => kercore/ark/ark_deviceinfo.rs} | 0 .../src/kercore/ark/ark_dpctimer.rs | 0 .../src/kercore/ark/ark_enumcallback.rs | 0 .../wdriver-rust/src/kercore/ark/ark_fsd.rs | 0 .../wdriver-rust/src/kercore/ark/ark_idt.rs | 0 .../src/kercore/ark/ark_mouse_keyboard.rs | 0 .../src/kercore/ark/ark_network.rs | 0 .../src/kercore/ark/ark_processinfo.rs | 0 .../wdriver-rust/src/kercore/ark/ark_ssdt.rs | 0 plugins/wdriver-rust/src/kercore/ark/mod.rs | 0 plugins/wdriver-rust/src/kercore/mod.rs | 0 .../wdriver-rust/src/kercore/wfp/datalink.rs | 0 plugins/wdriver-rust/src/kercore/wfp/dns.rs | 0 .../src/kercore/wfp/established.rs | 0 plugins/wdriver-rust/src/kercore/wfp/mod.rs | 0 plugins/wdriver-rust/src/kercore/wfp/tcp.rs | 0 plugins/wdriver-rust/src/lib.rs | 8 +- plugins/wdriver-rust/src/util.rs | 0 plugins/wdriver-rust/tests/ts_appcore.rs | 17 ++++ plugins/wdriver-rust/tests/ts_drvmg.rs | 2 +- plugins/wdriver-rust/tests/ts_rule.rs | 6 +- 39 files changed, 302 insertions(+), 37 deletions(-) create mode 100644 plugins/wdriver-rust/src/appcore/app_account.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_autostart.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_file.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_include.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_net.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_process.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_service_software.rs create mode 100644 plugins/wdriver-rust/src/appcore/app_sysinfo.rs create mode 100644 plugins/wdriver-rust/src/appcore/etw/etw.rs create mode 100644 plugins/wdriver-rust/src/appcore/etw/mod.rs create mode 100644 plugins/wdriver-rust/src/appcore/mod.rs rename plugins/wdriver-rust/src/{rule.rs => config/config.rs} (96%) create mode 100644 plugins/wdriver-rust/src/config/mod.rs rename plugins/wdriver-rust/src/{ => driver}/drvmg.rs (82%) create mode 100644 plugins/wdriver-rust/src/driver/mod.rs rename plugins/wdriver-rust/src/{handle.rs => events/event.rs} (76%) create mode 100644 plugins/wdriver-rust/src/events/mod.rs rename plugins/wdriver-rust/src/{mod.rs => kercore/ark/ark_deviceinfo.rs} (100%) create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_dpctimer.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_enumcallback.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_fsd.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_idt.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_mouse_keyboard.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_network.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_processinfo.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/ark_ssdt.rs create mode 100644 plugins/wdriver-rust/src/kercore/ark/mod.rs create mode 100644 plugins/wdriver-rust/src/kercore/mod.rs create mode 100644 plugins/wdriver-rust/src/kercore/wfp/datalink.rs create mode 100644 plugins/wdriver-rust/src/kercore/wfp/dns.rs create mode 100644 plugins/wdriver-rust/src/kercore/wfp/established.rs create mode 100644 plugins/wdriver-rust/src/kercore/wfp/mod.rs create mode 100644 plugins/wdriver-rust/src/kercore/wfp/tcp.rs create mode 100644 plugins/wdriver-rust/src/util.rs create mode 100644 plugins/wdriver-rust/tests/ts_appcore.rs diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index 9d7b0720..600c0e8b 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wdriver_rs" -version = "23.11.30" +version = "24.12.4" edition = "2021" [dependencies] @@ -17,11 +17,14 @@ napi = { version = "2.16.12", default-features = false, features = ["napi4", "se napi-derive = "2.16.12" tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } tokio-util = "0.7.12" +netstat = "0.7.0" +# netstat2 = "0.11.0" +sysinfo = "0.32.1" win_etw_macros = "0.1.*" win_etw_provider = "0.1.*" [dependencies.windows] -version = "0.52" +version = "0.58.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/plugins/wdriver-rust/src/appcore/app_account.rs b/plugins/wdriver-rust/src/appcore/app_account.rs new file mode 100644 index 00000000..c4d298f8 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_account.rs @@ -0,0 +1,39 @@ +use sysinfo::Users; + +use crate::{appcore::app_include::AppAccountInfo}; + +pub struct AppAccount { + pub account_info: Vec, +} + +impl AppAccount { + pub fn init() -> bool { + let mut account_info: Vec = vec![]; + let bOk = Self::get_account(&mut account_info); + if false == bOk { + return false; + } + + Self { + account_info: account_info, + }; + return true; + } + + pub fn get_account(account_info:&mut Vec) -> bool { + let users = Users::new_with_refreshed_list(); + for user in users.list() { + let account_ctx = AppAccountInfo{ + serveruser: user.name().to_string(), + servername: user.name().to_string(), + serverusid: user.id().to_string(), + serverflag: 0, + }; + account_info.push(account_ctx); + } + if account_info.is_empty() { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/app_autostart.rs b/plugins/wdriver-rust/src/appcore/app_autostart.rs new file mode 100644 index 00000000..9fb324f6 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_autostart.rs @@ -0,0 +1,5 @@ +pub struct AppAutoStart; + +impl AppAutoStart { + +} diff --git a/plugins/wdriver-rust/src/appcore/app_file.rs b/plugins/wdriver-rust/src/appcore/app_file.rs new file mode 100644 index 00000000..2784d917 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_file.rs @@ -0,0 +1,5 @@ +pub struct AppFile; + +impl AppFile { + +} diff --git a/plugins/wdriver-rust/src/appcore/app_include.rs b/plugins/wdriver-rust/src/appcore/app_include.rs new file mode 100644 index 00000000..f7c91ce5 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_include.rs @@ -0,0 +1,88 @@ +use serde::{Deserialize, Serialize}; + +// account +pub struct AppAccountInfo { + pub serveruser: String, + pub servername: String, + pub serverusid: String, + pub serverflag: u32, +} + +// auto start +pub struct AppRegRunInfo { + pub valuename: String, + pub valuekey: String, +} +pub struct AppTaskSchedulerRunInfo { + pub valuename: String, + pub state: u32, + pub lastime: u64, + pub nexttime: u64, + pub taskcommand: String, +} + +// file +pub struct AppFileExInfo { + pub filename: String, + pub filecreate: String, + pub filemodify: String, + pub fileaccess: String, + pub fileattributes:String, + pub filesize: String, + pub fileattributes_hide:String, + pub filepath: String, + pub filemd5: String +} +pub struct AppFileInfo{ + pub filesize: u32, + pub filename: String, + pub filepath: String, +} +pub struct AppDriectInfo { + pub directname: String, + pub directsize: u32, + pub filecount: u32, + pub file_array: Vec, +} + +// network +pub struct AppNetWorkInfo { + pub pid: String, + pub processname: String, + pub protocol: String, + pub localaddress: String, + pub remoteaddress: String, + pub localport: u32, + pub remoteport: u32, + pub state: String, +} + +// process +pub struct AppProcessInfo { + pub pid : u32, + pub th32parentprocessid: u32, + pub exefile: String, + pub priclassbase: String, + pub threadcount: u32, + pub processfullpath: String, +} + +// service software +pub struct AppSoftWareInfo { + pub name: String, + pub version: String, + pub data: String, + pub size: String, + pub insatllpath: String, + pub uninstallpath: String, + pub venrel: String, + pub icopath: String, +} +pub struct AppServiceInfo { + pub displayname: String, + pub servicename: String, + pub binarypath: String, + pub description: String, + pub currentstate: String, +} + diff --git a/plugins/wdriver-rust/src/appcore/app_net.rs b/plugins/wdriver-rust/src/appcore/app_net.rs new file mode 100644 index 00000000..1da19fba --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_net.rs @@ -0,0 +1,79 @@ +use netstat::*; +use sysinfo::*; +//use std::{os::ProcessInfo}; +use crate::{appcore::app_include::AppNetWorkInfo}; + +pub struct AppNetwork { + pub network_info: Vec, +} + +impl AppNetwork { + pub fn init() -> bool{ + let mut network_info: Vec = vec![]; + let bok = Self::get_socket_info(&mut network_info); + if false == bok { + return false; + } + + Self { + network_info: network_info, + }; + return true; + } + + pub fn get_socket_info(network_info:&mut Vec) -> bool{ + let af_flags: AddressFamilyFlags = AddressFamilyFlags::all(); + let proto_flags: ProtocolFlags = ProtocolFlags::all(); + let sockets_info = get_sockets_info(af_flags, proto_flags); + if sockets_info.is_err() { + return false; + } + let mut sysinfo = System::new_all(); + sysinfo.refresh_processes(ProcessesToUpdate::All, true); + + if let Ok(sockets_info) = sockets_info { + for si in sockets_info { + // let proc_info = si + // .associated_pids + // .into_iter() + // .find_map(|pid| sysinfo.process(Pid::from_u32(pid))) + // .map(|p| ProcessInfo::new(&p.name().to_string_lossy(), p.pid().as_u32())) + // .unwrap_or_default(); + + match si.protocol_socket_info { + ProtocolSocketInfo::Tcp(tcp_si) => { + let network_ctx: AppNetWorkInfo = AppNetWorkInfo { + pid: "".to_string(), + processname: "".to_string(), + protocol: "TCP".to_string(), + localaddress: tcp_si.local_addr.to_string(), + remoteaddress: tcp_si.remote_addr.to_string(), + localport: tcp_si.local_port as u32, + remoteport: tcp_si.remote_port as u32, + state: "".to_string(), + }; + network_info.push(network_ctx); + } + ProtocolSocketInfo::Udp(udp_si) => { + let network_ctx: AppNetWorkInfo = AppNetWorkInfo { + pid: "".to_string(), + processname: "".to_string(), + protocol: "UDP".to_string(), + localaddress: udp_si.local_addr.to_string(), + remoteaddress: "".to_string(), + localport: udp_si.local_port as u32, + remoteport: 0, + state: "".to_string(), + }; + network_info.push(network_ctx); + } + } + } + } + + if network_info.is_empty() { + return false; + } + return true; + } +} diff --git a/plugins/wdriver-rust/src/appcore/app_process.rs b/plugins/wdriver-rust/src/appcore/app_process.rs new file mode 100644 index 00000000..d2dd5188 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_process.rs @@ -0,0 +1,5 @@ +pub struct AppProcess; + +impl AppProcess { + +} diff --git a/plugins/wdriver-rust/src/appcore/app_service_software.rs b/plugins/wdriver-rust/src/appcore/app_service_software.rs new file mode 100644 index 00000000..ee3833d8 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_service_software.rs @@ -0,0 +1,5 @@ +pub struct AppServiceSoftWare; + +impl AppServiceSoftWare { + +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/app_sysinfo.rs b/plugins/wdriver-rust/src/appcore/app_sysinfo.rs new file mode 100644 index 00000000..26836f89 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/app_sysinfo.rs @@ -0,0 +1,5 @@ +pub struct AppSysInfo; + +impl AppSysInfo { + +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/etw/etw.rs b/plugins/wdriver-rust/src/appcore/etw/etw.rs new file mode 100644 index 00000000..b5622f5b --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/etw/etw.rs @@ -0,0 +1,6 @@ +pub struct Etw; + +impl Etw { + + +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/etw/mod.rs b/plugins/wdriver-rust/src/appcore/etw/mod.rs new file mode 100644 index 00000000..b12bd4ba --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/etw/mod.rs @@ -0,0 +1 @@ +pub mod etw; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/mod.rs b/plugins/wdriver-rust/src/appcore/mod.rs new file mode 100644 index 00000000..97dbc239 --- /dev/null +++ b/plugins/wdriver-rust/src/appcore/mod.rs @@ -0,0 +1,8 @@ +pub mod app_account; +pub mod app_autostart; +pub mod app_file; +pub mod app_net; +pub mod app_process; +pub mod app_service_software; +pub mod app_sysinfo; +pub mod app_include; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/rule.rs b/plugins/wdriver-rust/src/config/config.rs similarity index 96% rename from plugins/wdriver-rust/src/rule.rs rename to plugins/wdriver-rust/src/config/config.rs index bc09c926..a6e19922 100644 --- a/plugins/wdriver-rust/src/rule.rs +++ b/plugins/wdriver-rust/src/config/config.rs @@ -1,8 +1,6 @@ extern crate yaml_rust; -use crate::{log::init_log}; - -use napi::threadsafe_function::ThreadsafeFunction; use napi_derive::napi; +use napi::threadsafe_function::ThreadsafeFunction; use serde::{Deserialize, Serialize}; use std::{ fs, @@ -16,6 +14,8 @@ use std::{ }; use yaml_rust::{YamlEmitter, YamlLoader}; +use crate::{log::init_log}; + pub struct RuleImpl { // dns rule_dns: Vec, @@ -154,13 +154,13 @@ impl RuleImpl { } Self { - rule_dns, - rule_redirect, - rule_transport, - rule_directory, - rule_process, - rule_thread, - rule_register, + rule_dns: rule_dns, + rule_redirect: rule_redirect, + rule_transport: rule_transport, + rule_directory: rule_directory, + rule_process: rule_process, + rule_thread: rule_thread, + rule_register: rule_register, }; return true; @@ -180,7 +180,7 @@ impl RuleImpl { } // Analyze dns rule - pub fn get_dns_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { + pub fn get_dns_rule(file_path: String, _data: &mut String, rule_dns: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -217,7 +217,7 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn get_redirect_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { + pub fn get_redirect_rule(file_path: String, _data: &mut String, rule_redirect: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -418,9 +418,3 @@ pub struct RuleResgiter { pub register_permiss: String, } -// Unit test -#[cfg(test)] -mod test { - - -} diff --git a/plugins/wdriver-rust/src/config/mod.rs b/plugins/wdriver-rust/src/config/mod.rs new file mode 100644 index 00000000..a1059337 --- /dev/null +++ b/plugins/wdriver-rust/src/config/mod.rs @@ -0,0 +1 @@ +pub mod config; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/drvmg.rs b/plugins/wdriver-rust/src/driver/drvmg.rs similarity index 82% rename from plugins/wdriver-rust/src/drvmg.rs rename to plugins/wdriver-rust/src/driver/drvmg.rs index 92674214..47883684 100644 --- a/plugins/wdriver-rust/src/drvmg.rs +++ b/plugins/wdriver-rust/src/driver/drvmg.rs @@ -1,4 +1,4 @@ -use std::{fs, io::Read, path::PathBuf, ptr::null, result}; +use std::{fs, io::Read, path::PathBuf, ptr::{null, null_mut}, result}; use windows::{ core::*, Win32::Foundation::*, @@ -13,11 +13,12 @@ pub struct DrivenManageImpl { impl DrivenManageImpl { pub fn new() -> Self { - Self { handle: HANDLE(0) } + Self { handle: HANDLE(null_mut()) } } // Chekcout Driver Status pub fn get_driver_stu(driver_name: String) -> bool { + return true; } @@ -39,8 +40,8 @@ impl DrivenManageImpl { self.handle = driver_handle; return true; } else { - let err: std::prelude::v1::Result<(), Error> = GetLastError(); - println!("{}", err.unwrap_err().code()); + let err = GetLastError(); + println!("{}", err.to_hresult().0); return false; } } @@ -62,9 +63,9 @@ impl DrivenManageImpl { return; } unsafe { - CloseHandle(self.handle); + let _ = CloseHandle(self.handle); } - self.handle = HANDLE(0); + self.handle = HANDLE(null_mut()); } - + } diff --git a/plugins/wdriver-rust/src/driver/mod.rs b/plugins/wdriver-rust/src/driver/mod.rs new file mode 100644 index 00000000..1202c0fb --- /dev/null +++ b/plugins/wdriver-rust/src/driver/mod.rs @@ -0,0 +1 @@ +pub mod drvmg; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/handle.rs b/plugins/wdriver-rust/src/events/event.rs similarity index 76% rename from plugins/wdriver-rust/src/handle.rs rename to plugins/wdriver-rust/src/events/event.rs index a30b6334..13c384f1 100644 --- a/plugins/wdriver-rust/src/handle.rs +++ b/plugins/wdriver-rust/src/events/event.rs @@ -1,7 +1,6 @@ -// network packet filter handle -pub struct HandleMSG; +pub struct Event; -impl HandleMSG { +impl Event { // Handle Message diff --git a/plugins/wdriver-rust/src/events/mod.rs b/plugins/wdriver-rust/src/events/mod.rs new file mode 100644 index 00000000..c47b0f48 --- /dev/null +++ b/plugins/wdriver-rust/src/events/mod.rs @@ -0,0 +1 @@ +pub mod event; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/mod.rs b/plugins/wdriver-rust/src/kercore/ark/ark_deviceinfo.rs similarity index 100% rename from plugins/wdriver-rust/src/mod.rs rename to plugins/wdriver-rust/src/kercore/ark/ark_deviceinfo.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_dpctimer.rs b/plugins/wdriver-rust/src/kercore/ark/ark_dpctimer.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_enumcallback.rs b/plugins/wdriver-rust/src/kercore/ark/ark_enumcallback.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_fsd.rs b/plugins/wdriver-rust/src/kercore/ark/ark_fsd.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_idt.rs b/plugins/wdriver-rust/src/kercore/ark/ark_idt.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_mouse_keyboard.rs b/plugins/wdriver-rust/src/kercore/ark/ark_mouse_keyboard.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_network.rs b/plugins/wdriver-rust/src/kercore/ark/ark_network.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_processinfo.rs b/plugins/wdriver-rust/src/kercore/ark/ark_processinfo.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_ssdt.rs b/plugins/wdriver-rust/src/kercore/ark/ark_ssdt.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/ark/mod.rs b/plugins/wdriver-rust/src/kercore/ark/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/mod.rs b/plugins/wdriver-rust/src/kercore/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/wfp/datalink.rs b/plugins/wdriver-rust/src/kercore/wfp/datalink.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/wfp/dns.rs b/plugins/wdriver-rust/src/kercore/wfp/dns.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/wfp/established.rs b/plugins/wdriver-rust/src/kercore/wfp/established.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/wfp/mod.rs b/plugins/wdriver-rust/src/kercore/wfp/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/kercore/wfp/tcp.rs b/plugins/wdriver-rust/src/kercore/wfp/tcp.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs index 4e69bc34..3864a765 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver-rust/src/lib.rs @@ -1,4 +1,6 @@ -// @exterm lib mod -pub mod drvmg; -pub mod rule; +pub mod config; +pub mod events; +pub mod driver; +pub mod appcore; +pub mod kercore; pub mod log; diff --git a/plugins/wdriver-rust/src/util.rs b/plugins/wdriver-rust/src/util.rs new file mode 100644 index 00000000..e69de29b diff --git a/plugins/wdriver-rust/tests/ts_appcore.rs b/plugins/wdriver-rust/tests/ts_appcore.rs new file mode 100644 index 00000000..0369916c --- /dev/null +++ b/plugins/wdriver-rust/tests/ts_appcore.rs @@ -0,0 +1,17 @@ +use wdriver_rs::appcore::app_account; +use wdriver_rs::appcore::app_net; + +#[test] +// pub fn unit_test_account() { +// let b = app_account::AppAccount::init(); +// if b { +// println!("account init success."); +// } +// } + +pub fn unit_test_network() { + let b = app_net::AppNetwork::init(); + if b { + println!("account init success."); + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/tests/ts_drvmg.rs b/plugins/wdriver-rust/tests/ts_drvmg.rs index 4241312f..f8cdea19 100644 --- a/plugins/wdriver-rust/tests/ts_drvmg.rs +++ b/plugins/wdriver-rust/tests/ts_drvmg.rs @@ -1,4 +1,4 @@ -use wdriver_rs::drvmg::DrivenManageImpl; +use wdriver_rs::driver::drvmg::*; #[test] // 驱动启动状态 diff --git a/plugins/wdriver-rust/tests/ts_rule.rs b/plugins/wdriver-rust/tests/ts_rule.rs index 24c1d478..437b66db 100644 --- a/plugins/wdriver-rust/tests/ts_rule.rs +++ b/plugins/wdriver-rust/tests/ts_rule.rs @@ -1,11 +1,11 @@ use fast_log::{ - consts::LogSize, - plugin::{file_split::RollingType, packer::LogPacker}, + config, consts::LogSize, plugin::{file_split::RollingType, packer::LogPacker} }; -use wdriver_rs::rule::RuleImpl; use tokio::{io::AsyncWriteExt, sync::{mpsc::Sender, RwLock}, task::JoinHandle, runtime::Runtime}; use std::{thread::{self, current}, time}; +use wdriver_rs::config::config::*; + #[test] pub fn unit_get_dns_rule() { let runtime = tokio::runtime::Runtime::new().ok().unwrap(); From a4edb697df36b3448431fd9e9be038867fc9020d Mon Sep 17 00:00:00 2001 From: czy Date: Thu, 5 Dec 2024 11:37:04 +0800 Subject: [PATCH 07/38] appcore process --- plugins/wdriver-rust/Cargo.toml | 4 +- plugins/wdriver-rust/README.md | 18 ++++--- .../wdriver-rust/src/appcore/app_account.rs | 4 +- .../wdriver-rust/src/appcore/app_include.rs | 1 + plugins/wdriver-rust/src/appcore/app_net.rs | 38 ++++++++----- .../wdriver-rust/src/appcore/app_process.rs | 54 ++++++++++++++++++- plugins/wdriver-rust/tests/ts_appcore.rs | 24 ++++++--- 7 files changed, 109 insertions(+), 34 deletions(-) diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index 600c0e8b..26b7f6f2 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -17,8 +17,8 @@ napi = { version = "2.16.12", default-features = false, features = ["napi4", "se napi-derive = "2.16.12" tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } tokio-util = "0.7.12" -netstat = "0.7.0" -# netstat2 = "0.11.0" +#netstat = "0.7.0" +netstat2 = "0.11.0" sysinfo = "0.32.1" win_etw_macros = "0.1.*" win_etw_provider = "0.1.*" diff --git a/plugins/wdriver-rust/README.md b/plugins/wdriver-rust/README.md index 99cb2a33..d94dd6fb 100644 --- a/plugins/wdriver-rust/README.md +++ b/plugins/wdriver-rust/README.md @@ -1,6 +1,10 @@ # Hades Windows Driver -wdriver-rsut优先提供Windows插件模块管理,管理组件核心dll +wdriver-rsut等同于HadesSvc +``` +HadesSvc - 插件服务 +https://github.com/theSecHunter/Hades-Windows/tree/main/HadSvc +``` ``` NetDrvlib64.dll - 内核网络 https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib @@ -21,7 +25,7 @@ SysMonUserlib64.dll - 用户态探针 https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmonuserlib ``` -依赖于Driver驱动能力 +依赖Driver驱动 ``` SysMonDrvlib64 -> sysmondriver.sys - 内核驱动 https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrv @@ -32,22 +36,22 @@ NetDrvlib64 -> hadesndr.sys - 网络驱动 https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib ``` -wdriver-rsut目标替换c++组件库,整合NetDrvlib64,RuleEnginelib64,SysMonDrvlib64,SysMonUserlib64动态库能力,重构插件。 +wdriver-rsut目标重构c++组件库HadesSvc,整合NetDrvlib64,RuleEnginelib64,SysMonDrvlib64,SysMonUserlib64动态库能力。 ``` -rule.rs -> RuleEnginelib64.dll - 规则引擎 +config/config.rs -> RuleEnginelib64.dll - 规则引擎 ``` ``` -knetfilter.rs -> NetDrvlib64.dll - 内核网络 +kercore/wfp -> NetDrvlib64.dll - 内核网络 ``` ``` -kmonfilter.rs -> SysMonDrvlib64.dll - 内核探针 +kercore/ark -> SysMonDrvlib64.dll - 内核探针 ``` ``` -umonfilter.rs -> SysMonUserlib64.dll - 用户态探针 +appcore -> SysMonUserlib64.dll - 用户态探针 ``` driver sys不变 diff --git a/plugins/wdriver-rust/src/appcore/app_account.rs b/plugins/wdriver-rust/src/appcore/app_account.rs index c4d298f8..cacc8d70 100644 --- a/plugins/wdriver-rust/src/appcore/app_account.rs +++ b/plugins/wdriver-rust/src/appcore/app_account.rs @@ -9,7 +9,7 @@ pub struct AppAccount { impl AppAccount { pub fn init() -> bool { let mut account_info: Vec = vec![]; - let bOk = Self::get_account(&mut account_info); + let bOk = Self::get_account_info(&mut account_info); if false == bOk { return false; } @@ -20,7 +20,7 @@ impl AppAccount { return true; } - pub fn get_account(account_info:&mut Vec) -> bool { + pub fn get_account_info(account_info:&mut Vec) -> bool { let users = Users::new_with_refreshed_list(); for user in users.list() { let account_ctx = AppAccountInfo{ diff --git a/plugins/wdriver-rust/src/appcore/app_include.rs b/plugins/wdriver-rust/src/appcore/app_include.rs index f7c91ce5..d8b4bdc6 100644 --- a/plugins/wdriver-rust/src/appcore/app_include.rs +++ b/plugins/wdriver-rust/src/appcore/app_include.rs @@ -49,6 +49,7 @@ pub struct AppDriectInfo { pub struct AppNetWorkInfo { pub pid: String, pub processname: String, + pub cmd: String, pub protocol: String, pub localaddress: String, pub remoteaddress: String, diff --git a/plugins/wdriver-rust/src/appcore/app_net.rs b/plugins/wdriver-rust/src/appcore/app_net.rs index 1da19fba..fd5d92b5 100644 --- a/plugins/wdriver-rust/src/appcore/app_net.rs +++ b/plugins/wdriver-rust/src/appcore/app_net.rs @@ -1,6 +1,7 @@ -use netstat::*; +use std::ops::Deref; + +use netstat2::*; use sysinfo::*; -//use std::{os::ProcessInfo}; use crate::{appcore::app_include::AppNetWorkInfo}; pub struct AppNetwork { @@ -33,31 +34,40 @@ impl AppNetwork { if let Ok(sockets_info) = sockets_info { for si in sockets_info { - // let proc_info = si - // .associated_pids - // .into_iter() - // .find_map(|pid| sysinfo.process(Pid::from_u32(pid))) - // .map(|p| ProcessInfo::new(&p.name().to_string_lossy(), p.pid().as_u32())) - // .unwrap_or_default(); - + let proc_info = si + .associated_pids + .into_iter() + .find_map(|pid| sysinfo.process(Pid::from_u32(pid))).unwrap(); + // .map(|p| ProcessInfo::new(&p.name().to_string_lossy(), p.pid().as_u32())) + // .unwrap_or_default(); + let mut cmdline = "".to_string(); + for s in proc_info.cmd() { + if s.is_empty() { + continue; + } + cmdline.push_str(s.clone().into_string().unwrap().as_str()); + cmdline.push_str("|"); + } match si.protocol_socket_info { ProtocolSocketInfo::Tcp(tcp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: "".to_string(), - processname: "".to_string(), + pid: proc_info.pid().to_string(), + processname: proc_info.name().to_os_string().into_string().unwrap(), + cmd: cmdline, protocol: "TCP".to_string(), localaddress: tcp_si.local_addr.to_string(), remoteaddress: tcp_si.remote_addr.to_string(), localport: tcp_si.local_port as u32, remoteport: tcp_si.remote_port as u32, - state: "".to_string(), + state: tcp_si.state.to_string(), }; network_info.push(network_ctx); } ProtocolSocketInfo::Udp(udp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: "".to_string(), - processname: "".to_string(), + pid: proc_info.pid().to_string(), + processname: proc_info.name().to_os_string().into_string().unwrap(), + cmd: "".to_string(), protocol: "UDP".to_string(), localaddress: udp_si.local_addr.to_string(), remoteaddress: "".to_string(), diff --git a/plugins/wdriver-rust/src/appcore/app_process.rs b/plugins/wdriver-rust/src/appcore/app_process.rs index d2dd5188..85fe0acb 100644 --- a/plugins/wdriver-rust/src/appcore/app_process.rs +++ b/plugins/wdriver-rust/src/appcore/app_process.rs @@ -1,5 +1,55 @@ -pub struct AppProcess; +use sysinfo::*; + +use crate::{appcore::app_include::AppProcessInfo}; + +pub struct AppProcess { + pub process_info: Vec, +} impl AppProcess { - + pub fn init() -> bool { + let mut process_info: Vec = vec![]; + let bOk = Self::get_process_info(&mut process_info); + if false == bOk { + return false; + } + + Self { + process_info: process_info, + }; + return true; + } + + pub fn get_process_info(process_info:&mut Vec) -> bool { + let mut sysinfo = System::new_all(); + sysinfo.refresh_processes(ProcessesToUpdate::All, true); + + for (pid, process) in sysinfo.processes() { + let mut cmdline = "".to_string(); + for s in process.cmd() { + if s.is_empty() { + continue; + } + cmdline.push_str(s.clone().into_string().unwrap().as_str()); + cmdline.push_str("|"); + } + let process_ctx = AppProcessInfo{ + pid: pid.as_u32(), + th32parentprocessid: 0, + exefile: process.name().to_os_string().into_string().unwrap(), + priclassbase: process.status().to_string(), + threadcount: match process.parent() { + Some(_) => { process.parent().unwrap().as_u32() }, + None => { 0 }, + }, + processfullpath: cmdline, + }; + process_info.push(process_ctx); + } + + if process_info.is_empty() { + return false; + } + return true; + } } diff --git a/plugins/wdriver-rust/tests/ts_appcore.rs b/plugins/wdriver-rust/tests/ts_appcore.rs index 0369916c..2ebd4101 100644 --- a/plugins/wdriver-rust/tests/ts_appcore.rs +++ b/plugins/wdriver-rust/tests/ts_appcore.rs @@ -1,17 +1,27 @@ use wdriver_rs::appcore::app_account; use wdriver_rs::appcore::app_net; +use wdriver_rs::appcore::app_process; #[test] -// pub fn unit_test_account() { -// let b = app_account::AppAccount::init(); -// if b { -// println!("account init success."); -// } -// } +pub fn unit_test_account() { + let b = app_account::AppAccount::init(); + if b { + println!("account init success."); + } +} +#[test] pub fn unit_test_network() { let b = app_net::AppNetwork::init(); if b { - println!("account init success."); + println!("network init success."); + } +} + +#[test] +pub fn unit_test_processinfo() { + let b = app_process::AppProcess::init(); + if b { + println!("process init success."); } } \ No newline at end of file From 8c4e65a2eb85183bb7bc96aa97c1e97541edc56b Mon Sep 17 00:00:00 2001 From: czy Date: Thu, 5 Dec 2024 16:01:51 +0800 Subject: [PATCH 08/38] add appcore software & fix appcore processInfo. --- plugins/wdriver-rust/Cargo.toml | 3 +- .../wdriver-rust/src/appcore/app_account.rs | 1 + .../wdriver-rust/src/appcore/app_include.rs | 5 +- plugins/wdriver-rust/src/appcore/app_net.rs | 13 +- .../wdriver-rust/src/appcore/app_process.rs | 9 +- .../src/appcore/app_service_software.rs | 60 ++++++++- plugins/wdriver-rust/src/config/config.rs | 2 +- plugins/wdriver-rust/src/lib.rs | 2 +- plugins/wdriver-rust/src/util/installed.rs | 120 ++++++++++++++++++ plugins/wdriver-rust/src/{ => util}/log.rs | 0 plugins/wdriver-rust/src/util/mod.rs | 3 + plugins/wdriver-rust/src/{ => util}/util.rs | 0 plugins/wdriver-rust/tests/ts_appcore.rs | 20 +++ 13 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 plugins/wdriver-rust/src/util/installed.rs rename plugins/wdriver-rust/src/{ => util}/log.rs (100%) create mode 100644 plugins/wdriver-rust/src/util/mod.rs rename plugins/wdriver-rust/src/{ => util}/util.rs (100%) diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index 26b7f6f2..fd1ced86 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -19,7 +19,8 @@ tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", tokio-util = "0.7.12" #netstat = "0.7.0" netstat2 = "0.11.0" -sysinfo = "0.32.1" +sysinfo = "0.33.0" +winreg = "0.52.0" win_etw_macros = "0.1.*" win_etw_provider = "0.1.*" diff --git a/plugins/wdriver-rust/src/appcore/app_account.rs b/plugins/wdriver-rust/src/appcore/app_account.rs index cacc8d70..f24fba61 100644 --- a/plugins/wdriver-rust/src/appcore/app_account.rs +++ b/plugins/wdriver-rust/src/appcore/app_account.rs @@ -36,4 +36,5 @@ impl AppAccount { } return true; } + } \ No newline at end of file diff --git a/plugins/wdriver-rust/src/appcore/app_include.rs b/plugins/wdriver-rust/src/appcore/app_include.rs index d8b4bdc6..031b23ff 100644 --- a/plugins/wdriver-rust/src/appcore/app_include.rs +++ b/plugins/wdriver-rust/src/appcore/app_include.rs @@ -47,7 +47,8 @@ pub struct AppDriectInfo { // network pub struct AppNetWorkInfo { - pub pid: String, + pub pid: u32, + pub th32parentprocessid: u32, pub processname: String, pub cmd: String, pub protocol: String, @@ -72,7 +73,7 @@ pub struct AppProcessInfo { pub struct AppSoftWareInfo { pub name: String, pub version: String, - pub data: String, + pub helplink: String, pub size: String, pub insatllpath: String, pub uninstallpath: String, diff --git a/plugins/wdriver-rust/src/appcore/app_net.rs b/plugins/wdriver-rust/src/appcore/app_net.rs index fd5d92b5..55ad7687 100644 --- a/plugins/wdriver-rust/src/appcore/app_net.rs +++ b/plugins/wdriver-rust/src/appcore/app_net.rs @@ -51,7 +51,11 @@ impl AppNetwork { match si.protocol_socket_info { ProtocolSocketInfo::Tcp(tcp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: proc_info.pid().to_string(), + pid: proc_info.pid().as_u32(), + th32parentprocessid: match proc_info.parent() { + Some(_) => { proc_info.parent().unwrap().as_u32() }, + None => { 0 }, + }, processname: proc_info.name().to_os_string().into_string().unwrap(), cmd: cmdline, protocol: "TCP".to_string(), @@ -65,7 +69,11 @@ impl AppNetwork { } ProtocolSocketInfo::Udp(udp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: proc_info.pid().to_string(), + pid: proc_info.pid().as_u32(), + th32parentprocessid: match proc_info.parent() { + Some(_) => { proc_info.parent().unwrap().as_u32() }, + None => { 0 }, + }, processname: proc_info.name().to_os_string().into_string().unwrap(), cmd: "".to_string(), protocol: "UDP".to_string(), @@ -86,4 +94,5 @@ impl AppNetwork { } return true; } + } diff --git a/plugins/wdriver-rust/src/appcore/app_process.rs b/plugins/wdriver-rust/src/appcore/app_process.rs index 85fe0acb..023951b5 100644 --- a/plugins/wdriver-rust/src/appcore/app_process.rs +++ b/plugins/wdriver-rust/src/appcore/app_process.rs @@ -35,13 +35,13 @@ impl AppProcess { } let process_ctx = AppProcessInfo{ pid: pid.as_u32(), - th32parentprocessid: 0, - exefile: process.name().to_os_string().into_string().unwrap(), - priclassbase: process.status().to_string(), - threadcount: match process.parent() { + th32parentprocessid: match process.parent() { Some(_) => { process.parent().unwrap().as_u32() }, None => { 0 }, }, + exefile: process.name().to_os_string().into_string().unwrap(), + priclassbase: process.status().to_string(), + threadcount: 0, processfullpath: cmdline, }; process_info.push(process_ctx); @@ -52,4 +52,5 @@ impl AppProcess { } return true; } + } diff --git a/plugins/wdriver-rust/src/appcore/app_service_software.rs b/plugins/wdriver-rust/src/appcore/app_service_software.rs index ee3833d8..acaa4eb0 100644 --- a/plugins/wdriver-rust/src/appcore/app_service_software.rs +++ b/plugins/wdriver-rust/src/appcore/app_service_software.rs @@ -1,5 +1,63 @@ -pub struct AppServiceSoftWare; +use std::io::Empty; + +use crate::{util::installed::App, appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo}; + +pub struct AppServiceSoftWare { + services_info: Vec, + software_info: Vec, +} impl AppServiceSoftWare { + pub fn init() -> bool { + let mut services_info: Vec = vec![]; + let mut software_info: Vec = vec![]; + + Self::get_services_info(&mut services_info); + Self::get_software_info(&mut software_info); + + Self { + services_info: services_info, + software_info: software_info, + }; + return true; + } + + pub fn get_services_info(services_info:&mut Vec) -> bool { + if services_info.is_empty() { + return false; + } + return true; + } + + pub fn get_software_info(software_info:&mut Vec) -> bool { + // read uninstall register valuse + let apps = App::list().unwrap(); + for app in apps { + let mut installpath = "".to_string(); + if app.install_path().is_empty() { + installpath = app.installlocal_path().into_owned(); + } + else { + installpath = app.install_path().into_owned(); + } + let software_ctx = AppSoftWareInfo { + name:app.name().into_owned(), + version:app.version().into_owned(), + helplink: app.helplink().into_owned(), + size:app.size().into_owned(), + insatllpath: installpath, + uninstallpath: app.uninstall_path().into_owned(), + venrel: app.publisher().into_owned(), + icopath: app.icon().into_owned(), + }; + + software_info.push(software_ctx); + } + + if software_info.is_empty() { + return false; + } + return true; + } } \ No newline at end of file diff --git a/plugins/wdriver-rust/src/config/config.rs b/plugins/wdriver-rust/src/config/config.rs index a6e19922..e0611564 100644 --- a/plugins/wdriver-rust/src/config/config.rs +++ b/plugins/wdriver-rust/src/config/config.rs @@ -14,7 +14,7 @@ use std::{ }; use yaml_rust::{YamlEmitter, YamlLoader}; -use crate::{log::init_log}; +use crate::{util::log::init_log}; pub struct RuleImpl { // dns diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver-rust/src/lib.rs index 3864a765..a74e6522 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver-rust/src/lib.rs @@ -3,4 +3,4 @@ pub mod events; pub mod driver; pub mod appcore; pub mod kercore; -pub mod log; +pub mod util; diff --git a/plugins/wdriver-rust/src/util/installed.rs b/plugins/wdriver-rust/src/util/installed.rs new file mode 100644 index 00000000..065a7fa6 --- /dev/null +++ b/plugins/wdriver-rust/src/util/installed.rs @@ -0,0 +1,120 @@ +use std::borrow::Cow; +use std::error::Error; +use winreg::enums::*; +use winreg::reg_key::RegKey; +use winreg::HKEY; + +thread_local! { + static UNINSTALLS: Option = None; +} + +pub struct App { + reg: RegKey, +} +struct AppList { + uninstalls: RegKey, + index: usize, +} +impl Iterator for AppList { + type Item = App; + fn next(&mut self) -> Option { + let key = self.uninstalls.enum_keys().nth(self.index)?.ok()?; + self.index += 1; + let reg = self.uninstalls.open_subkey(key).ok()?; + Some(App { reg }) + } +} +impl AppList { + fn new(hive: HKEY, path: &str) -> Result> { + let hive = RegKey::predef(hive); + let uninstalls = hive.open_subkey(path)?; + + Ok(AppList { + uninstalls, + index: 0, + }) + } +} +impl App { + fn get_value(&self, name: &str) -> Cow { + self.reg + .get_value::(name) + .map(Cow::Owned) + .unwrap_or_else(|_| Cow::Borrowed("")) + } + pub fn name(&self) -> Cow { + self.get_value("DisplayName") + } + pub fn icon(&self) -> Cow { + self.get_value("DisplayIcon") + } + pub fn publisher(&self) -> Cow { + self.get_value("Publisher") + } + pub fn version(&self) -> Cow { + self.get_value("DisplayVersion") + } + pub fn size(&self) -> Cow { + self.get_value("Size") + } + pub fn helplink(&self) -> Cow { + self.get_value("HelpLink") + } + pub fn install_path(&self) -> Cow { + self.get_value("InstallLocation") + } + pub fn installlocal_path(&self) -> Cow { + self.get_value("InstallLocation") + } + pub fn uninstall_path(&self) -> Cow { + self.get_value("UninstallString") + } + pub fn dump(&self) -> Cow { + self.reg + .enum_values() + .map(|r| { + let (name, value) = r.unwrap(); + format!("{}: {}\n", name, value) + }) + .collect::() + .into() + } + pub fn list() -> Result, Box> { + let system_apps = AppList::new( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + ) + .ok() + .into_iter() + .flatten(); + let system_apps_32 = AppList::new( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + ) + .ok() + .into_iter() + .flatten(); + let user_apps = AppList::new( + HKEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + ) + .ok() + .into_iter() + .flatten(); + // this one may not exist + let user_apps_32 = AppList::new( + HKEY_CURRENT_USER, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + ) + .ok() + .into_iter() + .flatten(); + + let chain = system_apps + .chain(system_apps_32) + .chain(user_apps) + .chain(user_apps_32); + + Ok(chain) + } +} diff --git a/plugins/wdriver-rust/src/log.rs b/plugins/wdriver-rust/src/util/log.rs similarity index 100% rename from plugins/wdriver-rust/src/log.rs rename to plugins/wdriver-rust/src/util/log.rs diff --git a/plugins/wdriver-rust/src/util/mod.rs b/plugins/wdriver-rust/src/util/mod.rs new file mode 100644 index 00000000..cf288bc3 --- /dev/null +++ b/plugins/wdriver-rust/src/util/mod.rs @@ -0,0 +1,3 @@ +pub mod installed; +pub mod log; +pub mod util; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/util.rs b/plugins/wdriver-rust/src/util/util.rs similarity index 100% rename from plugins/wdriver-rust/src/util.rs rename to plugins/wdriver-rust/src/util/util.rs diff --git a/plugins/wdriver-rust/tests/ts_appcore.rs b/plugins/wdriver-rust/tests/ts_appcore.rs index 2ebd4101..e9da961b 100644 --- a/plugins/wdriver-rust/tests/ts_appcore.rs +++ b/plugins/wdriver-rust/tests/ts_appcore.rs @@ -1,6 +1,8 @@ +use wdriver_rs::appcore::app_include; use wdriver_rs::appcore::app_account; use wdriver_rs::appcore::app_net; use wdriver_rs::appcore::app_process; +use wdriver_rs::appcore::app_service_software; #[test] pub fn unit_test_account() { @@ -24,4 +26,22 @@ pub fn unit_test_processinfo() { if b { println!("process init success."); } +} + +#[test] +pub fn unit_test_servicesinfo() { + let mut serivce_info: Vec = vec![]; + let b = app_service_software::AppServiceSoftWare::get_services_info(&mut serivce_info); + if b { + println!("service init success."); + } +} + +#[test] +pub fn unit_test_softwareinfo() { + let mut software_info: Vec = vec![]; + let b = app_service_software::AppServiceSoftWare::get_software_info(&mut software_info); + if b { + println!("software init success."); + } } \ No newline at end of file From 4d38b4b642e8799de8d12d50614eccccfa708d0a Mon Sep 17 00:00:00 2001 From: czy Date: Fri, 6 Dec 2024 14:54:01 +0800 Subject: [PATCH 09/38] appcore autostart & services --- plugins/wdriver-rust/Cargo.toml | 14 ++- .../wdriver-rust/src/appcore/app_autostart.rs | 46 +++++++- .../src/appcore/app_service_software.rs | 7 +- .../wdriver-rust/src/appcore/app_sysinfo.rs | 47 +++++++- plugins/wdriver-rust/src/driver/drvmg.rs | 4 +- plugins/wdriver-rust/src/util/mod.rs | 6 +- .../{installed.rs => windows_installed.rs} | 0 .../wdriver-rust/src/util/windows_services.rs | 106 +++++++++++++++++ .../src/util/windwos_autostart.rs | 107 ++++++++++++++++++ plugins/wdriver-rust/tests/ts_appcore.rs | 14 ++- plugins/wdriver-rust/tests/ts_rule.rs | 18 ++- 11 files changed, 348 insertions(+), 21 deletions(-) rename plugins/wdriver-rust/src/util/{installed.rs => windows_installed.rs} (100%) create mode 100644 plugins/wdriver-rust/src/util/windows_services.rs create mode 100644 plugins/wdriver-rust/src/util/windwos_autostart.rs diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver-rust/Cargo.toml index fd1ced86..ebbda545 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver-rust/Cargo.toml @@ -17,21 +17,23 @@ napi = { version = "2.16.12", default-features = false, features = ["napi4", "se napi-derive = "2.16.12" tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } tokio-util = "0.7.12" -#netstat = "0.7.0" -netstat2 = "0.11.0" -sysinfo = "0.33.0" + winreg = "0.52.0" +sysinfo = "0.33.0" +netstat2 = "0.11.0" +windows-service = "0.7.0" win_etw_macros = "0.1.*" win_etw_provider = "0.1.*" [dependencies.windows] version = "0.58.0" features = [ - "Win32_Foundation", "Win32_Security", - "Win32_Storage_FileSystem", - "Win32_System_Threading", "Win32_System_IO", + "Win32_Foundation", + "Win32_System_Services", + "Win32_System_Threading", + "Win32_Storage_FileSystem", ] [profile.test] diff --git a/plugins/wdriver-rust/src/appcore/app_autostart.rs b/plugins/wdriver-rust/src/appcore/app_autostart.rs index 9fb324f6..e6ab2dd5 100644 --- a/plugins/wdriver-rust/src/appcore/app_autostart.rs +++ b/plugins/wdriver-rust/src/appcore/app_autostart.rs @@ -1,5 +1,47 @@ -pub struct AppAutoStart; + +use crate::{util::windwos_autostart::App, appcore::app_include::AppRegRunInfo, appcore::app_include::AppTaskSchedulerRunInfo}; + +pub struct AppAutoStart { + astart_register: Vec, + astart_tasksched: Vec, +} impl AppAutoStart { - + + pub fn init() -> bool { + let mut astart_register: Vec = vec![]; + let mut astart_tasksched: Vec = vec![]; + + let _ = Self::get_astart_register(&mut astart_register); + let _ = Self::get_astart_taskschedu(&mut astart_tasksched); + + if astart_register.is_empty() && astart_tasksched.is_empty() { + return false; + } + + Self { + astart_register: astart_register, + astart_tasksched: astart_tasksched, + }; + return true; + } + + pub fn get_astart_register(astart_register:&mut Vec) -> bool { + let apps = App::list().unwrap(); + for app in apps { + } + if astart_register.is_empty() { + return false; + } + return true; + } + + pub fn get_astart_taskschedu(astart_tasksched:&mut Vec) -> bool { + + if astart_tasksched.is_empty() { + return false; + } + return true; + } + } diff --git a/plugins/wdriver-rust/src/appcore/app_service_software.rs b/plugins/wdriver-rust/src/appcore/app_service_software.rs index acaa4eb0..5df80b86 100644 --- a/plugins/wdriver-rust/src/appcore/app_service_software.rs +++ b/plugins/wdriver-rust/src/appcore/app_service_software.rs @@ -1,6 +1,7 @@ -use std::io::Empty; +use std::ffi::OsString; +use windows_service::{Result, service_dispatcher, service}; -use crate::{util::installed::App, appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo}; +use crate::{util::windows_installed::App, appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo}; pub struct AppServiceSoftWare { services_info: Vec, @@ -23,6 +24,8 @@ impl AppServiceSoftWare { } pub fn get_services_info(services_info:&mut Vec) -> bool { + + if services_info.is_empty() { return false; } diff --git a/plugins/wdriver-rust/src/appcore/app_sysinfo.rs b/plugins/wdriver-rust/src/appcore/app_sysinfo.rs index 26836f89..e6b27459 100644 --- a/plugins/wdriver-rust/src/appcore/app_sysinfo.rs +++ b/plugins/wdriver-rust/src/appcore/app_sysinfo.rs @@ -1,5 +1,50 @@ -pub struct AppSysInfo; +pub struct AppSysInfo { + cpu: String, + name: String, + os_version: String, + display_card: Vec, + camera: Vec, + bluetooth: Vec, + voice: Vec, + microphone: Vec +} impl AppSysInfo { + pub fn init() { + + } + + pub fn get_computer_name() { + + } + + pub fn get_os_version() { + + } + + pub fn get_display_cardinfo_wmic() { + + } + + pub fn get_cpu_info() { + + } + + pub fn get_bluetooth_info() { + + } + + pub fn get_camera_info() { + + } + + pub fn get_micro_phone() { + + } + + pub fn get_gpu_info() { + + } + } \ No newline at end of file diff --git a/plugins/wdriver-rust/src/driver/drvmg.rs b/plugins/wdriver-rust/src/driver/drvmg.rs index 47883684..300d04dc 100644 --- a/plugins/wdriver-rust/src/driver/drvmg.rs +++ b/plugins/wdriver-rust/src/driver/drvmg.rs @@ -1,10 +1,10 @@ use std::{fs, io::Read, path::PathBuf, ptr::{null, null_mut}, result}; use windows::{ core::*, + Win32::System::IO::*, Win32::Foundation::*, - Win32::Storage::FileSystem::*, Win32::System::Threading::*, - Win32::System::IO::*, + Win32::Storage::FileSystem::*, }; pub struct DrivenManageImpl { diff --git a/plugins/wdriver-rust/src/util/mod.rs b/plugins/wdriver-rust/src/util/mod.rs index cf288bc3..b2d62120 100644 --- a/plugins/wdriver-rust/src/util/mod.rs +++ b/plugins/wdriver-rust/src/util/mod.rs @@ -1,3 +1,5 @@ -pub mod installed; pub mod log; -pub mod util; \ No newline at end of file +pub mod util; +pub mod windows_services; +pub mod windows_installed; +pub mod windwos_autostart; \ No newline at end of file diff --git a/plugins/wdriver-rust/src/util/installed.rs b/plugins/wdriver-rust/src/util/windows_installed.rs similarity index 100% rename from plugins/wdriver-rust/src/util/installed.rs rename to plugins/wdriver-rust/src/util/windows_installed.rs diff --git a/plugins/wdriver-rust/src/util/windows_services.rs b/plugins/wdriver-rust/src/util/windows_services.rs new file mode 100644 index 00000000..9836ecd3 --- /dev/null +++ b/plugins/wdriver-rust/src/util/windows_services.rs @@ -0,0 +1,106 @@ +use windows::{ + Win32::Foundation::*, + Win32::System::Threading::*, + Win32::System::Services::*, +}; + +use std::ptr::{null_mut}; + +pub struct Service; + +impl Service { + + pub unsafe fn get_services_info() { + + // let schandle = OpenSCManagerA ( + + // 0 as i32, + // SC_MANAGER_ENUMERATE_SERVICE + // ); + // println!("schandle: {:x?}",schandle); + + // let mut bytesneeded = 0; + // let mut numofservices = 0; + // EnumServicesStatusExA( + // schandle.unwrap(), + // SC_ENUM_PROCESS_INFO, + // SERVICE_WIN32, + // SERVICE_STATE_ALL, + // std::ptr::null_mut(), + // 0, + // &mut bytesneeded, + // &mut numofservices, + // std::ptr::null_mut(), + // std::ptr::null_mut() + // ); + + // println!("bytes needed: {}",bytesneeded); + // println!("number of services : {}",numofservices); + + // let baseptr = VirtualAlloc( + // std::ptr::null_mut(), + // bytesneeded as usize, + // 0x1000|0x2000, 0x40 + // ); + // EnumServicesStatusExA( + // schandle.unwrap(), + // SC_ENUM_PROCESS_INFO, + // SERVICE_WIN32, + // SERVICE_STATE_ALL, + // baseptr as *mut u8, + // bytesneeded, + // &mut bytesneeded, + // &mut numofservices, + // std::ptr::null_mut(), + // std::ptr::null_mut() + // ); + + // println!("bytes needed: {}",bytesneeded); + // println!("number of services : {}",numofservices); + + // //let mut enumservices = std::mem::zeroed::(); + // for i in 0..numofservices{ + // let mut enumservices = (*((baseptr as isize + (i as isize *std::mem::size_of::() as isize)) as *mut ENUM_SERVICE_STATUS_PROCESSA)); + // let dname = ReadStringFromMemory(GetCurrentProcess(), enumservices.lpDisplayName as *mut c_void); + // let sname = ReadStringFromMemory(GetCurrentProcess(), enumservices.lpServiceName as *mut c_void); + // //println!(" service display name: {}",dname); + // println!("service name: {}, pid: {}",sname,enumservices.ServiceStatusProcess.dwProcessId); + // let servicehandle = OpenServiceA( + // schandle.unwrap(), + // enumservices.lpServiceName, + // SERVICE_QUERY_CONFIG + // ); + // let mut sbytes = 0; + // QueryServiceConfigA( + // servicehandle.unwrap(), + // std::ptr::null_mut(), + // 0, &mut sbytes); + // let sbase =VirtualAlloc(std::ptr::null_mut(), sbytes as usize, 0x1000|0x2000, 0x40); + // QueryServiceConfigA( + // servicehandle.unwrap(), + // sbase as *mut QUERY_SERVICE_CONFIGA, + // sbytes, &mut sbytes); + // let sconfig = (*(sbase as *mut QUERY_SERVICE_CONFIGA)); + // let binpath = ReadStringFromMemory( + // GetCurrentProcess(), + // sconfig.lpBinaryPathName as *mut c_void + // ); + // if !binpath.contains("\""){ + // println!("binary path: {}",binpath); + // VirtualFree(sbase, 0, 0x8000); + // } + // } + + // Foundation::VirtualFree(baseptr, 0, 0x8000); + // /*let mut bytesneeded = 0; + + // let res = QueryServiceConfigA(schandle, + // std::ptr::null_mut(), + // 0, &mut bytesneeded); + + // println!("res: {}",res); + // println!("getlasterror: {}",GetLastError()); + // println!("bytes needed: {}",bytesneeded ); + // */ + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/src/util/windwos_autostart.rs b/plugins/wdriver-rust/src/util/windwos_autostart.rs new file mode 100644 index 00000000..884f40b1 --- /dev/null +++ b/plugins/wdriver-rust/src/util/windwos_autostart.rs @@ -0,0 +1,107 @@ +use std::borrow::Cow; +use std::error::Error; +use winreg::enums::*; +use winreg::reg_key::RegKey; +use winreg::HKEY; + +thread_local! { + static UNINSTALLS: Option = None; +} + +pub struct App { + reg: RegKey, +} +struct AppList { + uninstalls: RegKey, + index: usize, +} +impl Iterator for AppList { + type Item = App; + fn next(&mut self) -> Option { + let key = self.uninstalls.enum_keys().nth(self.index)?.ok()?; + self.index += 1; + let reg = self.uninstalls.open_subkey(key).ok()?; + Some(App { reg }) + } +} +impl AppList { + fn new(hive: HKEY, path: &str) -> Result> { + let hive = RegKey::predef(hive); + let uninstalls = hive.open_subkey(path)?; + + Ok(AppList { + uninstalls, + index: 0, + }) + } +} +impl App { + fn get_value(&self, name: &str) -> Cow { + self.reg + .get_value::(name) + .map(Cow::Owned) + .unwrap_or_else(|_| Cow::Borrowed("")) + } + pub fn name(&self) -> Cow { + self.get_value("DisplayName") + } + pub fn exec(&self) -> Cow { + self.get_value("DisplayIcon") + } + pub fn dump(&self) -> Cow { + self.reg + .enum_values() + .map(|r| { + let (name, value) = r.unwrap(); + format!("{}: {}\n", name, value) + }) + .collect::() + .into() + } + pub fn list() -> Result, Box> { + let system_apps = AppList::new( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + ) + .ok() + .into_iter() + .flatten(); + let system_apps_32 = AppList::new( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + ) + .ok() + .into_iter() + .flatten(); + let user_apps = AppList::new( + HKEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", + ) + .ok() + .into_iter() + .flatten(); + // this one may not exist + let user_apps_32 = AppList::new( + HKEY_CURRENT_USER, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", + ) + .ok() + .into_iter() + .flatten(); + let system_apps_runonce = AppList::new( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Runonce", + ) + .ok() + .into_iter() + .flatten(); + + let chain = system_apps + .chain(system_apps_32) + .chain(user_apps) + .chain(user_apps_32) + .chain(system_apps_runonce); + + Ok(chain) + } +} \ No newline at end of file diff --git a/plugins/wdriver-rust/tests/ts_appcore.rs b/plugins/wdriver-rust/tests/ts_appcore.rs index e9da961b..57a36e2b 100644 --- a/plugins/wdriver-rust/tests/ts_appcore.rs +++ b/plugins/wdriver-rust/tests/ts_appcore.rs @@ -1,5 +1,6 @@ -use wdriver_rs::appcore::app_include; use wdriver_rs::appcore::app_account; +use wdriver_rs::appcore::app_autostart; +use wdriver_rs::appcore::app_include; use wdriver_rs::appcore::app_net; use wdriver_rs::appcore::app_process; use wdriver_rs::appcore::app_service_software; @@ -44,4 +45,13 @@ pub fn unit_test_softwareinfo() { if b { println!("software init success."); } -} \ No newline at end of file +} + +#[test] +pub fn unit_test_astart_register() { + let mut astart_register: Vec = vec![]; + let b = app_autostart::AppAutoStart::get_astart_register(&mut astart_register); + if b { + println!("astart_register init success."); + } +} diff --git a/plugins/wdriver-rust/tests/ts_rule.rs b/plugins/wdriver-rust/tests/ts_rule.rs index 437b66db..f311bd14 100644 --- a/plugins/wdriver-rust/tests/ts_rule.rs +++ b/plugins/wdriver-rust/tests/ts_rule.rs @@ -1,8 +1,18 @@ use fast_log::{ - config, consts::LogSize, plugin::{file_split::RollingType, packer::LogPacker} + config, + consts::LogSize, + plugin::{file_split::RollingType, packer::LogPacker}, +}; +use std::{ + thread::{self, current}, + time, +}; +use tokio::{ + io::AsyncWriteExt, + runtime::Runtime, + sync::{mpsc::Sender, RwLock}, + task::JoinHandle, }; -use tokio::{io::AsyncWriteExt, sync::{mpsc::Sender, RwLock}, task::JoinHandle, runtime::Runtime}; -use std::{thread::{self, current}, time}; use wdriver_rs::config::config::*; @@ -10,7 +20,7 @@ use wdriver_rs::config::config::*; pub fn unit_get_dns_rule() { let runtime = tokio::runtime::Runtime::new().ok().unwrap(); runtime.spawn(async move { - let reply = RuleImpl::init().await; + let reply = RuleImpl::init().await; if false == reply { println!("rule init."); } From e057712cdf77cd8bad7e6fcc6b09b47cca942e3f Mon Sep 17 00:00:00 2001 From: czy Date: Sat, 14 Dec 2024 16:37:57 +0800 Subject: [PATCH 10/38] appcore autostart --- .../wdriver-rust/src/appcore/app_autostart.rs | 5 +++ .../src/util/windwos_autostart.rs | 43 +++++++------------ 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/plugins/wdriver-rust/src/appcore/app_autostart.rs b/plugins/wdriver-rust/src/appcore/app_autostart.rs index e6ab2dd5..0b9f0960 100644 --- a/plugins/wdriver-rust/src/appcore/app_autostart.rs +++ b/plugins/wdriver-rust/src/appcore/app_autostart.rs @@ -29,6 +29,11 @@ impl AppAutoStart { pub fn get_astart_register(astart_register:&mut Vec) -> bool { let apps = App::list().unwrap(); for app in apps { + let regrun_ctx = AppRegRunInfo { + valuename: app.get_key(), + valuekey: app.get_value(), + }; + astart_register.push(regrun_ctx); } if astart_register.is_empty() { return false; diff --git a/plugins/wdriver-rust/src/util/windwos_autostart.rs b/plugins/wdriver-rust/src/util/windwos_autostart.rs index 884f40b1..29ad2927 100644 --- a/plugins/wdriver-rust/src/util/windwos_autostart.rs +++ b/plugins/wdriver-rust/src/util/windwos_autostart.rs @@ -1,7 +1,9 @@ use std::borrow::Cow; use std::error::Error; +use std::fmt::Debug; use winreg::enums::*; use winreg::reg_key::RegKey; +use winreg::reg_value::RegValue; use winreg::HKEY; thread_local! { @@ -9,54 +11,41 @@ thread_local! { } pub struct App { - reg: RegKey, + name: String, + reg: RegValue, } struct AppList { - uninstalls: RegKey, + autostart: RegKey, index: usize, } impl Iterator for AppList { type Item = App; fn next(&mut self) -> Option { - let key = self.uninstalls.enum_keys().nth(self.index)?.ok()?; + let value_= self.autostart.enum_values().nth(self.index)?.ok()?; self.index += 1; - let reg = self.uninstalls.open_subkey(key).ok()?; - Some(App { reg }) + Some(App { + name: value_.0, + reg: value_.1 + }) } } impl AppList { fn new(hive: HKEY, path: &str) -> Result> { let hive = RegKey::predef(hive); - let uninstalls = hive.open_subkey(path)?; + let autostart = hive.open_subkey(path)?; Ok(AppList { - uninstalls, + autostart, index: 0, }) } } impl App { - fn get_value(&self, name: &str) -> Cow { - self.reg - .get_value::(name) - .map(Cow::Owned) - .unwrap_or_else(|_| Cow::Borrowed("")) - } - pub fn name(&self) -> Cow { - self.get_value("DisplayName") - } - pub fn exec(&self) -> Cow { - self.get_value("DisplayIcon") + pub fn get_key(&self) -> String { + self.name.to_string() } - pub fn dump(&self) -> Cow { - self.reg - .enum_values() - .map(|r| { - let (name, value) = r.unwrap(); - format!("{}: {}\n", name, value) - }) - .collect::() - .into() + pub fn get_value(&self) -> String { + self.reg.to_string() } pub fn list() -> Result, Box> { let system_apps = AppList::new( From a4245aae91c3c01211938a578f36d1afe9f45d67 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 17:34:14 +0800 Subject: [PATCH 11/38] fix: explicitly add musl rust target in ci --- .github/workflows/ci-edriver.yaml | 3 +++ .github/workflows/release-driver.yaml | 3 +++ .gitignore | 2 ++ 3 files changed, 8 insertions(+) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index c13fb465..681d2aeb 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -38,6 +38,9 @@ jobs: with: targets: ${{ matrix.target }} + - name: Add musl target + run: rustup target add ${{ matrix.target }} + - name: Cache cargo registry & build uses: actions/cache@v4 with: diff --git a/.github/workflows/release-driver.yaml b/.github/workflows/release-driver.yaml index 8cd2601b..5f6efb48 100644 --- a/.github/workflows/release-driver.yaml +++ b/.github/workflows/release-driver.yaml @@ -18,6 +18,9 @@ jobs: with: targets: x86_64-unknown-linux-musl + - name: Add musl target + run: rustup target add x86_64-unknown-linux-musl + - name: Cache cargo registry & build uses: actions/cache@v4 with: diff --git a/.gitignore b/.gitignore index 25644b0f..94358e1d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ agent/deploy/hades-agent # server server/webconsole/frontend +server/webconsole/frontend_backup +server/webconsole/frontend_v2 server/frontend # ignore certs From aa099c97ecbff22042389ddecc9bf80f8b2b1b96 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 18:01:43 +0800 Subject: [PATCH 12/38] fix: set LIBBPF_SYS_LIBRARY_PATH in ci for musl static builds --- .github/workflows/ci-edriver.yaml | 4 ++++ .github/workflows/release-driver.yaml | 3 ++- plugins/edriver/.cargo/config.toml | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 681d2aeb..378f0eaa 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -24,9 +24,11 @@ jobs: - arch: x86_64 runner: ubuntu-latest target: x86_64-unknown-linux-musl + lib_path: /usr/lib/x86_64-linux-gnu - arch: aarch64 runner: ubuntu-24.04-arm target: aarch64-unknown-linux-musl + lib_path: /usr/lib/aarch64-linux-gnu steps: - name: "Git checkout" uses: actions/checkout@v4 @@ -65,8 +67,10 @@ jobs: run: cd plugins/edriver && make build env: PLATFORM: ${{ matrix.arch }} + LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} - name: Test run: cd plugins/edriver && make test env: PLATFORM: ${{ matrix.arch }} + LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} diff --git a/.github/workflows/release-driver.yaml b/.github/workflows/release-driver.yaml index 5f6efb48..55b51d8f 100644 --- a/.github/workflows/release-driver.yaml +++ b/.github/workflows/release-driver.yaml @@ -46,7 +46,8 @@ jobs: cd plugins/edriver make build cd ../.. - + env: + LIBBPF_SYS_LIBRARY_PATH: /usr/lib/x86_64-linux-gnu - name: Strip & checksum run: | strip plugins/edriver/target/x86_64-unknown-linux-musl/release/edriver diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index dfc592b8..cbbc2c9a 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -1,5 +1,7 @@ [env] -LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" +LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" +LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" +LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" [target.x86_64-unknown-linux-musl] From 98b974db0b1d383305bd434f90ef60a6ac79ecdb Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 18:15:46 +0800 Subject: [PATCH 13/38] fix: add linux-libc-dev and TARGET_CC=gcc for libbpf-sys C compilation --- .github/workflows/ci-edriver.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 378f0eaa..9f563f09 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -56,7 +56,7 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends build-essential pkgconf libelf-dev libzstd-dev musl-tools llvm-14 clang-14 protobuf-compiler + sudo apt-get install -y --no-install-recommends build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev musl-tools llvm-14 clang-14 protobuf-compiler for tool in clang llc llvm-strip do sudo rm -f /usr/bin/$tool @@ -68,9 +68,11 @@ jobs: env: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} + TARGET_CC: gcc - name: Test run: cd plugins/edriver && make test env: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} + TARGET_CC: gcc From 07f9379dc3a32e125223eed52a2914c577636f77 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 18:54:55 +0800 Subject: [PATCH 14/38] fix: add -lzstd link flag for all targets (libbpf-sys missing zstd dep) --- plugins/edriver/.cargo/config.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index cbbc2c9a..83a1e39a 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -6,11 +6,15 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu [target.x86_64-unknown-linux-musl] linker = "x86_64-linux-musl-gcc" -rustflags = ["-C", "target-feature=+crt-static"] +rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-musl] linker = "aarch64-linux-musl-gcc" -rustflags = ["-C", "target-feature=+crt-static"] +rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" +rustflags = ["-C", "link-arg=-lzstd"] + +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-lzstd"] From 32fce5682a181bc75e5c99cc575688f15dada567 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 19:02:08 +0800 Subject: [PATCH 15/38] fix: add rust-toolchain.toml to ensure musl targets are always installed --- plugins/edriver/rust-toolchain.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 plugins/edriver/rust-toolchain.toml diff --git a/plugins/edriver/rust-toolchain.toml b/plugins/edriver/rust-toolchain.toml new file mode 100644 index 00000000..681d79b5 --- /dev/null +++ b/plugins/edriver/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +channel = "stable" +targets = [ + "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-musl", +] From ac097b4f91f9cb6bc226c885a20dae1586269a52 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 19:13:20 +0800 Subject: [PATCH 16/38] fix: use [host] rustflags for -lzstd to cover all build-script hosts --- plugins/edriver/.cargo/config.toml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 83a1e39a..d296c58c 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -4,6 +4,12 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu: LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" +# libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. +# Apply -lzstd to all host compilations (build scripts) via [host], and +# explicitly to each musl target for the final binary. +[host] +rustflags = ["-C", "link-arg=-lzstd"] + [target.x86_64-unknown-linux-musl] linker = "x86_64-linux-musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] @@ -14,7 +20,3 @@ rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" -rustflags = ["-C", "link-arg=-lzstd"] - -[target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-lzstd"] From be321f22451bb8113e0a843600a44e4eee4cb4ad Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 19:41:39 +0800 Subject: [PATCH 17/38] fix: use musl-gcc instead of nonexistent {arch}-linux-musl-gcc for CC and linker --- plugins/edriver/.cargo/config.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index d296c58c..184b89ca 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -3,6 +3,10 @@ LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" +# musl-tools provides `musl-gcc` (native arch wrapper) but NOT `{arch}-linux-musl-gcc`. +# Set CC_* so the cc crate finds it without guessing the nonexistent triplet name. +CC_x86_64_unknown_linux_musl = "musl-gcc" +CC_aarch64_unknown_linux_musl = "musl-gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. # Apply -lzstd to all host compilations (build scripts) via [host], and @@ -11,11 +15,11 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu rustflags = ["-C", "link-arg=-lzstd"] [target.x86_64-unknown-linux-musl] -linker = "x86_64-linux-musl-gcc" +linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-musl] -linker = "aarch64-linux-musl-gcc" +linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-gnu] From 0d18e919eaa83279da773ed4010da24ae07d279f Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 19:49:14 +0800 Subject: [PATCH 18/38] fix: use gcc (not musl-gcc) for C compilation in libbpf-sys build --- plugins/edriver/.cargo/config.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 184b89ca..6437e7a7 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -3,10 +3,10 @@ LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" -# musl-tools provides `musl-gcc` (native arch wrapper) but NOT `{arch}-linux-musl-gcc`. -# Set CC_* so the cc crate finds it without guessing the nonexistent triplet name. -CC_x86_64_unknown_linux_musl = "musl-gcc" -CC_aarch64_unknown_linux_musl = "musl-gcc" +# libbpf-sys vendors and compiles libbpf C sources; use the native gcc (always present). +# musl-gcc is only needed for the final Rust link step (set via [target.*].linker below). +CC_x86_64_unknown_linux_musl = "gcc" +CC_aarch64_unknown_linux_musl = "gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. # Apply -lzstd to all host compilations (build scripts) via [host], and From ab09cfef755b18c42ae577c6eb94da844c7f5fbb Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 20:18:04 +0800 Subject: [PATCH 19/38] fix: drop llvm-14/clang-14 version pins, use distro-default llvm/clang --- .github/workflows/ci-edriver.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 9f563f09..74113fa4 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -56,12 +56,9 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev musl-tools llvm-14 clang-14 protobuf-compiler - for tool in clang llc llvm-strip - do - sudo rm -f /usr/bin/$tool - sudo ln -s /usr/bin/${tool}-14 /usr/bin/$tool - done + sudo apt-get install -y --no-install-recommends \ + build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev \ + musl-tools llvm clang protobuf-compiler - name: Build run: cd plugins/edriver && make build From 7916a58e246770fe02b4ef5c182125d7c054e41e Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 20:24:49 +0800 Subject: [PATCH 20/38] fix: add -lzstd to host gnu targets for build script linking --- plugins/edriver/.cargo/config.toml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 6437e7a7..10a7de32 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -9,9 +9,15 @@ CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. -# Apply -lzstd to all host compilations (build scripts) via [host], and -# explicitly to each musl target for the final binary. -[host] +# Build scripts (edriver's build.rs) are compiled for the HOST gnu triple, so +# we must add -lzstd to those host targets explicitly. +# [host] rustflags is unreliable when a matching [target.*-gnu] section exists, +# so we spell it out per triple. +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-lzstd"] + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" rustflags = ["-C", "link-arg=-lzstd"] [target.x86_64-unknown-linux-musl] @@ -21,6 +27,3 @@ rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] [target.aarch64-unknown-linux-musl] linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] - -[target.aarch64-unknown-linux-gnu] -linker = "aarch64-linux-gnu-gcc" From 313a707cba3011159dabc8766c8b2c3e1e3625ea Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 20:36:37 +0800 Subject: [PATCH 21/38] fix: separate core apt deps from clang/llvm to ensure protoc always installs --- .github/workflows/ci-edriver.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 74113fa4..3d3b9190 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -56,9 +56,12 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq + # Core packages — must always succeed sudo apt-get install -y --no-install-recommends \ build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev \ - musl-tools llvm clang protobuf-compiler + musl-tools protobuf-compiler + # BPF compilation toolchain — package name is distro-version-dependent + sudo apt-get install -y --no-install-recommends clang llvm - name: Build run: cd plugins/edriver && make build From a170f38bd2806d1b59124d0c20f108636ff0d39a Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 20:46:29 +0800 Subject: [PATCH 22/38] fix: restore [host].rustflags for build-script -lzstd; add config.toml to cache key --- .github/workflows/ci-edriver.yaml | 2 +- plugins/edriver/.cargo/config.toml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 3d3b9190..143732ba 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -50,7 +50,7 @@ jobs: ~/.cargo/registry ~/.cargo/git plugins/edriver/target - key: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-${{ hashFiles('plugins/edriver/Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-${{ hashFiles('plugins/edriver/Cargo.lock', 'plugins/edriver/.cargo/config.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver- - name: Install build dependencies diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 10a7de32..08637a3e 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -9,16 +9,16 @@ CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. -# Build scripts (edriver's build.rs) are compiled for the HOST gnu triple, so -# we must add -lzstd to those host targets explicitly. -# [host] rustflags is unreliable when a matching [target.*-gnu] section exists, -# so we spell it out per triple. -[target.x86_64-unknown-linux-gnu] +# During cross-compilation (host-gnu → target-musl), build scripts are compiled for +# the HOST. Cargo ignores [target.HOST].rustflags for build scripts; only [host].rustflags +# is applied. This is per Cargo spec: https://doc.rust-lang.org/cargo/reference/config.html +[host] rustflags = ["-C", "link-arg=-lzstd"] +# Keep linker for local x86→aarch64 cross-compilation convenience. +# rustflags omitted here: they do NOT apply to build scripts during cross-compile. [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" -rustflags = ["-C", "link-arg=-lzstd"] [target.x86_64-unknown-linux-musl] linker = "musl-gcc" From 41528bb5931901f9f1de98066775d274893b0ac0 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 21:18:59 +0800 Subject: [PATCH 23/38] fix: isolate protobuf-compiler install and drop gnu target sections in cargo config - Install protobuf-compiler in a separate apt-get line so a linux-libc-dev version conflict on ubuntu-24.04-arm cannot silently skip it - Set PROTOC=/usr/bin/protoc explicitly in Build/Test steps to eliminate any PATH resolution ambiguity for prost-build - Remove [target.*-gnu] sections from .cargo/config.toml; only musl targets are built, and [host].rustflags handles build-script -lzstd injection --- .github/workflows/ci-edriver.yaml | 11 ++++++++--- plugins/edriver/.cargo/config.toml | 10 ++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 143732ba..9e1d7a99 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -56,10 +56,13 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - # Core packages — must always succeed + # protobuf-compiler is isolated: the SDK build script panics at runtime if + # protoc is missing, and it must not be silently skipped due to a conflict + # in a combined apt-get line (e.g. linux-libc-dev version mismatch on arm64). + sudo apt-get install -y protobuf-compiler + # Core packages sudo apt-get install -y --no-install-recommends \ - build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev \ - musl-tools protobuf-compiler + build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev musl-tools # BPF compilation toolchain — package name is distro-version-dependent sudo apt-get install -y --no-install-recommends clang llvm @@ -69,6 +72,7 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + PROTOC: /usr/bin/protoc - name: Test run: cd plugins/edriver && make test @@ -76,3 +80,4 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + PROTOC: /usr/bin/protoc diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 08637a3e..49d894f0 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -9,17 +9,11 @@ CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. -# During cross-compilation (host-gnu → target-musl), build scripts are compiled for -# the HOST. Cargo ignores [target.HOST].rustflags for build scripts; only [host].rustflags -# is applied. This is per Cargo spec: https://doc.rust-lang.org/cargo/reference/config.html +# Build scripts are compiled for the HOST (gnu) triple; [host].rustflags injects -lzstd +# so the build-script binary itself links successfully. [host] rustflags = ["-C", "link-arg=-lzstd"] -# Keep linker for local x86→aarch64 cross-compilation convenience. -# rustflags omitted here: they do NOT apply to build scripts during cross-compile. -[target.aarch64-unknown-linux-gnu] -linker = "aarch64-linux-gnu-gcc" - [target.x86_64-unknown-linux-musl] linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] From ad93f930236f25cf1c77f318b9176177d2861d73 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 21:29:35 +0800 Subject: [PATCH 24/38] fix: follow ci-agent pattern for protobuf-compiler install on arm64 Mixing linux-libc-dev with protobuf-compiler in a single apt-get line causes a version-conflict failure on ubuntu-24.04-arm, silently dropping protoc. Match the ci-agent/ci-collector approach: install protobuf-compiler and musl-tools together first, then BPF deps separately with || true for linux-libc-dev (pre-installed on GitHub runners, prone to conflicts). --- .github/workflows/ci-edriver.yaml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 9e1d7a99..962aed34 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -56,14 +56,17 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - # protobuf-compiler is isolated: the SDK build script panics at runtime if - # protoc is missing, and it must not be silently skipped due to a conflict - # in a combined apt-get line (e.g. linux-libc-dev version mismatch on arm64). - sudo apt-get install -y protobuf-compiler - # Core packages + # protobuf-compiler + musl-tools: keep together and away from linux-libc-dev. + # linux-libc-dev can conflict with the runner's pre-installed version on + # ubuntu-24.04-arm, causing the whole apt-get line to fail and silently + # dropping protobuf-compiler (pattern from ci-agent / ci-collector). + sudo apt-get install -y --no-install-recommends protobuf-compiler musl-tools + # BPF build deps; linux-libc-dev is pre-installed on GitHub runners so + # a version mismatch is common — allow failure. sudo apt-get install -y --no-install-recommends \ - build-essential pkgconf libelf-dev libzstd-dev linux-libc-dev musl-tools - # BPF compilation toolchain — package name is distro-version-dependent + build-essential pkgconf libelf-dev libzstd-dev || true + sudo apt-get install -y linux-libc-dev || true + # BPF compilation toolchain sudo apt-get install -y --no-install-recommends clang llvm - name: Build From 92f41a000befdc5b9443709bedfe696534d2a0aa Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 21:42:25 +0800 Subject: [PATCH 25/38] fix: replace [host].rustflags with [target.HOST].rustflags for -lzstd [host].rustflags is only triggered by Cargo for true cross-arch builds (e.g. x86_64 host -> aarch64 target). Both CI jobs are same-arch/different- libc (gnu->musl), so Cargo silently skips [host] and the build-script linker command never receives -lzstd, causing undefined ZSTD_* symbol errors. Use [target.x86_64-unknown-linux-gnu] and [target.aarch64-unknown-linux-gnu] rustflags instead: these apply to any compilation for those triples, including build scripts on same-arch cross-compile setups. --- plugins/edriver/.cargo/config.toml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 49d894f0..976a1b42 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -9,9 +9,16 @@ CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" # libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. -# Build scripts are compiled for the HOST (gnu) triple; [host].rustflags injects -lzstd -# so the build-script binary itself links successfully. -[host] +# Build scripts are compiled for the HOST (gnu) triple and need -lzstd at link time. +# +# [host].rustflags only fires for true cross-arch compilation (e.g. x86_64→aarch64). +# Both CI jobs are same-arch/different-libc (x86_64-gnu→x86_64-musl and +# aarch64-gnu→aarch64-musl), so Cargo skips [host] and uses [target.HOST] instead. +# No `linker` key needed: the default `cc` is correct on both GitHub-hosted runners. +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-lzstd"] + +[target.aarch64-unknown-linux-gnu] rustflags = ["-C", "link-arg=-lzstd"] [target.x86_64-unknown-linux-musl] From 4e04d819905a8f9cbb4b1a759b46de1010c97c44 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:17:00 +0800 Subject: [PATCH 26/38] fix: restore [host].rustflags for -lzstd; fix libzstd-dev install; add setup-protoc From Cargo 1.80+ target-applies-to-host = false by default, so [target.HOST.rustflags] does NOT reach build scripts. Build scripts only receive [host].rustflags (fires whenever --target != host triple, i.e. always: gnu != musl). Revert the [target.gnu] sections from 92f41a0 which were silently ignored for build scripts. Also separate libelf-dev + libzstd-dev onto their own mandatory apt-get line (no || true) so a build-essential/linux-libc-dev version conflict on ARM runners cannot silently drop the libraries needed by libbpf-sys. Use actions/setup-protoc@v3 instead of apt-get for protoc: downloads a prebuilt binary for the current arch (x86_64/aarch64) and sets PROTOC automatically, bypassing apt entirely. --- .github/workflows/ci-edriver.yaml | 23 ++++++++++++----------- plugins/edriver/.cargo/config.toml | 18 +++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 962aed34..11bbda9c 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -43,6 +43,11 @@ jobs: - name: Add musl target run: rustup target add ${{ matrix.target }} + - name: Setup protoc + uses: actions/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Cache cargo registry & build uses: actions/cache@v4 with: @@ -56,15 +61,13 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - # protobuf-compiler + musl-tools: keep together and away from linux-libc-dev. - # linux-libc-dev can conflict with the runner's pre-installed version on - # ubuntu-24.04-arm, causing the whole apt-get line to fail and silently - # dropping protobuf-compiler (pattern from ci-agent / ci-collector). - sudo apt-get install -y --no-install-recommends protobuf-compiler musl-tools - # BPF build deps; linux-libc-dev is pre-installed on GitHub runners so - # a version mismatch is common — allow failure. - sudo apt-get install -y --no-install-recommends \ - build-essential pkgconf libelf-dev libzstd-dev || true + sudo apt-get install -y --no-install-recommends musl-tools + # libelf-dev + libzstd-dev are required for libbpf-sys to link correctly. + # Keep on their own line so a build-essential conflict cannot silently + # drop them (build-essential pulls in linux-libc-dev which often conflicts + # with the pre-installed version on GitHub ARM runners). + sudo apt-get install -y --no-install-recommends pkgconf libelf-dev libzstd-dev + sudo apt-get install -y --no-install-recommends build-essential || true sudo apt-get install -y linux-libc-dev || true # BPF compilation toolchain sudo apt-get install -y --no-install-recommends clang llvm @@ -75,7 +78,6 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - PROTOC: /usr/bin/protoc - name: Test run: cd plugins/edriver && make test @@ -83,4 +85,3 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - PROTOC: /usr/bin/protoc diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 976a1b42..fb3eadb4 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -8,17 +8,13 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" -# libbpf-sys does not emit -lzstd even though system libelf.a is built with zstd support. -# Build scripts are compiled for the HOST (gnu) triple and need -lzstd at link time. -# -# [host].rustflags only fires for true cross-arch compilation (e.g. x86_64→aarch64). -# Both CI jobs are same-arch/different-libc (x86_64-gnu→x86_64-musl and -# aarch64-gnu→aarch64-musl), so Cargo skips [host] and uses [target.HOST] instead. -# No `linker` key needed: the default `cc` is correct on both GitHub-hosted runners. -[target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-lzstd"] - -[target.aarch64-unknown-linux-gnu] +# libbpf-sys 1.x does not emit cargo:rustc-link-lib=zstd even though +# Ubuntu 24.04's libelf.a is built with zstd support. +# Build scripts always compile for the HOST triple (gnu). From Cargo 1.80+, +# target-applies-to-host = false (default), so [target.HOST.rustflags] is +# NOT applied to build scripts. Only [host].rustflags reaches them. +# [host] fires whenever --target != host triple (always true: gnu != musl). +[host] rustflags = ["-C", "link-arg=-lzstd"] [target.x86_64-unknown-linux-musl] From 7bb01db1d72fec082670ac53583d1884b05f4757 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:21:04 +0800 Subject: [PATCH 27/38] fix: install protoc via apt-get, matching ci-agent/ci-collector pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the broken actions/setup-protoc step. Install protobuf-compiler via apt-get on its own line (away from linux-libc-dev) — same pattern as ci-agent and ci-collector. The linux-libc-dev conflict on ubuntu-24.04-arm only affects lines that mix it in; keeping protobuf-compiler separate ensures it is always installed successfully. --- .github/workflows/ci-edriver.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 11bbda9c..4e2afb22 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -43,11 +43,6 @@ jobs: - name: Add musl target run: rustup target add ${{ matrix.target }} - - name: Setup protoc - uses: actions/setup-protoc@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Cache cargo registry & build uses: actions/cache@v4 with: @@ -61,7 +56,9 @@ jobs: - name: Install build dependencies run: | sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends musl-tools + # protobuf-compiler on its own line, away from linux-libc-dev which + # conflicts on ubuntu-24.04-arm (matches ci-agent / ci-collector pattern). + sudo apt-get install -y --no-install-recommends protobuf-compiler musl-tools # libelf-dev + libzstd-dev are required for libbpf-sys to link correctly. # Keep on their own line so a build-essential conflict cannot silently # drop them (build-essential pulls in linux-libc-dev which often conflicts From d7caff351451bb15f5c29b4c044988f1cf01d120 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:38:05 +0800 Subject: [PATCH 28/38] fix(edriver): use RUSTFLAGS env var to add -lzstd for build scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [host].rustflags in Cargo config only applies when host arch != target arch (true cross-compilation). For same-arch gnu→musl builds it is silently ignored. Setting RUSTFLAGS in the CI step env is the correct bypass: - RUSTFLAGS applies to build scripts (gnu host, no config override) - [target.*.musl].rustflags overrides RUSTFLAGS for the final binary so crt-static and the musl -lzstd are unaffected Remove the non-functional [host] section from config.toml. --- .github/workflows/ci-edriver.yaml | 7 +++++++ plugins/edriver/.cargo/config.toml | 9 --------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 4e2afb22..0398a967 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -75,6 +75,12 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + # -lzstd is needed by the build-script link step: libbpf-sys links + # system libelf.a (Ubuntu 24.04 built with ZSTD support) but does not + # emit cargo:rustc-link-lib=zstd itself. RUSTFLAGS applies to build + # scripts (gnu host) and is overridden by [target.musl].rustflags for + # the final binary, so crt-static is not affected. + RUSTFLAGS: "-C link-arg=-lzstd" - name: Test run: cd plugins/edriver && make test @@ -82,3 +88,4 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + RUSTFLAGS: "-C link-arg=-lzstd" diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index fb3eadb4..11873394 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -8,15 +8,6 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" -# libbpf-sys 1.x does not emit cargo:rustc-link-lib=zstd even though -# Ubuntu 24.04's libelf.a is built with zstd support. -# Build scripts always compile for the HOST triple (gnu). From Cargo 1.80+, -# target-applies-to-host = false (default), so [target.HOST.rustflags] is -# NOT applied to build scripts. Only [host].rustflags reaches them. -# [host] fires whenever --target != host triple (always true: gnu != musl). -[host] -rustflags = ["-C", "link-arg=-lzstd"] - [target.x86_64-unknown-linux-musl] linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] From c2ce699a93ce2e76158149de33c8b3759f7ca3db Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:48:23 +0800 Subject: [PATCH 29/38] fix(edriver): use [build].rustflags for host -lzstd; set PROTOC explicitly Cargo doc says RUSTFLAGS env and [target.X].rustflags are mutually exclusive: setting RUSTFLAGS in CI silently dropped crt-static from the musl link. Switch to [build].rustflags in config.toml: - [build].rustflags only fires when no [target.X] matches the unit - musl units match [target.*-musl] -> get crt-static + lzstd (unchanged) - build scripts run on gnu host; no [target.*-gnu] defined -> [build].rustflags fires -> -lzstd added to host link Also set PROTOC=/usr/bin/protoc explicitly in CI so SDK/rust/build.rs (prost-build) finds it without relying on PATH heuristics. --- .github/workflows/ci-edriver.yaml | 11 ++++------- plugins/edriver/.cargo/config.toml | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 0398a967..f80baf70 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -75,12 +75,9 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - # -lzstd is needed by the build-script link step: libbpf-sys links - # system libelf.a (Ubuntu 24.04 built with ZSTD support) but does not - # emit cargo:rustc-link-lib=zstd itself. RUSTFLAGS applies to build - # scripts (gnu host) and is overridden by [target.musl].rustflags for - # the final binary, so crt-static is not affected. - RUSTFLAGS: "-C link-arg=-lzstd" + # prost-build (SDK/rust/build.rs) calls protoc; set PROTOC explicitly + # so it does not rely on PATH lookup heuristics. + PROTOC: /usr/bin/protoc - name: Test run: cd plugins/edriver && make test @@ -88,4 +85,4 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - RUSTFLAGS: "-C link-arg=-lzstd" + PROTOC: /usr/bin/protoc diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 11873394..65521e11 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -8,6 +8,20 @@ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" +# Build scripts compile for the HOST triple (gnu). libbpf-sys links the system +# libelf.a which (on Ubuntu 24.04) depends on ZSTD, but libbpf-sys does NOT +# emit `cargo:rustc-link-lib=zstd`. We must add it manually for the host link +# step. [host].rustflags is silently ignored when host and target share an arch +# (gnu vs musl), so we use [build].rustflags as the host-side fallback. +# +# Per Cargo docs, [target.].rustflags and [build].rustflags are +# mutually exclusive: when a unit matches [target.X], only that section is +# used. So [target.*-musl] below fully controls the musl link (crt-static + +# lzstd) and [build] only applies to build scripts on the gnu host (no +# [target.*-gnu] defined → fallback to [build]). +[build] +rustflags = ["-C", "link-arg=-lzstd"] + [target.x86_64-unknown-linux-musl] linker = "musl-gcc" rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] From 4e28ee455984d536db9537ddfe3ba0d8fe49937d Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:51:32 +0800 Subject: [PATCH 30/38] ci(edriver): split apt installs, verify protoc, auto-export PROTOC - Each apt-get install on its own line so a single package conflict cannot silently drop unrelated packages (mandatory ones lose || true). - New 'Verify toolchain' step runs which protoc / protoc --version so install failures surface immediately instead of failing later in prost-build with a confusing NotFound error. - Export PROTOC dynamically via $GITHUB_ENV using $(which protoc) instead of hardcoding /usr/bin/protoc. --- .github/workflows/ci-edriver.yaml | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index f80baf70..88fe78fa 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -55,19 +55,26 @@ jobs: - name: Install build dependencies run: | + set -euxo pipefail sudo apt-get update -qq - # protobuf-compiler on its own line, away from linux-libc-dev which - # conflicts on ubuntu-24.04-arm (matches ci-agent / ci-collector pattern). - sudo apt-get install -y --no-install-recommends protobuf-compiler musl-tools - # libelf-dev + libzstd-dev are required for libbpf-sys to link correctly. - # Keep on their own line so a build-essential conflict cannot silently - # drop them (build-essential pulls in linux-libc-dev which often conflicts - # with the pre-installed version on GitHub ARM runners). + # Install each package group on its own line so a single package + # conflict (notably linux-libc-dev on ubuntu-24.04-arm) cannot + # silently drop unrelated packages. Mandatory packages have NO + # `|| true` fallback so install failures are visible. + sudo apt-get install -y --no-install-recommends protobuf-compiler + sudo apt-get install -y --no-install-recommends musl-tools sudo apt-get install -y --no-install-recommends pkgconf libelf-dev libzstd-dev + sudo apt-get install -y --no-install-recommends clang llvm + # Optional: build-essential / linux-libc-dev sometimes conflict on ARM. sudo apt-get install -y --no-install-recommends build-essential || true sudo apt-get install -y linux-libc-dev || true - # BPF compilation toolchain - sudo apt-get install -y --no-install-recommends clang llvm + + - name: Verify toolchain and export PROTOC + run: | + set -euxo pipefail + which protoc + protoc --version + echo "PROTOC=$(which protoc)" >> "$GITHUB_ENV" - name: Build run: cd plugins/edriver && make build @@ -75,9 +82,6 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - # prost-build (SDK/rust/build.rs) calls protoc; set PROTOC explicitly - # so it does not rely on PATH lookup heuristics. - PROTOC: /usr/bin/protoc - name: Test run: cd plugins/edriver && make test @@ -85,4 +89,3 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - PROTOC: /usr/bin/protoc From 75af08c47ec3c5e77464975a752aeb691c6ba1ca Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 22:56:26 +0800 Subject: [PATCH 31/38] ci(edriver): drop target/ from cache to defeat stale build-script reuse The same build-script binary hash 'edriver-70c564e32bed78cd' appeared across consecutive CI runs even after .cargo/config.toml changes. Cargo does not always invalidate build-script fingerprints on rustflags-only config changes, so a stale target/ restored via restore-keys reused a build-script binary linked WITHOUT -lzstd -> link failure persisted. Fix: cache only ~/.cargo/registry (the big win) and rebuild target/ fresh every time. Bump key suffix to v2 to force this run to miss existing caches. --- .github/workflows/ci-edriver.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 88fe78fa..77ef18fb 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -43,15 +43,20 @@ jobs: - name: Add musl target run: rustup target add ${{ matrix.target }} - - name: Cache cargo registry & build + - name: Cache cargo registry uses: actions/cache@v4 with: + # Intentionally do NOT cache plugins/edriver/target across commits: + # Cargo's build-script fingerprint does not always invalidate on + # [build].rustflags / config.toml changes, so a restored stale + # target/ would reuse a build-script binary linked without -lzstd + # and break the ZSTD fix. Re-linking from scratch is cheap; the + # registry cache (much larger) still provides the speedup. path: | ~/.cargo/registry ~/.cargo/git - plugins/edriver/target - key: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-${{ hashFiles('plugins/edriver/Cargo.lock', 'plugins/edriver/.cargo/config.toml') }} - restore-keys: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver- + key: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-registry-v2-${{ hashFiles('plugins/edriver/Cargo.lock') }} + restore-keys: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-registry-v2- - name: Install build dependencies run: | From 5b03c0fc36e5fb4217b4a2f236b38b90859c5c89 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Fri, 22 May 2026 23:56:35 +0800 Subject: [PATCH 32/38] fix(edriver): vendor libelf to drop ZSTD link dependency Root cause: edriver's 'static' feature pulled libbpf-sys/static, which sets libbpf-sys's static-libelf and emits 'cargo:rustc-link-lib=static=elf'. On Ubuntu 24.04+ the system libelf.a is compiled with USE_ZSTD and references ZSTD_createCCtx/compressStream2/freeCCtx/decompress/isError, but libbpf-sys never emits 'cargo:rustc-link-lib=zstd', breaking both the host build-script link and the final musl link. Switch libbpf-rs (and libbpf-sys in [build-dependencies] under resolver 2) to the 'vendored' feature so libbpf-sys compiles its bundled elfutils with '--without-zstd'. The resulting libelf.a has no ZSTD_* references at all, so no -lzstd flag is needed anywhere. Removes the obsolete [build]/[target.*-musl] '-C link-arg=-lzstd' rustflags hacks. CI workflow: add autotools stack (autoconf/automake/libtool/m4/autopoint/ gettext/gperf/flex/bison/gawk) required to (re)generate elfutils' configure. Drop libelf-dev and libzstd-dev (vendored build does not need them). --- .github/workflows/ci-edriver.yaml | 15 ++++++++++----- plugins/edriver/.cargo/config.toml | 24 ++++++------------------ plugins/edriver/Cargo.toml | 19 +++++++++++++++++-- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 77ef18fb..1d9386b4 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -64,15 +64,20 @@ jobs: sudo apt-get update -qq # Install each package group on its own line so a single package # conflict (notably linux-libc-dev on ubuntu-24.04-arm) cannot - # silently drop unrelated packages. Mandatory packages have NO - # `|| true` fallback so install failures are visible. + # silently drop unrelated packages. sudo apt-get install -y --no-install-recommends protobuf-compiler sudo apt-get install -y --no-install-recommends musl-tools - sudo apt-get install -y --no-install-recommends pkgconf libelf-dev libzstd-dev + # libbpf-sys is built with the `vendored` feature (via edriver's + # `static` -> libbpf-rs/vendored). It compiles bundled libbpf, + # elfutils and zlib from source, which needs the autotools stack + # plus flex/bison/gettext/gperf to regenerate elfutils' configure. + sudo apt-get install -y --no-install-recommends \ + pkgconf autoconf automake libtool m4 \ + autopoint gettext gperf flex bison gawk + # BPF compilation toolchain sudo apt-get install -y --no-install-recommends clang llvm - # Optional: build-essential / linux-libc-dev sometimes conflict on ARM. + # build-essential occasionally conflicts on ARM via linux-libc-dev. sudo apt-get install -y --no-install-recommends build-essential || true - sudo apt-get install -y linux-libc-dev || true - name: Verify toolchain and export PROTOC run: | diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index 65521e11..fb3cbe27 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -3,29 +3,17 @@ LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" -# libbpf-sys vendors and compiles libbpf C sources; use the native gcc (always present). -# musl-gcc is only needed for the final Rust link step (set via [target.*].linker below). +# libbpf-sys vendors and compiles libbpf / elfutils / zlib C sources via the +# `vendored` feature (enabled by edriver's `static` feature). The bundled +# elfutils is configured `--without-zstd`, so no ZSTD_* symbols leak into the +# link line and no rustflags workaround is needed. CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" -# Build scripts compile for the HOST triple (gnu). libbpf-sys links the system -# libelf.a which (on Ubuntu 24.04) depends on ZSTD, but libbpf-sys does NOT -# emit `cargo:rustc-link-lib=zstd`. We must add it manually for the host link -# step. [host].rustflags is silently ignored when host and target share an arch -# (gnu vs musl), so we use [build].rustflags as the host-side fallback. -# -# Per Cargo docs, [target.].rustflags and [build].rustflags are -# mutually exclusive: when a unit matches [target.X], only that section is -# used. So [target.*-musl] below fully controls the musl link (crt-static + -# lzstd) and [build] only applies to build scripts on the gnu host (no -# [target.*-gnu] defined → fallback to [build]). -[build] -rustflags = ["-C", "link-arg=-lzstd"] - [target.x86_64-unknown-linux-musl] linker = "musl-gcc" -rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] +rustflags = ["-C", "target-feature=+crt-static"] [target.aarch64-unknown-linux-musl] linker = "musl-gcc" -rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lzstd"] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/plugins/edriver/Cargo.toml b/plugins/edriver/Cargo.toml index c5052d72..901cd331 100644 --- a/plugins/edriver/Cargo.toml +++ b/plugins/edriver/Cargo.toml @@ -7,7 +7,15 @@ description = "Rust version of hades edriver" [features] debug = ["sdk/debug"] -static = ["libbpf-rs/static"] +# `vendored` (not just `static`) makes libbpf-sys build its own copy of libbpf, +# elfutils and zlib from source. The bundled elfutils is configured with +# `--without-zstd`, so the resulting libelf.a does NOT reference ZSTD_* symbols. +# Plain `static` would link the system libelf.a which on Ubuntu 24.04+ is built +# with USE_ZSTD and therefore pulls in unresolved ZSTD_* references at link +# time (libbpf-sys never emits `cargo:rustc-link-lib=zstd`). +# `vendored` implies `static-*` for all three native deps, so the final musl +# binary is still fully self-contained. +static = ["libbpf-rs/vendored"] [dependencies] anyhow = "1.0" @@ -18,10 +26,17 @@ governor = "0.10.4" lazy_static = "1.5.0" twox-hash = "2.1" hex = "0.4" -libbpf-rs = { version = "0.26.2", features = ["static"] } +libbpf-rs = { version = "0.26.2", features = ["vendored"] } bitflags = "2.11.1" moka = { version = "0.12", features = ["sync"] } sdk = { path = "../../SDK/rust" } [build-dependencies] libbpf-cargo = "0.26.2" +# Resolver 2 splits feature sets between normal-deps and build-deps. Without +# this explicit pin, the build-script binary (which uses libbpf-cargo -> +# libbpf-sys) would still link the system libelf.a (Ubuntu 24.04+ built with +# ZSTD), causing "undefined reference to ZSTD_*". Enabling `vendored` here +# makes libbpf-sys compile elfutils with --without-zstd for the host link +# step too, matching the normal-deps tree. +libbpf-sys = { version = "1.7", features = ["vendored"] } From 37f6c7c7662ee397ec15421689a03e409b6adeb5 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Sat, 23 May 2026 01:10:53 +0800 Subject: [PATCH 33/38] fix(edriver): use static+explicit -lzstd instead of vendored elfutils The vendored approach requires autoreconf/autopoint to regenerate elfutils' configure script, which proved unreliable on the ubuntu-24.04-arm runner. Switch back to libbpf-rs/static (system libelf.a) and address the root cause directly: Ubuntu 24.04 system libelf.a is compiled with USE_ZSTD, referencing ZSTD_* symbols. libbpf-sys never emits cargo:rustc-link-lib=zstd, so we add -lzstd manually via: [build].rustflags = ["-C link-arg=-lzstd"] \u2192 applies to host build-script binaries (libbpf-cargo \u2192 libbpf-sys); links libzstd.so dynamically, which is fine for a host executable. [target.*-musl].rustflags += "-C link-arg=-Wl,-Bstatic,-lzstd,-Bdynamic" \u2192 forces libzstd.a static link for the final musl binary so it stays fully self-contained; libzstd.a comes from libzstd-dev in CI. libbpf-sys already emits cargo:rustc-link-search=native= for every path in LIBBPF_SYS_LIBRARY_PATH, so the linker finds libzstd.{a,so} in /usr/lib/{x86_64,aarch64}-linux-gnu/ without extra -L flags. CI: replace autotools stack with libelf-dev + libzstd-dev (two packages). --- .github/workflows/ci-edriver.yaml | 14 +++++++------- plugins/edriver/.cargo/config.toml | 26 ++++++++++++++++++++------ plugins/edriver/Cargo.toml | 30 +++++++++++++----------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 1d9386b4..82e15be5 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -67,13 +67,13 @@ jobs: # silently drop unrelated packages. sudo apt-get install -y --no-install-recommends protobuf-compiler sudo apt-get install -y --no-install-recommends musl-tools - # libbpf-sys is built with the `vendored` feature (via edriver's - # `static` -> libbpf-rs/vendored). It compiles bundled libbpf, - # elfutils and zlib from source, which needs the autotools stack - # plus flex/bison/gettext/gperf to regenerate elfutils' configure. - sudo apt-get install -y --no-install-recommends \ - pkgconf autoconf automake libtool m4 \ - autopoint gettext gperf flex bison gawk + # libelf-dev: provides libelf.a for libbpf-sys/static link. + # libzstd-dev: Ubuntu 24.04+ system libelf.a is compiled with USE_ZSTD, + # so libelf.a references ZSTD_* symbols. libbpf-sys never emits + # `cargo:rustc-link-lib=zstd`, so we must supply libzstd.a ourselves. + # .cargo/config.toml adds -lzstd via [build].rustflags (host build-script) + # and [target.*-musl].rustflags -Wl,-Bstatic,-lzstd,-Bdynamic (final binary). + sudo apt-get install -y --no-install-recommends pkgconf libelf-dev libzstd-dev # BPF compilation toolchain sudo apt-get install -y --no-install-recommends clang llvm # build-essential occasionally conflicts on ARM via linux-libc-dev. diff --git a/plugins/edriver/.cargo/config.toml b/plugins/edriver/.cargo/config.toml index fb3cbe27..a29763a3 100644 --- a/plugins/edriver/.cargo/config.toml +++ b/plugins/edriver/.cargo/config.toml @@ -3,17 +3,31 @@ LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/ LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib" LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib" -# libbpf-sys vendors and compiles libbpf / elfutils / zlib C sources via the -# `vendored` feature (enabled by edriver's `static` feature). The bundled -# elfutils is configured `--without-zstd`, so no ZSTD_* symbols leak into the -# link line and no rustflags workaround is needed. +# libbpf-sys compiles libbpf C sources using the native gcc. +# musl-gcc is only needed for the final Rust link step (set via [target.*].linker below). CC_x86_64_unknown_linux_musl = "gcc" CC_aarch64_unknown_linux_musl = "gcc" +# libbpf-sys/static links the system libelf.a. On Ubuntu 24.04+ that libelf.a +# is compiled with USE_ZSTD, referencing ZSTD_* symbols that libbpf-sys never +# emits `cargo:rustc-link-lib=zstd` for. We add -lzstd manually: +# +# [build].rustflags applies to build-script binaries (HOST triple), which is +# where libbpf-cargo -> libbpf-sys runs. Dynamic libzstd.so is fine for the +# host build-script executable. +[build] +rustflags = ["-C", "link-arg=-lzstd"] + +# For the final musl binary, libbpf-sys also emits `cargo:rustc-link-lib=static=elf` +# and the same ZSTD symbols need resolving. We force static libzstd here so +# the binary remains self-contained: -Wl,-Bstatic,-lzstd,-Bdynamic forces static +# for zstd only; libzstd.a is supplied by libzstd-dev in CI. +# libbpf-sys already emits cargo:rustc-link-search for the LIBBPF_SYS_LIBRARY_PATH +# dirs above, so the linker can find libzstd.a in e.g. /usr/lib/x86_64-linux-gnu/. [target.x86_64-unknown-linux-musl] linker = "musl-gcc" -rustflags = ["-C", "target-feature=+crt-static"] +rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-Wl,-Bstatic,-lzstd,-Bdynamic"] [target.aarch64-unknown-linux-musl] linker = "musl-gcc" -rustflags = ["-C", "target-feature=+crt-static"] +rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-Wl,-Bstatic,-lzstd,-Bdynamic"] diff --git a/plugins/edriver/Cargo.toml b/plugins/edriver/Cargo.toml index 901cd331..b3f11e84 100644 --- a/plugins/edriver/Cargo.toml +++ b/plugins/edriver/Cargo.toml @@ -7,15 +7,13 @@ description = "Rust version of hades edriver" [features] debug = ["sdk/debug"] -# `vendored` (not just `static`) makes libbpf-sys build its own copy of libbpf, -# elfutils and zlib from source. The bundled elfutils is configured with -# `--without-zstd`, so the resulting libelf.a does NOT reference ZSTD_* symbols. -# Plain `static` would link the system libelf.a which on Ubuntu 24.04+ is built -# with USE_ZSTD and therefore pulls in unresolved ZSTD_* references at link -# time (libbpf-sys never emits `cargo:rustc-link-lib=zstd`). -# `vendored` implies `static-*` for all three native deps, so the final musl -# binary is still fully self-contained. -static = ["libbpf-rs/vendored"] +# The `static` feature chains to libbpf-rs/static → libbpf-sys/static, which +# links the system libelf.a. On Ubuntu 24.04+ that libelf.a is compiled with +# USE_ZSTD and references ZSTD_* symbols. libbpf-sys does NOT emit +# `cargo:rustc-link-lib=zstd`, so we must add `-lzstd` manually in +# .cargo/config.toml ([build].rustflags for host build-scripts, and +# [target.*-musl].rustflags for the final binary). +static = ["libbpf-rs/static"] [dependencies] anyhow = "1.0" @@ -26,17 +24,15 @@ governor = "0.10.4" lazy_static = "1.5.0" twox-hash = "2.1" hex = "0.4" -libbpf-rs = { version = "0.26.2", features = ["vendored"] } +libbpf-rs = { version = "0.26.2", features = ["static"] } bitflags = "2.11.1" moka = { version = "0.12", features = ["sync"] } sdk = { path = "../../SDK/rust" } [build-dependencies] libbpf-cargo = "0.26.2" -# Resolver 2 splits feature sets between normal-deps and build-deps. Without -# this explicit pin, the build-script binary (which uses libbpf-cargo -> -# libbpf-sys) would still link the system libelf.a (Ubuntu 24.04+ built with -# ZSTD), causing "undefined reference to ZSTD_*". Enabling `vendored` here -# makes libbpf-sys compile elfutils with --without-zstd for the host link -# step too, matching the normal-deps tree. -libbpf-sys = { version = "1.7", features = ["vendored"] } +# Resolver 2 splits feature sets for [dependencies] and [build-dependencies]. +# libbpf-cargo → libbpf-sys is compiled for the HOST and also links the +# system libelf.a (USE_ZSTD on Ubuntu 24.04+). We pin `static` here so that +# `-lzstd` from [build].rustflags in .cargo/config.toml resolves those refs. +libbpf-sys = { version = "1.7", features = ["static"] } From 0087fe129895c315707a6cbc49bb4ac808d642e1 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Sat, 23 May 2026 01:17:26 +0800 Subject: [PATCH 34/38] fix(ci-edriver): inline PROTOC export in Build and Test steps GITHUB_ENV-based export is not reliably visible to cargo build-scripts (which run as child processes of cargo). Inline export PROTOC=/usr/bin/protoc directly in the run script for Build and Test steps to guarantee the variable is set before prost-build tries to locate protoc. --- .github/workflows/ci-edriver.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 82e15be5..92ad590a 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -79,22 +79,25 @@ jobs: # build-essential occasionally conflicts on ARM via linux-libc-dev. sudo apt-get install -y --no-install-recommends build-essential || true - - name: Verify toolchain and export PROTOC + - name: Verify toolchain run: | set -euxo pipefail which protoc protoc --version - echo "PROTOC=$(which protoc)" >> "$GITHUB_ENV" - name: Build - run: cd plugins/edriver && make build + run: | + export PROTOC=$(which protoc) + cd plugins/edriver && make build env: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc - name: Test - run: cd plugins/edriver && make test + run: | + export PROTOC=$(which protoc) + cd plugins/edriver && make test env: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} From e76a1b9600f2dcc749dd9989005043623a3a8b99 Mon Sep 17 00:00:00 2001 From: chriskaliX Date: Sat, 23 May 2026 01:48:48 +0800 Subject: [PATCH 35/38] fix(ci-edriver): set RUSTFLAGS env for Build/Test steps [build].rustflags in config.toml is not reliably propagated to build-script binary link steps by Cargo (empirically confirmed: ZSTD_* errors appeared in target/release/build/edriver-*/build_script_build even with [build].rustflags set). RUSTFLAGS env var has the highest priority in Cargo's rustflags chain (above config.toml entries) and is guaranteed to apply to all rustc invocations including build scripts. Effect on each compilation unit: - edriver build-script (HOST gnu): RUSTFLAGS adds -lzstd; libzstd.so found in -L /usr/lib/{arch}-linux-gnu via libbpf-sys cargo:rustc-link-search. Resolved. - edriver final binary (TARGET musl): RUSTFLAGS overrides [target.*-musl].rustflags but musl targets default to crt-static and -static-pie mode, so -lzstd still resolves to libzstd.a from libzstd-dev. Fully static binary preserved. --- .github/workflows/ci-edriver.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci-edriver.yaml b/.github/workflows/ci-edriver.yaml index 92ad590a..cdd0fe69 100644 --- a/.github/workflows/ci-edriver.yaml +++ b/.github/workflows/ci-edriver.yaml @@ -93,6 +93,15 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + # RUSTFLAGS applies to ALL rustc invocations (highest priority, overrides + # config.toml [build].rustflags and [target.*].rustflags). Needed because + # [build].rustflags in config.toml is not reliably applied to build-script + # binaries by Cargo. The -lzstd is required because Ubuntu 24.04 system + # libelf.a (linked by libbpf-sys/static) is compiled with USE_ZSTD but + # libbpf-sys never emits cargo:rustc-link-lib=zstd. + # For the musl final binary, -static-pie (set by Rust for musl targets) + # forces the linker to use libzstd.a from libzstd-dev, keeping it static. + RUSTFLAGS: "-C link-arg=-lzstd" - name: Test run: | @@ -102,3 +111,4 @@ jobs: PLATFORM: ${{ matrix.arch }} LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }} TARGET_CC: gcc + RUSTFLAGS: "-C link-arg=-lzstd" From ee1d0a5ecd3fae33a981e3db7a621670f5086013 Mon Sep 17 00:00:00 2001 From: czy Date: Sat, 23 May 2026 08:15:35 +0800 Subject: [PATCH 36/38] [feat] stabilize windows rust plugin reporting --- README-zh_CN.md | 1 + README.md | 1 + plugins/wdriver-rust/README.md | 66 ------ plugins/{wdriver-rust => wdriver}/Cargo.toml | 5 +- plugins/wdriver/README.md | 58 +++++ plugins/{wdriver-rust => wdriver}/build.rs | 0 .../config/directoryRuleConfig.json | 0 .../config/networkRuleConfig.yaml | 0 .../config/processRuleConfig.json | 0 .../config/registerRuleConfig.json | 0 .../config/registerRuleConfig_.json | 0 .../config/threadRuleConfig.json | 0 .../src/appcore/app_account.rs | 0 .../src/appcore/app_autostart.rs | 20 +- .../src/appcore/app_file.rs | 0 .../src/appcore/app_include.rs | 0 .../src/appcore/app_net.rs | 60 +++--- .../src/appcore/app_process.rs | 18 +- .../src/appcore/app_service_software.rs | 33 ++- .../src/appcore/app_sysinfo.rs | 0 .../src/appcore/etw/etw.rs | 0 .../src/appcore/etw/mod.rs | 0 .../src/appcore/mod.rs | 0 .../src/config/config.rs | 0 .../src/config/mod.rs | 0 .../src/driver/drvmg.rs | 0 .../src/driver/mod.rs | 0 .../src/events/event.rs | 0 .../src/events/mod.rs | 0 .../src/kercore/ark/ark_deviceinfo.rs | 0 .../src/kercore/ark/ark_dpctimer.rs | 0 .../src/kercore/ark/ark_enumcallback.rs | 0 .../src/kercore/ark/ark_fsd.rs | 0 .../src/kercore/ark/ark_idt.rs | 0 .../src/kercore/ark/ark_mouse_keyboard.rs | 0 .../src/kercore/ark/ark_network.rs | 0 .../src/kercore/ark/ark_processinfo.rs | 0 .../src/kercore/ark/ark_ssdt.rs | 0 .../src/kercore/ark/mod.rs | 0 .../src/kercore/mod.rs | 0 .../src/kercore/wfp/datalink.rs | 0 .../src/kercore/wfp/dns.rs | 0 .../src/kercore/wfp/established.rs | 0 .../src/kercore/wfp/mod.rs | 0 .../src/kercore/wfp/tcp.rs | 0 plugins/{wdriver-rust => wdriver}/src/lib.rs | 6 +- plugins/wdriver/src/main.rs | 204 ++++++++++++++++++ plugins/wdriver/src/protocol.rs | 29 +++ plugins/wdriver/src/transport.rs | 60 ++++++ .../{wdriver-rust => wdriver}/src/util/log.rs | 0 .../{wdriver-rust => wdriver}/src/util/mod.rs | 0 .../src/util/util.rs | 0 .../src/util/windows_installed.rs | 0 .../src/util/windows_services.rs | 0 .../src/util/windwos_autostart.rs | 0 .../tests/ts_appcore.rs | 12 +- .../tests/ts_drvmg.rs | 2 +- .../tests/ts_rule.rs | 2 +- .../grpc/handler/windows/200_uprocessinfo.go | 2 +- .../windows/303_uetw_networktabinfo.go | 5 + .../handler/windows/305_uetw_fileioinfo.go | 34 +++ 61 files changed, 473 insertions(+), 145 deletions(-) delete mode 100644 plugins/wdriver-rust/README.md rename plugins/{wdriver-rust => wdriver}/Cargo.toml (94%) create mode 100644 plugins/wdriver/README.md rename plugins/{wdriver-rust => wdriver}/build.rs (100%) rename plugins/{wdriver-rust => wdriver}/config/directoryRuleConfig.json (100%) rename plugins/{wdriver-rust => wdriver}/config/networkRuleConfig.yaml (100%) rename plugins/{wdriver-rust => wdriver}/config/processRuleConfig.json (100%) rename plugins/{wdriver-rust => wdriver}/config/registerRuleConfig.json (100%) rename plugins/{wdriver-rust => wdriver}/config/registerRuleConfig_.json (100%) rename plugins/{wdriver-rust => wdriver}/config/threadRuleConfig.json (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_account.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_autostart.rs (72%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_file.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_include.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_net.rs (62%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_process.rs (65%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_service_software.rs (71%) rename plugins/{wdriver-rust => wdriver}/src/appcore/app_sysinfo.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/etw/etw.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/etw/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/appcore/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/config/config.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/config/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/driver/drvmg.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/driver/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/events/event.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/events/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_deviceinfo.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_dpctimer.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_enumcallback.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_fsd.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_idt.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_mouse_keyboard.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_network.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_processinfo.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/ark_ssdt.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/ark/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/wfp/datalink.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/wfp/dns.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/wfp/established.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/wfp/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/kercore/wfp/tcp.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/lib.rs (72%) create mode 100644 plugins/wdriver/src/main.rs create mode 100644 plugins/wdriver/src/protocol.rs create mode 100644 plugins/wdriver/src/transport.rs rename plugins/{wdriver-rust => wdriver}/src/util/log.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/util/mod.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/util/util.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/util/windows_installed.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/util/windows_services.rs (100%) rename plugins/{wdriver-rust => wdriver}/src/util/windwos_autostart.rs (100%) rename plugins/{wdriver-rust => wdriver}/tests/ts_appcore.rs (84%) rename plugins/{wdriver-rust => wdriver}/tests/ts_drvmg.rs (92%) rename plugins/{wdriver-rust => wdriver}/tests/ts_rule.rs (94%) create mode 100644 server/webconsole/grpc/handler/windows/305_uetw_fileioinfo.go diff --git a/README-zh_CN.md b/README-zh_CN.md index 592641b5..c9237cd1 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -37,6 +37,7 @@ Hades 是一个基于 eBPF 的主机入侵检测系统,同时兼容低版本 - [EDriver](https://github.com/chriskaliX/Hades/tree/main/plugins/edriver) - [Collector](https://github.com/chriskaliX/Hades/tree/main/plugins/collector) - [Eguard](https://github.com/chriskaliX/Hades/tree/main/plugins/eguard) +- [WDriver](https://github.com/chriskaliX/Hades/tree/main/plugins/wdriver) - [NCP](https://github.com/chriskaliX/Hades/tree/main/plugins/ncp) - Scanner - Logger diff --git a/README.md b/README.md index 4b9eaaf2..02e141cf 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Declaration: This project is based on [Tracee](https://github.com/aquasecurity/t - [EDriver](https://github.com/chriskaliX/Hades/tree/main/plugins/edriver) - [Collector](https://github.com/chriskaliX/Hades/tree/main/plugins/collector) - [Eguard](https://github.com/chriskaliX/Hades/tree/main/plugins/eguard) +- [WDriver](https://github.com/chriskaliX/Hades/tree/main/plugins/wdriver) - [NCP](https://github.com/chriskaliX/Hades/tree/main/plugins/ncp) - Scanner - Logger diff --git a/plugins/wdriver-rust/README.md b/plugins/wdriver-rust/README.md deleted file mode 100644 index d94dd6fb..00000000 --- a/plugins/wdriver-rust/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Hades Windows Driver - -wdriver-rsut等同于HadesSvc -``` -HadesSvc - 插件服务 -https://github.com/theSecHunter/Hades-Windows/tree/main/HadSvc -``` -``` -NetDrvlib64.dll - 内核网络 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib -``` - -``` -RuleEnginelib64.dll - 规则引擎 -https://github.com/theSecHunter/Hades-Windows/tree/main/RuleEngineSvc -``` - -``` -SysMonDrvlib64.dll - 内核探针 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrvlib -``` - -``` -SysMonUserlib64.dll - 用户态探针 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmonuserlib -``` - -依赖Driver驱动 -``` -SysMonDrvlib64 -> sysmondriver.sys - 内核驱动 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrv -``` - -``` -NetDrvlib64 -> hadesndr.sys - 网络驱动 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib -``` - -wdriver-rsut目标重构c++组件库HadesSvc,整合NetDrvlib64,RuleEnginelib64,SysMonDrvlib64,SysMonUserlib64动态库能力。 - -``` -config/config.rs -> RuleEnginelib64.dll - 规则引擎 -``` - -``` -kercore/wfp -> NetDrvlib64.dll - 内核网络 -``` - -``` -kercore/ark -> SysMonDrvlib64.dll - 内核探针 -``` - -``` -appcore -> SysMonUserlib64.dll - 用户态探针 -``` - -driver sys不变 -``` -kmonfilter -> sysmondriver.sys - 内核驱动 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/sysmondrv -``` - -``` -knetfilter -> hadesndr.sys - 网络驱动 -https://github.com/theSecHunter/Hades-Windows/tree/main/MonitorEvent/netdrvlib -``` \ No newline at end of file diff --git a/plugins/wdriver-rust/Cargo.toml b/plugins/wdriver/Cargo.toml similarity index 94% rename from plugins/wdriver-rust/Cargo.toml rename to plugins/wdriver/Cargo.toml index ebbda545..b8703fb8 100644 --- a/plugins/wdriver-rust/Cargo.toml +++ b/plugins/wdriver/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wdriver_rs" +name = "wdriver" version = "24.12.4" edition = "2021" @@ -13,6 +13,7 @@ yaml-rust = "0.4" fast_log = "1.5.51" serde = { version = "1.0.211", features = ["derive"] } serde_json = "1.0.132" +prost = "0.13" napi = { version = "2.16.12", default-features = false, features = ["napi4", "serde-json", "tokio_rt"] } napi-derive = "2.16.12" tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } @@ -37,4 +38,4 @@ features = [ ] [profile.test] -debug-assertions = false \ No newline at end of file +debug-assertions = false diff --git a/plugins/wdriver/README.md b/plugins/wdriver/README.md new file mode 100644 index 00000000..928b47ef --- /dev/null +++ b/plugins/wdriver/README.md @@ -0,0 +1,58 @@ +# wdriver + +`wdriver` is the Windows Rust plugin track for Hades. It is intended to align +with the Hades-Windows C++ service/plugin semantics while keeping the transport +protocol unchanged. + +## Current Scope + +- Plugin transport uses Windows stdin/stdout framing: `uint32 little-endian length + protobuf payload`. +- Reports are encoded as `Record { data_type, timestamp, data.fields }`. +- Windows event payload keeps the existing server convention: `fields["data_type"]` and `fields["udata"]`. +- Task acknowledgements use data type `5100` with `token`, `status`, and `msg`. + +## Phase 1 Collection + +The current minimal loop supports server-triggered collection tasks: + +- `200`: process snapshot +- `202`: autorun registry snapshot +- `203`: network socket snapshot +- `207`: local account snapshot +- `208`: installed software snapshot + +The field names intentionally match the existing Hades-Linux Windows handlers +and Hades-Windows C++ reports. Keep the outer protocol stable; future expansion +should add fields inside `udata` instead of changing the transport frame or +top-level `Record` semantics. + +## ETW Direction + +ETW should be implemented as active reporting: after startup or explicit task +enablement, the plugin owns the ETW session and pushes events to stdout without +waiting for a polling task. This should follow the same `Record + udata` shape +used by the C++ path: + +- `300`: process ETW +- `301`: thread ETW +- `302`: image load ETW +- `303`: network ETW +- `304`: registry ETW +- `305`: file I/O ETW + +Keep ETW session start/stop, callback dispatch, and report throttling isolated +from the task collection loop so task handling cannot block active event upload. + +## Relationship To Hades-Windows + +The Rust plugin is not a rewrite of the full Windows C++ service yet. The +long-term goal is to gradually align with these Hades-Windows components: + +- `HadSvc`: service/plugin orchestration and report transport +- `MonitorEvent/sysmonuserlib`: user-mode collection and ETW +- `MonitorEvent/sysmondrvlib`: kernel monitor bridge +- `MonitorEvent/netdrvlib`: kernel network bridge +- `RuleEngineSvc`: rule evaluation and blocking semantics + +Driver and blocking capabilities should be added only after the reporting +semantics are stable and compatible with the server handlers. diff --git a/plugins/wdriver-rust/build.rs b/plugins/wdriver/build.rs similarity index 100% rename from plugins/wdriver-rust/build.rs rename to plugins/wdriver/build.rs diff --git a/plugins/wdriver-rust/config/directoryRuleConfig.json b/plugins/wdriver/config/directoryRuleConfig.json similarity index 100% rename from plugins/wdriver-rust/config/directoryRuleConfig.json rename to plugins/wdriver/config/directoryRuleConfig.json diff --git a/plugins/wdriver-rust/config/networkRuleConfig.yaml b/plugins/wdriver/config/networkRuleConfig.yaml similarity index 100% rename from plugins/wdriver-rust/config/networkRuleConfig.yaml rename to plugins/wdriver/config/networkRuleConfig.yaml diff --git a/plugins/wdriver-rust/config/processRuleConfig.json b/plugins/wdriver/config/processRuleConfig.json similarity index 100% rename from plugins/wdriver-rust/config/processRuleConfig.json rename to plugins/wdriver/config/processRuleConfig.json diff --git a/plugins/wdriver-rust/config/registerRuleConfig.json b/plugins/wdriver/config/registerRuleConfig.json similarity index 100% rename from plugins/wdriver-rust/config/registerRuleConfig.json rename to plugins/wdriver/config/registerRuleConfig.json diff --git a/plugins/wdriver-rust/config/registerRuleConfig_.json b/plugins/wdriver/config/registerRuleConfig_.json similarity index 100% rename from plugins/wdriver-rust/config/registerRuleConfig_.json rename to plugins/wdriver/config/registerRuleConfig_.json diff --git a/plugins/wdriver-rust/config/threadRuleConfig.json b/plugins/wdriver/config/threadRuleConfig.json similarity index 100% rename from plugins/wdriver-rust/config/threadRuleConfig.json rename to plugins/wdriver/config/threadRuleConfig.json diff --git a/plugins/wdriver-rust/src/appcore/app_account.rs b/plugins/wdriver/src/appcore/app_account.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/app_account.rs rename to plugins/wdriver/src/appcore/app_account.rs diff --git a/plugins/wdriver-rust/src/appcore/app_autostart.rs b/plugins/wdriver/src/appcore/app_autostart.rs similarity index 72% rename from plugins/wdriver-rust/src/appcore/app_autostart.rs rename to plugins/wdriver/src/appcore/app_autostart.rs index 0b9f0960..59c51f9a 100644 --- a/plugins/wdriver-rust/src/appcore/app_autostart.rs +++ b/plugins/wdriver/src/appcore/app_autostart.rs @@ -1,5 +1,7 @@ - -use crate::{util::windwos_autostart::App, appcore::app_include::AppRegRunInfo, appcore::app_include::AppTaskSchedulerRunInfo}; +use crate::{ + appcore::app_include::AppRegRunInfo, appcore::app_include::AppTaskSchedulerRunInfo, + util::windwos_autostart::App, +}; pub struct AppAutoStart { astart_register: Vec, @@ -7,11 +9,10 @@ pub struct AppAutoStart { } impl AppAutoStart { - pub fn init() -> bool { let mut astart_register: Vec = vec![]; let mut astart_tasksched: Vec = vec![]; - + let _ = Self::get_astart_register(&mut astart_register); let _ = Self::get_astart_taskschedu(&mut astart_tasksched); @@ -26,8 +27,11 @@ impl AppAutoStart { return true; } - pub fn get_astart_register(astart_register:&mut Vec) -> bool { - let apps = App::list().unwrap(); + pub fn get_astart_register(astart_register: &mut Vec) -> bool { + let apps = match App::list() { + Ok(apps) => apps, + Err(_) => return false, + }; for app in apps { let regrun_ctx = AppRegRunInfo { valuename: app.get_key(), @@ -41,12 +45,10 @@ impl AppAutoStart { return true; } - pub fn get_astart_taskschedu(astart_tasksched:&mut Vec) -> bool { - + pub fn get_astart_taskschedu(astart_tasksched: &mut Vec) -> bool { if astart_tasksched.is_empty() { return false; } return true; } - } diff --git a/plugins/wdriver-rust/src/appcore/app_file.rs b/plugins/wdriver/src/appcore/app_file.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/app_file.rs rename to plugins/wdriver/src/appcore/app_file.rs diff --git a/plugins/wdriver-rust/src/appcore/app_include.rs b/plugins/wdriver/src/appcore/app_include.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/app_include.rs rename to plugins/wdriver/src/appcore/app_include.rs diff --git a/plugins/wdriver-rust/src/appcore/app_net.rs b/plugins/wdriver/src/appcore/app_net.rs similarity index 62% rename from plugins/wdriver-rust/src/appcore/app_net.rs rename to plugins/wdriver/src/appcore/app_net.rs index 55ad7687..0efb986f 100644 --- a/plugins/wdriver-rust/src/appcore/app_net.rs +++ b/plugins/wdriver/src/appcore/app_net.rs @@ -1,15 +1,13 @@ -use std::ops::Deref; - +use crate::appcore::app_include::AppNetWorkInfo; use netstat2::*; use sysinfo::*; -use crate::{appcore::app_include::AppNetWorkInfo}; pub struct AppNetwork { pub network_info: Vec, } impl AppNetwork { - pub fn init() -> bool{ + pub fn init() -> bool { let mut network_info: Vec = vec![]; let bok = Self::get_socket_info(&mut network_info); if false == bok { @@ -22,7 +20,7 @@ impl AppNetwork { return true; } - pub fn get_socket_info(network_info:&mut Vec) -> bool{ + pub fn get_socket_info(network_info: &mut Vec) -> bool { let af_flags: AddressFamilyFlags = AddressFamilyFlags::all(); let proto_flags: ProtocolFlags = ProtocolFlags::all(); let sockets_info = get_sockets_info(af_flags, proto_flags); @@ -36,27 +34,35 @@ impl AppNetwork { for si in sockets_info { let proc_info = si .associated_pids - .into_iter() - .find_map(|pid| sysinfo.process(Pid::from_u32(pid))).unwrap(); - // .map(|p| ProcessInfo::new(&p.name().to_string_lossy(), p.pid().as_u32())) - // .unwrap_or_default(); - let mut cmdline = "".to_string(); - for s in proc_info.cmd() { - if s.is_empty() { - continue; + .iter() + .find_map(|pid| sysinfo.process(Pid::from_u32(*pid))); + let pid = proc_info + .map(|process| process.pid().as_u32()) + .or_else(|| si.associated_pids.first().copied()) + .unwrap_or(0); + let parent_pid = proc_info + .and_then(|process| process.parent()) + .map(|pid| pid.as_u32()) + .unwrap_or(0); + let process_name = proc_info + .map(|process| process.name().to_string_lossy().into_owned()) + .unwrap_or_default(); + let mut cmdline = "".to_string(); + if let Some(proc_info) = proc_info { + for s in proc_info.cmd() { + if s.is_empty() { + continue; + } + cmdline.push_str(s.to_string_lossy().as_ref()); + cmdline.push_str("|"); } - cmdline.push_str(s.clone().into_string().unwrap().as_str()); - cmdline.push_str("|"); } match si.protocol_socket_info { ProtocolSocketInfo::Tcp(tcp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: proc_info.pid().as_u32(), - th32parentprocessid: match proc_info.parent() { - Some(_) => { proc_info.parent().unwrap().as_u32() }, - None => { 0 }, - }, - processname: proc_info.name().to_os_string().into_string().unwrap(), + pid, + th32parentprocessid: parent_pid, + processname: process_name.clone(), cmd: cmdline, protocol: "TCP".to_string(), localaddress: tcp_si.local_addr.to_string(), @@ -69,13 +75,10 @@ impl AppNetwork { } ProtocolSocketInfo::Udp(udp_si) => { let network_ctx: AppNetWorkInfo = AppNetWorkInfo { - pid: proc_info.pid().as_u32(), - th32parentprocessid: match proc_info.parent() { - Some(_) => { proc_info.parent().unwrap().as_u32() }, - None => { 0 }, - }, - processname: proc_info.name().to_os_string().into_string().unwrap(), - cmd: "".to_string(), + pid, + th32parentprocessid: parent_pid, + processname: process_name.clone(), + cmd: cmdline, protocol: "UDP".to_string(), localaddress: udp_si.local_addr.to_string(), remoteaddress: "".to_string(), @@ -94,5 +97,4 @@ impl AppNetwork { } return true; } - } diff --git a/plugins/wdriver-rust/src/appcore/app_process.rs b/plugins/wdriver/src/appcore/app_process.rs similarity index 65% rename from plugins/wdriver-rust/src/appcore/app_process.rs rename to plugins/wdriver/src/appcore/app_process.rs index 023951b5..adeb02f7 100644 --- a/plugins/wdriver-rust/src/appcore/app_process.rs +++ b/plugins/wdriver/src/appcore/app_process.rs @@ -1,6 +1,6 @@ use sysinfo::*; -use crate::{appcore::app_include::AppProcessInfo}; +use crate::appcore::app_include::AppProcessInfo; pub struct AppProcess { pub process_info: Vec, @@ -20,26 +20,23 @@ impl AppProcess { return true; } - pub fn get_process_info(process_info:&mut Vec) -> bool { + pub fn get_process_info(process_info: &mut Vec) -> bool { let mut sysinfo = System::new_all(); sysinfo.refresh_processes(ProcessesToUpdate::All, true); for (pid, process) in sysinfo.processes() { - let mut cmdline = "".to_string(); + let mut cmdline = "".to_string(); for s in process.cmd() { if s.is_empty() { continue; } - cmdline.push_str(s.clone().into_string().unwrap().as_str()); + cmdline.push_str(s.to_string_lossy().as_ref()); cmdline.push_str("|"); } - let process_ctx = AppProcessInfo{ + let process_ctx = AppProcessInfo { pid: pid.as_u32(), - th32parentprocessid: match process.parent() { - Some(_) => { process.parent().unwrap().as_u32() }, - None => { 0 }, - }, - exefile: process.name().to_os_string().into_string().unwrap(), + th32parentprocessid: process.parent().map(|pid| pid.as_u32()).unwrap_or(0), + exefile: process.name().to_string_lossy().into_owned(), priclassbase: process.status().to_string(), threadcount: 0, processfullpath: cmdline, @@ -52,5 +49,4 @@ impl AppProcess { } return true; } - } diff --git a/plugins/wdriver-rust/src/appcore/app_service_software.rs b/plugins/wdriver/src/appcore/app_service_software.rs similarity index 71% rename from plugins/wdriver-rust/src/appcore/app_service_software.rs rename to plugins/wdriver/src/appcore/app_service_software.rs index 5df80b86..4a5ae545 100644 --- a/plugins/wdriver-rust/src/appcore/app_service_software.rs +++ b/plugins/wdriver/src/appcore/app_service_software.rs @@ -1,7 +1,7 @@ -use std::ffi::OsString; -use windows_service::{Result, service_dispatcher, service}; - -use crate::{util::windows_installed::App, appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo}; +use crate::{ + appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo, + util::windows_installed::App, +}; pub struct AppServiceSoftWare { services_info: Vec, @@ -15,7 +15,7 @@ impl AppServiceSoftWare { Self::get_services_info(&mut services_info); Self::get_software_info(&mut software_info); - + Self { services_info: services_info, software_info: software_info, @@ -23,31 +23,31 @@ impl AppServiceSoftWare { return true; } - pub fn get_services_info(services_info:&mut Vec) -> bool { - - + pub fn get_services_info(services_info: &mut Vec) -> bool { if services_info.is_empty() { return false; } return true; } - pub fn get_software_info(software_info:&mut Vec) -> bool { + pub fn get_software_info(software_info: &mut Vec) -> bool { // read uninstall register valuse - let apps = App::list().unwrap(); + let apps = match App::list() { + Ok(apps) => apps, + Err(_) => return false, + }; for app in apps { let mut installpath = "".to_string(); if app.install_path().is_empty() { installpath = app.installlocal_path().into_owned(); - } - else { + } else { installpath = app.install_path().into_owned(); } let software_ctx = AppSoftWareInfo { - name:app.name().into_owned(), - version:app.version().into_owned(), + name: app.name().into_owned(), + version: app.version().into_owned(), helplink: app.helplink().into_owned(), - size:app.size().into_owned(), + size: app.size().into_owned(), insatllpath: installpath, uninstallpath: app.uninstall_path().into_owned(), venrel: app.publisher().into_owned(), @@ -62,5 +62,4 @@ impl AppServiceSoftWare { } return true; } - -} \ No newline at end of file +} diff --git a/plugins/wdriver-rust/src/appcore/app_sysinfo.rs b/plugins/wdriver/src/appcore/app_sysinfo.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/app_sysinfo.rs rename to plugins/wdriver/src/appcore/app_sysinfo.rs diff --git a/plugins/wdriver-rust/src/appcore/etw/etw.rs b/plugins/wdriver/src/appcore/etw/etw.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/etw/etw.rs rename to plugins/wdriver/src/appcore/etw/etw.rs diff --git a/plugins/wdriver-rust/src/appcore/etw/mod.rs b/plugins/wdriver/src/appcore/etw/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/etw/mod.rs rename to plugins/wdriver/src/appcore/etw/mod.rs diff --git a/plugins/wdriver-rust/src/appcore/mod.rs b/plugins/wdriver/src/appcore/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/appcore/mod.rs rename to plugins/wdriver/src/appcore/mod.rs diff --git a/plugins/wdriver-rust/src/config/config.rs b/plugins/wdriver/src/config/config.rs similarity index 100% rename from plugins/wdriver-rust/src/config/config.rs rename to plugins/wdriver/src/config/config.rs diff --git a/plugins/wdriver-rust/src/config/mod.rs b/plugins/wdriver/src/config/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/config/mod.rs rename to plugins/wdriver/src/config/mod.rs diff --git a/plugins/wdriver-rust/src/driver/drvmg.rs b/plugins/wdriver/src/driver/drvmg.rs similarity index 100% rename from plugins/wdriver-rust/src/driver/drvmg.rs rename to plugins/wdriver/src/driver/drvmg.rs diff --git a/plugins/wdriver-rust/src/driver/mod.rs b/plugins/wdriver/src/driver/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/driver/mod.rs rename to plugins/wdriver/src/driver/mod.rs diff --git a/plugins/wdriver-rust/src/events/event.rs b/plugins/wdriver/src/events/event.rs similarity index 100% rename from plugins/wdriver-rust/src/events/event.rs rename to plugins/wdriver/src/events/event.rs diff --git a/plugins/wdriver-rust/src/events/mod.rs b/plugins/wdriver/src/events/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/events/mod.rs rename to plugins/wdriver/src/events/mod.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_deviceinfo.rs b/plugins/wdriver/src/kercore/ark/ark_deviceinfo.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_deviceinfo.rs rename to plugins/wdriver/src/kercore/ark/ark_deviceinfo.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_dpctimer.rs b/plugins/wdriver/src/kercore/ark/ark_dpctimer.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_dpctimer.rs rename to plugins/wdriver/src/kercore/ark/ark_dpctimer.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_enumcallback.rs b/plugins/wdriver/src/kercore/ark/ark_enumcallback.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_enumcallback.rs rename to plugins/wdriver/src/kercore/ark/ark_enumcallback.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_fsd.rs b/plugins/wdriver/src/kercore/ark/ark_fsd.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_fsd.rs rename to plugins/wdriver/src/kercore/ark/ark_fsd.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_idt.rs b/plugins/wdriver/src/kercore/ark/ark_idt.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_idt.rs rename to plugins/wdriver/src/kercore/ark/ark_idt.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_mouse_keyboard.rs b/plugins/wdriver/src/kercore/ark/ark_mouse_keyboard.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_mouse_keyboard.rs rename to plugins/wdriver/src/kercore/ark/ark_mouse_keyboard.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_network.rs b/plugins/wdriver/src/kercore/ark/ark_network.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_network.rs rename to plugins/wdriver/src/kercore/ark/ark_network.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_processinfo.rs b/plugins/wdriver/src/kercore/ark/ark_processinfo.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_processinfo.rs rename to plugins/wdriver/src/kercore/ark/ark_processinfo.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/ark_ssdt.rs b/plugins/wdriver/src/kercore/ark/ark_ssdt.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/ark_ssdt.rs rename to plugins/wdriver/src/kercore/ark/ark_ssdt.rs diff --git a/plugins/wdriver-rust/src/kercore/ark/mod.rs b/plugins/wdriver/src/kercore/ark/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/ark/mod.rs rename to plugins/wdriver/src/kercore/ark/mod.rs diff --git a/plugins/wdriver-rust/src/kercore/mod.rs b/plugins/wdriver/src/kercore/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/mod.rs rename to plugins/wdriver/src/kercore/mod.rs diff --git a/plugins/wdriver-rust/src/kercore/wfp/datalink.rs b/plugins/wdriver/src/kercore/wfp/datalink.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/wfp/datalink.rs rename to plugins/wdriver/src/kercore/wfp/datalink.rs diff --git a/plugins/wdriver-rust/src/kercore/wfp/dns.rs b/plugins/wdriver/src/kercore/wfp/dns.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/wfp/dns.rs rename to plugins/wdriver/src/kercore/wfp/dns.rs diff --git a/plugins/wdriver-rust/src/kercore/wfp/established.rs b/plugins/wdriver/src/kercore/wfp/established.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/wfp/established.rs rename to plugins/wdriver/src/kercore/wfp/established.rs diff --git a/plugins/wdriver-rust/src/kercore/wfp/mod.rs b/plugins/wdriver/src/kercore/wfp/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/wfp/mod.rs rename to plugins/wdriver/src/kercore/wfp/mod.rs diff --git a/plugins/wdriver-rust/src/kercore/wfp/tcp.rs b/plugins/wdriver/src/kercore/wfp/tcp.rs similarity index 100% rename from plugins/wdriver-rust/src/kercore/wfp/tcp.rs rename to plugins/wdriver/src/kercore/wfp/tcp.rs diff --git a/plugins/wdriver-rust/src/lib.rs b/plugins/wdriver/src/lib.rs similarity index 72% rename from plugins/wdriver-rust/src/lib.rs rename to plugins/wdriver/src/lib.rs index a74e6522..6715cbd6 100644 --- a/plugins/wdriver-rust/src/lib.rs +++ b/plugins/wdriver/src/lib.rs @@ -1,6 +1,8 @@ +pub mod appcore; pub mod config; -pub mod events; pub mod driver; -pub mod appcore; +pub mod events; pub mod kercore; +pub mod protocol; +pub mod transport; pub mod util; diff --git a/plugins/wdriver/src/main.rs b/plugins/wdriver/src/main.rs new file mode 100644 index 00000000..cde53c06 --- /dev/null +++ b/plugins/wdriver/src/main.rs @@ -0,0 +1,204 @@ +use std::collections::HashMap; + +use serde_json::{json, Value}; +use wdriver::{ + appcore::{ + app_account::AppAccount, app_autostart::AppAutoStart, app_net::AppNetwork, + app_process::AppProcess, app_service_software::AppServiceSoftWare, + }, + protocol::{Payload, Record, Task}, + transport::{unix_timestamp, Client}, +}; + +fn main() { + let mut client = Client::new(); + loop { + let task = match client.receive_task() { + Ok(task) => task, + Err(_) => break, + }; + + if task.data_type == 0 { + let _ = send_task_ack(&mut client, &task.token, "success", ""); + break; + } + + let result = dispatch_task(&mut client, &task); + match result { + Ok(count) => { + let msg = format!("{} records", count); + let _ = send_task_ack(&mut client, &task.token, "success", &msg); + } + Err(err) => { + let _ = send_task_ack(&mut client, &task.token, "failed", &err); + } + } + } +} + +fn dispatch_task(client: &mut Client, task: &Task) -> Result { + match task.data_type { + 200 => collect_process(client), + 202 => collect_autostart(client), + 203 => collect_network(client), + 207 => collect_account(client), + 208 => collect_software(client), + _ => Err(format!("unsupported task {}", task.data_type)), + } +} + +fn collect_process(client: &mut Client) -> Result { + let mut items = Vec::new(); + if !AppProcess::get_process_info(&mut items) { + return Err("process collection returned no data".to_string()); + } + let mut count = 0; + for item in items { + send_windows_json( + client, + 200, + json!({ + "win_user_process_pid": item.pid.to_string(), + "win_user_process_pribase": item.priclassbase, + "win_user_process_thrcout": item.threadcount.to_string(), + "win_user_process_parenid": item.th32parentprocessid.to_string(), + "win_user_process_Path": item.processfullpath, + "win_user_process_szExeFile": item.exefile, + }), + )?; + count += 1; + } + Ok(count) +} + +fn collect_autostart(client: &mut Client) -> Result { + let mut items = Vec::new(); + if !AppAutoStart::get_astart_register(&mut items) { + return Err("autostart collection returned no data".to_string()); + } + let mut count = 0; + for item in items { + send_windows_json( + client, + 202, + json!({ + "win_user_autorun_flag": "1", + "win_user_autorun_regName": item.valuename, + "win_user_autorun_regKey": item.valuekey, + }), + )?; + count += 1; + } + Ok(count) +} + +fn collect_network(client: &mut Client) -> Result { + let mut items = Vec::new(); + if !AppNetwork::get_socket_info(&mut items) { + return Err("network collection returned no data".to_string()); + } + let mut count = 0; + for item in items { + let is_tcp = item.protocol.eq_ignore_ascii_case("TCP"); + let src = format!("{}:{}", item.localaddress, item.localport); + let dst = if is_tcp { + format!("{}:{}", item.remoteaddress, item.remoteport) + } else { + String::new() + }; + send_windows_json( + client, + 203, + json!({ + "win_user_net_flag": if is_tcp { "1" } else { "2" }, + "win_user_net_src": src, + "win_user_net_dst": dst, + "win_user_net_status": item.state, + "win_user_net_pid": item.pid.to_string(), + }), + )?; + count += 1; + } + Ok(count) +} + +fn collect_account(client: &mut Client) -> Result { + let mut items = Vec::new(); + if !AppAccount::get_account_info(&mut items) { + return Err("account collection returned no data".to_string()); + } + let mut count = 0; + for item in items { + send_windows_json( + client, + 207, + json!({ + "win_user_sysuser_user": item.serveruser, + "win_user_sysuser_name": item.servername, + "win_user_sysuser_sid": item.serverusid, + "win_user_sysuser_flag": item.serverflag.to_string(), + }), + )?; + count += 1; + } + Ok(count) +} + +fn collect_software(client: &mut Client) -> Result { + let mut items = Vec::new(); + if !AppServiceSoftWare::get_software_info(&mut items) { + return Err("software collection returned no data".to_string()); + } + let mut count = 0; + for item in items { + send_windows_json( + client, + 208, + json!({ + "win_user_softwareserver_flag": "2", + "win_user_software_lpsName": item.name, + "win_user_software_Size": item.size, + "win_user_software_Ver": item.version, + "win_user_software_installpath": item.insatllpath, + "win_user_software_uninstallpath": item.uninstallpath, + "win_user_software_data": "", + "win_user_software_venrel": item.venrel, + }), + )?; + count += 1; + } + Ok(count) +} + +fn send_windows_json(client: &mut Client, data_type: i32, value: Value) -> Result<(), String> { + let udata = serde_json::to_string(&value).map_err(|err| err.to_string())?; + let rec = Record { + data_type, + timestamp: unix_timestamp(), + data: Some(Payload { + fields: HashMap::from([ + ("data_type".to_string(), data_type.to_string()), + ("udata".to_string(), udata), + ]), + }), + }; + client.send_record(&rec).map_err(|err| err.to_string()) +} + +fn send_task_ack(client: &mut Client, token: &str, status: &str, msg: &str) -> Result<(), String> { + if token.is_empty() { + return Ok(()); + } + let rec = Record { + data_type: 5100, + timestamp: unix_timestamp(), + data: Some(Payload { + fields: HashMap::from([ + ("token".to_string(), token.to_string()), + ("status".to_string(), status.to_string()), + ("msg".to_string(), msg.to_string()), + ]), + }), + }; + client.send_record(&rec).map_err(|err| err.to_string()) +} diff --git a/plugins/wdriver/src/protocol.rs b/plugins/wdriver/src/protocol.rs new file mode 100644 index 00000000..b12cae9d --- /dev/null +++ b/plugins/wdriver/src/protocol.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; + +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Record { + #[prost(int32, tag = "1")] + pub data_type: i32, + #[prost(int64, tag = "2")] + pub timestamp: i64, + #[prost(message, optional, tag = "3")] + pub data: Option, +} + +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Payload { + #[prost(map = "string, string", tag = "1")] + pub fields: HashMap, +} + +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Task { + #[prost(int32, tag = "1")] + pub data_type: i32, + #[prost(string, tag = "2")] + pub object_name: String, + #[prost(string, tag = "3")] + pub data: String, + #[prost(string, tag = "4")] + pub token: String, +} diff --git a/plugins/wdriver/src/transport.rs b/plugins/wdriver/src/transport.rs new file mode 100644 index 00000000..0246f6a2 --- /dev/null +++ b/plugins/wdriver/src/transport.rs @@ -0,0 +1,60 @@ +use std::{ + io::{self, BufReader, BufWriter, Read, Write}, + time::{SystemTime, UNIX_EPOCH}, +}; + +use prost::Message; + +use crate::protocol::{Record, Task}; + +const MAX_FRAME_SIZE: usize = 1024 * 1024; + +pub struct Client { + reader: BufReader, + writer: BufWriter, +} + +impl Client { + pub fn new() -> Self { + Self { + reader: BufReader::with_capacity(1024 * 1024, io::stdin()), + writer: BufWriter::with_capacity(512 * 1024, io::stdout()), + } + } + + pub fn send_record(&mut self, rec: &Record) -> io::Result<()> { + let payload = rec.encode_to_vec(); + if payload.len() > MAX_FRAME_SIZE { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "record frame too large", + )); + } + self.writer + .write_all(&(payload.len() as u32).to_le_bytes())?; + self.writer.write_all(&payload)?; + self.writer.flush() + } + + pub fn receive_task(&mut self) -> io::Result { + let mut len = [0u8; 4]; + self.reader.read_exact(&mut len)?; + let len = u32::from_le_bytes(len) as usize; + if len == 0 || len > MAX_FRAME_SIZE { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid task frame size", + )); + } + let mut buf = vec![0u8; len]; + self.reader.read_exact(&mut buf)?; + Task::decode(buf.as_slice()).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) + } +} + +pub fn unix_timestamp() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_secs() as i64) + .unwrap_or(0) +} diff --git a/plugins/wdriver-rust/src/util/log.rs b/plugins/wdriver/src/util/log.rs similarity index 100% rename from plugins/wdriver-rust/src/util/log.rs rename to plugins/wdriver/src/util/log.rs diff --git a/plugins/wdriver-rust/src/util/mod.rs b/plugins/wdriver/src/util/mod.rs similarity index 100% rename from plugins/wdriver-rust/src/util/mod.rs rename to plugins/wdriver/src/util/mod.rs diff --git a/plugins/wdriver-rust/src/util/util.rs b/plugins/wdriver/src/util/util.rs similarity index 100% rename from plugins/wdriver-rust/src/util/util.rs rename to plugins/wdriver/src/util/util.rs diff --git a/plugins/wdriver-rust/src/util/windows_installed.rs b/plugins/wdriver/src/util/windows_installed.rs similarity index 100% rename from plugins/wdriver-rust/src/util/windows_installed.rs rename to plugins/wdriver/src/util/windows_installed.rs diff --git a/plugins/wdriver-rust/src/util/windows_services.rs b/plugins/wdriver/src/util/windows_services.rs similarity index 100% rename from plugins/wdriver-rust/src/util/windows_services.rs rename to plugins/wdriver/src/util/windows_services.rs diff --git a/plugins/wdriver-rust/src/util/windwos_autostart.rs b/plugins/wdriver/src/util/windwos_autostart.rs similarity index 100% rename from plugins/wdriver-rust/src/util/windwos_autostart.rs rename to plugins/wdriver/src/util/windwos_autostart.rs diff --git a/plugins/wdriver-rust/tests/ts_appcore.rs b/plugins/wdriver/tests/ts_appcore.rs similarity index 84% rename from plugins/wdriver-rust/tests/ts_appcore.rs rename to plugins/wdriver/tests/ts_appcore.rs index 57a36e2b..b82f0c89 100644 --- a/plugins/wdriver-rust/tests/ts_appcore.rs +++ b/plugins/wdriver/tests/ts_appcore.rs @@ -1,9 +1,9 @@ -use wdriver_rs::appcore::app_account; -use wdriver_rs::appcore::app_autostart; -use wdriver_rs::appcore::app_include; -use wdriver_rs::appcore::app_net; -use wdriver_rs::appcore::app_process; -use wdriver_rs::appcore::app_service_software; +use wdriver::appcore::app_account; +use wdriver::appcore::app_autostart; +use wdriver::appcore::app_include; +use wdriver::appcore::app_net; +use wdriver::appcore::app_process; +use wdriver::appcore::app_service_software; #[test] pub fn unit_test_account() { diff --git a/plugins/wdriver-rust/tests/ts_drvmg.rs b/plugins/wdriver/tests/ts_drvmg.rs similarity index 92% rename from plugins/wdriver-rust/tests/ts_drvmg.rs rename to plugins/wdriver/tests/ts_drvmg.rs index f8cdea19..78bcd72b 100644 --- a/plugins/wdriver-rust/tests/ts_drvmg.rs +++ b/plugins/wdriver/tests/ts_drvmg.rs @@ -1,4 +1,4 @@ -use wdriver_rs::driver::drvmg::*; +use wdriver::driver::drvmg::*; #[test] // 驱动启动状态 diff --git a/plugins/wdriver-rust/tests/ts_rule.rs b/plugins/wdriver/tests/ts_rule.rs similarity index 94% rename from plugins/wdriver-rust/tests/ts_rule.rs rename to plugins/wdriver/tests/ts_rule.rs index f311bd14..7897dea1 100644 --- a/plugins/wdriver-rust/tests/ts_rule.rs +++ b/plugins/wdriver/tests/ts_rule.rs @@ -14,7 +14,7 @@ use tokio::{ task::JoinHandle, }; -use wdriver_rs::config::config::*; +use wdriver::config::config::*; #[test] pub fn unit_get_dns_rule() { diff --git a/server/webconsole/grpc/handler/windows/200_uprocessinfo.go b/server/webconsole/grpc/handler/windows/200_uprocessinfo.go index 5c593872..7144d4c7 100644 --- a/server/webconsole/grpc/handler/windows/200_uprocessinfo.go +++ b/server/webconsole/grpc/handler/windows/200_uprocessinfo.go @@ -8,7 +8,7 @@ import ( ) type UProcessInfo struct { - Win_User_Process_Pid string `json:"win_ser_process_pid"` + Win_User_Process_Pid string `json:"win_user_process_pid"` Win_User_Process_Pribase string `json:"win_user_process_pribase"` Win_User_Process_Thrcout string `json:"win_user_process_thrcout"` Win_User_Process_Parenid string `json:"win_user_process_parenid"` diff --git a/server/webconsole/grpc/handler/windows/303_uetw_networktabinfo.go b/server/webconsole/grpc/handler/windows/303_uetw_networktabinfo.go index 03ff1ddc..94922be1 100644 --- a/server/webconsole/grpc/handler/windows/303_uetw_networktabinfo.go +++ b/server/webconsole/grpc/handler/windows/303_uetw_networktabinfo.go @@ -2,6 +2,7 @@ package windows import ( "encoding/json" + "hboat/grpc/handler" "hboat/grpc/transfer/pool" pb "hboat/grpc/transfer/proto" ) @@ -27,3 +28,7 @@ func (c *UEtwNetWorkTabinfo) Handle(m map[string]string, req *pb.RawData, conn * data := m["udata"] return json.Unmarshal([]byte(data), c) } + +func init() { + handler.RegistEvent(&UEtwNetWorkTabinfo{}) +} diff --git a/server/webconsole/grpc/handler/windows/305_uetw_fileioinfo.go b/server/webconsole/grpc/handler/windows/305_uetw_fileioinfo.go new file mode 100644 index 00000000..19d95a99 --- /dev/null +++ b/server/webconsole/grpc/handler/windows/305_uetw_fileioinfo.go @@ -0,0 +1,34 @@ +package windows + +import ( + "encoding/json" + "hboat/grpc/handler" + "hboat/grpc/transfer/pool" + pb "hboat/grpc/transfer/proto" +) + +type UEtwFileIoInfo struct { + Win_Etw_fileio_EventName string `json:"win_etw_fileio_eventname"` + Win_Etw_fileio_FilePath string `json:"win_etw_fileio_FilePath"` + Win_Etw_fileio_FileName string `json:"win_etw_fileio_FileName"` + Win_Etw_fileio_Tid string `json:"win_etw_fileio_Tid"` + Win_Etw_fileio_FileAttributes string `json:"win_etw_fileio_FileAttributes"` + Win_Etw_fileio_CreateOptions string `json:"win_etw_fileio_CreateOptions"` + Win_Etw_fileio_ShareAccess string `json:"win_etw_fileio_ShareAccess"` + Win_Etw_fileio_Offset string `json:"win_etw_fileio_Offset"` + Win_Etw_fileio_FileKey string `json:"win_etw_fileio_FileKey"` + Win_Etw_fileio_FileObject string `json:"win_etw_fileio_FileObject"` +} + +func (k *UEtwFileIoInfo) ID() int32 { return 305 } + +func (k *UEtwFileIoInfo) Name() string { return "user_etw_fileioinfo" } + +func (c *UEtwFileIoInfo) Handle(m map[string]string, req *pb.RawData, conn *pool.Connection) error { + data := m["udata"] + return json.Unmarshal([]byte(data), c) +} + +func init() { + handler.RegistEvent(&UEtwFileIoInfo{}) +} From d85baacbeac78f5fff981ce1d61a92b24bb357e9 Mon Sep 17 00:00:00 2001 From: czy Date: Sat, 23 May 2026 08:47:21 +0800 Subject: [PATCH 37/38] [feat] clean windows rust plugin builds --- plugins/wdriver/README.md | 65 +++++++++++++------ plugins/wdriver/rust-toolchain.toml | 4 ++ plugins/wdriver/src/appcore/app_account.rs | 8 +-- plugins/wdriver/src/appcore/app_autostart.rs | 4 +- plugins/wdriver/src/appcore/app_include.rs | 3 - plugins/wdriver/src/appcore/app_process.rs | 4 +- .../src/appcore/app_service_software.rs | 13 ++-- plugins/wdriver/src/appcore/app_sysinfo.rs | 18 ++--- plugins/wdriver/src/config/config.rs | 36 ++++------ plugins/wdriver/src/driver/drvmg.rs | 6 +- plugins/wdriver/src/events/event.rs | 4 +- plugins/wdriver/src/util/windows_installed.rs | 22 +++---- plugins/wdriver/src/util/windows_services.rs | 10 +-- plugins/wdriver/src/util/windwos_autostart.rs | 4 +- 14 files changed, 101 insertions(+), 100 deletions(-) create mode 100644 plugins/wdriver/rust-toolchain.toml diff --git a/plugins/wdriver/README.md b/plugins/wdriver/README.md index 928b47ef..4f37ab41 100644 --- a/plugins/wdriver/README.md +++ b/plugins/wdriver/README.md @@ -4,55 +4,80 @@ with the Hades-Windows C++ service/plugin semantics while keeping the transport protocol unchanged. -## Current Scope +`wdriver` 是 Hades 的 Windows Rust 插件方向,用于逐步对齐 +Hades-Windows C++ 服务/插件语义,同时保持现有传输协议不变。 + +## Current Scope / 当前范围 - Plugin transport uses Windows stdin/stdout framing: `uint32 little-endian length + protobuf payload`. +- 插件传输使用 Windows stdin/stdout 帧格式:`uint32 小端长度 + protobuf payload`。 - Reports are encoded as `Record { data_type, timestamp, data.fields }`. +- 上报数据编码为 `Record { data_type, timestamp, data.fields }`。 - Windows event payload keeps the existing server convention: `fields["data_type"]` and `fields["udata"]`. +- Windows 事件载荷保持 server 现有约定:`fields["data_type"]` 和 `fields["udata"]`。 - Task acknowledgements use data type `5100` with `token`, `status`, and `msg`. +- 任务回执使用数据类型 `5100`,字段为 `token`、`status`、`msg`。 -## Phase 1 Collection +## Phase 1 Collection / 一期采集 The current minimal loop supports server-triggered collection tasks: -- `200`: process snapshot -- `202`: autorun registry snapshot -- `203`: network socket snapshot -- `207`: local account snapshot -- `208`: installed software snapshot +当前最小闭环支持 server 下发触发的采集任务: + +- `200`: process snapshot / 进程快照 +- `202`: autorun registry snapshot / 自启动注册表快照 +- `203`: network socket snapshot / 网络连接快照 +- `207`: local account snapshot / 本地账户快照 +- `208`: installed software snapshot / 已安装软件快照 The field names intentionally match the existing Hades-Linux Windows handlers and Hades-Windows C++ reports. Keep the outer protocol stable; future expansion should add fields inside `udata` instead of changing the transport frame or top-level `Record` semantics. -## ETW Direction +字段名刻意保持与 Hades-Linux Windows handler 和 Hades-Windows C++ 上报一致。 +外层协议需要保持稳定;后续扩展优先在 `udata` 内增加字段,不改变传输帧或 +顶层 `Record` 语义。 + +## ETW Direction / ETW 方向 ETW should be implemented as active reporting: after startup or explicit task enablement, the plugin owns the ETW session and pushes events to stdout without waiting for a polling task. This should follow the same `Record + udata` shape used by the C++ path: -- `300`: process ETW -- `301`: thread ETW -- `302`: image load ETW -- `303`: network ETW -- `304`: registry ETW -- `305`: file I/O ETW +ETW 应按主动上报实现:插件启动或收到显式启用任务后,由插件持有 ETW session, +并主动向 stdout 推送事件,不依赖轮询任务。上报结构应继续沿用 C++ 链路的 +`Record + udata` 形态: + +- `300`: process ETW / 进程 ETW +- `301`: thread ETW / 线程 ETW +- `302`: image load ETW / 镜像加载 ETW +- `303`: network ETW / 网络 ETW +- `304`: registry ETW / 注册表 ETW +- `305`: file I/O ETW / 文件 I/O ETW Keep ETW session start/stop, callback dispatch, and report throttling isolated from the task collection loop so task handling cannot block active event upload. -## Relationship To Hades-Windows +ETW session 启停、回调分发、上报限速应与任务采集循环隔离,避免任务处理阻塞 +主动事件上报。 + +## Relationship To Hades-Windows / 与 Hades-Windows 的关系 The Rust plugin is not a rewrite of the full Windows C++ service yet. The long-term goal is to gradually align with these Hades-Windows components: -- `HadSvc`: service/plugin orchestration and report transport -- `MonitorEvent/sysmonuserlib`: user-mode collection and ETW -- `MonitorEvent/sysmondrvlib`: kernel monitor bridge -- `MonitorEvent/netdrvlib`: kernel network bridge -- `RuleEngineSvc`: rule evaluation and blocking semantics +当前 Rust 插件还不是完整 Windows C++ 服务的重写版本。长期目标是逐步对齐以下 +Hades-Windows 组件能力: + +- `HadSvc`: service/plugin orchestration and report transport / 服务、插件编排与上报传输 +- `MonitorEvent/sysmonuserlib`: user-mode collection and ETW / 用户态采集与 ETW +- `MonitorEvent/sysmondrvlib`: kernel monitor bridge / 内核监控桥接 +- `MonitorEvent/netdrvlib`: kernel network bridge / 内核网络桥接 +- `RuleEngineSvc`: rule evaluation and blocking semantics / 规则评估与阻断语义 Driver and blocking capabilities should be added only after the reporting semantics are stable and compatible with the server handlers. + +驱动和阻断能力应在上报语义稳定、且与 server handler 兼容后再逐步补齐。 diff --git a/plugins/wdriver/rust-toolchain.toml b/plugins/wdriver/rust-toolchain.toml new file mode 100644 index 00000000..53d10127 --- /dev/null +++ b/plugins/wdriver/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +#channel = "1.77.0" +channel = "stable" +components = ["rustfmt", "clippy"] \ No newline at end of file diff --git a/plugins/wdriver/src/appcore/app_account.rs b/plugins/wdriver/src/appcore/app_account.rs index f24fba61..0aa78d9e 100644 --- a/plugins/wdriver/src/appcore/app_account.rs +++ b/plugins/wdriver/src/appcore/app_account.rs @@ -1,6 +1,6 @@ use sysinfo::Users; -use crate::{appcore::app_include::AppAccountInfo}; +use crate::appcore::app_include::AppAccountInfo; pub struct AppAccount { pub account_info: Vec, @@ -9,8 +9,8 @@ pub struct AppAccount { impl AppAccount { pub fn init() -> bool { let mut account_info: Vec = vec![]; - let bOk = Self::get_account_info(&mut account_info); - if false == bOk { + let b_ok = Self::get_account_info(&mut account_info); + if false == b_ok { return false; } @@ -37,4 +37,4 @@ impl AppAccount { return true; } -} \ No newline at end of file +} diff --git a/plugins/wdriver/src/appcore/app_autostart.rs b/plugins/wdriver/src/appcore/app_autostart.rs index 59c51f9a..f405727b 100644 --- a/plugins/wdriver/src/appcore/app_autostart.rs +++ b/plugins/wdriver/src/appcore/app_autostart.rs @@ -4,8 +4,8 @@ use crate::{ }; pub struct AppAutoStart { - astart_register: Vec, - astart_tasksched: Vec, + pub astart_register: Vec, + pub astart_tasksched: Vec, } impl AppAutoStart { diff --git a/plugins/wdriver/src/appcore/app_include.rs b/plugins/wdriver/src/appcore/app_include.rs index 031b23ff..1dd7cde0 100644 --- a/plugins/wdriver/src/appcore/app_include.rs +++ b/plugins/wdriver/src/appcore/app_include.rs @@ -1,5 +1,3 @@ -use serde::{Deserialize, Serialize}; - // account pub struct AppAccountInfo { pub serveruser: String, @@ -87,4 +85,3 @@ pub struct AppServiceInfo { pub description: String, pub currentstate: String, } - diff --git a/plugins/wdriver/src/appcore/app_process.rs b/plugins/wdriver/src/appcore/app_process.rs index adeb02f7..f51308e2 100644 --- a/plugins/wdriver/src/appcore/app_process.rs +++ b/plugins/wdriver/src/appcore/app_process.rs @@ -9,8 +9,8 @@ pub struct AppProcess { impl AppProcess { pub fn init() -> bool { let mut process_info: Vec = vec![]; - let bOk = Self::get_process_info(&mut process_info); - if false == bOk { + let b_ok = Self::get_process_info(&mut process_info); + if false == b_ok { return false; } diff --git a/plugins/wdriver/src/appcore/app_service_software.rs b/plugins/wdriver/src/appcore/app_service_software.rs index 4a5ae545..99105a3b 100644 --- a/plugins/wdriver/src/appcore/app_service_software.rs +++ b/plugins/wdriver/src/appcore/app_service_software.rs @@ -4,8 +4,8 @@ use crate::{ }; pub struct AppServiceSoftWare { - services_info: Vec, - software_info: Vec, + pub services_info: Vec, + pub software_info: Vec, } impl AppServiceSoftWare { @@ -37,12 +37,11 @@ impl AppServiceSoftWare { Err(_) => return false, }; for app in apps { - let mut installpath = "".to_string(); - if app.install_path().is_empty() { - installpath = app.installlocal_path().into_owned(); + let installpath = if app.install_path().is_empty() { + app.installlocal_path().into_owned() } else { - installpath = app.install_path().into_owned(); - } + app.install_path().into_owned() + }; let software_ctx = AppSoftWareInfo { name: app.name().into_owned(), version: app.version().into_owned(), diff --git a/plugins/wdriver/src/appcore/app_sysinfo.rs b/plugins/wdriver/src/appcore/app_sysinfo.rs index e6b27459..a652579b 100644 --- a/plugins/wdriver/src/appcore/app_sysinfo.rs +++ b/plugins/wdriver/src/appcore/app_sysinfo.rs @@ -1,12 +1,12 @@ pub struct AppSysInfo { - cpu: String, - name: String, - os_version: String, - display_card: Vec, - camera: Vec, - bluetooth: Vec, - voice: Vec, - microphone: Vec + pub cpu: String, + pub name: String, + pub os_version: String, + pub display_card: Vec, + pub camera: Vec, + pub bluetooth: Vec, + pub voice: Vec, + pub microphone: Vec } impl AppSysInfo { @@ -47,4 +47,4 @@ impl AppSysInfo { } -} \ No newline at end of file +} diff --git a/plugins/wdriver/src/config/config.rs b/plugins/wdriver/src/config/config.rs index e0611564..6416cd32 100644 --- a/plugins/wdriver/src/config/config.rs +++ b/plugins/wdriver/src/config/config.rs @@ -1,42 +1,31 @@ extern crate yaml_rust; -use napi_derive::napi; -use napi::threadsafe_function::ThreadsafeFunction; use serde::{Deserialize, Serialize}; -use std::{ - fs, - io::Read, - path::PathBuf, - ptr::null, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, -}; -use yaml_rust::{YamlEmitter, YamlLoader}; +use std::{fs, path::PathBuf, ptr::null}; +use yaml_rust::YamlLoader; use crate::{util::log::init_log}; pub struct RuleImpl { // dns - rule_dns: Vec, + pub rule_dns: Vec, // redirect - rule_redirect: Vec, + pub rule_redirect: Vec, // transport - rule_transport: Vec, + pub rule_transport: Vec, // directory - rule_directory: Vec, + pub rule_directory: Vec, // process - rule_process: RuleProcess, + pub rule_process: RuleProcess, // thread - rule_thread: RuleThread, + pub rule_thread: RuleThread, // register - rule_register: Vec, + pub rule_register: Vec, } impl RuleImpl { @@ -180,7 +169,7 @@ impl RuleImpl { } // Analyze dns rule - pub fn get_dns_rule(file_path: String, _data: &mut String, rule_dns: &mut Vec) -> bool { + pub fn get_dns_rule(file_path: String, _data: &mut String, _rule_dns: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -217,7 +206,7 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn get_redirect_rule(file_path: String, _data: &mut String, rule_redirect: &mut Vec) -> bool { + pub fn get_redirect_rule(file_path: String, _data: &mut String, _rule_redirect: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -238,7 +227,7 @@ impl RuleImpl { } // Analyze transport layer rule - pub fn get_transport_rule(file_path: String, _data: &mut String, rule_transport: &mut Vec) -> bool { + pub fn get_transport_rule(file_path: String, _data: &mut String, _rule_transport: &mut Vec) -> bool { if file_path.is_empty() { return false; } @@ -417,4 +406,3 @@ pub struct RuleResgiter { #[serde(rename = "permissions")] pub register_permiss: String, } - diff --git a/plugins/wdriver/src/driver/drvmg.rs b/plugins/wdriver/src/driver/drvmg.rs index 300d04dc..f3cfecbf 100644 --- a/plugins/wdriver/src/driver/drvmg.rs +++ b/plugins/wdriver/src/driver/drvmg.rs @@ -1,9 +1,7 @@ -use std::{fs, io::Read, path::PathBuf, ptr::{null, null_mut}, result}; +use std::ptr::null_mut; use windows::{ core::*, - Win32::System::IO::*, Win32::Foundation::*, - Win32::System::Threading::*, Win32::Storage::FileSystem::*, }; @@ -17,7 +15,7 @@ impl DrivenManageImpl { } // Chekcout Driver Status - pub fn get_driver_stu(driver_name: String) -> bool { + pub fn get_driver_stu(_driver_name: String) -> bool { return true; } diff --git a/plugins/wdriver/src/events/event.rs b/plugins/wdriver/src/events/event.rs index 13c384f1..426fd76a 100644 --- a/plugins/wdriver/src/events/event.rs +++ b/plugins/wdriver/src/events/event.rs @@ -4,7 +4,7 @@ impl Event { // Handle Message - fn handle_msg_notify() -> bool { + pub fn handle_msg_notify() -> bool { return true; } @@ -13,4 +13,4 @@ impl Event { return true; } -} \ No newline at end of file +} diff --git a/plugins/wdriver/src/util/windows_installed.rs b/plugins/wdriver/src/util/windows_installed.rs index 065a7fa6..9552c901 100644 --- a/plugins/wdriver/src/util/windows_installed.rs +++ b/plugins/wdriver/src/util/windows_installed.rs @@ -36,40 +36,40 @@ impl AppList { } } impl App { - fn get_value(&self, name: &str) -> Cow { + fn get_value(&self, name: &str) -> Cow<'_, str> { self.reg .get_value::(name) .map(Cow::Owned) .unwrap_or_else(|_| Cow::Borrowed("")) } - pub fn name(&self) -> Cow { + pub fn name(&self) -> Cow<'_, str> { self.get_value("DisplayName") } - pub fn icon(&self) -> Cow { + pub fn icon(&self) -> Cow<'_, str> { self.get_value("DisplayIcon") } - pub fn publisher(&self) -> Cow { + pub fn publisher(&self) -> Cow<'_, str> { self.get_value("Publisher") } - pub fn version(&self) -> Cow { + pub fn version(&self) -> Cow<'_, str> { self.get_value("DisplayVersion") } - pub fn size(&self) -> Cow { + pub fn size(&self) -> Cow<'_, str> { self.get_value("Size") } - pub fn helplink(&self) -> Cow { + pub fn helplink(&self) -> Cow<'_, str> { self.get_value("HelpLink") } - pub fn install_path(&self) -> Cow { + pub fn install_path(&self) -> Cow<'_, str> { self.get_value("InstallLocation") } - pub fn installlocal_path(&self) -> Cow { + pub fn installlocal_path(&self) -> Cow<'_, str> { self.get_value("InstallLocation") } - pub fn uninstall_path(&self) -> Cow { + pub fn uninstall_path(&self) -> Cow<'_, str> { self.get_value("UninstallString") } - pub fn dump(&self) -> Cow { + pub fn dump(&self) -> Cow<'_, str> { self.reg .enum_values() .map(|r| { diff --git a/plugins/wdriver/src/util/windows_services.rs b/plugins/wdriver/src/util/windows_services.rs index 9836ecd3..f766f573 100644 --- a/plugins/wdriver/src/util/windows_services.rs +++ b/plugins/wdriver/src/util/windows_services.rs @@ -1,11 +1,3 @@ -use windows::{ - Win32::Foundation::*, - Win32::System::Threading::*, - Win32::System::Services::*, -}; - -use std::ptr::{null_mut}; - pub struct Service; impl Service { @@ -103,4 +95,4 @@ impl Service { // println!("bytes needed: {}",bytesneeded ); // */ } -} \ No newline at end of file +} diff --git a/plugins/wdriver/src/util/windwos_autostart.rs b/plugins/wdriver/src/util/windwos_autostart.rs index 29ad2927..270105f3 100644 --- a/plugins/wdriver/src/util/windwos_autostart.rs +++ b/plugins/wdriver/src/util/windwos_autostart.rs @@ -1,6 +1,4 @@ -use std::borrow::Cow; use std::error::Error; -use std::fmt::Debug; use winreg::enums::*; use winreg::reg_key::RegKey; use winreg::reg_value::RegValue; @@ -93,4 +91,4 @@ impl App { Ok(chain) } -} \ No newline at end of file +} From 6030cab8848f6848f820df971debce158beef8b8 Mon Sep 17 00:00:00 2001 From: czy Date: Sat, 23 May 2026 09:40:25 +0800 Subject: [PATCH 38/38] [feat] align wdriver autorun and etw reporting --- plugins/wdriver/Cargo.toml | 9 + plugins/wdriver/document/REPORTING_MATRIX.md | 182 +++++ plugins/wdriver/src/appcore/app_account.rs | 5 +- plugins/wdriver/src/appcore/app_autostart.rs | 17 +- plugins/wdriver/src/appcore/app_file.rs | 153 ++++- plugins/wdriver/src/appcore/app_include.rs | 24 +- .../src/appcore/app_service_software.rs | 159 ++++- plugins/wdriver/src/appcore/app_sysinfo.rs | 40 +- plugins/wdriver/src/appcore/etw/etw.rs | 622 +++++++++++++++++- plugins/wdriver/src/appcore/etw/mod.rs | 2 +- plugins/wdriver/src/appcore/mod.rs | 3 +- plugins/wdriver/src/config/config.rs | 68 +- plugins/wdriver/src/config/mod.rs | 2 +- plugins/wdriver/src/driver/drvmg.rs | 23 +- plugins/wdriver/src/driver/mod.rs | 2 +- plugins/wdriver/src/events/event.rs | 3 - plugins/wdriver/src/events/mod.rs | 2 +- plugins/wdriver/src/kercore/mod.rs | 1 + plugins/wdriver/src/main.rs | 140 +++- plugins/wdriver/src/transport.rs | 50 +- plugins/wdriver/src/util/log.rs | 8 +- plugins/wdriver/src/util/mod.rs | 4 +- plugins/wdriver/src/util/util.rs | 1 + plugins/wdriver/src/util/windows_services.rs | 69 +- plugins/wdriver/src/util/windwos_autostart.rs | 155 ++++- plugins/wdriver/tests/common/mod.rs | 146 ++++ plugins/wdriver/tests/ts_appcore.rs | 122 ++-- plugins/wdriver/tests/ts_protocol.rs | 325 +++++++++ 28 files changed, 2138 insertions(+), 199 deletions(-) create mode 100644 plugins/wdriver/document/REPORTING_MATRIX.md create mode 100644 plugins/wdriver/tests/common/mod.rs create mode 100644 plugins/wdriver/tests/ts_protocol.rs diff --git a/plugins/wdriver/Cargo.toml b/plugins/wdriver/Cargo.toml index b8703fb8..0029a06d 100644 --- a/plugins/wdriver/Cargo.toml +++ b/plugins/wdriver/Cargo.toml @@ -14,6 +14,15 @@ fast_log = "1.5.51" serde = { version = "1.0.211", features = ["derive"] } serde_json = "1.0.132" prost = "0.13" +md5 = "0.7" +windows-sys = { version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_Storage_FileSystem", + "Win32_System_Diagnostics_Etw", + "Win32_System_Memory", + "Win32_System_Threading", + "Win32_System_Time", +] } napi = { version = "2.16.12", default-features = false, features = ["napi4", "serde-json", "tokio_rt"] } napi-derive = "2.16.12" tokio = { version = "1.40.0", features = ["rt-multi-thread", "sync", "io-util", "time", "process", "macros", "rt", "fs", "net"] } diff --git a/plugins/wdriver/document/REPORTING_MATRIX.md b/plugins/wdriver/document/REPORTING_MATRIX.md new file mode 100644 index 00000000..8a458830 --- /dev/null +++ b/plugins/wdriver/document/REPORTING_MATRIX.md @@ -0,0 +1,182 @@ +# wdriver Reporting Matrix + +## Scope + +This document describes the current user-mode collection and ETW reporting +surface of `plugins/wdriver`. + +本文档用于说明 `plugins/wdriver` 当前已经落地的用户态采集与 ETW +上报能力,重点关注: + +- server 下发任务后是否能真实采集并上报 +- `Record + fields["data_type"] + fields["udata"]` 协议是否保持稳定 +- 哪些模块已经完成,哪些仍是未接入或未完成状态 + +## Transport + +- Input task frame: `uint32 little-endian length + protobuf Task` +- Output record frame: `uint32 little-endian length + protobuf Record` +- Windows payload contract: + - `fields["data_type"]` + - `fields["udata"]` +- Task acknowledgement: + - `data_type = 5100` + - fields: `token`, `status`, `msg` + +## Implemented Task Matrix + +| data_type | status | purpose | notes | +| --- | --- | --- | --- | +| `200` | implemented | process snapshot | active | +| `202` | implemented | autorun registry + scheduled task snapshot | `flag=1` registry, `flag=2` task scheduler | +| `203` | implemented | network socket snapshot | active | +| `207` | implemented | local account snapshot | active | +| `208` | implemented | service + installed software snapshot | `flag=1` service, `flag=2` software | +| `209` | implemented | directory enumeration | bounded by max file count | +| `210` | implemented | file detail query | includes md5 | +| `300` | implemented | ETW process event active reporting | kernel logger + TDH parser | +| `301` | implemented | ETW thread event | kernel logger + TDH parser | +| `302` | implemented | ETW image load event | kernel logger + TDH parser | +| `303` | implemented | ETW network event | kernel logger + TDH parser | +| `304` | implemented | ETW registry event | kernel logger + TDH parser | +| `305` | implemented | ETW file I/O event | kernel logger + TDH parser | + +## Current Field Contract + +### 200 process snapshot + +- `win_user_process_pid` +- `win_user_process_pribase` +- `win_user_process_thrcout` +- `win_user_process_parenid` +- `win_user_process_Path` +- `win_user_process_szExeFile` + +### 202 autorun snapshot + +- `win_user_autorun_flag` +- `win_user_autorun_regName` +- `win_user_autorun_regKey` + +Scheduled task record: + +- `win_user_autorun_flag = 2` +- `win_user_autorun_tschname` +- `win_user_autorun_tscState` +- `win_user_autorun_tscLastTime` +- `win_user_autorun_tscNextTime` +- `win_user_autorun_tscCommand` + +### 203 network snapshot + +- `win_user_net_flag` +- `win_user_net_src` +- `win_user_net_dst` +- `win_user_net_status` +- `win_user_net_pid` + +### 207 account snapshot + +- `win_user_sysuser_user` +- `win_user_sysuser_name` +- `win_user_sysuser_sid` +- `win_user_sysuser_flag` + +### 208 service/software snapshot + +Service record: + +- `win_user_softwareserver_flag = 1` +- `win_user_server_lpsName` +- `win_user_server_lpdName` +- `win_user_server_lpPath` +- `win_user_server_lpDescr` +- `win_user_server_status` + +Software record: + +- `win_user_softwareserver_flag = 2` +- `win_user_software_lpsName` +- `win_user_software_Size` +- `win_user_software_Ver` +- `win_user_software_installpath` +- `win_user_software_uninstallpath` +- `win_user_software_data` +- `win_user_software_venrel` + +### 209 directory snapshot + +Summary record: + +- `win_user_driectinfo_flag = 1` +- `win_user_driectinfo_filecout` +- `win_user_driectinfo_size` + +File record: + +- `win_user_driectinfo_flag = 2` +- `win_user_driectinfo_filename` +- `win_user_driectinfo_filePath` +- `win_user_driectinfo_fileSize` + +### 210 file detail snapshot + +- `win_user_fileinfo_filename` +- `win_user_fileinfo_dwFileAttributes` +- `win_user_fileinfo_dwFileAttributesHide` +- `win_user_fileinfo_md5` +- `win_user_fileinfo_m_seFileSizeof` +- `win_user_fileinfo_seFileAccess` +- `win_user_fileinfo_seFileCreate` +- `win_user_fileinfo_seFileModify` + +### 300 ETW process event + +- `win_etw_processinfo_eventname` +- `win_etw_processinfo_parentid` +- `win_etw_processinfo_status` +- `win_etw_processinfo_pid` +- `win_etw_processinfo_path` + +## Appcore Status + +| module | status | notes | +| --- | --- | --- | +| `app_process` | implemented | snapshot collection | +| `app_net` | implemented | snapshot collection | +| `app_account` | implemented | snapshot collection | +| `app_autostart` | implemented | registry + task scheduler snapshot | +| `app_service_software` | partially implemented | service + software implemented | +| `app_file` | implemented | directory + file detail | +| `app_sysinfo` | not wired | no reporting task and methods remain skeletal | +| `appcore/etw` | implemented | `300-305` are connected through one ETW session | + +## Test Coverage + +Current test design: + +- `tests/ts_appcore.rs` + - verifies user-mode collectors return non-empty results + - verifies directory and file detail collection +- `tests/ts_protocol.rs` + - verifies downlink task -> stdout protobuf report -> `5100` ack + - verifies `200/203/207/208/209/210` + - verifies `300-305` ETW reporting smoke paths + - verifies repeated ETW start is idempotent + +## Stability And Performance Notes + +- Protocol writer is protected by a mutex so ETW async reporting does not corrupt + stdout framing while task responses are in flight. +- Directory collection is capped to avoid unbounded recursion and memory growth. +- File md5 is streamed instead of loading the whole file into memory. +- ETW currently starts one kernel logger session and shares it across `300-305` + so repeated task activation does not create multiple competing traces. + +## Known Gaps + +- `app_sysinfo` is not yet part of the reporting task surface. +- some process fields still use lightweight sources and are not fully aligned + with the C++ implementation depth. +- IPv4 ETW network addresses follow the legacy Hades-Windows numeric-string + semantics, while IPv6 addresses remain readable string literals. diff --git a/plugins/wdriver/src/appcore/app_account.rs b/plugins/wdriver/src/appcore/app_account.rs index 0aa78d9e..24a90088 100644 --- a/plugins/wdriver/src/appcore/app_account.rs +++ b/plugins/wdriver/src/appcore/app_account.rs @@ -20,10 +20,10 @@ impl AppAccount { return true; } - pub fn get_account_info(account_info:&mut Vec) -> bool { + pub fn get_account_info(account_info: &mut Vec) -> bool { let users = Users::new_with_refreshed_list(); for user in users.list() { - let account_ctx = AppAccountInfo{ + let account_ctx = AppAccountInfo { serveruser: user.name().to_string(), servername: user.name().to_string(), serverusid: user.id().to_string(), @@ -36,5 +36,4 @@ impl AppAccount { } return true; } - } diff --git a/plugins/wdriver/src/appcore/app_autostart.rs b/plugins/wdriver/src/appcore/app_autostart.rs index f405727b..727ea3ef 100644 --- a/plugins/wdriver/src/appcore/app_autostart.rs +++ b/plugins/wdriver/src/appcore/app_autostart.rs @@ -1,6 +1,6 @@ use crate::{ appcore::app_include::AppRegRunInfo, appcore::app_include::AppTaskSchedulerRunInfo, - util::windwos_autostart::App, + util::windwos_autostart::{list_task_scheduler, App}, }; pub struct AppAutoStart { @@ -46,6 +46,21 @@ impl AppAutoStart { } pub fn get_astart_taskschedu(astart_tasksched: &mut Vec) -> bool { + let tasks = match list_task_scheduler() { + Ok(tasks) => tasks, + Err(_) => return false, + }; + + for task in tasks.into_iter().take(1000) { + astart_tasksched.push(AppTaskSchedulerRunInfo { + valuename: task.get_name(), + state: task.get_state(), + lastime: task.get_last_time(), + nexttime: task.get_next_time(), + taskcommand: task.get_command(), + }); + } + if astart_tasksched.is_empty() { return false; } diff --git a/plugins/wdriver/src/appcore/app_file.rs b/plugins/wdriver/src/appcore/app_file.rs index 2784d917..35a7d9f3 100644 --- a/plugins/wdriver/src/appcore/app_file.rs +++ b/plugins/wdriver/src/appcore/app_file.rs @@ -1,5 +1,156 @@ +use std::{fs, io::Read, path::Path, time::SystemTime}; + +use chrono::{DateTime, Local}; + +use crate::appcore::app_include::{AppDriectInfo, AppFileExInfo, AppFileInfo}; + pub struct AppFile; impl AppFile { - + const MAX_DIRECTORY_FILES: usize = 0x4090; + + pub fn get_directory_info(path: &str) -> Option { + let root = Path::new(path); + if !root.is_dir() { + return None; + } + + let mut files = Vec::new(); + let mut total_size: u64 = 0; + Self::walk_directory(root, &mut files, &mut total_size); + + if files.is_empty() { + return None; + } + + Some(AppDriectInfo { + directname: root.to_string_lossy().into_owned(), + directsize: total_size.min(u32::MAX as u64) as u32, + filecount: files.len().min(u32::MAX as usize) as u32, + file_array: files, + }) + } + + pub fn get_file_info(path: &str) -> Option { + let path = Path::new(path); + let metadata = fs::metadata(path).ok()?; + if !metadata.is_file() { + return None; + } + + Some(AppFileExInfo { + filename: path + .file_name() + .map(|name| name.to_string_lossy().into_owned()) + .unwrap_or_default(), + filecreate: format_system_time(metadata.created().ok()), + filemodify: format_system_time(metadata.modified().ok()), + fileaccess: format_system_time(metadata.accessed().ok()), + fileattributes: file_attribute_text(&metadata), + filesize: format_file_size(metadata.len()), + fileattributes_hide: if is_hidden(path) { + "隐藏 ".to_string() + } else { + String::new() + }, + filepath: path.to_string_lossy().into_owned(), + filemd5: md5_file(path).unwrap_or_default(), + }) + } + + fn walk_directory(path: &Path, files: &mut Vec, total_size: &mut u64) { + if files.len() >= Self::MAX_DIRECTORY_FILES { + return; + } + + let entries = match fs::read_dir(path) { + Ok(entries) => entries, + Err(_) => return, + }; + + for entry in entries.flatten() { + if files.len() >= Self::MAX_DIRECTORY_FILES { + return; + } + + let entry_path = entry.path(); + let metadata = match entry.metadata() { + Ok(metadata) => metadata, + Err(_) => continue, + }; + + if metadata.is_dir() { + Self::walk_directory(&entry_path, files, total_size); + continue; + } + + if !metadata.is_file() { + continue; + } + + let size = metadata.len(); + *total_size = total_size.saturating_add(size); + files.push(AppFileInfo { + filesize: size.min(u32::MAX as u64) as u32, + filename: entry_path + .file_name() + .map(|name| name.to_string_lossy().into_owned()) + .unwrap_or_default(), + filepath: entry_path.to_string_lossy().into_owned(), + }); + } + } +} + +fn format_system_time(value: Option) -> String { + value + .map(|time| { + let datetime: DateTime = time.into(); + datetime.format("%Y/%m/%d %H:%M:%S").to_string() + }) + .unwrap_or_default() +} + +fn format_file_size(size: u64) -> String { + if size > 1024 * 1024 * 1024 { + format!("{:.2}GB", size as f64 / 1024.0 / 1024.0 / 1024.0) + } else if size > 1024 * 1024 { + format!("{:.2}MB", size as f64 / 1024.0 / 1024.0) + } else { + format!("{:.2}KB", size as f64 / 1024.0) + } +} + +#[cfg(windows)] +fn file_attribute_text(metadata: &fs::Metadata) -> String { + use std::os::windows::fs::MetadataExt; + + metadata.file_attributes().to_string() +} + +#[cfg(not(windows))] +fn file_attribute_text(_metadata: &fs::Metadata) -> String { + String::new() +} + +fn is_hidden(path: &Path) -> bool { + path.file_name() + .map(|name| name.to_string_lossy().starts_with('.')) + .unwrap_or(false) +} + +fn md5_file(path: &Path) -> Option { + let mut file = fs::File::open(path).ok()?; + let mut ctx = md5::Context::new(); + let mut buf = [0u8; 64 * 1024]; + + loop { + let read = file.read(&mut buf).ok()?; + if read == 0 { + break; + } + ctx.consume(&buf[..read]); + } + + Some(format!("{:x}", ctx.compute())) } diff --git a/plugins/wdriver/src/appcore/app_include.rs b/plugins/wdriver/src/appcore/app_include.rs index 1dd7cde0..870b9544 100644 --- a/plugins/wdriver/src/appcore/app_include.rs +++ b/plugins/wdriver/src/appcore/app_include.rs @@ -1,4 +1,4 @@ -// account +// account pub struct AppAccountInfo { pub serveruser: String, pub servername: String, @@ -6,7 +6,7 @@ pub struct AppAccountInfo { pub serverflag: u32, } -// auto start +// auto start pub struct AppRegRunInfo { pub valuename: String, pub valuekey: String, @@ -25,22 +25,22 @@ pub struct AppFileExInfo { pub filecreate: String, pub filemodify: String, pub fileaccess: String, - pub fileattributes:String, + pub fileattributes: String, pub filesize: String, - pub fileattributes_hide:String, + pub fileattributes_hide: String, pub filepath: String, - pub filemd5: String + pub filemd5: String, } -pub struct AppFileInfo{ +pub struct AppFileInfo { pub filesize: u32, pub filename: String, pub filepath: String, } pub struct AppDriectInfo { - pub directname: String, + pub directname: String, pub directsize: u32, pub filecount: u32, - pub file_array: Vec, + pub file_array: Vec, } // network @@ -57,9 +57,9 @@ pub struct AppNetWorkInfo { pub state: String, } -// process -pub struct AppProcessInfo { - pub pid : u32, +// process +pub struct AppProcessInfo { + pub pid: u32, pub th32parentprocessid: u32, pub exefile: String, pub priclassbase: String, @@ -80,7 +80,7 @@ pub struct AppSoftWareInfo { } pub struct AppServiceInfo { pub displayname: String, - pub servicename: String, + pub servicename: String, pub binarypath: String, pub description: String, pub currentstate: String, diff --git a/plugins/wdriver/src/appcore/app_service_software.rs b/plugins/wdriver/src/appcore/app_service_software.rs index 99105a3b..ffa4b788 100644 --- a/plugins/wdriver/src/appcore/app_service_software.rs +++ b/plugins/wdriver/src/appcore/app_service_software.rs @@ -2,6 +2,16 @@ use crate::{ appcore::app_include::AppServiceInfo, appcore::app_include::AppSoftWareInfo, util::windows_installed::App, }; +use windows::{ + core::PCWSTR, + Win32::System::Services::{ + CloseServiceHandle, EnumServicesStatusExW, OpenSCManagerW, OpenServiceW, + QueryServiceConfig2W, QueryServiceConfigW, ENUM_SERVICE_STATE, ENUM_SERVICE_STATUS_PROCESSW, + ENUM_SERVICE_TYPE, QUERY_SERVICE_CONFIGW, SC_ENUM_PROCESS_INFO, SC_MANAGER_ENUMERATE_SERVICE, + SERVICE_CONFIG_DESCRIPTION, SERVICE_DESCRIPTIONW, SERVICE_QUERY_CONFIG, + SERVICE_STATE_ALL, SERVICE_WIN32, + }, +}; pub struct AppServiceSoftWare { pub services_info: Vec, @@ -9,6 +19,8 @@ pub struct AppServiceSoftWare { } impl AppServiceSoftWare { + const MAX_SERVICES: usize = 4096; + pub fn init() -> bool { let mut services_info: Vec = vec![]; let mut software_info: Vec = vec![]; @@ -24,10 +36,75 @@ impl AppServiceSoftWare { } pub fn get_services_info(services_info: &mut Vec) -> bool { - if services_info.is_empty() { - return false; + unsafe { + let scm = match OpenSCManagerW(PCWSTR::null(), PCWSTR::null(), SC_MANAGER_ENUMERATE_SERVICE) { + Ok(handle) => handle, + Err(_) => return false, + }; + + let mut bytes_needed = 0u32; + let mut services_returned = 0u32; + let mut resume_handle = 0u32; + + let _ = EnumServicesStatusExW( + scm, + SC_ENUM_PROCESS_INFO, + ENUM_SERVICE_TYPE(SERVICE_WIN32.0), + ENUM_SERVICE_STATE(SERVICE_STATE_ALL.0), + None, + &mut bytes_needed, + &mut services_returned, + Some(&mut resume_handle), + PCWSTR::null(), + ); + + if bytes_needed == 0 { + let _ = CloseServiceHandle(scm); + return false; + } + + let mut buffer = vec![0u8; bytes_needed as usize]; + if EnumServicesStatusExW( + scm, + SC_ENUM_PROCESS_INFO, + ENUM_SERVICE_TYPE(SERVICE_WIN32.0), + ENUM_SERVICE_STATE(SERVICE_STATE_ALL.0), + Some(buffer.as_mut_slice()), + &mut bytes_needed, + &mut services_returned, + Some(&mut resume_handle), + PCWSTR::null(), + ) + .is_err() + { + let _ = CloseServiceHandle(scm); + return false; + } + + let entries = std::slice::from_raw_parts( + buffer.as_ptr() as *const ENUM_SERVICE_STATUS_PROCESSW, + services_returned as usize, + ); + + for entry in entries.iter().take(Self::MAX_SERVICES) { + let service_name = pwstr_to_string(entry.lpServiceName.0); + if service_name.is_empty() { + continue; + } + + let (binary_path, description) = query_service_details(scm, &service_name); + services_info.push(AppServiceInfo { + displayname: pwstr_to_string(entry.lpDisplayName.0), + servicename: service_name, + binarypath: binary_path, + description, + currentstate: service_state_text(entry.ServiceStatusProcess.dwCurrentState.0), + }); + } + + let _ = CloseServiceHandle(scm); } - return true; + !services_info.is_empty() } pub fn get_software_info(software_info: &mut Vec) -> bool { @@ -62,3 +139,79 @@ impl AppServiceSoftWare { return true; } } + +fn query_service_details( + scm: windows::Win32::System::Services::SC_HANDLE, + service_name: &str, +) -> (String, String) { + unsafe { + let wide_name = wide_null(service_name); + let service = match OpenServiceW(scm, PCWSTR(wide_name.as_ptr()), SERVICE_QUERY_CONFIG) { + Ok(handle) => handle, + Err(_) => return (String::new(), String::new()), + }; + + let mut bytes_needed = 0u32; + let _ = QueryServiceConfigW(service, None, 0, &mut bytes_needed); + let mut binary_path = String::new(); + if bytes_needed > 0 { + let mut buffer = vec![0u8; bytes_needed as usize]; + let config = buffer.as_mut_ptr() as *mut QUERY_SERVICE_CONFIGW; + if QueryServiceConfigW(service, Some(config), bytes_needed, &mut bytes_needed).is_ok() { + binary_path = pwstr_to_string((*config).lpBinaryPathName.0); + } + } + + let mut desc_needed = 0u32; + let _ = QueryServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, None, &mut desc_needed); + let mut description = String::new(); + if desc_needed > 0 { + let mut buffer = vec![0u8; desc_needed as usize]; + if QueryServiceConfig2W( + service, + SERVICE_CONFIG_DESCRIPTION, + Some(buffer.as_mut_slice()), + &mut desc_needed, + ) + .is_ok() + { + let desc = buffer.as_ptr() as *const SERVICE_DESCRIPTIONW; + description = pwstr_to_string((*desc).lpDescription.0); + } + } + + let _ = CloseServiceHandle(service); + (binary_path, description) + } +} + +fn pwstr_to_string(ptr: *mut u16) -> String { + if ptr.is_null() { + return String::new(); + } + unsafe { + let mut len = 0usize; + while *ptr.add(len) != 0 { + len += 1; + } + String::from_utf16_lossy(std::slice::from_raw_parts(ptr, len)) + } +} + +fn service_state_text(state: u32) -> String { + match state { + 1 => "STOPPED", + 2 => "START_PENDING", + 3 => "STOP_PENDING", + 4 => "RUNNING", + 5 => "CONTINUE_PENDING", + 6 => "PAUSE_PENDING", + 7 => "PAUSED", + _ => "UNKNOWN", + } + .to_string() +} + +fn wide_null(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() +} diff --git a/plugins/wdriver/src/appcore/app_sysinfo.rs b/plugins/wdriver/src/appcore/app_sysinfo.rs index a652579b..0308b31e 100644 --- a/plugins/wdriver/src/appcore/app_sysinfo.rs +++ b/plugins/wdriver/src/appcore/app_sysinfo.rs @@ -6,45 +6,25 @@ pub struct AppSysInfo { pub camera: Vec, pub bluetooth: Vec, pub voice: Vec, - pub microphone: Vec + pub microphone: Vec, } impl AppSysInfo { - - pub fn init() { + pub fn init() {} - } + pub fn get_computer_name() {} - pub fn get_computer_name() { - - } + pub fn get_os_version() {} - pub fn get_os_version() { + pub fn get_display_cardinfo_wmic() {} - } + pub fn get_cpu_info() {} - pub fn get_display_cardinfo_wmic() { + pub fn get_bluetooth_info() {} - } + pub fn get_camera_info() {} - pub fn get_cpu_info() { - - } - - pub fn get_bluetooth_info() { - - } - - pub fn get_camera_info() { - - } - - pub fn get_micro_phone() { - - } - - pub fn get_gpu_info() { - - } + pub fn get_micro_phone() {} + pub fn get_gpu_info() {} } diff --git a/plugins/wdriver/src/appcore/etw/etw.rs b/plugins/wdriver/src/appcore/etw/etw.rs index b5622f5b..d7ea4192 100644 --- a/plugins/wdriver/src/appcore/etw/etw.rs +++ b/plugins/wdriver/src/appcore/etw/etw.rs @@ -1,6 +1,624 @@ +use crate::transport::RecordWriter; + pub struct Etw; -impl Etw { +#[cfg(windows)] +mod platform { + use std::{ + collections::HashMap, + net::Ipv4Addr, + ptr, slice, + sync::{Mutex, OnceLock}, + thread, + }; + + use serde_json::{json, Value}; + use windows_sys::{ + core::GUID, + Win32::{ + Foundation::{CloseHandle, ERROR_ALREADY_EXISTS, ERROR_CANCELLED, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS}, + System::{ + Diagnostics::Etw::*, + Threading::{OpenProcess, QueryFullProcessImageNameW, PROCESS_QUERY_LIMITED_INFORMATION}, + }, + }, + }; + + use crate::{ + protocol::{Payload, Record}, + transport::{unix_timestamp, RecordWriter}, + }; + + const PROVIDER_NETWORK_V4: GUID = GUID::from_u128(0x9a280ac0_c8e0_11d1_84e2_00c04fb998a2); + const PROVIDER_NETWORK_V6: GUID = GUID::from_u128(0xbf3a50c5_a9c9_4988_a005_2df0b7c80f80); + const PROVIDER_PROCESS: GUID = GUID::from_u128(0x3d6fa8d0_fe05_11d0_9dda_00c04fd7ba7c); + const PROVIDER_THREAD: GUID = GUID::from_u128(0x3d6fa8d1_fe05_11d0_9dda_00c04fd7ba7c); + const PROVIDER_FILE: GUID = GUID::from_u128(0x90cbdc39_4a3e_11d1_84f4_0000f80464e3); + const PROVIDER_REGISTRY: GUID = GUID::from_u128(0xae53722e_c863_11d2_8659_00c04fa321a1); + const PROVIDER_IMAGE: GUID = GUID::from_u128(0x2cb15d1d_5fc1_11d2_abe1_00a0c911f518); + + static WRITER: OnceLock = OnceLock::new(); + static STATE: OnceLock>> = OnceLock::new(); + + struct EtwSession { + control_handle: CONTROLTRACE_HANDLE, + trace_handle: PROCESSTRACE_HANDLE, + } + + pub fn start(writer: RecordWriter) -> Result { + let _ = WRITER.set(writer); + let state = STATE.get_or_init(|| Mutex::new(None)); + let mut guard = state + .lock() + .map_err(|_| "ETW session state lock poisoned".to_string())?; + if guard.is_some() { + return Ok(0); + } + + let name = kernel_logger_name(); + let mut props_buf = build_properties(&name); + let props = props_buf.as_mut_ptr() as *mut EVENT_TRACE_PROPERTIES; + let mut control_handle = CONTROLTRACE_HANDLE { Value: 0 }; + + let mut status = unsafe { StartTraceW(&mut control_handle, name.as_ptr(), props) }; + if status == ERROR_ALREADY_EXISTS { + let _ = unsafe { + ControlTraceW( + CONTROLTRACE_HANDLE { Value: 0 }, + name.as_ptr(), + props, + EVENT_TRACE_CONTROL_STOP, + ) + }; + status = unsafe { StartTraceW(&mut control_handle, name.as_ptr(), props) }; + } + if status != ERROR_SUCCESS { + return Err(format!("StartTraceW failed: {}", status)); + } + + let mut trace = EVENT_TRACE_LOGFILEW::default(); + trace.LoggerName = KERNEL_LOGGER_NAMEW as *mut u16; + trace.Anonymous1.ProcessTraceMode = + PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; + trace.Anonymous2.EventRecordCallback = Some(event_callback); + trace.BufferCallback = Some(buffer_callback); + trace.IsKernelTrace = 1; + + let trace_handle = unsafe { OpenTraceW(&mut trace) }; + if trace_handle.Value == u64::MAX { + let _ = unsafe { + ControlTraceW( + control_handle, + name.as_ptr(), + props, + EVENT_TRACE_CONTROL_STOP, + ) + }; + return Err("OpenTraceW failed".to_string()); + } + + let worker_handle = trace_handle; + thread::spawn(move || { + let status = + unsafe { ProcessTrace(&worker_handle, 1, ptr::null_mut(), ptr::null_mut()) }; + if status != ERROR_SUCCESS && status != ERROR_CANCELLED { + log::warn!("ProcessTrace failed: {}", status); + } + }); + + *guard = Some(EtwSession { + control_handle, + trace_handle, + }); + Ok(1) + } + + pub fn stop() { + let Some(state) = STATE.get() else { + return; + }; + let Ok(mut guard) = state.lock() else { + return; + }; + let Some(session) = guard.take() else { + return; + }; + + let name = kernel_logger_name(); + let mut props_buf = build_properties(&name); + let props = props_buf.as_mut_ptr() as *mut EVENT_TRACE_PROPERTIES; + unsafe { + let _ = CloseTrace(session.trace_handle); + let _ = ControlTraceW( + session.control_handle, + name.as_ptr(), + props, + EVENT_TRACE_CONTROL_STOP, + ); + } + } + + unsafe extern "system" fn buffer_callback(_logfile: *mut EVENT_TRACE_LOGFILEW) -> u32 { + 1 + } + + unsafe extern "system" fn event_callback(record: *mut EVENT_RECORD) { + if record.is_null() { + return; + } + let record = &*record; + let provider = record.EventHeader.ProviderId; + if !is_supported_provider(&provider) { + return; + } + + let Some(buffer) = load_event_info(record) else { + return; + }; + let info = &*(buffer.as_ptr() as *const TRACE_EVENT_INFO); + let props = parse_properties(record, info); + let event_name = info_offset_string(info, info.OpcodeNameOffset); + let task_name = info_offset_string(info, info.TaskNameOffset); + + if guid_eq(&provider, &PROVIDER_PROCESS) { + emit_process_event(record, &props, &event_name); + } else if guid_eq(&provider, &PROVIDER_THREAD) { + emit_thread_event(&props, &event_name); + } else if guid_eq(&provider, &PROVIDER_IMAGE) { + emit_image_event(&props, &event_name); + } else if guid_eq(&provider, &PROVIDER_FILE) { + emit_file_event(&props, &event_name); + } else if guid_eq(&provider, &PROVIDER_REGISTRY) { + emit_registry_event(&props, &event_name); + } else if guid_eq(&provider, &PROVIDER_NETWORK_V4) || guid_eq(&provider, &PROVIDER_NETWORK_V6) { + emit_network_event(&props, &task_name, &event_name); + } + } + + unsafe fn load_event_info(record: &EVENT_RECORD) -> Option> { + let mut size = 0u32; + let status = TdhGetEventInformation(record, 0, ptr::null(), ptr::null_mut(), &mut size); + if size == 0 || (status != ERROR_INSUFFICIENT_BUFFER && status != ERROR_SUCCESS) { + return None; + } + + let mut buffer = vec![0u8; size as usize]; + let info = buffer.as_mut_ptr() as *mut TRACE_EVENT_INFO; + if TdhGetEventInformation(record, 0, ptr::null(), info, &mut size) != ERROR_SUCCESS { + return None; + } + Some(buffer) + } + + unsafe fn parse_properties( + record: &EVENT_RECORD, + info: &TRACE_EVENT_INFO, + ) -> HashMap { + let mut result = HashMap::new(); + let pointer_size = if (record.EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER as u16) != 0 { + 4 + } else { + 8 + }; + let mut user_len = record.UserDataLength; + let mut data = record.UserData as *const u8; + let properties = slice::from_raw_parts( + info.EventPropertyInfoArray.as_ptr(), + info.TopLevelPropertyCount as usize, + ); + + for property in properties { + let prop_name = info_offset_string(info, property.NameOffset); + let mut len = property.Anonymous3.length as u32; + + if (property.Flags & (PropertyStruct | PropertyParamCount)) == 0 { + let non_struct = property.Anonymous1.nonStructType; + let mut map_buffer = Vec::new(); + let mut map_info: *mut EVENT_MAP_INFO = ptr::null_mut(); + + if non_struct.MapNameOffset != 0 { + let map_name = (info as *const TRACE_EVENT_INFO as *const u8) + .add(non_struct.MapNameOffset as usize) as *const u16; + let mut map_size = 0u32; + if TdhGetEventMapInformation(record, map_name, ptr::null_mut(), &mut map_size) + == ERROR_INSUFFICIENT_BUFFER + { + map_buffer.resize(map_size as usize, 0u8); + map_info = map_buffer.as_mut_ptr() as *mut EVENT_MAP_INFO; + if TdhGetEventMapInformation(record, map_name, map_info, &mut map_size) + != ERROR_SUCCESS + { + map_info = ptr::null_mut(); + } + } + } + + if non_struct.InType as i32 == TDH_INTYPE_BINARY + && non_struct.OutType as i32 == TDH_OUTTYPE_IPV6 + { + len = 16; + } + + let mut value = [0u16; 512]; + let mut size = (value.len() * std::mem::size_of::()) as u32; + let mut consumed = 0u16; + let mut status = TdhFormatProperty( + info, + map_info, + pointer_size, + non_struct.InType, + non_struct.OutType, + len as u16, + user_len, + data, + &mut size, + value.as_mut_ptr(), + &mut consumed, + ); + if status != ERROR_SUCCESS && !map_info.is_null() { + status = TdhFormatProperty( + info, + ptr::null_mut(), + pointer_size, + non_struct.InType, + non_struct.OutType, + len as u16, + user_len, + data, + &mut size, + value.as_mut_ptr(), + &mut consumed, + ); + } + if status == ERROR_SUCCESS { + len = consumed as u32; + result.insert(prop_name, utf16_array_to_string(&value)); + } + } + if len > user_len as u32 { + break; + } + user_len -= len as u16; + data = data.add(len as usize); + } + + result + } + + fn emit_process_event(record: &EVENT_RECORD, props: &HashMap, event_name: &str) { + let pid = parse_u64(props.get("ProcessId")) as u32; + if pid == 0 { + return; + } + let parent = parse_u64(props.get("ParentId")) as u32; + let path = props + .get("CommandLine") + .cloned() + .or_else(|| props.get("ImageFileName").cloned()) + .unwrap_or_default(); + let status = if event_name.eq_ignore_ascii_case("End") { + "0".to_string() + } else { + "1".to_string() + }; + emit_json( + 300, + json!({ + "win_etw_processinfo_eventname": event_name, + "win_etw_processinfo_parentid": parent.to_string(), + "win_etw_processinfo_status": status, + "win_etw_processinfo_pid": pid.to_string(), + "win_etw_processinfo_path": path, + }), + ); + let _ = record; + } + + fn emit_thread_event(props: &HashMap, event_name: &str) { + let pid = parse_u64(props.get("ProcessId")); + let tid = parse_u64(props.get("TThreadId")); + if pid == 0 || tid == 0 { + return; + } + emit_json( + 301, + json!({ + "win_etw_threadinfo_pid": pid.to_string(), + "win_etw_threadinfo_tid": tid.to_string(), + "win_etw_threadinfo_win32startaddr": parse_u64(props.get("Win32StartAddr")).to_string(), + "win_etw_threadinfo_flags": parse_u64(props.get("ThreadFlags")).to_string(), + "win_etw_threadinfo_eventname": event_name, + }), + ); + } + + fn emit_image_event(props: &HashMap, event_name: &str) { + if event_name == "DCStart" { + return; + } + let pid = parse_u64(props.get("ProcessId")); + if pid <= 4 { + return; + } + emit_json( + 302, + json!({ + "win_etw_imageinfo_processId": pid.to_string(), + "win_etw_imageinfo_imageBase": parse_u64(props.get("ImageBase")).to_string(), + "win_etw_imageinfo_imageSize": parse_u64(props.get("ImageSize")).to_string(), + "win_etw_imageinfo_signatureLevel": parse_u64(props.get("SignatureLevel")).to_string(), + "win_etw_imageinfo_signatureType": parse_u64(props.get("SignatureType")).to_string(), + "win_etw_imageinfo_imageChecksum": parse_u64(props.get("ImageChecksum")).to_string(), + "win_etw_imageinfo_timeDateStamp": parse_u64(props.get("TimeDateStamp")).to_string(), + "win_etw_imageinfo_defaultBase": parse_u64(props.get("DefaultBase")).to_string(), + "win_etw_imageinfo_fileName": props.get("FileName").cloned().unwrap_or_default(), + "win_etw_imageinfo_eventname": event_name, + }), + ); + } + + fn emit_network_event(props: &HashMap, task_name: &str, event_name: &str) { + let protocol = if task_name.contains("TcpIp") { + 6 + } else if task_name.contains("UdpIp") { + 17 + } else { + return; + }; + let local = props.get("saddr").cloned().unwrap_or_default(); + let remote = props.get("daddr").cloned().unwrap_or_default(); + if local.is_empty() && remote.is_empty() { + return; + } + let family = if local.contains(':') || remote.contains(':') { 23 } else { 2 }; + let process_id = parse_u64(props.get("PID")) as u32; + let process_path = process_path_by_pid(process_id); + let process_path_size = process_path.encode_utf16().count(); + emit_json( + 303, + json!({ + "win_network_addressfamily": family.to_string(), + "win_network_localaddr": network_address_value(&local, family), + "win_network_toLocalport": parse_u64(props.get("sport")).to_string(), + "win_network_protocol": protocol.to_string(), + "win_network_remoteaddr": network_address_value(&remote, family), + "win_network_toremoteport": parse_u64(props.get("dport")).to_string(), + "win_network_procespath": process_path, + "win_network_processpathsize": process_path_size.to_string(), + "win_network_processid": process_id.to_string(), + "win_network_eventname": event_name, + }), + ); + } + + fn emit_registry_event(props: &HashMap, event_name: &str) { + if props.is_empty() { + return; + } + emit_json( + 304, + json!({ + "win_etw_regtab_initialTime": parse_i64(props.get("InitialTime")).to_string(), + "win_etw_regtab_status": parse_u64(props.get("Status")).to_string(), + "win_etw_regtab_index": parse_u64(props.get("Index")).to_string(), + "win_etw_regtab_keyHandle": parse_u64(props.get("KeyHandle")).to_string(), + "win_etw_regtab_keyName": props.get("KeyName").cloned().unwrap_or_default(), + "win_etw_regtab_eventname": event_name, + }), + ); + } + + fn emit_file_event(props: &HashMap, event_name: &str) { + if matches!(event_name, "OperationEnd" | "QueryInfo" | "FSControl") { + return; + } + if props.get("OpenPath").is_none() + && props.get("FileName").is_none() + && props.get("FileObject").is_none() + { + return; + } + emit_json( + 305, + json!({ + "win_etw_fileio_eventname": event_name, + "win_etw_fileio_FilePath": props.get("OpenPath").cloned().unwrap_or_default(), + "win_etw_fileio_FileName": props.get("FileName").cloned().unwrap_or_default(), + "win_etw_fileio_Tid": parse_u64(props.get("TTID")).to_string(), + "win_etw_fileio_FileAttributes": parse_u64(props.get("FileAttributes")).to_string(), + "win_etw_fileio_CreateOptions": parse_u64(props.get("CreateOptions")).to_string(), + "win_etw_fileio_ShareAccess": parse_u64(props.get("ShareAccess")).to_string(), + "win_etw_fileio_Offset": parse_u64(props.get("Offset")).to_string(), + "win_etw_fileio_FileKey": parse_u64(props.get("FileKey")).to_string(), + "win_etw_fileio_FileObject": parse_u64(props.get("FileObject")).to_string(), + }), + ); + } + + fn emit_json(data_type: i32, value: Value) { + let Some(writer) = WRITER.get() else { + return; + }; + let Ok(udata) = serde_json::to_string(&value) else { + return; + }; + let rec = Record { + data_type, + timestamp: unix_timestamp(), + data: Some(Payload { + fields: HashMap::from([ + ("data_type".to_string(), data_type.to_string()), + ("udata".to_string(), udata), + ]), + }), + }; + let _ = writer.send_record(&rec); + } + + fn info_offset_string(info: &TRACE_EVENT_INFO, offset: u32) -> String { + if offset == 0 { + return String::new(); + } + unsafe { + let ptr = (info as *const TRACE_EVENT_INFO as *const u8).add(offset as usize) as *const u16; + read_wide(ptr) + } + } + + unsafe fn read_wide(ptr: *const u16) -> String { + if ptr.is_null() { + return String::new(); + } + let mut len = 0usize; + while *ptr.add(len) != 0 { + len += 1; + } + String::from_utf16_lossy(slice::from_raw_parts(ptr, len)) + } + + fn utf16_array_to_string(buf: &[u16]) -> String { + let end = buf.iter().position(|&ch| ch == 0).unwrap_or(buf.len()); + String::from_utf16_lossy(&buf[..end]).trim().to_string() + } + + fn network_address_value(address: &str, family: u32) -> String { + if family == 2 { + if let Ok(ipv4) = address.parse::() { + return u32::from_le_bytes(ipv4.octets()).to_string(); + } + } + address.to_string() + } + + fn process_path_by_pid(pid: u32) -> String { + if pid == 0 { + return String::new(); + } + + unsafe { + let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid); + if handle.is_null() { + return String::new(); + } + + let mut buffer = vec![0u16; 260]; + let mut size = buffer.len() as u32; + let status = QueryFullProcessImageNameW(handle, 0, buffer.as_mut_ptr(), &mut size); + let _ = CloseHandle(handle); + if status == 0 || size == 0 { + return String::new(); + } + + String::from_utf16_lossy(&buffer[..size as usize]) + } + } + + fn parse_u64(value: Option<&String>) -> u64 { + let Some(value) = value else { + return 0; + }; + let value = value.trim(); + if value.is_empty() { + return 0; + } + if let Some(hex) = value.strip_prefix("0x").or_else(|| value.strip_prefix("0X")) { + return u64::from_str_radix(hex, 16).unwrap_or(0); + } + value + .parse::() + .or_else(|_| u64::from_str_radix(value, 16)) + .unwrap_or(0) + } + + fn parse_i64(value: Option<&String>) -> i64 { + let Some(value) = value else { + return 0; + }; + let value = value.trim(); + if value.is_empty() { + return 0; + } + value + .parse::() + .or_else(|_| i64::from_str_radix(value.trim_start_matches("0x"), 16)) + .unwrap_or(0) + } + + fn guid_eq(left: &GUID, right: &GUID) -> bool { + left.data1 == right.data1 + && left.data2 == right.data2 + && left.data3 == right.data3 + && left.data4 == right.data4 + } + + fn is_supported_provider(provider: &GUID) -> bool { + guid_eq(provider, &PROVIDER_PROCESS) + || guid_eq(provider, &PROVIDER_THREAD) + || guid_eq(provider, &PROVIDER_IMAGE) + || guid_eq(provider, &PROVIDER_FILE) + || guid_eq(provider, &PROVIDER_REGISTRY) + || guid_eq(provider, &PROVIDER_NETWORK_V4) + || guid_eq(provider, &PROVIDER_NETWORK_V6) + } + + fn build_properties(session_name: &[u16]) -> Vec { + let props_size = std::mem::size_of::(); + let buffer_size = props_size + session_name.len() * std::mem::size_of::(); + let mut buffer = vec![0u8; buffer_size]; + let props = buffer.as_mut_ptr() as *mut EVENT_TRACE_PROPERTIES; + unsafe { + (*props).Wnode.BufferSize = buffer_size as u32; + (*props).Wnode.Flags = WNODE_FLAG_TRACED_GUID; + (*props).Wnode.ClientContext = 1; + (*props).Wnode.Guid = SystemTraceControlGuid; + (*props).BufferSize = 64; + (*props).MinimumBuffers = 5; + (*props).MaximumBuffers = 64; + (*props).LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_SYSTEM_LOGGER_MODE; + (*props).FlushTimer = 1; + (*props).EnableFlags = EVENT_TRACE_FLAG_PROCESS + | EVENT_TRACE_FLAG_THREAD + | EVENT_TRACE_FLAG_IMAGE_LOAD + | EVENT_TRACE_FLAG_NETWORK_TCPIP + | EVENT_TRACE_FLAG_REGISTRY + | EVENT_TRACE_FLAG_FILE_IO + | EVENT_TRACE_FLAG_FILE_IO_INIT; + (*props).LoggerNameOffset = props_size as u32; + ptr::copy_nonoverlapping( + session_name.as_ptr(), + buffer.as_mut_ptr().add(props_size) as *mut u16, + session_name.len(), + ); + } + buffer + } + + fn kernel_logger_name() -> Vec { + "NT Kernel Logger" + .encode_utf16() + .chain(std::iter::once(0)) + .collect() + } +} + +#[cfg(not(windows))] +mod platform { + use crate::transport::RecordWriter; + + pub fn start(_writer: RecordWriter) -> Result { + Err("ETW active reporting is only supported on Windows".to_string()) + } + + pub fn stop() {} +} + +impl Etw { + pub fn start(writer: RecordWriter) -> Result { + platform::start(writer) + } -} \ No newline at end of file + pub fn stop() { + platform::stop() + } +} diff --git a/plugins/wdriver/src/appcore/etw/mod.rs b/plugins/wdriver/src/appcore/etw/mod.rs index b12bd4ba..468a0581 100644 --- a/plugins/wdriver/src/appcore/etw/mod.rs +++ b/plugins/wdriver/src/appcore/etw/mod.rs @@ -1 +1 @@ -pub mod etw; \ No newline at end of file +pub mod etw; diff --git a/plugins/wdriver/src/appcore/mod.rs b/plugins/wdriver/src/appcore/mod.rs index 97dbc239..ec82769d 100644 --- a/plugins/wdriver/src/appcore/mod.rs +++ b/plugins/wdriver/src/appcore/mod.rs @@ -1,8 +1,9 @@ pub mod app_account; pub mod app_autostart; pub mod app_file; +pub mod app_include; pub mod app_net; pub mod app_process; pub mod app_service_software; pub mod app_sysinfo; -pub mod app_include; \ No newline at end of file +pub mod etw; diff --git a/plugins/wdriver/src/config/config.rs b/plugins/wdriver/src/config/config.rs index 6416cd32..78c66b4e 100644 --- a/plugins/wdriver/src/config/config.rs +++ b/plugins/wdriver/src/config/config.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::{fs, path::PathBuf, ptr::null}; use yaml_rust::YamlLoader; -use crate::{util::log::init_log}; +use crate::util::log::init_log; pub struct RuleImpl { // dns @@ -32,8 +32,7 @@ impl RuleImpl { pub async fn init() -> bool { let path = get_path().unwrap(); let debug = init_log(&path); - if !debug { - } + if !debug {} // get cuurent exec path let current_path = std::env::current_dir() @@ -47,7 +46,7 @@ impl RuleImpl { } // init dns rule - let mut rule_dns : Vec = vec![]; + let mut rule_dns: Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; @@ -60,7 +59,7 @@ impl RuleImpl { } // init redirect rule - let mut rule_redirect :Vec = vec![]; + let mut rule_redirect: Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; @@ -73,7 +72,7 @@ impl RuleImpl { } // init transport rule - let mut rule_transport:Vec = vec![]; + let mut rule_transport: Vec = vec![]; { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\networkRuleConfig.yaml"; @@ -90,7 +89,8 @@ impl RuleImpl { { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\directoryRuleConfig.json"; - let b = RuleImpl::get_dirtecory_rule(rule_path, &mut _data, &mut rule_directory).is_ok(); + let b = + RuleImpl::get_dirtecory_rule(rule_path, &mut _data, &mut rule_directory).is_ok(); if true == b { log::debug!("analyze directory success. {}", _data); } else { @@ -106,7 +106,8 @@ impl RuleImpl { { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\processRuleConfig.json"; - let b: bool = RuleImpl::get_process_rule(rule_path, &mut _data, &mut rule_process).is_ok(); + let b: bool = + RuleImpl::get_process_rule(rule_path, &mut _data, &mut rule_process).is_ok(); if true == b { log::debug!("analyze transport success. {}", _data); } else { @@ -121,7 +122,8 @@ impl RuleImpl { { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\threadRuleConfig.json"; - let b: bool = RuleImpl::get_thread_rule(rule_path, &mut _data, &mut rule_thread).is_ok(); + let b: bool = + RuleImpl::get_thread_rule(rule_path, &mut _data, &mut rule_thread).is_ok(); if true == b { log::debug!("analyze thread success. {}", _data); } else { @@ -134,7 +136,8 @@ impl RuleImpl { { let mut _data: String = String::from(""); let rule_path: String = current_path.clone() + "\\config\\registerRuleConfig_.json"; - let b: bool = RuleImpl::get_register_rule(rule_path, &mut _data, &mut rule_register).is_ok(); + let b: bool = + RuleImpl::get_register_rule(rule_path, &mut _data, &mut rule_register).is_ok(); if true == b { log::debug!("analyze register success. {}", _data); } else { @@ -169,7 +172,11 @@ impl RuleImpl { } // Analyze dns rule - pub fn get_dns_rule(file_path: String, _data: &mut String, _rule_dns: &mut Vec) -> bool { + pub fn get_dns_rule( + file_path: String, + _data: &mut String, + _rule_dns: &mut Vec, + ) -> bool { if file_path.is_empty() { return false; } @@ -206,7 +213,11 @@ impl RuleImpl { } // Analyze Redirect rule - pub fn get_redirect_rule(file_path: String, _data: &mut String, _rule_redirect: &mut Vec) -> bool { + pub fn get_redirect_rule( + file_path: String, + _data: &mut String, + _rule_redirect: &mut Vec, + ) -> bool { if file_path.is_empty() { return false; } @@ -227,7 +238,11 @@ impl RuleImpl { } // Analyze transport layer rule - pub fn get_transport_rule(file_path: String, _data: &mut String, _rule_transport: &mut Vec) -> bool { + pub fn get_transport_rule( + file_path: String, + _data: &mut String, + _rule_transport: &mut Vec, + ) -> bool { if file_path.is_empty() { return false; } @@ -248,7 +263,11 @@ impl RuleImpl { } // Analyze directory rule - pub fn get_dirtecory_rule(file_path: String, _data: &mut String, rule_diretcory: &mut Vec) -> Result<(), napi::Error> { + pub fn get_dirtecory_rule( + file_path: String, + _data: &mut String, + rule_diretcory: &mut Vec, + ) -> Result<(), napi::Error> { loop { if file_path.is_empty() { break; @@ -267,7 +286,11 @@ impl RuleImpl { } // Analyze process rule - pub fn get_process_rule(file_path: String, _data: &mut String, rule_process: &mut RuleProcess) -> Result<(), napi::Error> { + pub fn get_process_rule( + file_path: String, + _data: &mut String, + rule_process: &mut RuleProcess, + ) -> Result<(), napi::Error> { loop { if file_path.is_empty() { break; @@ -286,7 +309,11 @@ impl RuleImpl { } // Analyze thread rule - pub fn get_thread_rule(file_path: String, _data: &mut String, rule_thread: &mut RuleThread) -> Result<(), napi::Error> { + pub fn get_thread_rule( + file_path: String, + _data: &mut String, + rule_thread: &mut RuleThread, + ) -> Result<(), napi::Error> { loop { if file_path.is_empty() { break; @@ -305,7 +332,11 @@ impl RuleImpl { } // Analyze register rule - pub fn get_register_rule(file_path: String, _data: &mut String, rule_register: &mut Vec) -> Result<(), napi::Error> { + pub fn get_register_rule( + file_path: String, + _data: &mut String, + rule_register: &mut Vec, + ) -> Result<(), napi::Error> { loop { if file_path.is_empty() { break; @@ -319,10 +350,9 @@ impl RuleImpl { break; } - + Ok(()) } - } pub fn get_path() -> Result { diff --git a/plugins/wdriver/src/config/mod.rs b/plugins/wdriver/src/config/mod.rs index a1059337..ef68c369 100644 --- a/plugins/wdriver/src/config/mod.rs +++ b/plugins/wdriver/src/config/mod.rs @@ -1 +1 @@ -pub mod config; \ No newline at end of file +pub mod config; diff --git a/plugins/wdriver/src/driver/drvmg.rs b/plugins/wdriver/src/driver/drvmg.rs index f3cfecbf..56d031e7 100644 --- a/plugins/wdriver/src/driver/drvmg.rs +++ b/plugins/wdriver/src/driver/drvmg.rs @@ -1,9 +1,5 @@ use std::ptr::null_mut; -use windows::{ - core::*, - Win32::Foundation::*, - Win32::Storage::FileSystem::*, -}; +use windows::{core::*, Win32::Foundation::*, Win32::Storage::FileSystem::*}; pub struct DrivenManageImpl { pub handle: HANDLE, @@ -11,12 +7,13 @@ pub struct DrivenManageImpl { impl DrivenManageImpl { pub fn new() -> Self { - Self { handle: HANDLE(null_mut()) } + Self { + handle: HANDLE(null_mut()), + } } // Chekcout Driver Status pub fn get_driver_stu(_driver_name: String) -> bool { - return true; } @@ -38,7 +35,7 @@ impl DrivenManageImpl { self.handle = driver_handle; return true; } else { - let err = GetLastError(); + let err = GetLastError(); println!("{}", err.to_hresult().0); return false; } @@ -46,18 +43,15 @@ impl DrivenManageImpl { } // Send Data Pop Data - pub async fn send_driver_data(&self, _code: u32, _data: String) -> bool { - + pub async fn send_driver_data(&self, _code: u32, _data: String) -> bool { return true; } // Read Data Push Queue - pub async fn read_driver_data(&self) { - - } + pub async fn read_driver_data(&self) {} pub fn close_driver_handle(&mut self) { - if self.handle.is_invalid(){ + if self.handle.is_invalid() { return; } unsafe { @@ -65,5 +59,4 @@ impl DrivenManageImpl { } self.handle = HANDLE(null_mut()); } - } diff --git a/plugins/wdriver/src/driver/mod.rs b/plugins/wdriver/src/driver/mod.rs index 1202c0fb..a51d2245 100644 --- a/plugins/wdriver/src/driver/mod.rs +++ b/plugins/wdriver/src/driver/mod.rs @@ -1 +1 @@ -pub mod drvmg; \ No newline at end of file +pub mod drvmg; diff --git a/plugins/wdriver/src/events/event.rs b/plugins/wdriver/src/events/event.rs index 426fd76a..3edbdf36 100644 --- a/plugins/wdriver/src/events/event.rs +++ b/plugins/wdriver/src/events/event.rs @@ -1,8 +1,6 @@ pub struct Event; impl Event { - - // Handle Message pub fn handle_msg_notify() -> bool { return true; @@ -12,5 +10,4 @@ impl Event { pub fn waiti_queue_data_dispatch() -> bool { return true; } - } diff --git a/plugins/wdriver/src/events/mod.rs b/plugins/wdriver/src/events/mod.rs index c47b0f48..53f11265 100644 --- a/plugins/wdriver/src/events/mod.rs +++ b/plugins/wdriver/src/events/mod.rs @@ -1 +1 @@ -pub mod event; \ No newline at end of file +pub mod event; diff --git a/plugins/wdriver/src/kercore/mod.rs b/plugins/wdriver/src/kercore/mod.rs index e69de29b..8b137891 100644 --- a/plugins/wdriver/src/kercore/mod.rs +++ b/plugins/wdriver/src/kercore/mod.rs @@ -0,0 +1 @@ + diff --git a/plugins/wdriver/src/main.rs b/plugins/wdriver/src/main.rs index cde53c06..4e3c2bf7 100644 --- a/plugins/wdriver/src/main.rs +++ b/plugins/wdriver/src/main.rs @@ -3,15 +3,17 @@ use std::collections::HashMap; use serde_json::{json, Value}; use wdriver::{ appcore::{ - app_account::AppAccount, app_autostart::AppAutoStart, app_net::AppNetwork, - app_process::AppProcess, app_service_software::AppServiceSoftWare, + app_account::AppAccount, app_autostart::AppAutoStart, app_file::AppFile, + app_net::AppNetwork, app_process::AppProcess, app_service_software::AppServiceSoftWare, + etw::etw::Etw, }, protocol::{Payload, Record, Task}, - transport::{unix_timestamp, Client}, + transport::{unix_timestamp, Client, RecordWriter}, }; fn main() { let mut client = Client::new(); + let record_writer = client.record_writer(); loop { let task = match client.receive_task() { Ok(task) => task, @@ -19,11 +21,12 @@ fn main() { }; if task.data_type == 0 { + Etw::stop(); let _ = send_task_ack(&mut client, &task.token, "success", ""); break; } - let result = dispatch_task(&mut client, &task); + let result = dispatch_task(&mut client, &record_writer, &task); match result { Ok(count) => { let msg = format!("{} records", count); @@ -36,13 +39,20 @@ fn main() { } } -fn dispatch_task(client: &mut Client, task: &Task) -> Result { +fn dispatch_task( + client: &mut Client, + record_writer: &RecordWriter, + task: &Task, +) -> Result { match task.data_type { 200 => collect_process(client), 202 => collect_autostart(client), 203 => collect_network(client), 207 => collect_account(client), 208 => collect_software(client), + 209 => collect_directory(client, task), + 210 => collect_file_info(client, task), + 300..=305 => Etw::start(record_writer.clone()), _ => Err(format!("unsupported task {}", task.data_type)), } } @@ -72,12 +82,16 @@ fn collect_process(client: &mut Client) -> Result { } fn collect_autostart(client: &mut Client) -> Result { - let mut items = Vec::new(); - if !AppAutoStart::get_astart_register(&mut items) { + let mut reg_items = Vec::new(); + let mut task_items = Vec::new(); + let reg_ok = AppAutoStart::get_astart_register(&mut reg_items); + let task_ok = AppAutoStart::get_astart_taskschedu(&mut task_items); + if !reg_ok && !task_ok { return Err("autostart collection returned no data".to_string()); } + let mut count = 0; - for item in items { + for item in reg_items { send_windows_json( client, 202, @@ -89,6 +103,23 @@ fn collect_autostart(client: &mut Client) -> Result { )?; count += 1; } + + for item in task_items { + send_windows_json( + client, + 202, + json!({ + "win_user_autorun_flag": "2", + "win_user_autorun_tschname": item.valuename, + "win_user_autorun_tscState": item.state.to_string(), + "win_user_autorun_tscLastTime": item.lastime.to_string(), + "win_user_autorun_tscNextTime": item.nexttime.to_string(), + "win_user_autorun_tscCommand": item.taskcommand, + }), + )?; + count += 1; + } + Ok(count) } @@ -145,11 +176,30 @@ fn collect_account(client: &mut Client) -> Result { } fn collect_software(client: &mut Client) -> Result { + let mut service_items = Vec::new(); let mut items = Vec::new(); - if !AppServiceSoftWare::get_software_info(&mut items) { - return Err("software collection returned no data".to_string()); + let service_ok = AppServiceSoftWare::get_services_info(&mut service_items); + let software_ok = AppServiceSoftWare::get_software_info(&mut items); + if !service_ok && !software_ok { + return Err("software/service collection returned no data".to_string()); } + let mut count = 0; + for item in service_items { + send_windows_json( + client, + 208, + json!({ + "win_user_softwareserver_flag": "1", + "win_user_server_lpsName": item.servicename, + "win_user_server_lpdName": item.displayname, + "win_user_server_lpPath": item.binarypath, + "win_user_server_lpDescr": item.description, + "win_user_server_status": item.currentstate, + }), + )?; + count += 1; + } for item in items { send_windows_json( client, @@ -170,6 +220,76 @@ fn collect_software(client: &mut Client) -> Result { Ok(count) } +fn collect_directory(client: &mut Client, task: &Task) -> Result { + let path = task_path(task)?; + let item = AppFile::get_directory_info(&path) + .ok_or_else(|| "directory collection returned no data".to_string())?; + + send_windows_json( + client, + 209, + json!({ + "win_user_driectinfo_flag": "1", + "win_user_driectinfo_filecout": item.filecount.to_string(), + "win_user_driectinfo_size": item.directsize.to_string(), + }), + )?; + + let mut count = 1; + for file in item.file_array { + send_windows_json( + client, + 209, + json!({ + "win_user_driectinfo_flag": "2", + "win_user_driectinfo_filename": file.filename, + "win_user_driectinfo_filePath": file.filepath, + "win_user_driectinfo_fileSize": file.filesize.to_string(), + }), + )?; + count += 1; + } + + Ok(count) +} + +fn collect_file_info(client: &mut Client, task: &Task) -> Result { + let path = task_path(task)?; + let item = AppFile::get_file_info(&path) + .ok_or_else(|| "file collection returned no data".to_string())?; + + send_windows_json( + client, + 210, + json!({ + "win_user_fileinfo_filename": item.filename, + "win_user_fileinfo_dwFileAttributes": item.fileattributes, + "win_user_fileinfo_dwFileAttributesHide": item.fileattributes_hide, + "win_user_fileinfo_md5": item.filemd5, + "win_user_fileinfo_m_seFileSizeof": item.filesize, + "win_user_fileinfo_seFileAccess": item.fileaccess, + "win_user_fileinfo_seFileCreate": item.filecreate, + "win_user_fileinfo_seFileModify": item.filemodify, + }), + )?; + + Ok(1) +} + +fn task_path(task: &Task) -> Result { + let path = if task.data.is_empty() { + task.object_name.trim() + } else { + task.data.trim() + }; + + if path.is_empty() { + return Err("task path is empty".to_string()); + } + + Ok(path.to_string()) +} + fn send_windows_json(client: &mut Client, data_type: i32, value: Value) -> Result<(), String> { let udata = serde_json::to_string(&value).map_err(|err| err.to_string())?; let rec = Record { diff --git a/plugins/wdriver/src/transport.rs b/plugins/wdriver/src/transport.rs index 0246f6a2..ca25f3f1 100644 --- a/plugins/wdriver/src/transport.rs +++ b/plugins/wdriver/src/transport.rs @@ -1,5 +1,6 @@ use std::{ io::{self, BufReader, BufWriter, Read, Write}, + sync::{Arc, Mutex}, time::{SystemTime, UNIX_EPOCH}, }; @@ -11,29 +12,33 @@ const MAX_FRAME_SIZE: usize = 1024 * 1024; pub struct Client { reader: BufReader, - writer: BufWriter, + writer: RecordWriter, +} + +#[derive(Clone)] +pub struct RecordWriter { + writer: Arc>>, } impl Client { pub fn new() -> Self { Self { reader: BufReader::with_capacity(1024 * 1024, io::stdin()), - writer: BufWriter::with_capacity(512 * 1024, io::stdout()), + writer: RecordWriter { + writer: Arc::new(Mutex::new(BufWriter::with_capacity( + 512 * 1024, + io::stdout(), + ))), + }, } } pub fn send_record(&mut self, rec: &Record) -> io::Result<()> { - let payload = rec.encode_to_vec(); - if payload.len() > MAX_FRAME_SIZE { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "record frame too large", - )); - } - self.writer - .write_all(&(payload.len() as u32).to_le_bytes())?; - self.writer.write_all(&payload)?; - self.writer.flush() + self.writer.send_record(rec) + } + + pub fn record_writer(&self) -> RecordWriter { + self.writer.clone() } pub fn receive_task(&mut self) -> io::Result { @@ -52,6 +57,25 @@ impl Client { } } +impl RecordWriter { + pub fn send_record(&self, rec: &Record) -> io::Result<()> { + let payload = rec.encode_to_vec(); + if payload.len() > MAX_FRAME_SIZE { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "record frame too large", + )); + } + let mut writer = self + .writer + .lock() + .map_err(|_| io::Error::new(io::ErrorKind::Other, "record writer lock poisoned"))?; + writer.write_all(&(payload.len() as u32).to_le_bytes())?; + writer.write_all(&payload)?; + writer.flush() + } +} + pub fn unix_timestamp() -> i64 { SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/plugins/wdriver/src/util/log.rs b/plugins/wdriver/src/util/log.rs index c888cff4..a8de7f3c 100644 --- a/plugins/wdriver/src/util/log.rs +++ b/plugins/wdriver/src/util/log.rs @@ -1,5 +1,5 @@ -use std::path::PathBuf; use log::LevelFilter; +use std::path::PathBuf; pub fn init_log(curr_path: &PathBuf) -> bool { let debug_mode = { @@ -50,7 +50,11 @@ pub fn init_log(curr_path: &PathBuf) -> bool { } log::info!("------------------------------------------------------------------"); - log::info!("process path:{:?} client version:{}", curr_path, version::version!()); + log::info!( + "process path:{:?} client version:{}", + curr_path, + version::version!() + ); debug_mode } diff --git a/plugins/wdriver/src/util/mod.rs b/plugins/wdriver/src/util/mod.rs index b2d62120..e6d6c96a 100644 --- a/plugins/wdriver/src/util/mod.rs +++ b/plugins/wdriver/src/util/mod.rs @@ -1,5 +1,5 @@ pub mod log; pub mod util; -pub mod windows_services; pub mod windows_installed; -pub mod windwos_autostart; \ No newline at end of file +pub mod windows_services; +pub mod windwos_autostart; diff --git a/plugins/wdriver/src/util/util.rs b/plugins/wdriver/src/util/util.rs index e69de29b..8b137891 100644 --- a/plugins/wdriver/src/util/util.rs +++ b/plugins/wdriver/src/util/util.rs @@ -0,0 +1 @@ + diff --git a/plugins/wdriver/src/util/windows_services.rs b/plugins/wdriver/src/util/windows_services.rs index f766f573..fbdddda7 100644 --- a/plugins/wdriver/src/util/windows_services.rs +++ b/plugins/wdriver/src/util/windows_services.rs @@ -1,12 +1,11 @@ pub struct Service; impl Service { - pub unsafe fn get_services_info() { // let schandle = OpenSCManagerA ( - - // 0 as i32, + + // 0 as i32, // SC_MANAGER_ENUMERATE_SERVICE // ); // println!("schandle: {:x?}",schandle); @@ -14,15 +13,15 @@ impl Service { // let mut bytesneeded = 0; // let mut numofservices = 0; // EnumServicesStatusExA( - // schandle.unwrap(), - // SC_ENUM_PROCESS_INFO, - // SERVICE_WIN32, - // SERVICE_STATE_ALL, - // std::ptr::null_mut(), - // 0, - // &mut bytesneeded, - // &mut numofservices, - // std::ptr::null_mut(), + // schandle.unwrap(), + // SC_ENUM_PROCESS_INFO, + // SERVICE_WIN32, + // SERVICE_STATE_ALL, + // std::ptr::null_mut(), + // 0, + // &mut bytesneeded, + // &mut numofservices, + // std::ptr::null_mut(), // std::ptr::null_mut() // ); @@ -30,64 +29,64 @@ impl Service { // println!("number of services : {}",numofservices); // let baseptr = VirtualAlloc( - // std::ptr::null_mut(), - // bytesneeded as usize, + // std::ptr::null_mut(), + // bytesneeded as usize, // 0x1000|0x2000, 0x40 // ); // EnumServicesStatusExA( - // schandle.unwrap(), - // SC_ENUM_PROCESS_INFO, - // SERVICE_WIN32, - // SERVICE_STATE_ALL, - // baseptr as *mut u8, - // bytesneeded, - // &mut bytesneeded, - // &mut numofservices, - // std::ptr::null_mut(), + // schandle.unwrap(), + // SC_ENUM_PROCESS_INFO, + // SERVICE_WIN32, + // SERVICE_STATE_ALL, + // baseptr as *mut u8, + // bytesneeded, + // &mut bytesneeded, + // &mut numofservices, + // std::ptr::null_mut(), // std::ptr::null_mut() // ); // println!("bytes needed: {}",bytesneeded); // println!("number of services : {}",numofservices); - + // //let mut enumservices = std::mem::zeroed::(); - // for i in 0..numofservices{ + // for i in 0..numofservices{ // let mut enumservices = (*((baseptr as isize + (i as isize *std::mem::size_of::() as isize)) as *mut ENUM_SERVICE_STATUS_PROCESSA)); // let dname = ReadStringFromMemory(GetCurrentProcess(), enumservices.lpDisplayName as *mut c_void); // let sname = ReadStringFromMemory(GetCurrentProcess(), enumservices.lpServiceName as *mut c_void); // //println!(" service display name: {}",dname); // println!("service name: {}, pid: {}",sname,enumservices.ServiceStatusProcess.dwProcessId); // let servicehandle = OpenServiceA( - // schandle.unwrap(), - // enumservices.lpServiceName, + // schandle.unwrap(), + // enumservices.lpServiceName, // SERVICE_QUERY_CONFIG // ); // let mut sbytes = 0; // QueryServiceConfigA( - // servicehandle.unwrap(), - // std::ptr::null_mut(), + // servicehandle.unwrap(), + // std::ptr::null_mut(), // 0, &mut sbytes); // let sbase =VirtualAlloc(std::ptr::null_mut(), sbytes as usize, 0x1000|0x2000, 0x40); // QueryServiceConfigA( - // servicehandle.unwrap(), - // sbase as *mut QUERY_SERVICE_CONFIGA, + // servicehandle.unwrap(), + // sbase as *mut QUERY_SERVICE_CONFIGA, // sbytes, &mut sbytes); // let sconfig = (*(sbase as *mut QUERY_SERVICE_CONFIGA)); // let binpath = ReadStringFromMemory( - // GetCurrentProcess(), + // GetCurrentProcess(), // sconfig.lpBinaryPathName as *mut c_void // ); // if !binpath.contains("\""){ // println!("binary path: {}",binpath); // VirtualFree(sbase, 0, 0x8000); - // } + // } // } // Foundation::VirtualFree(baseptr, 0, 0x8000); // /*let mut bytesneeded = 0; - // let res = QueryServiceConfigA(schandle, - // std::ptr::null_mut(), + // let res = QueryServiceConfigA(schandle, + // std::ptr::null_mut(), // 0, &mut bytesneeded); // println!("res: {}",res); diff --git a/plugins/wdriver/src/util/windwos_autostart.rs b/plugins/wdriver/src/util/windwos_autostart.rs index 270105f3..4f0e0639 100644 --- a/plugins/wdriver/src/util/windwos_autostart.rs +++ b/plugins/wdriver/src/util/windwos_autostart.rs @@ -1,4 +1,7 @@ -use std::error::Error; +use std::{error::Error, process::Command}; + +use serde::Deserialize; +use serde_json::Value; use winreg::enums::*; use winreg::reg_key::RegKey; use winreg::reg_value::RegValue; @@ -12,6 +15,29 @@ pub struct App { name: String, reg: RegValue, } + +pub struct ScheduledTaskApp { + name: String, + state: u32, + last_time: u64, + next_time: u64, + command: String, +} + +#[derive(Deserialize)] +struct ScheduledTaskRow { + #[serde(rename = "TaskName")] + task_name: String, + #[serde(rename = "State")] + state: u32, + #[serde(rename = "LastTime")] + last_time: String, + #[serde(rename = "NextTime")] + next_time: String, + #[serde(rename = "TaskCommand")] + task_command: String, +} + struct AppList { autostart: RegKey, index: usize, @@ -19,11 +45,11 @@ struct AppList { impl Iterator for AppList { type Item = App; fn next(&mut self) -> Option { - let value_= self.autostart.enum_values().nth(self.index)?.ok()?; + let value_ = self.autostart.enum_values().nth(self.index)?.ok()?; self.index += 1; - Some(App { + Some(App { name: value_.0, - reg: value_.1 + reg: value_.1, }) } } @@ -92,3 +118,124 @@ impl App { Ok(chain) } } + +impl ScheduledTaskApp { + pub fn get_name(&self) -> String { + self.name.clone() + } + + pub fn get_state(&self) -> u32 { + self.state + } + + pub fn get_last_time(&self) -> u64 { + self.last_time + } + + pub fn get_next_time(&self) -> u64 { + self.next_time + } + + pub fn get_command(&self) -> String { + self.command.clone() + } +} + +pub fn list_task_scheduler() -> Result, Box> { + let script = r#" +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | ForEach-Object { + $state = switch ($_.State.ToString()) { + 'Disabled' { 1 } + 'Queued' { 2 } + 'Ready' { 3 } + 'Running' { 4 } + default { 0 } + } + + $last = '0' + if ($_.LastRunTime -and $_.LastRunTime -gt [datetime]::MinValue) { + $last = ([uint32][Math]::Truncate($_.LastRunTime.ToOADate())).ToString() + } + + $next = '0' + if ($_.NextRunTime -and $_.NextRunTime -gt [datetime]::MinValue) { + $next = ([uint32][Math]::Truncate($_.NextRunTime.ToOADate())).ToString() + } + + $action = $_.Actions | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Execute) } | Select-Object -First 1 + $command = '' + if ($action) { + if ([string]::IsNullOrWhiteSpace($action.Arguments)) { + $command = $action.Execute + } + else { + $command = "$($action.Execute)&$($action.Arguments)" + } + } + + [PSCustomObject]@{ + TaskName = $_.TaskName + State = $state + LastTime = $last + NextTime = $next + TaskCommand = $command + } +} + +if ($null -eq $tasks) { + '[]' +} +else { + @($tasks) | ConvertTo-Json -Compress +} +"#; + + let output = Command::new("powershell.exe") + .args([ + "-NoProfile", + "-NonInteractive", + "-ExecutionPolicy", + "Bypass", + "-Command", + script, + ]) + .output()?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("query scheduled tasks failed: {}", stderr), + ) + .into()); + } + + parse_scheduled_tasks_json(&String::from_utf8_lossy(&output.stdout)) +} + +fn parse_scheduled_tasks_json(raw: &str) -> Result, Box> { + let raw = raw.trim(); + if raw.is_empty() { + return Ok(Vec::new()); + } + + let value: Value = serde_json::from_str(raw)?; + let rows: Vec = match value { + Value::Array(_) => serde_json::from_value(value)?, + Value::Object(_) => vec![serde_json::from_value(value)?], + _ => Vec::new(), + }; + + Ok(rows + .into_iter() + .filter(|row| !row.task_name.trim().is_empty()) + .map(|row| ScheduledTaskApp { + name: row.task_name, + state: row.state, + last_time: row.last_time.parse::().unwrap_or(0), + next_time: row.next_time.parse::().unwrap_or(0), + command: row.task_command, + }) + .collect()) +} diff --git a/plugins/wdriver/tests/common/mod.rs b/plugins/wdriver/tests/common/mod.rs new file mode 100644 index 00000000..0b41cc99 --- /dev/null +++ b/plugins/wdriver/tests/common/mod.rs @@ -0,0 +1,146 @@ +use std::{ + io::{self, BufReader, Read, Write}, + path::PathBuf, + process::{Child, ChildStdin, Command, Stdio}, + sync::mpsc::{self, Receiver}, + sync::OnceLock, + thread, + time::Duration, +}; + +use prost::Message; +use wdriver::protocol::{Record, Task}; + +pub struct TaskResult { + pub records: Vec, + pub ack: Record, +} + +pub struct PluginHarness { + child: Child, + stdin: ChildStdin, + rx: Receiver, +} + +impl PluginHarness { + pub fn new() -> Self { + let mut child = Command::new(binary_path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .expect("failed to spawn wdriver"); + let stdin = child.stdin.take().expect("missing stdin"); + let stdout = child.stdout.take().expect("missing stdout"); + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + let mut reader = BufReader::new(stdout); + while let Ok(record) = read_record(&mut reader) { + if tx.send(record).is_err() { + break; + } + } + }); + Self { child, stdin, rx } + } + + pub fn send_task(&mut self, task: &Task) { + let payload = task.encode_to_vec(); + self.stdin + .write_all(&(payload.len() as u32).to_le_bytes()) + .expect("write task length"); + self.stdin.write_all(&payload).expect("write task payload"); + self.stdin.flush().expect("flush task payload"); + } + + pub fn collect_until_ack(&self, token: &str, timeout: Duration) -> TaskResult { + let mut records = Vec::new(); + loop { + let record = self + .rx + .recv_timeout(timeout) + .expect("timed out waiting for plugin record"); + if is_ack_for(&record, token) { + return TaskResult { records, ack: record }; + } + records.push(record); + } + } + + pub fn collect_until_data_type(&self, data_type: i32, timeout: Duration) -> Record { + loop { + let record = self + .rx + .recv_timeout(timeout) + .expect("timed out waiting for plugin data record"); + if record.data_type == data_type { + return record; + } + } + } + + pub fn shutdown(mut self) { + let task = Task { + data_type: 0, + object_name: String::new(), + data: String::new(), + token: "shutdown".to_string(), + }; + self.send_task(&task); + let _ = self.collect_until_ack("shutdown", Duration::from_secs(5)); + let _ = self.child.wait(); + } +} + +fn binary_path() -> PathBuf { + static BIN_PATH: OnceLock = OnceLock::new(); + BIN_PATH + .get_or_init(|| { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let exe = manifest_dir.join("target").join("debug").join(if cfg!(windows) { + "wdriver.exe" + } else { + "wdriver" + }); + if !exe.exists() { + let status = Command::new("cargo") + .args(["build", "--bin", "wdriver"]) + .current_dir(&manifest_dir) + .status() + .expect("build wdriver binary"); + assert!(status.success(), "failed to build wdriver binary for tests"); + } + exe + }) + .clone() +} + +fn is_ack_for(record: &Record, token: &str) -> bool { + if record.data_type != 5100 { + return false; + } + record + .data + .as_ref() + .and_then(|payload| payload.fields.get("token")) + .map(|value| value == token) + .unwrap_or(false) +} + +fn read_record(reader: &mut BufReader) -> io::Result { + let mut len = [0u8; 4]; + reader.read_exact(&mut len)?; + let len = u32::from_le_bytes(len) as usize; + let mut payload = vec![0u8; len]; + reader.read_exact(&mut payload)?; + Record::decode(payload.as_slice()).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) +} + +pub fn ack_status(record: &Record) -> String { + record + .data + .as_ref() + .and_then(|payload| payload.fields.get("status")) + .cloned() + .unwrap_or_default() +} diff --git a/plugins/wdriver/tests/ts_appcore.rs b/plugins/wdriver/tests/ts_appcore.rs index b82f0c89..36882e46 100644 --- a/plugins/wdriver/tests/ts_appcore.rs +++ b/plugins/wdriver/tests/ts_appcore.rs @@ -1,57 +1,101 @@ -use wdriver::appcore::app_account; -use wdriver::appcore::app_autostart; -use wdriver::appcore::app_include; -use wdriver::appcore::app_net; -use wdriver::appcore::app_process; -use wdriver::appcore::app_service_software; +use std::{ + fs, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, +}; + +use wdriver::appcore::{ + app_account::AppAccount, app_autostart::AppAutoStart, app_file::AppFile, + app_include::{ + AppFileExInfo, AppFileInfo, AppRegRunInfo, AppServiceInfo, AppSoftWareInfo, + AppTaskSchedulerRunInfo, + }, + app_net::AppNetwork, app_process::AppProcess, app_service_software::AppServiceSoftWare, +}; + +#[test] +fn unit_collect_process_snapshot() { + let mut items = Vec::new(); + assert!(AppProcess::get_process_info(&mut items)); + assert!(!items.is_empty()); + assert!(items.iter().any(|item| item.pid > 0)); +} + +#[test] +fn unit_collect_network_snapshot() { + let mut items = Vec::new(); + assert!(AppNetwork::get_socket_info(&mut items)); + assert!(!items.is_empty()); + assert!( + items.iter() + .all(|item| !item.protocol.is_empty() && !item.localaddress.is_empty()) + ); +} #[test] -pub fn unit_test_account() { - let b = app_account::AppAccount::init(); - if b { - println!("account init success."); - } +fn unit_collect_account_snapshot() { + let mut items = Vec::new(); + assert!(AppAccount::get_account_info(&mut items)); + assert!(!items.is_empty()); + assert!(items.iter().all(|item| !item.serveruser.is_empty())); } #[test] -pub fn unit_test_network() { - let b = app_net::AppNetwork::init(); - if b { - println!("network init success."); - } +fn unit_collect_autorun_registry_snapshot() { + let mut items: Vec = vec![]; + assert!(AppAutoStart::get_astart_register(&mut items)); + assert!(!items.is_empty()); } #[test] -pub fn unit_test_processinfo() { - let b = app_process::AppProcess::init(); - if b { - println!("process init success."); - } +fn unit_collect_autorun_task_scheduler_snapshot() { + let mut items: Vec = vec![]; + assert!(AppAutoStart::get_astart_taskschedu(&mut items)); + assert!(!items.is_empty()); + assert!(items.iter().any(|item| !item.valuename.is_empty())); } #[test] -pub fn unit_test_servicesinfo() { - let mut serivce_info: Vec = vec![]; - let b = app_service_software::AppServiceSoftWare::get_services_info(&mut serivce_info); - if b { - println!("service init success."); - } +fn unit_collect_service_snapshot() { + let mut items: Vec = vec![]; + assert!(AppServiceSoftWare::get_services_info(&mut items)); + assert!(!items.is_empty()); + assert!(items.iter().any(|item| !item.servicename.is_empty())); } #[test] -pub fn unit_test_softwareinfo() { - let mut software_info: Vec = vec![]; - let b = app_service_software::AppServiceSoftWare::get_software_info(&mut software_info); - if b { - println!("software init success."); - } +fn unit_collect_software_snapshot() { + let mut items: Vec = vec![]; + assert!(AppServiceSoftWare::get_software_info(&mut items)); + assert!(!items.is_empty()); + assert!(items.iter().any(|item| !item.name.is_empty())); } #[test] -pub fn unit_test_astart_register() { - let mut astart_register: Vec = vec![]; - let b = app_autostart::AppAutoStart::get_astart_register(&mut astart_register); - if b { - println!("astart_register init success."); - } +fn unit_collect_directory_and_file_snapshot() { + let temp_dir = unique_temp_dir(); + fs::create_dir_all(&temp_dir).expect("create temp dir"); + let file_path = temp_dir.join("sample.txt"); + fs::write(&file_path, b"wdriver-test-payload").expect("write sample file"); + + let direct = AppFile::get_directory_info(temp_dir.to_string_lossy().as_ref()) + .expect("directory collection"); + assert!(direct.filecount >= 1); + assert!(direct.file_array.iter().any(|item: &AppFileInfo| item.filename == "sample.txt")); + + let file: AppFileExInfo = AppFile::get_file_info(file_path.to_string_lossy().as_ref()) + .expect("file collection"); + assert_eq!(file.filename, "sample.txt"); + assert!(!file.filemd5.is_empty()); + + let _ = fs::remove_file(file_path); + let _ = fs::remove_dir_all(temp_dir); +} + +fn unique_temp_dir() -> PathBuf { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock drift") + .as_nanos(); + std::env::temp_dir().join(format!("wdriver-appcore-{}", suffix)) } diff --git a/plugins/wdriver/tests/ts_protocol.rs b/plugins/wdriver/tests/ts_protocol.rs new file mode 100644 index 00000000..682ffce5 --- /dev/null +++ b/plugins/wdriver/tests/ts_protocol.rs @@ -0,0 +1,325 @@ +mod common; + +#[cfg(windows)] +mod tests { + use std::{ + fs, + io::{Read, Write}, + net::{TcpListener, TcpStream}, + process::Command, + thread, + time::Duration, + }; + + use serde_json::Value; + use wdriver::protocol::Task; + + use crate::common::{ack_status, PluginHarness}; + + #[test] + fn protocol_snapshot_tasks_emit_records_and_ack() { + let mut plugin = PluginHarness::new(); + + assert_snapshot_task( + &mut plugin, + Task { + data_type: 200, + object_name: String::new(), + data: String::new(), + token: "proc".to_string(), + }, + "proc", + &[ + "win_user_process_pid", + "win_user_process_pribase", + "win_user_process_thrcout", + "win_user_process_parenid", + "win_user_process_Path", + "win_user_process_szExeFile", + ], + ); + + assert_snapshot_task( + &mut plugin, + Task { + data_type: 203, + object_name: String::new(), + data: String::new(), + token: "net".to_string(), + }, + "net", + &[ + "win_user_net_flag", + "win_user_net_src", + "win_user_net_dst", + "win_user_net_status", + "win_user_net_pid", + ], + ); + + let autorun_records = assert_snapshot_task( + &mut plugin, + Task { + data_type: 202, + object_name: String::new(), + data: String::new(), + token: "autorun".to_string(), + }, + "autorun", + &["win_user_autorun_flag"], + ); + assert!(autorun_records.iter().any(|payload| { + payload["win_user_autorun_flag"].as_str() == Some("2") + && payload.get("win_user_autorun_tschname").is_some() + && payload.get("win_user_autorun_tscState").is_some() + && payload.get("win_user_autorun_tscLastTime").is_some() + && payload.get("win_user_autorun_tscNextTime").is_some() + && payload.get("win_user_autorun_tscCommand").is_some() + })); + + assert_snapshot_task( + &mut plugin, + Task { + data_type: 207, + object_name: String::new(), + data: String::new(), + token: "acct".to_string(), + }, + "acct", + &[ + "win_user_sysuser_user", + "win_user_sysuser_name", + "win_user_sysuser_sid", + "win_user_sysuser_flag", + ], + ); + + let result = assert_snapshot_task( + &mut plugin, + Task { + data_type: 208, + object_name: String::new(), + data: String::new(), + token: "soft".to_string(), + }, + "soft", + &["win_user_softwareserver_flag"], + ); + assert!( + result.iter().any(|payload| { + payload["win_user_softwareserver_flag"].as_str() == Some("1") + }) + || result + .iter() + .any(|payload| payload["win_user_softwareserver_flag"].as_str() == Some("2")) + ); + + plugin.shutdown(); + } + + #[test] + fn protocol_file_tasks_emit_records_and_ack() { + let mut plugin = PluginHarness::new(); + let temp = std::env::temp_dir().join("wdriver-protocol-dir"); + let file = temp.join("sample.txt"); + fs::create_dir_all(&temp).expect("create temp dir"); + fs::write(&file, b"protocol-file-test").expect("write temp file"); + + let directory_records = assert_snapshot_task( + &mut plugin, + Task { + data_type: 209, + object_name: String::new(), + data: temp.to_string_lossy().into_owned(), + token: "dir".to_string(), + }, + "dir", + &["win_user_driectinfo_flag"], + ); + assert!(directory_records.iter().any(|payload| { + payload["win_user_driectinfo_flag"].as_str() == Some("1") + })); + assert!(directory_records.iter().any(|payload| { + payload["win_user_driectinfo_flag"].as_str() == Some("2") + && payload["win_user_driectinfo_filename"].as_str() == Some("sample.txt") + })); + + let file_records = assert_snapshot_task( + &mut plugin, + Task { + data_type: 210, + object_name: String::new(), + data: file.to_string_lossy().into_owned(), + token: "file".to_string(), + }, + "file", + &[ + "win_user_fileinfo_filename", + "win_user_fileinfo_dwFileAttributes", + "win_user_fileinfo_dwFileAttributesHide", + "win_user_fileinfo_md5", + "win_user_fileinfo_m_seFileSizeof", + "win_user_fileinfo_seFileAccess", + "win_user_fileinfo_seFileCreate", + "win_user_fileinfo_seFileModify", + ], + ); + assert_eq!( + file_records[0]["win_user_fileinfo_filename"].as_str(), + Some("sample.txt") + ); + assert!(!file_records[0]["win_user_fileinfo_md5"].as_str().unwrap_or("").is_empty()); + + let _ = fs::remove_file(file); + let _ = fs::remove_dir_all(temp); + plugin.shutdown(); + } + + #[test] + fn protocol_etw_tasks_emit_expected_categories() { + let mut plugin = PluginHarness::new(); + for data_type in 300..=305 { + let token = format!("etw-{}", data_type); + plugin.send_task(&Task { + data_type, + object_name: String::new(), + data: String::new(), + token: token.clone(), + }); + let ack = plugin.collect_until_ack(&token, Duration::from_secs(10)).ack; + assert_eq!(ack_status(&ack), "success"); + } + + let _ = Command::new("cmd").args(["/C", "exit", "0"]).status(); + + let process_record = plugin.collect_until_data_type(300, Duration::from_secs(10)); + let process_payload = parse_udata(&process_record); + assert!(process_payload.get("win_etw_processinfo_pid").is_some()); + + let thread_record = plugin.collect_until_data_type(301, Duration::from_secs(10)); + let thread_payload = parse_udata(&thread_record); + assert!(thread_payload.get("win_etw_threadinfo_tid").is_some()); + + let image_record = plugin.collect_until_data_type(302, Duration::from_secs(10)); + let image_payload = parse_udata(&image_record); + assert!(image_payload.get("win_etw_imageinfo_fileName").is_some()); + + let listener = TcpListener::bind("127.0.0.1:0").expect("bind loopback"); + let addr = listener.local_addr().expect("listener addr"); + let accept_thread = thread::spawn(move || { + let (mut socket, _) = listener.accept().expect("accept socket"); + let mut buf = [0u8; 8]; + let _ = socket.read(&mut buf); + }); + let mut client = TcpStream::connect(addr).expect("connect loopback"); + let _ = client.write_all(b"etw"); + drop(client); + let _ = accept_thread.join(); + + let network_record = plugin.collect_until_data_type(303, Duration::from_secs(10)); + let network_payload = parse_udata(&network_record); + assert!(network_payload.get("win_network_processid").is_some()); + assert!(network_payload.get("win_network_procespath").is_some()); + assert!(network_payload.get("win_network_processpathsize").is_some()); + if network_payload["win_network_addressfamily"].as_str() == Some("2") { + assert!( + network_payload["win_network_localaddr"] + .as_str() + .unwrap_or("") + .chars() + .all(|ch| ch.is_ascii_digit()) + ); + assert!( + network_payload["win_network_remoteaddr"] + .as_str() + .unwrap_or("") + .chars() + .all(|ch| ch.is_ascii_digit()) + ); + } + let process_path = network_payload["win_network_procespath"].as_str().unwrap_or(""); + let process_path_size = network_payload["win_network_processpathsize"] + .as_str() + .unwrap_or("0") + .parse::() + .unwrap_or(0); + if !process_path.is_empty() { + assert!(process_path_size > 0); + } + + let _ = Command::new("reg") + .args([ + "add", + "HKCU\\Software\\wdriver-etw-test", + "/v", + "demo", + "/t", + "REG_SZ", + "/d", + "1", + "/f", + ]) + .status(); + let registry_record = plugin.collect_until_data_type(304, Duration::from_secs(10)); + let registry_payload = parse_udata(®istry_record); + assert!(registry_payload.get("win_etw_regtab_eventname").is_some()); + + let temp = std::env::temp_dir().join("wdriver-etw-file.txt"); + fs::write(&temp, b"file-io").expect("write etw file"); + let _ = fs::read(&temp); + let file_record = plugin.collect_until_data_type(305, Duration::from_secs(10)); + let file_payload = parse_udata(&file_record); + assert!(file_payload.get("win_etw_fileio_eventname").is_some()); + let _ = fs::remove_file(temp); + let _ = Command::new("reg") + .args(["delete", "HKCU\\Software\\wdriver-etw-test", "/f"]) + .status(); + + plugin.shutdown(); + } + + #[test] + fn protocol_repeated_etw_start_is_idempotent() { + let mut plugin = PluginHarness::new(); + for token in ["etw-a", "etw-b"] { + plugin.send_task(&Task { + data_type: 300, + object_name: String::new(), + data: String::new(), + token: token.to_string(), + }); + let ack = plugin.collect_until_ack(token, Duration::from_secs(10)).ack; + assert_eq!(ack_status(&ack), "success"); + } + plugin.shutdown(); + } + + fn assert_snapshot_task( + plugin: &mut PluginHarness, + task: Task, + token: &str, + required_keys: &[&str], + ) -> Vec { + plugin.send_task(&task); + let result = plugin.collect_until_ack(token, Duration::from_secs(20)); + assert_eq!(ack_status(&result.ack), "success"); + assert!(!result.records.is_empty()); + + let payloads: Vec = result.records.iter().map(parse_udata).collect(); + for key in required_keys { + assert!(payloads.iter().any(|payload| payload.get(*key).is_some())); + } + payloads + } + + fn parse_udata(record: &wdriver::protocol::Record) -> Value { + let payload = record + .data + .as_ref() + .expect("record payload") + .fields + .get("udata") + .expect("udata field"); + serde_json::from_str(payload).expect("parse udata") + } +}