github.com/iDigitalFlame/xmt@v0.5.4/cmd/exec_windows.go (about) 1 //go:build windows 2 // +build windows 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package cmd 21 22 import ( 23 "context" 24 "io" 25 "os" 26 "os/exec" 27 "runtime" 28 "strings" 29 "sync" 30 "sync/atomic" 31 "syscall" 32 "unsafe" 33 34 "github.com/iDigitalFlame/xmt/cmd/filter" 35 "github.com/iDigitalFlame/xmt/device/winapi" 36 "github.com/iDigitalFlame/xmt/util/bugtrack" 37 "github.com/iDigitalFlame/xmt/util/xerr" 38 ) 39 40 // NOTE(dij): This needs to be a var as if it's a const 'UpdateProcThreadAttribute' 41 // will throw an access violation. 42 // 43 // 0x100100000000 - PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON | 44 // PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON 45 var secProtect uint64 = 0x100100000000 46 var envOnce struct { 47 r string 48 e []string 49 sync.Once 50 } 51 var verOnce struct { 52 _ [0]func() 53 sync.Once 54 e, a bool 55 } 56 57 type closer uintptr 58 type file interface { 59 File() (*os.File, error) 60 } 61 type fileFd interface { 62 Fd() uintptr 63 } 64 type executable struct { 65 r *os.File 66 filter *filter.Filter 67 title string 68 user, domain, pass string 69 closers []io.Closer 70 i winapi.ProcessInformation 71 token, parent, m uintptr 72 sf, x, y, w, h uint32 73 mode uint16 74 } 75 76 func envOnceFunc() { 77 envOnce.e = syscall.Environ()[4:] // Removes all '=' prefixed vars 78 if envOnce.r, _ = syscall.Getenv(sysRoot); len(envOnce.e) == 0 || len(envOnce.r) == 0 { 79 var ( 80 v = winapi.SystemDirectory() 81 x = strings.LastIndexByte(v, '\\') 82 ) 83 if x > 6 { 84 envOnce.r = v[:x] 85 } else { 86 envOnce.r = v 87 } 88 } 89 } 90 func verOnceFunc() { 91 if m, x, _ := winapi.GetVersionNumbers(); m > 6 { 92 verOnce.e, verOnce.a = true, true 93 } else { 94 verOnce.e, verOnce.a = m >= 6 && x >= 3, m >= 6 95 } 96 } 97 func checkVersion() bool { 98 verOnce.Do(verOnceFunc) 99 return verOnce.a 100 } 101 func checkVersionSec() bool { 102 verOnce.Do(verOnceFunc) 103 return verOnce.e 104 } 105 func (e *executable) close() { 106 if atomic.LoadUintptr(&e.i.Process) == 0 { 107 return 108 } 109 for i := range e.closers { 110 e.closers[i].Close() 111 } 112 e.parent, e.closers = 0, nil 113 if atomic.StoreUintptr(&e.i.Process, 0); e.m > 0 { 114 winapi.SetEvent(e.m) 115 } 116 } 117 func (c closer) Close() error { 118 return winapi.CloseHandle(uintptr(c)) 119 } 120 func wait(h, m uintptr) error { 121 if m == 0 { 122 if _, err := winapi.WaitForSingleObject(h, -1); err != nil { 123 return err 124 } 125 return nil 126 } 127 if _, err := winapi.WaitForMultipleObjects([]uintptr{h, m}, false, -1); err != nil { 128 return err 129 } 130 return nil 131 } 132 func (e *executable) Pid() uint32 { 133 return e.i.ProcessID 134 } 135 136 // ResumeProcess will attempt to resume the process via its PID. This will 137 // attempt to resume the process using an OS-dependent syscall. 138 // 139 // This will not affect already running processes. 140 func ResumeProcess(p uint32) error { 141 // 0x800 - PROCESS_SUSPEND_RESUME 142 h, err := winapi.OpenProcess(0x800, false, p) 143 if err != nil { 144 return err 145 } 146 err = winapi.NtResumeProcess(h) 147 winapi.CloseHandle(h) 148 return err 149 } 150 151 // SuspendProcess will attempt to suspend the process via its PID. This will 152 // attempt to suspend the process using an OS-dependent syscall. 153 // 154 // This will not affect already suspended processes. 155 func SuspendProcess(p uint32) error { 156 // 0x800 - PROCESS_SUSPEND_RESUME 157 h, err := winapi.OpenProcess(0x800, false, p) 158 if err != nil { 159 return err 160 } 161 err = winapi.NtSuspendProcess(h) 162 winapi.CloseHandle(h) 163 return err 164 } 165 func (e *executable) Resume() error { 166 return winapi.NtResumeProcess(e.i.Process) 167 } 168 func (e *executable) Suspend() error { 169 return winapi.NtSuspendProcess(e.i.Process) 170 } 171 func (e *executable) isStarted() bool { 172 return atomic.LoadUint32(&e.i.ProcessID) > 0 || e.i.Process > 0 173 } 174 func (e *executable) isRunning() bool { 175 return e.i.Process > 0 176 } 177 func (e *executable) Handle() uintptr { 178 return e.i.Process 179 } 180 func pipe() (*os.File, *os.File, error) { 181 var ( 182 p [2]syscall.Handle 183 err = syscall.Pipe(p[:]) 184 ) 185 if err != nil { 186 return nil, nil, err 187 } 188 return newFile(p[0], "|0", "file"), newFile(p[1], "|1", "file"), nil 189 } 190 func (e *executable) SetToken(t uintptr) { 191 e.token = t 192 } 193 func writerCopy(w io.Writer, x *os.File) { 194 if bugtrack.Enabled { 195 defer bugtrack.Recover("cmd.writerCopy()") 196 } 197 io.Copy(w, x) 198 } 199 func readerCopy(y *os.File, r io.Reader) { 200 if bugtrack.Enabled { 201 defer bugtrack.Recover("cmd.readerCopy()") 202 } 203 io.Copy(y, r) 204 y.Close() 205 } 206 func (e *executable) SetFullscreen(f bool) { 207 // 0x20 - STARTF_RUNFULLSCREEN 208 if f { 209 e.sf |= 0x20 210 } else { 211 e.sf = e.sf &^ 0x20 212 } 213 } 214 func waitInner(w chan<- error, h, m uintptr) { 215 if atomic.LoadUintptr(&h) > 0 { 216 if bugtrack.Enabled { 217 defer bugtrack.Recover("cmd.waitInner()") 218 } 219 w <- wait(h, m) 220 } 221 close(w) 222 } 223 func (e *executable) SetWindowDisplay(m int) { 224 // 0x1 - STARTF_USESHOWWINDOW 225 if m < 0 { 226 e.sf = e.sf &^ 0x1 227 } else { 228 e.sf |= 0x1 229 e.mode = uint16(m) 230 } 231 } 232 func (e *executable) SetWindowTitle(s string) { 233 // 0x1000 - STARTF_TITLEISAPPID 234 if len(s) > 0 { 235 e.sf |= 0x1000 236 e.title = s 237 } else { 238 e.sf, e.title = e.sf&^0x1000, "" 239 } 240 } 241 func (e *executable) SetLogin(u, d, p string) { 242 if e.user, e.domain, e.pass = u, d, p; len(d) == 0 { 243 d = "." 244 } 245 } 246 func (executable) SetUID(_ int32, _ *Process) {} 247 func (executable) SetGID(_ int32, _ *Process) {} 248 func (e *executable) SetWindowSize(w, h uint32) { 249 // 0x2 - STARTF_USESIZE 250 e.sf |= 0x2 251 e.w, e.h = w, h 252 } 253 func (executable) SetNoWindow(h bool, p *Process) { 254 // 0x8000000 - CREATE_NO_WINDOW 255 if h { 256 p.flags |= 0x8000000 257 } else { 258 p.flags = p.flags &^ 0x8000000 259 } 260 } 261 func (executable) SetDetached(d bool, p *Process) { 262 // 0x8 - DETACHED_PROCESS 263 // 0x10 - CREATE_NEW_CONSOLE 264 if d { 265 p.flags = (p.flags | 0x8) &^ 0x10 266 } else { 267 p.flags = p.flags &^ 0x8 268 } 269 } 270 func (executable) SetChroot(_ string, _ *Process) {} 271 func (executable) SetSuspended(s bool, p *Process) { 272 // 0x4 - CREATE_SUSPENDED 273 if s { 274 p.flags |= 0x4 275 } else { 276 p.flags = p.flags &^ 0x4 277 } 278 } 279 280 //go:linkname newFile os.newFile 281 func newFile(h syscall.Handle, n, k string) *os.File 282 func (e *executable) SetWindowPosition(x, y uint32) { 283 // 0x4 - STARTF_USEPOSITION 284 e.sf |= 0x4 285 e.x, e.y = x, y 286 } 287 func (*executable) SetNewConsole(c bool, p *Process) { 288 // 0x10 - CREATE_NEW_CONSOLE 289 if c { 290 p.flags |= 0x10 291 } else { 292 p.flags = p.flags &^ 0x10 293 } 294 } 295 func (e *executable) kill(x uint32, p *Process) error { 296 if p.exit = x; e.i.Process == 0 { 297 return p.err 298 } 299 return winapi.TerminateProcess(e.i.Process, x) 300 } 301 func createEnvBlock(env []string, split bool) []string { 302 if envOnce.Do(envOnceFunc); len(env) == 0 && !split { 303 return envOnce.e 304 } 305 r := make([]string, len(env), len(env)+len(envOnce.e)) 306 if copy(r, env); !split { 307 // NOTE(dij): If split == true, do NOT add any env vars, but DO 308 // check and add %SYSTEMROOT% if it doesn't exist in the 309 // supplied block. 310 return append(r, envOnce.e...) 311 } 312 for i := range r { 313 if len(r) > 11 { 314 if x := strings.IndexByte(r[i], '='); x > 9 { 315 if strings.EqualFold(r[i][:x], sysRoot) { 316 return r 317 } 318 } 319 } 320 } 321 return append(r, sysRoot+"="+envOnce.r) 322 } 323 func (e *executable) wait(x context.Context, p *Process) { 324 if bugtrack.Enabled { 325 defer bugtrack.Recover("cmd.(*executable).wait()") 326 } 327 var ( 328 w = make(chan error) 329 err error 330 ) 331 if e.m, err = winapi.CreateEvent(nil, false, false, ""); err != nil { 332 if bugtrack.Enabled { 333 bugtrack.Track("cmd.(*executable).wait(): Creating Event failed, falling back to single wait: %s", err.Error()) 334 } 335 err = nil 336 } 337 go waitInner(w, e.i.Process, e.m) 338 select { 339 case err = <-w: 340 case <-x.Done(): 341 } 342 if e.m > 0 { 343 winapi.CloseHandle(e.m) 344 e.m = 0 345 } 346 if err != nil { 347 p.stopWith(exitStopped, err) 348 return 349 } 350 if err2 := x.Err(); err2 != nil { 351 p.stopWith(exitStopped, err2) 352 return 353 } 354 if atomic.SwapUint32(&p.cookie, atomic.LoadUint32(&p.cookie)|cookieStopped)&cookieStopped != 0 || atomic.LoadUintptr(&e.i.Process) == 0 { 355 p.stopWith(0, nil) 356 return 357 } 358 if err = winapi.GetExitCodeProcess(e.i.Process, &p.exit); err != nil { 359 p.stopWith(exitStopped, err) 360 return 361 } 362 if p.exit != 0 { 363 p.stopWith(p.exit, &ExitError{Exit: p.exit}) 364 return 365 } 366 p.stopWith(p.exit, nil) 367 } 368 func (e *executable) writer(w io.Writer) (uintptr, error) { 369 h, c, err := e.writerToHandle(w) 370 if err != nil { 371 return 0, err 372 } 373 return e.addRetHandle(c, h) 374 } 375 func (e *executable) reader(r io.Reader) (uintptr, error) { 376 h, c, err := e.readerToHandle(r) 377 if err != nil { 378 return 0, err 379 } 380 return e.addRetHandle(c, h) 381 } 382 func (e *executable) SetParent(f *filter.Filter, p *Process) { 383 if e.filter = f; f != nil { 384 e.SetNewConsole(true, p) 385 } 386 } 387 func (e *executable) StdinPipe(p *Process) (io.WriteCloser, error) { 388 var err error 389 if p.Stdin, e.r, err = pipe(); err != nil { 390 return nil, xerr.Wrap("unable to create Pipe", err) 391 } 392 e.closers = append(e.closers, p.Stdin.(io.Closer)) 393 return e.r, nil 394 } 395 func (e *executable) StdoutPipe(p *Process) (io.ReadCloser, error) { 396 r, w, err := pipe() 397 if err != nil { 398 return nil, xerr.Wrap("unable to create Pipe", err) 399 } 400 p.Stdout = w 401 e.closers = append(e.closers, w) 402 return r, nil 403 } 404 func (e *executable) StderrPipe(p *Process) (io.ReadCloser, error) { 405 r, w, err := pipe() 406 if err != nil { 407 return nil, xerr.Wrap("unable to create Pipe", err) 408 } 409 p.Stderr = w 410 e.closers = append(e.closers, w) 411 return r, nil 412 } 413 func (e *executable) addRetHandle(c bool, h uintptr) (uintptr, error) { 414 if e.parent == 0 { 415 if c { 416 e.closers = append(e.closers, closer(h)) 417 } 418 return h, nil 419 } 420 var ( 421 n uintptr 422 err = winapi.DuplicateHandle(winapi.CurrentProcess, h, e.parent, &n, 0, true, 0x2) 423 // 0x2 - DUPLICATE_SAME_ACCESS 424 ) 425 if c { 426 winapi.CloseHandle(h) 427 } 428 if err != nil { 429 return 0, xerr.Wrap("DuplicateHandle", err) 430 } 431 return n, nil 432 } 433 func (e *executable) readerToHandle(r io.Reader) (uintptr, bool, error) { 434 if r == nil { 435 // 0 - READONLY 436 f, err := os.OpenFile(os.DevNull, 0, 0) 437 if err != nil { 438 return 0, false, xerr.Wrap("cannot open NULL device", err) 439 } 440 e.closers = append(e.closers, f) 441 return f.Fd(), false, nil 442 } 443 switch i := r.(type) { 444 case file: 445 f, err := i.File() 446 if err != nil { 447 return 0, false, xerr.Wrap("cannot obtain file handle", err) 448 } 449 // Closeable "c" is true, since this /should/ be a separate 450 // handle from the initial "File" type. 451 // 452 // NOTE(dij): Technically on Windows this will always fail 453 // See: https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/net/fd_windows.go;l=175 454 // 455 // BUT if we're going off the *nix implementation, it would be a 456 // duplicate handle, and safe to close. 457 return f.Fd(), true, nil 458 case fileFd: 459 return i.Fd(), false, nil 460 } 461 x, y, err := pipe() 462 if err != nil { 463 return 0, false, xerr.Wrap("cannot create Pipe", err) 464 } 465 e.closers = append(e.closers, x) 466 go readerCopy(y, r) 467 return x.Fd(), false, nil 468 } 469 func (e *executable) writerToHandle(w io.Writer) (uintptr, bool, error) { 470 if w == nil { 471 // 1 - WRITEONLY 472 f, err := os.OpenFile(os.DevNull, 1, 0) 473 if err != nil { 474 return 0, false, xerr.Wrap("cannot open NULL device", err) 475 } 476 e.closers = append(e.closers, f) 477 return f.Fd(), false, nil 478 } 479 switch i := w.(type) { 480 case file: 481 f, err := i.File() 482 if err != nil { 483 return 0, false, xerr.Wrap("cannot obtain file handle", err) 484 } 485 // Closeable "c" is true, since this /should/ be a separate handle from 486 // the initial "File" type. 487 // 488 // NOTE(dij): Technically on Windows this will always fail 489 // See: https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/net/fd_windows.go;l=175 490 // 491 // BUT if we're going off the *nix implementation, it would be a duplicate 492 // handle, and safe to close. 493 return f.Fd(), true, nil 494 case fileFd: 495 return i.Fd(), false, nil 496 } 497 x, y, err := pipe() 498 if err != nil { 499 return 0, false, xerr.Wrap("cannot create Pipe", err) 500 } 501 e.closers = append(e.closers, y) 502 e.closers = append(e.closers, x) 503 go writerCopy(w, x) 504 return y.Fd(), false, nil 505 } 506 func (e *executable) start(x context.Context, p *Process, sus bool) error { 507 r, err := exec.LookPath(p.Args[0]) 508 if err != nil { 509 return err 510 } 511 v, y, err := e.startInfo() 512 if err != nil { 513 return err 514 } 515 if p.Stderr != nil || p.Stdout != nil || p.Stdin != nil { 516 var si, so, se uintptr 517 if si, err = e.reader(p.Stdin); err != nil { 518 if e.parent > 0 { 519 winapi.CloseHandle(e.parent) 520 e.parent = 0 521 } 522 return err 523 } 524 if so, err = e.writer(p.Stdout); err != nil { 525 if e.parent > 0 { 526 winapi.CloseHandle(e.parent) 527 e.parent = 0 528 } 529 return err 530 } 531 if p.Stderr == p.Stdout { 532 se = so 533 } else if se, err = e.writer(p.Stderr); err != nil { 534 if e.parent > 0 { 535 winapi.CloseHandle(e.parent) 536 e.parent = 0 537 } 538 return err 539 } 540 if v != nil { 541 v.StartupInfo.StdErr = se 542 v.StartupInfo.StdInput = si 543 v.StartupInfo.StdOutput = so 544 v.StartupInfo.Flags |= 0x100 545 // 0x100 - STARTF_USESTDHANDLES 546 } else if y != nil { 547 y.StdErr, y.StdInput, y.StdOutput = se, si, so 548 y.Flags |= 0x100 549 // 0x100 - STARTF_USESTDHANDLES 550 } 551 } 552 u := e.token 553 if runtime.LockOSThread(); u == 0 && e.parent == 0 && !winapi.IsWindowsXp() { 554 // NOTE(dij): Handle threads that currently have an impersonated Token 555 // set. This will trigger this function call to use 556 // 'CreateProcessWithToken' instead of 'CreateProcess'. 557 // This is only called IF there is no parent Process set, as 558 // Windows permissions cause some fucky stuff to happen. 559 // Failing silently is fine. 560 // 561 // NOTE(dij): Added a 'IsUserNetworkToken' token to check the Token origin 562 // to see if it's an impersonated user token or a stolen elevated 563 // process token, as impersonated user tokens do NOT like being 564 // ran with 'CreateProcessWithToken'. 565 // 0xF01FF - TOKEN_ALL_ACCESS 566 if winapi.OpenThreadToken(winapi.CurrentThread, 0xF01FF, true, &u); u > 0 && winapi.IsUserNetworkToken(u) { 567 if winapi.CloseHandle(u); bugtrack.Enabled { 568 bugtrack.Track("cmd.(*executable).start(): Removing user login token.") 569 } 570 u = 0 571 } 572 } 573 if sus { 574 // 0x4 - CREATE_SUSPENDED 575 p.flags |= 0x4 576 } 577 if e.r != nil { 578 e.r.Close() 579 e.r = nil 580 } 581 var t uintptr 582 if winapi.OpenThreadToken(winapi.CurrentThread, 0xF01FF, true, &t) == nil && (len(e.user) > 0 || u > 0) { 583 if winapi.SetThreadToken(winapi.CurrentThread, 0); bugtrack.Enabled { 584 bugtrack.Track("cmd.(*executable).start(): Clearing thread impersonation token.") 585 } 586 } 587 // NOTE(dij): Should we use CreateEnvironmentBlock? We'd have to keep track 588 // of the handle though. 589 switch z := createEnvBlock(p.Env, p.split); { 590 case len(e.user) > 0: 591 if bugtrack.Enabled { 592 bugtrack.Track("cmd.(*executable).start(): Using API call CreateProcessWithLogin for execution.") 593 } 594 // 0x0 - *shrug* 595 err = winapi.CreateProcessWithLogin(e.user, e.domain, e.pass, 0x0, r, strings.Join(p.Args, " "), p.flags, z, p.Dir, y, v, &e.i) 596 case u > 0: 597 if bugtrack.Enabled { 598 bugtrack.Track("cmd.(*executable).start(): Using API call CreateProcessWithToken for execution.") 599 } 600 // 0x2 - LOGON_NETCREDENTIALS_ONLY 601 err = winapi.CreateProcessWithToken(u, 0x2, r, strings.Join(p.Args, " "), p.flags, z, p.Dir, y, v, &e.i) 602 default: 603 if bugtrack.Enabled { 604 bugtrack.Track("cmd.(*executable).start(): Using API call CreateProcess for execution.") 605 } 606 err = winapi.CreateProcess(r, strings.Join(p.Args, " "), nil, nil, true, p.flags, z, p.Dir, y, v, &e.i) 607 } 608 if t > 0 { 609 winapi.SetThreadToken(winapi.CurrentThread, t) 610 winapi.CloseHandle(t) 611 } 612 if u > 0 && e.token == 0 { 613 winapi.CloseHandle(u) 614 } 615 if runtime.UnlockOSThread(); e.parent > 0 { 616 winapi.CloseHandle(e.parent) 617 e.parent = 0 618 } 619 if err != nil { 620 for i := range e.closers { 621 e.closers[i].Close() 622 } 623 return err 624 } 625 winapi.CloseHandle(e.i.Thread) 626 if e.closers = append(e.closers, closer(e.i.Process)); sus { 627 return nil 628 } 629 go e.wait(x, p) 630 return nil 631 } 632 func (e *executable) startInfo() (*winapi.StartupInfoEx, *winapi.StartupInfo, error) { 633 var ( 634 x winapi.StartupInfoEx 635 err error 636 ) 637 e.close() 638 x.StartupInfo.XSize, x.StartupInfo.YSize = e.w, e.h 639 x.StartupInfo.Flags, x.StartupInfo.ShowWindow = e.sf, e.mode 640 if x.StartupInfo.X, x.StartupInfo.Y = e.x, e.y; len(e.title) > 0 { 641 if x.StartupInfo.Title, err = winapi.UTF16PtrFromString(e.title); err != nil { 642 return nil, nil, xerr.Wrap("cannot convert title", err) 643 } 644 } 645 // NOTE(dij): checkVersion(): Retruns false if the system is < Windows Vista 646 if x.StartupInfo.Cb = uint32(unsafe.Sizeof(x)); !checkVersion() { 647 return nil, &x.StartupInfo, nil 648 } 649 if e.filter != nil && !e.filter.Empty() { 650 // (old 0x100CC1 - SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | 651 // PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE) 652 // (old 0x4C0 - PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS) 653 // 654 // 0x10C0 - PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_QUERY_LIMITED_INFORMATION 655 if e.parent, err = e.filter.HandleFunc(0x10C0, nil); err != nil { 656 return nil, nil, err 657 } 658 // FIXED(dij): Apparently sometimes this isn't closed? It seems to /only/ 659 // happen during spawn? Look into this later. 660 // 661 // FIX: Close handle immediately after spawning process! 662 // e.closers = append(e.closers, closer(e.parent)) 663 } 664 var c uint32 665 // NOTE(dij): SecProtect isn't allowed until Windows 8.1 and Windows Server 2012R2 666 // Thanks for the super small blurb of text on it Microsoft >:[ 667 switch v := checkVersionSec(); { 668 case !v && e.parent == 0: // No sec and no parent 669 return nil, &x.StartupInfo, nil 670 case !v && e.parent > 0: // No sec, has parent (1 slot) 671 fallthrough 672 case v && e.parent == 0: // Has sec, no parent (1 slot) 673 c = 1 674 case v && e.parent > 0: // Has sec, has parent (2 slots) 675 c = 2 676 } 677 x.AttributeList = &winapi.StartupAttributes{Count: c} 678 if x.StartupInfo.Cb = uint32(unsafe.Sizeof(x)); e.parent > 0 { 679 // 0x20000 - PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 680 if err = winapi.UpdateProcThreadAttribute(x.AttributeList, 0x20000, unsafe.Pointer(&e.parent), uint64(unsafe.Sizeof(e.parent)), nil, nil); err != nil { 681 winapi.CloseHandle(e.parent) 682 e.parent, x.AttributeList = 0, nil 683 return nil, nil, xerr.Wrap("UpdateProcThreadAttribute", err) 684 } 685 c-- 686 } 687 if c == 1 { 688 // 0x20007 - PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY 689 if err = winapi.UpdateProcThreadAttribute(x.AttributeList, 0x20007, unsafe.Pointer(&secProtect), uint64(unsafe.Sizeof(secProtect)), nil, nil); err != nil { 690 if x.AttributeList = nil; e.parent > 0 { 691 winapi.CloseHandle(e.parent) 692 e.parent = 0 693 } 694 return nil, nil, xerr.Wrap("UpdateProcThreadAttribute", err) 695 } 696 } 697 return &x, nil, nil 698 }