diff --git a/cmd/govm/main.go b/cmd/govm/main.go index 1908399..afef5c4 100644 --- a/cmd/govm/main.go +++ b/cmd/govm/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" +"math" "os" "path/filepath" "runtime/pprof" @@ -11,17 +12,18 @@ import ( "strings" "time" -"github.com/mirkobrombin/go-vm/pkg/arch" -"github.com/mirkobrombin/go-vm/pkg/cpu" -"github.com/mirkobrombin/go-vm/pkg/loader" -"github.com/mirkobrombin/go-vm/pkg/mmu" -"github.com/mirkobrombin/go-vm/pkg/ntapi" -gosyscall "github.com/mirkobrombin/go-vm/pkg/syscall" -"github.com/mirkobrombin/go-vm/pkg/thread" -"github.com/mirkobrombin/go-vm/pkg/vfs" -"github.com/mirkobrombin/go-vm/pkg/wl" -"github.com/mirkobrombin/go-vm/pkg/win32k" -"github.com/mirkobrombin/go-vm/pkg/x64" +"github.com/mirkobrombin/Raven/pkg/arch" +"github.com/mirkobrombin/Raven/pkg/cpu" +"github.com/mirkobrombin/Raven/pkg/loader" +"github.com/mirkobrombin/Raven/pkg/mmu" +"github.com/mirkobrombin/Raven/pkg/ntapi" +gosyscall "github.com/mirkobrombin/Raven/pkg/syscall" +"github.com/mirkobrombin/Raven/pkg/thread" +"github.com/mirkobrombin/Raven/pkg/vfs" +"github.com/mirkobrombin/Raven/pkg/wl" +"github.com/mirkobrombin/Raven/pkg/win32k" +"github.com/mirkobrombin/Raven/pkg/x64" + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) func utf16Decode(s []uint16) []byte { @@ -52,6 +54,8 @@ func utf16Encode(s string) []uint16 { } func main() { +ravelog.Banner("v0.1.0") + trace := flag.Bool("trace", false, "enable instruction trace to stderr") memMB := flag.Int("mem", 256, "guest memory limit in MiB (advisory)") sysroot := flag.String("sysroot", "", "path to Windows system DLL directory (for PE mode)") @@ -59,11 +63,13 @@ regFile := flag.String("registry", "", "path to registry hive JSON file (for PE cpuprof := flag.String("cpuprofile", "", "write CPU profile to file") wayland := flag.Bool("wayland", true, "enable Wayland passthrough (use --wayland=false for headless)") -flag.Usage = func() {fmt.Fprintf(os.Stderr, "Usage: govm [flags] [args...]\n\n") -fmt.Fprintf(os.Stderr, "go-vm - in-process RISC-V / x86_64 / Windows PE Library OS\n\n") -fmt.Fprintf(os.Stderr, "Supports ELF (Linux) and PE (Windows) executables.\n") -fmt.Fprintf(os.Stderr, "PE mode is auto-detected from the file header (.exe/.dll or MZ magic).\n\n") -flag.PrintDefaults() +flag.Usage = func() { + ravelog.Banner("v0.1.0") + fmt.Fprintf(os.Stderr, "Usage: raven [flags] [args...]\n\n") + fmt.Fprintf(os.Stderr, "Raven - in-process x86-64 Windows PE / ELF emulator\n\n") + fmt.Fprintf(os.Stderr, "Supports ELF (Linux) and PE (Windows) executables.\n") + fmt.Fprintf(os.Stderr, "PE mode is auto-detected from the file header (.exe/.dll or MZ magic).\n\n") + flag.PrintDefaults() } flag.Parse() @@ -220,7 +226,7 @@ c.SetTLS(result.TLSAddr) pageStart := patchAddr &^ (mmu.PageSize - 1) mem.Mprotect(pageStart, mmu.PageSize, mmu.PermRead|mmu.PermWrite|mmu.PermExecute) if err := mem.Write8(patchAddr, 0xeb); err != nil { - fmt.Fprintf(os.Stderr, "[PATCH] FAILED to apply double-free fix: %v\n", err) + ravelog.Logf("[PATCH] FAILED to apply double-free fix: %v\n", err) } mem.Mprotect(pageStart, mmu.PageSize, mmu.PermRead|mmu.PermExecute) } @@ -372,7 +378,7 @@ if ucrt, ok := loadedModules["ucrtbase.dll"]; ok { mem.Write32(entryAddr+20, 0) // CS.RecursionCount = 0 } if trace { - fmt.Fprintf(os.Stderr, "[pe] initialized ucrtbase debug channel table (%d entries)\n", numChannels) + ravelog.Logf("[pe] initialized ucrtbase debug channel table (%d entries)\n", numChannels) } } } @@ -448,6 +454,10 @@ dispatcher := ntapi.NewDispatcher(mem, fs, handles, registry, procState, synthNt dispatcher.SetTrace(trace) dispatcher.SetSysRoot(sysroot) +// Register no-op stubs for any remaining unresolved ordinal imports so +// cpu-recover never needs to handle them (they just return 0 quietly). +patchUnresolvedOrdinals(mem, dispatcher, result.MainModule) + // Register auto-synth stub names with the dispatcher for trace output for _, synth := range autoSynthDLLs { dispatcher.RegisterAutoStubNames(synth) @@ -492,22 +502,23 @@ mem.Write64(procState.TEBAddr+ntapi.TebHeapBase, heapRegion) mem.Write64(procState.TEBAddr+ntapi.TebHeapOffset, 0) // Map a zero-filled null page so that null-pointer dereferences with small -// offsets (like [rcx+0x28] with rcx=0) read zero instead of segfaulting. -// Many C++ vtable dispatch patterns null-check the loaded value after reading. -mem.Mmap(1, 0x10000, mmu.PermRead) // addr≠0 so Mmap uses it as-is; page 0 gets mapped +// offsets (like [rcx+0x28] with rcx=0) read/write zero instead of segfaulting. +// Many C++ vtable dispatch patterns null-check the loaded value after reading, +// and some code writes to null-derived pointers after a failed allocation. +mem.MapRange(0, 0x10000, mmu.PermRead|mmu.PermWrite) // force-map pages 0-0xFFFF // Patch hot Win32 API stubs with inline x64 code to avoid vmcall overhead. patchHotStubs(mem, autoSynthDLLs, flsDataArray) if trace && exceptionTable != nil { - fmt.Fprintf(os.Stderr, "[seh] parsed %d RUNTIME_FUNCTION entries from .pdata\n", len(exceptionTable.Entries)) + ravelog.Logf("[seh] parsed %d RUNTIME_FUNCTION entries from .pdata\n", len(exceptionTable.Entries)) } // Set up handlers c.SetVmcallHandler(dispatcher.Handle) // NT trap handler c.SetSyscallHandler(func(cpu arch.CPU) { if trace { -fmt.Fprintf(os.Stderr, "[pe] unexpected Linux syscall %d at RIP=0x%x\n", +ravelog.Logf("[pe] unexpected Linux syscall %d at RIP=0x%x\n", cpu.SyscallNum(), cpu.GetPC()) } cpu.SetSyscallReturn(0xC0000002) // STATUS_NOT_IMPLEMENTED @@ -518,9 +529,9 @@ tm := thread.NewManager(mem, dispatcher.Handle) mainThread := tm.MainThread(c) if trace { -fmt.Fprintf(os.Stderr, "[pe] entry=0x%x sp=0x%x teb=0x%x peb=0x%x\n", +ravelog.Logf("[pe] entry=0x%x sp=0x%x teb=0x%x peb=0x%x\n", result.EntryPoint, result.StackPtr, procState.TEBAddr, procState.PEBAddr) -fmt.Fprintf(os.Stderr, "[pe] ntdll=0x%x win32u=0x%x\n", +ravelog.Logf("[pe] ntdll=0x%x win32u=0x%x\n", synthNtdll.BaseAddr, synthWin32u.BaseAddr) } @@ -546,7 +557,7 @@ fmt.Fprintf(os.Stderr, "govm: warning: DllMain(%s) failed: %v\n", dll.Name, err) // Restore CPU state for main entry c.SetPC(result.EntryPoint) c.SetSP(result.StackPtr) -fmt.Fprintf(os.Stderr, "[pe] DllMain phase complete, starting main entry at 0x%x\n", result.EntryPoint) +ravelog.Logf("[pe] DllMain phase complete, starting main entry at 0x%x\n", result.EntryPoint) } // Run the PE @@ -608,9 +619,9 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. _ = mainTitle if wld, err := wl.Connect(); err == nil { wlDisplay = wld - fmt.Fprintf(os.Stderr, "[wl] connected to Wayland compositor\n") + ravelog.Logf("[wl] connected to Wayland compositor\n") } else { - fmt.Fprintf(os.Stderr, "[wl] no Wayland: %v (window display disabled)\n", err) + ravelog.Logf("[wl] no Wayland: %v (window display disabled)\n", err) } readGuestUTF16 := func(addr uint64) string { @@ -986,6 +997,9 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. bkColor uint32 bkMode int32 // 1=TRANSPARENT, 2=OPAQUE curBrush uint64 + isScreen bool // true = renders to Wayland surface; false = memory/offscreen DC + originX int32 // client-area origin in window coordinates (ReactOS: GreSetDCOrg) + originY int32 } dcMap := make(map[uint64]*dcState) brushColors := make(map[uint64]uint32) // brush handle → COLORREF @@ -1030,6 +1044,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. textColor: 0x000000, bkColor: 0xFFFFFF, bkMode: 2, // OPAQUE + isScreen: true, } } @@ -1500,6 +1515,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. if ucrt, ok := synthByName["ucrtbase.dll"]; ok { ucrtExit := func(ctx *ntapi.TrapContext) uint32 { code := ctx.Arg(0) + ravelog.Logf("[win32] exit(%d)\n", code) if wlDisplay != nil { wlDisplay.Close() } @@ -1847,9 +1863,14 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. dst := ctx.Arg(0) val := byte(ctx.Arg(1)) count := ctx.Arg(2) - for i := uint64(0); i < count && i < 0x1000000; i++ { - mem.Write8(dst+i, val) + if count > 0x1000000 { + count = 0x1000000 } + buf := make([]byte, count) + for i := range buf { + buf[i] = val + } + ctx.Mem.WriteBytes(dst, buf) ctx.SetReturn(dst) return 0 }) @@ -1857,10 +1878,11 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. dst := ctx.Arg(0) src := ctx.Arg(1) count := ctx.Arg(2) - for i := uint64(0); i < count && i < 0x1000000; i++ { - b, _ := mem.Read8(src + i) - mem.Write8(dst+i, b) + if count > 0x1000000 { + count = 0x1000000 } + buf, _ := ctx.Mem.ReadBytes(src, int(count)) + ctx.Mem.WriteBytes(dst, buf) ctx.SetReturn(dst) return 0 }) @@ -1871,13 +1893,8 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. if count > 0x1000000 { count = 0x1000000 } - tmp := make([]byte, count) - for i := uint64(0); i < count; i++ { - tmp[i], _ = mem.Read8(src + i) - } - for i := uint64(0); i < count; i++ { - mem.Write8(dst+i, tmp[i]) - } + buf, _ := ctx.Mem.ReadBytes(src, int(count)) + ctx.Mem.WriteBytes(dst, buf) ctx.SetReturn(dst) return 0 }) @@ -2104,6 +2121,10 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. }) d.RegisterAutoStubHandler(ucrt, "abort", ucrtExit) + d.RegisterAutoStubHandler(ucrt, "terminate", func(ctx *ntapi.TrapContext) uint32 { + ravelog.Logf("[win32] terminate() called - ignoring\n") + return 0 + }) d.RegisterAutoStubHandler(ucrt, "_c_exit", func(ctx *ntapi.TrapContext) uint32 { return 0 }) @@ -3107,6 +3128,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. // ExitProcess: cleanly terminate emulation d.RegisterAutoStubHandler(kernel32, "ExitProcess", func(ctx *ntapi.TrapContext) uint32 { code := ctx.Arg(0) + ravelog.Logf("[win32] ExitProcess(%d)\n", code) if wlDisplay != nil { wlDisplay.Close() } @@ -3627,7 +3649,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } name := string(utf16Decode(fname)) if ctx.Trace { - fmt.Fprintf(os.Stderr, "[win32] CreateFileW: %q access=0x%x disp=%d\n", name, desiredAccess, creationDisp) + ravelog.Logf("[win32] CreateFileW: %q access=0x%x disp=%d\n", name, desiredAccess, creationDisp) } // Convert Windows path to VFS path vfsPath := winPathToVFS(name) @@ -3673,7 +3695,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } if err != nil { if ctx.Trace { - fmt.Fprintf(os.Stderr, "[win32] CreateFileW: %q → INVALID_HANDLE_VALUE (%v)\n", name, err) + ravelog.Logf("[win32] CreateFileW: %q → INVALID_HANDLE_VALUE (%v)\n", name, err) } ctx.SetReturn(0xFFFFFFFFFFFFFFFF) // INVALID_HANDLE_VALUE procState.LastError = 2 // ERROR_FILE_NOT_FOUND @@ -3681,7 +3703,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } h := handles.Alloc(&ntapi.FileHandle{FD: fd, Path: vfsPath}) if ctx.Trace { - fmt.Fprintf(os.Stderr, "[win32] CreateFileW: %q → handle=0x%x (fd=%d)\n", name, uint64(h), fd) + ravelog.Logf("[win32] CreateFileW: %q → handle=0x%x (fd=%d)\n", name, uint64(h), fd) } ctx.SetReturn(uint64(h)) return 0 @@ -3702,9 +3724,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } buf := make([]byte, nBytesToRead) n, _ := ctx.FS.Read(fh.FD, buf) - for i := 0; i < n; i++ { - ctx.Mem.Write8(bufAddr+uint64(i), buf[i]) - } + ctx.Mem.WriteBytes(bufAddr, buf[:n]) if lpBytesRead != 0 { ctx.Mem.Write32(lpBytesRead, uint32(n)) } @@ -3994,7 +4014,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. d.RegisterAutoStubHandler(kernel32, "RaiseException", func(ctx *ntapi.TrapContext) uint32 { code := ctx.Arg(0) flags := ctx.Arg(1) - fmt.Fprintf(os.Stderr, "[win32] RaiseException: code=0x%x flags=0x%x\n", code, flags) + ravelog.Logf("[win32] RaiseException: code=0x%x flags=0x%x\n", code, flags) if code == 0x406D1388 { return 0 // MS_VC_EXCEPTION (SetThreadName) - ignore } @@ -4011,7 +4031,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. if handler.IsFallback { // FH4 format: can't parse catch blocks, unwind past the handler frame - fmt.Fprintf(os.Stderr, "[seh] C++ exception → FH4 fallback unwind to RIP=0x%x RSP=0x%x\n", + ravelog.Logf("[seh] C++ exception → FH4 fallback unwind to RIP=0x%x RSP=0x%x\n", handler.FallbackRIP, handler.FallbackRSP) if rw, ok := ctx.CPU.(ntapi.RegisterWriter); ok { rw.WriteReg(0, 0) // RAX = 0 (error return) @@ -4027,7 +4047,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. catchAddr := imageBase + uint64(handler.FuncletRVA) if ctx.Trace { - fmt.Fprintf(os.Stderr, "[seh] C++ exception → catch funclet at 0x%x, RBP=0x%x\n", + ravelog.Logf("[seh] C++ exception → catch funclet at 0x%x, RBP=0x%x\n", catchAddr, handler.EstablisherRBP) } // Set up the CPU to call the catch funclet: @@ -4040,7 +4060,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } // No handler found - log and continue (best effort) - fmt.Fprintf(os.Stderr, "[win32] RaiseException: C++ exception (0xe06d7363) - no SEH handler, continuing\n") + ravelog.Logf("[win32] RaiseException: C++ exception (0xe06d7363) - no SEH handler, continuing\n") return 0 } fmt.Fprintf(os.Stderr, "govm: RaiseException code=0x%x flags=0x%x - aborting\n", code, flags) @@ -4290,7 +4310,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. synth := ntapi.GenerateSynthDLL(mem, dllName, allFuncs, nextDelayID) synthByName[dllName] = synth d.RegisterAutoStubNames(synth) - fmt.Fprintf(os.Stderr, "[pe] delay-synth %s: %d stubs (id base 0x%X) funcs=%v\n", + ravelog.Logf("[pe] delay-synth %s: %d stubs (id base 0x%X) funcs=%v\n", dllName, len(allFuncs), nextDelayID, allFuncs) nextDelayID += uint32(len(allFuncs)) // Re-run all handler registrars so newly synthesized DLL gets handlers @@ -4307,7 +4327,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } } - fmt.Fprintf(os.Stderr, "[delay] unresolved: %s!%s\n", dllName, funcName) + ravelog.Logf("[delay] unresolved: %s!%s\n", dllName, funcName) fallback: if delayLoadFallback == 0 { @@ -4398,6 +4418,64 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. ctx.SetReturn(uint64(n)) return 0 }) + // ColorRGBToHLS: convert COLORREF to HLS - store results in out-params + d.RegisterAutoStubHandler(shlwapi, "ColorRGBToHLS", func(ctx *ntapi.TrapContext) uint32 { + clr := uint32(ctx.Arg(0)) + pHue := ctx.Arg(1) + pLum := ctx.Arg(2) + pSat := ctx.Arg(3) + r := float64((clr) & 0xFF) + g := float64((clr >> 8) & 0xFF) + b := float64((clr >> 16) & 0xFF) + r /= 255; g /= 255; b /= 255 + maxC := math.Max(math.Max(r, g), b) + minC := math.Min(math.Min(r, g), b) + l := (maxC + minC) / 2 + var h, s float64 + if maxC != minC { + d := maxC - minC + if l > 0.5 { s = d / (2 - maxC - minC) } else { s = d / (maxC + minC) } + switch maxC { + case r: h = (g - b) / d; if g < b { h += 6 } + case g: h = (b-r)/d + 2 + case b: h = (r-g)/d + 4 + } + h /= 6 + } + // Windows HLS: Hue 0-240, Lum 0-240, Sat 0-240 + if pHue != 0 { mem.Write16(pHue, uint16(h*240)) } + if pLum != 0 { mem.Write16(pLum, uint16(l*240)) } + if pSat != 0 { mem.Write16(pSat, uint16(s*240)) } + return 0 + }) + // ColorHLSToRGB: convert Windows HLS (0-240 range) to COLORREF + d.RegisterAutoStubHandler(shlwapi, "ColorHLSToRGB", func(ctx *ntapi.TrapContext) uint32 { + wH := float64(ctx.Arg(0)) / 240 + wL := float64(ctx.Arg(1)) / 240 + wS := float64(ctx.Arg(2)) / 240 + hue2rgb := func(p, q, t float64) float64 { + if t < 0 { t++ } + if t > 1 { t-- } + if t < 1.0/6 { return p + (q-p)*6*t } + if t < 1.0/2 { return q } + if t < 2.0/3 { return p + (q-p)*(2.0/3-t)*6 } + return p + } + var r, g, b float64 + if wS == 0 { + r, g, b = wL, wL, wL + } else { + var q float64 + if wL < 0.5 { q = wL * (1 + wS) } else { q = wL + wS - wL*wS } + p := 2*wL - q + r = hue2rgb(p, q, wH+1.0/3) + g = hue2rgb(p, q, wH) + b = hue2rgb(p, q, wH-1.0/3) + } + clr := uint32(r*255) | uint32(g*255)<<8 | uint32(b*255)<<16 + ctx.SetReturn(uint64(clr)) + return 0 + }) } }) delayHandlerRegistrars[len(delayHandlerRegistrars)-1]() @@ -4868,7 +4946,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. x: 0, y: defH - sbHeight, w: defW, h: sbHeight, visible: style&0x10000000 != 0, } - fmt.Fprintf(os.Stderr, "[comctl32] CreateStatusWindowW hwnd=0x%x parent=0x%x\n", hwnd, hwndParent) + ravelog.Logf("[comctl32] CreateStatusWindowW hwnd=0x%x parent=0x%x\n", hwnd, hwndParent) ctx.SetReturn(hwnd) return 0 }) @@ -5629,7 +5707,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. cchBufferMax := int(int32(ctx.Arg(3))) s, guestAddr, slen := peLoadStringW(uID) if s != "" { - fmt.Fprintf(os.Stderr, "[loadstr] id=%d → %q\n", uID, s) + ravelog.Logf("[loadstr] id=%d → %q\n", uID, s) } if s == "" || lpBuffer == 0 { ctx.SetReturn(0) @@ -5732,7 +5810,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } } } - fmt.Fprintf(os.Stderr, "[wndclass] RegisterClassW %q atom=0x%x wndProc=0x%x brush=%d\n", name, a, wndProc, hBrush) + ravelog.Logf("[wndclass] RegisterClassW %q atom=0x%x wndProc=0x%x brush=%d\n", name, a, wndProc, hBrush) } ctx.SetReturn(uint64(a)) return 0 @@ -5781,7 +5859,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } } } - fmt.Fprintf(os.Stderr, "[wndclass] RegisterClassExW name=%q lpName=0x%x atom=0x%x wndProc=0x%x brush=%d\n", name, lpName, a, wndProc, hBrush) + ravelog.Logf("[wndclass] RegisterClassExW name=%q lpName=0x%x atom=0x%x wndProc=0x%x brush=%d\n", name, lpName, a, wndProc, hBrush) } ctx.SetReturn(uint64(a)) return 0 @@ -5820,7 +5898,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. hwndClassAtom[hwnd] = classAtomForHwnd } wp := hwndWndProc[hwnd] - fmt.Fprintf(os.Stderr, "[wndclass] CreateWindowExW class=%q hwnd=0x%x wndProc=0x%x style=0x%x\n", className, hwnd, wp, style) + ravelog.Logf("[wndclass] CreateWindowExW class=%q hwnd=0x%x wndProc=0x%x style=0x%x\n", className, hwnd, wp, style) const wsChild = 0x40000000 // Track child windows (built-in classes like Edit, Static, Button) @@ -5880,10 +5958,10 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. wlWindow = win mainHWND = hwnd mainTitle = title - fmt.Fprintf(os.Stderr, "[wl] window created: %q %dx%d hwnd=0x%x\n", title, w, h, hwnd) + ravelog.Logf("[wl] window created: %q %dx%d hwnd=0x%x\n", title, w, h, hwnd) // Attach menu bar: first check hMenu parameter, then class menu hMenu := ctx.Arg(9) - fmt.Fprintf(os.Stderr, "[menu] CreateWindowExW hMenu=0x%x menus_has=%v\n", hMenu, menus[hMenu] != nil) + ravelog.Logf("[menu] CreateWindowExW hMenu=0x%x menus_has=%v\n", hMenu, menus[hMenu] != nil) if hMenu != 0 { if _, ok := menus[hMenu]; ok { hwndMenuBar[hwnd] = hMenu @@ -5902,7 +5980,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } } } else { - fmt.Fprintf(os.Stderr, "[wl] CreateWindow failed: %v\n", err) + ravelog.Logf("[wl] CreateWindow failed: %v\n", err) } } } @@ -5964,7 +6042,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. }) d.RegisterAutoStubHandler(user32, "DestroyWindow", func(ctx *ntapi.TrapContext) uint32 { hwnd := ctx.Arg(0) - fmt.Fprintf(os.Stderr, "[destroy] hwnd=0x%x\n", hwnd) + ravelog.Logf("[destroy] hwnd=0x%x\n", hwnd) delete(hwndChildren, hwnd) delete(hwndWndProc, hwnd) ctx.SetReturn(1) // TRUE @@ -6012,8 +6090,10 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 }) d.RegisterAutoStubHandler(user32, "GetDC", func(ctx *ntapi.TrapContext) uint32 { + hwnd := ctx.Arg(0) h := gdiAlloc() - dcMap[h] = screenDC() + ox, oy := clientAreaOrigin(hwnd) + dcMap[h] = &dcState{textColor: 0x000000, bkColor: 0xFFFFFF, bkMode: 2, isScreen: true, originX: ox, originY: oy} ctx.SetReturn(h) return 0 }) @@ -6125,7 +6205,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. if w > 0 && w < 0x7FFF { ci.w = w } if h > 0 && h < 0x7FFF { ci.h = h } } - fmt.Fprintf(os.Stderr, "[setpos] hwnd=0x%x class=%q flags=0x%x → %d,%d %dx%d\n", hwnd, ci.className, flags, ci.x, ci.y, ci.w, ci.h) + ravelog.Logf("[setpos] hwnd=0x%x class=%q flags=0x%x → %d,%d %dx%d\n", hwnd, ci.className, flags, ci.x, ci.y, ci.w, ci.h) // Queue WM_SIZE for windows with a guest WndProc if wp, ok := hwndWndProc[hwnd]; ok && wp != 0 { lParam := uint64(uint16(ci.w)) | (uint64(uint16(ci.h)) << 16) @@ -6182,7 +6262,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. if dp.w > 0 { ci.w = dp.w } if dp.h > 0 { ci.h = dp.h } } - fmt.Fprintf(os.Stderr, "[setpos] hwnd=0x%x class=%q flags=0x%x → %d,%d %dx%d\n", dp.hwnd, ci.className, dp.flags, ci.x, ci.y, ci.w, ci.h) + ravelog.Logf("[setpos] hwnd=0x%x class=%q flags=0x%x → %d,%d %dx%d\n", dp.hwnd, ci.className, dp.flags, ci.x, ci.y, ci.w, ci.h) } deferredPositions = deferredPositions[:0] ctx.SetReturn(1) @@ -6714,8 +6794,17 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. ctx.SetReturn(1) return 0 } + // Synthetic WM_PAINT frame: mainHWND + all visible children with guest WndProcs + msgQueue = append(msgQueue, queuedMsg{mainHWND, 0x000F, 0, 0}) + for h, cs := range hwndChildren { + if cs.visible && hwndWndProc[h] != 0 { + msgQueue = append(msgQueue, queuedMsg{h, 0x000F, 0, 0}) + } + } + m := msgQueue[0] + msgQueue = msgQueue[1:] if pMsg != 0 { - writeMsg(pMsg, mainHWND, 0x000F, 0, 0) // WM_PAINT + writeMsg(pMsg, m.hwnd, m.message, m.wParam, m.lParam) } ctx.SetReturn(1) return 0 @@ -6758,7 +6847,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. wndProc, ok := hwndWndProc[hwnd] if !ok || wndProc == 0 { - fmt.Fprintf(os.Stderr, "[dispatch] no WndProc for hwnd=0x%x msg=0x%x\n", hwnd, message) + ravelog.Logf("[dispatch] no WndProc for hwnd=0x%x msg=0x%x\n", hwnd, message) ctx.SetReturn(0) return 0 } @@ -6769,7 +6858,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } - fmt.Fprintf(os.Stderr, "[dispatch] hwnd=0x%x msg=0x%x → WndProc=0x%x\n", hwnd, message, wndProc) + ravelog.Logf("[dispatch] hwnd=0x%x msg=0x%x → WndProc=0x%x\n", hwnd, message, wndProc) // Stack is already correct: [RSP] = caller's return address, // [RSP+8..RSP+0x28] = shadow space. WndProc will RET to caller. @@ -6846,7 +6935,8 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. hwnd := ctx.Arg(0) pPaint := ctx.Arg(1) hdc := gdiAlloc() - dcMap[hdc] = screenDC() + ox, oy := clientAreaOrigin(hwnd) + dcMap[hdc] = &dcState{textColor: 0x000000, bkColor: 0xFFFFFF, bkMode: 2, isScreen: true, originX: ox, originY: oy} var rcLeft, rcTop, rcRight, rcBottom int32 @@ -7055,6 +7145,10 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. ctx.SetReturn(1) return 0 }) + d.RegisterAutoStubHandler(user32, "MessageBoxA", func(ctx *ntapi.TrapContext) uint32 { + ctx.SetReturn(1) // IDOK + return 0 + }) d.RegisterAutoStubHandler(user32, "MessageBoxW", func(ctx *ntapi.TrapContext) uint32 { ctx.SetReturn(1) // IDOK return 0 @@ -7178,15 +7272,15 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } menu := peLoadMenuResource(resID) if menu == nil { - fmt.Fprintf(os.Stderr, "[menu] LoadMenuW resID=%d → nil\n", resID) + ravelog.Logf("[menu] LoadMenuW resID=%d → nil\n", resID) ctx.SetReturn(0) return 0 } hmenu := uint64(0x60000 + resID) menus[hmenu] = menu - fmt.Fprintf(os.Stderr, "[menu] LoadMenuW resID=%d → hmenu=0x%x items=%d\n", resID, hmenu, len(menu.items)) + ravelog.Logf("[menu] LoadMenuW resID=%d → hmenu=0x%x items=%d\n", resID, hmenu, len(menu.items)) for i, item := range menu.items { - fmt.Fprintf(os.Stderr, "[menu] [%d] %q flags=0x%x submenu=%d\n", i, item.text, item.flags, len(item.submenu)) + ravelog.Logf("[menu] [%d] %q flags=0x%x submenu=%d\n", i, item.text, item.flags, len(item.submenu)) } ctx.SetReturn(hmenu) return 0 @@ -7309,6 +7403,106 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. ctx.SetReturn(sysMenu) return 0 }) + // GetSysColorBrush: return a pseudo-handle encoding the index + d.RegisterAutoStubHandler(user32, "GetSysColorBrush", func(ctx *ntapi.TrapContext) uint32 { + idx := ctx.Arg(0) + h := uint64(0xBBBB_0000) | idx // pseudo-handle + brushColors[h] = getSysColor(uint32(idx)) + ctx.SetReturn(h) + return 0 + }) + // FillRect (user32 export, same semantics as gdi32.FillRect) + d.RegisterAutoStubHandler(user32, "FillRect", func(ctx *ntapi.TrapContext) uint32 { + if wlWindow == nil { + ctx.SetReturn(1) + return 0 + } + hDC := ctx.Arg(0) + lpRect := ctx.Arg(1) + hBrush := ctx.Arg(2) + if lpRect == 0 { + ctx.SetReturn(1) + return 0 + } + dc := dcMap[hDC] + if dc != nil && !dc.isScreen { + ctx.SetReturn(1) + return 0 + } + var ox, oy int32 + if dc != nil { + ox, oy = dc.originX, dc.originY + } + left, _ := mem.Read32(lpRect) + top, _ := mem.Read32(lpRect + 4) + right, _ := mem.Read32(lpRect + 8) + bottom, _ := mem.Read32(lpRect + 12) + var color uint32 + if c, ok := brushColors[hBrush]; ok { + color = c + } else if hBrush >= 1 && hBrush <= 31 { + color = getSysColor(uint32(hBrush - 1)) + } else { + color = 0xF0F0F0 + } + wlWindow.DrawRect(int32(left)+ox, int32(top)+oy, int32(right)+ox, int32(bottom)+oy, color) + ctx.SetReturn(1) + return 0 + }) + // CreateDialogParamW: modeless dialog creation - return a fake HWND + d.RegisterAutoStubHandler(user32, "CreateDialogParamW", func(ctx *ntapi.TrapContext) uint32 { + hwnd := hwndAlloc() + ctx.SetReturn(hwnd) + return 0 + }) + // SetMenuItemBitmaps: associate bitmaps with menu items - no-op (no bitmap rendering) + d.RegisterAutoStubHandler(user32, "SetMenuItemBitmaps", func(ctx *ntapi.TrapContext) uint32 { + ctx.SetReturn(1) + return 0 + }) + // RemoveMenu: remove a menu item - no-op stub + d.RegisterAutoStubHandler(user32, "RemoveMenu", func(ctx *ntapi.TrapContext) uint32 { + ctx.SetReturn(1) + return 0 + }) + // GetDlgCtrlID: return control ID from HWND - return 0 + d.RegisterAutoStubHandler(user32, "GetDlgCtrlID", func(ctx *ntapi.TrapContext) uint32 { + ctx.SetReturn(0) + return 0 + }) + // DestroyIcon: free an icon handle - no-op stub + d.RegisterAutoStubHandler(user32, "DestroyIcon", func(ctx *ntapi.TrapContext) uint32 { + ctx.SetReturn(1) + return 0 + }) + // GetMenuStringW: copy menu item text into caller's buffer + d.RegisterAutoStubHandler(user32, "GetMenuStringW", func(ctx *ntapi.TrapContext) uint32 { + hMenu := ctx.Arg(0) + idItem := ctx.Arg(1) + lpStr := ctx.Arg(2) + nMaxCount := int(ctx.Arg(3)) + // flags := ctx.Arg(4) // MF_BYPOSITION or MF_BYCOMMAND + md, ok := menus[hMenu] + if !ok || nMaxCount == 0 || lpStr == 0 { + ctx.SetReturn(0) + return 0 + } + for _, item := range md.items { + if item.id == idItem { + runes := []rune(item.text) + n := len(runes) + if n >= nMaxCount { n = nMaxCount - 1 } + for i := 0; i < n; i++ { + mem.Write16(lpStr+uint64(i)*2, uint16(runes[i])) + } + mem.Write16(lpStr+uint64(n)*2, 0) + ctx.SetReturn(uint64(n)) + return 0 + } + } + ctx.SetReturn(0) + return 0 + }) } }) delayHandlerRegistrars[len(delayHandlerRegistrars)-1]() @@ -7336,7 +7530,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. }) d.RegisterAutoStubHandler(gdi32, "CreateCompatibleDC", func(ctx *ntapi.TrapContext) uint32 { h := gdiAlloc() - dcMap[h] = screenDC() + dcMap[h] = &dcState{textColor: 0x000000, bkColor: 0xFFFFFF, bkMode: 2, isScreen: false} ctx.SetReturn(h) return 0 }) @@ -7465,11 +7659,15 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } hdc := ctx.Arg(0) - x := int32(ctx.Arg(1)) - y := int32(ctx.Arg(2)) + dc := getDC(hdc) + if !dc.isScreen { + ctx.SetReturn(1) + return 0 + } + x := int32(ctx.Arg(1)) + dc.originX + y := int32(ctx.Arg(2)) + dc.originY lpStr := ctx.Arg(3) nLen := int(ctx.Arg(4)) - dc := getDC(hdc) text := readGuestUTF16(lpStr) if nLen > 0 && nLen < len(text) { text = text[:nLen] @@ -7484,12 +7682,16 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } hdc := ctx.Arg(0) - x := int32(ctx.Arg(1)) - y := int32(ctx.Arg(2)) + dc := getDC(hdc) + if !dc.isScreen { + ctx.SetReturn(1) + return 0 + } + x := int32(ctx.Arg(1)) + dc.originX + y := int32(ctx.Arg(2)) + dc.originY // arg3 = options, arg4 = lpRect (skip for now) lpStr := ctx.Arg(4 + 1) // 5th arg: lpString (stack arg after rect) nLen := int(ctx.Arg(4 + 2)) // 6th arg: cbCount - dc := getDC(hdc) text := readGuestUTF16(lpStr) if nLen > 0 && nLen < len(text) { text = text[:nLen] @@ -7508,11 +7710,15 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. return 0 } hdc := ctx.Arg(0) - x := int32(ctx.Arg(1)) - y := int32(ctx.Arg(2)) + dc := getDC(hdc) + if !dc.isScreen { + ctx.SetReturn(1) + return 0 + } + x := int32(ctx.Arg(1)) + dc.originX + y := int32(ctx.Arg(2)) + dc.originY w := int32(ctx.Arg(3)) h := int32(ctx.Arg(4 + 0)) // 5th arg on stack - dc := getDC(hdc) color := uint32(0xF0F0F0) if c, ok := brushColors[dc.curBrush]; ok { color = c @@ -7530,12 +7736,22 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. ctx.SetReturn(1) return 0 } + hDC := ctx.Arg(0) lpRect := ctx.Arg(1) hBrush := ctx.Arg(2) if lpRect == 0 { ctx.SetReturn(1) return 0 } + dc := dcMap[hDC] + if dc != nil && !dc.isScreen { + ctx.SetReturn(1) + return 0 + } + var ox, oy int32 + if dc != nil { + ox, oy = dc.originX, dc.originY + } left, _ := mem.Read32(lpRect) top, _ := mem.Read32(lpRect + 4) right, _ := mem.Read32(lpRect + 8) @@ -7549,7 +7765,7 @@ func registerAutoSynthHandlers(mem *mmu.MMU, d *ntapi.Dispatcher, dlls []*ntapi. } else { color = 0xF0F0F0 } - wlWindow.DrawRect(int32(left), int32(top), int32(right), int32(bottom), color) + wlWindow.DrawRect(int32(left)+ox, int32(top)+oy, int32(right)+ox, int32(bottom)+oy, color) ctx.SetReturn(1) return 0 }) @@ -7672,7 +7888,7 @@ func autoSynthUnresolved(mem *mmu.MMU, mainMod *loader.PEModule, loaded map[stri synth := ntapi.GenerateSynthDLL(mem, dllName, unique, nextID) result = append(result, synth) nextID += uint32(len(unique)) - fmt.Fprintf(os.Stderr, "[pe] auto-synth %s: %d stubs (id base 0x%X)\n", + ravelog.Logf("[pe] auto-synth %s: %d stubs (id base 0x%X)\n", dllName, len(unique), nextID-uint32(len(unique))) } return result @@ -7733,6 +7949,62 @@ func patchCRTDataExports(mem *mmu.MMU, mainMod *loader.PEModule, argv []string) } } +// patchUnresolvedOrdinals finds imports that are still pointing at the +// unresolved-sentinel address and replaces each IAT slot with a tiny vmcall +// trampoline that logs the call and returns 0. This covers ordinal-only +// imports (e.g. COMCTL32 FlatSB ordinals) that no synthetic DLL exposes. +func patchUnresolvedOrdinals(mem *mmu.MMU, d *ntapi.Dispatcher, mainMod *loader.PEModule) { + const unresolvedSentinel = 0xDEAD_DEAD_0001_0000 + // ID range reserved for ordinal stubs - well above auto-synth (0x2000+) + const ordinalIDBase = uint32(0xF000) + const stubBytes = 9 // B8 xx xx xx xx | 0F 01 C1 | C3 + + // Collect unresolved entries + var unresolved []loader.PEImportEntry + for _, imp := range mainMod.Imports { + if !mainMod.Is64 { + continue + } + v, _ := mem.Read64(imp.IATSlot) + if v == unresolvedSentinel { + unresolved = append(unresolved, imp) + } + } + if len(unresolved) == 0 { + return + } + + // Allocate one page for all stubs + trampolinePage := mem.Mmap(0, uint64(len(unresolved))*stubBytes+64, mmu.PermRead|mmu.PermWrite|mmu.PermExecute) + + for i, imp := range unresolved { + id := ordinalIDBase + uint32(i) + stubAddr := trampolinePage + uint64(i)*stubBytes + + // mov eax, id (B8 id_lo id_hi 00 00) + mem.Write8(stubAddr+0, 0xB8) + mem.Write32(stubAddr+1, id) + // vmcall (0F 01 C1) + mem.Write8(stubAddr+5, 0x0F) + mem.Write8(stubAddr+6, 0x01) + mem.Write8(stubAddr+7, 0xC1) + // ret (C3) + mem.Write8(stubAddr+8, 0xC3) + + // Capture for closure + capturedImp := imp + d.RegisterHandler(id, fmt.Sprintf("%s!ord%d", capturedImp.DLLName, capturedImp.Ordinal), + func(ctx *ntapi.TrapContext) uint32 { + ravelog.Logf("[ordinal] %s!ord%d → 0\n", capturedImp.DLLName, capturedImp.Ordinal) + return 0 + }) + + mem.Write64Raw(imp.IATSlot, stubAddr) + ravelog.Logf("[ordinal] patched %s!ord%d IAT=0x%x → stub=0x%x\n", + imp.DLLName, imp.Ordinal, imp.IATSlot, stubAddr) + } +} + // loadBYODLLs loads real Windows DLLs from the sysroot directory. // Returns updated module info, loaded modules map, and DLL load order (for DllMain). // Uses two-pass loading: first map all DLLs into memory, then resolve imports. @@ -7789,7 +8061,7 @@ fmt.Fprintf(os.Stderr, "govm: warning: failed to load %s: %v\n", dllName, err) continue } -fmt.Fprintf(os.Stderr, "[pe] BYODLL %s: base=0x%x size=0x%x entry=0x%x\n", mod.Name, mod.BaseAddr, mod.ImageSize, mod.EntryPoint) +ravelog.Logf("[pe] BYODLL %s: base=0x%x size=0x%x entry=0x%x\n", mod.Name, mod.BaseAddr, mod.ImageSize, mod.EntryPoint) loaded[strings.ToLower(mod.Name)] = mod loadOrder = append(loadOrder, mod) modules = append(modules, ntapi.ModuleInfo{ diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000..9c37000 Binary files /dev/null and b/docs/logo.png differ diff --git a/docs/logo.svg b/docs/logo.svg new file mode 100644 index 0000000..cfa4696 --- /dev/null +++ b/docs/logo.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/go.mod b/go.mod index c17b62b..a58666d 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/mirkobrombin/go-vm +module github.com/mirkobrombin/Raven go 1.25.7 diff --git a/pkg/arch/arch.go b/pkg/arch/arch.go index 95134f0..c4ed240 100644 --- a/pkg/arch/arch.go +++ b/pkg/arch/arch.go @@ -1,6 +1,6 @@ package arch -import "github.com/mirkobrombin/go-vm/pkg/mmu" +import "github.com/mirkobrombin/Raven/pkg/mmu" // Arch identifies the guest architecture. type Arch int diff --git a/pkg/cpu/cpu.go b/pkg/cpu/cpu.go index 5fa9475..b601474 100644 --- a/pkg/cpu/cpu.go +++ b/pkg/cpu/cpu.go @@ -3,8 +3,8 @@ package cpu import ( "fmt" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // CPU represents a single RISC-V RV64GC hardware thread. diff --git a/pkg/cpu/cpu_test.go b/pkg/cpu/cpu_test.go index 6f38290..50d6583 100644 --- a/pkg/cpu/cpu_test.go +++ b/pkg/cpu/cpu_test.go @@ -3,8 +3,8 @@ package cpu import ( "testing" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" ) func TestADDI(t *testing.T) { diff --git a/pkg/cpu/extensions_test.go b/pkg/cpu/extensions_test.go index 0877453..53ba772 100644 --- a/pkg/cpu/extensions_test.go +++ b/pkg/cpu/extensions_test.go @@ -3,7 +3,7 @@ package cpu import ( "testing" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) func TestCompressed_CADDI(t *testing.T) { diff --git a/pkg/cpu/fpu_test.go b/pkg/cpu/fpu_test.go index e830d7b..564a6c3 100644 --- a/pkg/cpu/fpu_test.go +++ b/pkg/cpu/fpu_test.go @@ -4,7 +4,7 @@ import ( "math" "testing" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // encodeR builds an R-type RISC-V instruction. diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go index c500f93..44950be 100644 --- a/pkg/loader/loader.go +++ b/pkg/loader/loader.go @@ -8,8 +8,10 @@ import ( "io" "os" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) const ( @@ -139,7 +141,7 @@ func LoadELF(path string, mem *mmu.MMU, argv []string, envp []string) (*LoadResu // brk must be page-aligned, just like real Linux. brkAddr := (maxAddr + mmu.PageSize - 1) & mmu.PageMask - fmt.Fprintf(os.Stderr, "[loader] ELF Type: %v, elfBase: 0x%x, maxAddr: 0x%x, brkAddr: 0x%x\n", ef.Type, elfBase, maxAddr, brkAddr) + ravelog.Logf("[loader] ELF Type: %v, elfBase: 0x%x, maxAddr: 0x%x, brkAddr: 0x%x\n", ef.Type, elfBase, maxAddr, brkAddr) mem.SetBrk(brkAddr) var interpBase uint64 diff --git a/pkg/loader/pe.go b/pkg/loader/pe.go index 1f377da..fe4a47e 100644 --- a/pkg/loader/pe.go +++ b/pkg/loader/pe.go @@ -9,8 +9,10 @@ import ( "path/filepath" "strings" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // PEModule represents a loaded PE image in guest memory. @@ -573,7 +575,7 @@ func ResolveImports(mem *mmu.MMU, mod *PEModule, modules map[string]*PEModule) e target, ok = modules[dllKey] } if !ok { - fmt.Fprintf(os.Stderr, "[resolve] DLL NOT FOUND: %s (for %s)\n", imp.DLLName, mod.Name) + ravelog.Logf("[resolve] DLL NOT FOUND: %s (for %s)\n", imp.DLLName, mod.Name) continue // unresolved - leave stub (will trap if called) } @@ -603,7 +605,7 @@ func ResolveImports(mem *mmu.MMU, mod *PEModule, modules map[string]*PEModule) e } resolved++ } else { - fmt.Fprintf(os.Stderr, "[resolve] UNRESOLVED: %s!%s (ord=%d) for %s IAT=0x%x\n", + ravelog.Logf("[resolve] UNRESOLVED: %s!%s (ord=%d) for %s IAT=0x%x\n", imp.DLLName, imp.FuncName, imp.Ordinal, mod.Name, imp.IATSlot) // Write a sentinel address outside crash-recovery range (> 0x10000) // to prevent silent recovery; will cause a clear "unmapped" fault. @@ -616,7 +618,7 @@ func ResolveImports(mem *mmu.MMU, mod *PEModule, modules map[string]*PEModule) e unresolved++ } } - fmt.Fprintf(os.Stderr, "[resolve] %s: %d imports, %d resolved, %d unresolved, exports=%d\n", + ravelog.Logf("[resolve] %s: %d imports, %d resolved, %d unresolved, exports=%d\n", mod.Name, len(mod.Imports), resolved, unresolved, len(modules)) return nil } diff --git a/pkg/log/log.go b/pkg/log/log.go new file mode 100644 index 0000000..0179111 --- /dev/null +++ b/pkg/log/log.go @@ -0,0 +1,110 @@ +// Package log provides colored terminal logging for Raven. +// All colors are drawn from the Raven brand gradient: red → pink → purple. +package log + +import ( + "fmt" + "os" + "strings" +) + +// isTTY is true when stderr is an interactive terminal. +var isTTY = func() bool { + fi, err := os.Stderr.Stat() + if err != nil { + return false + } + return (fi.Mode() & os.ModeCharDevice) != 0 +}() + +// Raven brand gradient (red → crimson → hot-pink → deep-purple). +const ( + colorRed = "\033[38;2;255;58;45m" // #FF3A2D - bright red + colorCrimson = "\033[38;2;225;30;100m" // #E11E64 - crimson + colorPink = "\033[38;2;190;20;155m" // #BE149B - hot pink + colorViolet = "\033[38;2;155;15;190m" // #9B0FBE - violet + colorPurple = "\033[38;2;120;10;210m" // #780AD2 - deep purple + colorDim = "\033[38;2;140;100;160m\033[2m" // muted purple-grey + colorReset = "\033[0m" +) + +// tagColor maps a log tag to its brand gradient color. +var tagColor = map[string]string{ + // red end - low-level PE/load machinery + "pe": colorRed, + "resolve": colorRed, + "PATCH": colorRed, + "loader": colorRed, + // crimson - win32 / wayland surface + "win32": colorCrimson, + "wl": colorCrimson, + "wndclass": colorCrimson, + "ordinal": colorCrimson, + // hot pink - high-level win32 subsystem + "menu": colorPink, + "setpos": colorPink, + "dispatch": colorPink, + "destroy": colorPink, + "comctl32": colorPink, + // violet - exception / SEH path + "seh": colorViolet, + // purple end - ntapi / syscall / runtime + "ntapi": colorPurple, + "govm": colorPurple, + "trace": colorPurple, + "x11-trace":colorPurple, + "brk": colorPurple, + "heap-sig": colorPurple, + "HEAP-CORRUPT": colorRed, + "delay": colorPurple, + "loadstr": colorPurple, + "flags": colorPurple, +} + +// Logf writes a formatted log line to stderr. +// If the message begins with "[tag]", the tag is colorized using the Raven +// brand gradient; otherwise the message is printed verbatim. +func Logf(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + if !isTTY { + fmt.Fprint(os.Stderr, msg) + return + } + if len(msg) > 2 && msg[0] == '[' { + end := strings.IndexByte(msg, ']') + if end > 0 { + tag := msg[1:end] + rest := msg[end+1:] + if color, ok := tagColor[tag]; ok { + fmt.Fprintf(os.Stderr, "%s[%s]%s%s", color, tag, colorReset, rest) + return + } + } + } + fmt.Fprint(os.Stderr, msg) +} + +// Banner prints the Raven startup banner to stderr. +// It is a no-op when stderr is not a terminal. +func Banner(version string) { + if !isTTY { + return + } + // Each letter of RAVEN gets a step along the red→purple gradient. + r := colorRed + a := colorCrimson + v := colorPink + e := colorViolet + n := colorPurple + dim := colorDim + rst := colorReset + + fmt.Fprintf(os.Stderr, + "\n %sR%sA%sV%sE%sN%s %s─ Windows PE · ELF · x86-64 emulator%s\n", + r, a, v, e, n, rst, dim, rst, + ) + fmt.Fprintf(os.Stderr, + " %s%s · MIT License · Wayland native%s\n\n", + dim, version, rst, + ) +} diff --git a/pkg/ntapi/crt.go b/pkg/ntapi/crt.go index 291cb0e..76331f1 100644 --- a/pkg/ntapi/crt.go +++ b/pkg/ntapi/crt.go @@ -1,6 +1,6 @@ package ntapi -import "github.com/mirkobrombin/go-vm/pkg/mmu" +import "github.com/mirkobrombin/Raven/pkg/mmu" // PatchCRTFunctions replaces synthetic ntdll CRT stubs with real x64 machine // code so they execute natively in the emulator instead of trapping. diff --git a/pkg/ntapi/dllmain.go b/pkg/ntapi/dllmain.go index 2d605cb..e9d8626 100644 --- a/pkg/ntapi/dllmain.go +++ b/pkg/ntapi/dllmain.go @@ -3,8 +3,8 @@ package ntapi import ( "fmt" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // DllMain reason codes. diff --git a/pkg/ntapi/handler.go b/pkg/ntapi/handler.go index 23475e1..247a444 100644 --- a/pkg/ntapi/handler.go +++ b/pkg/ntapi/handler.go @@ -8,7 +8,9 @@ import ( "strings" "time" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // NTSTATUS codes. @@ -773,7 +775,7 @@ func handleNtAllocateVirtualMemory(ctx *TrapContext) uint32 { perms := mmu.PermRead | mmu.PermWrite addr := ctx.Mem.Mmap(baseAddr, size, perms) if addr < 0x1000 { - fmt.Fprintf(os.Stderr, "[WARN] NtAllocateVirtualMemory mapped addr=0x%x size=0x%x baseAddr=0x%x\n", addr, size, baseAddr) + ravelog.Logf("[WARN] NtAllocateVirtualMemory mapped addr=0x%x size=0x%x baseAddr=0x%x\n", addr, size, baseAddr) } ctx.Mem.Write64(basePtr, addr) @@ -2602,7 +2604,7 @@ func handleNtGetNlsSectionPtr(ctx *TrapContext) uint32 { ptrOut := ctx.Arg(3) sizeOut := ctx.Arg(4) - fmt.Fprintf(os.Stderr, "[ntapi] NtGetNlsSectionPtr type=%d id=%d ptrOut=0x%x sizeOut=0x%x\n", + 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 == "" { @@ -2635,7 +2637,7 @@ func handleNtGetNlsSectionPtr(ctx *TrapContext) uint32 { } if err != nil { - fmt.Fprintf(os.Stderr, "[ntapi] NtGetNlsSectionPtr: failed to load NLS type=%d id=%d: %v\n", nlsType, nlsID, err) + ravelog.Logf("[ntapi] NtGetNlsSectionPtr: failed to load NLS type=%d id=%d: %v\n", nlsType, nlsID, err) return StatusNotImplemented } @@ -2655,7 +2657,7 @@ func handleNtGetNlsSectionPtr(ctx *TrapContext) uint32 { ctx.Mem.Write64(sizeOut, size) } - fmt.Fprintf(os.Stderr, "[ntapi] NtGetNlsSectionPtr: mapped type=%d id=%d at 0x%x size=0x%x\n", nlsType, nlsID, dataAddr, size) + ravelog.Logf("[ntapi] NtGetNlsSectionPtr: mapped type=%d id=%d at 0x%x size=0x%x\n", nlsType, nlsID, dataAddr, size) return StatusSuccess } @@ -2691,7 +2693,7 @@ func handleRtlGetLocaleFileMappingAddress(ctx *TrapContext) uint32 { dataAddr := base + uint64(pad) ctx.Mem.WriteBytes(dataAddr, data) - fmt.Fprintf(os.Stderr, "[ntapi] RtlGetLocaleFileMappingAddress: mapped locale.nls at 0x%x size=0x%x\n", dataAddr, len(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) diff --git a/pkg/ntapi/nls.go b/pkg/ntapi/nls.go index 5c496fa..0cb6c39 100644 --- a/pkg/ntapi/nls.go +++ b/pkg/ntapi/nls.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // buildNlsCodePageData generates a minimal NLS code page table in the Windows diff --git a/pkg/ntapi/peb.go b/pkg/ntapi/peb.go index 8450110..74c870f 100644 --- a/pkg/ntapi/peb.go +++ b/pkg/ntapi/peb.go @@ -3,7 +3,7 @@ package ntapi import ( "encoding/binary" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // Windows 10 x64 structure offsets (verified against phnt/Vergilius Project). diff --git a/pkg/ntapi/process.go b/pkg/ntapi/process.go index e46ceda..dc157ab 100644 --- a/pkg/ntapi/process.go +++ b/pkg/ntapi/process.go @@ -1,7 +1,7 @@ package ntapi import ( - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // ProcessManager handles NtAllocateVirtualMemory, NtProtectVirtualMemory, diff --git a/pkg/ntapi/seh.go b/pkg/ntapi/seh.go index 3e816de..1db9ef8 100644 --- a/pkg/ntapi/seh.go +++ b/pkg/ntapi/seh.go @@ -3,10 +3,11 @@ package ntapi import ( "encoding/binary" "fmt" - "os" "sort" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // RUNTIME_FUNCTION represents an x64 exception handling entry from .pdata. @@ -79,6 +80,8 @@ type UnwindInfo struct { FrameOffset uint8 HandlerRVA uint32 // valid if Flags & (EHandler|UHandler) HandlerData uint64 // guest addr of handler-specific data + // Chained: valid if Flags & ChainInfo - points to parent function + ChainedRF *RuntimeFunction } // ParseUnwindInfo reads UNWIND_INFO from guest memory. @@ -106,6 +109,17 @@ func ParseUnwindInfo(mem *mmu.MMU, imageBase uint64, unwindRVA uint32) *UnwindIn handlerAddr := addr + 4 + codesSize ui.HandlerRVA, _ = mem.Read32(handlerAddr) ui.HandlerData = handlerAddr + 4 + } else if ui.Flags&UnwFlagChainInfo != 0 { + // Chained: after unwind codes is a RUNTIME_FUNCTION (12 bytes) + codesSize := uint64(ui.CountOfCodes) * 2 + if codesSize%4 != 0 { + codesSize += 2 + } + chainAddr := addr + 4 + codesSize + begin, _ := mem.Read32(chainAddr) + end, _ := mem.Read32(chainAddr + 4) + unwindData, _ := mem.Read32(chainAddr + 8) + ui.ChainedRF = &RuntimeFunction{begin, end, unwindData} } return ui @@ -135,25 +149,38 @@ func CxxDispatchException(mem *mmu.MMU, et *ExceptionTable, rsp, rip uint64, tra currentRSP := rsp currentRIP := rip + // Always trace on failure path - retry with trace enabled if first pass fails + result := cxxDispatch(mem, et, imageBase, imageEnd, rsp, rip, trace) + if result == nil && !trace { + ravelog.Logf("[seh] no handler found for RIP=0x%x RSP=0x%x - retrying with trace\n", rip, rsp) + cxxDispatch(mem, et, imageBase, imageEnd, currentRSP, currentRIP, true) + } + return result +} + +func cxxDispatch(mem *mmu.MMU, et *ExceptionTable, imageBase, imageEnd uint64, rsp, rip uint64, trace bool) *CxxCatchHandler { if trace { - fmt.Fprintf(os.Stderr, "[seh] dispatch: RSP=0x%x RIP=0x%x imageBase=0x%x\n", rsp, rip, imageBase) + ravelog.Logf("[seh] dispatch: RSP=0x%x RIP=0x%x imageBase=0x%x\n", rsp, rip, imageBase) } // Track saved registers across all frames for proper unwind savedRegs := make(map[int]uint64) + currentRSP := rsp + currentRIP := rip + for depth := 0; depth < 64; depth++ { // Check if RIP is in the PE image range if currentRIP < imageBase || currentRIP >= imageEnd { retAddr, err := mem.Read64(currentRSP) if err != nil || retAddr == 0 { if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: RIP 0x%x out of range, retAddr read failed\n", depth, currentRIP) + ravelog.Logf("[seh] depth %d: RIP 0x%x out of range, retAddr read failed\n", depth, currentRIP) } return nil } if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: RIP 0x%x out of range, following retAddr 0x%x\n", depth, currentRIP, retAddr) + ravelog.Logf("[seh] depth %d: RIP 0x%x out of range, following retAddr 0x%x\n", depth, currentRIP, retAddr) } currentRIP = retAddr currentRSP += 8 @@ -168,7 +195,7 @@ func CxxDispatchException(mem *mmu.MMU, et *ExceptionTable, rsp, rip uint64, tra return nil } if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: RVA 0x%x no .pdata, following retAddr 0x%x\n", depth, rva, retAddr) + ravelog.Logf("[seh] depth %d: RVA 0x%x no .pdata, following retAddr 0x%x\n", depth, rva, retAddr) } currentRIP = retAddr currentRSP += 8 @@ -185,10 +212,53 @@ func CxxDispatchException(mem *mmu.MMU, et *ExceptionTable, rsp, rip uint64, tra parentRSP := currentRSP + frameSize if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: RVA 0x%x func [0x%x-0x%x] flags=0x%x frameSize=%d\n", + ravelog.Logf("[seh] depth %d: RVA 0x%x func [0x%x-0x%x] flags=0x%x frameSize=%d\n", depth, rva, rf.BeginAddress, rf.EndAddress, ui.Flags, frameSize) } + // Follow CHAININFO: accumulate frame size from chained parent. + // The secondary (chained) record covers part of the prolog but the return + // address (+8) is counted only by the primary (parent) record. So we + // subtract 8 from the secondary's frame size before handing off to the primary. + chainedUI := ui + chainedRSP := currentRSP + (frameSize - 8) // strip the virtual return-addr slot + for chainedUI.Flags&UnwFlagChainInfo != 0 && chainedUI.ChainedRF != nil { + parentUI := ParseUnwindInfo(mem, imageBase, chainedUI.ChainedRF.UnwindData) + if parentUI == nil { + break + } + parentFrameSize := unwindFrame(mem, imageBase+uint64(chainedUI.ChainedRF.UnwindData), parentUI, chainedRSP, savedRegs) + if trace { + ravelog.Logf("[seh] depth %d: chain → func [0x%x-0x%x] flags=0x%x parentFrameSize=%d chainedRSP=0x%x\n", + depth, chainedUI.ChainedRF.BeginAddress, chainedUI.ChainedRF.EndAddress, parentUI.Flags, parentFrameSize, chainedRSP) + } + chainedParentRSP := chainedRSP + parentFrameSize + if parentUI.Flags&UnwFlagEHandler != 0 { + // Found the exception handler in the chained parent + handler := findCxxCatchAll(mem, imageBase, parentUI, chainedRSP, currentRIP, chainedParentRSP, trace) + if handler != nil { + if handler.IsFallback { + handler.SavedRegs = make(map[int]uint64, len(savedRegs)) + for k, v := range savedRegs { + handler.SavedRegs[k] = v + } + } + return handler + } + } + chainedUI = parentUI + if parentUI.Flags&UnwFlagChainInfo != 0 { + // Continue chain: strip return-addr slot again for next secondary + chainedRSP = chainedParentRSP - 8 + } else { + chainedRSP = chainedParentRSP + } + } + // Update parentRSP to the resolved chained parent RSP if we followed a chain + if chainedRSP != currentRSP+(frameSize-8) { + parentRSP = chainedRSP + } + if ui.Flags&UnwFlagEHandler != 0 { handler := findCxxCatchAll(mem, imageBase, ui, currentRSP, currentRIP, parentRSP, trace) if handler != nil { @@ -207,12 +277,12 @@ func CxxDispatchException(mem *mmu.MMU, et *ExceptionTable, rsp, rip uint64, tra retAddr, err := mem.Read64(parentRSP - 8) if err != nil || retAddr == 0 { if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: parentRSP 0x%x retAddr read failed\n", depth, parentRSP) + ravelog.Logf("[seh] depth %d: parentRSP 0x%x retAddr read failed\n", depth, parentRSP) } return nil } if trace { - fmt.Fprintf(os.Stderr, "[seh] depth %d: → parent retAddr 0x%x parentRSP 0x%x\n", depth, retAddr, parentRSP) + ravelog.Logf("[seh] depth %d: → parent retAddr 0x%x parentRSP 0x%x\n", depth, retAddr, parentRSP) } currentRIP = retAddr currentRSP = parentRSP @@ -363,7 +433,7 @@ func findCxxCatchAll(mem *mmu.MMU, imageBase uint64, ui *UnwindInfo, rsp, rip, p magic, _ := mem.Read32(funcInfoAddr) if trace { - fmt.Fprintf(os.Stderr, "[seh] handlerRVA=0x%x handlerData=0x%x funcInfoRVA=0x%x funcInfoAddr=0x%x magic=0x%x countOfCodes=%d\n", + ravelog.Logf("[seh] handlerRVA=0x%x handlerData=0x%x funcInfoRVA=0x%x funcInfoAddr=0x%x magic=0x%x countOfCodes=%d\n", ui.HandlerRVA, ui.HandlerData, funcInfoRVA, funcInfoAddr, magic, ui.CountOfCodes) } @@ -375,12 +445,12 @@ func findCxxCatchAll(mem *mmu.MMU, imageBase uint64, ui *UnwindInfo, rsp, rip, p retAddr, err := mem.Read64(parentRSP - 8) if err != nil || retAddr == 0 { if trace { - fmt.Fprintf(os.Stderr, "[seh] FH4 magic 0x%x - can't read parent retAddr\n", magic) + ravelog.Logf("[seh] FH4 magic 0x%x - can't read parent retAddr\n", magic) } return nil } if trace { - fmt.Fprintf(os.Stderr, "[seh] FH4 magic 0x%x - fallback unwind to retAddr=0x%x RSP=0x%x\n", + ravelog.Logf("[seh] FH4 magic 0x%x - fallback unwind to retAddr=0x%x RSP=0x%x\n", magic, retAddr, parentRSP) } return &CxxCatchHandler{ @@ -417,7 +487,7 @@ func findCxxCatchAll(mem *mmu.MMU, imageBase uint64, ui *UnwindInfo, rsp, rip, p } if trace { - fmt.Fprintf(os.Stderr, "[seh] nTryBlocks=%d throwState=%d\n", nTryBlocks, throwState) + ravelog.Logf("[seh] nTryBlocks=%d throwState=%d\n", nTryBlocks, throwState) } // Walk try blocks @@ -433,7 +503,7 @@ func findCxxCatchAll(mem *mmu.MMU, imageBase uint64, ui *UnwindInfo, rsp, rip, p handlerArrayRVA, _ := mem.Read32(entryAddr + 16) if trace { - fmt.Fprintf(os.Stderr, "[seh] tryBlock[%d]: low=%d high=%d nCatches=%d handlerArrayRVA=0x%x\n", + ravelog.Logf("[seh] tryBlock[%d]: low=%d high=%d nCatches=%d handlerArrayRVA=0x%x\n", i, tryLow, tryHigh, nCatches, handlerArrayRVA) } @@ -455,7 +525,7 @@ func findCxxCatchAll(mem *mmu.MMU, imageBase uint64, ui *UnwindInfo, rsp, rip, p handlerRVA, _ := mem.Read32(hAddr + 12) if trace { - fmt.Fprintf(os.Stderr, "[seh] catch[%d]: typeRVA=0x%x handlerRVA=0x%x\n", c, typeRVA, handlerRVA) + ravelog.Logf("[seh] catch[%d]: typeRVA=0x%x handlerRVA=0x%x\n", c, typeRVA, handlerRVA) } // catch(...) has typeRVA == 0 diff --git a/pkg/ntapi/synthpe.go b/pkg/ntapi/synthpe.go index adfaa56..0b39e06 100644 --- a/pkg/ntapi/synthpe.go +++ b/pkg/ntapi/synthpe.go @@ -4,8 +4,8 @@ import ( "encoding/binary" "fmt" - "github.com/mirkobrombin/go-vm/pkg/loader" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/loader" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // PE section characteristics (duplicated from loader for use in synth PE gen). diff --git a/pkg/ntapi/trap.go b/pkg/ntapi/trap.go index 4b45abe..55b0a20 100644 --- a/pkg/ntapi/trap.go +++ b/pkg/ntapi/trap.go @@ -4,9 +4,11 @@ import ( "fmt" "os" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" - "github.com/mirkobrombin/go-vm/pkg/vfs" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/vfs" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // TrapHandler is a function that handles a single NT syscall. @@ -283,7 +285,7 @@ func (d *Dispatcher) Handle(cpu arch.CPU) { if name == "" { name = fmt.Sprintf("unknown_0x%04X", syscallID) } - fmt.Fprintf(os.Stderr, "[ntapi] UNIMPLEMENTED: %s (id=0x%04X)\n", name, syscallID) + ravelog.Logf("[ntapi] UNIMPLEMENTED: %s (id=0x%04X)\n", name, syscallID) // For auto-synth user-mode DLL stubs, return 0 (NULL/FALSE). // For NT syscalls, return STATUS_NOT_IMPLEMENTED. if syscallID >= AutoSynthIDBase { @@ -299,7 +301,7 @@ func (d *Dispatcher) Handle(cpu arch.CPU) { sp := cpu.GetSP() if name != "__wine_dbg_header" || sp < 0x7C000000 { retAddr, _ := d.ctx.Mem.Read64(sp) - fmt.Fprintf(os.Stderr, "[trace] → %s (id=0x%04X) sp=0x%x ret=0x%x\n", name, syscallID, sp, retAddr) + ravelog.Logf("[trace] → %s (id=0x%04X) sp=0x%x ret=0x%x\n", name, syscallID, sp, retAddr) } } diff --git a/pkg/syscall/handler.go b/pkg/syscall/handler.go index 6c18a24..c29f58a 100644 --- a/pkg/syscall/handler.go +++ b/pkg/syscall/handler.go @@ -15,11 +15,13 @@ import ( "golang.org/x/sys/unix" - "github.com/mirkobrombin/go-vm/pkg/arch" - "github.com/mirkobrombin/go-vm/pkg/mmu" - "github.com/mirkobrombin/go-vm/pkg/signal" - "github.com/mirkobrombin/go-vm/pkg/thread" - "github.com/mirkobrombin/go-vm/pkg/vfs" + "github.com/mirkobrombin/Raven/pkg/arch" + "github.com/mirkobrombin/Raven/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/signal" + "github.com/mirkobrombin/Raven/pkg/thread" + "github.com/mirkobrombin/Raven/pkg/vfs" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // Linux RISC-V syscall numbers (from asm-generic/unistd.h) @@ -218,11 +220,11 @@ func (h *Handler) Handle(c arch.CPU) { a0, a1, a2, a3, a4, a5 := args[0], args[1], args[2], args[3], args[4], args[5] if h.debugX11 { - fmt.Fprintf(os.Stderr, "[x11-trace] syscall %d (a0=0x%x a1=0x%x a2=0x%x)\n", num, a0, a1, a2) + ravelog.Logf("[x11-trace] syscall %d (a0=0x%x a1=0x%x a2=0x%x)\n", num, a0, a1, a2) } if h.Trace { - fmt.Fprintf(os.Stderr, "[trace] syscall %d (a0=0x%x a1=0x%x a2=0x%x a3=0x%x) instret=%d\n", num, a0, a1, a2, a3, c.GetInstret()) + ravelog.Logf("[trace] syscall %d (a0=0x%x a1=0x%x a2=0x%x a3=0x%x) instret=%d\n", num, a0, a1, a2, a3, c.GetInstret()) } // DEBUG: heap integrity check before/after each syscall @@ -230,17 +232,17 @@ func (h *Handler) Handle(c arch.CPU) { if h.Trace { heapSigBefore = h.heapSignature() if heapSigBefore != 0 { - fmt.Fprintf(os.Stderr, "[heap-sig] before syscall %d: 0x%x\n", num, heapSigBefore) + ravelog.Logf("[heap-sig] before syscall %d: 0x%x\n", num, heapSigBefore) } } defer func() { if h.Trace { after := h.heapSignature() if after != 0 { - fmt.Fprintf(os.Stderr, "[heap-sig] after syscall %d: 0x%x\n", num, after) + ravelog.Logf("[heap-sig] after syscall %d: 0x%x\n", num, after) } if heapSigBefore != 0 && after != 0 && heapSigBefore != after { - fmt.Fprintf(os.Stderr, "[HEAP-CORRUPT] syscall %d (a0=0x%x a1=0x%x a2=0x%x) changed heap sig 0x%x→0x%x instret=%d\n", + ravelog.Logf("[HEAP-CORRUPT] syscall %d (a0=0x%x a1=0x%x a2=0x%x) changed heap sig 0x%x→0x%x instret=%d\n", num, a0, a1, a2, heapSigBefore, after, c.GetInstret()) } } @@ -270,12 +272,12 @@ func (h *Handler) Handle(c arch.CPU) { case SysBrk: result := h.Mem.Brk(a0) if h.Trace { - fmt.Fprintf(os.Stderr, "[brk] req=0x%x → 0x%x\n", a0, result) + ravelog.Logf("[brk] req=0x%x → 0x%x\n", a0, result) if a0 != 0 && result == a0 { // Dump first 128 bytes of the heap region heapBase := h.Mem.GetBrkBase() if data, err := h.Mem.ReadBytes(heapBase, 128); err == nil { - fmt.Fprintf(os.Stderr, "[brk] heap@0x%x: %x\n", heapBase, data) + ravelog.Logf("[brk] heap@0x%x: %x\n", heapBase, data) } } } @@ -601,7 +603,7 @@ func (h *Handler) Handle(c arch.CPU) { c.SetSyscallReturn(0) default: - fmt.Fprintf(os.Stderr, "[govm] unhandled syscall %d (a0=0x%x a1=0x%x a2=0x%x)\n", num, a0, a1, a2) + ravelog.Logf("[govm] unhandled syscall %d (a0=0x%x a1=0x%x a2=0x%x)\n", num, a0, a1, a2) c.SetSyscallReturn(negErrno(ENOSYS)) } } @@ -630,7 +632,7 @@ func (h *Handler) sysExecve(c arch.CPU, pathPtr, argvPtr, envpPtr uint64) { // Build envp envp := h.readGuestStringArray(envpPtr) - fmt.Fprintf(os.Stderr, "[govm] execve(%q, %v)\n", path, argv) + ravelog.Logf("[govm] execve(%q, %v)\n", path, argv) h.vforkMu.Lock() pending := h.vforkPending @@ -643,7 +645,7 @@ func (h *Handler) sysExecve(c arch.CPU, pathPtr, argvPtr, envpPtr uint64) { if !pending { // Not inside a vfork child - can't replace main guest image. - fmt.Fprintf(os.Stderr, "[govm] execve outside vfork → ENOSYS\n") + ravelog.Logf("[govm] execve outside vfork → ENOSYS\n") c.SetSyscallReturn(negErrno(ENOSYS)) return } @@ -678,7 +680,7 @@ func (h *Handler) sysExecve(c arch.CPU, pathPtr, argvPtr, envpPtr uint64) { cmd.Stdout = stdoutF cmd.Stderr = stderrF if err := cmd.Start(); err != nil { - fmt.Fprintf(os.Stderr, "[govm] execve host start error: %v\n", err) + ravelog.Logf("[govm] execve host start error: %v\n", err) ch <- -1 c.Stop() return @@ -710,7 +712,7 @@ func (h *Handler) sysExecve(c arch.CPU, pathPtr, argvPtr, envpPtr uint64) { h.childProcs[gpid] = cmd.Process h.childMu.Unlock() - fmt.Fprintf(os.Stderr, "[govm] execve launched pid=%d (guest=%d)\n", cmd.Process.Pid, gpid) + ravelog.Logf("[govm] execve launched pid=%d (guest=%d)\n", cmd.Process.Pid, gpid) ch <- gpid // Stop this child CPU - it has been replaced by the host process. c.Stop() @@ -722,7 +724,7 @@ func (h *Handler) sysWait4(c arch.CPU, gpidArg, wstatusPtr, options, rusagePtr u h.childMu.Lock() if len(h.childProcs) == 0 { h.childMu.Unlock() - fmt.Fprintf(os.Stderr, "[govm] wait4(pid=%d) → ECHILD (no children)\n", gpid) + ravelog.Logf("[govm] wait4(pid=%d) → ECHILD (no children)\n", gpid) c.SetSyscallReturn(negErrno(ECHILD)) return } @@ -738,14 +740,14 @@ func (h *Handler) sysWait4(c arch.CPU, gpidArg, wstatusPtr, options, rusagePtr u } if proc == nil { h.childMu.Unlock() - fmt.Fprintf(os.Stderr, "[govm] wait4(pid=%d) → ECHILD (not found)\n", gpid) + ravelog.Logf("[govm] wait4(pid=%d) → ECHILD (not found)\n", gpid) c.SetSyscallReturn(negErrno(ECHILD)) return } h.childMu.Unlock() const WNOHANG = 1 - fmt.Fprintf(os.Stderr, "[govm] wait4(guest=%d host=%d options=0x%x)\n", gpid, proc.Pid, options) + ravelog.Logf("[govm] wait4(guest=%d host=%d options=0x%x)\n", gpid, proc.Pid, options) if options&WNOHANG != 0 { // Non-blocking: use unix.Wait4 with WNOHANG. @@ -763,7 +765,7 @@ func (h *Handler) sysWait4(c arch.CPU, gpidArg, wstatusPtr, options, rusagePtr u if wstatusPtr != 0 { h.Mem.Write32(wstatusPtr, ws) } - fmt.Fprintf(os.Stderr, "[govm] wait4 WNOHANG → pid=%d wstatus=0x%x\n", foundPID, ws) + ravelog.Logf("[govm] wait4 WNOHANG → pid=%d wstatus=0x%x\n", foundPID, ws) c.SetSyscallReturn(uint64(foundPID)) return } @@ -775,7 +777,7 @@ func (h *Handler) sysWait4(c arch.CPU, gpidArg, wstatusPtr, options, rusagePtr u delete(h.childProcs, foundPID) h.childMu.Unlock() if err != nil { - fmt.Fprintf(os.Stderr, "[govm] wait4 error: %v\n", err) + ravelog.Logf("[govm] wait4 error: %v\n", err) c.SetSyscallReturn(negErrno(ECHILD)) return } @@ -783,7 +785,7 @@ func (h *Handler) sysWait4(c arch.CPU, gpidArg, wstatusPtr, options, rusagePtr u if wstatusPtr != 0 { h.Mem.Write32(wstatusPtr, ws) } - fmt.Fprintf(os.Stderr, "[govm] wait4 → pid=%d wstatus=0x%x exitCode=%d\n", foundPID, ws, wstatus.ExitStatus()) + ravelog.Logf("[govm] wait4 → pid=%d wstatus=0x%x exitCode=%d\n", foundPID, ws, wstatus.ExitStatus()) c.SetSyscallReturn(uint64(foundPID)) } @@ -942,13 +944,13 @@ func (h *Handler) sysOpenat(c arch.CPU, dirfd, pathAddr, flags, mode uint64) { fd, err := h.FS.OpenAt(int(int32(dirfd)), path, int(flags), uint32(mode)) if err != nil { if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] openat(%q) → ENOENT\n", path) + ravelog.Logf("[govm] openat(%q) → ENOENT\n", path) } c.SetSyscallReturn(negErrno(ENOENT)) return } if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] openat(%q) → fd=%d\n", path, fd) + ravelog.Logf("[govm] openat(%q) → fd=%d\n", path, fd) } c.SetSyscallReturn(uint64(fd)) } @@ -972,7 +974,7 @@ func (h *Handler) sysMprotect(c arch.CPU, addr, length, prot uint64) { for pg := addr; pg < addr+length; pg += mmu.PageSize { if !h.Mem.IsMapped(pg) { if h.Trace { - fmt.Fprintf(os.Stderr, "[mprotect] ENOMEM: addr=0x%x len=0x%x prot=%d unmapped_page=0x%x\n", addr, length, prot, pg) + ravelog.Logf("[mprotect] ENOMEM: addr=0x%x len=0x%x prot=%d unmapped_page=0x%x\n", addr, length, prot, pg) } c.SetSyscallReturn(negErrno(ENOMEM)) return @@ -981,11 +983,11 @@ func (h *Handler) sysMprotect(c arch.CPU, addr, length, prot uint64) { // Change permissions if h.Trace { - fmt.Fprintf(os.Stderr, "[mprotect] OK: addr=0x%x len=0x%x end=0x%x prot=%d perms=0x%x pages=%d\n", addr, length, addr+length, prot, perms, length/mmu.PageSize) + ravelog.Logf("[mprotect] OK: addr=0x%x len=0x%x end=0x%x prot=%d perms=0x%x pages=%d\n", addr, length, addr+length, prot, perms, length/mmu.PageSize) brkBase := h.Mem.GetBrkBase() brkCur := h.Mem.Brk(0) if brkBase != 0 && addr+length > brkBase { - fmt.Fprintf(os.Stderr, "[mprotect] WARNING: overlaps heap! heap=0x%x-0x%x mprotect=0x%x-0x%x prot=%d\n", + ravelog.Logf("[mprotect] WARNING: overlaps heap! heap=0x%x-0x%x mprotect=0x%x-0x%x prot=%d\n", brkBase, brkCur, addr, addr+length, prot) } } @@ -998,7 +1000,7 @@ func (h *Handler) sysMprotect(c arch.CPU, addr, length, prot uint64) { func (h *Handler) sysIoctl(c arch.CPU, fd, cmd uint64) { if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] ioctl(fd=%d, cmd=0x%x)\n", fd, cmd) + ravelog.Logf("[govm] ioctl(fd=%d, cmd=0x%x)\n", fd, cmd) } const FIONREAD = 0x541B const FIONBIO = 0x5421 @@ -1020,7 +1022,7 @@ func (h *Handler) sysIoctl(c arch.CPU, fd, cmd uint64) { if err == nil { h.Mem.Write32(arg, uint32(avail)) if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] ioctl FIONREAD fd=%d→hfd=%d avail=%d\n", fd, hostFD, avail) + ravelog.Logf("[govm] ioctl FIONREAD fd=%d→hfd=%d avail=%d\n", fd, hostFD, avail) } c.SetSyscallReturn(0) return @@ -1072,7 +1074,7 @@ func (h *Handler) dumpGuestEnviron() { // to find the argc, but that's complex. Instead, just dump the strings // in strArea directly. if data, err := h.Mem.ReadBytes(strArea, 512); err == nil { - fmt.Fprintf(os.Stderr, "[ENVIRON DUMP] strArea=0x%x:\n", strArea) + ravelog.Logf("[ENVIRON DUMP] strArea=0x%x:\n", strArea) // Print as individual nul-terminated strings start := 0 for i, b := range data { @@ -1229,7 +1231,7 @@ func (h *Handler) sysPipe2(c arch.CPU, pipefdAddr, flags uint64) { h.Mem.Write32(pipefdAddr, uint32(rfd)) h.Mem.Write32(pipefdAddr+4, uint32(wfd)) if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] pipe2 → rfd=%d wfd=%d\n", rfd, wfd) + ravelog.Logf("[govm] pipe2 → rfd=%d wfd=%d\n", rfd, wfd) } c.SetSyscallReturn(0) } @@ -1295,7 +1297,7 @@ func (h *Handler) sysReadlinkat(c arch.CPU, dirfd, pathAddr, buf, bufsiz uint64) data = data[:bufsiz] } if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] readlinkat(%q) → %q (len=%d)\n", path, data, len(data)) + ravelog.Logf("[govm] readlinkat(%q) → %q (len=%d)\n", path, data, len(data)) } h.Mem.WriteBytes(buf, data) c.SetSyscallReturn(uint64(len(data))) @@ -1310,7 +1312,7 @@ func (h *Handler) sysFaccessat(c arch.CPU, dirfd, pathAddr uint64) { _, err = h.FS.StatPath(int(int32(dirfd)), path) if strings.Contains(path, "Xauth") || strings.Contains(path, "xauth") || strings.Contains(path, ".X") || strings.Contains(path, "mutter") { if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] access(%q) → err=%v\n", path, err) + ravelog.Logf("[govm] access(%q) → err=%v\n", path, err) } } if err != nil { @@ -1956,7 +1958,7 @@ func (h *Handler) sysClone(c arch.CPU, flags, childStack, parentTid, a3, a4 uint childCPU.SetSyscallHandler(h.Handle) go func() { if err2 := childCPU.Run(); err2 != nil { - fmt.Fprintf(os.Stderr, "[govm] vfork-child exited: %v\n", err2) + ravelog.Logf("[govm] vfork-child exited: %v\n", err2) } // If child exited without execve, restore FD table and unblock parent. h.vforkMu.Lock() @@ -1973,7 +1975,7 @@ func (h *Handler) sysClone(c arch.CPU, flags, childStack, parentTid, a3, a4 uint guestPID := <-ch c.SetSyscallReturn(uint64(guestPID)) } else { - fmt.Fprintf(os.Stderr, "[govm] clone fork-like flags=0x%x → ENOSYS\n", flags) + ravelog.Logf("[govm] clone fork-like flags=0x%x → ENOSYS\n", flags) c.SetSyscallReturn(negErrno(ENOSYS)) } } @@ -2057,7 +2059,7 @@ func (h *Handler) sysClone3(c arch.CPU, uargsPtr, size uint64) { } go func() { if err2 := childCPU.Run(); err2 != nil { - fmt.Fprintf(os.Stderr, "[govm] vfork-child exited: %v\n", err2) + ravelog.Logf("[govm] vfork-child exited: %v\n", err2) } h.vforkMu.Lock() pending := h.vforkPending @@ -2075,7 +2077,7 @@ func (h *Handler) sysClone3(c arch.CPU, uargsPtr, size uint64) { } c.SetSyscallReturn(uint64(guestPID)) } else { - fmt.Fprintf(os.Stderr, "[govm] clone3 fork-like flags=0x%x → ENOSYS\n", flags) + ravelog.Logf("[govm] clone3 fork-like flags=0x%x → ENOSYS\n", flags) c.SetSyscallReturn(negErrno(ENOSYS)) } } @@ -2102,7 +2104,7 @@ func (h *Handler) sysWritev(c arch.CPU, fdNum, iovAddr, iovcnt uint64) { if heapBase != 0 { // Dump first 2048 bytes for better visibility if data, err := h.Mem.ReadBytes(heapBase, 2048); err == nil { - fmt.Fprintf(os.Stderr, "[heap-dump] base=0x%x\n", heapBase) + ravelog.Logf("[heap-dump] base=0x%x\n", heapBase) // Print as 8-byte words for easier reading for off := 0; off+8 <= len(data); off += 8 { val := binary.LittleEndian.Uint64(data[off:]) @@ -2136,7 +2138,7 @@ func (h *Handler) sysWritev(c arch.CPU, fdNum, iovAddr, iovcnt uint64) { if len(disp) > 24 { disp = disp[:24] } - fmt.Fprintf(os.Stderr, "[govm] writev(fd=%d→hfd=%d, buf=%d, base=0x%x): %02x\n", fdNum, hfd, length, base, disp) + ravelog.Logf("[govm] writev(fd=%d→hfd=%d, buf=%d, base=0x%x): %02x\n", fdNum, hfd, length, base, disp) } } n, err := h.FS.Write(int(fdNum), data) @@ -2154,14 +2156,14 @@ func (h *Handler) sysRead(c arch.CPU, fdNum, bufAddr, count uint64) { n, err := h.FS.Read(int(fdNum), buf) if err != nil && err != io.EOF { hfd := h.FS.GetHostFDNum(int(fdNum)) - fmt.Fprintf(os.Stderr, "[govm] read(fd=%d→hfd=%d) err=%v\n", fdNum, hfd, err) + ravelog.Logf("[govm] read(fd=%d→hfd=%d) err=%v\n", fdNum, hfd, err) c.SetSyscallReturn(negErrno(errnoFromErr(err))) return } if n > 0 { if h.Trace { if hfd := h.FS.GetHostFDNum(int(fdNum)); hfd >= 0 { - fmt.Fprintf(os.Stderr, "[govm] read(fd=%d→hfd=%d, n=%d): %02x\n", fdNum, hfd, n, buf[:min(8, n)]) + ravelog.Logf("[govm] read(fd=%d→hfd=%d, n=%d): %02x\n", fdNum, hfd, n, buf[:min(8, n)]) } } if err := h.Mem.WriteBytes(bufAddr, buf[:n]); err != nil { @@ -2271,7 +2273,7 @@ const ( func (h *Handler) sysSocket(c arch.CPU, domain, sockType, protocol uint64) { stype := int(sockType & 0xff) // strip SOCK_CLOEXEC/SOCK_NONBLOCK if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] socket(domain=%d, type=%d)\n", domain, stype) + ravelog.Logf("[govm] socket(domain=%d, type=%d)\n", domain, stype) } if domain == vfs.AF_UNIX { @@ -2313,7 +2315,7 @@ func (h *Handler) sysConnect(c arch.CPU, fd, addrPtr, addrLen uint64) { } err := syscall.Connect(hostFD, sa) if err != nil { - fmt.Fprintf(os.Stderr, "[govm] connect ERROR: %v\n", err) + ravelog.Logf("[govm] connect ERROR: %v\n", err) c.SetSyscallReturn(negErrno(errnoFromErr(err))) return } @@ -2469,14 +2471,14 @@ func (h *Handler) sysRecvfrom(c arch.CPU, fd, bufAddr, bufLen, flags, srcAddr, s n, err := syscall.Read(hostFD, buf) if err != nil { if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] recvfrom(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, bufLen, err) + ravelog.Logf("[govm] recvfrom(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, bufLen, err) } c.SetSyscallReturn(negErrno(errnoFromErr(err))) return } if n > 0 { if h.Trace { - fmt.Fprintf(os.Stderr, "[govm] recvfrom(fd=%d→hfd=%d, n=%d): %02x\n", fd, hostFD, n, buf[:min(64, n)]) + ravelog.Logf("[govm] recvfrom(fd=%d→hfd=%d, n=%d): %02x\n", fd, hostFD, n, buf[:min(64, n)]) } h.Mem.WriteBytes(bufAddr, buf[:n]) } @@ -2816,11 +2818,11 @@ func (h *Handler) sysSendmsg(c arch.CPU, fd, msgPtr, flags uint64) { err := syscall.Sendmsg(hostFD, payload, oob, nil, int(flags)) if err != nil { - fmt.Fprintf(os.Stderr, "[govm] sendmsg(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, len(payload), err) + ravelog.Logf("[govm] sendmsg(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, len(payload), err) c.SetSyscallReturn(negErrno(errnoFromErr(err))) return } - fmt.Fprintf(os.Stderr, "[govm] sendmsg(fd=%d→hfd=%d, len=%d): %02x\n", fd, hostFD, len(payload), payload[:min(8, len(payload))]) + ravelog.Logf("[govm] sendmsg(fd=%d→hfd=%d, len=%d): %02x\n", fd, hostFD, len(payload), payload[:min(8, len(payload))]) c.SetSyscallReturn(uint64(len(payload))) } @@ -2867,7 +2869,7 @@ func (h *Handler) sysRecvmsg(c arch.CPU, fd, msgPtr, flags uint64) { n, oobn, _, _, err := syscall.Recvmsg(hostFD, buf, oob, int(flags)) if err != nil { if h.debugX11 { - fmt.Fprintf(os.Stderr, "[govm] recvmsg(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, totalCap, err) + ravelog.Logf("[govm] recvmsg(fd=%d→hfd=%d, len=%d) err=%v\n", fd, hostFD, totalCap, err) } c.SetSyscallReturn(negErrno(errnoFromErr(err))) return @@ -2877,9 +2879,9 @@ func (h *Handler) sysRecvmsg(c arch.CPU, fd, msgPtr, flags uint64) { if len(disp) > 64 { disp = disp[:64] } - fmt.Fprintf(os.Stderr, "[govm] recvmsg(fd=%d→hfd=%d, n=%d, iovs=%d): %x\n", fd, hostFD, n, iovLen, disp) + ravelog.Logf("[govm] recvmsg(fd=%d→hfd=%d, n=%d, iovs=%d): %x\n", fd, hostFD, n, iovLen, disp) for i, iov := range iovEntries { - fmt.Fprintf(os.Stderr, "[govm] iov[%d]: base=0x%x len=%d\n", i, iov.base, iov.blen) + ravelog.Logf("[govm] iov[%d]: base=0x%x len=%d\n", i, iov.base, iov.blen) } } @@ -2894,7 +2896,7 @@ func (h *Handler) sysRecvmsg(c arch.CPU, fd, msgPtr, flags uint64) { chunk = int(iov.blen) } if h.debugX11 { - fmt.Fprintf(os.Stderr, "[govm] scatter %d bytes at 0x%x: %x\n", chunk, iov.base, buf[off:off+chunk]) + ravelog.Logf("[govm] scatter %d bytes at 0x%x: %x\n", chunk, iov.base, buf[off:off+chunk]) } h.Mem.WriteBytes(iov.base, buf[off:off+chunk]) off += chunk @@ -3268,7 +3270,7 @@ func (h *Handler) sysMmap(c arch.CPU) { origAddr := addr logMmap := func(result uint64) { if h.Trace { - fmt.Fprintf(os.Stderr, "[mmap] req=0x%x len=0x%x prot=%d flags=0x%x fd=%d off=0x%x → 0x%x\n", + ravelog.Logf("[mmap] req=0x%x len=0x%x prot=%d flags=0x%x fd=%d off=0x%x → 0x%x\n", origAddr, length, prot, flags, int64(fd), offset, result) } } diff --git a/pkg/thread/thread.go b/pkg/thread/thread.go index 34020e5..7adaf5e 100644 --- a/pkg/thread/thread.go +++ b/pkg/thread/thread.go @@ -1,12 +1,12 @@ package thread import ( -"fmt" -"os" "sync" -"github.com/mirkobrombin/go-vm/pkg/arch" -"github.com/mirkobrombin/go-vm/pkg/mmu" +"github.com/mirkobrombin/Raven/pkg/arch" +"github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // Thread represents a guest hardware thread (hart). @@ -93,10 +93,10 @@ m.mu.Unlock() m.wg.Add(1) go func() { defer m.wg.Done() - fmt.Fprintf(os.Stderr, "[govm] thread %d start: PC=0x%x SP=0x%x TLS=0x%x\n", + ravelog.Logf("[govm] thread %d start: PC=0x%x SP=0x%x TLS=0x%x\n", tid, c.GetPC(), c.GetSP(), c.GetTLS()) if err := c.Run(); err != nil { - fmt.Fprintf(os.Stderr, "[govm] thread %d crashed: %v (PC=0x%x SP=0x%x TLS=0x%x)\n", + ravelog.Logf("[govm] thread %d crashed: %v (PC=0x%x SP=0x%x TLS=0x%x)\n", tid, err, c.GetPC(), c.GetSP(), c.GetTLS()) } // When the thread exits, unregister its stack so future munmaps succeed. diff --git a/pkg/vulkan/passthrough.go b/pkg/vulkan/passthrough.go index 330238d..6274bf5 100644 --- a/pkg/vulkan/passthrough.go +++ b/pkg/vulkan/passthrough.go @@ -3,8 +3,8 @@ package vulkan import ( "fmt" - "github.com/mirkobrombin/go-vm/pkg/mmu" - "github.com/mirkobrombin/go-vm/pkg/ntapi" + "github.com/mirkobrombin/Raven/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/ntapi" ) // Passthrough manages Vulkan API call forwarding from guest to host. diff --git a/pkg/vulkan/synthpe.go b/pkg/vulkan/synthpe.go index affbf29..91ecabf 100644 --- a/pkg/vulkan/synthpe.go +++ b/pkg/vulkan/synthpe.go @@ -1,8 +1,8 @@ package vulkan import ( - "github.com/mirkobrombin/go-vm/pkg/ntapi" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/ntapi" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // vulkanExports lists the Vulkan API entry points for the synthetic vulkan-1.dll. diff --git a/pkg/win32k/gdi.go b/pkg/win32k/gdi.go index 21856d0..95ede19 100644 --- a/pkg/win32k/gdi.go +++ b/pkg/win32k/gdi.go @@ -1,8 +1,8 @@ package win32k import ( - "github.com/mirkobrombin/go-vm/pkg/mmu" - "github.com/mirkobrombin/go-vm/pkg/ntapi" + "github.com/mirkobrombin/Raven/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/ntapi" ) // GDISubsystem handles GDI rendering operations using MMU-backed pixel buffers. diff --git a/pkg/win32k/msgqueue.go b/pkg/win32k/msgqueue.go index cbe640a..16435c9 100644 --- a/pkg/win32k/msgqueue.go +++ b/pkg/win32k/msgqueue.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/mirkobrombin/go-vm/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/mmu" ) // MessageQueue translates host input events into Windows messages. diff --git a/pkg/win32k/trap.go b/pkg/win32k/trap.go index 4557f77..db3168f 100644 --- a/pkg/win32k/trap.go +++ b/pkg/win32k/trap.go @@ -1,7 +1,7 @@ package win32k import ( - "github.com/mirkobrombin/go-vm/pkg/ntapi" + "github.com/mirkobrombin/Raven/pkg/ntapi" ) // TrapDispatcher routes Win32k (Shadow SSDT) syscalls to the appropriate handler. diff --git a/pkg/win32k/window.go b/pkg/win32k/window.go index 203d63a..ec4e9f2 100644 --- a/pkg/win32k/window.go +++ b/pkg/win32k/window.go @@ -6,8 +6,8 @@ import ( "sync/atomic" "time" - "github.com/mirkobrombin/go-vm/pkg/mmu" - "github.com/mirkobrombin/go-vm/pkg/ntapi" + "github.com/mirkobrombin/Raven/pkg/mmu" + "github.com/mirkobrombin/Raven/pkg/ntapi" ) // Window represents a guest window mapped to a Wayland surface. diff --git a/pkg/x64/cpu.go b/pkg/x64/cpu.go index b314b3f..7136cc2 100644 --- a/pkg/x64/cpu.go +++ b/pkg/x64/cpu.go @@ -6,8 +6,10 @@ import ( "math/bits" "os" -"github.com/mirkobrombin/go-vm/pkg/arch" -"github.com/mirkobrombin/go-vm/pkg/mmu" +"github.com/mirkobrombin/Raven/pkg/arch" +"github.com/mirkobrombin/Raven/pkg/mmu" + + ravelog "github.com/mirkobrombin/Raven/pkg/log" ) // Register indices (matching AMD64 encoding). @@ -139,7 +141,7 @@ for c.running { if _, verr := c.mem.Read8(retAddr); verr == nil { recoverCount++ if recoverCount <= 20 { - fmt.Fprintf(os.Stderr, "[cpu-recover] #%d call to unmapped 0x%x, returning to 0x%x RBX=0x%x RSP=0x%x\n", recoverCount, c.RIP, retAddr, c.Regs[RBX], c.GetSP()) + ravelog.Logf("[cpu-recover] #%d call to unmapped 0x%x, returning to 0x%x RBX=0x%x RSP=0x%x\n", recoverCount, c.RIP, retAddr, c.Regs[RBX], c.GetSP()) if recoverCount == 1 { // Print full register state and stack fmt.Fprintf(os.Stderr, " RAX=0x%x RCX=0x%x RDX=0x%x RSI=0x%x RDI=0x%x\n", @@ -175,7 +177,7 @@ for c.running { for bi := 0; bi < 6; bi++ { callBytes[bi], _ = c.mem.Read8(retAddr - 6 + uint64(bi)) } - fmt.Fprintf(os.Stderr, "[cpu-recover] #%d fetch fault at 0x%x, returning to 0x%x RBX=0x%x callsite=[%02x %02x %02x %02x %02x %02x]\n", + ravelog.Logf("[cpu-recover] #%d fetch fault at 0x%x, returning to 0x%x RBX=0x%x callsite=[%02x %02x %02x %02x %02x %02x]\n", recoverCount, c.RIP, retAddr, c.Regs[RBX], callBytes[0], callBytes[1], callBytes[2], callBytes[3], callBytes[4], callBytes[5]) } @@ -188,16 +190,16 @@ for c.running { } } } - fmt.Fprintf(os.Stderr, "[crash-regs] RIP=0x%x TLS=0x%x err=%v\n", c.RIP, c.GetTLS(), err) - fmt.Fprintf(os.Stderr, "[crash-regs] RAX=0x%x RBX=0x%x RCX=0x%x RDX=0x%x RSI=0x%x RDI=0x%x\n", + ravelog.Logf("[crash-regs] RIP=0x%x TLS=0x%x err=%v\n", c.RIP, c.GetTLS(), err) + ravelog.Logf("[crash-regs] RAX=0x%x RBX=0x%x RCX=0x%x RDX=0x%x RSI=0x%x RDI=0x%x\n", c.Regs[RAX], c.Regs[RBX], c.Regs[RCX], c.Regs[RDX], c.Regs[RSI], c.Regs[RDI]) - fmt.Fprintf(os.Stderr, "[crash-regs] R8=0x%x R9=0x%x R10=0x%x R11=0x%x R12=0x%x R13=0x%x R14=0x%x R15=0x%x RBP=0x%x RSP=0x%x\n", + ravelog.Logf("[crash-regs] R8=0x%x R9=0x%x R10=0x%x R11=0x%x R12=0x%x R13=0x%x R14=0x%x R15=0x%x RBP=0x%x RSP=0x%x\n", c.Regs[R8], c.Regs[R9], c.Regs[R10], c.Regs[R11], c.Regs[R12], c.Regs[R13], c.Regs[R14], c.Regs[R15], c.Regs[RBP], c.GetSP()) // Print stack sp := c.GetSP() for i := 0; i < 8; i++ { v, _ := c.mem.Read64(sp + uint64(i*8)) - fmt.Fprintf(os.Stderr, "[crash-regs] [RSP+0x%02x]=0x%x\n", i*8, v) + ravelog.Logf("[crash-regs] [RSP+0x%02x]=0x%x\n", i*8, v) } return fmt.Errorf("at RIP=0x%x: %w", c.RIP, err) } @@ -879,7 +881,7 @@ return err } target := uint64(int64(c.RIP) + int64(int32(rel))) if target == 0x120011 { - fmt.Fprintf(os.Stderr, "[DBG] call rel32 to 0x120011 from RIP=0x%x\n", c.RIP) + ravelog.Logf("[DBG] call rel32 to 0x120011 from RIP=0x%x\n", c.RIP) } if err := c.push64(c.RIP); err != nil { return err @@ -2281,6 +2283,14 @@ return err src[0], _ = c.mem.Read64(addr) src[1], _ = c.mem.Read64(addr + 8) } +// CVTPD2PS (66 0F 5A): two doubles → two singles in low 64 bits +if op2 == 0x5A { +f0 := float32(math.Float64frombits(src[0])) +f1 := float32(math.Float64frombits(src[1])) +c.xmm[m.reg][0] = uint64(math.Float32bits(f0)) | uint64(math.Float32bits(f1))<<32 +c.xmm[m.reg][1] = 0 +return nil +} d := c.xmm[m.reg] for i := 0; i < 2; i++ { sv := math.Float64frombits(src[i]) @@ -2311,6 +2321,14 @@ return err src[0], _ = c.mem.Read64(addr) src[1], _ = c.mem.Read64(addr + 8) } +// CVTPS2PD (NP 0F 5A): two singles from low 64 bits → two doubles +if op2 == 0x5A { +f0 := math.Float32frombits(uint32(src[0])) +f1 := math.Float32frombits(uint32(src[0] >> 32)) +c.xmm[m.reg][0] = math.Float64bits(float64(f0)) +c.xmm[m.reg][1] = math.Float64bits(float64(f1)) +return nil +} d := c.xmm[m.reg] for q := 0; q < 2; q++ { var rq uint64 @@ -4277,7 +4295,40 @@ c.xmm[m.reg] = r } return nil -// --- SSE4.1/4.2 3-byte escape: 0F 38 xx --- +// --- CVTPD2DQ (F2 0F E6) / CVTTPD2DQ (66 0F E6) --- +// Convert two packed double-precision FP values to two packed int32 values. +// Result placed in low 64 bits of dst XMM; high 64 bits zeroed. +case 0xE6: +m, err := c.decodeModRM(pfx) +if err != nil { +return err +} +var src [2]uint64 +if m.mod == 3 { +src = c.xmm[m.rm] +} else { +addr, _, err := c.resolveRMAddr(m, pfx) +if err != nil { +return err +} +src[0], _ = c.mem.Read64(addr) +src[1], _ = c.mem.Read64(addr + 8) +} +d0 := math.Float64frombits(src[0]) +d1 := math.Float64frombits(src[1]) +var i0, i1 int32 +if pfx.repne { // F2 0F E6: CVTPD2DQ - round to nearest even +i0 = int32(math.RoundToEven(d0)) +i1 = int32(math.RoundToEven(d1)) +} else { // 66 0F E6: CVTTPD2DQ - truncate toward zero +i0 = int32(d0) +i1 = int32(d1) +} +var dst [2]uint64 +dst[0] = uint64(uint32(i0)) | uint64(uint32(i1))<<32 +dst[1] = 0 +c.xmm[m.reg] = dst +return nil case 0x38: return c.dispatch0F38(pfx, startRIP) @@ -4810,11 +4861,11 @@ return err if val == 0x120011 { addr2, _, _ := c.resolveRMAddr(m, pfx) raw, _ := c.mem.Read64(addr2) - fmt.Fprintf(os.Stderr, "[DBG] call to 0x120011 from RIP=0x%x addr=0x%x raw_read=0x%x\n", + ravelog.Logf("[DBG] call to 0x120011 from RIP=0x%x addr=0x%x raw_read=0x%x\n", c.RIP, addr2, raw) } if val == 0 { - fmt.Fprintf(os.Stderr, "[DBG] call to 0x0 from retAddr=0x%x RSP=0x%x RBX=0x%x RCX=0x%x\n", + ravelog.Logf("[DBG] call to 0x0 from retAddr=0x%x RSP=0x%x RBX=0x%x RCX=0x%x\n", c.RIP, c.Regs[RSP], c.Regs[RBX], c.Regs[RCX]) } if err := c.push64(c.RIP); err != nil { @@ -4830,10 +4881,10 @@ return err } if val == 0x120011 { addr2, _, _ := c.resolveRMAddr(m, pfx) - fmt.Fprintf(os.Stderr, "[DBG] jmp to 0x120011 from RIP=0x%x addr=0x%x\n", c.RIP, addr2) + ravelog.Logf("[DBG] jmp to 0x120011 from RIP=0x%x addr=0x%x\n", c.RIP, addr2) } if val == 0 { - fmt.Fprintf(os.Stderr, "[DBG] jmp to 0x0 from retAddr=0x%x RSP=0x%x RBX=0x%x\n", + ravelog.Logf("[DBG] jmp to 0x0 from retAddr=0x%x RSP=0x%x RBX=0x%x\n", c.RIP, c.Regs[RSP], c.Regs[RBX]) } c.RIP = val