Skip to content

Instantly share code, notes, and snippets.

@xpn

xpn/vm_poc.swift Secret

Created Dec 27, 2020
Embed
What would you like to do?
import Foundation
import Virtualization
import Darwin.C
class VirtualMachine :NSObject, VZVirtualMachineDelegate {
private var virtualMachine: VZVirtualMachine! = nil
private var memSize :UInt64
private var cpuCount :Int
private let kernelURL :URL
private var ramdiskURL :URL? = nil
private var bootableImageURL :URL? = nil
init(cpuCount :Int, memSize : UInt64, kernelPath :String, ramdiskPath:String?, blockdiskPath :String?) {
self.cpuCount = cpuCount
self.memSize = memSize
self.kernelURL = URL.init(fileURLWithPath: kernelPath)
if ramdiskPath != nil {
self.ramdiskURL = URL.init(fileURLWithPath: ramdiskPath!)
}
if blockdiskPath != nil {
self.bootableImageURL = URL.init(fileURLWithPath: blockdiskPath!)
}
}
func resizeMemory(newMemSize : UInt64) {
if let memDevice = self.virtualMachine!.memoryBalloonDevices.first as? VZVirtioTraditionalMemoryBalloonDevice {
memDevice.targetVirtualMachineMemorySize = newMemSize
}
}
func stopvm() -> Bool {
if self.virtualMachine!.canRequestStop {
do {
try self.virtualMachine!.requestStop()
return true
} catch {
return false
}
}
return false
}
func startvm() -> Bool {
// Set up the Linux bootloader (default console to hypervisor)
let bootloader = VZLinuxBootLoader(kernelURL: kernelURL)
bootloader.initialRamdiskURL = ramdiskURL
bootloader.commandLine = "console=hvc0"
// We'll use a serial VirtIO device for comms
// Just attach our STDIN/STDOUT for this example
let serialPort = VZVirtioConsoleDeviceSerialPortConfiguration()
serialPort.attachment = VZFileHandleSerialPortAttachment(
fileHandleForReading: FileHandle.standardOutput,
fileHandleForWriting: FileHandle.standardInput
)
// Allows us to dynamically control the size of memory allocated to the guest
let balloonConfig = VZVirtioTraditionalMemoryBalloonDeviceConfiguration()
// Allocate persistant storage
var blockAttachment: VZDiskImageStorageDeviceAttachment? = nil
var blockDevice :VZVirtioBlockDeviceConfiguration? = nil
if self.bootableImageURL != nil {
do {
blockAttachment = try VZDiskImageStorageDeviceAttachment(
url: bootableImageURL!,
readOnly: true
)
blockDevice = VZVirtioBlockDeviceConfiguration(attachment: blockAttachment!)
} catch {
NSLog("[!] Failed to load bootableImage: \(error)")
return false
}
}
// Only option is to NAT at the minute because... Apple :(
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_vm_networking?language=objc
let networkDevice = VZVirtioNetworkDeviceConfiguration()
networkDevice.attachment = VZNATNetworkDeviceAttachment()
networkDevice.macAddress = VZMACAddress.randomLocallyAdministered()
// Configuration of devices for this VM
let config = VZVirtualMachineConfiguration()
config.bootLoader = bootloader
config.memoryBalloonDevices = [balloonConfig]
config.serialPorts = [serialPort]
config.networkDevices = [networkDevice]
if blockDevice != nil {
config.storageDevices = [blockDevice!]
}
// Set resources allocated
config.cpuCount = self.cpuCount
config.memorySize = self.memSize
do {
// Make sure our config checks out
try config.validate()
// Create the VM
self.virtualMachine = VZVirtualMachine(configuration: config)
self.virtualMachine.delegate = self
// Start the VM
self.virtualMachine.start { result in
switch result {
case .success:
break
case .failure(_):
NSLog("[!] Failure whilst starting VM: \(result)")
break
}
}
return true
} catch {
NSLog("[!] Unexpected error: \(error).")
return false
}
}
// Delegate function - Called when VM stops
func guestDidStop(_ virtualMachine: VZVirtualMachine) {
print("[*] VM Stopped")
}
func virtualMachine(_ virtualMachine: VZVirtualMachine,
didStopWithError error: Error) {
print("[!] Error occurred and VM Stopped")
}
}
print("@_xpn_ - Apple Virtualization framework POC")
var v = VirtualMachine(cpuCount: 2, memSize: 2 * 1024 * 1024 * 1024, kernelPath: "/tmp/kernel", ramdiskPath: "/tmp/ramdisk", blockdiskPath: nil)
if v.startvm() == false {
print("[x] Error starting VM")
exit(1)
}
var runloop = RunLoop.current
var d = Date.init(timeIntervalSinceNow: 1.0)
while(runloop.run(mode: RunLoop.Mode.default, before: d)) {
// pass
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment