Задать вопрос
@dimone73

Почему скрипт запуска виртуальной машины перестал работать?

Как-то давно писал скрипт на PowerShell дабы отмотать время Bios на виртуальной машине.
Скрипт набирал из кусков разных. Он вычисляет разницу во времени и стартует виртуалку "в прошлом", только что-то перестал работать, стартует все равно в настоящем. Сверху/снизу обрамление jscript больше для красоты.

@set @x=0; /*
@echo off
ver |>NUL find /v "5." && if "%~1"=="" cscript.exe //nologo //e:jscript "%~f0"& exit /b


;@echo off
;@Findstr -bv ;@ "%~f0" | powershell -noprofile -command - & goto:eof
# All except ;@ is a powershell script!
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
# Config parameters #
# Path of the VBoxManage executable.
$VB_MANAGE ='C:\Program Files\Oracle\VirtualBox\VBoxManage'

# Name or uuid of the virtualbox to start.
$vmname ='server'

# Default BIOS time dd/mm/yyyy
$BIOStime = '08/01/2023'

function CustomInputBox([string] $title, [string] $message, [string] $defaultText) {
  [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
  [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 

 $userForm = New-Object System.Windows.Forms.Form
  $userForm.Text = "$title"
  $userForm.Size = New-Object System.Drawing.Size(400,150)
  $userForm.StartPosition = "CenterScreen"
      $userForm.AutoSize = $False
      $userForm.MinimizeBox = $False
      $userForm.MaximizeBox = $False
      $userForm.SizeGripStyle= "Hide"
      $userForm.WindowState = "Normal"
      $userForm.FormBorderStyle="Fixed3D"  
      $userForm.ShowInTaskBar = $False
     
  $OKButton = New-Object System.Windows.Forms.Button
  $OKButton.Location = New-Object System.Drawing.Size(115,80)
  $OKButton.Size = New-Object System.Drawing.Size(75,23)
  $OKButton.Text = "OK"
  $OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
  $userForm.AcceptButton = $OKButton
  $userForm.Controls.Add($OKButton)
  $OKButton.tabindex = 1

 $CancelButton = New-Object System.Windows.Forms.Button
  $CancelButton.Location = New-Object System.Drawing.Size(195,80)
  $CancelButton.Size = New-Object System.Drawing.Size(75,23)
  $CancelButton.Text = "Cancel"
  $CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
  $userForm.CancelButton = $CancelButton
  $userForm.Controls.Add($CancelButton)
  $CancelButton.tabindex = 2

 $userLabel = New-Object System.Windows.Forms.Label
  $userLabel.Location = New-Object System.Drawing.Size(20,20)
  $userLabel.Size = New-Object System.Drawing.Size(400,20)
  $userLabel.Text = "$message"
  $userForm.Controls.Add($userLabel) 

 $objTextBox = New-Object System.Windows.Forms.TextBox
  $objTextBox.Location = New-Object System.Drawing.Size(150,45)
  $objTextBox.Size = New-Object System.Drawing.Size(100,40)
  $objTextBox.Text="$defaultText"
  $userForm.Controls.Add($objTextBox) 
  $objTextBox.tabindex = 0

 $userForm.Topmost = $True
  $userForm.Opacity = 1
  $userForm.ShowIcon = $False

 $userForm.Add_Shown({$userForm.Activate(); $objTextBox.Focus()})
 $dialogResult = $userForm.ShowDialog()
 if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK) { $objTextBox.Text } 
 $userForm.dispose() }

do {
$title = "Input BIOS date for VM $vmname"
$msg   = "Enter new BIOS date in the format of dd/mm/yyyy:"
$reply = CustomInputBox $title $msg $BIOStime
if($reply.Length -eq 0) { exit }
$date = ($reply + " " + [datetime]::Now.ToShortTimeString()) -as [datetime]
if (!$date) { $shell = new-object -comobject "WScript.Shell"
$shell.popup("Date is not in a valid format",0,"Syntax error",0+48+4096) }
} while ($date -isnot [datetime])

start-process $VB_MANAGE 'setextradata', $vmname, 'VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled 1' -NoNewWindow 

$timeoffset_milliseconds='{0:0}' -f ([datetime]$date - [datetime]::Now).TotalMilliseconds

$shell = new-object -comobject "WScript.Shell"
$date2 = Get-Date($date) -UFormat "%d/%m/%Y"
$result = $shell.popup("Do you want to set the BIOS time of $vmname to dd/mm/yyyy where is $date2 wich equals an offset of $timeoffset_milliseconds msec from current time and start the virtual machine?",0,"Verify action for VM $vmname",4+32+4096)
# $result will be 6 for yes, 7 for no.
if ($result -eq 7) { exit }

start-process $VB_MANAGE 'modifyvm', $vmname, '--biossystemtimeoffset', $timeoffset_milliseconds -NoNewWindow 

echo "Starting virtual machine $vmname"
start-process $VB_MANAGE startvm, $vmname

pause
exit /B
 
:: Эту строку не трогать. Ниже ничего не писать!!!
*/new ActiveXObject('Shell.Application').ShellExecute (WScript.ScriptFullName,'Admin','','runas',1);
  • Вопрос задан
  • 81 просмотр
Подписаться 1 Средний 3 комментария
Решения вопроса 1
@rPman
какое красивое садамаза, скрипт запускает сам себя на javascript являясь bat, да еще и внутри powershell, как это читать и отлаживать, известно только его разработчику :)

И самое главное, вам точно нужно GUI для смены даты? И уж точно для этого не требуется ни powershell ни повышение прав доступа (хотя может у вас там так настроено)

