//go:build guest_windows package ntapi import ( "encoding/binary" "fmt" "os" "runtime" "sort" "strings" "time" "github.com/mirkobrombin/Raven/pkg/arch" "github.com/mirkobrombin/Raven/pkg/mmu" ravelog "github.com/mirkobrombin/Raven/pkg/log" "golang.org/x/sys/unix" ) // NTSTATUS codes. const ( StatusSuccess uint32 = 0x00000000 StatusInformationLength uint32 = 0x00000004 StatusBufferOverflow uint32 = 0x80000005 StatusNoMoreEntries uint32 = 0x8000001A StatusNoMoreFiles uint32 = 0x80000006 StatusObjectNameNotFound uint32 = 0xC0000034 StatusObjectNameInvalid uint32 = 0xC0000033 StatusObjectPathNotFound uint32 = 0xC000003A StatusNotImplemented uint32 = 0xC0000002 StatusAccessViolation uint32 = 0xC0000005 StatusInvalidHandle uint32 = 0xC0000008 StatusInvalidParameter uint32 = 0xC000000D StatusAccessDenied uint32 = 0xC0000022 StatusBufferTooSmall uint32 = 0xC0000023 StatusNoSuchFile uint32 = 0xC000000F StatusNoMemory uint32 = 0xC0000017 StatusObjectTypeMismatch uint32 = 0xC0000024 StatusObjectNameCollision uint32 = 0xC0000035 StatusKeyNotFound uint32 = 0xC0000225 StatusNotFound uint32 = 0xC0000225 StatusPending uint32 = 0x00000103 StatusTimeout uint32 = 0x00000102 StatusWait0 uint32 = 0x00000000 StatusAbandoned uint32 = 0x00000080 StatusAlerted uint32 = 0x00000101 StatusProcessIsTerminating uint32 = 0xC000010A StatusEndOfFile uint32 = 0xC0000011 StatusFileLockConflict uint32 = 0xC0000054 StatusLockNotGranted uint32 = 0xC0000055 StatusRangeNotLocked uint32 = 0xC000007E StatusCancelled uint32 = 0xC0000120 ) // File access and creation flags. const ( fileSupersede = 0x00000000 fileOpen = 0x00000001 fileCreate = 0x00000002 fileOpenIf = 0x00000003 fileOverwrite = 0x00000004 fileOverwriteIf = 0x00000005 fileDirectoryFile = 0x00000001 fileNonDirectoryFile = 0x00000040 fileSynchronousIoAlert = 0x00000010 fileSynchronousIoNonalert = 0x00000020 nativeFileFlagOverlapped = uint32(0x40000000) nativeUseFilePointer = uint64(0xFFFFFFFFFFFFFFFE) nativeWriteToEndOfFile = uint64(0xFFFFFFFFFFFFFFFF) ) // FileInformationClass values. const ( fileDirectoryInformation = 1 fileBasicInformation = 4 fileStandardInformation = 5 fileNameInformation = 9 filePositionInformation = 14 fileDispositionInformation = 13 fileBothDirectoryInformation = 3 fileEndOfFileInformation = 20 fileAllInformation = 18 ) // ======================== FILE I/O ======================== const ( dupCloseSource = 0x00000001 dupSameAccess = 0x00000002 memCommit = 0x00001000 memReserve = 0x00002000 memDecommit = 0x00004000 memRelease = 0x00008000 secCommit = 0x08000000 ) type ntDirEntry struct { Name string IsDir bool Size int64 Atime time.Time Mtime time.Time Ctime time.Time Nlink uint32 } func writeGuestPtr(mem *mmu.MMU, addr, val uint64) { if addr == 0 { return } if mem.Mode32 { mem.Write32(addr, uint32(val)) return } mem.Write64(addr, val) } func guestPtrSize(mem *mmu.MMU) uint64 { if mem.Mode32 { return 4 } return 8 } func validateGuestWrite(mem *mmu.MMU, addr, size uint64) bool { return addr != 0 && mem.ValidateRange(addr, size, mmu.PermWrite) == nil } func signalHandleEvent(ctx *TrapContext, eventHandle Handle) { if eventHandle == 0 { return } if eh := ctx.Handles.GetEvent(eventHandle); eh != nil { eh.Set() } } func ntStatusFromErr(err error, notFound uint32) uint32 { if err == nil { return StatusSuccess } msg := strings.ToLower(err.Error()) switch { case strings.Contains(msg, "not found"), strings.Contains(msg, "no such"): return notFound case strings.Contains(msg, "exist"), strings.Contains(msg, "collision"): return StatusObjectNameCollision case strings.Contains(msg, "directory"): return StatusAccessDenied default: return StatusAccessDenied } } func desiredAccessToOpenFlags(desiredAccess uint32) int { const ( genericRead = 0x80000000 genericWrite = 0x40000000 fileReadData = 0x0001 fileWriteData = 0x0002 fileAppend = 0x0004 ) canRead := desiredAccess&genericRead != 0 || desiredAccess&fileReadData != 0 canWrite := desiredAccess&genericWrite != 0 || desiredAccess&(fileWriteData|fileAppend) != 0 switch { case canRead && canWrite: return os.O_RDWR case canWrite: return os.O_WRONLY default: return os.O_RDONLY } } func getFileOffset(ctx *TrapContext, fh *FileHandle) uint64 { if fh == nil { return 0 } off, err := ctx.FS.Lseek(fh.FD, 0, 1) if err != nil || off < 0 { return 0 } return uint64(off) } func wildcardMatch(pattern, name string) bool { pattern = strings.ToLower(pattern) name = strings.ToLower(name) var match func(pi, ni int) bool match = func(pi, ni int) bool { for pi < len(pattern) { switch pattern[pi] { case '*': for pi+1 < len(pattern) && pattern[pi+1] == '*' { pi++ } if pi+1 == len(pattern) { return true } for i := ni; i <= len(name); i++ { if match(pi+1, i) { return true } } return false case '?': if ni >= len(name) { return false } pi++ ni++ default: if ni >= len(name) || pattern[pi] != name[ni] { return false } pi++ ni++ } } return ni == len(name) } if pattern == "" { return true } if pattern == "*.*" { return true } return match(0, 0) } func listDirectoryEntries(ctx *TrapContext, fh *FileHandle) []ntDirEntry { if fh == nil { return nil } if dirNode := ctx.FS.Stat(fh.FD); dirNode != nil && dirNode.IsDir && len(dirNode.Children) > 0 { names := make([]string, 0, len(dirNode.Children)) for name := range dirNode.Children { names = append(names, name) } sort.Strings(names) out := make([]ntDirEntry, 0, len(names)) for _, name := range names { child := dirNode.Children[name] out = append(out, ntDirEntry{ Name: child.Name, IsDir: child.IsDir, Size: child.Size, Atime: child.Atime, Mtime: child.Mtime, Ctime: child.Ctime, Nlink: child.Nlink, }) } return out } hostEntries, err := os.ReadDir(fh.Path) if err != nil { return nil } sort.Slice(hostEntries, func(i, j int) bool { return strings.ToLower(hostEntries[i].Name()) < strings.ToLower(hostEntries[j].Name()) }) out := make([]ntDirEntry, 0, len(hostEntries)) for _, entry := range hostEntries { info, ierr := entry.Info() if ierr != nil { continue } out = append(out, ntDirEntry{ Name: entry.Name(), IsDir: entry.IsDir(), Size: info.Size(), Atime: info.ModTime(), Mtime: info.ModTime(), Ctime: info.ModTime(), Nlink: 1, }) } return out } func closeHandleObject( ctx *TrapContext, handle Handle, obj HandleObject, objectRefs, ownerRefs int, ) { switch v := obj.(type) { case *FileHandle: CancelNativeIOForHandle(ctx.Handles, handle) if ownerRefs == 0 { FileLocksFor(ctx.FS).ReleaseOwner(v.LockOwner()) } if v.DeletePending && v.Path != "" && objectRefs == 0 { _ = ctx.FS.Unlinkat(-1, v.Path, 0) } if objectRefs == 0 { _ = ctx.FS.Close(v.FD) } case *SectionHandle: if v.FileFD >= 0 && objectRefs == 0 { _ = ctx.FS.Close(v.FileFD) } case *IoCompletionPortHandle: if objectRefs == 0 { v.Close() } } _ = handle } func cloneHandleObject(ctx *TrapContext, obj HandleObject) (HandleObject, uint32) { switch v := obj.(type) { case *FileHandle: newFD, err := ctx.FS.Dup(v.FD) if err != nil { return nil, StatusInvalidHandle } v.completionState() cp := *v cp.FD = newFD return &cp, StatusSuccess case *SectionHandle: cp := *v if v.FileFD >= 0 { newFD, err := ctx.FS.Dup(v.FileFD) if err != nil { return nil, StatusInvalidHandle } cp.FileFD = newFD } if v.ViewSizes != nil { cp.ViewSizes = make(map[uint64]uint64, len(v.ViewSizes)) for k, val := range v.ViewSizes { cp.ViewSizes[k] = val } } return &cp, StatusSuccess default: return obj, StatusSuccess } } func handleNtCreateFile(ctx *TrapContext) uint32 { // NtCreateFile(FileHandle*, DesiredAccess, ObjectAttributes*, IoStatusBlock*, // AllocationSize*, FileAttributes, ShareAccess, CreateDisposition, // CreateOptions, EaBuffer*, EaLength) handlePtr := ctx.Arg(0) desiredAccess := uint32(ctx.Arg(1)) objAttr := ctx.Arg(2) ioStatus := ctx.Arg(3) createDisp := uint32(ctx.Arg(7)) createOpts := uint32(ctx.Arg(8)) rootDir, _, attrs, err := ReadObjectAttributes(ctx, objAttr) path, err := ResolveObjectPath(ctx, objAttr) if err != nil || path == "" { writeIOStatus(ctx.Mem, ioStatus, StatusObjectPathNotFound, 0) return StatusObjectPathNotFound } if ctx.Trace { fmt.Printf(" NtCreateFile: path=%q root=0x%x attrs=0x%x access=0x%x disp=%d opts=0x%x\n", path, rootDir, attrs, desiredAccess, createDisp, createOpts) } isDir := createOpts&fileDirectoryFile != 0 baseFlags := desiredAccessToOpenFlags(desiredAccess) fileInfo := uint64(1) _, statErr := ctx.FS.StatPath(-100, path) exists := statErr == nil if createDisp == fileCreate && exists { writeIOStatus(ctx.Mem, ioStatus, StatusObjectNameCollision, 0) return StatusObjectNameCollision } if createDisp == fileOpen && !exists { writeIOStatus(ctx.Mem, ioStatus, StatusObjectNameNotFound, 0) return StatusObjectNameNotFound } flags := baseFlags switch createDisp { case fileCreate, fileOpenIf: flags |= os.O_CREATE case fileOverwrite, fileOverwriteIf, fileSupersede: flags |= os.O_CREATE | os.O_TRUNC } if desiredAccess&0x0004 != 0 { flags |= os.O_APPEND } if isDir { if createDisp == fileCreate || createDisp == fileOpenIf || createDisp == fileOverwriteIf || createDisp == fileSupersede { ctx.FS.MkdirAll(path) fileInfo = 2 } } else { switch createDisp { case fileCreate: fileInfo = 2 case fileOpenIf: if !exists { fileInfo = 2 } case fileOverwrite, fileOverwriteIf: fileInfo = 3 case fileSupersede: fileInfo = 4 } } fd, openErr := ctx.FS.OpenAt(-100, path, flags, 0644) if openErr != nil { status := ntStatusFromErr(openErr, StatusObjectNameNotFound) writeIOStatus(ctx.Mem, ioStatus, status, 0) return status } isDirectory := isDir if node := ctx.FS.Stat(fd); node != nil { isDirectory = node.IsDir } var fileFlags uint32 if createOpts&(fileSynchronousIoAlert|fileSynchronousIoNonalert) == 0 { fileFlags = 0x40000000 // FILE_FLAG_OVERLAPPED } fh := &FileHandle{ FD: fd, Path: path, Access: desiredAccess, Flags: fileFlags, IsDirectory: isDirectory, } h := ctx.Handles.Alloc(fh) writeGuestPtr(ctx.Mem, handlePtr, uint64(h)) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, fileInfo) return StatusSuccess } func handleNtOpenFile(ctx *TrapContext) uint32 { // Same as NtCreateFile but with fewer params return handleNtCreateFile(ctx) } func fileHandleCanReadNative(file *FileHandle) bool { const ( genericRead = uint32(0x80000000) genericAll = uint32(0x10000000) fileRead = uint32(0x00000001) ) return file != nil && file.Access&(genericRead|genericAll|fileRead) != 0 } func fileHandleCanWriteNative(file *FileHandle) bool { const ( genericWrite = uint32(0x40000000) genericAll = uint32(0x10000000) fileWrite = uint32(0x00000002) fileAppend = uint32(0x00000004) ) return file != nil && file.Access&(genericWrite|genericAll|fileWrite|fileAppend) != 0 } func fileHandleAppendOnlyNative(file *FileHandle) bool { const ( genericWrite = uint32(0x40000000) genericAll = uint32(0x10000000) fileWrite = uint32(0x00000002) fileAppend = uint32(0x00000004) ) if file == nil { return false } canOverwrite := file.Access&(genericWrite|genericAll|fileWrite) != 0 return !canOverwrite && file.Access&fileAppend != 0 } func nativeFileCurrentOffset(ctx *TrapContext, file *FileHandle) uint64 { offset, err := ctx.FS.Lseek(file.FD, 0, 1) if err != nil || offset < 0 { return 0 } return uint64(offset) } func nativeFileIORangeValid(offset, length uint64) bool { const maxFileOffset = uint64(1<<63 - 1) return offset <= maxFileOffset && length <= maxFileOffset-offset } func nativeFileWriteOffset( ctx *TrapContext, file *FileHandle, requested uint64, useCurrent bool, ) uint64 { const ( genericWrite = uint32(0x40000000) genericAll = uint32(0x10000000) fileWrite = uint32(0x00000002) fileAppend = uint32(0x00000004) ) canOverwrite := file.Access&(genericWrite|genericAll|fileWrite) != 0 if !canOverwrite && file.Access&fileAppend != 0 { if node := ctx.FS.Stat(file.FD); node != nil && node.Size > 0 { return uint64(node.Size) } return 0 } if useCurrent { return nativeFileCurrentOffset(ctx, file) } return requested } func withNativeFileRead( ctx *TrapContext, file *FileHandle, start, length uint64, access func(), ) bool { identity, ok := ctx.FS.FileIdentity(file.FD) if !ok { access() return true } return FileLocksFor(ctx.FS).WithReadAccess( identity, file.LockOwner(), start, length, access, ) } func withNativeFileWrite( ctx *TrapContext, file *FileHandle, start, length uint64, access func(), ) bool { identity, ok := ctx.FS.FileIdentity(file.FD) if !ok { access() return true } return FileLocksFor(ctx.FS).WithWriteAccess( identity, file.LockOwner(), start, length, access, ) } func handleNtReadFile(ctx *TrapContext) uint32 { // NtReadFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock*, Buffer, Length, ByteOffset*, Key*) // ReactOS reference: ntoskrnl/io/iomgr/iofunc.c NtReadFile fileHandle := Handle(ctx.Arg(0)) eventHandle := Handle(ctx.Arg(1)) apcRoutine := ctx.Arg(2) apcContext := ctx.Arg(3) ioStatus := ctx.Arg(4) bufAddr := ctx.Arg(5) length := ctx.Arg(6) pByteOffset := ctx.Arg(7) keyAddr := ctx.Arg(8) const maxNativeIOBuffer = uint64(256 << 20) if length > maxNativeIOBuffer { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) { return StatusAccessViolation } fh := ctx.Handles.GetFile(fileHandle) if fh == nil { if pipe := ctx.Handles.GetNamedPipe(fileHandle); pipe != nil { if length != 0 && ctx.Mem.ValidateRange(bufAddr, length, mmu.PermWrite) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } data := make([]byte, length) n := pipe.PipeRead(data) status := uint32(StatusSuccess) if n == 0 && length > 0 { status = StatusEndOfFile } if n > 0 { ctx.Mem.WriteBytes(bufAddr, data[:n]) } writeIOStatus(ctx.Mem, ioStatus, status, uint64(n)) signalHandleEvent(ctx, eventHandle) return status } } if fh == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } if !fileHandleCanReadNative(fh) { writeIOStatus(ctx.Mem, ioStatus, StatusAccessDenied, 0) return StatusAccessDenied } if length != 0 && ctx.Mem.ValidateRange(bufAddr, length, mmu.PermWrite) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } if pByteOffset != 0 && ctx.Mem.ValidateRange(pByteOffset, 8, mmu.PermRead) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } if keyAddr != 0 && ctx.Mem.ValidateRange(keyAddr, 4, mmu.PermRead) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } var event *EventHandle if eventHandle != 0 { event = ctx.Handles.GetEvent(eventHandle) if event == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } } useCurrent := true offset := uint64(0) if pByteOffset != 0 { offset, _ = ctx.Mem.Read64(pByteOffset) useCurrent = offset == nativeUseFilePointer } isAsync := fh.Flags&nativeFileFlagOverlapped != 0 if useCurrent && isAsync { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } lockOffset := offset if useCurrent { lockOffset = nativeFileCurrentOffset(ctx, fh) } if !nativeFileIORangeValid(lockOffset, length) { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } perform := func() (uint32, uint64) { buf := make([]byte, length) var n int var err error if !withNativeFileRead(ctx, fh, lockOffset, length, func() { if useCurrent { n, err = ctx.FS.Read(fh.FD, buf) } else { n, err = ctx.FS.Pread(fh.FD, buf, int64(offset)) if !isAsync { _, seekErr := ctx.FS.Lseek( fh.FD, int64(offset)+int64(n), 0, ) if err == nil { err = seekErr } } } }) { return StatusFileLockConflict, 0 } status := uint32(StatusSuccess) if n == 0 && err != nil { status = StatusEndOfFile } if n > 0 { ctx.Mem.WriteBytes(bufAddr, buf[:n]) } return status, uint64(n) } if isAsync { operation := beginNativeIO( ctx, fileHandle, fh, ioStatus, event, apcRoutine, apcContext, ) go func() { select { case <-operation.cancel: operation.complete(StatusCancelled, 0) return default: } status, information := perform() select { case <-operation.cancel: operation.complete(StatusCancelled, 0) default: operation.complete(status, information) } }() return StatusPending } status, information := perform() completeNativeIOInline( ctx, fileHandle, fh, ioStatus, event, apcRoutine, apcContext, status, information, ) return status } func handleNtWriteFile(ctx *TrapContext) uint32 { // NtWriteFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock*, Buffer, Length, ByteOffset*, Key*) // ReactOS reference: ntoskrnl/io/iomgr/iofunc.c NtWriteFile fileHandle := Handle(ctx.Arg(0)) eventHandle := Handle(ctx.Arg(1)) apcRoutine := ctx.Arg(2) apcContext := ctx.Arg(3) ioStatus := ctx.Arg(4) bufAddr := ctx.Arg(5) length := ctx.Arg(6) pByteOffset := ctx.Arg(7) keyAddr := ctx.Arg(8) const maxNativeIOBuffer = uint64(256 << 20) if length > maxNativeIOBuffer { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) { return StatusAccessViolation } // Handle standard output/error pseudo-handles if uint64(fileHandle) == 0xFFFFFFF5 || uint64(fileHandle) == 0xFFFFFFF4 { data, err := ctx.Mem.ReadBytes(bufAddr, int(length)) if err != nil { return StatusInvalidParameter } fd := 1 if uint64(fileHandle) == 0xFFFFFFF4 { fd = 2 } ctx.FS.Write(fd, data) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, length) return StatusSuccess } fh := ctx.Handles.GetFile(fileHandle) if fh == nil { if pipe := ctx.Handles.GetNamedPipe(fileHandle); pipe != nil { data, err := ctx.Mem.ReadBytes(bufAddr, int(length)) if err != nil { return StatusInvalidParameter } if pByteOffset != 0 { offset, _ := ctx.Mem.Read64(pByteOffset) _ = offset } if !pipe.PipeWrite(data) { return StatusAccessDenied } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, length) signalHandleEvent(ctx, eventHandle) return StatusSuccess } } if fh == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } if !fileHandleCanWriteNative(fh) { writeIOStatus(ctx.Mem, ioStatus, StatusAccessDenied, 0) return StatusAccessDenied } if length != 0 && ctx.Mem.ValidateRange(bufAddr, length, mmu.PermRead) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } if pByteOffset != 0 && ctx.Mem.ValidateRange(pByteOffset, 8, mmu.PermRead) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } if keyAddr != 0 && ctx.Mem.ValidateRange(keyAddr, 4, mmu.PermRead) != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } var event *EventHandle if eventHandle != 0 { event = ctx.Handles.GetEvent(eventHandle) if event == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } } data, err := ctx.Mem.ReadBytes(bufAddr, int(length)) if err != nil { writeIOStatus(ctx.Mem, ioStatus, StatusAccessViolation, 0) return StatusAccessViolation } useCurrent := true offset := uint64(0) writeToEnd := false if pByteOffset != 0 { offset, _ = ctx.Mem.Read64(pByteOffset) useCurrent = offset == nativeUseFilePointer writeToEnd = offset == nativeWriteToEndOfFile } isAsync := fh.Flags&nativeFileFlagOverlapped != 0 if useCurrent && isAsync && !fileHandleAppendOnlyNative(fh) { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } if writeToEnd { offset = 0 if node := ctx.FS.Stat(fh.FD); node != nil && node.Size > 0 { offset = uint64(node.Size) } useCurrent = false } lockOffset := nativeFileWriteOffset(ctx, fh, offset, useCurrent) if !nativeFileIORangeValid(lockOffset, length) { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } perform := func() (uint32, uint64) { var n int var writeErr error if !withNativeFileWrite(ctx, fh, lockOffset, length, func() { if useCurrent { n, writeErr = ctx.FS.Write(fh.FD, data) } else { n, writeErr = ctx.FS.Pwrite(fh.FD, data, int64(offset)) if !isAsync { _, seekErr := ctx.FS.Lseek( fh.FD, int64(lockOffset)+int64(n), 0, ) if writeErr == nil { writeErr = seekErr } } } }) { return StatusFileLockConflict, 0 } if writeErr != nil { return StatusAccessDenied, 0 } return StatusSuccess, uint64(n) } if isAsync { operation := beginNativeIO( ctx, fileHandle, fh, ioStatus, event, apcRoutine, apcContext, ) go func() { select { case <-operation.cancel: operation.complete(StatusCancelled, 0) return default: } status, information := perform() select { case <-operation.cancel: operation.complete(StatusCancelled, 0) default: operation.complete(status, information) } }() return StatusPending } status, information := perform() completeNativeIOInline( ctx, fileHandle, fh, ioStatus, event, apcRoutine, apcContext, status, information, ) return status } func handleNtClose(ctx *TrapContext) uint32 { handle := Handle(ctx.Arg(0)) obj, objectRefs, ownerRefs, ok := ctx.Handles.Take(handle) if !ok { return StatusInvalidHandle } closeHandleObject(ctx, handle, obj, objectRefs, ownerRefs) return StatusSuccess } func handleNtQueryInformationFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) ioStatus := ctx.Arg(1) buffer := ctx.Arg(2) length := ctx.Arg(3) infoClass := uint32(ctx.Arg(4)) fh := ctx.Handles.GetFile(fileHandle) if fh == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } node := ctx.FS.Stat(fh.FD) if node == nil { writeIOStatus(ctx.Mem, ioStatus, StatusNoSuchFile, 0) return StatusNoSuchFile } attrs := uint32(0x80) if node.IsDir { attrs = 0x10 } createFT := windowsFileTime(node.Ctime) accessFT := windowsFileTime(node.Atime) writeFT := windowsFileTime(node.Mtime) changeFT := windowsFileTime(node.Ctime) eof := uint64(node.Size) alloc := (eof + mmu.PageSize - 1) & mmu.PageMask if eof == 0 { alloc = 0 } winPath := `\??\` + VFSToNtPath(fh.Path) nameBytes := encodeUTF16LE(winPath) position := getFileOffset(ctx, fh) switch infoClass { case fileBasicInformation: if length < 40 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write64(buffer+0, createFT) ctx.Mem.Write64(buffer+8, accessFT) ctx.Mem.Write64(buffer+16, writeFT) ctx.Mem.Write64(buffer+24, changeFT) ctx.Mem.Write32(buffer+32, attrs) ctx.Mem.Write32(buffer+36, 0) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 40) return StatusSuccess case fileStandardInformation: if length < 24 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write64(buffer+0, alloc) ctx.Mem.Write64(buffer+8, eof) ctx.Mem.Write32(buffer+16, node.Nlink) ctx.Mem.Write8(buffer+20, boolToByte(fh.DeletePending)) ctx.Mem.Write8(buffer+21, boolToByte(node.IsDir)) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 24) return StatusSuccess case fileNameInformation: needed := uint64(4 + len(nameBytes)) if length < 4 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write32(buffer+0, uint32(len(nameBytes))) toCopy := minUint32(uint32(len(nameBytes)), uint32(length-4)) if toCopy > 0 { ctx.Mem.WriteBytes(buffer+4, nameBytes[:toCopy]) } if length < needed { writeIOStatus(ctx.Mem, ioStatus, StatusBufferOverflow, length) return StatusBufferOverflow } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, needed) return StatusSuccess case filePositionInformation: if length < 8 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write64(buffer, position) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 8) return StatusSuccess case fileEndOfFileInformation: if length < 8 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write64(buffer, eof) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 8) return StatusSuccess case fileAllInformation: needed := uint64(96 + 4 + len(nameBytes)) if length < 96 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write64(buffer+0, createFT) ctx.Mem.Write64(buffer+8, accessFT) ctx.Mem.Write64(buffer+16, writeFT) ctx.Mem.Write64(buffer+24, changeFT) ctx.Mem.Write32(buffer+32, attrs) ctx.Mem.Write32(buffer+36, 0) ctx.Mem.Write64(buffer+40, alloc) ctx.Mem.Write64(buffer+48, eof) ctx.Mem.Write32(buffer+56, node.Nlink) ctx.Mem.Write8(buffer+60, boolToByte(fh.DeletePending)) ctx.Mem.Write8(buffer+61, boolToByte(node.IsDir)) ctx.Mem.Write32(buffer+76, fh.Access) ctx.Mem.Write64(buffer+80, position) ctx.Mem.Write32(buffer+88, 0) ctx.Mem.Write32(buffer+92, 0) ctx.Mem.Write32(buffer+96, uint32(len(nameBytes))) avail := uint32(0) if length > 100 { avail = uint32(length - 100) } toCopy := minUint32(uint32(len(nameBytes)), avail) if toCopy > 0 { ctx.Mem.WriteBytes(buffer+100, nameBytes[:toCopy]) } if length < needed { writeIOStatus(ctx.Mem, ioStatus, StatusBufferOverflow, length) return StatusBufferOverflow } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, needed) return StatusSuccess default: writeIOStatus(ctx.Mem, ioStatus, StatusNotImplemented, 0) return StatusNotImplemented } } func handleNtSetInformationFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) ioStatus := ctx.Arg(1) buffer := ctx.Arg(2) length := uint32(ctx.Arg(3)) class := uint32(ctx.Arg(4)) fh := ctx.Handles.GetFile(fileHandle) if fh == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } switch class { case fileDispositionInformation: deleteFile := false if buffer != 0 { flag, _ := ctx.Mem.Read8(buffer) deleteFile = flag != 0 } fh.DeletePending = deleteFile writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess case filePositionInformation: pos, _ := ctx.Mem.Read64(buffer) if _, err := ctx.FS.Lseek(fh.FD, int64(pos), 0); err != nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess case fileEndOfFileInformation, 19: eof, _ := ctx.Mem.Read64(buffer) if err := ctx.FS.Ftruncate(fh.FD, int64(eof)); err != nil { status := ntStatusFromErr(err, StatusInvalidParameter) writeIOStatus(ctx.Mem, ioStatus, status, 0) return status } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess case 10: var replaceIfExists bool var rootDir Handle var nameLen uint32 var nameAddr uint64 if ctx.Mem.Mode32 { flag, _ := ctx.Mem.Read8(buffer) replaceIfExists = flag != 0 rootVal, _ := ctx.Mem.Read32(buffer + 4) rootDir = Handle(rootVal) nameLen, _ = ctx.Mem.Read32(buffer + 8) nameAddr = buffer + 12 } else { flag, _ := ctx.Mem.Read8(buffer) replaceIfExists = flag != 0 rootVal, _ := ctx.Mem.Read64(buffer + 8) rootDir = Handle(rootVal) nameLen, _ = ctx.Mem.Read32(buffer + 16) nameAddr = buffer + 20 } if nameLen == 0 || length < nameLen { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } nameBytes, err := ctx.Mem.ReadBytes(nameAddr, int(nameLen)) if err != nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidParameter, 0) return StatusInvalidParameter } newName := decodeUTF16LE(nameBytes) newPath := NtPathToVFS(newName) if !(strings.HasPrefix(newName, `\`) || (len(newName) >= 2 && newName[1] == ':')) { basePath := fh.Path if rootDir != 0 { if base := ctx.Handles.GetFile(rootDir); base != nil { basePath = base.Path } } else if slash := strings.LastIndex(basePath, "/"); slash >= 0 { basePath = basePath[:slash] if basePath == "" { basePath = "/" } } if !strings.HasPrefix(newPath, "/") { newPath = "/" + newPath } if basePath == "/" { newPath = basePath + strings.TrimPrefix(newPath, "/") } else { newPath = basePath + newPath } } if replaceIfExists { _ = ctx.FS.Unlinkat(-1, newPath, 0) } if err := ctx.FS.Renameat(-1, fh.Path, -1, newPath); err != nil { status := ntStatusFromErr(err, StatusObjectNameNotFound) writeIOStatus(ctx.Mem, ioStatus, status, 0) return status } fh.Path = newPath writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess default: writeIOStatus(ctx.Mem, ioStatus, StatusNotImplemented, 0) return StatusNotImplemented } } func decodeUTF16(chars []uint16) []rune { var out []rune for i := 0; i < len(chars); i++ { if chars[i] >= 0xD800 && chars[i] <= 0xDBFF && i+1 < len(chars) { hi := rune(chars[i]) - 0xD800 lo := rune(chars[i+1]) - 0xDC00 out = append(out, 0x10000+hi<<10+lo) i++ } else { out = append(out, rune(chars[i])) } } return out } func handleNtQueryDirectoryFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) eventHandle := Handle(ctx.Arg(1)) ioStatus := ctx.Arg(4) buffer := ctx.Arg(5) length := ctx.Arg(6) infoClass := uint32(ctx.Arg(7)) returnSingleEntry := ctx.Arg(8) != 0 fileNamePtr := ctx.Arg(9) restartScan := ctx.Arg(10) != 0 fh := ctx.Handles.GetFile(fileHandle) if fh == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } if restartScan { fh.DirIndex = 0 } if fileNamePtr != 0 { mask, err := ReadUnicodeString(ctx.Mem, fileNamePtr) if err == nil { fh.DirMask = mask } } allEntries := listDirectoryEntries(ctx, fh) if len(allEntries) == 0 { writeIOStatus(ctx.Mem, ioStatus, StatusNoMoreFiles, 0) return StatusNoMoreFiles } matches := make([]ntDirEntry, 0, len(allEntries)) for _, entry := range allEntries { if fh.DirMask == "" || wildcardMatch(fh.DirMask, entry.Name) { matches = append(matches, entry) } } if fh.DirIndex >= len(matches) { writeIOStatus(ctx.Mem, ioStatus, StatusNoMoreFiles, 0) return StatusNoMoreFiles } offset := uint64(0) prevOffset := uint64(0) prevSize := uint64(0) written := 0 for idx := fh.DirIndex; idx < len(matches); idx++ { entry := matches[idx] nameUTF16 := encodeUTF16LE(entry.Name) baseSize := uint64(64) nameOffset := uint64(64) if infoClass == fileBothDirectoryInformation { baseSize = 94 nameOffset = 94 } else if infoClass != fileDirectoryInformation { writeIOStatus(ctx.Mem, ioStatus, StatusNotImplemented, 0) return StatusNotImplemented } entrySize := (baseSize + uint64(len(nameUTF16)) + 7) &^ uint64(7) if offset+entrySize > length { break } addr := buffer + offset if written > 0 { ctx.Mem.Write32(buffer+prevOffset, uint32(prevSize)) } createFT := windowsFileTime(entry.Ctime) accessFT := windowsFileTime(entry.Atime) writeFT := windowsFileTime(entry.Mtime) changeFT := windowsFileTime(entry.Ctime) attrs := uint32(0x80) if entry.IsDir { attrs = 0x10 } ctx.Mem.Write32(addr+0, 0) ctx.Mem.Write32(addr+4, uint32(idx)) ctx.Mem.Write64(addr+8, createFT) ctx.Mem.Write64(addr+16, accessFT) ctx.Mem.Write64(addr+24, writeFT) ctx.Mem.Write64(addr+32, changeFT) ctx.Mem.Write64(addr+40, uint64(entry.Size)) ctx.Mem.Write64(addr+48, uint64(entry.Size)) ctx.Mem.Write32(addr+56, attrs) ctx.Mem.Write32(addr+60, uint32(len(nameUTF16))) if infoClass == fileBothDirectoryInformation { ctx.Mem.Write32(addr+64, 0) ctx.Mem.Write8(addr+68, 0) ctx.Mem.Write8(addr+69, 0) } ctx.Mem.WriteBytes(addr+nameOffset, nameUTF16) prevOffset = offset prevSize = entrySize offset += entrySize written++ fh.DirIndex = idx + 1 if returnSingleEntry { break } } if written == 0 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } ctx.Mem.Write32(buffer+prevOffset, 0) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, offset) signalHandleEvent(ctx, eventHandle) return StatusSuccess } func handleNtQueryVolumeInformationFile(ctx *TrapContext) uint32 { _ = Handle(ctx.Arg(0)) // FileHandle ioStatus := ctx.Arg(1) buffer := ctx.Arg(2) length := uint32(ctx.Arg(3)) class := uint32(ctx.Arg(4)) switch class { case 1: // FileFsVolumeInformation label := "Raven" labelUTF16 := encodeUTF16LE(label) needed := uint32(18 + len(labelUTF16)) if length < needed { writeIOStatus(ctx.Mem, ioStatus, StatusBufferOverflow, 0) return StatusBufferOverflow } if buffer != 0 { ctx.Mem.Write64(buffer, 0) // VolumeCreationTime ctx.Mem.Write32(buffer+8, 0x12345678) // VolumeSerialNumber ctx.Mem.Write32(buffer+12, uint32(len(labelUTF16))) // VolumeLabelLength ctx.Mem.Write8(buffer+16, 0) // SupportsObjects (BOOLEAN) ctx.Mem.Write8(buffer+17, 0) // padding ctx.Mem.WriteBytes(buffer+18, labelUTF16) // VolumeLabel } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, uint64(needed)) case 3: // FileFsSizeInformation if length < 24 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write64(buffer, 1000000) // TotalAllocationUnits ctx.Mem.Write64(buffer+8, 500000) // AvailableAllocationUnits ctx.Mem.Write32(buffer+16, 8) // SectorsPerAllocationUnit ctx.Mem.Write32(buffer+20, 512) // BytesPerSector } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 24) case 4: // FileFsDeviceInformation if length < 8 { writeIOStatus(ctx.Mem, ioStatus, StatusBufferTooSmall, 0) return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write32(buffer, 7) // DeviceType (FILE_DEVICE_DISK) ctx.Mem.Write32(buffer+4, 0) // Characteristics } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 8) case 5: // FileFsAttributeInformation fsName := "NTFS" fsNameUTF16 := encodeUTF16LE(fsName) needed := uint32(12 + len(fsNameUTF16)) if length < needed { writeIOStatus(ctx.Mem, ioStatus, StatusBufferOverflow, 0) return StatusBufferOverflow } if buffer != 0 { ctx.Mem.Write32(buffer, 0x00000003) // FileSystemAttributes (CASE_PRESERVED|CASE_SENSITIVE) ctx.Mem.Write32(buffer+4, 255) // MaximumComponentNameLength ctx.Mem.Write32(buffer+8, uint32(len(fsNameUTF16))) // FileSystemNameLength ctx.Mem.WriteBytes(buffer+12, fsNameUTF16) // FileSystemName } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, uint64(needed)) default: return StatusNotImplemented } return StatusSuccess } func handleNtDeleteFile(ctx *TrapContext) uint32 { // NtDeleteFile(POBJECT_ATTRIBUTES ObjectAttributes) objAttr := ctx.Arg(0) path, err := ResolveObjectPath(ctx, objAttr) if err != nil || path == "" { return StatusObjectPathNotFound } vfsPath := path if strings.TrimSpace(vfsPath) == "" { return StatusObjectPathNotFound } if err := ctx.FS.Unlinkat(-1, vfsPath, 0); err != nil { msg := strings.ToLower(err.Error()) switch { case strings.Contains(msg, "not found"), strings.Contains(msg, "no such"): return StatusObjectNameNotFound case strings.Contains(msg, "is a directory"), strings.Contains(msg, "not a directory"): return StatusAccessDenied case strings.Contains(msg, "directory not empty"): return StatusAccessDenied default: return StatusAccessDenied } } return StatusSuccess } func handleNtFlushBuffersFile(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtDeviceIoControlFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) _ = ctx.Arg(1) // Event _ = ctx.Arg(2) // ApcRoutine _ = ctx.Arg(3) // ApcContext ioStatus := ctx.Arg(4) ioctlCode := uint32(ctx.Arg(5)) if ctx.Trace { fmt.Printf(" NtDeviceIoControlFile: handle=0x%x ioctl=0x%08x\n", fileHandle, ioctlCode) } // Decode device type from IOCTL code deviceType := (ioctlCode >> 16) & 0xFFFF // Console device IOCTLs (FILE_DEVICE_CONSOLE = 0x50) - succeed silently // even for invalid handles, as Wine uses these for debug output via condrv if deviceType == 0x50 { if ctx.Trace { inputBuf := ctx.Arg(6) inputLen := uint32(ctx.Arg(7)) // ULONG - 32-bit if inputBuf != 0 && inputLen > 0 && inputLen < 4096 { data, err := ctx.Mem.ReadBytes(inputBuf, int(inputLen)) if err == nil { fmt.Printf(" [wine] %s", string(data)) } } } if ioStatus != 0 { ctx.Mem.Write32(ioStatus, StatusSuccess) ctx.Mem.Write64(ioStatus+8, 0) } return StatusSuccess } // Reject truly invalid handles for non-console IOCTLs if fileHandle == 0xFFFFFFFFFFFFFFFF || fileHandle == 0 { if ioStatus != 0 { ctx.Mem.Write32(ioStatus, StatusInvalidHandle) ctx.Mem.Write64(ioStatus+8, 0) } return StatusInvalidHandle } // Console mode IOCTLs (legacy path) const ( ctlCodeGetMode = 0x500000 ctlCodeSetMode = 0x500004 ) switch ioctlCode { case ctlCodeGetMode: outBuf := ctx.Arg(8) if outBuf != 0 { ctx.Mem.Write32(outBuf, 0x0007) } case ctlCodeSetMode: // Accept silently default: writeIOStatus(ctx.Mem, ioStatus, 0xC0000010, 0) // STATUS_INVALID_DEVICE_REQUEST return 0xC0000010 } if ioStatus != 0 { ctx.Mem.Write32(ioStatus, StatusSuccess) ctx.Mem.Write64(ioStatus+8, 0) } return StatusSuccess } func handleNtCreateSection(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess _ = ctx.Arg(2) // ObjectAttributes maxSizePtr := ctx.Arg(3) sectionProtect := uint32(ctx.Arg(4)) allocationAttrs := uint32(ctx.Arg(5)) fileHandle := Handle(ctx.Arg(6)) var maxSize uint64 if maxSizePtr != 0 { maxSize, _ = ctx.Mem.Read64(maxSizePtr) } perms := NtMemProtectToMMU(sectionProtect) if perms == 0 { perms = mmu.PermRead | mmu.PermWrite } sec := &SectionHandle{ Perms: perms, FileFD: -1, ViewSizes: make(map[uint64]uint64), } if fh := ctx.Handles.GetFile(fileHandle); fh != nil { if maxSize == 0 { if node := ctx.FS.Stat(fh.FD); node != nil { maxSize = uint64(node.Size) } } if dupFD, err := ctx.FS.Dup(fh.FD); err == nil { sec.FileFD = dupFD } sec.Path = fh.Path } if maxSize == 0 && allocationAttrs&secCommit != 0 { maxSize = mmu.PageSize } if maxSize == 0 { maxSize = mmu.PageSize } sec.Size = (maxSize + mmu.PageSize - 1) & mmu.PageMask h := ctx.Handles.Alloc(sec) writeGuestPtr(ctx.Mem, handlePtr, uint64(h)) return StatusSuccess } func handleNtMapViewOfSection(ctx *TrapContext) uint32 { sectionHandle := Handle(ctx.Arg(0)) _ = ctx.Arg(1) // ProcessHandle baseAddrPtr := ctx.Arg(2) _ = ctx.Arg(3) // ZeroBits _ = ctx.Arg(4) // CommitSize sectionOffsetPtr := ctx.Arg(5) viewSizePtr := ctx.Arg(6) _ = ctx.Arg(7) // InheritDisposition _ = ctx.Arg(8) // AllocationType win32Protect := uint32(ctx.Arg(9)) sec := ctx.Handles.GetSection(sectionHandle) if sec == nil { return StatusInvalidHandle } baseAddr := uint64(0) if baseAddrPtr != 0 { baseAddr, _ = ctx.Mem.Read64(baseAddrPtr) } sectionOffset := uint64(0) if sectionOffsetPtr != 0 { sectionOffset, _ = ctx.Mem.Read64(sectionOffsetPtr) } size := sec.Size if viewSizePtr != 0 { if requested, _ := ctx.Mem.Read64(viewSizePtr); requested != 0 { size = requested } } if size == 0 { size = mmu.PageSize } size = (size + mmu.PageSize - 1) & mmu.PageMask perms := sec.Perms if win32Protect != 0 { perms = NtMemProtectToMMU(win32Protect) } addr := ctx.Mem.Mmap(baseAddr, size, perms) if sec.FileFD >= 0 { buf := make([]byte, size) if n, err := ctx.FS.Pread(sec.FileFD, buf, int64(sectionOffset)); err == nil && n > 0 { ctx.Mem.WriteBytes(addr, buf[:n]) } } sec.ViewSizes[addr] = size if baseAddrPtr != 0 { ctx.Mem.Write64(baseAddrPtr, addr) } if viewSizePtr != 0 { ctx.Mem.Write64(viewSizePtr, size) } return StatusSuccess } func handleNtUnmapViewOfSection(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // ProcessHandle baseAddr := ctx.Arg(1) if baseAddr != 0 { _, size, mapped, _ := ctx.Mem.QueryRegion(baseAddr) if mapped && size > 0 { ctx.Mem.Munmap(baseAddr, size) } } return StatusSuccess } // ======================== REGISTRY ======================== func handleNtOpenKey(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // desiredAccess objAttr := ctx.Arg(2) _, name, _, err := ReadObjectAttributes(ctx, objAttr) if err != nil { return StatusObjectNameInvalid } key := ctx.Reg.OpenKey(name) if key == nil { if ctx.Trace { fmt.Printf(" NtOpenKey: NOT FOUND: %q\n", name) } return StatusKeyNotFound } h := ctx.Handles.Alloc(&RegistryKeyHandle{Key: key, Path: name}) ctx.Mem.Write64(handlePtr, uint64(h)) if ctx.Trace { fmt.Printf(" NtOpenKey: opened %q → handle=0x%x\n", name, h) } return StatusSuccess } func handleNtOpenKeyEx(ctx *TrapContext) uint32 { return handleNtOpenKey(ctx) } func handleNtCreateKey(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // desiredAccess objAttr := ctx.Arg(2) _, name, _, err := ReadObjectAttributes(ctx, objAttr) if err != nil { return StatusObjectNameInvalid } key := ctx.Reg.CreateKey(name) h := ctx.Handles.Alloc(&RegistryKeyHandle{Key: key, Path: name}) ctx.Mem.Write64(handlePtr, uint64(h)) return StatusSuccess } func handleNtQueryValueKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) valueNameAddr := ctx.Arg(1) // PUNICODE_STRING _ = ctx.Arg(2) // KeyValueInformationClass buffer := ctx.Arg(3) // stack arg 0 rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } valueName, err := ReadUnicodeString(ctx.Mem, valueNameAddr) if err != nil { return StatusObjectNameInvalid } val, ok := rk.Key.Values[valueName] if !ok { // Case-insensitive search for k, v := range rk.Key.Values { if strings.EqualFold(k, valueName) { val = v ok = true break } } } if !ok { return StatusObjectNameNotFound } if buffer != 0 { // KEY_VALUE_PARTIAL_INFORMATION: TitleIndex(4) + Type(4) + DataLength(4) + Data ctx.Mem.Write32(buffer+0, 0) // TitleIndex ctx.Mem.Write32(buffer+4, val.Type) // Type ctx.Mem.Write32(buffer+8, uint32(len(val.Data))) // DataLength ctx.Mem.WriteBytes(buffer+12, val.Data) // Data } return StatusSuccess } func handleNtSetValueKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) valueNameAddr := ctx.Arg(1) _ = ctx.Arg(2) // TitleIndex valType := uint32(ctx.Arg(3)) dataAddr := ctx.Arg(4) dataSize := uint32(ctx.Arg(5)) rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } valueName, err := ReadUnicodeString(ctx.Mem, valueNameAddr) if err != nil { return StatusObjectNameInvalid } // Stack args: DataPtr and DataSize var data []byte if dataAddr != 0 && dataSize > 0 { if raw, err := ctx.Mem.ReadBytes(dataAddr, int(dataSize)); err == nil { data = append([]byte(nil), raw...) } } rk.Key.Values[valueName] = &RegistryValue{ Name: valueName, Type: valType, Data: data, } return StatusSuccess } func handleNtDeleteKey(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtDeleteValueKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) valueNameAddr := ctx.Arg(1) rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } valueName, err := ReadUnicodeString(ctx.Mem, valueNameAddr) if err != nil { return StatusObjectNameInvalid } delete(rk.Key.Values, valueName) for k := range rk.Key.Values { if strings.EqualFold(k, valueName) { delete(rk.Key.Values, k) break } } return StatusSuccess } func handleNtEnumerateKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) index := uint32(ctx.Arg(1)) _ = ctx.Arg(2) // KeyInformationClass buffer := ctx.Arg(3) rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } names := ctx.Reg.EnumerateKeys(rk.Key) if int(index) >= len(names) { return StatusNoMoreEntries } name := names[index] child := rk.Key.Children[strings.ToUpper(name)] if child != nil { name = child.Name // use original case } // KEY_BASIC_INFORMATION if buffer != 0 { nameUTF16 := encodeUTF16LE(name) ctx.Mem.Write64(buffer+0, 0) // LastWriteTime ctx.Mem.Write32(buffer+8, 0) // TitleIndex ctx.Mem.Write32(buffer+12, uint32(len(nameUTF16))) // NameLength ctx.Mem.WriteBytes(buffer+16, nameUTF16) // Name } return StatusSuccess } func handleNtEnumerateValueKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) index := uint32(ctx.Arg(1)) _ = ctx.Arg(2) // KeyValueInformationClass buffer := ctx.Arg(3) rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } names := ctx.Reg.EnumerateValues(rk.Key) if int(index) >= len(names) { return StatusNoMoreEntries } val := rk.Key.Values[names[index]] if val == nil { return StatusNoMoreEntries } // KEY_VALUE_BASIC_INFORMATION if buffer != 0 { nameUTF16 := encodeUTF16LE(val.Name) ctx.Mem.Write32(buffer+0, 0) // TitleIndex ctx.Mem.Write32(buffer+4, val.Type) // Type ctx.Mem.Write32(buffer+8, uint32(len(nameUTF16))) // NameLength ctx.Mem.WriteBytes(buffer+12, nameUTF16) } return StatusSuccess } func handleNtQueryKey(ctx *TrapContext) uint32 { keyHandle := Handle(ctx.Arg(0)) infoClass := uint32(ctx.Arg(1)) buffer := ctx.Arg(2) length := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) rk := ctx.Handles.GetRegKey(keyHandle) if rk == nil { return StatusInvalidHandle } keyName := rk.Path if keyName == "" && rk.Key != nil { keyName = rk.Key.Name } nameUTF16 := encodeUTF16LE(keyName) switch infoClass { case 0: // KeyBasicInformation needed := uint32(16 + len(nameUTF16)) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if length < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write64(buffer+0, 0) ctx.Mem.Write32(buffer+8, 0) ctx.Mem.Write32(buffer+12, uint32(len(nameUTF16))) ctx.Mem.WriteBytes(buffer+16, nameUTF16) } return StatusSuccess case 1: // KeyNodeInformation valueCount := uint32(len(rk.Key.Values)) subKeyCount := uint32(len(rk.Key.Children)) needed := uint32(28 + len(nameUTF16)) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if length < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write64(buffer+0, 0) ctx.Mem.Write32(buffer+8, 0) ctx.Mem.Write32(buffer+12, uint32(len(nameUTF16))) ctx.Mem.Write32(buffer+16, subKeyCount) ctx.Mem.Write32(buffer+20, valueCount) ctx.Mem.Write32(buffer+24, 0) ctx.Mem.WriteBytes(buffer+28, nameUTF16) } return StatusSuccess default: return StatusNotImplemented } } // ======================== PROCESS / THREAD ======================== func handleNtQueryInformationProcess(ctx *TrapContext) uint32 { // NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength) infoClass := uint32(ctx.Arg(1)) buffer := ctx.Arg(2) bufLen := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) writeRetLen := func(n uint32) { if retLen != 0 { ctx.Mem.Write32(retLen, n) } } switch infoClass { case 0: // ProcessBasicInformation - PROCESS_BASIC_INFORMATION (48 bytes on x64) const needed = 48 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write64(buffer+0, 0) // ExitStatus (NTSTATUS) ctx.Mem.Write32(buffer+4, 0) // padding ctx.Mem.Write64(buffer+8, ctx.State.PEBAddr) // PebBaseAddress ctx.Mem.Write64(buffer+16, 0xF) // AffinityMask ctx.Mem.Write32(buffer+24, 8) // BasePriority (NORMAL) ctx.Mem.Write32(buffer+28, 0) // padding ctx.Mem.Write64(buffer+32, 4) // UniqueProcessId ctx.Mem.Write64(buffer+40, 0) // InheritedFromUniqueProcessId } case 1: // ProcessQuotaLimits - QUOTA_LIMITS (48 bytes) const needed = 48 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { for i := uint64(0); i < needed; i++ { ctx.Mem.Write8(buffer+i, 0) } ctx.Mem.Write64(buffer+0, 0x80000) // MinimumWorkingSetSize ctx.Mem.Write64(buffer+8, 0x100000) // MaximumWorkingSetSize ctx.Mem.Write64(buffer+16, ^uint64(0)) // PagedPoolLimit = unlimited ctx.Mem.Write64(buffer+24, ^uint64(0)) // NonPagedPoolLimit ctx.Mem.Write64(buffer+32, ^uint64(0)) // MinimumWorkingSetSize ctx.Mem.Write64(buffer+40, ^uint64(0)) // PagefileLimit } case 2: // ProcessIoCounters - IO_COUNTERS (48 bytes) const needed = 48 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { for i := uint64(0); i < needed; i++ { ctx.Mem.Write8(buffer+i, 0) } } case 3: // ProcessVmCounters - VM_COUNTERS_EX (80 bytes) const needed = 80 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { for i := uint64(0); i < needed; i++ { ctx.Mem.Write8(buffer+i, 0) } ctx.Mem.Write64(buffer+0, 0x10000) // PeakVirtualSize ctx.Mem.Write64(buffer+8, 0x10000) // VirtualSize ctx.Mem.Write64(buffer+24, 0x1000) // PeakWorkingSetSize ctx.Mem.Write64(buffer+32, 0x1000) // WorkingSetSize } case 4: // ProcessTimes - KERNEL_USER_TIMES (32 bytes) const needed = 32 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { now := uint64(time.Now().UnixNano()/100 + 116444736000000000) // FILETIME ctx.Mem.Write64(buffer+0, now) // CreateTime ctx.Mem.Write64(buffer+8, 0) // ExitTime ctx.Mem.Write64(buffer+16, 0) // KernelTime (100ns units) ctx.Mem.Write64(buffer+24, 0) // UserTime } case 5: // ProcessBasePriority writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 8) // NORMAL_PRIORITY_CLASS } case 7: // ProcessDebugPort - NULL = no debugger writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) } case 9: // ProcessWow64Information - NULL = 64-bit process writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) // not WOW64 } case 11: // ProcessHandleCount writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, uint32(ctx.Handles.Count())) } case 12: // ProcessAffinityMask writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0xF) // 4 CPUs } case 17: // ProcessSessionInformation - PROCESS_SESSION_INFORMATION (4 bytes) writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) // Session 0 } case 18: // ProcessWx86Information (return 0 = no x86 emulation) writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) } case 20: // ProcessDeviceMap writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) } case 22: // ProcessPriorityClass - PROCESS_PRIORITY_CLASS (2 bytes) writeRetLen(2) if buffer != 0 { ctx.Mem.Write8(buffer+0, 0) // Foreground = false ctx.Mem.Write8(buffer+1, 2) // PriorityClass = NORMAL } case 27: // ProcessImageFileName - UNICODE_STRING + data imgName := ctx.State.ImagePath if imgName == "" { imgName = "program.exe" } imgPath := `\Device\HarddiskVolume1\` + imgName utf16 := encodeUTF16LE(imgPath) needed := uint32(16 + len(utf16) + 2) writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write16(buffer+0, uint16(len(utf16))) // Length ctx.Mem.Write16(buffer+2, uint16(len(utf16)+2)) // MaximumLength ctx.Mem.Write64(buffer+8, buffer+16) // Buffer pointer ctx.Mem.WriteBytes(buffer+16, utf16) ctx.Mem.Write16(buffer+16+uint64(len(utf16)), 0) // null terminator } case 29: // ProcessBreakOnTermination writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) // not a critical process } case 30: // ProcessDebugObjectHandle - STATUS_PORT_NOT_SET when no debugger writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) } return 0xC0000353 // STATUS_PORT_NOT_SET case 31: // ProcessDebugFlags writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 1) // PROCESS_NO_DEBUG_INHERIT } case 33: // ProcessDefaultHardErrorMode writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) } case 34: // ProcessHandleTracing return StatusNotImplemented case 36: // ProcessImageFileName (Win32) - same as 27 imgName := ctx.State.ImagePath if imgName == "" { imgName = "program.exe" } imgPath := `\Device\HarddiskVolume1\` + imgName utf16 := encodeUTF16LE(imgPath) needed := uint32(16 + len(utf16) + 2) writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write16(buffer+0, uint16(len(utf16))) ctx.Mem.Write16(buffer+2, uint16(len(utf16)+2)) ctx.Mem.Write64(buffer+8, buffer+16) ctx.Mem.WriteBytes(buffer+16, utf16) ctx.Mem.Write16(buffer+16+uint64(len(utf16)), 0) } case 40: // ProcessCookie - random cookie for stack canary writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0x12345678) } case 43: // ProcessImageInformation - SECTION_IMAGE_INFORMATION (48 bytes) const needed = 48 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { imageBase, _ := ctx.Mem.Read64(ctx.State.PEBAddr + 0x10) // PEB.ImageBaseAddress ctx.Mem.Write64(buffer+0, imageBase) // TransferAddress ctx.Mem.Write32(buffer+8, 0) // ZeroBits ctx.Mem.Write64(buffer+16, 0x100000) // MaximumStackSize ctx.Mem.Write64(buffer+24, 0x1000) // CommittedStackSize ctx.Mem.Write32(buffer+32, 0) // SubSystemType (native=0) ctx.Mem.Write16(buffer+36, 6) // MinorSubsystemVersion ctx.Mem.Write16(buffer+38, 1) // MajorSubsystemVersion ctx.Mem.Write32(buffer+40, 0) // GpValue ctx.Mem.Write16(buffer+44, 0x100) // ImageCharacteristics ctx.Mem.Write16(buffer+46, 0x8664) // DllCharacteristics (AMD64) } case 60: // ProcessCommandLineInformation - UNICODE_STRING + command line cmdLine := "" if ctx.State != nil { paramsAddr, _ := ctx.Mem.Read64(ctx.State.PEBAddr + PebProcessParameters) if paramsAddr != 0 { cmdLine = readGuestUnicodeString(ctx.Mem, paramsAddr+ProcessParamsCommandLine) } } if cmdLine == "" && ctx.State != nil { cmdLine = ctx.State.ImagePath } if cmdLine == "" { cmdLine = "program.exe" } utf16 := encodeUTF16LE(cmdLine) needed := uint32(16 + len(utf16) + 2) writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write16(buffer+0, uint16(len(utf16))) ctx.Mem.Write16(buffer+2, uint16(len(utf16)+2)) ctx.Mem.Write64(buffer+8, buffer+16) ctx.Mem.WriteBytes(buffer+16, utf16) ctx.Mem.Write16(buffer+16+uint64(len(utf16)), 0) } case 61: // ProcessProtectionInformation writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) // no protection level } case 64: // ProcessMitigationPolicy writeRetLen(0) case 77: // ProcessConsoleHostProcess writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) // no console host process } default: return StatusNotImplemented } return StatusSuccess } func handleNtQueryInformationThread(ctx *TrapContext) uint32 { // NtQueryInformationThread(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength) threadHandle := ctx.Handles.GetThread(Handle(ctx.Arg(0))) infoClass := uint32(ctx.Arg(1)) buffer := ctx.Arg(2) bufLen := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) writeRetLen := func(n uint32) { if retLen != 0 { ctx.Mem.Write32(retLen, n) } } switch infoClass { case 0: // ThreadBasicInformation - THREAD_BASIC_INFORMATION (48 bytes) const needed = 48 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } tid := uint64(1) priority := uint32(8) if threadHandle != nil { tid = uint64(threadHandle.TID) priority = uint32(threadHandle.Priority) } else if ctx.State != nil && ctx.State.ThreadId != 0 { tid = uint64(ctx.State.ThreadId) } if buffer != 0 { ctx.Mem.Write64(buffer+0, 0) // ExitStatus if ctx.State != nil { ctx.Mem.Write64(buffer+8, uint64(ctx.State.TEBAddr)) // TebBaseAddress ctx.Mem.Write64(buffer+16, uint64(ctx.State.ProcessId)) } ctx.Mem.Write64(buffer+24, tid) // ClientId.UniqueThread ctx.Mem.Write64(buffer+32, 0xF) // AffinityMask ctx.Mem.Write32(buffer+40, priority) // Priority ctx.Mem.Write32(buffer+44, priority) // BasePriority } case 1: // ThreadTimes - KERNEL_USER_TIMES (32 bytes) const needed = 32 writeRetLen(needed) if bufLen < needed { return StatusBufferTooSmall } if buffer != 0 { now := uint64(time.Now().UnixNano()/100 + 116444736000000000) ctx.Mem.Write64(buffer+0, now) // CreateTime ctx.Mem.Write64(buffer+8, 0) // ExitTime ctx.Mem.Write64(buffer+16, 0) // KernelTime ctx.Mem.Write64(buffer+24, 0) // UserTime } case 2: // ThreadPriority writeRetLen(4) if buffer != 0 { priority := uint32(8) if threadHandle != nil { priority = uint32(threadHandle.Priority) } ctx.Mem.Write32(buffer, priority) } case 3: // ThreadBasePriority writeRetLen(4) if buffer != 0 { basePriority := uint32(8) if threadHandle != nil { basePriority = uint32(threadHandle.Priority) } ctx.Mem.Write32(buffer, basePriority) } case 4: // ThreadAffinityMask writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0xF) } case 9: // ThreadQuerySetWin32StartAddress writeRetLen(8) if buffer != 0 { ctx.Mem.Write64(buffer, 0) // not tracked } case 14: // ThreadHideFromDebugger (query: returns BOOLEAN) writeRetLen(1) if buffer != 0 { hidden := uint8(0) if threadHandle != nil && threadHandle.HideFromDebugger { hidden = 1 } ctx.Mem.Write8(buffer, hidden) } case 16: // ThreadTebInformation writeRetLen(8) if buffer != 0 { teb := uint64(0) if ctx.State != nil { teb = uint64(ctx.State.TEBAddr) } ctx.Mem.Write64(buffer, teb) } case 19: // ThreadIoPriority writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 2) // NORMAL } case 27: // ThreadActualBasePriority writeRetLen(4) if buffer != 0 { priority := uint32(8) if threadHandle != nil { priority = uint32(threadHandle.Priority) } ctx.Mem.Write32(buffer, priority) } case 17: // ThreadIsTerminated writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) // running } case 34: // ThreadSuspendCount writeRetLen(4) if buffer != 0 { ctx.Mem.Write32(buffer, 0) } default: return StatusNotImplemented } return StatusSuccess } func handleNtQuerySystemInformation(ctx *TrapContext) uint32 { infoClass := uint32(ctx.Arg(0)) buffer := ctx.Arg(1) length := uint32(ctx.Arg(2)) retLen := ctx.Arg(3) writeRetLen := func(n uint32) { if retLen != 0 { ctx.Mem.Write32(retLen, n) } } switch infoClass { case 0: // SystemBasicInformation const needed = 56 writeRetLen(needed) if buffer == 0 || length < needed { return StatusBufferTooSmall } if buffer != 0 { ctx.Mem.Write32(buffer+0, 0) // Reserved ctx.Mem.Write32(buffer+4, 100) // TimerResolution ctx.Mem.Write32(buffer+8, uint32(mmu.PageSize)) // PageSize ctx.Mem.Write32(buffer+12, 262144) // NumberOfPhysicalPages ctx.Mem.Write32(buffer+16, 256) // LowestPhysicalPageNumber ctx.Mem.Write32(buffer+20, 262144) // HighestPhysicalPageNumber ctx.Mem.Write32(buffer+24, uint32(mmu.PageSize)) // AllocationGranularity ctx.Mem.Write64(buffer+28, 0x10000) // MinimumUserModeAddress ctx.Mem.Write64(buffer+36, 0x7FFFFFFEFFFF) // MaximumUserModeAddress ctx.Mem.Write64(buffer+44, 0xF) // ActiveProcessorsAffinityMask ctx.Mem.Write8(buffer+52, uint8(runtime.NumCPU())) // NumberOfProcessors } case 1: // SystemProcessorInformation if buffer != 0 { ctx.Mem.Write16(buffer+0, 9) // ProcessorArchitecture = AMD64 ctx.Mem.Write16(buffer+2, 0) // ProcessorLevel ctx.Mem.Write16(buffer+4, 0) // ProcessorRevision } case 2: // SystemPerformanceInformation (256 bytes SYSTEM_PERFORMANCE_INFORMATION) if buffer != 0 { total := uint64(1 * 1024 * 1024 * 1024) // 1GB total virtual now := uint64(time.Now().UnixNano() / 100) ctx.Mem.Write64(buffer+0, now) // IdleProcessTime ctx.Mem.Write64(buffer+8, 0) // IoReadTransferCount ctx.Mem.Write64(buffer+16, 0) // IoWriteTransferCount ctx.Mem.Write64(buffer+24, 0) // IoOtherTransferCount ctx.Mem.Write32(buffer+32, 0) // IoReadOperationCount ctx.Mem.Write32(buffer+36, 0) // IoWriteOperationCount ctx.Mem.Write32(buffer+40, 0) // IoOtherOperationCount ctx.Mem.Write32(buffer+44, uint32(total/mmu.PageSize)) // AvailablePages ctx.Mem.Write32(buffer+48, uint32(total/mmu.PageSize)) // CommittedPages ctx.Mem.Write32(buffer+52, uint32(total*2/mmu.PageSize)) // CommitLimit ctx.Mem.Write32(buffer+56, 0) // PeakCommitment ctx.Mem.Write32(buffer+60, 1024*1024) // PageFaultCount ctx.Mem.Write32(buffer+64, 0) // CopyOnWriteCount ctx.Mem.Write32(buffer+68, 0) // TransitionCount ctx.Mem.Write32(buffer+72, 0) // CacheTransitionCount ctx.Mem.Write32(buffer+76, 0) // DemandZeroCount ctx.Mem.Write32(buffer+80, 0) // PageReadCount ctx.Mem.Write32(buffer+84, 0) // PageReadIoCount ctx.Mem.Write32(buffer+88, 0) // CacheReadCount ctx.Mem.Write32(buffer+92, 0) // CacheIoCount ctx.Mem.Write32(buffer+96, 0) // DirtyPagesWriteCount ctx.Mem.Write32(buffer+100, 0) // DirtyWriteIoCount ctx.Mem.Write32(buffer+104, 0) // MappedPagesWriteCount ctx.Mem.Write32(buffer+108, 0) // MappedWriteIoCount ctx.Mem.Write32(buffer+112, uint32(total/mmu.PageSize)) // PagedPoolPages ctx.Mem.Write32(buffer+116, 1024) // NonPagedPoolPages ctx.Mem.Write32(buffer+120, 0) // PagedPoolAllocs ctx.Mem.Write32(buffer+124, 0) // PagedPoolFrees ctx.Mem.Write32(buffer+128, 0) // NonPagedPoolAllocs ctx.Mem.Write32(buffer+132, 0) // NonPagedPoolFrees ctx.Mem.Write32(buffer+136, 4096) // FreeSystemPtes ctx.Mem.Write32(buffer+140, 1024) // ResidentSystemCodePage ctx.Mem.Write32(buffer+144, 0) // TotalSystemDriverPages ctx.Mem.Write32(buffer+148, 0) // TotalSystemCodePages ctx.Mem.Write32(buffer+152, 0) // NonPagedPoolLookasideHits ctx.Mem.Write32(buffer+156, 0) // PagedPoolLookasideHits ctx.Mem.Write32(buffer+160, 0) // Spare3Count ctx.Mem.Write32(buffer+164, 0) // ResidentSystemCachePage ctx.Mem.Write32(buffer+168, 0) // ResidentPagedPoolPage ctx.Mem.Write32(buffer+172, 0) // ResidentSystemDriverPage ctx.Mem.Write32(buffer+176, 0) // CcFastReadNoWait ctx.Mem.Write32(buffer+180, 0) // CcFastReadWait ctx.Mem.Write32(buffer+184, 0) // CcFastReadResourceMiss ctx.Mem.Write32(buffer+188, 0) // CcFastReadNotPossible ctx.Mem.Write32(buffer+192, 0) // CcFastMdlReadNoWait ctx.Mem.Write32(buffer+196, 0) // CcFastMdlReadWait ctx.Mem.Write32(buffer+200, 0) // CcFastMdlReadResourceMiss ctx.Mem.Write32(buffer+204, 0) // CcFastMdlReadNotPossible ctx.Mem.Write32(buffer+208, 0) // CcMapDataNoWait ctx.Mem.Write32(buffer+212, 0) // CcMapDataWait ctx.Mem.Write32(buffer+216, 0) // CcMapDataNoWaitMiss ctx.Mem.Write32(buffer+220, 0) // CcMapDataWaitMiss ctx.Mem.Write32(buffer+224, 0) // CcPinMappedDataCount ctx.Mem.Write32(buffer+228, 0) // CcPinReadNoWait ctx.Mem.Write32(buffer+232, 0) // CcPinReadWait ctx.Mem.Write32(buffer+236, 0) // CcPinReadNoWaitMiss ctx.Mem.Write32(buffer+240, 0) // CcPinReadWaitMiss ctx.Mem.Write32(buffer+244, 0) // CcCopyReadNoWait ctx.Mem.Write32(buffer+248, 0) // CcCopyReadWait ctx.Mem.Write32(buffer+252, 0) // CcCopyReadNoWaitMiss } case 3: // SystemTimeOfDayInformation (48 bytes) const needed = 48 writeRetLen(needed) if buffer == 0 || length < needed { return StatusBufferTooSmall } if buffer != 0 { now := uint64(time.Now().UnixNano()/100 + 116444736000000000) ctx.Mem.Write64(buffer+0, 0) ctx.Mem.Write64(buffer+8, now) ctx.Mem.Write64(buffer+16, 0) ctx.Mem.Write32(buffer+24, 0) ctx.Mem.Write32(buffer+28, 0) ctx.Mem.Write64(buffer+32, 0) ctx.Mem.Write64(buffer+40, 0) } case 5: // SystemProcessInformation - minimal single-process entry imageName := "Raven" if ctx.State != nil && ctx.State.ImagePath != "" { imageName = ctx.State.ImagePath if slash := strings.LastIndexAny(imageName, `\/`); slash >= 0 { imageName = imageName[slash+1:] } } nameBytes := encodeUTF16LE(imageName) needed := uint32(0x100 + len(nameBytes) + 2) writeRetLen(needed) if buffer == 0 || length < needed { return StatusBufferTooSmall } ctx.Mem.Write32(buffer+0, 0) ctx.Mem.Write32(buffer+4, 1) ctx.Mem.Write64(buffer+8, 0) ctx.Mem.Write32(buffer+16, 0) ctx.Mem.Write32(buffer+20, 1) ctx.Mem.Write64(buffer+24, 0) ctx.Mem.Write64(buffer+32, 0) ctx.Mem.Write64(buffer+40, 0) ctx.Mem.Write64(buffer+48, 0) ctx.Mem.Write16(buffer+56, uint16(len(nameBytes))) ctx.Mem.Write16(buffer+58, uint16(len(nameBytes)+2)) ctx.Mem.Write32(buffer+60, 0) ctx.Mem.Write64(buffer+64, buffer+0x100) ctx.Mem.Write32(buffer+72, 8) pid := uint64(4) if ctx.State != nil && ctx.State.ProcessId != 0 { pid = uint64(ctx.State.ProcessId) } ctx.Mem.Write64(buffer+80, pid) ctx.Mem.Write64(buffer+88, 0) ctx.Mem.Write32(buffer+96, uint32(ctx.Handles.Count())) ctx.Mem.Write32(buffer+100, 0) ctx.Mem.WriteBytes(buffer+0x100, nameBytes) ctx.Mem.Write16(buffer+0x100+uint64(len(nameBytes)), 0) case 7: // SystemProcessorSpeedInformation / SystemNumaProcessorMap if buffer != 0 { ctx.Mem.Write32(buffer, 0) // HighestNodeNumber = 0 ctx.Mem.Write32(buffer+4, 0) } case 16: // SystemHandleInformation if buffer != 0 { ctx.Mem.Write32(buffer, 0) // NumberOfHandles ctx.Mem.Write32(buffer+4, 0) // Reserved } case 18: // SystemPagefileInformation if buffer != 0 { ctx.Mem.Write32(buffer, 0) // NextEntryOffset ctx.Mem.Write32(buffer+4, 512*1024) // TotalSize (in pages) ctx.Mem.Write32(buffer+8, 0) // TotalInUse ctx.Mem.Write32(buffer+12, 0) // PeakUsage // PageFileName is empty UNICODE_STRING at +16 } case 21: // SystemFileCacheInformation if buffer != 0 { total := uint64(256 * 1024 * 1024) // 256MB cache ctx.Mem.Write64(buffer, total/mmu.PageSize) // CurrentSize ctx.Mem.Write64(buffer+8, total/mmu.PageSize) // PeakSize ctx.Mem.Write32(buffer+16, 0) // PageFaultCount ctx.Mem.Write64(buffer+24, total/mmu.PageSize) // MinimumWorkingSet ctx.Mem.Write64(buffer+32, total/mmu.PageSize) // MaximumWorkingSet ctx.Mem.Write64(buffer+40, total/mmu.PageSize) // CurrentSizeIncludingTransitionInPages ctx.Mem.Write64(buffer+48, total/mmu.PageSize) // PeakSizeIncludingTransitionInPages ctx.Mem.Write64(buffer+56, 0) // TransitionRePurposeCount ctx.Mem.Write32(buffer+64, 0) // Flags } case 35: // SystemKernelDebuggerInformation if buffer != 0 { ctx.Mem.Write8(buffer, 0) // KernelDebuggerEnabled = FALSE ctx.Mem.Write8(buffer+1, 1) // KernelDebuggerNotPresent = TRUE } case 37: // SystemRegistryQuotaInformation if buffer != 0 { ctx.Mem.Write32(buffer, 128*1024*1024) // RegistryQuotaAllowed (128MB) ctx.Mem.Write32(buffer+4, 0) // RegistryQuotaUsed ctx.Mem.Write64(buffer+8, 0) // PagedPoolSize } case 45: // SystemLookasideInformation // success with empty data case 8: // SystemProcessorPerformanceInformation (alias for case 2) if buffer != 0 { nCPU := runtime.NumCPU() now := uint64(time.Now().UnixNano() / 100) for i := 0; i < nCPU; i++ { base := buffer + uint64(i)*48 ctx.Mem.Write64(base+0, 0) ctx.Mem.Write64(base+8, now) ctx.Mem.Write64(base+16, now) ctx.Mem.Write64(base+24, 0) ctx.Mem.Write64(base+32, 0) ctx.Mem.Write32(base+40, 0) } } case 11: // SystemModuleInformation (RTL_PROCESS_MODULES) if buffer != 0 { ctx.Mem.Write32(buffer, 0) // NumberOfModules = 0 } case 23: // SystemInterruptInformation // success with empty data case 44: // SystemExtendedHandleInformation if buffer != 0 { ctx.Mem.Write64(buffer, 0) // NumberOfHandles ctx.Mem.Write64(buffer+8, 0) // Reserved } case 64: // SystemCodeIntegrityInformation if buffer != 0 { ctx.Mem.Write32(buffer, 0) } case 173: // SystemCodeIntegrityUnlockModeInformation // success with empty data default: // Many apps only check NTSTATUS, not the data - return success. if ctx.Trace { fmt.Printf(" NtQuerySystemInformation: unhandled class %d\n", infoClass) } } return StatusSuccess } func handleNtTerminateProcess(ctx *TrapContext) uint32 { exitCode := ctx.Arg(1) if ctx.Trace { fmt.Printf(" NtTerminateProcess: exitCode=%d\n", exitCode) } // Signal the CPU to stop ctx.CPU.Stop() return StatusSuccess } // ======================== VIRTUAL MEMORY ======================== func handleNtAllocateVirtualMemory(ctx *TrapContext) uint32 { // ProcessHandle, BaseAddress*, ZeroBits, RegionSize*, AllocationType, Protect basePtr := ctx.Arg(1) sizePtr := ctx.Arg(3) allocationType := uint32(ctx.Arg(4)) protect := uint32(ctx.Arg(5)) baseAddr, _ := ctx.Mem.Read64(basePtr) size, _ := ctx.Mem.Read64(sizePtr) if size == 0 { return StatusInvalidParameter } if allocationType&(memCommit|memReserve) == 0 { return StatusInvalidParameter } size = (size + mmu.PageSize - 1) & mmu.PageMask perms := NtMemProtectToMMU(protect) if perms == 0 { perms = mmu.PermRead | mmu.PermWrite } addr := ctx.Mem.Mmap(baseAddr, size, perms) if addr < 0x1000 { ravelog.Logf("[WARN] NtAllocateVirtualMemory failed addr=0x%x size=0x%x baseAddr=0x%x\n", addr, size, baseAddr) return StatusNoMemory } writeGuestPtr(ctx.Mem, basePtr, addr) writeGuestPtr(ctx.Mem, sizePtr, size) return StatusSuccess } func handleNtFreeVirtualMemory(ctx *TrapContext) uint32 { basePtr := ctx.Arg(1) sizePtr := ctx.Arg(2) freeType := uint32(ctx.Arg(3)) baseAddr, _ := ctx.Mem.Read64(basePtr) size, _ := ctx.Mem.Read64(sizePtr) if baseAddr == 0 { return StatusInvalidParameter } if freeType&memRelease != 0 { _, regionSize, mapped, _ := ctx.Mem.QueryRegion(baseAddr) if mapped && regionSize > 0 { size = regionSize } } else if freeType&memDecommit != 0 { if size == 0 { _, regionSize, mapped, _ := ctx.Mem.QueryRegion(baseAddr) if mapped && regionSize > 0 { size = regionSize } } } else { return StatusInvalidParameter } if size == 0 { return StatusInvalidParameter } ctx.Mem.Munmap(baseAddr, size) writeGuestPtr(ctx.Mem, sizePtr, size) return StatusSuccess } func handleNtProtectVirtualMemory(ctx *TrapContext) uint32 { basePtr := ctx.Arg(1) sizePtr := ctx.Arg(2) newProtect := uint32(ctx.Arg(3)) oldProtectPtr := ctx.Arg(4) baseAddr, _ := ctx.Mem.Read64(basePtr) size, _ := ctx.Mem.Read64(sizePtr) _, _, mapped, oldPerms := ctx.Mem.QueryRegion(baseAddr) if !mapped { return StatusInvalidParameter } perms := NtMemProtectToMMU(newProtect) if perms == 0 && newProtect != 0x01 { return StatusInvalidParameter } ctx.Mem.Mprotect(baseAddr, size, perms) if oldProtectPtr != 0 { ctx.Mem.Write32(oldProtectPtr, MMUPermsToNtProtect(oldPerms)) } return StatusSuccess } func handleNtQueryVirtualMemory(ctx *TrapContext) uint32 { addr := ctx.Arg(1) infoClass := ctx.Arg(2) buffer := ctx.Arg(3) bufLen := ctx.Arg(4) retLen := ctx.Arg(5) if buffer == 0 { return StatusSuccess } switch infoClass { case 2: // MemorySectionName (UNICODE_STRING with image path for mapped files) // Find the loaded module containing this address so apps can ask for the // mapped file name of code/data pages. imagePath := "" if ctx.State != nil { ldrAddr, _ := ctx.Mem.Read64(ctx.State.PEBAddr + PebLdr) if ldrAddr != 0 { listHead := ldrAddr + LdrInLoadOrderModuleList flink, _ := ctx.Mem.Read64(listHead) for flink != 0 && flink != listHead { entry := flink base, _ := ctx.Mem.Read64(entry + LdrEntryDllBase) size, _ := ctx.Mem.Read32(entry + LdrEntrySizeOfImage) if base != 0 && addr >= base && addr < base+uint64(size) { imagePath = readGuestUnicodeString(ctx.Mem, entry+LdrEntryFullDllName) break } flink, _ = ctx.Mem.Read64(flink) } } if imagePath == "" { pebBase, _ := ctx.Mem.Read64(ctx.State.PEBAddr + 0x10) if pebBase != 0 && addr >= pebBase { imagePath = ctx.State.ImagePath } } } if imagePath == "" { imagePath = `\Device\HarddiskVolume1\Windows\System32\unknown.dll` } // Convert Win32 path to an NT device path when necessary. if strings.HasPrefix(imagePath, `C:\`) { imagePath = `\Device\HarddiskVolume1\` + imagePath[3:] } else if strings.HasPrefix(imagePath, `/`) { imagePath = `\Device\HarddiskVolume1` + imagePath } // Write UNICODE_STRING followed by the path data // UNICODE_STRING: Length(2) + MaxLength(2) + pad(4) + Buffer(8) = 16 bytes wsBytes := encodeUTF16LE(imagePath) needed := uint64(16 + len(wsBytes) + 2) if retLen != 0 { writeGuestPtr(ctx.Mem, retLen, needed) } if bufLen < needed { return StatusBufferTooSmall } strData := buffer + 16 ctx.Mem.Write16(buffer, uint16(len(wsBytes))) // Length ctx.Mem.Write16(buffer+2, uint16(len(wsBytes)+2)) // MaxLength ctx.Mem.Write32(buffer+4, 0) // padding ctx.Mem.Write64(buffer+8, strData) // Buffer ctx.Mem.WriteBytes(strData, wsBytes) ctx.Mem.Write16(strData+uint64(len(wsBytes)), 0) return StatusSuccess } base, size, mapped, perms := ctx.Mem.QueryRegion(addr) // Convert MMU perms to Win32 PAGE_xxx constants. var protect uint32 if !mapped { protect = 0x01 // PAGE_NOACCESS } else { switch { case perms&(mmu.PermRead|mmu.PermWrite|mmu.PermExecute) == mmu.PermRead|mmu.PermWrite|mmu.PermExecute: protect = 0x40 // PAGE_EXECUTE_READWRITE case perms&(mmu.PermRead|mmu.PermExecute) == mmu.PermRead|mmu.PermExecute: protect = 0x20 // PAGE_EXECUTE_READ case perms&(mmu.PermRead|mmu.PermWrite) == mmu.PermRead|mmu.PermWrite: protect = 0x04 // PAGE_READWRITE default: protect = 0x02 // PAGE_READONLY } } var state, typ uint32 if mapped { state = 0x1000 // MEM_COMMIT typ = 0x20000 // MEM_PRIVATE } else { state = 0x10000 // MEM_FREE typ = 0 protect = 0x01 // PAGE_NOACCESS } if ctx.Mem.Mode32 { if retLen != 0 { ctx.Mem.Write32(retLen, 28) } if bufLen < 28 { return StatusBufferTooSmall } ctx.Mem.Write32(buffer+0, uint32(base)) ctx.Mem.Write32(buffer+4, uint32(base)) ctx.Mem.Write32(buffer+8, protect) ctx.Mem.Write32(buffer+12, uint32(size)) ctx.Mem.Write32(buffer+16, state) ctx.Mem.Write32(buffer+20, protect) ctx.Mem.Write32(buffer+24, typ) return StatusSuccess } if retLen != 0 { ctx.Mem.Write64(retLen, 48) } if bufLen < 48 { return StatusBufferTooSmall } ctx.Mem.Write64(buffer+0, base) ctx.Mem.Write64(buffer+8, base) ctx.Mem.Write32(buffer+16, protect) ctx.Mem.Write32(buffer+20, 0) ctx.Mem.Write64(buffer+24, size) ctx.Mem.Write32(buffer+32, state) ctx.Mem.Write32(buffer+36, protect) ctx.Mem.Write32(buffer+40, typ) return StatusSuccess } // ======================== SYNCHRONIZATION ======================== func handleNtCreateEvent(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) eventType := ctx.Arg(3) autoReset := eventType == 1 // SynchronizationEvent evt := NewEvent(!autoReset, false) // Read name from OBJECT_ATTRIBUTES if present var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } var h Handle if name != "" { var isNew bool h, isNew = ctx.Handles.AllocNamed(name, evt) if !isNew { // STATUS_OBJECT_NAME_COLLISION is not quite right for NT; // the real kernel returns STATUS_OBJECT_NAME_EXISTS (0x40000000) ctx.Mem.Write64(handlePtr, uint64(h)) return 0x40000000 // STATUS_OBJECT_NAME_EXISTS (success with info) } } else { h = ctx.Handles.Alloc(evt) } ctx.Mem.Write64(handlePtr, uint64(h)) return StatusSuccess } func handleNtSetEvent(ctx *TrapContext) uint32 { handle := Handle(ctx.Arg(0)) evt := ctx.Handles.GetEvent(handle) if evt == nil { return StatusInvalidHandle } evt.Set() return StatusSuccess } func handleNtResetEvent(ctx *TrapContext) uint32 { handle := Handle(ctx.Arg(0)) evt := ctx.Handles.GetEvent(handle) if evt == nil { return StatusInvalidHandle } evt.Reset() return StatusSuccess } func handleNtClearEvent(ctx *TrapContext) uint32 { return handleNtResetEvent(ctx) } func handleNtPulseEvent(ctx *TrapContext) uint32 { // NtPulseEvent(EventHandle, PreviousState*) - signal then immediately reset handle := Handle(ctx.Arg(0)) prevPtr := ctx.Arg(1) evt := ctx.Handles.GetEvent(handle) if evt == nil { return StatusInvalidHandle } prev := evt.PeekSignaled() if prevPtr != 0 { if prev { ctx.Mem.Write32(prevPtr, 1) } else { ctx.Mem.Write32(prevPtr, 0) } } evt.Signal() // Auto-reset: clear immediately after waking (best effort) evt.Reset() return StatusSuccess } func handleNtQueryEvent(ctx *TrapContext) uint32 { // NtQueryEvent(EventHandle, EventInformationClass, EventInfo, Length, ReturnLength) handle := Handle(ctx.Arg(0)) infoClass := ctx.Arg(1) buffer := ctx.Arg(2) bufLen := ctx.Arg(3) retLen := ctx.Arg(4) _ = infoClass // EventBasicInformation = 0 const needed = 8 if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if bufLen < needed { return 0xC0000023 } evt := ctx.Handles.GetEvent(handle) signaled := false eventType := uint32(0) // NotificationEvent if evt != nil { signaled = evt.PeekSignaled() if evt.IsManualReset() { eventType = 0 // NotificationEvent } else { eventType = 1 // SynchronizationEvent } } if buffer != 0 { ctx.Mem.Write32(buffer, eventType) if signaled { ctx.Mem.Write32(buffer+4, 1) } else { ctx.Mem.Write32(buffer+4, 0) } } return StatusSuccess } func handleNtQueryMutant(ctx *TrapContext) uint32 { // NtQueryMutant(MutantHandle, MutantInformationClass, MutantInfo, Length, ReturnLength) buffer := ctx.Arg(2) bufLen := ctx.Arg(3) retLen := ctx.Arg(4) const needed = 12 if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if bufLen < needed { return 0xC0000023 } if buffer != 0 { ctx.Mem.Write32(buffer, 0) // CurrentCount ctx.Mem.Write32(buffer+4, 0) // OwnedByCaller ctx.Mem.Write32(buffer+8, 0) // AbandonedState } return StatusSuccess } func handleNtQuerySemaphore(ctx *TrapContext) uint32 { // NtQuerySemaphore(SemHandle, SemInfoClass, SemInfo, Length, ReturnLength) handle := Handle(ctx.Arg(0)) buffer := ctx.Arg(2) bufLen := ctx.Arg(3) retLen := ctx.Arg(4) const needed = 8 if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if bufLen < needed { return 0xC0000023 } count, maxCount := int32(0), int32(1) sem := ctx.Handles.GetSemaphore(handle) if sem != nil { count = sem.Count() maxCount = sem.MaxCount() } if buffer != 0 { ctx.Mem.Write32(buffer, uint32(count)) ctx.Mem.Write32(buffer+4, uint32(maxCount)) } return StatusSuccess } func handleNtQueryTimerResolution(ctx *TrapContext) uint32 { // NtQueryTimerResolution(MinimumResolution*, MaximumResolution*, ActualResolution*) minPtr := ctx.Arg(0) maxPtr := ctx.Arg(1) curPtr := ctx.Arg(2) if minPtr != 0 { ctx.Mem.Write32(minPtr, 156250) } // 15.625ms in 100ns units if maxPtr != 0 { ctx.Mem.Write32(maxPtr, 5000) } // 0.5ms if curPtr != 0 { ctx.Mem.Write32(curPtr, 156250) } // 15.625ms current return StatusSuccess } // ────────────────────────────────────────────────────────── // NtCreateKeyedEvent / NtOpenKeyedEvent / NtWaitForKeyedEvent / NtReleaseKeyedEvent // Reference: reactos/ntoskrnl/ex/keyedevt.c // Used by ntdll SRW locks, condition variables, critical section fallback // ────────────────────────────────────────────────────────── func handleNtCreateKeyedEvent(ctx *TrapContext) uint32 { outHandle := ctx.Arg(0) // arg1=DesiredAccess, arg2=ObjectAttributes, arg3=Flags - all ignored for now name := "" ke := NewKeyedEvent(name) h := ctx.Handles.Alloc(ke) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } func handleNtOpenKeyedEvent(ctx *TrapContext) uint32 { outHandle := ctx.Arg(0) // For now always return the global fallback event handle h := ctx.Handles.Alloc(GlobalKeyedEvent) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } func handleNtWaitForKeyedEvent(ctx *TrapContext) uint32 { // NtWaitForKeyedEvent(Handle, Key, Alertable, Timeout*) hVal := Handle(ctx.Arg(0)) key := ctx.Arg(1) // guest address used as key timeoutP := ctx.Arg(3) var ke *KeyedEventHandle if hVal == 0 || hVal == Handle(0xFFFFFFFFFFFFFFFF) { ke = GlobalKeyedEvent } else { ke = ctx.Handles.GetKeyedEvent(hVal) if ke == nil { ke = GlobalKeyedEvent } } d := ntWaitDuration(ctx, timeoutP) status := ke.Wait(key, d) return status } func handleNtReleaseKeyedEvent(ctx *TrapContext) uint32 { // NtReleaseKeyedEvent(Handle, Key, Alertable, Timeout*) hVal := Handle(ctx.Arg(0)) key := ctx.Arg(1) timeoutP := ctx.Arg(3) var ke *KeyedEventHandle if hVal == 0 || hVal == Handle(0xFFFFFFFFFFFFFFFF) { ke = GlobalKeyedEvent } else { ke = ctx.Handles.GetKeyedEvent(hVal) if ke == nil { ke = GlobalKeyedEvent } } d := ntWaitDuration(ctx, timeoutP) status := ke.Release(key, d) return status } // ────────────────────────────────────────────────────────── // NtAllocateUuids - generates UUID components // Reference: reactos/ntoskrnl/ex/uuid.c // ────────────────────────────────────────────────────────── func handleNtAllocateUuids(ctx *TrapContext) uint32 { // NtAllocateUuids(Time*, Range*, Sequence*, Seed*) timePtr := ctx.Arg(0) rangePtr := ctx.Arg(1) seqPtr := ctx.Arg(2) seedPtr := ctx.Arg(3) // Use current time as the time component now := uint64(time.Now().UnixNano()/100 + 116444736000000000) // FILETIME if timePtr != 0 { ctx.Mem.Write64(timePtr, now) } if rangePtr != 0 { ctx.Mem.Write32(rangePtr, 1) } var seq [2]byte unix.Getrandom(seq[:], 0) if seqPtr != 0 { ctx.Mem.Write16(seqPtr, uint16(seq[0])|(uint16(seq[1])<<8)) } var seed [8]byte unix.Getrandom(seed[:], 0) if seedPtr != 0 { for i, b := range seed { ctx.Mem.Write8(seedPtr+uint64(i), b) } } return StatusSuccess } func handleNtWaitForSingleObject(ctx *TrapContext) uint32 { handle := Handle(ctx.Arg(0)) _ = ctx.Arg(1) // Alertable timeoutPtr := ctx.Arg(2) timeout := ntWaitDuration(ctx, timeoutPtr) return ntWaitOne(ctx, handle, timeout) } // ntWaitDuration converts an optional NT timeout pointer to a Go duration. // Negative NT time = relative (100-ns units). Nil pointer = INFINITE. func ntWaitDuration(ctx *TrapContext, ptr uint64) time.Duration { if ptr == 0 { return -1 // INFINITE } t, _ := ctx.Mem.Read64(ptr) if t == 0 { return 0 // poll } if int64(t) < 0 { return time.Duration(-int64(t)) * 100 * time.Nanosecond } // Absolute FILETIME: compute delta from now nowFT := uint64(time.Now().UnixNano()/100 + 116444736000000000) if t <= nowFT { return 0 } return time.Duration(t-nowFT) * 100 * time.Nanosecond } // ntWaitOne waits on a single handle; returns NTSTATUS. func ntWaitOne(ctx *TrapContext, h Handle, timeout time.Duration) uint32 { obj := ctx.Handles.Get(h) switch v := obj.(type) { case *EventHandle: if v.WaitTimeout(timeout) { return StatusWait0 } return StatusTimeout case *MutantHandle: res := v.WaitTimeout(1, timeout) // TID=1 (simplified) if res == 0 { return StatusWait0 } if res == 0x80 { return 0x00000080 // STATUS_ABANDONED_WAIT_0 } return StatusTimeout case *SemaphoreHandle: res := v.WaitTimeout(timeout) if res == 0 { return StatusWait0 } return StatusTimeout case *WaitableTimerHandle: res := v.WaitTimeout(timeout) if res == 0 { return StatusWait0 } return StatusTimeout case *ThreadHandle: if v.Done == nil { return StatusWait0 } if timeout < 0 { <-v.Done return StatusWait0 } select { case <-v.Done: return StatusWait0 case <-time.After(timeout): return StatusTimeout } case nil: return StatusInvalidHandle case *GenericHandle: switch strings.ToLower(v.Kind) { case "process", "thread": return StatusWait0 default: return StatusWait0 } default: // Unknown handle type - treat as signaled return StatusWait0 } } func handleNtWaitForMultipleObjects(ctx *TrapContext) uint32 { // NtWaitForMultipleObjects(Count, Handles*, WaitType, Alertable, Timeout*) count := uint32(ctx.Arg(0)) handlesPtr := ctx.Arg(1) waitAll := ctx.Arg(2) == 0 // 0=WaitAll, 1=WaitAny _ = ctx.Arg(3) // Alertable timeoutPtr := ctx.Arg(4) if count == 0 || handlesPtr == 0 { return StatusWait0 } if count > 64 { count = 64 } timeout := ntWaitDuration(ctx, timeoutPtr) if !waitAll { // WaitAny: try each handle, return first signaled for i := uint32(0); i < count; i++ { hVal, _ := ctx.Mem.Read64(handlesPtr + uint64(i)*8) res := ntWaitOne(ctx, Handle(hVal), timeout) if res == StatusWait0 || res == 0x80 { return StatusWait0 + uint32(i) } } return StatusTimeout } // WaitAll: wait for each in sequence for i := uint32(0); i < count; i++ { hVal, _ := ctx.Mem.Read64(handlesPtr + uint64(i)*8) res := ntWaitOne(ctx, Handle(hVal), timeout) if res == StatusTimeout { return StatusTimeout } } return StatusWait0 } func handleNtCreateMutant(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) initialOwner := ctx.Arg(3) != 0 m := NewMutant(0) if initialOwner { m.WaitTimeout(1, 0) // TID=1 as simplified owner } var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } var h Handle if name != "" { var isNew bool h, isNew = ctx.Handles.AllocNamed(name, m) if !isNew { ctx.Mem.Write64(handlePtr, uint64(h)) return 0x40000000 // STATUS_OBJECT_NAME_EXISTS } } else { h = ctx.Handles.Alloc(m) } ctx.Mem.Write64(handlePtr, uint64(h)) return StatusSuccess } func handleNtReleaseMutant(ctx *TrapContext) uint32 { h := Handle(ctx.Arg(0)) pPrevCount := ctx.Arg(1) if mh := ctx.Handles.GetMutant(h); mh != nil { prev := mh.Count() mh.Release(1) // TID=1 simplified if pPrevCount != 0 { ctx.Mem.Write32(pPrevCount, uint32(prev)) } } return StatusSuccess } func handleNtDelayExecution(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // Alertable intervalPtr := ctx.Arg(1) if intervalPtr != 0 { interval, _ := ctx.Mem.Read64(intervalPtr) if int64(interval) < 0 { dur := time.Duration(-int64(interval)) * 100 time.Sleep(dur) } } return StatusSuccess } func handleNtYieldExecution(ctx *TrapContext) uint32 { runtime.Gosched() return 0x40000024 // STATUS_NO_YIELD_PERFORMED } func handleNtQueryPerformanceCounter(ctx *TrapContext) uint32 { counterPtr := ctx.Arg(0) freqPtr := ctx.Arg(1) now := time.Now().UnixNano() if counterPtr != 0 { ctx.Mem.Write64(counterPtr, uint64(now/100)) // 100ns intervals } if freqPtr != 0 { ctx.Mem.Write64(freqPtr, 10000000) // 10 MHz } return StatusSuccess } func handleNtQuerySystemTime(ctx *TrapContext) uint32 { timePtr := ctx.Arg(0) if timePtr != 0 { // Windows FILETIME: 100ns intervals since Jan 1, 1601 // Epoch offset: 11644473600 seconds now := time.Now() ft := (now.Unix()+11644473600)*10000000 + int64(now.Nanosecond()/100) ctx.Mem.Write64(timePtr, uint64(ft)) } return StatusSuccess } // ======================== CRITICAL SECTIONS ======================== func handleRtlInitCritSec(ctx *TrapContext) uint32 { csAddr := ctx.Arg(0) if csAddr != 0 { // Allocate RTL_CRITICAL_SECTION_DEBUG structure (48 bytes) dbgInfo := ctx.Mem.Mmap(0, 48, mmu.PermRead|mmu.PermWrite) // DebugInfo: Type=0, CreatorBackTraceIndex=0, CriticalSection pointer at +8 ctx.Mem.Write64(dbgInfo+8, csAddr) // CriticalSection pointer // ProcessLocksList (+16): point to self (empty list) ctx.Mem.Write64(dbgInfo+16, dbgInfo+16) // Flink = &self ctx.Mem.Write64(dbgInfo+24, dbgInfo+16) // Blink = &self // Initialize CRITICAL_SECTION ctx.Mem.Write64(csAddr+0, dbgInfo) // DebugInfo pointer ctx.Mem.Write32(csAddr+8, 0xFFFFFFFF) // LockCount = -1 (unlocked) ctx.Mem.Write32(csAddr+12, 0) // RecursionCount ctx.Mem.Write64(csAddr+16, 0) // OwningThread ctx.Mem.Write64(csAddr+24, 0) // LockSemaphore ctx.Mem.Write64(csAddr+32, 0) // SpinCount } return StatusSuccess } func handleRtlEnterCritSec(ctx *TrapContext) uint32 { // Simplified: just set the lock count to 0 (locked) csAddr := ctx.Arg(0) if csAddr != 0 { ctx.Mem.Write32(csAddr+8, 0) // LockCount = 0 ctx.Mem.Write32(csAddr+12, 1) // RecursionCount = 1 } return StatusSuccess } func handleRtlLeaveCritSec(ctx *TrapContext) uint32 { csAddr := ctx.Arg(0) if csAddr != 0 { ctx.Mem.Write32(csAddr+8, 0xFFFFFFFF) // LockCount = -1 ctx.Mem.Write32(csAddr+12, 0) // RecursionCount = 0 } return StatusSuccess } func handleRtlDeleteCritSec(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlTryEnterCritSec(ctx *TrapContext) uint32 { // Always succeed handleRtlEnterCritSec(ctx) // Return TRUE (1) for success ctx.CPU.SetSyscallReturn(1) return 1 // TRUE } // ======================== SRW LOCKS ======================== func handleRtlInitSRWLock(ctx *TrapContext) uint32 { addr := ctx.Arg(0) if addr != 0 { ctx.Mem.Write64(addr, 0) } return StatusSuccess } func handleRtlAcquireSRWExcl(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlReleaseSRWExcl(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAcquireSRWShared(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlReleaseSRWShared(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlTryAcquireSRWExcl(ctx *TrapContext) uint32 { ctx.CPU.SetSyscallReturn(1) return 1 } func handleRtlTryAcquireSRWShared(ctx *TrapContext) uint32 { ctx.CPU.SetSyscallReturn(1) return 1 } // ======================== CONDITION VARIABLES ======================== func handleRtlInitCondVar(ctx *TrapContext) uint32 { addr := ctx.Arg(0) if addr != 0 { ctx.Mem.Write64(addr, 0) } return StatusSuccess } func handleRtlWakeCondVar(ctx *TrapContext) uint32 { GlobalCondVars.Get(ctx.Arg(0)).Signal() return StatusSuccess } func handleRtlWakeAllCondVar(ctx *TrapContext) uint32 { GlobalCondVars.Get(ctx.Arg(0)).Broadcast() return StatusSuccess } // ======================== HEAP ======================== func handleRtlCreateHeap(ctx *TrapContext) uint32 { heap := &HeapHandle{ Allocations: make(map[uint64]uint64), } h := ctx.Handles.Alloc(heap) ctx.CPU.SetSyscallReturn(uint64(h)) ctx.ReturnOverridden = true return 0 } func handleRtlDestroyHeap(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAllocateHeap(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // HeapHandle flags := ctx.Arg(1) size := ctx.Arg(2) if size == 0 { size = 16 } var addr uint64 if ctx.State != nil && ctx.State.HeapAlloc != nil { zeroInit := flags&0x08 != 0 // HEAP_ZERO_MEMORY addr = ctx.State.HeapAlloc(size, zeroInit) } else { addr = ctx.Mem.Mmap(0, size, mmu.PermRead|mmu.PermWrite) } ctx.CPU.SetSyscallReturn(addr) ctx.ReturnOverridden = true return 0 } func handleRtlFreeHeap(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // HeapHandle _ = ctx.Arg(1) // Flags addr := ctx.Arg(2) if addr != 0 { if ctx.State != nil && ctx.State.HeapFree != nil { ctx.State.HeapFree(addr) } else { ctx.Mem.Munmap(addr, mmu.PageSize) } } ctx.CPU.SetSyscallReturn(1) // TRUE ctx.ReturnOverridden = true return 0 } func handleRtlReAllocateHeap(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // HeapHandle flags := ctx.Arg(1) oldAddr := ctx.Arg(2) newSize := ctx.Arg(3) if newSize == 0 { newSize = 16 } var newAddr uint64 if ctx.State != nil && ctx.State.HeapAlloc != nil { zeroInit := flags&0x08 != 0 // HEAP_ZERO_MEMORY newAddr = ctx.State.HeapAlloc(newSize, zeroInit) } else { newAddr = ctx.Mem.Mmap(0, newSize, mmu.PermRead|mmu.PermWrite) } // Copy old data (best effort) if oldAddr != 0 && newAddr != 0 { copyLen := newSize if ctx.State != nil && ctx.State.HeapSize != nil { if old := ctx.State.HeapSize(oldAddr); old < copyLen { copyLen = old } } oldData, err := ctx.Mem.ReadBytes(oldAddr, int(copyLen)) if err == nil { ctx.Mem.WriteBytes(newAddr, oldData) } if ctx.State != nil && ctx.State.HeapFree != nil { ctx.State.HeapFree(oldAddr) } } ctx.CPU.SetSyscallReturn(newAddr) ctx.ReturnOverridden = true return 0 } func handleRtlSizeHeap(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // HeapHandle _ = ctx.Arg(1) // Flags ptr := ctx.Arg(2) var sz uint64 if ctx.State != nil && ctx.State.HeapSize != nil && ptr != 0 { sz = ctx.State.HeapSize(ptr) } else { sz = mmu.PageSize } ctx.CPU.SetSyscallReturn(sz) ctx.ReturnOverridden = true return 0 } // ======================== STRING / UNICODE ======================== func handleRtlInitUnicodeString(ctx *TrapContext) uint32 { destAddr := ctx.Arg(0) // PUNICODE_STRING srcAddr := ctx.Arg(1) // PCWSTR if destAddr == 0 { return StatusSuccess } if srcAddr == 0 { ctx.Mem.Write16(destAddr+0, 0) // Length ctx.Mem.Write16(destAddr+2, 0) // MaximumLength ctx.Mem.Write64(destAddr+8, 0) // Buffer return StatusSuccess } // Find length of UTF-16 string length := uint16(0) for i := uint64(0); i < 65536; i += 2 { ch, err := ctx.Mem.Read16(srcAddr + i) if err != nil || ch == 0 { break } length += 2 } ctx.Mem.Write16(destAddr+0, length) // Length (bytes, not including null) ctx.Mem.Write16(destAddr+2, length+2) // MaximumLength ctx.Mem.Write64(destAddr+8, srcAddr) // Buffer return StatusSuccess } // ======================== MISC ======================== func handleRtlGetVersion(ctx *TrapContext) uint32 { buffer := ctx.Arg(0) // PRTL_OSVERSIONINFOW if buffer != 0 { // RTL_OSVERSIONINFOEXW layout (284 bytes): // +0x00 DWORD dwOSVersionInfoSize // +0x04 DWORD dwMajorVersion // +0x08 DWORD dwMinorVersion // +0x0C DWORD dwBuildNumber // +0x10 DWORD dwPlatformId // +0x14 WCHAR szCSDVersion[128] (256 bytes, offsets 20..275) // +0x114 WORD wServicePackMajor (offset 276) // +0x116 WORD wServicePackMinor // +0x118 WORD wSuiteMask // +0x11A BYTE wProductType // +0x11B BYTE wReserved structSize, _ := ctx.Mem.Read32(buffer) ctx.Mem.Write32(buffer+4, 10) // dwMajorVersion ctx.Mem.Write32(buffer+8, 0) // dwMinorVersion ctx.Mem.Write32(buffer+12, 19041) // dwBuildNumber ctx.Mem.Write32(buffer+16, 2) // dwPlatformId = VER_PLATFORM_WIN32_NT // szCSDVersion[128]: empty string (null-terminate first WCHAR) ctx.Mem.Write16(buffer+20, 0) if structSize >= 284 { // RTL_OSVERSIONINFOEXW ctx.Mem.Write16(buffer+276, 0) // wServicePackMajor ctx.Mem.Write16(buffer+278, 0) // wServicePackMinor ctx.Mem.Write16(buffer+280, 0x300) // wSuiteMask: VER_SUITE_SINGLEUSERTS|VER_SUITE_TERMINAL ctx.Mem.Write8(buffer+282, 1) // wProductType = VER_NT_WORKSTATION ctx.Mem.Write8(buffer+283, 0) // wReserved } } return StatusSuccess } func handleRtlNtStatusToDosError(ctx *TrapContext) uint32 { status := uint32(ctx.Arg(0)) var dosError uint32 switch status { case StatusSuccess: dosError = 0 // ERROR_SUCCESS case StatusNoSuchFile, StatusObjectNameNotFound: dosError = 2 // ERROR_FILE_NOT_FOUND case StatusObjectPathNotFound: dosError = 3 // ERROR_PATH_NOT_FOUND case StatusAccessDenied: dosError = 5 // ERROR_ACCESS_DENIED case StatusInvalidHandle: dosError = 6 // ERROR_INVALID_HANDLE case StatusNoMemory: dosError = 8 // ERROR_NOT_ENOUGH_MEMORY case StatusInvalidParameter: dosError = 87 // ERROR_INVALID_PARAMETER case StatusFileLockConflict, StatusLockNotGranted: dosError = 33 // ERROR_LOCK_VIOLATION case StatusRangeNotLocked: dosError = 158 // ERROR_NOT_LOCKED case StatusPending: dosError = 997 // ERROR_IO_PENDING case StatusCancelled: dosError = 995 // ERROR_OPERATION_ABORTED default: // SXS/activation-context errors → ERROR_SXS_KEY_NOT_FOUND (14007) if status >= 0xC0150000 && status <= 0xC015FFFF { dosError = 14007 } else { dosError = 317 // ERROR_MR_MID_NOT_FOUND } } ctx.CPU.SetSyscallReturn(uint64(dosError)) return dosError } func handleRtlSetLastWin32Error(ctx *TrapContext) uint32 { errorCode := uint32(ctx.Arg(0)) if ctx.State != nil && ctx.State.TEBAddr != 0 { off := uint64(TebLastErrorValue) if ctx.State.Is32 { off = Teb32LastErrorValue } ctx.Mem.Write32(ctx.State.TEBAddr+off, errorCode) } return StatusSuccess } func handleRtlGetLastWin32Error(ctx *TrapContext) uint32 { if ctx.State != nil && ctx.State.TEBAddr != 0 { off := uint64(TebLastErrorValue) if ctx.State.Is32 { off = Teb32LastErrorValue } val, err := ctx.Mem.Read32(ctx.State.TEBAddr + off) if err == nil { ctx.CPU.SetSyscallReturn(uint64(val)) return val } } return 0 } func handleNtDuplicateObject(ctx *TrapContext) uint32 { _ = Handle(ctx.Arg(0)) // SourceProcessHandle _ = Handle(ctx.Arg(1)) // TargetProcessHandle (not used for same-process) srcHandle := Handle(ctx.Arg(2)) targetHandlePtr := ctx.Arg(3) _ = ctx.Arg(4) // DesiredAccess _ = ctx.Arg(5) // HandleAttributes options := uint32(ctx.Arg(6)) if !validateGuestWrite(ctx.Mem, targetHandlePtr, guestPtrSize(ctx.Mem)) { return StatusAccessViolation } status := StatusSuccess closeSource := options&dupCloseSource != 0 newHandle, source, objectRefs, ownerRefs, ok := ctx.Handles.Clone( srcHandle, closeSource, func(obj HandleObject) (HandleObject, bool) { var cloned HandleObject cloned, status = cloneHandleObject(ctx, obj) return cloned, status == StatusSuccess }, ) if !ok { if status != StatusSuccess { return status } return StatusInvalidHandle } writeGuestPtr(ctx.Mem, targetHandlePtr, uint64(newHandle)) if closeSource { closeHandleObject(ctx, srcHandle, source, objectRefs, ownerRefs) } _ = options & dupSameAccess return StatusSuccess } func handleNtQueryObject(ctx *TrapContext) uint32 { h := Handle(ctx.Arg(0)) infoClass := ctx.Arg(1) buffer := ctx.Arg(2) bufLen := ctx.Arg(3) retLen := ctx.Arg(4) obj := ctx.Handles.Get(h) typeName := "Unknown" name := "" if obj != nil { switch strings.ToLower(obj.HandleType()) { case "file": typeName = "File" if fh := ctx.Handles.GetFile(h); fh != nil { name = `\\??\\` + VFSToNtPath(fh.Path) } case "event": typeName = "Event" case "mutant": typeName = "Mutant" case "semaphore": typeName = "Semaphore" case "thread": typeName = "Thread" case "process": typeName = "Process" case "section": typeName = "Section" default: typeName = obj.HandleType() } if name == "" { if n, ok := ctx.Handles.NameOf(h); ok && n != "" { name = `\BaseNamedObjects\` + n } } } dataOff := uint64(16) if ctx.Mem.Mode32 { dataOff = 8 } switch infoClass { case 0: // ObjectBasicInformation (40 bytes) if retLen != 0 { ctx.Mem.Write32(retLen, 40) } if buffer == 0 || bufLen < 40 { return 0xC0000023 } ctx.Mem.Write32(buffer, 0) // Attributes ctx.Mem.Write32(buffer+4, 0xF) // GrantedAccess ctx.Mem.Write32(buffer+8, 1) // HandleCount ctx.Mem.Write32(buffer+12, 1) // PointerCount ctx.Mem.Write32(buffer+16, 0) // PagedPoolCharge ctx.Mem.Write32(buffer+20, 0) // NonPagedPoolCharge ctx.Mem.Write32(buffer+24, 0) // Reserved[0] ctx.Mem.Write32(buffer+28, 0) // Reserved[1] ctx.Mem.Write32(buffer+32, 0) // Reserved[2] ctx.Mem.Write64(buffer+36, 0) // NameInformationLength + TypeInformationLength return StatusSuccess case 1: // ObjectNameInformation (UNICODE_STRING + name) wsBytes := encodeUTF16LE(name) needed := uint64(dataOff + uint64(len(wsBytes)) + 2) if retLen != 0 { ctx.Mem.Write32(retLen, uint32(needed)) } if buffer == 0 { return StatusSuccess } if bufLen < needed { return StatusBufferTooSmall } strData := buffer + dataOff ctx.Mem.Write16(buffer, uint16(len(wsBytes))) ctx.Mem.Write16(buffer+2, uint16(len(wsBytes)+2)) if ctx.Mem.Mode32 { ctx.Mem.Write32(buffer+4, uint32(strData)) } else { ctx.Mem.Write32(buffer+4, 0) ctx.Mem.Write64(buffer+8, strData) } ctx.Mem.WriteBytes(strData, wsBytes) ctx.Mem.Write16(strData+uint64(len(wsBytes)), 0) return StatusSuccess case 2: // ObjectTypeInformation wsBytes := encodeUTF16LE(typeName) needed := uint64(72 + len(wsBytes) + 2) if retLen != 0 { ctx.Mem.Write32(retLen, uint32(needed)) } if buffer == 0 { return StatusSuccess } if bufLen < needed { return StatusBufferTooSmall } strData := buffer + dataOff ctx.Mem.Write16(buffer, uint16(len(wsBytes))) ctx.Mem.Write16(buffer+2, uint16(len(wsBytes)+2)) if ctx.Mem.Mode32 { ctx.Mem.Write32(buffer+4, uint32(strData)) } else { ctx.Mem.Write32(buffer+4, 0) ctx.Mem.Write64(buffer+8, strData) } ctx.Mem.WriteBytes(strData, wsBytes) ctx.Mem.Write16(strData+uint64(len(wsBytes)), 0) return StatusSuccess case 3: // ObjectTypesInformation // Return a tiny but non-empty type count to satisfy callers that check >0. needed := uint32(4) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if buffer == 0 { return StatusSuccess } if bufLen < uint64(needed) { return StatusBufferTooSmall } ctx.Mem.Write32(buffer, 1) return StatusSuccess case 4: // ObjectHandleFlagInformation if retLen != 0 { ctx.Mem.Write32(retLen, 2) } if buffer == 0 || bufLen < 2 { return StatusBufferTooSmall } flags := objectHandleFlags[h] if flags&1 != 0 { ctx.Mem.Write8(buffer+0, 1) // Inherit } else { ctx.Mem.Write8(buffer+0, 0) } if flags&2 != 0 { ctx.Mem.Write8(buffer+1, 1) // ProtectFromClose } else { ctx.Mem.Write8(buffer+1, 0) } return StatusSuccess } return StatusNotImplemented } // ======================== LDR (DLL LOADING) ======================== func handleLdrGetDllHandle(ctx *TrapContext) uint32 { // LdrGetDllHandle(PathToFile, Unused, ModuleName*, ModuleHandle*) // ModuleName is UNICODE_STRING*, ModuleHandle is HMODULE* moduleNamePtr := ctx.Arg(2) // UNICODE_STRING* handlePtr := ctx.Arg(3) // HMODULE* if moduleNamePtr == 0 || handlePtr == 0 { return StatusNotImplemented } nameStr := readGuestUnicodeString(ctx.Mem, moduleNamePtr) nameStr = strings.ToLower(nameStr) // Strip path, keep only filename if idx := strings.LastIndexAny(nameStr, "\\/"); idx >= 0 { nameStr = nameStr[idx+1:] } if !strings.HasSuffix(nameStr, ".dll") { nameStr += ".dll" } base := findModuleByName(ctx, nameStr) if base == 0 { writeGuestPointer(ctx.Mem, handlePtr, 0) return 0xC0000135 // STATUS_DLL_NOT_FOUND } writeGuestPointer(ctx.Mem, handlePtr, base) return StatusSuccess } func handleLdrGetProcedureAddress(ctx *TrapContext) uint32 { // LdrGetProcedureAddress(ModuleHandle, FunctionName*, Ordinal, FunctionAddress*) moduleHandle := ctx.Arg(0) // HMODULE (base address) funcNamePtr := ctx.Arg(1) // ANSI_STRING* ordinal := uint16(ctx.Arg(2)) funcAddrPtr := ctx.Arg(3) // PVOID* if funcAddrPtr != 0 { writeGuestPointer(ctx.Mem, funcAddrPtr, 0) } if moduleHandle == 0 { return StatusNotImplemented } var funcName string if funcNamePtr != 0 { funcName = readGuestAnsiString(ctx.Mem, funcNamePtr) } // Look up in the Dispatcher's synthetic export table addr := ctx.Disp.LookupExportByModule(moduleHandle, funcName, ordinal) if ctx.Trace { fmt.Printf(" LdrGetProcedureAddress: module=0x%x name=%q ordinal=%d → addr=0x%x\n", moduleHandle, funcName, ordinal, addr) } if addr != 0 && funcAddrPtr != 0 { writeGuestPointer(ctx.Mem, funcAddrPtr, addr) return StatusSuccess } return 0xC0000139 // STATUS_ENTRYPOINT_NOT_FOUND } // ======================== Helpers ======================== // writeIOStatus writes an IO_STATUS_BLOCK. func writeIOStatus(mem *mmu.MMU, addr uint64, status uint32, info uint64) { if addr == 0 { return } mem.Write32(addr, status) if mem.Mode32 { mem.Write32(addr+4, uint32(info)) return } mem.Write64(addr+8, info) } // windowsFileTime converts a time.Time to Windows FILETIME (100ns since 1601-01-01). func windowsFileTime(t time.Time) uint64 { const epochDiff = 11644473600 return uint64((t.Unix()+epochDiff)*10000000 + int64(t.Nanosecond()/100)) } // readGuestStringUTF16 reads a null-terminated UTF-16LE string from guest memory. func readGuestStringUTF16(mem *mmu.MMU, addr uint64, maxBytes int) string { if addr == 0 { return "" } data, err := mem.ReadBytes(addr, maxBytes) if err != nil { return "" } return decodeUTF16LE(data) } func readGuestAnsiString(mem *mmu.MMU, addr uint64) string { if mem == nil || addr == 0 { return "" } length, _ := mem.Read16(addr) var buffer uint64 if mem.Mode32 { ptr, _ := mem.Read32(addr + 4) buffer = uint64(ptr) } else { buffer, _ = mem.Read64(addr + 8) } if buffer == 0 || length == 0 { return "" } data, err := mem.ReadBytes(buffer, int(length)) if err != nil { return "" } return string(data) } func writeGuestPointer(mem *mmu.MMU, addr, value uint64) { if mem == nil || addr == 0 { return } if mem.Mode32 { _ = mem.Write32(addr, uint32(value)) return } _ = mem.Write64(addr, value) } // readGuestUnicodeString reads a UNICODE_STRING structure from guest memory. func readGuestUnicodeString(mem *mmu.MMU, addr uint64) string { if addr == 0 { return "" } length, _ := mem.Read16(addr + UnicodeStringLength) var bufPtr uint64 if mem.Mode32 { ptr32, _ := mem.Read32(addr + UnicodeString32Buffer) bufPtr = uint64(ptr32) } else { bufPtr, _ = mem.Read64(addr + UnicodeStringBuffer) } if bufPtr == 0 || length == 0 { return "" } return readGuestStringUTF16(mem, bufPtr, int(length)) } // findModuleByName searches PEB_LDR_DATA InLoadOrderModuleList for a DLL by name. func findModuleByName(ctx *TrapContext, name string) uint64 { if ctx == nil || ctx.Mem == nil || ctx.State == nil { return 0 } pebAddr := ctx.State.PEBAddr is32 := ctx.Mem.Mode32 || ctx.State.Is32 var ldrAddr uint64 if is32 { value, _ := ctx.Mem.Read32(pebAddr + Peb32Ldr) ldrAddr = uint64(value) } else { ldrAddr, _ = ctx.Mem.Read64(pebAddr + PebLdr) } if ldrAddr == 0 { return 0 } listOffset := uint64(LdrInLoadOrderModuleList) baseOffset := uint64(LdrEntryDllBase) nameOffset := uint64(LdrEntryBaseDllName) if is32 { listOffset = Ldr32InLoadOrderModuleList baseOffset = Ldr32EntryDllBase nameOffset = Ldr32EntryBaseDllName } listHead := ldrAddr + listOffset readPointer := func(addr uint64) uint64 { if is32 { value, _ := ctx.Mem.Read32(addr) return uint64(value) } value, _ := ctx.Mem.Read64(addr) return value } flink := readPointer(listHead) for flink != 0 && flink != listHead { entry := flink baseName := readGuestUnicodeString(ctx.Mem, entry+nameOffset) if strings.EqualFold(baseName, name) { return readPointer(entry + baseOffset) } flink = readPointer(flink) } return 0 } // writeGuestUTF16 writes a string as UTF-16LE with null terminator to guest memory. func writeGuestUTF16(mem *mmu.MMU, addr uint64, s string) int { utf16 := encodeUTF16LE(s) data := append(utf16, 0, 0) mem.WriteBytes(addr, data) return len(data) } // encode32LE encodes a uint32 as little-endian bytes. func encode32LE(val uint32) []byte { b := make([]byte, 4) binary.LittleEndian.PutUint32(b, val) return b } // ======================== Additional handlers for Wine DLL init ======================== func handleRtlGetCurrentPeb(ctx *TrapContext) uint32 { ctx.SetReturn(ctx.State.PEBAddr) return StatusSuccess } func handleRtlAcquirePebLock(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlReleasePebLock(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlInitAnsiString(ctx *TrapContext) uint32 { // RtlInitAnsiString(PANSI_STRING dest, PCSZ source) dest := ctx.Arg(0) src := ctx.Arg(1) if dest == 0 { return StatusSuccess } if src == 0 { ctx.Mem.Write16(dest+0, 0) // Length ctx.Mem.Write16(dest+2, 0) // MaximumLength ctx.Mem.Write64(dest+8, 0) // Buffer return StatusSuccess } // Measure string length length := uint16(0) for { b, err := ctx.Mem.Read8(src + uint64(length)) if err != nil || b == 0 { break } length++ } ctx.Mem.Write16(dest+0, length) ctx.Mem.Write16(dest+2, length+1) ctx.Mem.Write64(dest+8, src) return StatusSuccess } func handleRtlInitUnicodeStringEx(ctx *TrapContext) uint32 { // Same as RtlInitUnicodeString but returns NTSTATUS handleRtlInitUnicodeString(ctx) return StatusSuccess } func handleRtlMultiByteToUnicodeN(ctx *TrapContext) uint32 { // RtlMultiByteToUnicodeN(wchar* dst, ULONG dstMax, ULONG* resultLen, char* src, ULONG srcLen) dst := ctx.Arg(0) dstMax := uint32(ctx.Arg(1)) resultLenPtr := ctx.Arg(2) src := ctx.Arg(3) srcLen := uint32(ctx.Arg(4)) written := uint32(0) for i := uint32(0); i < srcLen && written+2 <= dstMax; i++ { b, err := ctx.Mem.Read8(src + uint64(i)) if err != nil { break } ctx.Mem.Write16(dst+uint64(written), uint16(b)) written += 2 } if resultLenPtr != 0 { ctx.Mem.Write32(resultLenPtr, written) } return StatusSuccess } func handleRtlMultiByteToUnicodeSize(ctx *TrapContext) uint32 { // RtlMultiByteToUnicodeSize(ULONG* resultSize, char* src, ULONG srcLen) resultPtr := ctx.Arg(0) srcLen := uint32(ctx.Arg(2)) if resultPtr != 0 { ctx.Mem.Write32(resultPtr, srcLen*2) } return StatusSuccess } func handleRtlUnicodeToMultiByteN(ctx *TrapContext) uint32 { // RtlUnicodeToMultiByteN(char* dst, ULONG dstMax, ULONG* resultLen, wchar* src, ULONG srcLen) dst := ctx.Arg(0) dstMax := uint32(ctx.Arg(1)) resultLenPtr := ctx.Arg(2) src := ctx.Arg(3) srcLen := uint32(ctx.Arg(4)) written := uint32(0) for i := uint32(0); i+2 <= srcLen && written < dstMax; i += 2 { ch, err := ctx.Mem.Read16(src + uint64(i)) if err != nil { break } if ch > 127 { ch = '?' } ctx.Mem.Write8(dst+uint64(written), uint8(ch)) written++ } if resultLenPtr != 0 { ctx.Mem.Write32(resultLenPtr, written) } return StatusSuccess } func handleRtlUnicodeToMultiByteSize(ctx *TrapContext) uint32 { resultPtr := ctx.Arg(0) srcLen := uint32(ctx.Arg(2)) if resultPtr != 0 { ctx.Mem.Write32(resultPtr, srcLen/2) } return StatusSuccess } func handleRtlAnsiStringToUnicodeString(ctx *TrapContext) uint32 { // RtlAnsiStringToUnicodeString(PUNICODE_STRING dest, PANSI_STRING src, BOOLEAN alloc) dest := ctx.Arg(0) src := ctx.Arg(1) allocate := ctx.Arg(2) != 0 if dest == 0 || src == 0 { return StatusInvalidParameter } srcLen, _ := ctx.Mem.Read16(src + 0) srcBuf, _ := ctx.Mem.Read64(src + 8) uniLen := uint16(srcLen) * 2 uniMaxLen := uniLen + 2 var uniBuf uint64 if allocate { uniBuf = ctx.Mem.Mmap(0, uint64(uniMaxLen), mmu.PermRead|mmu.PermWrite) } else { uniBuf, _ = ctx.Mem.Read64(dest + 8) } for i := uint16(0); i < srcLen; i++ { b, _ := ctx.Mem.Read8(srcBuf + uint64(i)) ctx.Mem.Write16(uniBuf+uint64(i)*2, uint16(b)) } ctx.Mem.Write16(uniBuf+uint64(srcLen)*2, 0) // null terminator ctx.Mem.Write16(dest+0, uniLen) ctx.Mem.Write16(dest+2, uniMaxLen) ctx.Mem.Write64(dest+8, uniBuf) return StatusSuccess } func handleRtlUnicodeStringToAnsiString(ctx *TrapContext) uint32 { dest := ctx.Arg(0) src := ctx.Arg(1) allocate := ctx.Arg(2) != 0 if dest == 0 || src == 0 { return StatusInvalidParameter } srcLen, _ := ctx.Mem.Read16(src + 0) srcBuf, _ := ctx.Mem.Read64(src + 8) ansiLen := srcLen / 2 ansiMaxLen := ansiLen + 1 var ansiBuf uint64 if allocate { ansiBuf = ctx.Mem.Mmap(0, uint64(ansiMaxLen), mmu.PermRead|mmu.PermWrite) } else { ansiBuf, _ = ctx.Mem.Read64(dest + 8) } for i := uint16(0); i < ansiLen; i++ { ch, _ := ctx.Mem.Read16(srcBuf + uint64(i)*2) if ch > 127 { ch = '?' } ctx.Mem.Write8(ansiBuf+uint64(i), uint8(ch)) } ctx.Mem.Write8(ansiBuf+uint64(ansiLen), 0) ctx.Mem.Write16(dest+0, ansiLen) ctx.Mem.Write16(dest+2, ansiMaxLen) ctx.Mem.Write64(dest+8, ansiBuf) return StatusSuccess } func handleRtlFreeAnsiString(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCreateUnicodeString(ctx *TrapContext) uint32 { // RtlCreateUnicodeString(PUNICODE_STRING dest, PCWSTR src) dest := ctx.Arg(0) src := ctx.Arg(1) if dest == 0 || src == 0 { ctx.SetReturn(0) // FALSE return StatusSuccess } // Measure wide string length := uint64(0) for { ch, err := ctx.Mem.Read16(src + length*2) if err != nil || ch == 0 { break } length++ } byteLen := uint16(length * 2) bufSize := uint64(byteLen + 2) buf := ctx.Mem.Mmap(0, bufSize, mmu.PermRead|mmu.PermWrite) // Copy for i := uint64(0); i < length; i++ { ch, _ := ctx.Mem.Read16(src + i*2) ctx.Mem.Write16(buf+i*2, ch) } ctx.Mem.Write16(buf+length*2, 0) // null terminator ctx.Mem.Write16(dest+0, byteLen) ctx.Mem.Write16(dest+2, byteLen+2) ctx.Mem.Write64(dest+8, buf) ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlCompareUnicodeStrings(ctx *TrapContext) uint32 { s1 := ctx.Arg(0) l1 := uint32(ctx.Arg(1)) s2 := ctx.Arg(2) l2 := uint32(ctx.Arg(3)) caseInsensitive := ctx.Arg(4) != 0 minLen := l1 if l2 < minLen { minLen = l2 } for i := uint32(0); i < minLen; i++ { c1, _ := ctx.Mem.Read16(s1 + uint64(i)*2) c2, _ := ctx.Mem.Read16(s2 + uint64(i)*2) if caseInsensitive { if c1 >= 'A' && c1 <= 'Z' { c1 += 32 } if c2 >= 'A' && c2 <= 'Z' { c2 += 32 } } if c1 != c2 { if c1 < c2 { ctx.SetReturn(^uint64(0)) // -1 } else { ctx.SetReturn(1) } return StatusSuccess } } if l1 == l2 { ctx.SetReturn(0) } else if l1 < l2 { ctx.SetReturn(^uint64(0)) } else { ctx.SetReturn(1) } return StatusSuccess } func handleRtlImageNtHeader(ctx *TrapContext) uint32 { base := ctx.Arg(0) if base == 0 { ctx.SetReturn(0) return StatusSuccess } // Read e_lfanew lfanew, err := ctx.Mem.Read32(base + 0x3C) if err != nil { ctx.SetReturn(0) return StatusSuccess } ntHeader := base + uint64(lfanew) sig, err := ctx.Mem.Read32(ntHeader) if err != nil || sig != 0x00004550 { ctx.SetReturn(0) return StatusSuccess } ctx.SetReturn(ntHeader) return StatusSuccess } func handleRtlImageDirectoryEntryToData(ctx *TrapContext) uint32 { base := ctx.Arg(0) _ = ctx.Arg(1) // MappedAsImage dirIdx := ctx.Arg(2) sizePtr := ctx.Arg(3) if base == 0 { ctx.SetReturn(0) return StatusSuccess } lfanew, err := ctx.Mem.Read32(base + 0x3C) if err != nil { ctx.SetReturn(0) return StatusSuccess } optHdr := base + uint64(lfanew) + 4 + 20 magic, _ := ctx.Mem.Read16(optHdr) var dataDirOff uint64 if magic == 0x20B { // PE32+ dataDirOff = optHdr + 112 } else { dataDirOff = optHdr + 96 } entryOff := dataDirOff + dirIdx*8 rva, _ := ctx.Mem.Read32(entryOff) size, _ := ctx.Mem.Read32(entryOff + 4) if rva == 0 || size == 0 { ctx.SetReturn(0) return StatusSuccess } if sizePtr != 0 { ctx.Mem.Write32(sizePtr, size) } ctx.SetReturn(base + uint64(rva)) return StatusSuccess } func handleRtlGetNativeSystemInformation(ctx *TrapContext) uint32 { return handleNtQuerySystemInformation(ctx) } func handleNtGetTickCount(ctx *TrapContext) uint32 { ctx.SetReturn(uint64(time.Now().UnixMilli()) & 0xFFFFFFFF) return StatusSuccess } func handleNtFlushInstructionCache(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtGetContextThread(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtSetContextThread(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtResumeThread(ctx *TrapContext) uint32 { prevCount := ctx.Arg(1) if prevCount != 0 { ctx.Mem.Write32(prevCount, 1) } return StatusSuccess } func handleNtSuspendThread(ctx *TrapContext) uint32 { prevCount := ctx.Arg(1) if prevCount != 0 { ctx.Mem.Write32(prevCount, 0) } return StatusSuccess } func handleNtOpenProcessToken(ctx *TrapContext) uint32 { tokenHandle := ctx.Arg(2) if tokenHandle != 0 { h := ctx.Handles.Alloc(NewDefaultTokenHandle()) ctx.Mem.Write64(tokenHandle, uint64(h)) } return StatusSuccess } func handleNtOpenThreadToken(ctx *TrapContext) uint32 { tokenHandle := ctx.Arg(2) if tokenHandle != 0 { h := ctx.Handles.Alloc(NewDefaultTokenHandle()) ctx.Mem.Write64(tokenHandle, uint64(h)) } return StatusSuccess } func handleNtQueryInformationToken(ctx *TrapContext) uint32 { token := ctx.Handles.GetToken(Handle(ctx.Arg(0))) if token == nil { token = NewDefaultTokenHandle() } infoClass := uint32(ctx.Arg(1)) outBuf := ctx.Arg(2) outLen := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) switch infoClass { case 1: // TokenUser - write a minimal SID sid := token.UserSID if len(sid) == 0 { sid = NewDefaultTokenHandle().UserSID } needed := uint32(16 + len(sid)) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if outLen < needed { return 0xC0000023 // STATUS_BUFFER_TOO_SMALL } if outBuf != 0 { // TOKEN_USER: SID_AND_ATTRIBUTES (pointer + attributes) sidAddr := outBuf + 16 ctx.Mem.Write64(outBuf, sidAddr) // SID pointer ctx.Mem.Write32(outBuf+8, 0) // Attributes ctx.Mem.WriteBytes(sidAddr, sid) } return StatusSuccess case 25: // TokenElevation if retLen != 0 { ctx.Mem.Write32(retLen, 4) } if outLen >= 4 && outBuf != 0 { if token.Elevated { ctx.Mem.Write32(outBuf, 1) } else { ctx.Mem.Write32(outBuf, 0) } } return StatusSuccess default: if retLen != 0 { ctx.Mem.Write32(retLen, 0) } return StatusNotImplemented } } func handleNtAllocateLocallyUniqueId(ctx *TrapContext) uint32 { luid := ctx.Arg(0) if luid != 0 { id := uint64(time.Now().UnixNano() & 0xFFFFFFFF) ctx.Mem.Write32(luid, uint32(id)) // LowPart ctx.Mem.Write32(luid+4, 0) // HighPart } return StatusSuccess } func handleNtQueryDefaultLocale(ctx *TrapContext) uint32 { localePtr := ctx.Arg(1) if localePtr != 0 { ctx.Mem.Write32(localePtr, 0x0409) // en-US } return StatusSuccess } func handleNtQueryDefaultUILanguage(ctx *TrapContext) uint32 { langPtr := ctx.Arg(0) if langPtr != 0 { ctx.Mem.Write16(langPtr, 0x0409) // en-US } return StatusSuccess } func handleNtQueryInstallUILanguage(ctx *TrapContext) uint32 { langPtr := ctx.Arg(0) if langPtr != 0 { ctx.Mem.Write16(langPtr, 0x0409) } return StatusSuccess } func handleRtlGetProcessHeaps(ctx *TrapContext) uint32 { // RtlGetProcessHeaps(ULONG count, PVOID* heaps) count := uint32(ctx.Arg(0)) heaps := ctx.Arg(1) // Read process heap from PEB processHeap, _ := ctx.Mem.Read64(ctx.State.PEBAddr + PebProcessHeap) if count > 0 && heaps != 0 && processHeap != 0 { ctx.Mem.Write64(heaps, processHeap) } ctx.SetReturn(1) // 1 heap return StatusSuccess } func handleRtlLockHeap(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlUnlockHeap(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlValidateHeap(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlSetHeapInformation(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlQueryHeapInformation(ctx *TrapContext) uint32 { retLen := ctx.Arg(4) if retLen != 0 { ctx.Mem.Write32(retLen, 0) } return StatusSuccess } func handleRtlGetUserInfoHeap(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlSetUserValueHeap(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCompactHeap(ctx *TrapContext) uint32 { ctx.SetReturn(0) return StatusSuccess } func handleRtlWalkHeap(ctx *TrapContext) uint32 { return 0xC0000225 // STATUS_NO_MORE_ENTRIES } func handleRtlInitCodePageTable(ctx *TrapContext) uint32 { nlsPtr := ctx.Arg(0) // USHORT* - pointer to raw NLS data infoPtr := ctx.Arg(1) // CPTABLEINFO* - output struct if nlsPtr == 0 || infoPtr == 0 { return StatusSuccess } initCodePageTableAt(ctx, nlsPtr, infoPtr) return StatusSuccess } // handleRtlInitNlsTables initializes NLSTABLEINFO from raw NLS data pointers. // VOID RtlInitNlsTables(PUSHORT AnsiNlsBase, PUSHORT OemNlsBase, PUSHORT CaseTableNlsBase, PNLSTABLEINFO TableInfo) func handleRtlInitNlsTables(ctx *TrapContext) uint32 { ansiPtr := ctx.Arg(0) oemPtr := ctx.Arg(1) casePtr := ctx.Arg(2) infoPtr := ctx.Arg(3) if infoPtr == 0 { return StatusSuccess } // NLSTABLEINFO layout (x64): // +0x00 CPTABLEINFO OemTableInfo (0x40 bytes) // +0x40 CPTABLEINFO AnsiTableInfo (0x40 bytes) // +0x80 PUSHORT UpperCaseTable // +0x88 PUSHORT LowerCaseTable if ansiPtr != 0 { initCodePageTableAt(ctx, ansiPtr, infoPtr+0x40) } if oemPtr != 0 { initCodePageTableAt(ctx, oemPtr, infoPtr) } if casePtr != 0 { ctx.Mem.Write64(infoPtr+0x80, casePtr) ctx.Mem.Write64(infoPtr+0x88, casePtr) // LowerCaseTable same as Upper for now } return StatusSuccess } // initCodePageTableAt is the core logic of RtlInitCodePageTable. func initCodePageTableAt(ctx *TrapContext, nlsPtr, infoPtr uint64) { read16 := func(off uint64) uint16 { v, _ := ctx.Mem.Read16(nlsPtr + off*2) return v } cp := read16(1) ctx.Mem.Write16(infoPtr+0x00, cp) ctx.Mem.Write16(infoPtr+0x02, read16(2)) ctx.Mem.Write16(infoPtr+0x04, read16(3)) ctx.Mem.Write16(infoPtr+0x06, read16(4)) ctx.Mem.Write16(infoPtr+0x08, read16(5)) ctx.Mem.Write16(infoPtr+0x0A, read16(6)) ctx.Mem.Write16(infoPtr+0x0C, 0) for i := uint64(0); i < 12; i++ { b, _ := ctx.Mem.Read8(nlsPtr + 7*2 + i) ctx.Mem.Write8(infoPtr+0x0E+i, b) } headerSize := read16(0) mbTableAddr := nlsPtr + uint64(headerSize+1)*2 ctx.Mem.Write64(infoPtr+0x20, mbTableAddr) glyphSizeAddr := mbTableAddr + 256*2 glyphSize, _ := ctx.Mem.Read16(glyphSizeAddr) glyphEndAddr := glyphSizeAddr + uint64(glyphSize+1)*2 ctx.Mem.Write64(infoPtr+0x30, glyphEndAddr) dbcsMarker, _ := ctx.Mem.Read16(glyphEndAddr) if dbcsMarker != 0 { ctx.Mem.Write16(infoPtr+0x0C, cp) } wcTableAddr := glyphEndAddr + 2 ctx.Mem.Write64(infoPtr+0x28, wcTableAddr) ctx.Mem.Write64(infoPtr+0x38, 0) } // handleRtlResetRtlTranslations sets PEB NLS fields from NLSTABLEINFO. // VOID RtlResetRtlTranslations(PNLSTABLEINFO TableInfo) func handleRtlResetRtlTranslations(ctx *TrapContext) uint32 { infoPtr := ctx.Arg(0) if infoPtr == 0 || ctx.State == nil { return StatusSuccess } peb := ctx.State.PEBAddr // AnsiTableInfo.MultiByteTable is at infoPtr+0x40+0x20 // PEB.AnsiCodePageData = MultiByteTable - 1 (pointer to USHORT before table) ansiMBTable, _ := ctx.Mem.Read64(infoPtr + 0x40 + 0x20) if ansiMBTable > 2 { ctx.Mem.Write64(peb+PebAnsiCodePageData, ansiMBTable-2) } // OemTableInfo.MultiByteTable is at infoPtr+0x00+0x20 oemMBTable, _ := ctx.Mem.Read64(infoPtr + 0x20) if oemMBTable > 2 { ctx.Mem.Write64(peb+PebOemCodePageData, oemMBTable-2) } // UpperCaseTable is at infoPtr+0x80 ucTable, _ := ctx.Mem.Read64(infoPtr + 0x80) if ucTable != 0 { ctx.Mem.Write64(peb+PebUnicodeCaseTableData, ucTable) } return StatusSuccess } func handleRtlFlsAlloc(ctx *TrapContext) uint32 { // RtlFlsAlloc(callback, *index_out) // RCX = callback (ignored), RDX = pointer to output index indexPtr := ctx.Arg(1) // Use PEB FlsBitmap to allocate an index pebAddr := ctx.State.PEBAddr flsBitmapPtr, _ := ctx.Mem.Read64(pebAddr + PebFlsBitmap) if flsBitmapPtr == 0 { if indexPtr != 0 { ctx.Mem.Write32(indexPtr, 0xFFFFFFFF) } return 0xC0000017 // STATUS_NO_MEMORY } // RtlFindClearBitsAndSet on the FLS bitmap sizeOfBitMap, _ := ctx.Mem.Read32(flsBitmapPtr) bufferAddr, _ := ctx.Mem.Read64(flsBitmapPtr + 8) idx := uint32(0xFFFFFFFF) for i := uint32(0); i < sizeOfBitMap; i++ { wordIdx := i / 32 bitIdx := i % 32 word, _ := ctx.Mem.Read32(bufferAddr + uint64(wordIdx)*4) if word&(1< 0 { if rw, ok := ctx.CPU.(RegisterWriter); ok { rw.WriteReg(regRCX, ctx.Arg(0)) // PEXCEPTION_RECORD } for _, h := range handlers { if h == nil || h.Addr == 0 { continue } rsp := ctx.CPU.GetSP() if rsp >= 8 { rsp -= 8 ctx.Mem.Write64(rsp, ctx.CPU.GetPC()) ctx.CPU.SetSP(rsp) ctx.CPU.SetPC(h.Addr) return StatusSuccess } } } return StatusSuccess } func handleRtlRaiseStatus(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAddVectoredExceptionHandler(ctx *TrapContext) uint32 { first := ctx.Arg(0) != 0 handler := ctx.Arg(1) h := GlobalVEH.Add(handler, first) ctx.SetReturn(h) return StatusSuccess } func handleRtlRemoveVectoredExceptionHandler(ctx *TrapContext) uint32 { GlobalVEH.Remove(ctx.Arg(0)) ctx.SetReturn(1) return StatusSuccess } func handleRtlPcToFileHeader(ctx *TrapContext) uint32 { ctx.SetReturn(0) return StatusSuccess } func handleRtlGetEnabledExtendedFeatures(ctx *TrapContext) uint32 { ctx.SetReturn(0) // no extended features return StatusSuccess } func handleRtlSetUnhandledExceptionFilter(ctx *TrapContext) uint32 { if ctx.State != nil { prev := ctx.State.UnhandledExceptionFilter ctx.State.UnhandledExceptionFilter = ctx.Arg(0) ctx.SetReturn(prev) } return StatusSuccess } func handleNtCreateSemaphore(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) initialCount := int32(ctx.Arg(3)) maxCount := int32(ctx.Arg(4)) if maxCount <= 0 { maxCount = 0x7fffffff } sem := NewSemaphore(initialCount, maxCount) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } var h Handle if name != "" { var isNew bool h, isNew = ctx.Handles.AllocNamed(name, sem) if !isNew { if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return 0x40000000 // STATUS_OBJECT_NAME_EXISTS } } else { h = ctx.Handles.Alloc(sem) } if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtReleaseSemaphore(ctx *TrapContext) uint32 { // NtReleaseSemaphore(SemaphoreHandle, ReleaseCount, PreviousCount*) h := Handle(ctx.Arg(0)) releaseCount := int32(ctx.Arg(1)) pPrevCount := ctx.Arg(2) if releaseCount <= 0 { releaseCount = 1 } if sh := ctx.Handles.GetSemaphore(h); sh != nil { prev, _ := sh.Release(releaseCount) if pPrevCount != 0 { ctx.Mem.Write32(pPrevCount, uint32(prev)) } } return StatusSuccess } func handleNtOpenEvent(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } h := ctx.Handles.GetNamed(name) if h == 0 { return StatusObjectNameNotFound } if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtOpenMutant(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } h := ctx.Handles.GetNamed(name) if h == 0 { return StatusObjectNameNotFound } if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtOpenSemaphore(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) _ = ctx.Arg(1) // DesiredAccess objAttr := ctx.Arg(2) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } h := ctx.Handles.GetNamed(name) if h == 0 { return StatusObjectNameNotFound } if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtOpenSection(ctx *TrapContext) uint32 { return 0xC0000034 } func handleNtQueryAttributesFile(ctx *TrapContext) uint32 { return 0xC0000034 // not found } func handleNtOpenProcess(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if handlePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "Process"}) ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtSetInformationProcess(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtSetInformationThread(ctx *TrapContext) uint32 { threadHandle := ctx.Handles.GetThread(Handle(ctx.Arg(0))) infoClass := uint32(ctx.Arg(1)) switch infoClass { case 2, 3: if threadHandle != nil { threadHandle.Priority = int32(ctx.Arg(2)) } return StatusSuccess case 0x11: if threadHandle != nil { threadHandle.HideFromDebugger = true } return StatusSuccess case 0, 1: return StatusSuccess default: return StatusInvalidParameter } } func handleNtOpenThread(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if handlePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "Thread"}) ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtAlertThread(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtAlertResumeThread(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtTestAlert(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtCreateTimer(ctx *TrapContext) uint32 { // NtCreateTimer(TimerHandle*, DesiredAccess, ObjectAttributes*, TimerType) handlePtr := ctx.Arg(0) objAttr := ctx.Arg(2) timerType := ctx.Arg(3) // 0=NotificationTimer(manual), 1=SynchronizationTimer(auto) manualReset := timerType == 0 timer := NewWaitableTimer(manualReset) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } var h Handle if name != "" { var isNew bool h, isNew = ctx.Handles.AllocNamed(name, timer) if !isNew { if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return 0x40000000 } } else { h = ctx.Handles.Alloc(timer) } if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtSetTimer(ctx *TrapContext) uint32 { // NtSetTimer(TimerHandle, DueTime*, ApcRoutine, ApcContext, Resume, Period, PreviousState*) h := Handle(ctx.Arg(0)) dueTimePtr := ctx.Arg(1) period := int32(ctx.Arg(5)) // ms prevStatePtr := ctx.Arg(6) wt, ok := ctx.Handles.Get(h).(*WaitableTimerHandle) if !ok { return StatusInvalidHandle } if prevStatePtr != 0 { ctx.Mem.Write8(prevStatePtr, boolToByte(wt.IsSignaled())) } var dueNs int64 if dueTimePtr != 0 { dueRaw, _ := ctx.Mem.Read64(dueTimePtr) dueInt := int64(dueRaw) if dueInt < 0 { // Relative time: negative 100-ns intervals dueNs = (-dueInt) * 100 } else if dueInt > 0 { // Absolute FILETIME: convert to duration from now nowFT := uint64(time.Now().UnixNano()/100 + 116444736000000000) if uint64(dueInt) > nowFT { dueNs = int64(uint64(dueInt)-nowFT) * 100 } else { dueNs = 0 } } } due := time.Duration(dueNs) if due < 0 { due = 0 } var periodDur time.Duration if period > 0 { periodDur = time.Duration(period) * time.Millisecond } wt.Set(due, periodDur) return StatusSuccess } func handleNtCancelTimer(ctx *TrapContext) uint32 { h := Handle(ctx.Arg(0)) prevStatePtr := ctx.Arg(1) wt, ok := ctx.Handles.Get(h).(*WaitableTimerHandle) if !ok { return StatusInvalidHandle } if prevStatePtr != 0 { ctx.Mem.Write8(prevStatePtr, boolToByte(wt.IsSignaled())) } wt.Cancel() return StatusSuccess } func handleNtQueryTimer(ctx *TrapContext) uint32 { // NtQueryTimer(TimerHandle, TimerInformationClass, TimerInformation*, Length, ReturnLength*) h := Handle(ctx.Arg(0)) buffer := ctx.Arg(2) retLen := ctx.Arg(4) wt, ok := ctx.Handles.Get(h).(*WaitableTimerHandle) const needed = 16 // TIMER_BASIC_INFORMATION: RemainingTime(8) + TimerState(4) + pad(4) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if buffer != 0 { ctx.Mem.Write64(buffer+0, 0) // RemainingTime (unknown without real tracking) if ok && wt.IsSignaled() { ctx.Mem.Write32(buffer+8, 1) } else { ctx.Mem.Write32(buffer+8, 0) } } return StatusSuccess } func boolToByte(b bool) uint8 { if b { return 1 } return 0 } var rtlCurrentDirectory = `C:\` var rtlProcessEnv = map[string]string{ "SYSTEMROOT": `C:\Windows`, "WINDIR": `C:\Windows`, "TEMP": `C:\Windows\Temp`, "TMP": `C:\Windows\Temp`, "COMSPEC": `C:\Windows\System32\cmd.exe`, } var rtlEnvBlocks = map[uint64]map[string]string{} func normalizeDosPath(path string) string { p := strings.TrimSpace(path) if p == "" { return rtlCurrentDirectory } p = strings.ReplaceAll(p, "/", `\`) if strings.HasPrefix(p, `\??\`) { return p } if strings.HasPrefix(p, `\\?\`) { return `\??\` + strings.TrimPrefix(p, `\\?\`) } if strings.HasPrefix(p, `\\`) { return `\??\UNC\` + strings.TrimPrefix(p, `\\`) } if strings.HasPrefix(p, `\`) { drive := "C:" if len(rtlCurrentDirectory) >= 2 && rtlCurrentDirectory[1] == ':' { drive = rtlCurrentDirectory[:2] } p = drive + p } else if !(len(p) >= 2 && p[1] == ':') { base := rtlCurrentDirectory if base == "" { base = `C:\` } if !strings.HasSuffix(base, `\`) { base += `\` } p = base + p } return p } func dosPathToNtPath(path string) string { p := normalizeDosPath(path) if strings.HasPrefix(p, `\??\`) || strings.HasPrefix(p, `\Device\`) { return p } return `\??\` + p } func normalizeEnvKey(name string) string { return strings.ToUpper(strings.TrimSpace(name)) } func cloneEnv(src map[string]string) map[string]string { dst := make(map[string]string, len(src)) for k, v := range src { dst[k] = v } return dst } func lookupEnvMap(envBlock uint64) map[string]string { if envBlock != 0 { if m, ok := rtlEnvBlocks[envBlock]; ok { return m } } return rtlProcessEnv } func lookupEnvValue(envBlock uint64, name string) (string, bool) { key := normalizeEnvKey(name) if key == "" { return "", false } if v, ok := lookupEnvMap(envBlock)[key]; ok { return v, true } if v, ok := rtlProcessEnv[key]; ok { return v, true } if v := os.Getenv(key); v != "" { return v, true } return "", false } func expandEnvVars(envBlock uint64, src string) string { var b strings.Builder for i := 0; i < len(src); { if src[i] != '%' { b.WriteByte(src[i]) i++ continue } j := strings.IndexByte(src[i+1:], '%') if j < 0 { b.WriteByte(src[i]) i++ continue } j = i + 1 + j name := src[i+1 : j] if val, ok := lookupEnvValue(envBlock, name); ok { b.WriteString(val) } else { b.WriteString(src[i : j+1]) } i = j + 1 } return b.String() } func handleLdrLoadDll(ctx *TrapContext) uint32 { // LdrLoadDll(path, flags, moduleName*, handle*) handlePtr := ctx.Arg(3) if handlePtr != 0 { writeGuestPointer(ctx.Mem, handlePtr, 0) } return StatusNotImplemented } func handleLdrGetDllHandleEx(ctx *TrapContext) uint32 { // LdrGetDllHandleEx(flags, path, unused, moduleName*, handle*) _ = ctx.Arg(0) // flags moduleNamePtr := ctx.Arg(3) // UNICODE_STRING* handlePtr := ctx.Arg(4) if handlePtr != 0 { writeGuestPointer(ctx.Mem, handlePtr, 0) } if moduleNamePtr == 0 { return 0xC0000135 // STATUS_DLL_NOT_FOUND } nameStr := readGuestUnicodeString(ctx.Mem, moduleNamePtr) nameStr = strings.ToLower(nameStr) if idx := strings.LastIndexAny(nameStr, "\\/"); idx >= 0 { nameStr = nameStr[idx+1:] } if !strings.HasSuffix(nameStr, ".dll") { nameStr += ".dll" } base := findModuleByName(ctx, nameStr) if base == 0 { return 0xC0000135 // STATUS_DLL_NOT_FOUND } if handlePtr != 0 { writeGuestPointer(ctx.Mem, handlePtr, base) } return StatusSuccess } func handleLdrAddRefDll(ctx *TrapContext) uint32 { return StatusSuccess } func handleLdrGetDllPath(ctx *TrapContext) uint32 { dllPathPtr := ctx.Arg(2) searchPathsPtr := ctx.Arg(3) if dllPathPtr != 0 { writeGuestPointer(ctx.Mem, dllPathPtr, 0) } if searchPathsPtr != 0 { writeGuestPointer(ctx.Mem, searchPathsPtr, 0) } return StatusSuccess } func handleLdrGetDllFullName(ctx *TrapContext) uint32 { return StatusNotImplemented } func handleRtlDosPathNameToNtPathName_U(ctx *TrapContext) uint32 { dosName := readGuestStringUTF16(ctx.Mem, ctx.Arg(0), 4096) ntNameUS := ctx.Arg(1) filePartPtr := ctx.Arg(2) if ntNameUS == 0 || dosName == "" { ctx.SetReturn(0) return StatusSuccess } ntPath := dosPathToNtPath(dosName) ntBytes := encodeUTF16LE(ntPath) buf := ctx.Mem.Mmap(0, uint64(len(ntBytes)+2), mmu.PermRead|mmu.PermWrite) ctx.Mem.WriteBytes(buf, ntBytes) ctx.Mem.Write16(buf+uint64(len(ntBytes)), 0) ctx.Mem.Write16(ntNameUS+UnicodeStringLength, uint16(len(ntBytes))) ctx.Mem.Write16(ntNameUS+UnicodeStringMaxLength, uint16(len(ntBytes)+2)) ctx.Mem.Write64(ntNameUS+UnicodeStringBuffer, buf) if filePartPtr != 0 { lastSlash := strings.LastIndex(ntPath, `\`) if lastSlash >= 0 && lastSlash+1 < len(ntPath) { offsetBytes := uint64(len(encodeUTF16LE(ntPath[:lastSlash+1]))) ctx.Mem.Write64(filePartPtr, buf+offsetBytes) } else { ctx.Mem.Write64(filePartPtr, 0) } } ctx.SetReturn(1) return StatusSuccess } func handleRtlDosPathNameToNtPathName_U_WithStatus(ctx *TrapContext) uint32 { _ = handleRtlDosPathNameToNtPathName_U(ctx) return StatusSuccess } func handleRtlGetCurrentDirectory_U(ctx *TrapContext) uint32 { bufLen := uint32(ctx.Arg(0)) buf := ctx.Arg(1) cwd := rtlCurrentDirectory if cwd == "" { cwd = `C:\` } if buf != 0 && bufLen >= uint32(len(cwd)*2+2) { for i, ch := range []rune(cwd) { ctx.Mem.Write16(buf+uint64(i)*2, uint16(ch)) } ctx.Mem.Write16(buf+uint64(len(cwd))*2, 0) } ctx.SetReturn(uint64(len(cwd) * 2)) return StatusSuccess } func handleRtlSetCurrentDirectory_U(ctx *TrapContext) uint32 { pathUS := ctx.Arg(0) path := readGuestUnicodeString(ctx.Mem, pathUS) if path == "" { return StatusInvalidParameter } rtlCurrentDirectory = normalizeDosPath(path) if strings.HasPrefix(rtlCurrentDirectory, `\??\`) { rtlCurrentDirectory = strings.TrimPrefix(rtlCurrentDirectory, `\??\`) } return StatusSuccess } func handleRtlGetFullPathName_U(ctx *TrapContext) uint32 { fileName := readGuestStringUTF16(ctx.Mem, ctx.Arg(0), 4096) bufLen := uint32(ctx.Arg(1)) buf := ctx.Arg(2) filePartPtr := ctx.Arg(3) if fileName == "" { ctx.SetReturn(0) return StatusSuccess } fullPath := normalizeDosPath(fileName) if strings.HasPrefix(fullPath, `\??\`) { fullPath = strings.TrimPrefix(fullPath, `\??\`) } utf16Bytes := encodeUTF16LE(fullPath) required := uint32(len(utf16Bytes)) // bytes without null if buf == 0 || bufLen < required+2 { ctx.SetReturn(uint64(required + 2)) if filePartPtr != 0 { ctx.Mem.Write64(filePartPtr, 0) } return StatusSuccess } ctx.Mem.WriteBytes(buf, utf16Bytes) ctx.Mem.Write16(buf+uint64(required), 0) if filePartPtr != 0 { lastSlash := strings.LastIndex(fullPath, `\`) if lastSlash >= 0 && lastSlash+1 < len(fullPath) { offset := uint64(len(encodeUTF16LE(fullPath[:lastSlash+1]))) ctx.Mem.Write64(filePartPtr, buf+offset) } else { ctx.Mem.Write64(filePartPtr, 0) } } ctx.SetReturn(uint64(required)) return StatusSuccess } func handleRtlDetermineDosPathNameType_U(ctx *TrapContext) uint32 { path := readGuestStringUTF16(ctx.Mem, ctx.Arg(0), 1024) switch { case strings.HasPrefix(path, `\??\`) || strings.HasPrefix(path, `\\?\`): ctx.SetReturn(5) // RtlPathTypeRootLocalDevice case strings.HasPrefix(path, `\\`): ctx.SetReturn(1) // RtlPathTypeUncAbsolute case len(path) >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'): ctx.SetReturn(3) // RtlPathTypeDriveAbsolute case len(path) >= 2 && path[1] == ':': ctx.SetReturn(4) // RtlPathTypeDriveRelative case strings.HasPrefix(path, `\`): ctx.SetReturn(2) // RtlPathTypeRooted default: ctx.SetReturn(6) // RtlPathTypeRelative } return StatusSuccess } func handleRtlGetNtGlobalFlags(ctx *TrapContext) uint32 { // Return retail-like defaults: no debug heap/pageheap flags enabled. ctx.SetReturn(0) return StatusSuccess } func handleRtlGetNtProductType(ctx *TrapContext) uint32 { // BOOLEAN RtlGetNtProductType(NT_PRODUCT_TYPE* ProductType) out := ctx.Arg(0) if out == 0 { ctx.SetReturn(0) return StatusSuccess } ctx.Mem.Write32(out, 1) // NtProductWinNt ctx.SetReturn(1) return StatusSuccess } func handleRtlInitializeBitMap(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlSetBits(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlClearBits(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAreBitsSet(ctx *TrapContext) uint32 { ctx.SetReturn(1) return StatusSuccess } func handleRtlAreBitsClear(ctx *TrapContext) uint32 { ctx.SetReturn(1) return StatusSuccess } func handleRtlGetThreadErrorMode(ctx *TrapContext) uint32 { ctx.SetReturn(0) return StatusSuccess } func handleRtlSetThreadErrorMode(ctx *TrapContext) uint32 { oldMode := ctx.Arg(1) if oldMode != 0 { ctx.Mem.Write32(oldMode, 0) } return StatusSuccess } func handleRtlWow64GetProcessMachines(ctx *TrapContext) uint32 { _ = ctx.Arg(0) // ProcessHandle processMachinePtr := ctx.Arg(1) nativeMachinePtr := ctx.Arg(2) const imageFileMachineAMD64 = 0x8664 if processMachinePtr != 0 { ctx.Mem.Write16(processMachinePtr, imageFileMachineAMD64) } if nativeMachinePtr != 0 { ctx.Mem.Write16(nativeMachinePtr, imageFileMachineAMD64) } return StatusSuccess } // Wine-specific stubs func handleWineGetVersion(ctx *TrapContext) uint32 { // wine_get_version() returns pointer to version string ver := "9.0" buf := ctx.Mem.Mmap(0, 16, mmu.PermRead|mmu.PermWrite) for i, b := range []byte(ver) { ctx.Mem.Write8(buf+uint64(i), b) } ctx.Mem.Write8(buf+uint64(len(ver)), 0) ctx.SetReturn(buf) return StatusSuccess } func handleWineServerCall(ctx *TrapContext) uint32 { // wine_server_call(req) - return STATUS_NOT_IMPLEMENTED ctx.SetReturn(0xC0000002) return StatusSuccess } func handleWineDbgGetChannelFlags(ctx *TrapContext) uint32 { ctx.SetReturn(0) // no debug channels return StatusSuccess } func handleWineDbgHeader(ctx *TrapContext) uint32 { // Return pointer to an empty string (the NUL at PEB+0x100 which is always 0) ctx.SetReturn(ctx.State.PEBAddr + 0x100) return StatusSuccess } func handleWineDbgOutput(ctx *TrapContext) uint32 { ctx.SetReturn(0) // 0 chars written return StatusSuccess } func handleWineDbgStrdup(ctx *TrapContext) uint32 { ctx.SetReturn(ctx.Arg(0)) // return same pointer return StatusSuccess } func handleRtlOpenCurrentUser(ctx *TrapContext) uint32 { // RtlOpenCurrentUser(ACCESS_MASK access, PHANDLE result) // Opens \\Registry\\User\\.Default _ = ctx.Arg(0) // access handlePtr := ctx.Arg(1) key := ctx.Reg.OpenKey("\\Registry\\User\\.Default") if key == nil { // Create a stub key so callers get something valid ctx.Reg.CreateKey("\\Registry\\User\\.Default") key = ctx.Reg.OpenKey("\\Registry\\User\\.Default") } if key != nil { h := ctx.Handles.Alloc(&RegistryKeyHandle{Key: key}) if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } return StatusObjectNameNotFound } func handleRtlQueryDynamicTimeZoneInformation(ctx *TrapContext) uint32 { // RTL_DYNAMIC_TIME_ZONE_INFORMATION is 0x80 (128) bytes infoPtr := ctx.Arg(0) if infoPtr != 0 { // Zero out the structure (UTC timezone) for i := uint64(0); i < 128; i++ { ctx.Mem.Write8(infoPtr+i, 0) } } return StatusSuccess } func handleWineNtToUnixFileName(ctx *TrapContext) uint32 { return StatusNotImplemented } func handleWineUnixToNtFileName(ctx *TrapContext) uint32 { return StatusNotImplemented } func handleRtlCreateEnvironment(ctx *TrapContext) uint32 { cloneCurrent := ctx.Arg(0) != 0 envPtr := ctx.Arg(1) if envPtr != 0 { // Keep a guest pointer for API compatibility and track logical values in Go. envBuf := ctx.Mem.Mmap(0, 4, mmu.PermRead|mmu.PermWrite) ctx.Mem.Write16(envBuf, 0) if cloneCurrent { rtlEnvBlocks[envBuf] = cloneEnv(rtlProcessEnv) } else { rtlEnvBlocks[envBuf] = map[string]string{ "SYSTEMROOT": rtlProcessEnv["SYSTEMROOT"], "WINDIR": rtlProcessEnv["WINDIR"], "TEMP": rtlProcessEnv["TEMP"], "TMP": rtlProcessEnv["TMP"], "COMSPEC": rtlProcessEnv["COMSPEC"], } } ctx.Mem.Write64(envPtr, envBuf) } return StatusSuccess } func handleRtlDestroyEnvironment(ctx *TrapContext) uint32 { envBlock := ctx.Arg(0) if envBlock != 0 { delete(rtlEnvBlocks, envBlock) } return StatusSuccess } func handleRtlQueryEnvironmentVariable_U(ctx *TrapContext) uint32 { // RtlQueryEnvironmentVariable_U(Environment, Name, Value) envBlock := ctx.Arg(0) nameUS := ctx.Arg(1) valueUS := ctx.Arg(2) if nameUS == 0 || valueUS == 0 { return StatusInvalidParameter } name := readGuestUnicodeString(ctx.Mem, nameUS) val, ok := lookupEnvValue(envBlock, name) if !ok { return 0xC0000100 // STATUS_VARIABLE_NOT_FOUND } dstMax, _ := ctx.Mem.Read16(valueUS + UnicodeStringMaxLength) dstBuf, _ := ctx.Mem.Read64(valueUS + UnicodeStringBuffer) utf16Val := encodeUTF16LE(val) required := uint16(len(utf16Val)) if dstBuf == 0 { return StatusInvalidParameter } if uint32(dstMax) < uint32(required)+2 { ctx.Mem.Write16(valueUS+UnicodeStringLength, required) return StatusBufferTooSmall } ctx.Mem.WriteBytes(dstBuf, utf16Val) ctx.Mem.Write16(dstBuf+uint64(required), 0) ctx.Mem.Write16(valueUS+UnicodeStringLength, required) return StatusSuccess } func handleRtlSetEnvironmentVariable(ctx *TrapContext) uint32 { // RtlSetEnvironmentVariable(PWSTR* Environment, Name, Value) envPtrPtr := ctx.Arg(0) nameUS := ctx.Arg(1) valueUS := ctx.Arg(2) if nameUS == 0 { return StatusInvalidParameter } name := normalizeEnvKey(readGuestUnicodeString(ctx.Mem, nameUS)) if name == "" { return StatusInvalidParameter } var envBlock uint64 var target map[string]string if envPtrPtr != 0 { envBlock, _ = ctx.Mem.Read64(envPtrPtr) if envBlock == 0 { envBlock = ctx.Mem.Mmap(0, 4, mmu.PermRead|mmu.PermWrite) ctx.Mem.Write16(envBlock, 0) ctx.Mem.Write64(envPtrPtr, envBlock) rtlEnvBlocks[envBlock] = cloneEnv(rtlProcessEnv) } target = lookupEnvMap(envBlock) } else { target = rtlProcessEnv } if valueUS == 0 { delete(target, name) return StatusSuccess } val := readGuestUnicodeString(ctx.Mem, valueUS) target[name] = val return StatusSuccess } func handleRtlExpandEnvironmentStrings_U(ctx *TrapContext) uint32 { // RtlExpandEnvironmentStrings_U(Environment, Source, Destination, ReturnedLength) envBlock := ctx.Arg(0) // optional environment block pointer srcUS := ctx.Arg(1) dstUS := ctx.Arg(2) returnLenPtr := ctx.Arg(3) if srcUS == 0 || dstUS == 0 { return StatusInvalidParameter } src := readGuestUnicodeString(ctx.Mem, srcUS) if src == "" { if returnLenPtr != 0 { ctx.Mem.Write32(returnLenPtr, 0) } ctx.Mem.Write16(dstUS+UnicodeStringLength, 0) return StatusSuccess } expanded := expandEnvVars(envBlock, src) dstMax, _ := ctx.Mem.Read16(dstUS + UnicodeStringMaxLength) dstBuf, _ := ctx.Mem.Read64(dstUS + UnicodeStringBuffer) outUTF16 := encodeUTF16LE(expanded) required := uint32(len(outUTF16)) if returnLenPtr != 0 { ctx.Mem.Write32(returnLenPtr, required) } if dstBuf == 0 { return StatusInvalidParameter } if uint32(dstMax) < required+2 { ctx.Mem.Write16(dstUS+UnicodeStringLength, uint16(minUint32(required, uint32(dstMax)))) return StatusBufferTooSmall } ctx.Mem.WriteBytes(dstBuf, outUTF16) ctx.Mem.Write16(dstBuf+uint64(len(outUTF16)), 0) ctx.Mem.Write16(dstUS+UnicodeStringLength, uint16(len(outUTF16))) return StatusSuccess } func handleRtlCreateProcessParametersEx(ctx *TrapContext) uint32 { paramsPtr := ctx.Arg(0) if paramsPtr != 0 { params := ctx.Mem.Mmap(0, 1024, mmu.PermRead|mmu.PermWrite) ctx.Mem.Write32(params, 1024) // MaximumLength ctx.Mem.Write32(params+4, 512) // Length ctx.Mem.Write64(paramsPtr, params) } return StatusSuccess } func handleRtlDestroyProcessParameters(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlRunOnceBeginInitialize(ctx *TrapContext) uint32 { return StatusSuccess // proceed with initialization } func handleRtlRunOnceComplete(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlRunOnceExecuteOnce(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtCreateIoCompletion(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if !validateGuestWrite(ctx.Mem, handlePtr, guestPtrSize(ctx.Mem)) { return StatusAccessViolation } h := ctx.Handles.Alloc(&IoCompletionPortHandle{}) writeGuestPtr(ctx.Mem, handlePtr, uint64(h)) return StatusSuccess } func handleNtSetIoCompletion(ctx *TrapContext) uint32 { // NtSetIoCompletion(IoCompletionHandle, CompletionKey, CompletionContext, Status, Information) h := Handle(ctx.Arg(0)) key := ctx.Arg(1) overlapped := ctx.Arg(2) status := uint32(ctx.Arg(3)) info := uint32(ctx.Arg(4)) iocp := ctx.Handles.GetIoCompletion(h) if iocp == nil { return StatusInvalidHandle } iocp.Post(key, overlapped, info, status) return StatusSuccess } func handleNtRemoveIoCompletion(ctx *TrapContext) uint32 { // NtRemoveIoCompletion(IoCompletionHandle, KeyContext, ApcContext, IoStatusBlock, Timeout) h := Handle(ctx.Arg(0)) keyPtr := ctx.Arg(1) apcPtr := ctx.Arg(2) ioStatus := ctx.Arg(3) timeoutPtr := ctx.Arg(4) iocp := ctx.Handles.GetIoCompletion(h) if iocp == nil { return StatusInvalidHandle } ptrSize := guestPtrSize(ctx.Mem) if !validateGuestWrite(ctx.Mem, keyPtr, ptrSize) || !validateGuestWrite(ctx.Mem, apcPtr, ptrSize) || !validateGuestWrite(ctx.Mem, ioStatus, ptrSize*2) { return StatusAccessViolation } timeout := time.Duration(-1) if timeoutPtr != 0 { t, _ := ctx.Mem.Read64(timeoutPtr) tv := int64(t) if tv == 0 { timeout = 0 } else if tv < 0 { timeout = time.Duration(-tv) * 100 * time.Nanosecond } else { now := time.Now() const epochDiff = 11644473600 ftNow := (now.Unix()+epochDiff)*10000000 + int64(now.Nanosecond()/100) rel := tv - ftNow if rel <= 0 { timeout = 0 } else { timeout = time.Duration(rel) * 100 * time.Nanosecond } } } pkt, removeResult := iocp.RemoveEx(timeout) if removeResult != IoCompletionRemoved { status := StatusTimeout if removeResult == IoCompletionClosed { status = StatusAbandoned } if ioStatus != 0 { writeIOStatus(ctx.Mem, ioStatus, status, 0) } return status } if keyPtr != 0 { writeGuestPtr(ctx.Mem, keyPtr, pkt.CompletionKey) } if apcPtr != 0 { writeGuestPtr(ctx.Mem, apcPtr, pkt.Overlapped) } if ioStatus != 0 { writeIOStatus(ctx.Mem, ioStatus, pkt.Status, uint64(pkt.BytesTransferred)) } return StatusSuccess } func handleNtRemoveIoCompletionEx(ctx *TrapContext) uint32 { // NtRemoveIoCompletionEx(IoCompletionHandle, Entries, Count, NumRemoved, Timeout, Alertable) h := Handle(ctx.Arg(0)) entries := ctx.Arg(1) count := uint32(ctx.Arg(2)) removedPtr := ctx.Arg(3) timeoutPtr := ctx.Arg(4) _ = ctx.Arg(5) // Alertable iocp := ctx.Handles.GetIoCompletion(h) if iocp == nil { return StatusInvalidHandle } if count == 0 { return StatusInvalidParameter } ptrSize := guestPtrSize(ctx.Mem) entrySize := ptrSize * 4 if !validateGuestWrite(ctx.Mem, entries, uint64(count)*entrySize) || !validateGuestWrite(ctx.Mem, removedPtr, 4) { return StatusAccessViolation } timeout := time.Duration(-1) if timeoutPtr != 0 { t, _ := ctx.Mem.Read64(timeoutPtr) tv := int64(t) if tv == 0 { timeout = 0 } else if tv < 0 { timeout = time.Duration(-tv) * 100 * time.Nanosecond } else { now := time.Now() const epochDiff = 11644473600 ftNow := (now.Unix()+epochDiff)*10000000 + int64(now.Nanosecond()/100) rel := tv - ftNow if rel <= 0 { timeout = 0 } else { timeout = time.Duration(rel) * 100 * time.Nanosecond } } } removed := uint32(0) for removed < count { wait := time.Duration(0) if removed == 0 { wait = timeout } pkt, removeResult := iocp.RemoveEx(wait) if removeResult != IoCompletionRemoved { if removeResult == IoCompletionClosed && removed == 0 { if removedPtr != 0 { ctx.Mem.Write32(removedPtr, 0) } return StatusAbandoned } break } // FILE_IO_COMPLETION_INFORMATION {KeyContext, ApcContext, IoStatusBlock} base := entries + uint64(removed)*entrySize writeGuestPtr(ctx.Mem, base, pkt.CompletionKey) writeGuestPtr(ctx.Mem, base+ptrSize, pkt.Overlapped) writeGuestPtr(ctx.Mem, base+ptrSize*2, uint64(pkt.Status)) writeGuestPtr(ctx.Mem, base+ptrSize*3, uint64(pkt.BytesTransferred)) removed++ } if removedPtr != 0 { ctx.Mem.Write32(removedPtr, removed) } if removed == 0 { return StatusTimeout } return StatusSuccess } func handleNtCancelIoFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) ioStatus := ctx.Arg(1) if ctx.Handles.GetFile(fileHandle) == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) { return StatusAccessViolation } nativeIOFor(ctx.Handles).cancelMatching( fileHandle, nativeCurrentThreadID(ctx), true, 0, ) writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess } func handleNtCancelIoFileEx(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) requestIoStatus := ctx.Arg(1) ioStatus := ctx.Arg(2) if ctx.Handles.GetFile(fileHandle) == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) { return StatusAccessViolation } if requestIoStatus != 0 && ctx.Mem.ValidateRange(requestIoStatus, ioStatusSize, mmu.PermRead) != nil { return StatusAccessViolation } if nativeIOFor(ctx.Handles).cancelMatching( fileHandle, 0, false, requestIoStatus, ) == 0 { writeIOStatus(ctx.Mem, ioStatus, StatusNotFound, 0) return StatusNotFound } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess } func handleNtCreateDirectoryObject(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if handlePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "Directory"}) ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtOpenDirectoryObject(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if handlePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "Directory"}) ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtCreateSymbolicLinkObject(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) if handlePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "SymLink"}) ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } func handleNtOpenSymbolicLinkObject(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) objAttr := ctx.Arg(2) if handlePtr == 0 || objAttr == 0 { return StatusInvalidParameter } _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err != nil || oaName == "" { return StatusObjectNameNotFound } name := strings.ToLower(oaName) name = strings.ReplaceAll(name, "/", `\`) target := "" switch name { case `\??\nulssectionptr`, `\??\nlssectionptr`: target = `\Windows\System32\locale.nls` case `\??\conin$`: target = `\Device\ConDrv\Input` case `\??\conout$`: target = `\Device\ConDrv\Output` case `\??\pipe\`, `\device\namedpipe`: target = `\Device\NamedPipe` case `\systemroot`: target = `\Windows` default: return StatusObjectNameNotFound } h := ctx.Handles.Alloc(&SymbolicLinkHandle{Name: oaName, Target: target}) ctx.Mem.Write64(handlePtr, uint64(h)) return StatusSuccess } func handleNtQuerySymbolicLinkObject(ctx *TrapContext) uint32 { linkHandle := Handle(ctx.Arg(0)) linkTarget := ctx.Arg(1) // PUNICODE_STRING retLen := ctx.Arg(2) // PULONG if linkTarget == 0 { return StatusInvalidParameter } sl, ok := ctx.Handles.Get(linkHandle).(*SymbolicLinkHandle) if !ok || sl == nil { return StatusObjectTypeMismatch } ws := encodeUTF16LE(sl.Target) maxLen, _ := ctx.Mem.Read16(linkTarget + 2) // MaximumLength bufPtr, _ := ctx.Mem.Read64(linkTarget + 8) // Buffer if retLen != 0 { ctx.Mem.Write32(retLen, uint32(len(ws))) } if bufPtr == 0 { return StatusInvalidParameter } if uint32(maxLen) < uint32(len(ws)) { ctx.Mem.Write16(linkTarget+0, 0) return StatusBufferTooSmall } ctx.Mem.Write16(linkTarget+0, uint16(len(ws))) ctx.Mem.WriteBytes(bufPtr, ws) if uint32(maxLen) >= uint32(len(ws)+2) { ctx.Mem.Write16(bufPtr+uint64(len(ws)), 0) } return StatusSuccess } func handleNtSetSecurityObject(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtQuerySecurityObject(ctx *TrapContext) uint32 { _ = Handle(ctx.Arg(0)) // handle _ = ctx.Arg(1) // security info mask secDesc := ctx.Arg(2) // buffer length := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) const sdLen uint32 = 40 // SECURITY_DESCRIPTOR_RELATIVE minimal size if retLen != 0 { ctx.Mem.Write32(retLen, sdLen) } if secDesc == 0 { return StatusInvalidParameter } if length < sdLen { return StatusBufferTooSmall } // Absolute-like minimal descriptor compatible with our other SD helpers. ctx.Mem.Write8(secDesc+0, 1) // Revision ctx.Mem.Write8(secDesc+1, 0) // Sbz1 ctx.Mem.Write16(secDesc+2, 0) // Control (no owner/group/dacl) ctx.Mem.Write64(secDesc+8, 0) // Owner ctx.Mem.Write64(secDesc+16, 0) ctx.Mem.Write64(secDesc+24, 0) // Group ctx.Mem.Write64(secDesc+32, 0) // DACL pointer/offset return StatusSuccess } func handleNtSetInformationObject(ctx *TrapContext) uint32 { h := Handle(ctx.Arg(0)) infoClass := uint32(ctx.Arg(1)) info := ctx.Arg(2) length := uint32(ctx.Arg(3)) if ctx.Handles.Get(h) == nil { return StatusInvalidHandle } switch infoClass { case 4: // ObjectHandleFlagInformation if info == 0 || length < 2 { return StatusInvalidParameter } inherit, _ := ctx.Mem.Read8(info) protect, _ := ctx.Mem.Read8(info + 1) var f uint8 if inherit != 0 { f |= 1 } if protect != 0 { f |= 2 } objectHandleFlags[h] = f return StatusSuccess default: return StatusNotImplemented } } func fileHandleCanLockNative(file *FileHandle) bool { const ( genericRead = uint32(0x80000000) genericWrite = uint32(0x40000000) genericAll = uint32(0x10000000) ) return file != nil && file.Access&(genericRead|genericWrite|genericAll) != 0 } func handleNtLockFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) eventHandle := Handle(ctx.Arg(1)) apcRoutine := ctx.Arg(2) apcContext := ctx.Arg(3) ioStatus := ctx.Arg(4) byteOffset := ctx.Arg(5) lengthAddr := ctx.Arg(6) key := uint32(ctx.Arg(7)) failImmediately := ctx.Arg(8) != 0 exclusive := ctx.Arg(9) != 0 file := ctx.Handles.GetFile(fileHandle) if file == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } if !fileHandleCanLockNative(file) { writeIOStatus(ctx.Mem, ioStatus, StatusAccessDenied, 0) return StatusAccessDenied } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) || byteOffset == 0 || lengthAddr == 0 || ctx.Mem.ValidateRange(byteOffset, 8, mmu.PermRead) != nil || ctx.Mem.ValidateRange(lengthAddr, 8, mmu.PermRead) != nil { return StatusAccessViolation } var event *EventHandle if eventHandle != 0 { event = ctx.Handles.GetEvent(eventHandle) if event == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } } identity, ok := ctx.FS.FileIdentity(file.FD) if !ok { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } start, _ := ctx.Mem.Read64(byteOffset) length, _ := ctx.Mem.Read64(lengthAddr) if FileLocksFor(ctx.FS).LockKey( identity, file.LockOwner(), start, length, key, exclusive, false, ) { completeNativeIOInline( ctx, fileHandle, file, ioStatus, event, apcRoutine, apcContext, StatusSuccess, 0, ) return StatusSuccess } if failImmediately { writeIOStatus(ctx.Mem, ioStatus, StatusLockNotGranted, 0) return StatusLockNotGranted } const fileFlagOverlapped = uint32(0x40000000) if file.Flags&fileFlagOverlapped == 0 { if !FileLocksFor(ctx.FS).LockKey( identity, file.LockOwner(), start, length, key, exclusive, true, ) { writeIOStatus(ctx.Mem, ioStatus, StatusCancelled, 0) return StatusCancelled } completeNativeIOInline( ctx, fileHandle, file, ioStatus, event, apcRoutine, apcContext, StatusSuccess, 0, ) return StatusSuccess } operation := beginNativeIO( ctx, fileHandle, file, ioStatus, event, apcRoutine, apcContext, ) owner := file.LockOwner() manager := FileLocksFor(ctx.FS) go func() { if !manager.LockKeyCancelable( identity, owner, start, length, key, exclusive, true, operation.cancel, ) { operation.complete(StatusCancelled, 0) return } select { case <-operation.cancel: manager.UnlockKey(identity, owner, start, length, key) operation.complete(StatusCancelled, 0) default: if !operation.complete(StatusSuccess, 0) { manager.UnlockKey(identity, owner, start, length, key) } } }() return StatusPending } func handleNtUnlockFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) ioStatus := ctx.Arg(1) byteOffset := ctx.Arg(2) lengthAddr := ctx.Arg(3) file := ctx.Handles.GetFile(fileHandle) if file == nil { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } if !fileHandleCanLockNative(file) { writeIOStatus(ctx.Mem, ioStatus, StatusAccessDenied, 0) return StatusAccessDenied } ioStatusSize := uint64(16) if ctx.Mem.Mode32 { ioStatusSize = 8 } if !validateGuestWrite(ctx.Mem, ioStatus, ioStatusSize) || byteOffset == 0 || lengthAddr == 0 || ctx.Mem.ValidateRange(byteOffset, 8, mmu.PermRead) != nil || ctx.Mem.ValidateRange(lengthAddr, 8, mmu.PermRead) != nil { return StatusAccessViolation } identity, ok := ctx.FS.FileIdentity(file.FD) if !ok { writeIOStatus(ctx.Mem, ioStatus, StatusInvalidHandle, 0) return StatusInvalidHandle } start, _ := ctx.Mem.Read64(byteOffset) length, _ := ctx.Mem.Read64(lengthAddr) if !FileLocksFor(ctx.FS).UnlockKey( identity, file.LockOwner(), start, length, uint32(ctx.Arg(4)), ) { writeIOStatus(ctx.Mem, ioStatus, StatusRangeNotLocked, 0) return StatusRangeNotLocked } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess } func handleNtCreateMailslotFile(ctx *TrapContext) uint32 { // NtCreateMailslotFile(FileHandle*, DesiredAccess, ObjectAttributes*, IoStatusBlock*, // CreateOptions, MailslotQuota, MaximumMessageSize, ReadTimeout*) handlePtr := ctx.Arg(0) objAttr := ctx.Arg(2) ioStatus := ctx.Arg(3) maxMsg := uint32(ctx.Arg(6)) readTimeoutPtr := ctx.Arg(7) name := "" if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } if name == "" { name = fmt.Sprintf(`\Device\Mailslot\mailslot-%d`, time.Now().UnixNano()) } if maxMsg == 0 { maxMsg = 4096 } var readTimeout uint32 if readTimeoutPtr != 0 { if rt, err := ctx.Mem.Read64(readTimeoutPtr); err == nil { if int64(rt) < 0 { // Negative values are relative timeout in 100ns units. readTimeout = uint32((-int64(rt)) / 10000) // to ms } } } m := NewMailslot(name, maxMsg, readTimeout) RegisterMailslot(name, m) h := ctx.Handles.Alloc(m) if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } writeIOStatus(ctx.Mem, ioStatus, StatusSuccess, 0) return StatusSuccess } func handleNtCreateNamedPipeFile(ctx *TrapContext) uint32 { handlePtr := ctx.Arg(0) objAttr := ctx.Arg(2) var name string if objAttr != 0 { _, oaName, _, err := ReadObjectAttributes(ctx, objAttr) if err == nil { name = NormalizeObjectName(oaName) } } if name == "" { name = fmt.Sprintf("pipe-%d", time.Now().UnixNano()) } pipe := NewServerPipe(name) RegisterNamedPipe(name, pipe) h, _ := ctx.Handles.AllocNamed(name, pipe) if handlePtr != 0 { ctx.Mem.Write64(handlePtr, uint64(h)) } return StatusSuccess } // signalEventHandle signals a guest event handle if non-zero. func signalEventHandle(ctx *TrapContext, handle uint64) { if handle == 0 { return } if eh, ok := ctx.Handles.Get(Handle(handle)).(*EventHandle); ok { eh.Set() } } func handleNtFsControlFile(ctx *TrapContext) uint32 { fileHandle := Handle(ctx.Arg(0)) eventHandle := ctx.Arg(1) _ = ctx.Arg(2) // ApcRoutine _ = ctx.Arg(3) // ApcContext ioStatus := ctx.Arg(4) fsctlCode := uint32(ctx.Arg(5)) _ = ctx.Arg(6) // InputBuffer _ = ctx.Arg(7) // InputBufferLength outputBuf := ctx.Arg(8) outputLen := uint32(ctx.Arg(9)) if ctx.Trace { fmt.Printf(" NtFsControlFile: fsctl=0x%08x outLen=%d\n", fsctlCode, outputLen) } var status uint32 = StatusSuccess var info uint64 pipe := ctx.Handles.GetNamedPipe(fileHandle) switch fsctlCode { case 0x00110000, 0x00110004, 0x00110008, 0x00110010, 0x00110018: // PIPE_ASSIGN_EVENT / DISCONNECT / LISTEN / QUERY_EVENT / WAIT if pipe == nil { status = StatusInvalidHandle break } if fsctlCode == 0x00110018 && outputBuf != 0 && outputLen >= 16 { avail := uint32(pipe.AvailableBytes()) ctx.Mem.Write32(outputBuf, 0) // pipe state ctx.Mem.Write32(outputBuf+4, avail) // bytes available ctx.Mem.Write32(outputBuf+8, 0) // number of messages ctx.Mem.Write32(outputBuf+12, avail) // message length info = 16 } case 0x0011000C: // PIPE_PEEK if pipe == nil { status = StatusInvalidHandle break } if outputBuf != 0 && outputLen >= 16 { avail := uint32(pipe.AvailableBytes()) ctx.Mem.Write32(outputBuf, 0) ctx.Mem.Write32(outputBuf+4, avail) ctx.Mem.Write32(outputBuf+8, 0) ctx.Mem.Write32(outputBuf+12, avail) info = 16 } case 0x00110014: // PIPE_TRANSCEIVE if pipe == nil { status = StatusInvalidHandle break } inputBuf := ctx.Arg(6) inputLen := uint32(ctx.Arg(7)) if inputBuf != 0 && inputLen > 0 { if data, err := ctx.Mem.ReadBytes(inputBuf, int(inputLen)); err == nil { _ = pipe.PipeWrite(data) } } if outputBuf != 0 && outputLen > 0 { buf := make([]byte, outputLen) n := pipe.PipeRead(buf) if n > 0 { ctx.Mem.WriteBytes(outputBuf, buf[:n]) } info = uint64(n) } case 0x0009009C: // FSCTL_GET_COMPRESSION if outputBuf != 0 && outputLen >= 2 { ctx.Mem.Write16(outputBuf, 0) // COMPRESSION_FORMAT_NONE info = 2 } case 0x000900A8: // FSCTL_GET_REPARSE_POINT status = 0xC0000275 // STATUS_NOT_A_REPARSE_POINT case 0x004D0008: // FSCTL_GET_NTFS_VOLUME_DATA status = 0xC0000010 // STATUS_INVALID_DEVICE_REQUEST case 0x00090028: // FSCTL_SET_COMPRESSION // Accept silently case 0x000900C0: // FSCTL_SET_SPARSE // Accept silently default: status = 0xC0000010 // STATUS_INVALID_DEVICE_REQUEST if ctx.Trace { fmt.Printf(" NtFsControlFile: unknown FSCTL 0x%08x\n", fsctlCode) } } writeIOStatus(ctx.Mem, ioStatus, status, info) signalEventHandle(ctx, eventHandle) return status } func handleNtPowerInformation(ctx *TrapContext) uint32 { infoLevel := uint32(ctx.Arg(0)) _ = ctx.Arg(1) // input buffer _ = uint32(ctx.Arg(2)) outBuf := ctx.Arg(3) outLen := uint32(ctx.Arg(4)) switch infoLevel { case 0: // SystemPowerPolicyAc (best-effort, opaque) if outBuf != 0 && outLen >= 4 { ctx.Mem.Write32(outBuf, 0) } return StatusSuccess case 4: // SystemPowerCapabilities // Caller often expects a non-zeroed structure and success. if outBuf == 0 || outLen < 64 { return StatusBufferTooSmall } for i := uint32(0); i < outLen && i < 64; i++ { ctx.Mem.Write8(outBuf+uint64(i), 0) } ctx.Mem.Write8(outBuf+0, 1) // PowerButtonPresent ctx.Mem.Write8(outBuf+1, 1) // SleepButtonPresent ctx.Mem.Write8(outBuf+2, 1) // LidPresent ctx.Mem.Write8(outBuf+8, 1) // SystemS1 ctx.Mem.Write8(outBuf+9, 1) // SystemS2 ctx.Mem.Write8(outBuf+10, 1) // SystemS3 return StatusSuccess case 11: // SystemExecutionState if outBuf != 0 && outLen >= 4 { ctx.Mem.Write32(outBuf, 0) } return StatusSuccess case 12: // SystemPowerInformation if outBuf == 0 || outLen < 24 { return StatusBufferTooSmall } ctx.Mem.Write32(outBuf+0, 1) // MaxIdlenessAllowed ctx.Mem.Write32(outBuf+4, 1) // Idleness ctx.Mem.Write32(outBuf+8, 0) // TimeRemaining ctx.Mem.Write8(outBuf+12, 1) // CoolingMode return StatusSuccess case 5: // SystemBatteryState if outBuf == 0 || outLen < 32 { return StatusBufferTooSmall } for i := uint32(0); i < 32; i++ { ctx.Mem.Write8(outBuf+uint64(i), 0) } // AC online, no battery present (desktop-like behavior) ctx.Mem.Write8(outBuf+0, 1) // AcOnLine return StatusSuccess case 20: // ProcessorInformation if outBuf == 0 || outLen < 24 { return StatusBufferTooSmall } // Minimal 1-processor entry ctx.Mem.Write32(outBuf+0, 0) // Number ctx.Mem.Write32(outBuf+4, 60) // MaxMhz ctx.Mem.Write32(outBuf+8, 35) // CurrentMhz ctx.Mem.Write32(outBuf+12, 100) // MhzLimit ctx.Mem.Write32(outBuf+16, 0) // MaxIdleState ctx.Mem.Write32(outBuf+20, 0) // CurrentIdleState return StatusSuccess default: return StatusNotImplemented } } func handleNtQueueApcThread(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtGetNlsSectionPtr(ctx *TrapContext) uint32 { nlsType := ctx.Arg(0) nlsID := ctx.Arg(1) ptrOut := ctx.Arg(3) sizeOut := ctx.Arg(4) ravelog.Logf("[ntapi] NtGetNlsSectionPtr type=%d id=%d ptrOut=0x%x sizeOut=0x%x\n", nlsType, nlsID, ptrOut, sizeOut) if ctx.Disp == nil || ctx.Disp.sysRoot == "" { return StatusNotImplemented } var data []byte var err error switch nlsType { case 11: // NLS_SECTION_CODEPAGE - c_XXXX.nls data, err = ctx.Disp.loadNlsFile(uint32(nlsID)) case 9: // NLS_SECTION_SORTKEYS - build minimal valid sort key table data = BuildSortKeyData() err = nil case 12: // NLS_SECTION_NORMALIZE - normNFC/NFD/NFKC/NFKD/IDNA nameMap := map[uint64]string{ 1: "normnfc.nls", 2: "normnfd.nls", 5: "normnfkc.nls", 6: "normnfkd.nls", 13: "normidna.nls", } if fname, ok := nameMap[nlsID]; ok { path := ctx.Disp.sysRoot + "/" + fname data, err = os.ReadFile(path) } else { return StatusNotImplemented } default: return StatusNotImplemented } if err != nil { ravelog.Logf("[ntapi] NtGetNlsSectionPtr: failed to load NLS type=%d id=%d: %v\n", nlsType, nlsID, err) return StatusNotImplemented } // Map the NLS data into guest memory with a page of padding before // the data so that Wine's negative-offset accesses (ptr[-n]) work. size := uint64(len(data)) pad := uint64(mmu.PageSize) allocSize := (size + pad + mmu.PageSize - 1) & mmu.PageMask base := ctx.Mem.Mmap(0, allocSize, mmu.PermRead|mmu.PermWrite) dataAddr := base + pad ctx.Mem.WriteBytes(dataAddr, data) if ptrOut != 0 { ctx.Mem.Write64(ptrOut, dataAddr) } if sizeOut != 0 { ctx.Mem.Write64(sizeOut, size) } ravelog.Logf("[ntapi] NtGetNlsSectionPtr: mapped type=%d id=%d at 0x%x size=0x%x\n", nlsType, nlsID, dataAddr, size) return StatusSuccess } func handleRtlGetLocaleFileMappingAddress(ctx *TrapContext) uint32 { // NTSTATUS RtlGetLocaleFileMappingAddress( // PVOID *BaseAddress, // RCX - OUT // PULONG DefaultLocaleId, // RDX - OUT (LCID) // PLARGE_INTEGER DefaultCasingTableSize // R8 - OUT // ) baseAddrPtr := ctx.Arg(0) localeIdPtr := ctx.Arg(1) casingSizePtr := ctx.Arg(2) // Load locale.nls from sysroot (Wine uses locale.nls, not l_intl.nls) var data []byte if ctx.Disp != nil && ctx.Disp.sysRoot != "" { path := ctx.Disp.sysRoot + "/locale.nls" if d, err := os.ReadFile(path); err == nil { data = d } } if data == nil { // Minimal locale data: just a zeroed page data = make([]byte, 4096) } // Allocate with one extra page of padding before the data so that // small negative offsets from the returned pointer read valid memory // (Wine's kernelbase accesses ptr[-n] for header fields). pad := mmu.PageSize size := (uint64(len(data)) + uint64(pad) + mmu.PageSize - 1) & mmu.PageMask base := ctx.Mem.Mmap(0, size, mmu.PermRead|mmu.PermWrite) dataAddr := base + uint64(pad) ctx.Mem.WriteBytes(dataAddr, data) ravelog.Logf("[ntapi] RtlGetLocaleFileMappingAddress: mapped locale.nls at 0x%x size=0x%x\n", dataAddr, len(data)) if baseAddrPtr != 0 { ctx.Mem.Write64(baseAddrPtr, dataAddr) } if localeIdPtr != 0 { ctx.Mem.Write32(localeIdPtr, 0x0409) // en-US } if casingSizePtr != 0 { ctx.Mem.Write64(casingSizePtr, uint64(len(data))) } return StatusSuccess } func handleRtlGetProductInfo(ctx *TrapContext) uint32 { retPtr := ctx.Arg(4) if retPtr != 0 { ctx.Mem.Write32(retPtr, 0x65) // PRODUCT_CORE = Windows 10 Home } ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlGetSystemPreferredUILanguages(ctx *TrapContext) uint32 { numLangs := ctx.Arg(1) buf := ctx.Arg(2) bufLen := ctx.Arg(3) if numLangs != 0 { ctx.Mem.Write32(numLangs, 1) } if buf != 0 && bufLen != 0 { lang := "en-US" maxLen, _ := ctx.Mem.Read32(bufLen) if maxLen >= uint32((len(lang)+2)*2) { for i, ch := range lang { ctx.Mem.Write16(buf+uint64(i)*2, uint16(ch)) } ctx.Mem.Write16(buf+uint64(len(lang))*2, 0) ctx.Mem.Write16(buf+uint64(len(lang)+1)*2, 0) } ctx.Mem.Write32(bufLen, uint32((len(lang)+2)*2)) } return StatusSuccess } func handleRtlGetUserPreferredUILanguages(ctx *TrapContext) uint32 { return handleRtlGetSystemPreferredUILanguages(ctx) } func handleRtlGetProcessPreferredUILanguages(ctx *TrapContext) uint32 { return handleRtlGetSystemPreferredUILanguages(ctx) } func handleRtlGetThreadPreferredUILanguages(ctx *TrapContext) uint32 { return handleRtlGetSystemPreferredUILanguages(ctx) } func handleRtlGetSystemTimePrecise(ctx *TrapContext) uint32 { // Returns 100ns intervals since 1601-01-01 now := time.Now() epoch := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) intervals := now.Sub(epoch).Nanoseconds() / 100 ctx.SetReturn(uint64(intervals)) return StatusSuccess } func handleRtlTimeFieldsToTime(ctx *TrapContext) uint32 { // RtlTimeFieldsToTime(TIME_FIELDS* tf, LARGE_INTEGER* time) tf := ctx.Arg(0) out := ctx.Arg(1) if tf == 0 || out == 0 { ctx.SetReturn(0) return StatusSuccess } yr, _ := ctx.Mem.Read16(tf + 0) mo, _ := ctx.Mem.Read16(tf + 2) dy, _ := ctx.Mem.Read16(tf + 4) hr, _ := ctx.Mem.Read16(tf + 6) mi, _ := ctx.Mem.Read16(tf + 8) se, _ := ctx.Mem.Read16(tf + 10) ms, _ := ctx.Mem.Read16(tf + 12) t := time.Date(int(yr), time.Month(mo), int(dy), int(hr), int(mi), int(se), int(ms)*1e6, time.UTC) ft := uint64(t.UnixNano()/100 + 116444736000000000) ctx.Mem.Write64(out, ft) ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlTimeToTimeFields(ctx *TrapContext) uint32 { // RtlTimeToTimeFields(LARGE_INTEGER* time, TIME_FIELDS* tf) timePtr := ctx.Arg(0) tf := ctx.Arg(1) if timePtr == 0 || tf == 0 { return StatusSuccess } ft, _ := ctx.Mem.Read64(timePtr) ns := (int64(ft) - 116444736000000000) * 100 t := time.Unix(0, ns).UTC() ctx.Mem.Write16(tf+0, uint16(t.Year())) ctx.Mem.Write16(tf+2, uint16(t.Month())) ctx.Mem.Write16(tf+4, uint16(t.Day())) ctx.Mem.Write16(tf+6, uint16(t.Hour())) ctx.Mem.Write16(tf+8, uint16(t.Minute())) ctx.Mem.Write16(tf+10, uint16(t.Second())) ctx.Mem.Write16(tf+12, uint16(t.Nanosecond()/1e6)) ctx.Mem.Write16(tf+14, uint16(t.Weekday())) return StatusSuccess } func handleRtlQueryUnbiasedInterruptTime(ctx *TrapContext) uint32 { ptr := ctx.Arg(0) if ptr != 0 { ctx.Mem.Write64(ptr, uint64(time.Now().UnixNano()/100)) } return StatusSuccess } func handleNtSetInformationToken(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCreateSecurityDescriptor(ctx *TrapContext) uint32 { sd := ctx.Arg(0) if sd != 0 { for i := uint64(0); i < 40; i++ { ctx.Mem.Write8(sd+i, 0) } ctx.Mem.Write8(sd, 1) // Revision ctx.Mem.Write16(sd+2, 0) // Control } return StatusSuccess } func handleRtlSetDaclSecurityDescriptor(ctx *TrapContext) uint32 { sd := ctx.Arg(0) present := ctx.Arg(1) != 0 dacl := ctx.Arg(2) defaulted := ctx.Arg(3) != 0 if sd != 0 { control, _ := ctx.Mem.Read16(sd + 2) control &^= 0x0004 | 0x0008 if present { control |= 0x0004 ctx.Mem.Write64(sd+28, dacl) } else { ctx.Mem.Write64(sd+28, 0) } if defaulted { control |= 0x0008 } ctx.Mem.Write16(sd+2, control) } return StatusSuccess } func handleRtlGetDaclSecurityDescriptor(ctx *TrapContext) uint32 { sd := ctx.Arg(0) pbPresent := ctx.Arg(1) ppDacl := ctx.Arg(2) pbDefaulted := ctx.Arg(3) if pbPresent != 0 { present := uint32(0) if sd != 0 { control, _ := ctx.Mem.Read16(sd + 2) if control&0x0004 != 0 { present = 1 } } ctx.Mem.Write32(pbPresent, present) } if ppDacl != 0 { var dacl uint64 if sd != 0 { dacl, _ = ctx.Mem.Read64(sd + 28) } ctx.Mem.Write64(ppDacl, dacl) } if pbDefaulted != 0 { defaulted := uint32(0) if sd != 0 { control, _ := ctx.Mem.Read16(sd + 2) if control&0x0008 != 0 { defaulted = 1 } } ctx.Mem.Write32(pbDefaulted, defaulted) } return StatusSuccess } func handleRtlCreateAcl(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAddAccessAllowedAce(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAddAccessAllowedAceEx(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlAllocateAndInitializeSid(ctx *TrapContext) uint32 { sidPtr := ctx.Arg(0) if sidPtr != 0 { sid := ctx.Mem.Mmap(0, 28, mmu.PermRead|mmu.PermWrite) ctx.Mem.Write8(sid, 1) // Revision ctx.Mem.Write8(sid+1, 1) // SubAuthorityCount ctx.Mem.Write64(sidPtr, sid) } return StatusSuccess } func handleRtlFreeSid(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCopySid(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlEqualSid(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlLengthSid(ctx *TrapContext) uint32 { ctx.SetReturn(12) return StatusSuccess } func handleRtlInitializeSid(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlValidSid(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlSubAuthorityCountSid(ctx *TrapContext) uint32 { sid := ctx.Arg(0) if sid != 0 { ctx.SetReturn(sid + 1) // pointer to SubAuthorityCount byte } return StatusSuccess } func handleRtlSubAuthoritySid(ctx *TrapContext) uint32 { sid := ctx.Arg(0) idx := ctx.Arg(1) if sid != 0 { ctx.SetReturn(sid + 8 + idx*4) } return StatusSuccess } func handleRtlIdentifierAuthoritySid(ctx *TrapContext) uint32 { sid := ctx.Arg(0) if sid != 0 { ctx.SetReturn(sid + 2) // pointer to IdentifierAuthority } return StatusSuccess } func handleNtPrivilegeCheck(ctx *TrapContext) uint32 { result := ctx.Arg(2) if result != 0 { ctx.Mem.Write8(result, 1) // privileges held } return StatusSuccess } func handleNtAdjustPrivilegesToken(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlQueryActivationContextApplicationSettings(ctx *TrapContext) uint32 { return 0xC015000F // STATUS_SXS_KEY_NOT_FOUND } func handleRtlFindActivationContextSectionString(ctx *TrapContext) uint32 { return 0xC000008B // STATUS_SXS_SECTION_NOT_FOUND } func handleRtlFindActivationContextSectionGuid(ctx *TrapContext) uint32 { return 0xC000008B } func handleRtlActivateActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlDeactivateActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlGetActiveActivationContext(ctx *TrapContext) uint32 { cookie := ctx.Arg(0) if cookie != 0 { ctx.Mem.Write64(cookie, 0) } return StatusSuccess } func handleRtlAddRefActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlReleaseActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCreateActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlZombifyActivationContext(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlFreeActivationContextStack(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlQueryInformationActivationContext(ctx *TrapContext) uint32 { // RtlQueryInformationActivationContext(Flags, ActCtx, SubInst, Class, Buffer, BufSize, RetLen) _ = ctx.Arg(0) // flags _ = ctx.Arg(1) // activation context handle _ = ctx.Arg(2) // sub instance index infoClass := uint32(ctx.Arg(3)) buf := ctx.Arg(4) bufSize := uint32(ctx.Arg(5)) retLen := ctx.Arg(6) var needed uint32 switch infoClass { case 1: // ActivationContextBasicInformation needed = 16 case 2: // ActivationContextDetailedInformation needed = 32 default: return StatusNotImplemented } if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if buf == 0 || bufSize < needed { return StatusBufferTooSmall } for i := uint32(0); i < needed; i++ { ctx.Mem.Write8(buf+uint64(i), 0) } return StatusSuccess } func handleRtlQueueWorkItem(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCreateTimer(ctx *TrapContext) uint32 { timerPtr := ctx.Arg(1) if timerPtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "RtlTimer"}) ctx.Mem.Write64(timerPtr, uint64(h)) } return StatusSuccess } func handleRtlDeleteTimer(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlUpdateTimer(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlCreateTimerQueue(ctx *TrapContext) uint32 { queuePtr := ctx.Arg(0) if queuePtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "TimerQueue"}) ctx.Mem.Write64(queuePtr, uint64(h)) } return StatusSuccess } func handleRtlDeleteTimerQueueEx(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlRegisterWait(ctx *TrapContext) uint32 { waitPtr := ctx.Arg(0) if waitPtr != 0 { h := ctx.Handles.Alloc(&GenericHandle{Kind: "Wait"}) ctx.Mem.Write64(waitPtr, uint64(h)) } return StatusSuccess } func handleRtlDeregisterWait(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlDeregisterWaitEx(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlSetIoCompletionCallback(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlGUIDFromString(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlFindMessage(ctx *TrapContext) uint32 { // RtlFindMessage(Base, Type, LangId, MsgId, MessageEntry**) _ = ctx.Arg(0) _ = ctx.Arg(1) _ = ctx.Arg(2) _ = ctx.Arg(3) entryOut := ctx.Arg(4) if entryOut == 0 { return StatusInvalidParameter } // MESSAGE_RESOURCE_ENTRY: Length(2), Flags(2), Text[] msg := []byte("stub message\x00") entry := ctx.Mem.Mmap(0, uint64(4+len(msg)), mmu.PermRead|mmu.PermWrite) ctx.Mem.Write16(entry+0, uint16(4+len(msg))) ctx.Mem.Write16(entry+2, 0) // ANSI ctx.Mem.WriteBytes(entry+4, msg) ctx.Mem.Write64(entryOut, entry) return StatusSuccess } func handleRtlFormatMessage(ctx *TrapContext) uint32 { // Best-effort formatter: copy source WCHAR string to destination. src := ctx.Arg(0) // PWSTR source _ = ctx.Arg(1) // width _ = ctx.Arg(2) // ignore inserts _ = ctx.Arg(3) // arguments dst := ctx.Arg(4) // PWSTR destination dstBytes := uint32(ctx.Arg(5)) retLen := ctx.Arg(6) if src == 0 || dst == 0 || dstBytes < 2 { return StatusInvalidParameter } text := readGuestStringUTF16(ctx.Mem, src, 4096) if text == "" { ctx.Mem.Write16(dst, 0) if retLen != 0 { ctx.Mem.Write32(retLen, 0) } return StatusSuccess } utf16b := encodeUTF16LE(text) maxCopy := int(dstBytes - 2) if maxCopy < 0 { maxCopy = 0 } if len(utf16b) > maxCopy { utf16b = utf16b[:maxCopy] } ctx.Mem.WriteBytes(dst, utf16b) ctx.Mem.Write16(dst+uint64(len(utf16b)), 0) if retLen != 0 { ctx.Mem.Write32(retLen, uint32(len(utf16b))) } return StatusSuccess } func minUint32(a, b uint32) uint32 { if a < b { return a } return b } func handleRtlUTF8ToUnicodeN(ctx *TrapContext) uint32 { dst := ctx.Arg(0) dstMax := uint32(ctx.Arg(1)) resultLenPtr := ctx.Arg(2) src := ctx.Arg(3) srcLen := uint32(ctx.Arg(4)) written := uint32(0) i := uint32(0) for i < srcLen && written+2 <= dstMax { b, err := ctx.Mem.Read8(src + uint64(i)) if err != nil { break } if b < 0x80 { if dst != 0 { ctx.Mem.Write16(dst+uint64(written), uint16(b)) } written += 2 i++ } else if b < 0xE0 && i+1 < srcLen { b2, _ := ctx.Mem.Read8(src + uint64(i+1)) ch := uint16(b&0x1F)<<6 | uint16(b2&0x3F) if dst != 0 { ctx.Mem.Write16(dst+uint64(written), ch) } written += 2 i += 2 } else if b < 0xF0 && i+2 < srcLen { b2, _ := ctx.Mem.Read8(src + uint64(i+1)) b3, _ := ctx.Mem.Read8(src + uint64(i+2)) ch := uint16(b&0x0F)<<12 | uint16(b2&0x3F)<<6 | uint16(b3&0x3F) if dst != 0 { ctx.Mem.Write16(dst+uint64(written), ch) } written += 2 i += 3 } else { if dst != 0 { ctx.Mem.Write16(dst+uint64(written), '?') } written += 2 i++ } } if resultLenPtr != 0 { ctx.Mem.Write32(resultLenPtr, written) } return StatusSuccess } func handleRtlUnicodeToUTF8N(ctx *TrapContext) uint32 { dst := ctx.Arg(0) dstMax := uint32(ctx.Arg(1)) resultLenPtr := ctx.Arg(2) src := ctx.Arg(3) srcLen := uint32(ctx.Arg(4)) written := uint32(0) for i := uint32(0); i+2 <= srcLen && written < dstMax; i += 2 { ch, _ := ctx.Mem.Read16(src + uint64(i)) if ch < 0x80 { if dst != 0 { ctx.Mem.Write8(dst+uint64(written), uint8(ch)) } written++ } else if ch < 0x800 { if written+2 > dstMax { break } if dst != 0 { ctx.Mem.Write8(dst+uint64(written), uint8(0xC0|(ch>>6))) ctx.Mem.Write8(dst+uint64(written+1), uint8(0x80|(ch&0x3F))) } written += 2 } else { if written+3 > dstMax { break } if dst != 0 { ctx.Mem.Write8(dst+uint64(written), uint8(0xE0|(ch>>12))) ctx.Mem.Write8(dst+uint64(written+1), uint8(0x80|((ch>>6)&0x3F))) ctx.Mem.Write8(dst+uint64(written+2), uint8(0x80|(ch&0x3F))) } written += 3 } } if resultLenPtr != 0 { ctx.Mem.Write32(resultLenPtr, written) } return StatusSuccess } func handleRtlIsNormalizedString(ctx *TrapContext) uint32 { resultPtr := ctx.Arg(2) if resultPtr != 0 { ctx.Mem.Write8(resultPtr, 1) // already normalized } return StatusSuccess } func handleRtlNormalizeString(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlSleepConditionVariableCS(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlSleepConditionVariableSRW(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlWaitOnAddress(ctx *TrapContext) uint32 { return StatusSuccess } func handleDbgBreakPoint(ctx *TrapContext) uint32 { return StatusSuccess // ignore breakpoints } func handleNtNotifyChangeKey(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtNotifyChangeDirectoryFile(ctx *TrapContext) uint32 { return StatusSuccess } // Etw stubs func handleEtwEventRegister(ctx *TrapContext) uint32 { return StatusSuccess } func handleEtwEventUnregister(ctx *TrapContext) uint32 { return StatusSuccess } func handleEtwEventWrite(ctx *TrapContext) uint32 { return StatusSuccess } func handleEtwEventEnabled(ctx *TrapContext) uint32 { ctx.SetReturn(0) // not enabled return StatusSuccess } func handleNtQueryFullAttributesFile(ctx *TrapContext) uint32 { return 0xC0000034 // STATUS_OBJECT_NAME_NOT_FOUND } func handleNtCompareObjects(ctx *TrapContext) uint32 { h1 := ctx.Arg(0) h2 := ctx.Arg(1) if h1 == h2 { return StatusSuccess } return StatusNotImplemented } func handleNtSetSystemInformation(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtSetSystemTime(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlWow64EnableFsRedirection(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlWow64EnableFsRedirectionEx(ctx *TrapContext) uint32 { return StatusSuccess } // handleLdrDisableThreadCalloutsForDll disables DLL_THREAD_ATTACH/DETACH notifications. func handleLdrDisableThreadCalloutsForDll(ctx *TrapContext) uint32 { ctx.SetReturn(0) // STATUS_SUCCESS return StatusSuccess } // handleLdrFindResource_U navigates the PE resource directory tree. // LdrFindResource_U(HMODULE hModule, LDR_RESOURCE_INFO *info, ULONG level, IMAGE_RESOURCE_DATA_ENTRY **entry) func handleLdrFindResource_U(ctx *TrapContext) uint32 { hModule := ctx.Arg(0) // module base address infoPtr := ctx.Arg(1) // LDR_RESOURCE_INFO* level := ctx.Arg(2) // number of levels (typically 3) entryPtr := ctx.Arg(3) // output IMAGE_RESOURCE_DATA_ENTRY** if hModule == 0 || entryPtr == 0 { return 0xC0000135 // STATUS_DLL_NOT_FOUND } // Read resource directory RVA from PE headers in guest memory rsrcRVA, rsrcSize := peResourceDir(ctx.Mem, hModule) if rsrcRVA == 0 || rsrcSize == 0 { return 0xC0000135 // STATUS_RESOURCE_TYPE_NOT_FOUND } rsrcBase := hModule + uint64(rsrcRVA) // LDR_RESOURCE_INFO: Type (+0), Name (+8), Language (+16) - all ULONG_PTR (8 bytes on x64) var resType, resName, resLang uint64 if infoPtr != 0 { resType, _ = ctx.Mem.Read64(infoPtr) if level >= 2 { resName, _ = ctx.Mem.Read64(infoPtr + 8) } if level >= 3 { resLang, _ = ctx.Mem.Read64(infoPtr + 16) } } // Navigate resource directory tree dirAddr := rsrcBase // Level 1: find type if level >= 1 { found, isDir := findResourceEntry(ctx.Mem, dirAddr, rsrcBase, uint32(resType)) if found == 0 { return 0xC0000135 // STATUS_RESOURCE_TYPE_NOT_FOUND } if !isDir { // Leaf at level 1 - unlikely but handle it ctx.Mem.Write64(entryPtr, found) return StatusSuccess } dirAddr = found } // Level 2: find name/ID if level >= 2 { found, isDir := findResourceEntry(ctx.Mem, dirAddr, rsrcBase, uint32(resName)) if found == 0 { // If name lookup fails and name is a string pointer, try first entry found, isDir = findResourceFirstEntry(ctx.Mem, dirAddr, rsrcBase) if found == 0 { return 0xC0000089 // STATUS_RESOURCE_NAME_NOT_FOUND } } if !isDir { ctx.Mem.Write64(entryPtr, found) return StatusSuccess } dirAddr = found } // Level 3: find language (pick best match or first) if level >= 3 { var found uint64 if resLang != 0 { found, _ = findResourceEntry(ctx.Mem, dirAddr, rsrcBase, uint32(resLang)) } if found == 0 { // Try neutral (0x0000) or English found = pickBestResourceLang(ctx.Mem, dirAddr, rsrcBase) } if found == 0 { return 0xC000008A // STATUS_RESOURCE_LANG_NOT_FOUND } ctx.Mem.Write64(entryPtr, found) return StatusSuccess } return 0xC000008A } // handleLdrAccessResource returns the address and size of a resource data entry. // LdrAccessResource(HMODULE hModule, IMAGE_RESOURCE_DATA_ENTRY *entry, void **ptr, ULONG *size) func handleLdrAccessResource(ctx *TrapContext) uint32 { hModule := ctx.Arg(0) entryAddr := ctx.Arg(1) ptrOut := ctx.Arg(2) sizeOut := ctx.Arg(3) if hModule == 0 || entryAddr == 0 { return 0xC0000135 } // IMAGE_RESOURCE_DATA_ENTRY: OffsetToData(4), Size(4), CodePage(4), Reserved(4) dataRVA, _ := ctx.Mem.Read32(entryAddr) dataSize, _ := ctx.Mem.Read32(entryAddr + 4) dataAddr := hModule + uint64(dataRVA) if ptrOut != 0 { ctx.Mem.Write64(ptrOut, dataAddr) } if sizeOut != 0 { ctx.Mem.Write32(sizeOut, dataSize) } return StatusSuccess } // peResourceDir reads the resource directory RVA and size from PE headers in guest memory. func peResourceDir(mem *mmu.MMU, baseAddr uint64) (rva, size uint32) { // Check MZ signature mz, _ := mem.Read16(baseAddr) if mz != 0x5A4D { return 0, 0 } // e_lfanew at offset 0x3C lfanew, _ := mem.Read32(baseAddr + 0x3C) peAddr := baseAddr + uint64(lfanew) // Check PE signature peSig, _ := mem.Read32(peAddr) if peSig != 0x00004550 { return 0, 0 } // Optional header starts at PE+24 // For PE32+ (64-bit), data directories start at optional header + 0x70 // Resource table is directory entry 2 (each entry = 8 bytes) optHdr := peAddr + 24 rva, _ = mem.Read32(optHdr + 0x70 + 2*8) // resource dir RVA size, _ = mem.Read32(optHdr + 0x70 + 2*8 + 4) // resource dir size return rva, size } // findResourceEntry searches a resource directory for an entry with the given ID. func findResourceEntry(mem *mmu.MMU, dirAddr, rsrcBase uint64, id uint32) (addr uint64, isDir bool) { numNamed, _ := mem.Read16(dirAddr + 12) numID, _ := mem.Read16(dirAddr + 14) total := int(numNamed) + int(numID) entryBase := dirAddr + 16 for i := 0; i < total; i++ { eid, _ := mem.Read32(entryBase + uint64(i)*8) off, _ := mem.Read32(entryBase + uint64(i)*8 + 4) if eid == id { isSubDir := off&0x80000000 != 0 return rsrcBase + uint64(off&0x7FFFFFFF), isSubDir } } return 0, false } // findResourceFirstEntry returns the first entry in a resource directory. func findResourceFirstEntry(mem *mmu.MMU, dirAddr, rsrcBase uint64) (addr uint64, isDir bool) { numNamed, _ := mem.Read16(dirAddr + 12) numID, _ := mem.Read16(dirAddr + 14) total := int(numNamed) + int(numID) if total == 0 { return 0, false } off, _ := mem.Read32(dirAddr + 16 + 4) // first entry's offset isSubDir := off&0x80000000 != 0 return rsrcBase + uint64(off&0x7FFFFFFF), isSubDir } // pickBestResourceLang picks the best language entry: neutral (0), English (0x09), or first. func pickBestResourceLang(mem *mmu.MMU, dirAddr, rsrcBase uint64) uint64 { numNamed, _ := mem.Read16(dirAddr + 12) numID, _ := mem.Read16(dirAddr + 14) total := int(numNamed) + int(numID) if total == 0 { return 0 } entryBase := dirAddr + 16 var fallback uint64 for i := 0; i < total; i++ { langID, _ := mem.Read32(entryBase + uint64(i)*8) off, _ := mem.Read32(entryBase + uint64(i)*8 + 4) addr := rsrcBase + uint64(off&0x7FFFFFFF) if i == 0 { fallback = addr } if langID == 0x0000 || langID&0xFF == 0x09 { return addr } } return fallback } // handleVsnprintf implements _vsnprintf(buf, count, fmt, args). // We do a minimal implementation that reads the format string and copies it without formatting. func handleVsnprintf(ctx *TrapContext) uint32 { buf := ctx.Arg(0) count := ctx.Arg(1) fmtAddr := ctx.Arg(2) if buf == 0 || count == 0 || fmtAddr == 0 { ctx.SetReturn(0) return StatusSuccess } // Read format string var fmtBytes []byte for i := uint64(0); i < 4096; i++ { b, _ := ctx.Mem.Read8(fmtAddr + i) if b == 0 { break } fmtBytes = append(fmtBytes, b) } // Simple: just copy format string as-is (no substitution) // This is enough for Wine debug output which uses it for logging n := uint64(len(fmtBytes)) if n >= count { n = count - 1 } for i := uint64(0); i < n; i++ { ctx.Mem.Write8(buf+i, fmtBytes[i]) } ctx.Mem.Write8(buf+n, 0) ctx.SetReturn(n) return StatusSuccess } // handleVsnwprintf implements _vsnwprintf(buf, count, fmt, args). func handleVsnwprintf(ctx *TrapContext) uint32 { buf := ctx.Arg(0) count := ctx.Arg(1) fmtAddr := ctx.Arg(2) if buf == 0 || count == 0 || fmtAddr == 0 { ctx.SetReturn(0) return StatusSuccess } // Read wide format string var fmtChars []uint16 for i := uint64(0); i < 4096; i++ { ch, _ := ctx.Mem.Read16(fmtAddr + i*2) if ch == 0 { break } fmtChars = append(fmtChars, ch) } n := uint64(len(fmtChars)) if n >= count { n = count - 1 } for i := uint64(0); i < n; i++ { ctx.Mem.Write16(buf+i*2, fmtChars[i]) } ctx.Mem.Write16(buf+n*2, 0) ctx.SetReturn(n) return StatusSuccess } // handleRtlFindClearBitsAndSet finds 'count' contiguous clear bits in a bitmap, // sets them, and returns the starting index (or 0xFFFFFFFF if not found). func handleRtlFindClearBitsAndSet(ctx *TrapContext) uint32 { bitmapAddr := ctx.Arg(0) numBits := uint32(ctx.Arg(1)) hintIdx := uint32(ctx.Arg(2)) if bitmapAddr == 0 || numBits == 0 { ctx.SetReturn(0xFFFFFFFF) return StatusSuccess } // RTL_BITMAP: { ULONG SizeOfBitMap; PULONG Buffer; } sizeOfBitMap, _ := ctx.Mem.Read32(bitmapAddr) bufferAddr, _ := ctx.Mem.Read64(bitmapAddr + 8) if bufferAddr == 0 || sizeOfBitMap == 0 { ctx.SetReturn(0xFFFFFFFF) return StatusSuccess } // Search for numBits contiguous clear bits starting from hintIdx for start := hintIdx; start+numBits <= sizeOfBitMap; start++ { found := true for bit := uint32(0); bit < numBits; bit++ { idx := start + bit wordIdx := idx / 32 bitIdx := idx % 32 word, _ := ctx.Mem.Read32(bufferAddr + uint64(wordIdx)*4) if word&(1< 0 if hintIdx > 0 { for start := uint32(0); start+numBits <= hintIdx; start++ { found := true for bit := uint32(0); bit < numBits; bit++ { idx := start + bit wordIdx := idx / 32 bitIdx := idx % 32 word, _ := ctx.Mem.Read32(bufferAddr + uint64(wordIdx)*4) if word&(1< 0 { name = readGuestStringUTF16(ctx.Mem, namePtr, int(nameLen)) } atom, ok := atomTable[name] if !ok { atom = atomCounter atomCounter++ atomTable[name] = atom } if atomPtr != 0 { ctx.Mem.Write16(atomPtr, uint16(atom)) } return StatusSuccess } func handleNtDeleteAtom(ctx *TrapContext) uint32 { return StatusSuccess } func handleNtFindAtom(ctx *TrapContext) uint32 { namePtr := ctx.Arg(0) nameLen := ctx.Arg(1) atomPtr := ctx.Arg(2) name := "" if namePtr != 0 && nameLen > 0 { name = readGuestStringUTF16(ctx.Mem, namePtr, int(nameLen)) } atom, ok := atomTable[name] if !ok { return StatusObjectNameNotFound } if atomPtr != 0 { ctx.Mem.Write16(atomPtr, uint16(atom)) } return StatusSuccess } func handleNtQueryInformationAtom(ctx *TrapContext) uint32 { atom := uint32(ctx.Arg(0)) infoClass := uint32(ctx.Arg(1)) outBuf := ctx.Arg(2) outLen := uint32(ctx.Arg(3)) retLen := ctx.Arg(4) var atomName string for k, v := range atomTable { if v == atom { atomName = k break } } if atomName == "" { return StatusObjectNameNotFound } switch infoClass { case 0: // AtomBasicInformation nameUTF16 := encodeUTF16LE(atomName) needed := uint32(8 + len(nameUTF16)) if retLen != 0 { ctx.Mem.Write32(retLen, needed) } if outBuf == 0 || outLen < needed { return StatusBufferTooSmall } ctx.Mem.Write16(outBuf+0, uint16(atom)) // Atom ctx.Mem.Write16(outBuf+2, uint16(len(nameUTF16))) // NameLength (bytes) ctx.Mem.Write16(outBuf+4, 1) // UsageCount ctx.Mem.Write16(outBuf+6, 0) // Flags ctx.Mem.WriteBytes(outBuf+8, nameUTF16) // Name return StatusSuccess case 1: // AtomTableInformation (minimal) if retLen != 0 { ctx.Mem.Write32(retLen, 4) } if outBuf == 0 || outLen < 4 { return StatusBufferTooSmall } ctx.Mem.Write32(outBuf, uint32(len(atomTable))) return StatusSuccess default: return StatusNotImplemented } } // ======================== NtUserCallOneParam / NtUserCallTwoParam ======================== func handleNtUserCallOneParam(ctx *TrapContext) uint32 { param := ctx.Arg(0) code := ctx.Arg(1) // function code if ctx.Trace { fmt.Printf(" NtUserCallOneParam: code=%d param=0x%x\n", code, param) } switch code { case 11: // ONEPARAM_ROUTINE_GETSYSTEMMETRICS ctx.SetReturn(getSystemMetric(uint32(param))) case 21: // ONEPARAM_ROUTINE_SETCARETBLINKTIME ctx.SetReturn(1) // TRUE case 26: // ONEPARAM_ROUTINE_SETPROCESSDEFAULTLAYOUT ctx.SetReturn(1) case 28: // ONEPARAM_ROUTINE_CREATEEMPTYCURSOROBJECT h := ctx.Handles.Alloc(&GenericHandle{Kind: "cursor"}) ctx.SetReturn(uint64(h)) case 37: // ONEPARAM_ROUTINE_GETDPIFORMONITOR ctx.SetReturn(96) // 96 DPI case 73: // ONEPARAM_ROUTINE_RELEASEDC ctx.SetReturn(1) default: ctx.SetReturn(0) } return StatusSuccess } func getSystemMetric(index uint32) uint64 { switch index { case 0: // SM_CXSCREEN return 1920 case 1: // SM_CYSCREEN return 1080 case 2: // SM_CXVSCROLL return 17 case 3: // SM_CYHSCROLL return 17 case 4: // SM_CYCAPTION return 23 case 5: // SM_CXBORDER return 1 case 6: // SM_CYBORDER return 1 case 7: // SM_CXDLGFRAME / SM_CXFIXEDFRAME return 3 case 8: // SM_CYDLGFRAME / SM_CYFIXEDFRAME return 3 case 11: // SM_CXHTHUMB return 17 case 12: // SM_CYICON return 32 case 13: // SM_CXCURSOR (also SM_CXICON) return 32 case 14: // SM_CYCURSOR return 32 case 15: // SM_CYMENU return 20 case 16: // SM_CXFULLSCREEN return 1920 case 17: // SM_CYFULLSCREEN return 1017 // 1080 - caption - menu case 18: // SM_CYKANJIWINDOW return 0 case 20: // SM_CXHSCROLL return 17 case 21: // SM_CYVSCROLL return 17 case 22: // SM_CXMIN return 136 case 23: // SM_CYMIN return 39 case 24: // SM_CXSIZE return 36 case 25: // SM_CYSIZE return 36 case 26: // SM_CXFRAME / SM_CXSIZEFRAME return 4 case 27: // SM_CYFRAME / SM_CYSIZEFRAME return 4 case 28: // SM_CXMINTRACK return 136 case 29: // SM_CYMINTRACK return 39 case 30: // SM_CXDOUBLECLK return 4 case 31: // SM_CYDOUBLECLK return 4 case 32: // SM_CXICONSPACING return 75 case 33: // SM_CYICONSPACING return 75 case 34: // SM_MENUDROPALIGNMENT return 0 case 36: // SM_CXDRAG return 4 case 37: // SM_CYDRAG return 4 case 43: // SM_SWAPBUTTON return 0 case 45: // SM_CXEDGE (SM_CXBORDER*2) return 2 case 46: // SM_CYEDGE (SM_CYBORDER*2) return 2 case 47: // SM_CXMINSPACING return 160 case 48: // SM_CYMINSPACING return 24 case 49: // SM_CXSMICON return 16 case 50: // SM_CYSMICON return 16 case 51: // SM_CYSMCAPTION return 23 case 52: // SM_CXSMSIZE return 12 case 53: // SM_CYSMSIZE return 14 case 54: // SM_CXMENUSIZE return 18 case 55: // SM_CYMENUSIZE return 18 case 67: // SM_IMMENABLED return 0 case 70: // SM_CMETRICS return 76 case 75: // SM_CXFOCUSBORDER return 1 case 76: // SM_CYFOCUSBORDER return 1 case 78: // SM_CXPADDEDBORDER return 4 case 80: // SM_DIGITIZER return 0 default: return 0 } } func handleNtUserCallTwoParam(ctx *TrapContext) uint32 { // param1 := ctx.Arg(0) // param2 := ctx.Arg(1) // code := ctx.Arg(2) ctx.SetReturn(0) // generic success return StatusSuccess } // ---- SList (Singly-Linked List) operations ---- // SLIST_HEADER is 16 bytes: { NextEntry uint64; Depth uint16; CpuId uint16; pad uint32 } func handleRtlInitializeSListHead(ctx *TrapContext) uint32 { head := ctx.Arg(0) if head != 0 { ctx.Mem.Write64(head, 0) // NextEntry = NULL ctx.Mem.Write64(head+8, 0) // Depth=0, CpuId=0 } return StatusSuccess } func handleRtlInterlockedFlushSList(ctx *TrapContext) uint32 { head := ctx.Arg(0) if head == 0 { ctx.SetReturn(0) return StatusSuccess } first, _ := ctx.Mem.Read64(head) ctx.Mem.Write64(head, 0) // clear list ctx.Mem.Write64(head+8, 0) // depth=0 ctx.SetReturn(first) return StatusSuccess } func handleRtlInterlockedPopEntrySList(ctx *TrapContext) uint32 { head := ctx.Arg(0) if head == 0 { ctx.SetReturn(0) return StatusSuccess } first, _ := ctx.Mem.Read64(head) if first == 0 { ctx.SetReturn(0) return StatusSuccess } next, _ := ctx.Mem.Read64(first) // SLIST_ENTRY.Next ctx.Mem.Write64(head, next) // Decrement depth depthWord, _ := ctx.Mem.Read16(head + 8) if depthWord > 0 { ctx.Mem.Write16(head+8, depthWord-1) } ctx.SetReturn(first) return StatusSuccess } func handleRtlInterlockedPushEntrySList(ctx *TrapContext) uint32 { head := ctx.Arg(0) entry := ctx.Arg(1) if head == 0 || entry == 0 { ctx.SetReturn(0) return StatusSuccess } oldFirst, _ := ctx.Mem.Read64(head) ctx.Mem.Write64(entry, oldFirst) // entry->Next = old first ctx.Mem.Write64(head, entry) // head->NextEntry = entry // Increment depth depthWord, _ := ctx.Mem.Read16(head + 8) ctx.Mem.Write16(head+8, depthWord+1) ctx.SetReturn(oldFirst) return StatusSuccess } func handleRtlQueryDepthSList(ctx *TrapContext) uint32 { head := ctx.Arg(0) if head == 0 { ctx.SetReturn(0) return StatusSuccess } depth, _ := ctx.Mem.Read16(head + 8) ctx.SetReturn(uint64(depth)) return StatusSuccess } // ---- Performance Counter ---- var perfFreq uint64 = 1_000_000_000 // 1GHz (nanosecond resolution) func handleRtlQueryPerformanceCounter(ctx *TrapContext) uint32 { out := ctx.Arg(0) // LARGE_INTEGER* if out != 0 { ctx.Mem.Write64(out, uint64(time.Now().UnixNano())) } ctx.SetReturn(1) // TRUE return StatusSuccess } func handleRtlQueryPerformanceFrequency(ctx *TrapContext) uint32 { out := ctx.Arg(0) // LARGE_INTEGER* if out != 0 { ctx.Mem.Write64(out, perfFreq) } ctx.SetReturn(1) // TRUE return StatusSuccess } // ---- Misc ---- func handleRtlRandom(ctx *TrapContext) uint32 { seedPtr := ctx.Arg(0) // ULONG* var seed uint32 if seedPtr != 0 { v, _ := ctx.Mem.Read32(seedPtr) seed = v } // Simple LCG seed = seed*214013 + 2531011 if seedPtr != 0 { ctx.Mem.Write32(seedPtr, seed) } ctx.SetReturn(uint64(seed >> 16 & 0x7FFF)) return StatusSuccess } func handleWineGetHostVersion(ctx *TrapContext) uint32 { // wine_get_host_version(const char **sysname, const char **release) sysname := ctx.Arg(0) release := ctx.Arg(1) if sysname != 0 { buf := ctx.Mem.Mmap(0, 16, mmu.PermRead|mmu.PermWrite) for i, b := range []byte("Linux") { ctx.Mem.Write8(buf+uint64(i), b) } ctx.Mem.Write8(buf+5, 0) ctx.Mem.Write64(sysname, buf) } if release != 0 { buf := ctx.Mem.Mmap(0, 16, mmu.PermRead|mmu.PermWrite) for i, b := range []byte("6.1.0") { ctx.Mem.Write8(buf+uint64(i), b) } ctx.Mem.Write8(buf+5, 0) ctx.Mem.Write64(release, buf) } return StatusSuccess } // ======================== NtGdi* - GDI Object Stubs ======================== // GDI object type codes (for GdiSharedHandleTable entry+0x0E). const ( gdiObjDC = 0x01 gdiObjBrush = 0x02 gdiObjPen = 0x03 gdiObjPal = 0x05 gdiObjFont = 0x06 gdiObjBitmap = 0x07 gdiObjRegion = 0x08 ) const gdiEntrySize = 24 // allocGdiHandle allocates a handle and writes a corresponding entry in the // GDI shared handle table so gdi32 user-mode code can validate it. func allocGdiHandle(ctx *TrapContext, kind string, objType byte) Handle { h := ctx.Handles.Alloc(&GenericHandle{Kind: kind}) idx := uint64(h) & 0xFFFF if ctx.State.GdiTableAddr != 0 && idx < 0x4000 { entry := ctx.State.GdiTableAddr + idx*gdiEntrySize ctx.Mem.Write8(entry+0x0E, objType) } return h } func handleNtGdiCreateBitmap(ctx *TrapContext) uint32 { // NtGdiCreateBitmap(width, height, planes, bpp, bits) w := ctx.Arg(0) h := ctx.Arg(1) if ctx.Trace { fmt.Printf(" NtGdiCreateBitmap: %dx%d planes=%d bpp=%d\n", w, h, ctx.Arg(2), ctx.Arg(3)) } handle := allocGdiHandle(ctx, "bitmap", gdiObjBitmap) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiCreatePatternBrushInternal(ctx *TrapContext) uint32 { bitmapHandle := ctx.Arg(0) if ctx.Trace { fmt.Printf(" NtGdiCreatePatternBrushInternal: bitmap=0x%x\n", bitmapHandle) } handle := allocGdiHandle(ctx, "brush", gdiObjBrush) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiDeleteObjectApp(ctx *TrapContext) uint32 { h := Handle(ctx.Arg(0)) // Clear GDI table entry idx := uint64(h) & 0xFFFF if ctx.State.GdiTableAddr != 0 && idx < 0x4000 { entry := ctx.State.GdiTableAddr + idx*gdiEntrySize ctx.Mem.Write8(entry+0x0E, 0) } ctx.Handles.Close(h) ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiSelectBitmap(ctx *TrapContext) uint32 { // Returns previous bitmap handle (fake) ctx.SetReturn(ctx.Arg(1)) // return the bitmap being selected return StatusSuccess } func handleNtGdiSelectBrush(ctx *TrapContext) uint32 { ctx.SetReturn(ctx.Arg(1)) return StatusSuccess } func handleNtGdiSelectPen(ctx *TrapContext) uint32 { ctx.SetReturn(ctx.Arg(1)) return StatusSuccess } func handleNtGdiSelectFont(ctx *TrapContext) uint32 { ctx.SetReturn(ctx.Arg(1)) return StatusSuccess } func handleNtGdiCreateSolidBrush(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "brush", gdiObjBrush) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiCreatePen(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "pen", gdiObjPen) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiCreateRectRgn(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "region", gdiObjRegion) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiCreateCompatibleDC(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "dc", gdiObjDC) ctx.SetReturn(uint64(handle)) return StatusSuccess } // handleNtGdiGetStockObject returns a ReactOS-format stock-object HGDIOBJ for a // stock index (WHITE_BRUSH=0 ... DC_PEN=19). The handle encodes the object type in // bits 16-22 (GDILoObjType_LO_*_TYPE) plus the stock mask (0x00800000); gdi32 // caches it and passes it to SelectObject/GetObject. Stock objects share a fixed // low index range so they never collide with allocated handles. func handleNtGdiGetStockObject(ctx *TrapContext) uint32 { idx := ctx.Arg(0) var loType uint32 switch { case idx <= 5 || idx == 18: // WHITE..DKGRAY..HOLLOW brush, DC_BRUSH loType = 0x00100000 // LO_BRUSH_TYPE case (idx >= 6 && idx <= 8) || idx == 19: // WHITE/BLACK/NULL pen, DC_PEN loType = 0x00300000 // LO_PEN_TYPE case idx >= 10 && idx <= 17: // stock fonts (OEM..DEFAULT_GUI) loType = 0x000a0000 // LO_FONT_TYPE (GDIObjType_LFONT_TYPE << 16) case idx == 9 || idx == 14: // *_PALETTE loType = 0x00080000 // LO_PALETTE_TYPE default: loType = 0x00100000 } // stock index lives in the reserved low table range [0x20, 0x40) handle := 0x00800000 | loType | (0x20 + uint32(idx&0x1F)) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiCreateCompatibleBitmap(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "bitmap", gdiObjBitmap) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiHfontCreate(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "font", gdiObjFont) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiExtGetObjectW(ctx *TrapContext) uint32 { // Returns the size of data written (0 = error, but return 1 for basic compat) ctx.SetReturn(0) return StatusSuccess } func handleNtGdiGetDeviceCaps(ctx *TrapContext) uint32 { index := ctx.Arg(1) var val uint64 switch index { case 8: // HORZRES val = 1920 case 10: // VERTRES val = 1080 case 12: // BITSPIXEL val = 32 case 14: // PLANES val = 1 case 88: // LOGPIXELSX val = 96 case 90: // LOGPIXELSY val = 96 case 118: // DESKTOPHORZRES val = 1920 case 117: // DESKTOPVERTRES val = 1080 default: val = 0 } ctx.SetReturn(val) return StatusSuccess } func handleNtGdiGetAndSetDCDword(ctx *TrapContext) uint32 { resultPtr := ctx.Arg(3) if resultPtr != 0 { ctx.Mem.Write32(resultPtr, 0) } ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiSaveDC(ctx *TrapContext) uint32 { ctx.SetReturn(1) // Save level 1 return StatusSuccess } func handleNtGdiRestoreDC(ctx *TrapContext) uint32 { ctx.SetReturn(1) return StatusSuccess } func handleNtGdiCombineRgn(ctx *TrapContext) uint32 { ctx.SetReturn(1) // NULLREGION return StatusSuccess } func handleNtGdiGetRgnBox(ctx *TrapContext) uint32 { rectPtr := ctx.Arg(1) if rectPtr != 0 { ctx.Mem.Write32(rectPtr, 0) // left ctx.Mem.Write32(rectPtr+4, 0) // top ctx.Mem.Write32(rectPtr+8, 0) // right ctx.Mem.Write32(rectPtr+12, 0) // bottom } ctx.SetReturn(1) // NULLREGION return StatusSuccess } func handleNtGdiSetRectRgn(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiOffsetRgn(ctx *TrapContext) uint32 { ctx.SetReturn(1) // NULLREGION return StatusSuccess } func handleNtGdiPatBlt(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiBitBlt(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiOpenDCW(ctx *TrapContext) uint32 { handle := allocGdiHandle(ctx, "dc", gdiObjDC) ctx.SetReturn(uint64(handle)) return StatusSuccess } func handleNtGdiGetTextMetricsW(ctx *TrapContext) uint32 { tmPtr := ctx.Arg(1) if tmPtr != 0 { // Fill TEXTMETRICW with reasonable defaults (60 bytes) for i := uint64(0); i < 60; i += 4 { ctx.Mem.Write32(tmPtr+i, 0) } ctx.Mem.Write32(tmPtr+0, 16) // tmHeight ctx.Mem.Write32(tmPtr+4, 12) // tmAscent ctx.Mem.Write32(tmPtr+8, 4) // tmDescent ctx.Mem.Write32(tmPtr+20, 8) // tmAveCharWidth ctx.Mem.Write32(tmPtr+24, 16) // tmMaxCharWidth } ctx.SetReturn(1) // TRUE return StatusSuccess } func handleNtGdiFlush(ctx *TrapContext) uint32 { ctx.SetReturn(1) // TRUE return StatusSuccess } // ======================== CRT HANDLERS (complex, trap-based) ======================== func handleStrtol(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) endptrAddr := ctx.Arg(1) base := ctx.Arg(2) s := readCString(ctx.Mem, strAddr, 64) val, _ := strconvParseInt(s, int(base)) ctx.CPU.SetSyscallReturn(uint64(val)) ctx.ReturnOverridden = true if endptrAddr != 0 { ctx.Mem.Write64(endptrAddr, strAddr+uint64(len(s))) } return 0 } func handleWcstol(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) endptrAddr := ctx.Arg(1) base := ctx.Arg(2) s := readWideString(ctx.Mem, strAddr, 64) val, _ := strconvParseInt(s, int(base)) ctx.CPU.SetSyscallReturn(uint64(val)) ctx.ReturnOverridden = true if endptrAddr != 0 { ctx.Mem.Write64(endptrAddr, strAddr+uint64(len(s)*2)) } return 0 } func handleWcstoul(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) endptrAddr := ctx.Arg(1) base := ctx.Arg(2) s := readWideString(ctx.Mem, strAddr, 64) val, _ := strconvParseUint(s, int(base)) ctx.CPU.SetSyscallReturn(val) ctx.ReturnOverridden = true if endptrAddr != 0 { ctx.Mem.Write64(endptrAddr, strAddr+uint64(len(s)*2)) } return 0 } func handleStrnicmp(ctx *TrapContext) uint32 { s1Addr := ctx.Arg(0) s2Addr := ctx.Arg(1) n := ctx.Arg(2) for i := uint64(0); i < n; i++ { c1, _ := ctx.Mem.Read8(s1Addr + i) c2, _ := ctx.Mem.Read8(s2Addr + i) if c1 == 0 && c2 == 0 { return 0 } l1, l2 := toLowerByte(c1), toLowerByte(c2) if l1 != l2 { if l1 < l2 { ctx.CPU.SetSyscallReturn(^uint64(0)) // -1 } else { ctx.CPU.SetSyscallReturn(1) } ctx.ReturnOverridden = true return 0 } if c1 == 0 { return 0 } } return 0 } func handleWcsicmp(ctx *TrapContext) uint32 { s1Addr := ctx.Arg(0) s2Addr := ctx.Arg(1) for i := uint64(0); ; i += 2 { c1, _ := ctx.Mem.Read16(s1Addr + i) c2, _ := ctx.Mem.Read16(s2Addr + i) l1, l2 := toLowerWide(c1), toLowerWide(c2) if l1 != l2 { if l1 < l2 { ctx.CPU.SetSyscallReturn(^uint64(0)) } else { ctx.CPU.SetSyscallReturn(1) } ctx.ReturnOverridden = true return 0 } if c1 == 0 { return 0 } } } func handleWcsnicmp(ctx *TrapContext) uint32 { s1Addr := ctx.Arg(0) s2Addr := ctx.Arg(1) n := ctx.Arg(2) for i := uint64(0); i < n; i++ { c1, _ := ctx.Mem.Read16(s1Addr + i*2) c2, _ := ctx.Mem.Read16(s2Addr + i*2) l1, l2 := toLowerWide(c1), toLowerWide(c2) if l1 != l2 { if l1 < l2 { ctx.CPU.SetSyscallReturn(^uint64(0)) } else { ctx.CPU.SetSyscallReturn(1) } ctx.ReturnOverridden = true return 0 } if c1 == 0 { return 0 } } return 0 } func handleIsalpha(ctx *TrapContext) uint32 { c := byte(ctx.Arg(0)) if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') { ctx.CPU.SetSyscallReturn(1) ctx.ReturnOverridden = true } return 0 } func handleIslower(ctx *TrapContext) uint32 { c := byte(ctx.Arg(0)) if c >= 'a' && c <= 'z' { ctx.CPU.SetSyscallReturn(1) ctx.ReturnOverridden = true } return 0 } func handleIsxdigit(ctx *TrapContext) uint32 { c := byte(ctx.Arg(0)) if (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') { ctx.CPU.SetSyscallReturn(1) ctx.ReturnOverridden = true } return 0 } func handleStrpbrk(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) charsAddr := ctx.Arg(1) accept := readCString(ctx.Mem, charsAddr, 256) acceptSet := make(map[byte]bool) for _, c := range []byte(accept) { acceptSet[c] = true } for i := uint64(0); ; i++ { c, _ := ctx.Mem.Read8(strAddr + i) if c == 0 { break } if acceptSet[c] { ctx.CPU.SetSyscallReturn(strAddr + i) ctx.ReturnOverridden = true return 0 } } ctx.CPU.SetSyscallReturn(0) ctx.ReturnOverridden = true return 0 } func handleWcspbrk(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) charsAddr := ctx.Arg(1) acceptSet := make(map[uint16]bool) for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(charsAddr + i) if c == 0 { break } acceptSet[c] = true } for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(strAddr + i) if c == 0 { break } if acceptSet[c] { ctx.CPU.SetSyscallReturn(strAddr + i) ctx.ReturnOverridden = true return 0 } } ctx.CPU.SetSyscallReturn(0) ctx.ReturnOverridden = true return 0 } func handleWcscspn(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) rejectAddr := ctx.Arg(1) rejectSet := make(map[uint16]bool) for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(rejectAddr + i) if c == 0 { break } rejectSet[c] = true } count := uint64(0) for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(strAddr + i) if c == 0 || rejectSet[c] { break } count++ } ctx.CPU.SetSyscallReturn(count) ctx.ReturnOverridden = true return 0 } func handleWcsspn(ctx *TrapContext) uint32 { strAddr := ctx.Arg(0) acceptAddr := ctx.Arg(1) acceptSet := make(map[uint16]bool) for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(acceptAddr + i) if c == 0 { break } acceptSet[c] = true } count := uint64(0) for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(strAddr + i) if c == 0 || !acceptSet[c] { break } count++ } ctx.CPU.SetSyscallReturn(count) ctx.ReturnOverridden = true return 0 } func handleWcsstr(ctx *TrapContext) uint32 { haystackAddr := ctx.Arg(0) needleAddr := ctx.Arg(1) needle := readWideChars(ctx.Mem, needleAddr, 256) if len(needle) == 0 { ctx.CPU.SetSyscallReturn(haystackAddr) ctx.ReturnOverridden = true return 0 } for i := uint64(0); ; i += 2 { c, _ := ctx.Mem.Read16(haystackAddr + i) if c == 0 { break } if c == needle[0] { match := true for j := 1; j < len(needle); j++ { h, _ := ctx.Mem.Read16(haystackAddr + i + uint64(j*2)) if h != needle[j] { match = false break } } if match { ctx.CPU.SetSyscallReturn(haystackAddr + i) ctx.ReturnOverridden = true return 0 } } } ctx.CPU.SetSyscallReturn(0) ctx.ReturnOverridden = true return 0 } // helpers func toLowerByte(c byte) byte { if c >= 'A' && c <= 'Z' { return c + 0x20 } return c } func toLowerWide(c uint16) uint16 { if c >= 'A' && c <= 'Z' { return c + 0x20 } return c } func readWideChars(mem *mmu.MMU, addr uint64, maxChars int) []uint16 { var result []uint16 for i := 0; i < maxChars; i++ { c, err := mem.Read16(addr + uint64(i*2)) if err != nil || c == 0 { break } result = append(result, c) } return result } func readWideString(mem *mmu.MMU, addr uint64, maxChars int) string { chars := readWideChars(mem, addr, maxChars) buf := make([]byte, 0, len(chars)) for _, c := range chars { if c < 128 { buf = append(buf, byte(c)) } else { buf = append(buf, '?') } } return string(buf) } func readCString(mem *mmu.MMU, addr uint64, maxLen int) string { var buf []byte for i := 0; i < maxLen; i++ { b, err := mem.Read8(addr + uint64(i)) if err != nil || b == 0 { break } buf = append(buf, b) } return string(buf) } func strconvParseInt(s string, base int) (int64, error) { i := 0 for i < len(s) && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r') { i++ } if i >= len(s) { return 0, nil } neg := false if s[i] == '-' { neg = true i++ } else if s[i] == '+' { i++ } if base == 0 { if i+1 < len(s) && s[i] == '0' && (s[i+1] == 'x' || s[i+1] == 'X') { base = 16 i += 2 } else if i < len(s) && s[i] == '0' { base = 8 i++ } else { base = 10 } } var val int64 for i < len(s) { c := s[i] var digit int64 if c >= '0' && c <= '9' { digit = int64(c - '0') } else if c >= 'a' && c <= 'f' { digit = int64(c-'a') + 10 } else if c >= 'A' && c <= 'F' { digit = int64(c-'A') + 10 } else { break } if digit >= int64(base) { break } val = val*int64(base) + digit i++ } if neg { val = -val } return val, nil } func strconvParseUint(s string, base int) (uint64, error) { v, err := strconvParseInt(s, base) return uint64(v), err } // ────────────────────────────────────────────────────────── // Wave 10: RtlEncodePointer / RtlDecodePointer // Windows Vista+ uses a per-process cookie XOR to encode pointers // stored in function pointer tables (CRT uses this heavily). // Reference: wine/dlls/ntdll/misc.c, reactos/lib/rtl/process.c // ────────────────────────────────────────────────────────── // processCookie is a process-wide random value used for pointer encoding. // In Windows it is stored at PEB+0x700, but for HLE purposes we keep it here. var processCookie = func() uint64 { var b [8]byte unix.Getrandom(b[:], 0) v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 if v == 0 { v = 0xDEADBEEFCAFEBABE } return v }() func handleRtlEncodePointer(ctx *TrapContext) uint32 { ptr := ctx.Arg(0) ctx.SetReturn(ptr ^ processCookie) return StatusSuccess } func handleRtlDecodePointer(ctx *TrapContext) uint32 { ptr := ctx.Arg(0) ctx.SetReturn(ptr ^ processCookie) return StatusSuccess } func handleRtlEncodeSystemPointer(ctx *TrapContext) uint32 { ptr := ctx.Arg(0) ctx.SetReturn(ptr ^ processCookie) return StatusSuccess } func handleRtlDecodeSystemPointer(ctx *TrapContext) uint32 { ptr := ctx.Arg(0) ctx.SetReturn(ptr ^ processCookie) return StatusSuccess } // ────────────────────────────────────────────────────────── // NtTerminateThread - terminate a specific thread // Reference: reactos/ntoskrnl/ps/kill.c // ────────────────────────────────────────────────────────── func handleNtTerminateThread(ctx *TrapContext) uint32 { // NtTerminateThread(ThreadHandle, ExitStatus) // ThreadHandle == NULL or current thread → exit the process (single-threaded HLE) hThread := ctx.Arg(0) exitCode := uint32(ctx.Arg(1)) _ = hThread // In single-threaded HLE, terminating any thread terminates the process os.Exit(int(exitCode)) return StatusSuccess } // ────────────────────────────────────────────────────────── // NtCreateJobObject / NtOpenJobObject / NtAssignProcessToJobObject // Reference: reactos/ntoskrnl/ps/job.c // ────────────────────────────────────────────────────────── func handleNtCreateJobObject(ctx *TrapContext) uint32 { outHandle := ctx.Arg(0) // arg1=DesiredAccess, arg2=ObjectAttributes name := "" j := NewJobHandle(name) h := ctx.Handles.Alloc(j) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } func handleNtOpenJobObject(ctx *TrapContext) uint32 { outHandle := ctx.Arg(0) j := NewJobHandle("") h := ctx.Handles.Alloc(j) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } func handleNtAssignProcessToJobObject(ctx *TrapContext) uint32 { hJob := Handle(ctx.Arg(0)) // hProcess := Handle(ctx.Arg(1)) j := ctx.Handles.GetJob(hJob) if j != nil { j.AddProcess(ctx.State.ProcessId) } return StatusSuccess } func handleNtQueryInformationJobObject(ctx *TrapContext) uint32 { // NtQueryInformationJobObject(JobHandle, InfoClass, Info, InfoLen, ReturnLen*) // Class 1 = JobObjectBasicAccountingInformation - return zeros (no limits) infoPtr := ctx.Arg(2) infoLen := ctx.Arg(3) retLen := ctx.Arg(4) if infoPtr != 0 && infoLen > 0 { for i := uint64(0); i < infoLen; i++ { ctx.Mem.Write8(infoPtr+i, 0) } } if retLen != 0 { ctx.Mem.Write32(retLen, uint32(infoLen)) } return StatusSuccess } // ────────────────────────────────────────────────────────── // NtCreateThread / NtCreateThreadEx (stub - Raven is single-threaded for now) // Reference: reactos/ntoskrnl/ps/thread.c // ────────────────────────────────────────────────────────── func handleNtCreateThread(ctx *TrapContext) uint32 { // NtCreateThread(ThreadHandle*, Access, ObjAttr*, ProcessHandle, // // ClientId*, ThreadContext*, InitialTeb*, Suspended) // // We don't support true multi-threaded guests; return a pseudo-handle. outHandle := ctx.Arg(0) tid := 1 if ctx.State != nil && ctx.State.ThreadId != 0 { tid = int(ctx.State.ThreadId) } h := ctx.Handles.Alloc(&ThreadHandle{ TID: tid, Done: make(chan struct{}), Priority: 8, }) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } func handleNtCreateThreadEx(ctx *TrapContext) uint32 { outHandle := ctx.Arg(0) tid := 1 if ctx.State != nil && ctx.State.ThreadId != 0 { tid = int(ctx.State.ThreadId) } h := ctx.Handles.Alloc(&ThreadHandle{ TID: tid, Done: make(chan struct{}), Priority: 8, }) if outHandle != 0 { ctx.Mem.Write64(outHandle, uint64(h)) } return StatusSuccess } // ────────────────────────────────────────────────────────── // NtSetTimerEx - extended timer with flags // ────────────────────────────────────────────────────────── func handleNtSetTimerEx(ctx *TrapContext) uint32 { return handleNtSetTimer(ctx) } // ────────────────────────────────────────────────────────── // RtlAddFunctionTable / RtlInstallFunctionTableCallback / RtlDeleteFunctionTable // Used by MSVC for x64 exception handling (SEH) - we return TRUE (registered). // Reference: wine/dlls/ntdll/unwind.c // ────────────────────────────────────────────────────────── func handleRtlAddFunctionTable(ctx *TrapContext) uint32 { if AddDynamicFunctionTable(ctx.Mem, ctx.Arg(0), uint32(ctx.Arg(1)), ctx.Arg(2)) { ctx.SetReturn(1) } else { ctx.SetReturn(0) } return StatusSuccess } func handleRtlInstallFunctionTableCallback(ctx *TrapContext) uint32 { if InstallDynamicFunctionTableCallback(ctx.Arg(0), ctx.Arg(1), ctx.Arg(2), ctx.Arg(3), ctx.Arg(4)) { ctx.SetReturn(1) } else { ctx.SetReturn(0) } return StatusSuccess } func handleRtlDeleteFunctionTable(ctx *TrapContext) uint32 { if DeleteDynamicFunctionTable(ctx.Arg(0)) { ctx.SetReturn(1) } else { ctx.SetReturn(0) } return StatusSuccess } func handleRtlAddGrowableFunctionTable(ctx *TrapContext) uint32 { ctx.SetReturn(0) // STATUS_SUCCESS return StatusSuccess } func handleRtlGrowFunctionTable(ctx *TrapContext) uint32 { return StatusSuccess } func handleRtlDeleteGrowableFunctionTable(ctx *TrapContext) uint32 { return StatusSuccess } // ────────────────────────────────────────────────────────── // RtlVirtualUnwind - x64 SEH virtual unwind // Reference: wine/dlls/ntdll/signal_x86_64.c // ────────────────────────────────────────────────────────── func handleRtlVirtualUnwind(ctx *TrapContext) uint32 { contextRecord, err := ReadAMD64Context(ctx.Mem, ctx.Arg(4)) if err != nil { ctx.SetReturn(0) return StatusSuccess } functionEntry := ReadRuntimeFunctionEntry(ctx.Mem, ctx.Arg(3)) if functionEntry == nil { trampoline := AllocTrampoline(ctx.Mem) guestCall := func(addr uint64, args []uint64) (uint64, error) { return CallGuestFunction(ctx.CPU, ctx.Mem, trampoline, addr, args, ctx.Trace) } functionEntry, _, _ = LookupFunctionEntry(ctx.Mem, ctx.Arg(2), guestCall) } handlerAddr, handlerData, establisherFrame, err := VirtualUnwind( ctx.Mem, uint32(ctx.Arg(0)), ctx.Arg(1), ctx.Arg(2), functionEntry, contextRecord, ) if err == nil { _ = WriteAMD64Context(ctx.Mem, ctx.Arg(4), contextRecord) if ctx.Arg(5) != 0 { ctx.Mem.Write64(ctx.Arg(5), handlerData) } if ctx.Arg(6) != 0 { ctx.Mem.Write64(ctx.Arg(6), establisherFrame) } } ctx.SetReturn(handlerAddr) return StatusSuccess } // ────────────────────────────────────────────────────────── // NtAreMappedFilesTheSame // ────────────────────────────────────────────────────────── func handleNtAreMappedFilesTheSame(ctx *TrapContext) uint32 { if ctx.Arg(0) == ctx.Arg(1) { return StatusSuccess } return 0xC0000002 // STATUS_NOT_SAME_DEVICE } const ( cmResourceTypePort = 1 cmResourceTypeMemory = 3 cmResourceTypeMemoryLarge = 7 cmResourceMemoryLargeMask = 0x0e00 cmResourceMemoryLarge40 = 0x0200 cmResourceMemoryLarge48 = 0x0400 cmResourceMemoryLarge64 = 0x0800 ) func decodeMemIoLength(resourceType byte, flags uint16, encoded uint32) (uint64, bool) { switch resourceType { case cmResourceTypePort, cmResourceTypeMemory: return uint64(encoded), true case cmResourceTypeMemoryLarge: var shift uint switch flags & cmResourceMemoryLargeMask { case cmResourceMemoryLarge40: shift = 8 case cmResourceMemoryLarge48: shift = 16 case cmResourceMemoryLarge64: shift = 32 default: return 0, false } return uint64(encoded) << shift, true default: return 0, false } } func setUint64Return(ctx *TrapContext, value uint64) { if ctx.CPU.GetArch() == arch.ArchI386 { if regs, ok := ctx.CPU.(RegisterWriter); ok { regs.WriteReg(regRDX, value>>32) } ctx.SetReturn(uint64(uint32(value))) return } ctx.SetReturn(value) } func handleRtlCmDecodeMemIoResource(ctx *TrapContext) uint32 { descriptor := ctx.Arg(0) startOut := ctx.Arg(1) resourceType, typeErr := ctx.Mem.Read8(descriptor) flags, flagsErr := ctx.Mem.Read16(descriptor + 2) start, startErr := ctx.Mem.Read64(descriptor + 4) encodedLength, lengthErr := ctx.Mem.Read32(descriptor + 12) if typeErr != nil || flagsErr != nil || startErr != nil || lengthErr != nil { setUint64Return(ctx, 0) return StatusSuccess } length, valid := decodeMemIoLength(resourceType, flags, encodedLength) if !valid { setUint64Return(ctx, 0) return StatusSuccess } if startOut != 0 { ctx.Mem.Write64(startOut, start) } setUint64Return(ctx, length) return StatusSuccess } func handleRtlIoDecodeMemIoResource(ctx *TrapContext) uint32 { descriptor := ctx.Arg(0) alignmentOut := ctx.Arg(1) minimumOut := ctx.Arg(2) maximumOut := ctx.Arg(3) resourceType, typeErr := ctx.Mem.Read8(descriptor + 1) flags, flagsErr := ctx.Mem.Read16(descriptor + 4) encodedLength, lengthErr := ctx.Mem.Read32(descriptor + 8) encodedAlignment, alignmentErr := ctx.Mem.Read32(descriptor + 12) minimum, minimumErr := ctx.Mem.Read64(descriptor + 16) maximum, maximumErr := ctx.Mem.Read64(descriptor + 24) if typeErr != nil || flagsErr != nil || lengthErr != nil || alignmentErr != nil || minimumErr != nil || maximumErr != nil { setUint64Return(ctx, 0) return StatusSuccess } length, validLength := decodeMemIoLength(resourceType, flags, encodedLength) alignment, validAlignment := decodeMemIoLength(resourceType, flags, encodedAlignment) if !validLength || !validAlignment { setUint64Return(ctx, 0) return StatusSuccess } if alignmentOut != 0 { ctx.Mem.Write64(alignmentOut, alignment) } if minimumOut != 0 { ctx.Mem.Write64(minimumOut, minimum) } if maximumOut != 0 { ctx.Mem.Write64(maximumOut, maximum) } setUint64Return(ctx, length) return StatusSuccess }