github.com/iDigitalFlame/xmt@v0.5.4/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 // TODO(dij): There is a memory leak here! 671 // Need to call 'localFree' with the ptr to 'i'. 672 return i.SID, nil 673 } 674 func heapFree(h, m uintptr) error { 675 r, _, err := syscallN(funcRtlFreeHeap.address(), h, 0, m) 676 if r == 0 { 677 return unboxError(err) 678 } 679 return nil 680 } 681 func heapDestroy(h uintptr) error { 682 r, _, err := syscallN(funcRtlDestroyHeap.address(), h) 683 if r == 0 { 684 return unboxError(err) 685 } 686 return nil 687 } 688 689 // SetWallpaper uses the 'SystemParametersInfo' API call to set the user's 690 // wallpaper. Changes take effect immediately. 691 // 692 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 693 func SetWallpaper(s string) error { 694 v, err := UTF16PtrFromString(s) 695 if err != nil { 696 return err 697 } 698 // 0x14 - SPI_SETDESKWALLPAPER 699 r, _, err1 := syscallN(funcSystemParametersInfo.address(), 0x14, 1, uintptr(unsafe.Pointer(v)), 0x3) 700 if r == 0 { 701 return unboxError(err1) 702 } 703 return nil 704 } 705 706 // SetHighContrast uses the 'SystemParametersInfo' API call to trigger the 707 // HighContrast theme setting. Set to 'True' to enable it and 'False' to disbale 708 // it. 709 // 710 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 711 func SetHighContrast(e bool) error { 712 var c highContrast 713 if c.Size = uint32(unsafe.Sizeof(c)); e { 714 c.Flags = 1 715 } 716 // 0x43 - SPI_SETHIGHCONTRAST 717 r, _, err := syscallN(funcSystemParametersInfo.address(), 0x43, 0, uintptr(unsafe.Pointer(&c)), 0x3) 718 if r == 0 { 719 return unboxError(err) 720 } 721 return nil 722 } 723 724 // InWow64Process is a helper function that just calls'IsWow64Process' with the 725 // 'CurrentProcess' handle to determine if the current process is a WOW64 process. 726 func InWow64Process() (bool, error) { 727 return IsWow64Process(CurrentProcess) 728 } 729 730 /* 731 // IsVirtualizationEnabled return true if the current device processor has the 732 // PF_VIRT_FIRMWARE_ENABLED flag. This will just indicate if the device has the 733 // capability to run Virtual Machines. This is commonly not the case of many VMs 734 // themselves. 735 // 736 // Be cautious, as many hypervisors have the ability to still expose this CPU 737 // flag to guests. 738 // 739 // This flag is grabbed from KSHARED_USER_DATA. 740 func IsVirtualizationEnabled() bool { 741 // 0x15 - PF_VIRT_FIRMWARE_ENABLED 742 return (*kernelSharedData)(unsafe.Pointer(kernelShared)).ProcessorFeatures[0x15] > 0 743 }*/ 744 745 // SetCommandLine will attempt to read the Process PEB and overrite the 746 // 'ProcessParameters.CommandLine' property with the supplied string value. 747 // 748 // This will NOT change the ImagePath or Binary Name. 749 // 750 // This will return any errors that occur during reading the PEB. 751 // 752 // DOES NOT WORK ON WOW6432 PEBs! 753 // - These are in a separate memory space and seem to only be read once? or the 754 // data is copied somewhere else. Even if I call 'NtWow64QueryInformationProcess64' 755 // and change it, it does NOT seem to care. *shrug* who TF uses x86 anyway in 2022!? 756 // 757 // TODO(dij): Since we have backwards compatibility now. The 32bit PEB can be read 758 // using NtQueryInformationProcess/ProcessWow64Information which returns 759 // 32bit pointer to the PEB in 32bit mode. 760 func SetCommandLine(s string) error { 761 c, err := UTF16FromString(s) 762 if err != nil { 763 return err 764 } 765 p, err := getProcessPeb() 766 if err != nil { 767 return err 768 } 769 p.ProcessParameters.CommandLine.Buffer = &c[0] 770 p.ProcessParameters.CommandLine.Length = uint16(len(c)*2) - 1 771 p.ProcessParameters.CommandLine.MaximumLength = p.ProcessParameters.CommandLine.Length 772 return nil 773 } 774 775 // SwapMouseButtons uses the 'SystemParametersInfo' API call to trigger the 776 // swapping of the left and right mouse buttons. Set to 'True' to swap and 777 // 'False' to disable it. 778 // 779 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa 780 func SwapMouseButtons(e bool) error { 781 var v uint32 782 if e { 783 v = 1 784 } 785 // 0x21 - SPI_SETMOUSEBUTTONSWAP 786 r, _, err := syscallN(funcSystemParametersInfo.address(), 0x21, uintptr(v), 0, 0x3) 787 if r == 0 { 788 return unboxError(err) 789 } 790 return nil 791 } 792 func formatNtError(e uintptr) error { 793 // NOTE(dij): Not loading NTDLL here as we /should/ already have loaded it 794 // as we're calling this function due to an Nt* function error 795 // status. If not, this just acts like a standard 'FormatMessage' 796 // call. 797 var ( 798 o [300]uint16 799 r, _, _ = syscallN(funcFormatMessage.address(), 0x3A00, dllNtdll.addr, e, 0x409, uintptr(unsafe.Pointer(&o)), 0x12C, 0) 800 // 0x3A00 - FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE | 801 // FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS 802 // 0x409 - English LANG and English SUB 803 ) 804 if r == 0 { 805 return syscall.Errno(e) 806 } 807 v := r 808 // Remove newline at the end 809 for ; r > 0; r-- { 810 if o[r] == '\n' || o[r] == '\r' { 811 if r > 1 && (o[r-1] == '\n' || o[r-1] == '\r') { 812 r-- 813 } 814 break 815 } 816 } 817 // CAan't find it? Just return what we have. 818 if r == 0 { 819 return errors.New(UTF16ToString(o[:v])) 820 } 821 // Remove prepended "{TYPE}" string 822 if o[0] == '{' { 823 for i := uintptr(1); i < r; i++ { 824 if o[i] == '\n' || o[i] == '\r' { 825 if i+1 < r && (o[i+1] == '\n' || o[i+1] == '\r') { 826 i++ 827 } 828 return errors.New(UTF16ToString(o[i+1 : r])) 829 } 830 } 831 } 832 return errors.New(UTF16ToString(o[:r])) 833 } 834 835 // GetLocalUser attempts to return the username associated with the current Thread 836 // or Process. 837 // 838 // This function will first check if the Thread is using a Token (Impersonation) 839 // and if not it will then pull the Token for the Process instead. 840 // 841 // This function will concationate the domain (or local workstation) name if the 842 // Token provides one. 843 // 844 // If any errors occur, an empty string with the error will be returned. 845 func GetLocalUser() (string, error) { 846 var t uintptr 847 // 0x20008 - TOKEN_READ | TOKEN_QUERY 848 if err := OpenThreadToken(CurrentThread, 0x20008, true, &t); err != nil { 849 if err = OpenProcessToken(CurrentProcess, 0x20008, &t); err != nil { 850 return "", err 851 } 852 } 853 u, err := UserFromToken(t) 854 if CloseHandle(t); err != nil { 855 return "", err 856 } 857 return u, nil 858 } 859 860 // CheckDebugWithLoad will attempt to check for a debugger by loading a non-loaded 861 // DLL specified and will check for exclusive access (which is false for debuggers). 862 // 863 // If the file can be opened, the library is freed and the file is closed. This 864 // will return true ONLY if opening for exclusive access fails. 865 // 866 // Any errors opening or loading DLLs will silently return false. 867 func CheckDebugWithLoad(d string) bool { 868 var ( 869 p = fullPath(d) 870 n, err = UTF16PtrFromString(p) 871 ) 872 if err != nil { 873 return false 874 } 875 var ( 876 h uintptr 877 r, _, _ = syscallN(funcGetModuleHandleEx.address(), 0x2, uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&h))) 878 // 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 879 ) 880 if r > 0 { 881 return false 882 } 883 if h, err = loadLibraryEx(p); err != nil || h == 0 { 884 return false 885 } 886 // 0x80000000 - FILE_FLAG_WRITE_THROUGH 887 // 0x0 - EXCLUSIVE 888 // 0x3 - OPEN_EXISTING 889 f, err := CreateFile(p, 0x80000000, 0, nil, 0x3, 0, 0) 890 if syscall.FreeLibrary(syscall.Handle(h)); err != nil { 891 return err.(syscall.Errno) != 0x2 892 } 893 CloseHandle(f) 894 return false 895 } 896 897 // IsUserNetworkToken will return true if the origin of the Token was a LoginUser 898 // network impersonation API call and NOT a duplicated Token via Token or Thread 899 // impersonation. 900 func IsUserNetworkToken(t uintptr) bool { 901 if t == 0 { 902 return false 903 } 904 var ( 905 n uint32 906 b [16]byte 907 err = GetTokenInformation(t, 0x7, &b[0], 16, &n) 908 // 0x7 - TokenSource 909 ) 910 if err != nil { 911 return false 912 } 913 // Match [65 100 118 97 112 105 32 32] == "Advapi" 914 return b[0] == 65 && b[1] == 100 && b[6] == 32 && b[7] == 32 915 } 916 917 // EnablePrivileges will attempt to enable the supplied Windows privilege values 918 // on the current process's Token. 919 // 920 // Errors during encoding, lookup or assignment will be returned and not all 921 // privileges will be assigned, if they occur. 922 func EnablePrivileges(s ...string) error { 923 if len(s) == 0 { 924 return nil 925 } 926 var ( 927 t uintptr 928 err = OpenProcessToken(CurrentProcess, 0x200E8, &t) 929 // 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE 930 // (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT) 931 ) 932 if err != nil { 933 return xerr.Wrap("OpenProcessToken", err) 934 } 935 err = EnableTokenPrivileges(t, s...) 936 CloseHandle(t) 937 return err 938 } 939 940 // IsSecureBootEnabled returns true if Secure Boot is enabled in the current device. 941 // 942 // This function returns true or false and any errors that may occur during checking 943 // for secure boot. 944 func IsSecureBootEnabled() (bool, error) { 945 var ( 946 i uint16 947 n uint32 948 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x91, uintptr(unsafe.Pointer(&i)), 2, uintptr(unsafe.Pointer(&n))) 949 // 0x91 - SystemSecureBootInformation 950 ) 951 if r > 0 { 952 return false, formatNtError(r) 953 } 954 return (i & 0xFF) == 1, nil 955 } 956 957 // SetAllThreadsToken sets the Token for all current Golang threads. This is an 958 // easy way to do thread impersonation across the entire runtime. 959 // 960 // Calls 'ForEachThread' -> 'SetThreadToken' under the hood. 961 func SetAllThreadsToken(h uintptr) error { 962 return ForEachThread(func(t uintptr) error { return SetThreadToken(t, h) }) 963 } 964 func getProcessPeb() (*processPeb, error) { 965 /* 966 PVOID64 GetPeb64() 967 { 968 PVOID64 peb64 = NULL; 969 970 if (API::IsAvailable(API_IDENTIFIER::API_NtWow64QueryInformationProcess64)) 971 { 972 PROCESS_BASIC_INFORMATION_WOW64 pbi64 = {}; 973 974 auto NtWow64QueryInformationProcess64 = static_cast<pNtWow64QueryInformationProcess64>(API::GetAPI(API_IDENTIFIER::API_NtWow64QueryInformationProcess64)); 975 NTSTATUS status = NtWow64QueryInformationProcess64(GetCurrentProcess(), ProcessBasicInformation, &pbi64, sizeof(pbi64), nullptr); 976 if ( NT_SUCCESS ( status ) ) 977 peb64 = pbi64.PebBaseAddress; 978 } 979 980 return peb64; 981 } 982 */ 983 var ( 984 p processBasicInfo 985 r, _, _ = syscallN( 986 funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)), 987 unsafe.Sizeof(p), 0, 988 ) 989 ) 990 if r > 0 { 991 return nil, formatNtError(r) 992 } 993 return (*processPeb)(unsafe.Pointer(p.PebBaseAddress)), nil 994 } 995 996 // ImpersonatePipeToken will attempt to impersonate the Token used by the Named 997 // Pipe client. 998 // 999 // This function is only usable on Windows with a Server Pipe handle. 1000 // 1001 // BUG(dij): I'm not sure if this is broken or this is how it's handled. I'm 1002 // 1003 // getting error 5. 1004 // 1005 // Pipe insights: 1006 // 1007 // https://papers.vx-underground.org/papers/Windows/System%20Components%20and%20Abuse/Offensive%20Windows%20IPC%20Internals%201%20Named%20Pipes.pdf 1008 func ImpersonatePipeToken(h uintptr) error { 1009 // NOTE(dij): For best results, we FIRST impersonate the token, THEN 1010 // we try to set the token to each user thread with a duplicated 1011 // token set to impersonate. (Similar to an Impersonate call). 1012 runtime.LockOSThread() 1013 if err := ImpersonateNamedPipeClient(h); err != nil { 1014 runtime.UnlockOSThread() 1015 return err 1016 } 1017 var y uintptr 1018 // 0xF01FF - TOKEN_ALL_ACCESS 1019 if err := OpenThreadToken(CurrentThread, 0xF01FF, true, &y); err != nil { 1020 runtime.UnlockOSThread() 1021 return err 1022 } 1023 err := SetAllThreadsToken(y) 1024 CloseHandle(y) 1025 runtime.UnlockOSThread() 1026 return err 1027 } 1028 func heapCreate(n uint64) (uintptr, error) { 1029 // 0x1002 - MEM_COMMIT? | HEAP_GROWABLE 1030 r, _, err := syscallN(funcRtlCreateHeap.address(), 0x1002, 0, 0, uintptr(n), 0, 0) 1031 if r == 0 { 1032 return 0, unboxError(err) 1033 } 1034 return r, nil 1035 } 1036 func (p *dumpParam) resize(n uint64) error { 1037 if n < p.s { 1038 return nil 1039 } 1040 var ( 1041 v = (p.s + n) * 2 1042 h, err = heapReAlloc(p.b, p.h, v, false) 1043 ) 1044 if err != nil { 1045 return err 1046 } 1047 p.h, p.s = h, v 1048 return nil 1049 } 1050 1051 // PhysicalInfo will query the system using NtQuerySystemInformation to grab the 1052 // number of CPUs installed and the current memory (in MB) that is avaliable to 1053 // the system (installed physically). 1054 func PhysicalInfo() (uint8, uint32, error) { 1055 var ( 1056 n uint64 1057 i systemBasicInfo 1058 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x0, uintptr(unsafe.Pointer(&i)), unsafe.Sizeof(i), uintptr(unsafe.Pointer(&n))) 1059 // 0x0 - SystemBasicInformation 1060 ) 1061 if r > 0 { 1062 return 0, 0, formatNtError(r) 1063 } 1064 return i.NumProc, uint32((uint64(i.PageSize)*uint64(i.PhysicalPages))/0x100000) + 1, nil 1065 } 1066 func getThreadID(h uintptr) (uint32, error) { 1067 var ( 1068 t threadBasicInfo 1069 r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0, uintptr(unsafe.Pointer(&t)), unsafe.Sizeof(t), 0) 1070 ) 1071 if r > 0 { 1072 return 0, formatNtError(r) 1073 } 1074 return uint32(t.ClientID.Thread), nil 1075 } 1076 1077 // GetCodeIntegrityState returns a bitvalue that returns the Code Integrity status 1078 // of the current device. If the return value is zero without an error, this means 1079 // that code integrity is disabled. 1080 func GetCodeIntegrityState() (uint32, error) { 1081 var ( 1082 n uint32 1083 s = [2]uint32{8, 0} 1084 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x67, uintptr(unsafe.Pointer(&s)), 8, uintptr(unsafe.Pointer(&n))) 1085 // 0x67 - SystemCodeIntegrityInformation 1086 ) 1087 if r > 0 { 1088 return 0, formatNtError(r) 1089 } 1090 return s[1], nil 1091 } 1092 func (p *dumpParam) write(w io.Writer) error { 1093 var ( 1094 b = (*[]byte)(unsafe.Pointer(&SliceHeader{Data: unsafe.Pointer(p.h), Len: int(p.w), Cap: int(p.w)})) 1095 n, err = w.Write(*b) 1096 ) 1097 if b, *b = nil, nil; err != nil { 1098 return err 1099 } 1100 if n != int(p.w) { 1101 return io.ErrShortWrite 1102 } 1103 return nil 1104 } 1105 1106 // GetDiskSize returns the size in bytes of the disk by it's NT path or the path 1107 // to a partition or volume on the disk. 1108 // 1109 // Any errors encountered during reading will be returned. 1110 // 1111 // The name can be in the format of an NT path such as: 1112 // 1113 // - \\.\C: 1114 // - \\.\PhysicalDrive0 1115 // 1116 // Both are equal on /most/ systems. 1117 func GetDiskSize(name string) (uint64, error) { 1118 // 0x1 - FILE_SHARE_READ 1119 // 0x3 - OPEN_EXISTING 1120 h, err := CreateFile(name, 0, 0x1, nil, 0x3, 0, 0) 1121 if err != nil { 1122 return 0, err 1123 } 1124 var ( 1125 g diskGeometryEx 1126 s [4 + ptrSize]byte // IO_STATUS_BLOCK 1127 ) 1128 // 0x700A0 - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 1129 r, _, err := syscallN(funcNtDeviceIoControlFile.address(), h, 0, 0, 0, uintptr(unsafe.Pointer(&s)), 0x700A0, 0, 0, uintptr(unsafe.Pointer(&g)), 0x20+ptrSize) 1130 if CloseHandle(h); r > 0 { 1131 return 0, formatNtError(r) 1132 } 1133 return g.Size, nil 1134 } 1135 1136 // UserFromToken will attempt to get the User SID from the supplied Token and 1137 // return the associated Username and Domain string from the SID. 1138 func UserFromToken(h uintptr) (string, error) { 1139 u, err := GetTokenUser(h) 1140 if err != nil { 1141 return "", err 1142 } 1143 return u.User.Sid.UserName() 1144 } 1145 1146 // ForEachThread is a helper function that allows a function to be executed with 1147 // the handle of the Thread. 1148 // 1149 // This function only returns an error if enumerating the Threads generates an 1150 // error or the supplied function returns an error. 1151 // 1152 // This function ONLY targets Golang threads. To target all Process threads, 1153 // use 'ForEachProcThread'. 1154 func ForEachThread(f func(uintptr) error) error { 1155 var err error 1156 for i := uintptr(allm); ; { 1157 if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 { 1158 if err = f(h); err != nil { 1159 break 1160 } 1161 } 1162 n := (*uintptr)(unsafe.Pointer(i + ptrNext)) 1163 if n == nil || *n == 0 { 1164 break // Reached bottom of linked list 1165 } 1166 i = *n 1167 } 1168 return err 1169 } 1170 1171 // GetTokenUser retrieves access token user account information and SID. 1172 func GetTokenUser(h uintptr) (*TokenUser, error) { 1173 u, err := getTokenInfo(h, 1, 50) 1174 if err != nil { 1175 return nil, err 1176 } 1177 return (*TokenUser)(u), nil 1178 } 1179 1180 // GetVersionNumbers returns the NTDLL internal version numbers as Major, Minor 1181 // and Build. 1182 // 1183 // This function should return the correct values regardless of manifest version. 1184 func GetVersionNumbers() (uint32, uint32, uint16) { 1185 var m, n, b uint32 1186 syscallN(funcRtlGetNtVersionNumbers.address(), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Pointer(&n)), uintptr(unsafe.Pointer(&b))) 1187 return m, n, uint16(b) 1188 } 1189 func enablePrivileges(h uintptr, s []string) error { 1190 var ( 1191 p privileges 1192 err error 1193 ) 1194 for i := range s { 1195 if i > 5 { 1196 break 1197 } 1198 if err = LookupPrivilegeValue("", s[i], &p.Privileges[i].Luid); err != nil { 1199 if xerr.ExtendedInfo { 1200 return xerr.Wrap(`cannot lookup "`+s[i]+`"`, err) 1201 } 1202 return xerr.Wrap("cannot lookup Privilege", err) 1203 } 1204 p.Privileges[i].Attributes = 0x2 // SE_PRIVILEGE_ENABLED 1205 } 1206 p.PrivilegeCount = uint32(len(s)) 1207 if err = AdjustTokenPrivileges(h, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil); err != nil { 1208 return xerr.Wrap("cannot assign all Privileges", err) 1209 } 1210 return nil 1211 } 1212 1213 // GetProcessFileName will attempt to retrieve the basename of the process 1214 // related to the open Process handle supplied. 1215 func GetProcessFileName(h uintptr) (string, error) { 1216 var ( 1217 u ntUnicodeString 1218 n uint32 1219 ) 1220 r, _, _ := syscallN( 1221 funcNtQueryInformationProcess.address(), h, 0x1B, uintptr(unsafe.Pointer(&u)), 1222 unsafe.Sizeof(u)+260, uintptr(unsafe.Pointer(&n)), 1223 ) 1224 // 0x1B - ProcessImageFileName 1225 if r > 0 { 1226 return "", formatNtError(r) 1227 } 1228 v := UTF16ToString(u.Buffer[4:n]) 1229 for i := len(v) - 1; i > 0; i-- { 1230 if v[i] == '\\' { 1231 return v[i+1:], nil 1232 } 1233 } 1234 return v, nil 1235 } 1236 1237 // ForEachProcThread is a helper function that allows a function to be executed 1238 // with the handle of the Thread. 1239 // 1240 // This function only returns an error if enumerating the Threads generates an 1241 // error or the supplied function returns an error. 1242 // 1243 // This function targets ALL threads (including non-Golang threads). To target 1244 // all only Golang threads, use 'ForEachThread'. 1245 func ForEachProcThread(f func(uintptr) error) error { 1246 return EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error { 1247 // old (0xE0 - THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN) 1248 // 0x1FFFFF - THREAD_ALL_ACCESS 1249 v, err := t.Handle(0x1FFFFF) 1250 if err != nil { 1251 return err 1252 } 1253 err = f(v) 1254 CloseHandle(v) 1255 return err 1256 }) 1257 } 1258 func getThreadStartAddress(h uintptr) (uintptr, error) { 1259 var ( 1260 i uintptr 1261 r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0x9, uintptr(unsafe.Pointer(&i)), ptrSize, 0) 1262 // 0x9 - ThreadQuerySetWin32StartAddress 1263 ) 1264 if r > 0 { 1265 return 0, formatNtError(r) 1266 } 1267 return i, nil 1268 } 1269 1270 // FileSigningIssuerName attempts to read the Authenticate signing certificate 1271 // issuer name for the specified file path. 1272 // 1273 // If the file does not exist or a certificate cannot be found, this returns the 1274 // error 'syscall.EINVAL'. 1275 // 1276 // If the function success, the return result will be the string name of the 1277 // certificate issuer. 1278 func FileSigningIssuerName(path string) (string, error) { 1279 f, err1 := UTF16PtrFromString(path) 1280 if err1 != nil { 1281 return "", err1 1282 } 1283 var ( 1284 s, h uintptr 1285 r, _, err = syscallN( 1286 funcCryptQueryObject.address(), 0x1, uintptr(unsafe.Pointer(f)), 0x400, 0x2, 1287 0, 0, 0, 0, uintptr(unsafe.Pointer(&s)), uintptr(unsafe.Pointer(&h)), 0, 1288 ) 1289 // 0x1 - CERT_QUERY_OBJECT_FILE 1290 // 0x400 - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED 1291 // 0x2 - CERT_QUERY_FORMAT_FLAG_BINARY 1292 ) 1293 if r == 0 { 1294 if err == 0x80092009 { // 0x80092009 - Object not found, file isn't signed. 1295 return "", syscall.EINVAL 1296 } 1297 return "", unboxError(err) 1298 } 1299 var x uint32 1300 // 0x6 - CMSG_SIGNER_INFO_PARAM 1301 if r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, 0, uintptr(unsafe.Pointer(&x))); r == 0 { 1302 syscallN(funcCryptMsgClose.address(), h) 1303 syscallN(funcCertCloseStore.address(), s) 1304 return "", unboxError(err) 1305 } 1306 b := make([]byte, x) 1307 // 0x6 - CMSG_SIGNER_INFO_PARAM 1308 r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&x))) 1309 if syscallN(funcCryptMsgClose.address(), h); r == 0 { 1310 syscallN(funcCertCloseStore.address(), s) 1311 return "", unboxError(err) 1312 } 1313 var ( 1314 v = (*certSigner)(unsafe.Pointer(&b[0])) 1315 i = certInfo{Issuer: v.Issuer, Serial: v.Serial} 1316 ) 1317 // 0x10001 - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING 1318 // 0xB0000 - CERT_FIND_SUBJECT_CERT 1319 r, _, err = syscallN(funcCertFindCertificateInStore.address(), s, 0x10001, 0, 0xB0000, uintptr(unsafe.Pointer(&i)), 0) 1320 if syscallN(funcCertCloseStore.address(), s); r == 0 { 1321 return "", unboxError(err) 1322 } 1323 var ( 1324 n string 1325 k uintptr 1326 ) 1327 // 0x4 - CERT_NAME_SIMPLE_DISPLAY_TYPE 1328 // 0x0 - CERT_NAME_ISSUER_FLAG 1329 if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, 0, 0); k > 0 { 1330 c := make([]uint16, k) 1331 if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, uintptr(unsafe.Pointer(&c[0])), k); k > 0 { 1332 n = UTF16ToString(c[:k]) 1333 } 1334 } 1335 if syscallN(funcCertFreeCertificateContext.address(), r); k == 0 { 1336 return "", unboxError(err) 1337 } 1338 return n, nil 1339 } 1340 1341 // StringListToUTF16Block creates a UTF16 encoded block for usage as a Process 1342 // environment block. 1343 // 1344 // This function returns an error if any of the environment strings are not in 1345 // the 'KEY=VALUE' format or contain a NUL byte. 1346 func StringListToUTF16Block(s []string) (*uint16, error) { 1347 if len(s) == 0 { 1348 return nil, nil 1349 } 1350 var t, i, l int 1351 for _, x := range s { 1352 for v := range x { 1353 if x[v] == 0 { 1354 return nil, syscall.EINVAL 1355 } 1356 } 1357 if q := strings.IndexByte(x, '='); q <= 0 { 1358 if xerr.ExtendedInfo { 1359 return nil, xerr.Sub(`invalid env value "`+x+`"`, 0x17) 1360 } 1361 return nil, xerr.Sub("invalid env value", 0x17) 1362 } 1363 t += len(x) + 1 1364 } 1365 t++ 1366 b := make([]byte, t) 1367 for _, v := range s { 1368 l = len(v) 1369 copy(b[i:i+l], v) 1370 b[i+l] = 0 1371 i = i + l + 1 1372 } 1373 b[i] = 0 1374 return &UTF16EncodeStd([]rune(string(b)))[0], nil 1375 } 1376 1377 // EnableTokenPrivileges will attempt to enable the supplied Windows privilege 1378 // values on the supplied process Token. 1379 // 1380 // Errors during encoding, lookup or assignment will be returned and not all 1381 // privileges will be assigned, if they occur. 1382 func EnableTokenPrivileges(h uintptr, s ...string) error { 1383 if len(s) == 0 { 1384 return nil 1385 } 1386 if len(s) <= 5 { 1387 return enablePrivileges(h, s) 1388 } 1389 for x, w := 0, 0; x < len(s); { 1390 if w = 5; x+w > len(s) { 1391 w = len(s) - x 1392 } 1393 if err := enablePrivileges(h, s[x:x+w]); err != nil { 1394 return err 1395 } 1396 x += w 1397 } 1398 return nil 1399 } 1400 func heapAlloc(h uintptr, s uint64, z bool) (uintptr, error) { 1401 var f uint32 1402 if z { 1403 f |= 0x08 1404 } 1405 r, _, err := syscallN(funcRtlAllocateHeap.address(), h, uintptr(f), uintptr(s)) 1406 if r == 0 { 1407 return 0, unboxError(err) 1408 } 1409 return r, nil 1410 } 1411 func (p *dumpParam) copy(o uint64, b uintptr, s uint32) error { 1412 if err := p.resize(o + uint64(s)); err != nil { 1413 return err 1414 } 1415 copyMemory(p.h+uintptr(o), b, s) 1416 p.w += uint64(s) 1417 return nil 1418 } 1419 func heapReAlloc(h, m uintptr, s uint64, z bool) (uintptr, error) { 1420 var f uint32 1421 if z { 1422 // 0x8 - HEAP_ZERO_MEMORY 1423 f |= 0x8 1424 } 1425 r, _, err := syscallN(funcRtlReAllocateHeap.address(), h, uintptr(f), m, uintptr(s)) 1426 if r == 0 { 1427 return 0, unboxError(err) 1428 } 1429 return r, nil 1430 } 1431 func dumpCallbackFunc(_ uintptr, i uintptr, r *dumpOutput) uintptr { 1432 switch *(*uint32)(unsafe.Pointer(i + 4 + ptrSize)) { 1433 case 11: 1434 r.Status = 1 1435 case 12: 1436 var ( 1437 o = *(*uint64)(unsafe.Pointer(i + 8 + (ptrSize * 2))) // Offset 1438 b = *(*uintptr)(unsafe.Pointer(i + 16 + (ptrSize * 2))) // Buffer 1439 s = *(*uint32)(unsafe.Pointer(i + 16 + (ptrSize * 3))) // Size 1440 ) 1441 if err := dumpStack.copy(o, b, s); err != nil { 1442 r.Status = 1 1443 return 0 1444 } 1445 r.Status = 0 1446 case 13: 1447 r.Status = 0 1448 case 16, 17: 1449 r.Status = 0 1450 } 1451 return 1 1452 } 1453 func getTokenInfo(t uintptr, c uint32, i int) (unsafe.Pointer, error) { 1454 for n := uint32(i); ; { 1455 var ( 1456 b = make([]byte, n) 1457 err = GetTokenInformation(t, c, &b[0], uint32(len(b)), &n) 1458 ) 1459 if err == nil { 1460 return unsafe.Pointer(&b[0]), nil 1461 } 1462 if err != syscall.ERROR_INSUFFICIENT_BUFFER { 1463 return nil, err 1464 } 1465 if n <= uint32(len(b)) { 1466 return nil, err 1467 } 1468 } 1469 } 1470 1471 // MiniDumpWriteDump Windows API Call 1472 // 1473 // Writes user-mode minidump information to the specified file handle. 1474 // 1475 // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump 1476 // 1477 // Updated version that will take and use the supplied Writer instead of the file 1478 // handle is zero. 1479 // 1480 // This function may fail if attempting to dump a process that is a different CPU 1481 // architecture than the host process. 1482 // 1483 // Dumping to a Writer instead of a file is not avaliable on systems older than 1484 // Windows Vista and will return 'syscall.EINVAL' instead. 1485 func MiniDumpWriteDump(h uintptr, pid uint32, o uintptr, f uint32, w io.Writer) error { 1486 if o > 0 { 1487 r, _, err := syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), o, uintptr(f), 0, 0, 0) 1488 if r == 0 { 1489 return unboxError(err) 1490 } 1491 return nil 1492 } 1493 if !IsWindowsVista() { 1494 return syscall.EINVAL 1495 } 1496 if err := dumpStack.init(); err != nil { 1497 return err 1498 } 1499 var ( 1500 a = dumpCallback{Func: dumpCallbackOnce.f} 1501 r, _, err1 = syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), 0, uintptr(f), 0, 0, uintptr(unsafe.Pointer(&a))) 1502 ) 1503 if r == 0 { 1504 dumpStack.close() 1505 return unboxError(err1) 1506 } 1507 err := dumpStack.write(w) 1508 dumpStack.close() 1509 return err 1510 }