У virtualbox есть соответствующие команды VBoxManage, которые можно было просто прописать в текстовом файлике, назвать blablabla.bat и запускать
upd. нужно считать разницу в миллисекундах между текущим временим и целевым, на bat это сложно, поэтому лучше сразу писать на чем то типа javascript, не изобретая паровоза (и от туда же запускать vboxmanage)

p.s. кайфую с современного ИИ, который это сумел разобрать и по полочкам разложить этот кошмар
anthropic:claude sonnet 3.5
...
проанализируй этот скрипт для windows, почему тут сместь cmd и javascript, и начни описание, что именно делают первые строчки
Давайте разберем этот интересный гибридный скрипт:

Первые строки скрипта представляют собой "полиглот" - код, который может быть интерпретирован несколькими языками программирования:

```
set @x=0; /*
@echo off
ver |>NUL find /v "5." && if "%~1"=="" cscript.exe //nologo //e:jscript "%~f0"& exit /b
```

Это хитрая конструкция, которая:
1. Начинается как cmd-скрипт (`@set @x=0;`)
2. Открывает многострочный комментарий в JavaScript (`/*`)
3. Выключает вывод команд в cmd (`@echo off`)
4. Проверяет версию Windows и если скрипт запущен без параметров, перезапускает сам себя через cscript.exe в режиме JavaScript

Последняя строка скрипта:
```javascript
*/new ActiveXObject('Shell.Application').ShellExecute (WScript.ScriptFullName,'Admin','','runas',1);
```
закрывает JavaScript-комментарий и содержит код для повышения привилегий - перезапуска скрипта с правами администратора.

Это техника самоповышения привилегий (UAC bypass), которая позволяет скрипту перезапустить себя с правами администратора, если он был запущен от обычного пользователя.

Основная часть скрипта написана на PowerShell и использует Windows Forms для создания графического интерфейса. Согласно документации [learn.microsoft.com](https://learn.microsoft.com/en-us/dotnet/api/syste...), скрипт создает форму для ввода даты, которая будет использоваться для настройки BIOS времени виртуальной машины VirtualBox.

Такая сложная структура используется для:
1. Обеспечения запуска с правами администратора
2. Совместимости с разными версиями Windows
3. Комбинирования возможностей разных языков скриптования Windows

продолжай дальше анализ, почему там еще и powershell?
PowerShell используется в этом скрипте для основной функциональности, и его часть начинается после строк:

```
;@echo off
;@Findstr -bv ;@ "%~f0" | powershell -noprofile -command - & goto:eof
```

Эта конструкция особенно интересна, потому что:
1. Использует Findstr для фильтрации содержимого файла, пропуская строки, начинающиеся с `;@`
2. Передает отфильтрованное содержимое в PowerShell через пайп (`|`)
3. Запускает PowerShell без профиля (`-noprofile`) для ускорения загрузки

PowerShell был выбран для основной части скрипта по нескольким причинам:

1. **Объектная модель**: Как отмечается в [thenewandshiny.com](https://thenewandshiny.com/powershell-everywhere/), PowerShell работает с объектами, а не с текстом, что делает его более надежным для работы с системными компонентами.

2. **Удобство работы с GUI**: PowerShell позволяет легко создавать Windows Forms для пользовательского интерфейса, что используется в функции `CustomInputBox`

3. **Управление VirtualBox**: PowerShell предоставляет удобные средства для запуска процессов и управления VirtualBox через `VBoxManage`

4. **Работа с датами**: PowerShell имеет мощные средства для работы с датами и временем, что критично для этого скрипта

Важно отметить, что в скрипте используется разделение на Windows PowerShell и PowerShell Core. Как упоминается в [get-powershellblog.blogspot.com](https://get-powershellblog.blogspot.com/2017/10/wh...), это важно учитывать при работе с разными версиями PowerShell.

Скрипт использует стандартный формат командлетов PowerShell `Verb-Noun`, хотя, как отмечается в [scoop-docs.vercel.app](https://scoop-docs.vercel.app/docs/misc/Why-PowerS...), такой формат может казаться излишне многословным, но он обеспечивает четкую структуру и понятность команд.

Основная функциональность скрипта:
1. Создает GUI для ввода даты
2. Проверяет корректность введенной даты
3. Настраивает время BIOS для виртуальной машины VirtualBox
4. Запускает виртуальную машину с модифицированными настройками времени

какими строчками настраивается время
В скрипте время настраивается несколькими ключевыми командами:

1. Сначала отключается синхронизация времени с хостом:
```powershell
start-process $VB_MANAGE 'setextradata', $vmname, 'VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled 1' -NoNewWindow
```

2. Затем вычисляется смещение времени в миллисекундах:
```powershell
$timeoffset_milliseconds='{0:0}' -f ([datetime]$date - [datetime]::Now).TotalMilliseconds
```

3. И наконец, применяется смещение времени BIOS:
```powershell
start-process $VB_MANAGE 'modifyvm', $vmname, '--biossystemtimeoffset', $timeoffset_milliseconds -NoNewWindow
```

Здесь важно отметить несколько моментов:
- Используется VBoxManage - утилита командной строки VirtualBox (путь хранится в переменной `$VB_MANAGE`)
- Смещение вычисляется как разница между желаемым временем (`$date`) и текущим системным временем
- Параметр `--biossystemtimeoffset` устанавливает смещение времени BIOS в миллисекундах

После установки времени виртуальная машина запускается командой:
```powershell
start-process $VB_MANAGE startvm, $vmname
```

Похожий подход используется в проекте [github.com/bitbank2/rtc_setter](https://github.com..., где также реализована настройка времени, но для реального RTC (часов реального времени), а не виртуальной машины.

Важно понимать, что этот скрипт не просто устанавливает время, а именно смещение относительно системного времени хоста, что позволяет виртуальной машине работать с другой датой, но сохранять корректный ход времени.

Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы