returned existing files to origin during restruct
This commit is contained in:
parent
7fbb0fe7e1
commit
a2ed11b2fb
19 changed files with 1678 additions and 102 deletions
|
|
@ -42,15 +42,751 @@ import (
|
|||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
getDriveType = windows.NewLazySystemDLL("kernel32.dll").NewProc("GetDriveTypeW")
|
||||
)
|
||||
|
||||
func NewAgentConfig() *rmm.AgentConfig {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||
|
||||
if err != nil {
|
||||
return &rmm.AgentConfig{}
|
||||
}
|
||||
|
||||
baseurl, _, _ := k.GetStringValue("BaseURL")
|
||||
agentid, _, _ := k.GetStringValue("AgentID")
|
||||
apiurl, _, _ := k.GetStringValue("ApiURL")
|
||||
token, _, _ := k.GetStringValue("Token")
|
||||
agentpk, _, _ := k.GetStringValue("AgentPK")
|
||||
pk, _ := strconv.Atoi(agentpk)
|
||||
cert, _, _ := k.GetStringValue("Cert")
|
||||
proxy, _, _ := k.GetStringValue("Proxy")
|
||||
customMeshDir, _, _ := k.GetStringValue("MeshDir")
|
||||
|
||||
return &rmm.AgentConfig{
|
||||
BaseURL: baseurl,
|
||||
AgentID: agentid,
|
||||
APIURL: apiurl,
|
||||
Token: token,
|
||||
AgentPK: agentpk,
|
||||
PK: pk,
|
||||
Cert: cert,
|
||||
Proxy: proxy,
|
||||
CustomMeshDir: customMeshDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) {
|
||||
|
||||
content := []byte(code)
|
||||
|
||||
dir := filepath.Join(os.TempDir(), "trmm")
|
||||
if !trmm.FileExists(dir) {
|
||||
a.CreateTRMMTempDir()
|
||||
}
|
||||
|
||||
const defaultExitCode = 1
|
||||
|
||||
var (
|
||||
outb bytes.Buffer
|
||||
errb bytes.Buffer
|
||||
exe string
|
||||
ext string
|
||||
cmdArgs []string
|
||||
)
|
||||
|
||||
switch shell {
|
||||
case "powershell":
|
||||
ext = "*.ps1"
|
||||
case "python":
|
||||
ext = "*.py"
|
||||
case "cmd":
|
||||
ext = "*.bat"
|
||||
}
|
||||
|
||||
tmpfn, err := ioutil.TempFile(dir, ext)
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return "", err.Error(), 85, err
|
||||
}
|
||||
defer os.Remove(tmpfn.Name())
|
||||
|
||||
if _, err := tmpfn.Write(content); err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return "", err.Error(), 85, err
|
||||
}
|
||||
if err := tmpfn.Close(); err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return "", err.Error(), 85, err
|
||||
}
|
||||
|
||||
switch shell {
|
||||
case "powershell":
|
||||
exe = "Powershell"
|
||||
cmdArgs = []string{"-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass", tmpfn.Name()}
|
||||
case "python":
|
||||
exe = a.PyBin
|
||||
cmdArgs = []string{tmpfn.Name()}
|
||||
case "cmd":
|
||||
exe = tmpfn.Name()
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var timedOut bool = false
|
||||
cmd := exec.Command(exe, cmdArgs...)
|
||||
cmd.Stdout = &outb
|
||||
cmd.Stderr = &errb
|
||||
|
||||
if cmdErr := cmd.Start(); cmdErr != nil {
|
||||
a.Logger.Debugln(cmdErr)
|
||||
return "", cmdErr.Error(), 65, cmdErr
|
||||
}
|
||||
pid := int32(cmd.Process.Pid)
|
||||
|
||||
// custom context handling, we need to kill child procs if this is a batch script,
|
||||
// otherwise it will hang forever
|
||||
// the normal exec.CommandContext() doesn't work since it only kills the parent process
|
||||
go func(p int32) {
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
_ = KillProc(p)
|
||||
timedOut = true
|
||||
}(pid)
|
||||
|
||||
cmdErr := cmd.Wait()
|
||||
|
||||
if timedOut {
|
||||
stdout = CleanString(outb.String())
|
||||
stderr = fmt.Sprintf("%s\nScript timed out after %d seconds", CleanString(errb.String()), timeout)
|
||||
exitcode = 98
|
||||
a.Logger.Debugln("Script check timeout:", ctx.Err())
|
||||
} else {
|
||||
stdout = CleanString(outb.String())
|
||||
stderr = CleanString(errb.String())
|
||||
|
||||
// get the exit code
|
||||
if cmdErr != nil {
|
||||
if exitError, ok := cmdErr.(*exec.ExitError); ok {
|
||||
if ws, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||
exitcode = ws.ExitStatus()
|
||||
} else {
|
||||
exitcode = defaultExitCode
|
||||
}
|
||||
} else {
|
||||
exitcode = defaultExitCode
|
||||
}
|
||||
|
||||
} else {
|
||||
if ws, ok := cmd.ProcessState.Sys().(syscall.WaitStatus); ok {
|
||||
exitcode = ws.ExitStatus()
|
||||
} else {
|
||||
exitcode = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return stdout, stderr, exitcode, nil
|
||||
}
|
||||
|
||||
func SetDetached() *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
}
|
||||
|
||||
func CMD(exe string, args []string, timeout int, detached bool) (output [2]string, e error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var outb, errb bytes.Buffer
|
||||
cmd := exec.CommandContext(ctx, exe, args...)
|
||||
if detached {
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
}
|
||||
cmd.Stdout = &outb
|
||||
cmd.Stderr = &errb
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return [2]string{"", ""}, fmt.Errorf("%s: %s", err, CleanString(errb.String()))
|
||||
}
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return [2]string{"", ""}, ctx.Err()
|
||||
}
|
||||
|
||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, nil
|
||||
}
|
||||
|
||||
func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) {
|
||||
var (
|
||||
outb bytes.Buffer
|
||||
errb bytes.Buffer
|
||||
cmd *exec.Cmd
|
||||
timedOut = false
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if len(cmdArgs) > 0 && command == "" {
|
||||
switch shell {
|
||||
case "cmd":
|
||||
cmdArgs = append([]string{"/C"}, cmdArgs...)
|
||||
cmd = exec.Command("cmd.exe", cmdArgs...)
|
||||
case "powershell":
|
||||
cmdArgs = append([]string{"-NonInteractive", "-NoProfile"}, cmdArgs...)
|
||||
cmd = exec.Command("powershell.exe", cmdArgs...)
|
||||
}
|
||||
} else {
|
||||
switch shell {
|
||||
case "cmd":
|
||||
cmd = exec.Command("cmd.exe")
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CmdLine: fmt.Sprintf("cmd.exe /C %s", command),
|
||||
}
|
||||
case "powershell":
|
||||
cmd = exec.Command("Powershell", "-NonInteractive", "-NoProfile", command)
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
|
||||
if detached {
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
}
|
||||
cmd.Stdout = &outb
|
||||
cmd.Stderr = &errb
|
||||
cmd.Start()
|
||||
|
||||
pid := int32(cmd.Process.Pid)
|
||||
|
||||
go func(p int32) {
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
_ = KillProc(p)
|
||||
timedOut = true
|
||||
}(pid)
|
||||
|
||||
err := cmd.Wait()
|
||||
|
||||
if timedOut {
|
||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, ctx.Err()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, err
|
||||
}
|
||||
|
||||
return [2]string{CleanString(outb.String()), CleanString(errb.String())}, nil
|
||||
}
|
||||
|
||||
// GetDisks returns a list of fixed disks
|
||||
func (a *Agent) GetDisks() []trmm.Disk {
|
||||
ret := make([]trmm.Disk, 0)
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
return ret
|
||||
}
|
||||
|
||||
for _, p := range partitions {
|
||||
typepath, _ := windows.UTF16PtrFromString(p.Device)
|
||||
typeval, _, _ := getDriveType.Call(uintptr(unsafe.Pointer(typepath)))
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
|
||||
if typeval != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
usage, err := disk.Usage(p.Mountpoint)
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
d := trmm.Disk{
|
||||
Device: p.Device,
|
||||
Fstype: p.Fstype,
|
||||
Total: ByteCountSI(usage.Total),
|
||||
Used: ByteCountSI(usage.Used),
|
||||
Free: ByteCountSI(usage.Free),
|
||||
Percent: int(usage.UsedPercent),
|
||||
}
|
||||
ret = append(ret, d)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// LoggedOnUser returns the first logged on user it finds
|
||||
func (a *Agent) LoggedOnUser() string {
|
||||
pyCode := `
|
||||
import psutil
|
||||
|
||||
try:
|
||||
u = psutil.users()[0].name
|
||||
if u.isascii():
|
||||
print(u, end='')
|
||||
else:
|
||||
print('notascii', end='')
|
||||
except Exception as e:
|
||||
print("None", end='')
|
||||
|
||||
`
|
||||
// try with psutil first, if fails, fallback to golang
|
||||
user, err := a.RunPythonCode(pyCode, 5, []string{})
|
||||
if err == nil && user != "notascii" {
|
||||
return user
|
||||
}
|
||||
|
||||
users, err := wapf.ListLoggedInUsers()
|
||||
if err != nil {
|
||||
a.Logger.Debugln("LoggedOnUser error", err)
|
||||
return "None"
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
return "None"
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
// remove the computername or domain
|
||||
return strings.Split(u.FullUser(), `\`)[1]
|
||||
}
|
||||
return "None"
|
||||
}
|
||||
|
||||
// ShowStatus prints windows service status
|
||||
// If called from an interactive desktop, pops up a message box
|
||||
// Otherwise prints to the console
|
||||
func ShowStatus(version string) {
|
||||
statusMap := make(map[string]string)
|
||||
svcs := []string{winSvcName, meshSvcName}
|
||||
|
||||
for _, service := range svcs {
|
||||
status, err := GetServiceStatus(service)
|
||||
if err != nil {
|
||||
statusMap[service] = "Not Installed"
|
||||
continue
|
||||
}
|
||||
statusMap[service] = status
|
||||
}
|
||||
|
||||
window := w32.GetForegroundWindow()
|
||||
if window != 0 {
|
||||
_, consoleProcID := w32.GetWindowThreadProcessId(window)
|
||||
if w32.GetCurrentProcessId() == consoleProcID {
|
||||
w32.ShowWindow(window, w32.SW_HIDE)
|
||||
}
|
||||
var handle w32.HWND
|
||||
msg := fmt.Sprintf("Agent: %s\n\nMesh Agent: %s", statusMap[winSvcName], statusMap[meshSvcName])
|
||||
w32.MessageBox(handle, msg, fmt.Sprintf("Tactical RMM v%s", version), w32.MB_OK|w32.MB_ICONINFORMATION)
|
||||
} else {
|
||||
fmt.Println("Tactical RMM Version", version)
|
||||
fmt.Println("Tactical Agent:", statusMap[winSvcName])
|
||||
fmt.Println("Mesh Agent:", statusMap[meshSvcName])
|
||||
}
|
||||
}
|
||||
|
||||
// PatchMgmnt enables/disables automatic update
|
||||
// 0 - Enable Automatic Updates (Default)
|
||||
// 1 - Disable Automatic Updates
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd939844(v=ws.10)?redirectedfrom=MSDN
|
||||
func (a *Agent) PatchMgmnt(enable bool) error {
|
||||
var val uint32
|
||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if enable {
|
||||
val = 1
|
||||
} else {
|
||||
val = 0
|
||||
}
|
||||
|
||||
err = k.SetDWordValue("AUOptions", val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) PlatVer() (string, error) {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return "n/a", err
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
dv, _, err := k.GetStringValue("DisplayVersion")
|
||||
if err == nil {
|
||||
return dv, nil
|
||||
}
|
||||
|
||||
relid, _, err := k.GetStringValue("ReleaseId")
|
||||
if err != nil {
|
||||
return "n/a", err
|
||||
}
|
||||
return relid, nil
|
||||
}
|
||||
|
||||
// EnablePing enables ping
|
||||
func EnablePing() {
|
||||
args := make([]string, 0)
|
||||
cmd := `netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow`
|
||||
_, err := CMDShell("cmd", args, cmd, 10, false)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// EnableRDP enables Remote Desktop
|
||||
func EnableRDP() {
|
||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Terminal Server`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
err = k.SetDWordValue("fDenyTSConnections", 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
args := make([]string, 0)
|
||||
cmd := `netsh advfirewall firewall set rule group="remote desktop" new enable=Yes`
|
||||
_, cerr := CMDShell("cmd", args, cmd, 10, false)
|
||||
if cerr != nil {
|
||||
fmt.Println(cerr)
|
||||
}
|
||||
}
|
||||
|
||||
// DisableSleepHibernate disables sleep and hibernate
|
||||
func DisableSleepHibernate() {
|
||||
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Power`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
err = k.SetDWordValue("HiberbootEnabled", 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
args := make([]string, 0)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
currents := []string{"ac", "dc"}
|
||||
for _, i := range currents {
|
||||
wg.Add(1)
|
||||
go func(c string) {
|
||||
defer wg.Done()
|
||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /set%svalueindex scheme_current sub_buttons lidaction 0", c), 5, false)
|
||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -standby-timeout-%s 0", c), 5, false)
|
||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -hibernate-timeout-%s 0", c), 5, false)
|
||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -disk-timeout-%s 0", c), 5, false)
|
||||
_, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -monitor-timeout-%s 0", c), 5, false)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
_, _ = CMDShell("cmd", args, "powercfg -S SCHEME_CURRENT", 5, false)
|
||||
}
|
||||
|
||||
// NewCOMObject creates a new COM object for the specifed ProgramID.
|
||||
func NewCOMObject(id string) (*ole.IDispatch, error) {
|
||||
unknown, err := oleutil.CreateObject(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create initial unknown object: %v", err)
|
||||
}
|
||||
defer unknown.Release()
|
||||
|
||||
obj, err := unknown.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create query interface: %v", err)
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// SystemRebootRequired checks whether a system reboot is required.
|
||||
func (a *Agent) SystemRebootRequired() (bool, error) {
|
||||
regKeys := []string{
|
||||
`SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired`,
|
||||
}
|
||||
for _, key := range regKeys {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE)
|
||||
if err == nil {
|
||||
k.Close()
|
||||
return true, nil
|
||||
} else if err != registry.ErrNotExist {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (a *Agent) SendSoftware() {
|
||||
sw := a.GetInstalledSoftware()
|
||||
a.Logger.Debugln(sw)
|
||||
|
||||
payload := map[string]interface{}{"agent_id": a.AgentID, "software": sw}
|
||||
_, err := a.rClient.R().SetBody(payload).Post("/api/v3/software/")
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) UninstallCleanup() {
|
||||
registry.DeleteKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`)
|
||||
a.PatchMgmnt(false)
|
||||
a.CleanupAgentUpdates()
|
||||
CleanupSchedTasks()
|
||||
}
|
||||
|
||||
func (a *Agent) AgentUpdate(url, inno, version string) {
|
||||
time.Sleep(time.Duration(randRange(1, 15)) * time.Second)
|
||||
a.KillHungUpdates()
|
||||
a.CleanupAgentUpdates()
|
||||
updater := filepath.Join(a.ProgramDir, inno)
|
||||
a.Logger.Infof("Agent updating from %s to %s", a.Version, version)
|
||||
a.Logger.Infoln("Downloading agent update from", url)
|
||||
|
||||
rClient := resty.New()
|
||||
rClient.SetCloseConnection(true)
|
||||
rClient.SetTimeout(15 * time.Minute)
|
||||
rClient.SetDebug(a.Debug)
|
||||
if len(a.Proxy) > 0 {
|
||||
rClient.SetProxy(a.Proxy)
|
||||
}
|
||||
r, err := rClient.R().SetOutput(updater).Get(url)
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
CMD("net", []string{"start", winSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
if r.IsError() {
|
||||
a.Logger.Errorln("Download failed with status code", r.StatusCode())
|
||||
CMD("net", []string{"start", winSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "tacticalrmm")
|
||||
if err != nil {
|
||||
a.Logger.Errorln("Agentupdate create tempdir:", err)
|
||||
CMD("net", []string{"start", winSvcName}, 10, false)
|
||||
return
|
||||
}
|
||||
|
||||
innoLogFile := filepath.Join(dir, "tacticalrmm.txt")
|
||||
|
||||
args := []string{"/C", updater, "/VERYSILENT", fmt.Sprintf("/LOG=%s", innoLogFile)}
|
||||
cmd := exec.Command("cmd.exe", args...)
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
cmd.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
func (a *Agent) osString() string {
|
||||
host, _ := ps.Host()
|
||||
info := host.Info()
|
||||
osInf := info.OS
|
||||
|
||||
var arch string
|
||||
switch info.Architecture {
|
||||
case "x86_64":
|
||||
arch = "64 bit"
|
||||
case "x86":
|
||||
arch = "32 bit"
|
||||
}
|
||||
|
||||
var osFullName string
|
||||
platver, err := a.PlatVer()
|
||||
if err != nil {
|
||||
osFullName = fmt.Sprintf("%s, %s (build %s)", osInf.Name, arch, osInf.Build)
|
||||
} else {
|
||||
osFullName = fmt.Sprintf("%s, %s v%s (build %s)", osInf.Name, arch, platver, osInf.Build)
|
||||
}
|
||||
return osFullName
|
||||
}
|
||||
|
||||
func (a *Agent) AgentUninstall(code string) {
|
||||
a.KillHungUpdates()
|
||||
tacUninst := filepath.Join(a.ProgramDir, a.GetUninstallExe())
|
||||
args := []string{"/C", tacUninst, "/VERYSILENT"}
|
||||
cmd := exec.Command("cmd.exe", args...)
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
cmd.Start()
|
||||
}
|
||||
|
||||
func (a *Agent) addDefenderExlusions() {
|
||||
code := `
|
||||
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm\*'
|
||||
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
||||
`
|
||||
_, _, _, err := a.RunScript(code, "powershell", []string{}, 20)
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// RunMigrations cleans up unused stuff from older agents
|
||||
func (a *Agent) RunMigrations() {
|
||||
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
||||
nssm := filepath.Join(a.ProgramDir, i)
|
||||
if trmm.FileExists(nssm) {
|
||||
os.Remove(nssm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) installMesh(meshbin, exe, proxy string) (string, error) {
|
||||
var meshNodeID string
|
||||
meshInstallArgs := []string{"-fullinstall"}
|
||||
if len(proxy) > 0 {
|
||||
meshProxy := fmt.Sprintf("--WebProxy=%s", proxy)
|
||||
meshInstallArgs = append(meshInstallArgs, meshProxy)
|
||||
}
|
||||
a.Logger.Debugln("Mesh install args:", meshInstallArgs)
|
||||
|
||||
meshOut, meshErr := CMD(meshbin, meshInstallArgs, int(90), false)
|
||||
if meshErr != nil {
|
||||
fmt.Println(meshOut[0])
|
||||
fmt.Println(meshOut[1])
|
||||
fmt.Println(meshErr)
|
||||
}
|
||||
|
||||
fmt.Println(meshOut)
|
||||
a.Logger.Debugln("Sleeping for 5")
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
meshSuccess := false
|
||||
|
||||
for !meshSuccess {
|
||||
a.Logger.Debugln("Getting mesh node id")
|
||||
pMesh, pErr := CMD(exe, []string{"-nodeid"}, int(30), false)
|
||||
if pErr != nil {
|
||||
a.Logger.Errorln(pErr)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
if pMesh[1] != "" {
|
||||
a.Logger.Errorln(pMesh[1])
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
meshNodeID = StripAll(pMesh[0])
|
||||
a.Logger.Debugln("Node id:", meshNodeID)
|
||||
if strings.Contains(strings.ToLower(meshNodeID), "not defined") {
|
||||
a.Logger.Errorln(meshNodeID)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
meshSuccess = true
|
||||
}
|
||||
|
||||
return meshNodeID, nil
|
||||
}
|
||||
|
||||
// ChecksRunning prevents duplicate checks from running
|
||||
// Have to do it this way, can't use atomic because they can run from both rpc and tacticalagent services
|
||||
func (a *Agent) ChecksRunning() bool {
|
||||
running := false
|
||||
procs, err := ps.Processes()
|
||||
if err != nil {
|
||||
return running
|
||||
}
|
||||
|
||||
Out:
|
||||
for _, process := range procs {
|
||||
p, err := process.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if p.PID == 0 {
|
||||
continue
|
||||
}
|
||||
if p.Exe != a.EXE {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, arg := range p.Args {
|
||||
if arg == "runchecks" || arg == "checkrunner" {
|
||||
running = true
|
||||
break Out
|
||||
}
|
||||
}
|
||||
}
|
||||
return running
|
||||
}
|
||||
|
||||
func (a *Agent) GetPython(force bool) {
|
||||
if trmm.FileExists(a.PyBin) && !force {
|
||||
return
|
||||
}
|
||||
|
||||
var archZip string
|
||||
var folder string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
archZip = "py38-x64.zip"
|
||||
folder = "py38-x64"
|
||||
case "386":
|
||||
archZip = "py38-x32.zip"
|
||||
folder = "py38-x32"
|
||||
}
|
||||
pyFolder := filepath.Join(a.ProgramDir, folder)
|
||||
pyZip := filepath.Join(a.ProgramDir, archZip)
|
||||
a.Logger.Debugln(pyZip)
|
||||
a.Logger.Debugln(a.PyBin)
|
||||
defer os.Remove(pyZip)
|
||||
|
||||
if force {
|
||||
os.RemoveAll(pyFolder)
|
||||
}
|
||||
|
||||
rClient := resty.New()
|
||||
rClient.SetTimeout(20 * time.Minute)
|
||||
rClient.SetRetryCount(10)
|
||||
rClient.SetRetryWaitTime(1 * time.Minute)
|
||||
rClient.SetRetryMaxWaitTime(15 * time.Minute)
|
||||
if len(a.Proxy) > 0 {
|
||||
rClient.SetProxy(a.Proxy)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://github.com/amidaware/rmmagent/releases/download/v2.0.0/%s", archZip)
|
||||
a.Logger.Debugln(url)
|
||||
r, err := rClient.R().SetOutput(pyZip).Get(url)
|
||||
if err != nil {
|
||||
a.Logger.Errorln("Unable to download py3.zip from github.", err)
|
||||
return
|
||||
}
|
||||
if r.IsError() {
|
||||
a.Logger.Errorln("Unable to download py3.zip from github. Status code", r.StatusCode())
|
||||
return
|
||||
}
|
||||
|
||||
err = Unzip(pyZip, a.ProgramDir)
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) RecoverMesh() {
|
||||
a.Logger.Infoln("Attempting mesh recovery")
|
||||
|
|
@ -61,9 +797,33 @@ func (a *Agent) RecoverMesh() {
|
|||
a.SyncMeshNodeID()
|
||||
}
|
||||
|
||||
func (a *Agent) getMeshNodeID() (string, error) {
|
||||
out, err := CMD(a.MeshSystemBin, []string{"-nodeid"}, 10, false)
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
stdout := out[0]
|
||||
stderr := out[1]
|
||||
|
||||
if stderr != "" {
|
||||
a.Logger.Debugln(stderr)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if stdout == "" || strings.Contains(strings.ToLower(StripAll(stdout)), "not defined") {
|
||||
a.Logger.Debugln("Failed getting mesh node id", stdout)
|
||||
return "", errors.New("failed to get mesh node id")
|
||||
}
|
||||
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
func (a *Agent) Start(_ service.Service) error {
|
||||
go a.RunRPC()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Stop(_ service.Service) error {
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue