chore: rename the runtime to Raven and update rendering

This commit is contained in:
Mirko Brombin committed 2025-09-10 15:34:41 +01:00
1 parent 3f7390aa97
commit 4e41fc2b3e
30 files changed
+794 -217

No files matched your search

+356 -84
View File
@@ -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] <binary> [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] <binary> [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{
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

+64
View File
@@ -0,0 +1,64 @@
<svg width="1135" height="1108" viewBox="0 0 1135 1108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_4_378)">
<path d="M830.051 390.336C829.251 340.736 781.5 294.836 766 290.336L771.051 276.836L855.551 290.836L940.551 302.336L990.551 510.836C985.717 510.836 971.851 526.236 955.051 587.836C938.251 649.436 847.051 688.169 803.551 699.836L594.551 713.836H325.551C325.551 706.336 464.051 512.336 613.051 510.836C732.251 509.636 779.051 488.669 787.551 478.336C802.051 469.669 830.851 439.936 830.051 390.336Z" fill="url(#paint0_linear_4_378)"/>
<path d="M276.551 461.836C279.751 328.636 362.051 277.836 425.051 266.836L334.051 221.836L182.051 274.836C152.217 321.502 88.5505 417.036 72.5505 425.836C56.5505 434.636 53.5505 784.169 54.0505 957.836C59.3839 991.669 91.0505 1057.24 175.051 1048.84C259.051 1040.44 277.717 968.669 276.551 933.836C275.217 832.002 273.351 595.036 276.551 461.836Z" fill="url(#paint1_linear_4_378)"/>
<mask id="mask0_4_378" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="333" y="276" width="748" height="795">
<path d="M838.051 390.336C837.251 340.736 789.551 296.336 774.051 291.836L779.051 276.836L863.551 290.836L948.551 302.336L998.551 510.836C993.717 510.836 979.851 526.236 963.051 587.836C946.251 649.436 855.051 688.169 811.551 699.836L602.551 713.836H333.551C333.551 706.336 472.051 512.336 621.051 510.836C740.251 509.636 787.051 488.669 795.551 478.336C810.051 469.669 838.851 439.936 838.051 390.336Z" fill="url(#paint2_linear_4_378)"/>
<path d="M505.467 513.875C353.067 527.875 347.833 592.669 354.5 661.336C439.386 518.969 557.645 748.875 584.467 779.875C611.768 811.429 678.467 911.375 769.467 1014.88C899.467 1135.88 1062.47 1025.38 1080.47 1006.88C996.967 979.875 932.467 878.875 863.967 791.375C785.967 704.375 695.967 496.375 505.467 513.875Z" fill="black" fill-opacity="0.14"/>
</mask>
<g mask="url(#mask0_4_378)">
<path d="M500.602 465.806C344.527 478.878 319.163 610.371 325.99 674.484C412.923 541.559 554.038 685.221 581.507 714.165C609.466 743.626 677.773 836.944 770.968 933.58C904.102 1046.55 1071.03 943.383 1089.47 926.11C1003.95 900.901 937.898 806.599 867.746 724.902C787.866 643.672 695.695 449.467 500.602 465.806Z" fill="black" fill-opacity="0.14"/>
</g>
<mask id="mask1_4_378" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="325" y="276" width="666" height="438">
<path d="M830.051 390.336C829.251 340.736 781.5 294.836 766 290.336L771.051 276.836L855.551 290.836L940.551 302.336L990.551 510.836C985.717 510.836 971.851 526.236 955.051 587.836C938.251 649.436 847.051 688.169 803.551 699.836L594.551 713.836H325.551C325.551 706.336 464.051 512.336 613.051 510.836C732.251 509.636 779.051 488.669 787.551 478.336C802.051 469.669 830.851 439.936 830.051 390.336Z" fill="url(#paint3_linear_4_378)"/>
</mask>
<g mask="url(#mask1_4_378)">
<path d="M895.5 666.336C1029.9 525.136 889 387.002 811.5 343.836C780.3 280.636 732.667 268.336 703 261.836L793 281.336L913.5 322.336L954 370.336L987.5 427.336L995 539.836L969.5 598.836C961.167 609.002 934.7 636.736 895.5 666.336Z" fill="black" fill-opacity="0.14"/>
<path d="M497.467 518.875C345.067 532.875 318.833 645.669 325.5 714.336C431.5 631.836 549.645 753.875 576.467 784.875C603.768 816.429 670.467 916.375 761.467 1019.88C891.467 1140.88 1054.47 1030.38 1072.47 1011.88C988.967 984.875 924.467 883.875 855.967 796.375C777.967 709.375 687.967 501.375 497.467 518.875Z" fill="black" fill-opacity="0.14"/>
</g>
<path d="M496.051 490.336C343.651 504.336 318.884 645.169 325.551 713.836C410.436 571.469 548.228 725.335 575.051 756.336C602.352 787.889 669.051 887.836 760.051 991.336C890.051 1112.34 1053.05 1001.84 1071.05 983.336C987.551 956.336 923.051 855.336 854.551 767.836C776.551 680.836 686.551 472.836 496.051 490.336Z" fill="url(#paint4_linear_4_378)"/>
<path d="M261.05 303.336C59.4054 368.956 55.7129 539.336 56.5505 689.336C48.0505 458.836 71.5505 349.336 100.55 278.336C129.55 207.336 163.55 162.336 212.55 124.836C261.55 87.3356 346.051 58.3356 504.051 51.3356C662.051 44.3356 762.55 66.3356 822.05 82.8356C881.551 99.3356 961 143.5 1012 230C1063 316.5 1041.05 437.836 1018.55 509.836C996.051 581.836 920.05 642.336 889.05 661.336C964.551 604.336 966.55 540.336 969.05 509.836C971.551 479.336 949.05 412.836 921.05 383.836C893.051 354.836 843.5 313.836 785.05 295.836C729.834 278.831 511.905 221.701 261.05 303.336Z" fill="url(#paint5_linear_4_378)"/>
</g>
<defs>
<filter id="filter0_d_4_378" x="0" y="0" width="1134.47" height="1107.57" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="27"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.14 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4_378"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4_378" result="shape"/>
</filter>
<linearGradient id="paint0_linear_4_378" x1="991" y1="384.836" x2="302" y2="697.336" gradientUnits="userSpaceOnUse">
<stop stop-color="#5D013C"/>
<stop offset="0.495192" stop-color="#75024B"/>
<stop offset="1" stop-color="#5D0244"/>
</linearGradient>
<linearGradient id="paint1_linear_4_378" x1="239.525" y1="221.836" x2="239.525" y2="1049.57" gradientUnits="userSpaceOnUse">
<stop stop-color="#920B3F"/>
<stop offset="0.495192" stop-color="#B00D45"/>
<stop offset="1" stop-color="#E20F52"/>
</linearGradient>
<linearGradient id="paint2_linear_4_378" x1="1080.97" y1="473.039" x2="215.923" y2="715.61" gradientUnits="userSpaceOnUse">
<stop stop-color="#5D013C"/>
<stop offset="0.495192" stop-color="#75024B"/>
<stop offset="1" stop-color="#5D0244"/>
</linearGradient>
<linearGradient id="paint3_linear_4_378" x1="991" y1="384.836" x2="302" y2="697.336" gradientUnits="userSpaceOnUse">
<stop stop-color="#5D013C"/>
<stop offset="0.495192" stop-color="#75024B"/>
<stop offset="1" stop-color="#5D0244"/>
</linearGradient>
<linearGradient id="paint4_linear_4_378" x1="401" y1="488.836" x2="1054" y2="1079.84" gradientUnits="userSpaceOnUse">
<stop stop-color="#DD1155"/>
<stop offset="0.519231" stop-color="#BA0D65"/>
<stop offset="1" stop-color="#8C066D"/>
</linearGradient>
<linearGradient id="paint5_linear_4_378" x1="55" y1="321.836" x2="1042" y2="321.836" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF292E"/>
<stop offset="0.645502" stop-color="#FE2B32"/>
<stop offset="1" stop-color="#D41752"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
module github.com/mirkobrombin/go-vm
module github.com/mirkobrombin/Raven
go 1.25.7
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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.
+5 -3
View File
@@ -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
+7 -5
View File
@@ -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
}
+110
View File
@@ -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,
)
}
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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.
+8 -6
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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).
+1 -1
View File
@@ -1,7 +1,7 @@
package ntapi
import (
"github.com/mirkobrombin/go-vm/pkg/mmu"
"github.com/mirkobrombin/Raven/pkg/mmu"
)
// ProcessManager handles NtAllocateVirtualMemory, NtProtectVirtualMemory,
+85 -15
View File
@@ -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
+2 -2
View File
@@ -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).
+7 -5
View File
@@ -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)
}
}
+55 -53
View File
@@ -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)
}
}
+6 -6
View File
@@ -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.
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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.
+65 -14
View File
@@ -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