github.com/iDigitalFlame/xmt@v0.5.1/device/winapi/helpers.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 winapi 21 22 import ( 23 "errors" 24 "io" 25 "runtime" 26 "runtime/debug" 27 "strings" 28 "sync" 29 "syscall" 30 "time" 31 "unsafe" 32 33 "github.com/iDigitalFlame/xmt/util/bugtrack" 34 "github.com/iDigitalFlame/xmt/util/xerr" 35 ) 36 37 const ( 38 ptrSize = unsafe.Sizeof(uintptr(0)) 39 kernelShared = uintptr(0x7FFE0000) 40 ) 41 42 var caught struct{} 43 44 //go:linkname allm runtime.allm 45 var allm unsafe.Pointer 46 47 // We have this to be used to prevent crashing the stack of the program 48 // when we call minidump as we need to track extra parameters. 49 // The lock will stay enabled until it's done, so it's "thread safe". 50 var dumpStack dumpParam 51 52 //go:linkname setConsoleCtrlHandler runtime._SetConsoleCtrlHandler 53 var setConsoleCtrlHandler uintptr 54 55 var dumpCallbackOnce struct { 56 _ [0]func() 57 sync.Once 58 f uintptr 59 } 60 61 type dumpCallback struct { 62 // DO NOT REORDER 63 Func uintptr 64 Args uintptr 65 } 66 type kernelSharedData struct { 67 // DO NOT REORDER 68 _ [20]byte 69 SystemTime struct { 70 LowPart uint32 71 HighPart int32 72 _ int32 73 } 74 _ [16]byte 75 NtSystemRoot [260]uint16 76 MaxStackTraceDepth uint32 77 _ uint32 78 _ [32]byte 79 NtBuildNumber uint32 80 _ [8]byte 81 NtMajorVersion uint32 82 NtMinorVersion uint32 83 ProcessorFeatures [64]byte 84 _ [20]byte 85 SystemExpirationDate uint64 86 _ [4]byte 87 KdDebuggerEnabled uint8 88 MitigationPolicies uint8 89 _ [2]byte 90 ActiveConsoleID uint32 91 _ [12]byte 92 NumberOfPhysicalPages uint32 93 SafeBootMode uint8 94 VirtualizationFlags uint8 95 _ [2]byte 96 SharedDataFlags uint32 97 } 98 99 // KillRuntime attempts to walk through the process threads and will forcefully 100 // kill all Golang based OS-Threads based on their starting address (which 101 // should be the same when starting from CGo). 102 // 103 // This will attempt to determine the base thread and any children that may be 104 // running and take action on what type of host we're in to best end the 105 // runtime without crashing. 106 // 107 // This function can be used on binaries, shared libraries or Zombified processes. 108 // 109 // DO NOT EXPECT ANYTHING (INCLUDING DEFERS) TO HAPPEN AFTER THIS FUNCTION. 110 func KillRuntime() { 111 runtime.GC() 112 debug.FreeOSMemory() 113 runtime.LockOSThread() 114 killRuntime() 115 // Below shouldn't run. 116 runtime.UnlockOSThread() 117 } 118 func killRuntime() { 119 //// Workflow for killRuntime() 120 // 121 // 1 - Find the module that's us (this thread) 122 // 2 - Find the base of the module we are in 123 // 3 - Enumerate the runtime's M to find all open threads (finally) 124 // 4 - Look through process threads to see if any other threads exist 125 // 5 - Collect threads that exist in the base address space 126 // > 6 - If we are in the base, it's a binary - syscall.Exit(0) 127 // 7 - Check suspend cout of each thread in base address to see if we're a Zombie 128 // > 8 - If only one thread in base address is suspended, we're a Zombie - syscall.Exit(0) 129 // 9 - Iterate through all our threads and terminate them 130 // > 0 - Terminate self thread 131 // 132 var q uintptr 133 // 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 134 if r, _, err := syscallN(funcGetModuleHandleEx.address(), 0x2, 0, uintptr(unsafe.Pointer(&q))); r == 0 { 135 if bugtrack.Enabled { 136 bugtrack.Track("winapi.killRuntime(): GetModuleHandleEx failed err=%s", err.Error()) 137 } 138 return 139 } 140 var k modInfo 141 if err := getCurrentModuleInfo(q, &k); err != nil { 142 if bugtrack.Enabled { 143 bugtrack.Track("winapi.killRuntime(): GetModuleInformation failed err=%s", err.Error()) 144 } 145 return 146 } 147 a, e := k.Base, k.Base+uintptr(k.Size) 148 if bugtrack.Enabled { 149 bugtrack.Track("winapi.killRuntime(): Module range a=%d, e=%d", a, e) 150 } 151 runtime.GC() 152 debug.FreeOSMemory() 153 var ( 154 x = make(map[uint32]struct{}, 8) 155 g = make([]uintptr, 0, 8) 156 ) 157 for i := uintptr(allm); ; { 158 if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 { 159 if z, err := getThreadID(h); err == nil { 160 if x[z] = caught; bugtrack.Enabled { 161 bugtrack.Track("winapi.killRuntime(): Found runtime thread ID z=%d, h=%d", z, h) 162 } 163 } 164 g = append(g, h) 165 } 166 n := (*uintptr)(unsafe.Pointer(i + ptrNext)) 167 if n == nil || *n == 0 { 168 break // Reached bottom of linked list 169 } 170 i = *n 171 } 172 var ( 173 y = getCurrentThreadID() 174 m = make([]uintptr, 0, len(x)) 175 b bool 176 z uint8 177 ) 178 err := EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error { 179 // 0x43 - THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME | THREAD_TERMINATE 180 h, err1 := t.Handle(0x43) 181 if err1 != nil { 182 if bugtrack.Enabled { 183 bugtrack.Track("winapi.killRuntime(): Thread failed to have it's handle opened t.TID=%d, err1=%s!", t.TID, err1) 184 } 185 // NOTE(dij): Workaround on attribute weirdness where we can see our 186 // threads, but we can't get a handle to them. If we are 187 // using QSI, we can see if it's suspended and we can add 188 // it to the Zombie flag as 9/10 it's a Zombie. 189 // 190 // We also check to see if it's part of the runtime's threads 191 // as it shouldn't. This makes false-positives less likely. 192 var ( 193 q, _ = t.IsSuspended() 194 _, ok = x[t.TID] 195 ) 196 if q && !ok { 197 if z++; bugtrack.Enabled { 198 bugtrack.Track("winapi.killRuntime(): Failed thread seems to be a Zombie thread t.TID=%d!", t.TID) 199 } 200 } 201 return nil // Continue on handle errors instead of bailing. 202 } 203 s, err1 := getThreadStartAddress(h) 204 if err1 != nil { 205 return err1 206 } 207 if t.TID == y { // Skip the current thread 208 if b = s >= a && s < e; b { 209 return ErrNoMoreFiles 210 } 211 if bugtrack.Enabled { 212 bugtrack.Track("winapi.killRuntime(): Found our thread t.TID=%d y=%d, s=%d, b=%t", t.TID, y, s, b) 213 } 214 return nil 215 } 216 k, err1 := t.suspended(h) 217 if err1 != nil { 218 return err1 219 } 220 if (s > a && s < e) && k && z < 0xFF { // Prevent overflow here 221 z++ 222 } 223 if _, ok := x[t.TID]; !ok { 224 CloseHandle(h) 225 return nil 226 } 227 m = append(m, h) 228 return nil 229 }) 230 if err != nil { 231 if bugtrack.Enabled { 232 bugtrack.Track("winapi.killRuntime(): EnumThreads failed err=%s", err.Error()) 233 } 234 return 235 } 236 // Unmap all function mappings (if any) 237 if FuncUnmapAll(); b || len(m) == 0 { 238 for i := range m { 239 CloseHandle(m[i]) 240 } 241 if g, m = nil, nil; b { 242 if bugtrack.Enabled { 243 bugtrack.Track("winapi.killRuntime(): We're in the base thread, we can exit normally.") 244 } 245 // Base thread (us), is in the base module address 246 // This is a binary, it's safe to exit cleanly. 247 syscall.Exit(0) 248 return 249 } 250 if bugtrack.Enabled { 251 bugtrack.Track("winapi.killRuntime(): Failed to find base threads!") 252 } 253 return 254 } 255 if z == 1 { 256 if bugtrack.Enabled { 257 bugtrack.Track("winapi.killRuntime(): Zombie check passed z=%d", z) 258 } 259 for i := range m { 260 CloseHandle(m[i]) 261 } 262 if g, m = nil, nil; bugtrack.Enabled { 263 bugtrack.Track("winapi.killRuntime(): We're a Zombie, we can exit normally.") 264 } 265 // Out of all the base threads, only one exists and is suspended, 266 // 99% chance this is a Zombified process, it's ok to exit cleanly. 267 syscall.Exit(0) 268 return 269 } 270 if bugtrack.Enabled { 271 bugtrack.Track("winapi.killRuntime(): Zombie check failed z=%d", z) 272 } 273 freeChunkHeap() 274 // NOTE(dij): Potential footgun? Free all loaded libaries since we're leaving 275 // but not /exiting/. FreeLibrary shouldn't cause an issue as it 276 // /should/ only clean unused libraries after we are done. 277 // ntdll.dll will NOT be unloaded. 278 freeLoadedLibaries() 279 // Stop all running Goroutines 280 stopTheWorld("exit") 281 // Disable the CTRL console handler that Go sets. 282 removeCtrlHandler() 283 // What's left is that we're probally injected into memory somewhere, and 284 // we just need to nuke the runtime without affecting the host. 285 for i := range g { 286 CloseHandle(g[i]) 287 } 288 g = nil 289 for i := range m { 290 if err = TerminateThread(m[i], 0); err != nil { 291 break 292 } 293 } 294 // Close all timers and open handles 295 // Even if the world is stopped, we still run into this occasionally. So it's 296 // down here instead. 297 destoryAllM() 298 for i := range m { 299 CloseHandle(m[i]) 300 } 301 if m = nil; err != nil { 302 if bugtrack.Enabled { 303 bugtrack.Track("winapi.killRuntime(): Terminate error err=%s", err.Error()) 304 } 305 return 306 } 307 if bugtrack.Enabled { 308 bugtrack.Track("winapi.killRuntime(): Bye bye!") 309 } 310 EmptyWorkingSet() 311 freeRuntimeMemory() // Buck Stops here. 312 } 313 314 // Getppid returns the Parent Process ID of this Process by reading the PEB. 315 // If this fails, this returns zero. 316 func Getppid() uint32 { 317 var ( 318 p processBasicInfo 319 r, _, _ = syscallN( 320 funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)), 321 unsafe.Sizeof(p), 0, 322 ) 323 ) 324 if r > 0 { 325 return 0 326 } 327 return uint32(p.InheritedFromUniqueProcessID) 328 } 329 func createDumpFunc() { 330 dumpCallbackOnce.f = syscall.NewCallback(dumpCallbackFunc) 331 } 332 333 // InSafeMode returns true if the current device was booted into Safe Mode, false 334 // otherwise. 335 func InSafeMode() bool { 336 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SafeBootMode > 0 337 } 338 339 // IsDebugged attempts to check multiple system calls in order to determine 340 // REAL debugging status. 341 // 342 // NOTE: Systems that are "Debug" / "Checked" versions of Windows will always 343 // return false! 344 // 345 // This function checks in this order: 346 // 347 // - KSHARED.KdDebuggerEnabled 348 // - KSHARED.SharedDataFlags.DbgErrorPortPresent 349 // - NtQuerySystemInformation/SystemKernelDebuggerInformation 350 // - IsDebuggerPresent (from PEB) 351 // - NtGlobalFlag (from PEB) 352 // - OutputDebugStringA 353 // - CheckRemoteDebuggerPresent 354 // 355 // Errors make the function return false only if they are the last call. 356 func IsDebugged() bool { 357 switch s := (*kernelSharedData)(unsafe.Pointer(kernelShared)); { 358 case s.KdDebuggerEnabled > 1: 359 return true 360 case s.SharedDataFlags&0x1 != 0: // 0x1 - DbgErrorPortPresent 361 // NOTE(dij): This returns true when on a Debug/Checked version on Windows. 362 // Not sure if we want to ignore this or not, but I doubt that 363 // actual systems are using "Multiprocessor Debug/Checked" unless 364 // the system is a driver test or builder. 365 return true 366 } 367 var ( 368 d uint16 369 x uint32 370 ) 371 // 0x23 - SystemKernelDebuggerInformation 372 syscallN(funcNtQuerySystemInformation.address(), 0x23, uintptr(unsafe.Pointer(&d)), 2, uintptr(unsafe.Pointer(&x))) 373 // The SYSTEM_KERNEL_DEBUGGER_INFORMATION short offset 1 (last 8 bits) is not 374 // filled out by systems older than Vista, so we ignore them. 375 if x == 2 && ((d&0xFF) > 1 || ((d>>8) == 0 && IsWindowsVista())) { 376 return true 377 } 378 switch p, err := getProcessPeb(); { 379 case err != nil: 380 case p.BeingDebugged > 0: 381 return true 382 case p.NtGlobalFlag&(0x70) != 0: // 0x70 - FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS 383 return true 384 } 385 o := [2]byte{'_', 0} 386 // Take advantage of a "bug" in OutputDebugStringA where the "r2" return value 387 // will NOT be zero when a debugger is present to receive the debug string. 388 if _, r, _ := syscallN(funcOutputDebugString.address(), uintptr(unsafe.Pointer(&o[0]))); r > 0 { 389 return true 390 } 391 // 0x400 - PROCESS_QUERY_INFORMATION 392 h, err := OpenProcess(0x400, false, GetCurrentProcessID()) 393 if err != nil { 394 return false 395 } 396 var v bool 397 err = CheckRemoteDebuggerPresent(h, &v) 398 CloseHandle(h) 399 return err == nil && v 400 } 401 402 //go:linkname stopTheWorld runtime.stopTheWorld 403 func stopTheWorld(string) 404 405 // IsSystemEval returns true if the KSHARED_USER_DATA.SystemExpirationDate value 406 // is greater than zero. 407 // 408 // SystemExpirationDate is the time that remains in any evaluation copies of 409 // Windows. This can be used to find systems that may be used for testing and 410 // are not production machines. 411 func IsSystemEval() bool { 412 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SystemExpirationDate > 0 413 } 414 415 // IsUACEnabled returns true if UAC (User Account Control) is enabled, false 416 // otherwise. 417 func IsUACEnabled() bool { 418 // 0x2 - DbgElevationEnabled 419 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SharedDataFlags&0x2 != 0 420 } 421 func freeLoadedLibaries() { 422 dllAmsi.free() 423 dllGdi32.free() 424 dllUser32.free() 425 dllWinhttp.free() 426 dllDbgHelp.free() 427 dllAdvapi32.free() 428 dllWtsapi32.free() 429 dllKernel32.free() 430 dllKernelBase.free() 431 } 432 433 // ErasePEHeader erases the first page of the mapped PE memory data. This is 434 // recommended to ONLY use when using a shipped binary. 435 // 436 // Any errors found during zeroing will returned. 437 // 438 // Retrieved from: https://github.com/LordNoteworthy/al-khaser/blob/master/al-khaser/AntiDump/ErasePEHeaderFromMemory.cpp 439 func ErasePEHeader() error { 440 var ( 441 h uintptr 442 r, _, err1 = syscallN(funcGetModuleHandleEx.address(), 0x2, 0, uintptr(unsafe.Pointer(&h))) 443 // 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 444 ) 445 if r == 0 { 446 return unboxError(err1) 447 } 448 var ( 449 n = uint32(syscall.Getpagesize()) 450 o, err = NtProtectVirtualMemory(CurrentProcess, h, n, 0x40) 451 // 0x40 - PAGE_EXECUTE_READWRITE 452 ) 453 if err != nil { 454 return err 455 } 456 for i := uint32(0); i < n; i++ { 457 (*(*[1]byte)(unsafe.Pointer(h + uintptr(i))))[0] = 0 458 } 459 _, err = NtProtectVirtualMemory(CurrentProcess, h, n, o) 460 return err 461 } 462 func (p *dumpParam) close() { 463 heapFree(p.b, p.h) 464 heapDestroy(p.b) 465 p.Unlock() 466 } 467 468 // Untrust will attempt to revoke all Token permissions and change the Token 469 // integrity level to "Untrusted". 470 // 471 // This effectively revokes all permissions for the application with the supplied 472 // PID to run. 473 // 474 // Ensure a call to 'GetDebugPrivilege' is made first before starting. 475 // 476 // Thanks for the find by @zha0gongz1 in their article: 477 // 478 // https://golangexample.com/without-closing-windows-defender-to-make-defender-useless-by-removing-its-token-privileges-and-lowering-the-token-integrity/ 479 func Untrust(p uint32) error { 480 // 0x400 - PROCESS_QUERY_INFORMATION 481 h, err := OpenProcess(0x400, false, p) 482 if err != nil { 483 return err 484 } 485 var t uintptr 486 // 0x200A8 - TOKEN_READ | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT | TOKEN_QUERY 487 if err = OpenProcessToken(h, 0x200A8, &t); err != nil { 488 CloseHandle(h) 489 return err 490 } 491 var n uint32 492 // 0x3 - TokenPrivileges 493 if err = GetTokenInformation(t, 0x3, nil, 0, &n); n == 0 { 494 CloseHandle(h) 495 CloseHandle(t) 496 return err 497 } 498 b := make([]byte, n) 499 // 0x3 - TokenPrivileges 500 if err = GetTokenInformation(t, 0x3, &b[0], n, &n); err != nil { 501 CloseHandle(h) 502 CloseHandle(t) 503 return err 504 } 505 _ = b[n-1] 506 // NOTE(dij): Loop over all the privileges and disable them. Yes we 507 // call "disableAll", but this is a failsafe. 508 for c, i, a := uint32(b[3])<<24|uint32(b[2])<<16|uint32(b[1])<<8|uint32(b[0]), uint32(12), uint32(0); a < c && i < n; a, i = a+1, i+12 { 509 b[i], b[i+1], b[i+2], b[i+3] = 0x4, 0, 0, 0 510 } 511 if err = AdjustTokenPrivileges(t, false, unsafe.Pointer(&b[0]), n, nil, nil); err != nil { 512 CloseHandle(h) 513 CloseHandle(t) 514 return err 515 } 516 // We don't care if this errors. 517 if AdjustTokenPrivileges(t, true, nil, 0, nil, nil); !IsWindowsVista() { 518 CloseHandle(h) 519 CloseHandle(t) 520 return nil 521 } 522 var ( 523 c = uint32(32) 524 s [32]byte 525 ) 526 // 0x41 - WinUntrustedLabelSid 527 r, _, err1 := syscallN(funcCreateWellKnownSid.address(), 0x41, 0, uintptr(unsafe.Pointer(&s[0])), uintptr(unsafe.Pointer(&c))) 528 if r == 0 { 529 CloseHandle(h) 530 CloseHandle(t) 531 return unboxError(err1) 532 } 533 var x SIDAndAttributes 534 // 0x20 - SE_GROUP_INTEGRITY 535 x.Sid, x.Attributes = (*SID)(unsafe.Pointer(&s[0])), 0x20 536 // 0x19 - TokenIntegrityLevel 537 r, _, _ = syscallN(funcNtSetInformationToken.address(), t, 0x19, uintptr(unsafe.Pointer(&x)), uintptr(c+4)) 538 CloseHandle(h) 539 if CloseHandle(t); r == 0 { 540 return nil 541 } 542 return formatNtError(r) 543 } 544 545 // SystemDirectory Windows API Call 546 // 547 // Retrieves the path of the system directory. The system directory contains 548 // system files such as dynamic-link libraries and drivers. 549 // 550 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemdirectoryw 551 // 552 // Technically a link to the runtime "GetSystemDirectory" cached API call. 553 func SystemDirectory() string { 554 return systemDirectoryPrefix 555 } 556 557 // GetDebugPrivilege is a quick helper function that will attempt to grant the 558 // caller the "SeDebugPrivilege" privilege. 559 func GetDebugPrivilege() error { 560 var ( 561 t uintptr 562 err = OpenProcessToken(CurrentProcess, 0x200E8, &t) 563 // 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE 564 // (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT) 565 ) 566 if err != nil { 567 return err 568 } 569 var p privileges 570 if err = LookupPrivilegeValue("", debugPriv, &p.Privileges[0].Luid); err != nil { 571 CloseHandle(t) 572 return err 573 } 574 p.Privileges[0].Attributes, p.PrivilegeCount = 0x2, 1 // SE_PRIVILEGE_ENABLED 575 err = AdjustTokenPrivileges(t, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil) 576 CloseHandle(t) 577 return err 578 } 579 func fullPath(n string) string { 580 if !isBaseName(n) { 581 return n 582 } 583 return systemDirectoryPrefix + n 584 } 585 586 // IsUTCTime checks the current system TimeZone information to see if the device 587 // is set to the UTC time zone. Most systems in debugging/logging environments will 588 // have this set. 589 // 590 // This function detects UTC as it's biases are always zero and is the only time 591 // zone that has this feature. 592 func IsUTCTime() (bool, error) { 593 var ( 594 t timeZoneInfo 595 n int 596 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x2C, uintptr(unsafe.Pointer(&t)), 172, uintptr(unsafe.Pointer(&n))) 597 // 0x2C - SystemCurrentTimeZoneInformation 598 ) 599 if r > 0 { 600 return false, formatNtError(r) 601 } 602 return t.Bias == 0 && t.DaylightBias == 0 && t.StdBias == 0, nil 603 } 604 605 // GetKernelTime returns the system time based on the KSHARED_USER_DATA struct in 606 // memory that is converted to a time.Time struct. 607 // 608 // This can be used to get the system time without relying on any API calls. 609 // 610 // NOTE(dij): Supposedly Go already reads this for 'time.Now()'? 611 func GetKernelTime() time.Time { 612 var ( 613 s = (*kernelSharedData)(unsafe.Pointer(kernelShared)) 614 t = time.Unix(0, ((int64(s.SystemTime.HighPart)<<32|int64(s.SystemTime.LowPart))-epoch)*100) 615 ) 616 return t 617 } 618 func getCurrentThreadID() uint32 { 619 r, _, _ := syscallN(funcGetCurrentThreadID.address()) 620 return uint32(r) 621 } 622 func (p *dumpParam) init() error { 623 p.Lock() 624 var err error 625 // 2 << 20 = ~20MB 626 if p.b, err = heapCreate(2 << 20); err != nil { 627 return err 628 } 629 if p.h, err = heapAlloc(p.b, 2<<20, true); err != nil { 630 heapDestroy(p.b) 631 return err 632 } 633 p.s, p.w = 2<<20, 0 634 dumpCallbackOnce.Do(createDumpFunc) 635 return nil 636 } 637 638 // LoadLibraryAddress is a simple function that returns the raw address of the 639 // 'LoadLibraryW' function in 'kernel32.dll' that's currently loaded. 640 func LoadLibraryAddress() uintptr { 641 return funcLoadLibrary 642 } 643 644 // IsStackTracingEnabled returns true if the KSHARED_USER_DATA.MaxStackTraceDepth 645 // value is greater than zero. 646 // 647 // MaxStackTraceDepth is a value that represents the stack trace depth if tracing 648 // is enabled. If this flag is greater than zero, it is likely that some form of 649 // debug tracing is enabled. 650 func IsStackTracingEnabled() bool { 651 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).MaxStackTraceDepth > 0 652 } 653 654 // GetSystemSID will attempt to determine the System SID value and return it. 655 func GetSystemSID() (*SID, error) { 656 var ( 657 o lsaAttributes 658 h uintptr 659 ) 660 o.Length = uint32(unsafe.Sizeof(o)) 661 r, _, err := syscallN(funcLsaOpenPolicy.address(), 0, uintptr(unsafe.Pointer(&o)), 1, uintptr(unsafe.Pointer(&h))) 662 if r > 0 { 663 return nil, unboxError(err) 664 } 665 i := new(lsaAccountDomainInfo) 666 r, _, err = syscallN(funcLsaQueryInformationPolicy.address(), h, 5, uintptr(unsafe.Pointer(&i))) 667 if syscallN(funcLsaClose.address(), h); r > 0 { 668 return nil, unboxError(err) 669 } 670 return i.SID, nil 671 } 672 func heapFree(h, m uintptr) error { 673 r, _, err := syscallN(funcRtlFreeHeap.address(), h, 0, m) 674 if r == 0 { 675 return unboxError(err) 676 } 677 return nil 678 } 679 func heapDestroy(h uintptr) error { 680 r, _, err := syscallN(funcRtlDestroyHeap.address(), h) 681 if r == 0 { 682 return unboxError(err) 683 } 684 return nil 685 } 686 687 // SetWallpaper uses the 'SystemParametersInfo' API call to set the user's 688 // wallpaper. Changes take effect immediately. 689 // 690 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 691 func SetWallpaper(s string) error { 692 v, err := UTF16PtrFromString(s) 693 if err != nil { 694 return err 695 } 696 // 0x14 - SPI_SETDESKWALLPAPER 697 r, _, err1 := syscallN(funcSystemParametersInfo.address(), 0x14, 1, uintptr(unsafe.Pointer(v)), 0x3) 698 if r == 0 { 699 return unboxError(err1) 700 } 701 return nil 702 } 703 704 // SetHighContrast uses the 'SystemParametersInfo' API call to trigger the 705 // HighContrast theme setting. Set to 'True' to enable it and 'False' to disbale 706 // it. 707 // 708 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 709 func SetHighContrast(e bool) error { 710 var c highContrast 711 if c.Size = uint32(unsafe.Sizeof(c)); e { 712 c.Flags = 1 713 } 714 // 0x43 - SPI_SETHIGHCONTRAST 715 r, _, err := syscallN(funcSystemParametersInfo.address(), 0x43, 0, uintptr(unsafe.Pointer(&c)), 0x3) 716 if r == 0 { 717 return unboxError(err) 718 } 719 return nil 720 } 721 722 // InWow64Process is a helper function that just calls'IsWow64Process' with the 723 // 'CurrentProcess' handle to determine if the current process is a WOW64 process. 724 func InWow64Process() (bool, error) { 725 return IsWow64Process(CurrentProcess) 726 } 727 728 /* 729 // IsVirtualizationEnabled return true if the current device processor has the 730 // PF_VIRT_FIRMWARE_ENABLED flag. This will just indicate if the device has the 731 // capability to run Virtual Machines. This is commonly not the case of many VMs 732 // themselves. 733 // 734 // Be cautious, as many hypervisors have the ability to still expose this CPU 735 // flag to guests. 736 // 737 // This flag is grabbed from KSHARED_USER_DATA. 738 func IsVirtualizationEnabled() bool { 739 // 0x15 - PF_VIRT_FIRMWARE_ENABLED 740 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).ProcessorFeatures[0x15] > 0 741 }*/ 742 743 // SetCommandLine will attempt to read the Process PEB and overrite the 744 // 'ProcessParameters.CommandLine' property with the supplied string value. 745 // 746 // This will NOT change the ImagePath or Binary Name. 747 // 748 // This will return any errors that occur during reading the PEB. 749 // 750 // DOES NOT WORK ON WOW6432 PEBs! 751 // - These are in a separate memory space and seem to only be read once? or the 752 // data is copied somewhere else. Even if I call 'NtWow64QueryInformationProcess64' 753 // and change it, it does NOT seem to care. *shrug* who TF uses x86 anyway in 2022!? 754 // 755 // TODO(dij): Since we have backwards compatibility now. The 32bit PEB can be read 756 // using NtQueryInformationProcess/ProcessWow64Information which returns 757 // 32bit pointer to the PEB in 32bit mode. 758 func SetCommandLine(s string) error { 759 c, err := UTF16FromString(s) 760 if err != nil { 761 return err 762 } 763 p, err := getProcessPeb() 764 if err != nil { 765 return err 766 } 767 p.ProcessParameters.CommandLine.Buffer = &c[0] 768 p.ProcessParameters.CommandLine.Length = uint16(len(c)*2) - 1 769 p.ProcessParameters.CommandLine.MaximumLength = p.ProcessParameters.CommandLine.Length 770 return nil 771 } 772 773 // SwapMouseButtons uses the 'SystemParametersInfo' API call to trigger the 774 // swapping of the left and right mouse buttons. Set to 'True' to swap and 775 // 'False' to disable it. 776 // 777 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 778 func SwapMouseButtons(e bool) error { 779 var v uint32 780 if e { 781 v = 1 782 } 783 // 0x21 - SPI_SETMOUSEBUTTONSWAP 784 r, _, err := syscallN(funcSystemParametersInfo.address(), 0x21, uintptr(v), 0, 0x3) 785 if r == 0 { 786 return unboxError(err) 787 } 788 return nil 789 } 790 func formatNtError(e uintptr) error { 791 // NOTE(dij): Not loading NTDLL here as we /should/ already have loaded it 792 // as we're calling this function due to an Nt* function error 793 // status. If not, this just acts like a standard 'FormatMessage' 794 // call. 795 var ( 796 o [300]uint16 797 r, _, _ = syscallN(funcFormatMessage.address(), 0x3A00, dllNtdll.addr, e, 0x409, uintptr(unsafe.Pointer(&o)), 0x12C, 0) 798 // 0x3A00 - FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE | 799 // FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS 800 // 0x409 - English LANG and English SUB 801 ) 802 if r == 0 { 803 return syscall.Errno(e) 804 } 805 // Remove newline at the end 806 for ; r > 0; r-- { 807 if o[r] == '\n' || o[r] == '\r' { 808 if r > 1 && (o[r-1] == '\n' || o[r-1] == '\r') { 809 r-- 810 } 811 break 812 } 813 } 814 // CAan't find it? Just return what we have. 815 if r == 0 { 816 return errors.New(UTF16ToString(o[:])) 817 } 818 // Remove prepended "{TYPE}" string 819 if o[0] == '{' { 820 for i := uintptr(1); i < r; i++ { 821 if o[i] == '\n' || o[i] == '\r' { 822 if i+1 < r && (o[i+1] == '\n' || o[i+1] == '\r') { 823 i++ 824 } 825 return errors.New(UTF16ToString(o[i+1 : r])) 826 } 827 } 828 } 829 return errors.New(UTF16ToString(o[:r])) 830 } 831 832 // GetLocalUser attempts to return the username associated with the current Thread 833 // or Process. 834 // 835 // This function will first check if the Thread is using a Token (Impersonation) 836 // and if not it will then pull the Token for the Process instead. 837 // 838 // This function will concationate the domain (or local workstation) name if the 839 // Token provides one. 840 // 841 // If any errors occur, an empty string with the error will be returned. 842 func GetLocalUser() (string, error) { 843 var t uintptr 844 // 0x20008 - TOKEN_READ | TOKEN_QUERY 845 if err := OpenThreadToken(CurrentThread, 0x20008, true, &t); err != nil { 846 if err = OpenProcessToken(CurrentProcess, 0x20008, &t); err != nil { 847 return "", err 848 } 849 } 850 u, err := UserFromToken(t) 851 if CloseHandle(t); err != nil { 852 return "", err 853 } 854 return u, nil 855 } 856 857 // CheckDebugWithLoad will attempt to check for a debugger by loading a non-loaded 858 // DLL specified and will check for exclusive access (which is false for debuggers). 859 // 860 // If the file can be opened, the library is freed and the file is closed. This 861 // will return true ONLY if opening for exclusive access fails. 862 // 863 // Any errors opening or loading DLLs will silently return false. 864 func CheckDebugWithLoad(d string) bool { 865 var ( 866 p = fullPath(d) 867 n, err = UTF16PtrFromString(p) 868 ) 869 if err != nil { 870 return false 871 } 872 var ( 873 h uintptr 874 r, _, _ = syscallN(funcGetModuleHandleEx.address(), 0x2, uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&h))) 875 // 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 876 ) 877 if r > 0 { 878 return false 879 } 880 if h, err = loadLibraryEx(p); err != nil || h == 0 { 881 return false 882 } 883 // 0x80000000 - FILE_FLAG_WRITE_THROUGH 884 // 0x0 - EXCLUSIVE 885 // 0x3 - OPEN_EXISTING 886 f, err := CreateFile(p, 0x80000000, 0, nil, 0x3, 0, 0) 887 if syscall.FreeLibrary(syscall.Handle(h)); err != nil { 888 return err.(syscall.Errno) != 0x2 889 } 890 CloseHandle(f) 891 return false 892 } 893 894 // IsUserNetworkToken will return true if the origin of the Token was a LoginUser 895 // network impersonation API call and NOT a duplicated Token via Token or Thread 896 // impersonation. 897 func IsUserNetworkToken(t uintptr) bool { 898 if t == 0 { 899 return false 900 } 901 var ( 902 n uint32 903 b [16]byte 904 err = GetTokenInformation(t, 0x7, &b[0], 16, &n) 905 // 0x7 - TokenSource 906 ) 907 if err != nil { 908 return false 909 } 910 // Match [65 100 118 97 112 105 32 32] == "Advapi" 911 return b[0] == 65 && b[1] == 100 && b[6] == 32 && b[7] == 32 912 } 913 914 // EnablePrivileges will attempt to enable the supplied Windows privilege values 915 // on the current process's Token. 916 // 917 // Errors during encoding, lookup or assignment will be returned and not all 918 // privileges will be assigned, if they occur. 919 func EnablePrivileges(s ...string) error { 920 if len(s) == 0 { 921 return nil 922 } 923 var ( 924 t uintptr 925 err = OpenProcessToken(CurrentProcess, 0x200E8, &t) 926 // 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE 927 // (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT) 928 ) 929 if err != nil { 930 return xerr.Wrap("OpenProcessToken", err) 931 } 932 err = EnableTokenPrivileges(t, s...) 933 CloseHandle(t) 934 return err 935 } 936 937 // IsSecureBootEnabled returns true if Secure Boot is enabled in the current device. 938 // 939 // This function returns true or false and any errors that may occur during checking 940 // for secure boot. 941 func IsSecureBootEnabled() (bool, error) { 942 var ( 943 i uint16 944 n uint32 945 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x91, uintptr(unsafe.Pointer(&i)), 2, uintptr(unsafe.Pointer(&n))) 946 // 0x91 - SystemSecureBootInformation 947 ) 948 if r > 0 { 949 return false, formatNtError(r) 950 } 951 return (i & 0xFF) == 1, nil 952 } 953 954 // SetAllThreadsToken sets the Token for all current Golang threads. This is an 955 // easy way to do thread impersonation across the entire runtime. 956 // 957 // Calls 'ForEachThread' -> 'SetThreadToken' under the hood. 958 func SetAllThreadsToken(h uintptr) error { 959 return ForEachThread(func(t uintptr) error { return SetThreadToken(t, h) }) 960 } 961 func getProcessPeb() (*processPeb, error) { 962 /* 963 PVOID64 GetPeb64() 964 { 965 PVOID64 peb64 = NULL; 966 967 if (API::IsAvailable(API_IDENTIFIER::API_NtWow64QueryInformationProcess64)) 968 { 969 PROCESS_BASIC_INFORMATION_WOW64 pbi64 = {}; 970 971 auto NtWow64QueryInformationProcess64 = static_cast<pNtWow64QueryInformationProcess64>(API::GetAPI(API_IDENTIFIER::API_NtWow64QueryInformationProcess64)); 972 NTSTATUS status = NtWow64QueryInformationProcess64(GetCurrentProcess(), ProcessBasicInformation, &pbi64, sizeof(pbi64), nullptr); 973 if ( NT_SUCCESS ( status ) ) 974 peb64 = pbi64.PebBaseAddress; 975 } 976 977 return peb64; 978 } 979 */ 980 var ( 981 p processBasicInfo 982 r, _, _ = syscallN( 983 funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)), 984 unsafe.Sizeof(p), 0, 985 ) 986 ) 987 if r > 0 { 988 return nil, formatNtError(r) 989 } 990 return (*processPeb)(unsafe.Pointer(p.PebBaseAddress)), nil 991 } 992 993 // ImpersonatePipeToken will attempt to impersonate the Token used by the Named 994 // Pipe client. 995 // 996 // This function is only usable on Windows with a Server Pipe handle. 997 // 998 // BUG(dij): I'm not sure if this is broken or this is how it's handled. I'm 999 // 1000 // getting error 5. 1001 // 1002 // Pipe insights: 1003 // 1004 // https://papers.vx-underground.org/papers/Windows/System%20Components%20and%20Abuse/Offensive%20Windows%20IPC%20Internals%201%20Named%20Pipes.pdf 1005 func ImpersonatePipeToken(h uintptr) error { 1006 // NOTE(dij): For best results, we FIRST impersonate the token, THEN 1007 // we try to set the token to each user thread with a duplicated 1008 // token set to impersonate. (Similar to an Impersonate call). 1009 runtime.LockOSThread() 1010 if err := ImpersonateNamedPipeClient(h); err != nil { 1011 runtime.UnlockOSThread() 1012 return err 1013 } 1014 var y uintptr 1015 // 0xF01FF - TOKEN_ALL_ACCESS 1016 if err := OpenThreadToken(CurrentThread, 0xF01FF, true, &y); err != nil { 1017 runtime.UnlockOSThread() 1018 return err 1019 } 1020 err := SetAllThreadsToken(y) 1021 CloseHandle(y) 1022 runtime.UnlockOSThread() 1023 return err 1024 } 1025 func heapCreate(n uint64) (uintptr, error) { 1026 // 0x1002 - MEM_COMMIT? | HEAP_GROWABLE 1027 r, _, err := syscallN(funcRtlCreateHeap.address(), 0x1002, 0, 0, uintptr(n), 0, 0) 1028 if r == 0 { 1029 return 0, unboxError(err) 1030 } 1031 return r, nil 1032 } 1033 func (p *dumpParam) resize(n uint64) error { 1034 if n < p.s { 1035 return nil 1036 } 1037 var ( 1038 v = (p.s + n) * 2 1039 h, err = heapReAlloc(p.b, p.h, v, false) 1040 ) 1041 if err != nil { 1042 return err 1043 } 1044 p.h, p.s = h, v 1045 return nil 1046 } 1047 1048 // PhysicalInfo will query the system using NtQuerySystemInformation to grab the 1049 // number of CPUs installed and the current memory (in MB) that is avaliable to 1050 // the system (installed physically). 1051 func PhysicalInfo() (uint8, uint32, error) { 1052 var ( 1053 n uint64 1054 i systemBasicInfo 1055 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x0, uintptr(unsafe.Pointer(&i)), unsafe.Sizeof(i), uintptr(unsafe.Pointer(&n))) 1056 // 0x0 - SystemBasicInformation 1057 ) 1058 if r > 0 { 1059 return 0, 0, formatNtError(r) 1060 } 1061 return i.NumProc, uint32((uint64(i.PageSize)*uint64(i.PhysicalPages))/0x100000) + 1, nil 1062 } 1063 func getThreadID(h uintptr) (uint32, error) { 1064 var ( 1065 t threadBasicInfo 1066 r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0, uintptr(unsafe.Pointer(&t)), unsafe.Sizeof(t), 0) 1067 ) 1068 if r > 0 { 1069 return 0, formatNtError(r) 1070 } 1071 return uint32(t.ClientID.Thread), nil 1072 } 1073 1074 // GetCodeIntegrityState returns a bitvalue that returns the Code Integrity status 1075 // of the current device. If the return value is zero without an error, this means 1076 // that code integrity is disabled. 1077 func GetCodeIntegrityState() (uint32, error) { 1078 var ( 1079 n uint32 1080 s = [2]uint32{8, 0} 1081 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x67, uintptr(unsafe.Pointer(&s)), 8, uintptr(unsafe.Pointer(&n))) 1082 // 0x67 - SystemCodeIntegrityInformation 1083 ) 1084 if r > 0 { 1085 return 0, formatNtError(r) 1086 } 1087 return s[1], nil 1088 } 1089 func (p *dumpParam) write(w io.Writer) error { 1090 var ( 1091 b = (*[]byte)(unsafe.Pointer(&SliceHeader{Data: unsafe.Pointer(p.h), Len: int(p.w), Cap: int(p.w)})) 1092 n, err = w.Write(*b) 1093 ) 1094 if b, *b = nil, nil; err != nil { 1095 return err 1096 } 1097 if n != int(p.w) { 1098 return io.ErrShortWrite 1099 } 1100 return nil 1101 } 1102 1103 // GetDiskSize returns the size in bytes of the disk by it's NT path or the path 1104 // to a partition or volume on the disk. 1105 // 1106 // Any errors encountered during reading will be returned. 1107 // 1108 // The name can be in the format of an NT path such as: 1109 // 1110 // - \\.\C: 1111 // - \\.\PhysicalDrive0 1112 // 1113 // Both are equal on /most/ systems. 1114 func GetDiskSize(name string) (uint64, error) { 1115 // 0x1 - FILE_SHARE_READ 1116 // 0x3 - OPEN_EXISTING 1117 h, err := CreateFile(name, 0, 0x1, nil, 0x3, 0, 0) 1118 if err != nil { 1119 return 0, err 1120 } 1121 var ( 1122 g diskGeometryEx 1123 s [4 + ptrSize]byte // IO_STATUS_BLOCK 1124 ) 1125 // 0x700A0 - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 1126 r, _, err := syscallN(funcNtDeviceIoControlFile.address(), h, 0, 0, 0, uintptr(unsafe.Pointer(&s)), 0x700A0, 0, 0, uintptr(unsafe.Pointer(&g)), 0x20+ptrSize) 1127 if CloseHandle(h); r > 0 { 1128 return 0, formatNtError(r) 1129 } 1130 return g.Size, nil 1131 } 1132 1133 // UserFromToken will attempt to get the User SID from the supplied Token and 1134 // return the associated Username and Domain string from the SID. 1135 func UserFromToken(h uintptr) (string, error) { 1136 u, err := GetTokenUser(h) 1137 if err != nil { 1138 return "", err 1139 } 1140 return u.User.Sid.UserName() 1141 } 1142 1143 // ForEachThread is a helper function that allows a function to be executed with 1144 // the handle of the Thread. 1145 // 1146 // This function only returns an error if enumerating the Threads generates an 1147 // error or the supplied function returns an error. 1148 // 1149 // This function ONLY targets Golang threads. To target all Process threads, 1150 // use 'ForEachProcThread'. 1151 func ForEachThread(f func(uintptr) error) error { 1152 var err error 1153 for i := uintptr(allm); ; { 1154 if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 { 1155 if err = f(h); err != nil { 1156 break 1157 } 1158 } 1159 n := (*uintptr)(unsafe.Pointer(i + ptrNext)) 1160 if n == nil || *n == 0 { 1161 break // Reached bottom of linked list 1162 } 1163 i = *n 1164 } 1165 return err 1166 } 1167 1168 // GetTokenUser retrieves access token user account information and SID. 1169 func GetTokenUser(h uintptr) (*TokenUser, error) { 1170 u, err := getTokenInfo(h, 1, 50) 1171 if err != nil { 1172 return nil, err 1173 } 1174 return (*TokenUser)(u), nil 1175 } 1176 1177 // GetVersionNumbers returns the NTDLL internal version numbers as Major, Minor 1178 // and Build. 1179 // 1180 // This function should return the correct values regardless of manifest version. 1181 func GetVersionNumbers() (uint32, uint32, uint16) { 1182 var m, n, b uint32 1183 syscallN(funcRtlGetNtVersionNumbers.address(), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Pointer(&n)), uintptr(unsafe.Pointer(&b))) 1184 return m, n, uint16(b) 1185 } 1186 func enablePrivileges(h uintptr, s []string) error { 1187 var ( 1188 p privileges 1189 err error 1190 ) 1191 for i := range s { 1192 if i > 5 { 1193 break 1194 } 1195 if err = LookupPrivilegeValue("", s[i], &p.Privileges[i].Luid); err != nil { 1196 if xerr.ExtendedInfo { 1197 return xerr.Wrap(`cannot lookup "`+s[i]+`"`, err) 1198 } 1199 return xerr.Wrap("cannot lookup Privilege", err) 1200 } 1201 p.Privileges[i].Attributes = 0x2 // SE_PRIVILEGE_ENABLED 1202 } 1203 p.PrivilegeCount = uint32(len(s)) 1204 if err = AdjustTokenPrivileges(h, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil); err != nil { 1205 return xerr.Wrap("cannot assign all Privileges", err) 1206 } 1207 return nil 1208 } 1209 1210 // GetProcessFileName will attempt to retrieve the basename of the process 1211 // related to the open Process handle supplied. 1212 func GetProcessFileName(h uintptr) (string, error) { 1213 var ( 1214 u ntUnicodeString 1215 n uint32 1216 ) 1217 r, _, _ := syscallN( 1218 funcNtQueryInformationProcess.address(), h, 0x1B, uintptr(unsafe.Pointer(&u)), 1219 unsafe.Sizeof(u)+260, uintptr(unsafe.Pointer(&n)), 1220 ) 1221 // 0x1B - ProcessImageFileName 1222 if r > 0 { 1223 return "", formatNtError(r) 1224 } 1225 v := UTF16ToString(u.Buffer[4:n]) 1226 for i := len(v) - 1; i > 0; i-- { 1227 if v[i] == '\\' { 1228 return v[i+1:], nil 1229 } 1230 } 1231 return v, nil 1232 } 1233 1234 // ForEachProcThread is a helper function that allows a function to be executed 1235 // with the handle of the Thread. 1236 // 1237 // This function only returns an error if enumerating the Threads generates an 1238 // error or the supplied function returns an error. 1239 // 1240 // This function targets ALL threads (including non-Golang threads). To target 1241 // all only Golang threads, use 'ForEachThread'. 1242 func ForEachProcThread(f func(uintptr) error) error { 1243 return EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error { 1244 // old (0xE0 - THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN) 1245 // 0x1FFFFF - THREAD_ALL_ACCESS 1246 v, err := t.Handle(0x1FFFFF) 1247 if err != nil { 1248 return err 1249 } 1250 err = f(v) 1251 CloseHandle(v) 1252 return err 1253 }) 1254 } 1255 func getThreadStartAddress(h uintptr) (uintptr, error) { 1256 var ( 1257 i uintptr 1258 r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0x9, uintptr(unsafe.Pointer(&i)), ptrSize, 0) 1259 // 0x9 - ThreadQuerySetWin32StartAddress 1260 ) 1261 if r > 0 { 1262 return 0, formatNtError(r) 1263 } 1264 return i, nil 1265 } 1266 1267 // FileSigningIssuerName attempts to read the Authenticate signing certificate 1268 // issuer name for the specified file path. 1269 // 1270 // If the file does not exist or a certificate cannot be found, this returns the 1271 // error 'syscall.EINVAL'. 1272 // 1273 // If the function success, the return result will be the string name of the 1274 // certificate issuer. 1275 func FileSigningIssuerName(path string) (string, error) { 1276 f, err1 := UTF16PtrFromString(path) 1277 if err1 != nil { 1278 return "", err1 1279 } 1280 var ( 1281 s, h uintptr 1282 r, _, err = syscallN( 1283 funcCryptQueryObject.address(), 0x1, uintptr(unsafe.Pointer(f)), 0x400, 0x2, 1284 0, 0, 0, 0, uintptr(unsafe.Pointer(&s)), uintptr(unsafe.Pointer(&h)), 0, 1285 ) 1286 // 0x1 - CERT_QUERY_OBJECT_FILE 1287 // 0x400 - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED 1288 // 0x2 - CERT_QUERY_FORMAT_FLAG_BINARY 1289 ) 1290 if r == 0 { 1291 if err == 0x80092009 { // 0x80092009 - Object not found, file isn't signed. 1292 return "", syscall.EINVAL 1293 } 1294 return "", unboxError(err) 1295 } 1296 var x uint32 1297 // 0x6 - CMSG_SIGNER_INFO_PARAM 1298 if r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, 0, uintptr(unsafe.Pointer(&x))); r == 0 { 1299 syscallN(funcCryptMsgClose.address(), h) 1300 syscallN(funcCertCloseStore.address(), s) 1301 return "", unboxError(err) 1302 } 1303 b := make([]byte, x) 1304 // 0x6 - CMSG_SIGNER_INFO_PARAM 1305 r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&x))) 1306 if syscallN(funcCryptMsgClose.address(), h); r == 0 { 1307 syscallN(funcCertCloseStore.address(), s) 1308 return "", unboxError(err) 1309 } 1310 var ( 1311 v = (*certSigner)(unsafe.Pointer(&b[0])) 1312 i = certInfo{Issuer: v.Issuer, Serial: v.Serial} 1313 ) 1314 // 0x10001 - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING 1315 // 0xB0000 - CERT_FIND_SUBJECT_CERT 1316 r, _, err = syscallN(funcCertFindCertificateInStore.address(), s, 0x10001, 0, 0xB0000, uintptr(unsafe.Pointer(&i)), 0) 1317 if syscallN(funcCertCloseStore.address(), s); r == 0 { 1318 return "", unboxError(err) 1319 } 1320 var ( 1321 n string 1322 k uintptr 1323 ) 1324 // 0x4 - CERT_NAME_SIMPLE_DISPLAY_TYPE 1325 // 0x0 - CERT_NAME_ISSUER_FLAG 1326 if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, 0, 0); k > 0 { 1327 c := make([]uint16, k) 1328 if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, uintptr(unsafe.Pointer(&c[0])), k); k > 0 { 1329 n = UTF16ToString(c[:k]) 1330 } 1331 } 1332 if syscallN(funcCertFreeCertificateContext.address(), r); k == 0 { 1333 return "", unboxError(err) 1334 } 1335 return n, nil 1336 } 1337 1338 // StringListToUTF16Block creates a UTF16 encoded block for usage as a Process 1339 // environment block. 1340 // 1341 // This function returns an error if any of the environment strings are not in 1342 // the 'KEY=VALUE' format or contain a NUL byte. 1343 func StringListToUTF16Block(s []string) (*uint16, error) { 1344 if len(s) == 0 { 1345 return nil, nil 1346 } 1347 var t, i, l int 1348 for _, x := range s { 1349 for v := range x { 1350 if x[v] == 0 { 1351 return nil, syscall.EINVAL 1352 } 1353 } 1354 if q := strings.IndexByte(x, '='); q <= 0 { 1355 if xerr.ExtendedInfo { 1356 return nil, xerr.Sub(`invalid env value "`+x+`"`, 0x17) 1357 } 1358 return nil, xerr.Sub("invalid env value", 0x17) 1359 } 1360 t += len(x) + 1 1361 } 1362 t++ 1363 b := make([]byte, t) 1364 for _, v := range s { 1365 l = len(v) 1366 copy(b[i:i+l], v) 1367 b[i+l] = 0 1368 i = i + l + 1 1369 } 1370 b[i] = 0 1371 return &UTF16EncodeStd([]rune(string(b)))[0], nil 1372 } 1373 1374 // EnableTokenPrivileges will attempt to enable the supplied Windows privilege 1375 // values on the supplied process Token. 1376 // 1377 // Errors during encoding, lookup or assignment will be returned and not all 1378 // privileges will be assigned, if they occur. 1379 func EnableTokenPrivileges(h uintptr, s ...string) error { 1380 if len(s) == 0 { 1381 return nil 1382 } 1383 if len(s) <= 5 { 1384 return enablePrivileges(h, s) 1385 } 1386 for x, w := 0, 0; x < len(s); { 1387 if w = 5; x+w > len(s) { 1388 w = len(s) - x 1389 } 1390 if err := enablePrivileges(h, s[x:x+w]); err != nil { 1391 return err 1392 } 1393 x += w 1394 } 1395 return nil 1396 } 1397 func heapAlloc(h uintptr, s uint64, z bool) (uintptr, error) { 1398 var f uint32 1399 if z { 1400 f |= 0x08 1401 } 1402 r, _, err := syscallN(funcRtlAllocateHeap.address(), h, uintptr(f), uintptr(s)) 1403 if r == 0 { 1404 return 0, unboxError(err) 1405 } 1406 return r, nil 1407 } 1408 func (p *dumpParam) copy(o uint64, b uintptr, s uint32) error { 1409 if err := p.resize(o + uint64(s)); err != nil { 1410 return err 1411 } 1412 copyMemory(p.h+uintptr(o), b, s) 1413 p.w += uint64(s) 1414 return nil 1415 } 1416 func heapReAlloc(h, m uintptr, s uint64, z bool) (uintptr, error) { 1417 var f uint32 1418 if z { 1419 // 0x8 - HEAP_ZERO_MEMORY 1420 f |= 0x8 1421 } 1422 r, _, err := syscallN(funcRtlReAllocateHeap.address(), h, uintptr(f), m, uintptr(s)) 1423 if r == 0 { 1424 return 0, unboxError(err) 1425 } 1426 return r, nil 1427 } 1428 func dumpCallbackFunc(_ uintptr, i uintptr, r *dumpOutput) uintptr { 1429 switch *(*uint32)(unsafe.Pointer(i + 4 + ptrSize)) { 1430 case 11: 1431 r.Status = 1 1432 case 12: 1433 var ( 1434 o = *(*uint64)(unsafe.Pointer(i + 8 + (ptrSize * 2))) // Offset 1435 b = *(*uintptr)(unsafe.Pointer(i + 16 + (ptrSize * 2))) // Buffer 1436 s = *(*uint32)(unsafe.Pointer(i + 16 + (ptrSize * 3))) // Size 1437 ) 1438 if err := dumpStack.copy(o, b, s); err != nil { 1439 r.Status = 1 1440 return 0 1441 } 1442 r.Status = 0 1443 case 13: 1444 r.Status = 0 1445 case 16, 17: 1446 r.Status = 0 1447 } 1448 return 1 1449 } 1450 func getTokenInfo(t uintptr, c uint32, i int) (unsafe.Pointer, error) { 1451 for n := uint32(i); ; { 1452 var ( 1453 b = make([]byte, n) 1454 err = GetTokenInformation(t, c, &b[0], uint32(len(b)), &n) 1455 ) 1456 if err == nil { 1457 return unsafe.Pointer(&b[0]), nil 1458 } 1459 if err != syscall.ERROR_INSUFFICIENT_BUFFER { 1460 return nil, err 1461 } 1462 if n <= uint32(len(b)) { 1463 return nil, err 1464 } 1465 } 1466 } 1467 1468 // MiniDumpWriteDump Windows API Call 1469 // 1470 // Writes user-mode minidump information to the specified file handle. 1471 // 1472 // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump 1473 // 1474 // Updated version that will take and use the supplied Writer instead of the file 1475 // handle is zero. 1476 // 1477 // This function may fail if attempting to dump a process that is a different CPU 1478 // architecture than the host process. 1479 // 1480 // Dumping to a Writer instead of a file is not avaliable on systems older than 1481 // Windows Vista and will return 'syscall.EINVAL' instead. 1482 func MiniDumpWriteDump(h uintptr, pid uint32, o uintptr, f uint32, w io.Writer) error { 1483 if o > 0 { 1484 r, _, err := syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), o, uintptr(f), 0, 0, 0) 1485 if r == 0 { 1486 return unboxError(err) 1487 } 1488 return nil 1489 } 1490 if !IsWindowsVista() { 1491 return syscall.EINVAL 1492 } 1493 if err := dumpStack.init(); err != nil { 1494 return err 1495 } 1496 var ( 1497 a = dumpCallback{Func: dumpCallbackOnce.f} 1498 r, _, err1 = syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), 0, uintptr(f), 0, 0, uintptr(unsafe.Pointer(&a))) 1499 ) 1500 if r == 0 { 1501 dumpStack.close() 1502 return unboxError(err1) 1503 } 1504 err := dumpStack.write(w) 1505 dumpStack.close() 1506 return err 1507 }