[#] Kernel Debugging with Vagrant
[0x00] Overview
커널 디버깅을 진행하기 위한 선행 작업으로 가상머신 설정에 많은 시간을 소모합니다. 이러한 설정을 좀 더 가볍게 할 수 있도록 Vagrant
를 이용할 수 있습니다. 쉽게 생각하면 도커와 비슷한 개념으로 생각할 수 있습니다. 해당 글에서는 이러한 Vagrant
를 이용하여 커널 디버깅을 좀 더 쉽게 설정하고 자동화할 수 있는 방법에 대해 설명합니다.
이 글에서는 Vagrant
와 Hypervisor
가 설치되었다는 가정하에 진행합니다. 이곳을 클릭 하여 다운로드 받을 수 있습니다.
Vagrant
가 지원하는 하이퍼바이저는 아래와 같습니다.
- VirtualBox
- VMWare
- Hyper-V
제공되는 내용에서는 VirtualBox
를 이용합니다. 다만 공식 가이드에서는 안정성과 속도의 측면에서 VMWare
를 추천한다고 되어있습니다.
[0x01] Create Box Image
Vagrant
에는 최소 디스크 크기의 이미지인 box
라는 개념을 가지고 있습니다. 부팅하는데 최소한의 소프트웨어만 존재하는 상태를 의미합니다. 이러한 박스는 쉘 스크립트를 이용하여 가상 머신 템플릿을 구성할 수 있습니다. Vagrant Cloud 에는 다른 사용자가 공개적으로 사용 가능한 박스 이미지들이 업로드되어 있습니다. 해당 부분은 아래의 활용 부분에서 더 다루도록 하겠습니다.
먼저 게스트 OS를 설치 후 계정명과 패스워드는 "vagrant"
로 설정해야 합니다. 설치 후 Vagrant Documentation 에 따르면 다음과 같은 작업이 필요합니다.
- Install the guest tools for your hypervisor.
- Ensure the virtual network adapter is configured as a private network.
- Completely disable UAC.
- Disable complex passwords.
- Disable Shutdown Tracker.
그리고 Vagrant
가 게스트 OS 내에서 스크립트를 실행할 수 있도록 winrm
을 사용하여 원격 관리를 활성화해야 합니다. 관리자 권한으로 명령 프롬프트를 실행하고 다음과 같이 실행하십시오.
winrm quickconfig -q
winrm set winrm/config/winrs @{MaxMemoryPerShellMB="512"}
winrm set winrm/config @{MaxTimeoutms="1800000"}
winrm set winrm/config/service @{AllowUnencrypted="true"}
winrm set winrm/config/service/auth @{Basic="true"}
sc config WinRM start= auto
이제 위에서 말한 박스 이미지로 만들 수 있는 하나의 게스트 OS가 완성되었습니다. 아무런 작업을 하지말고 게스트 OS를 종료하십시오.
다음은 호스트 OS에서의 작업입니다.
가상 머신 파일이 있는 디렉토리에서 명령 프롬프트를 열고 아래와 같이 vagrant
명령을 실행합니다. 가상 머신을 박스 이미지로 만드는 과정입니다.
vagrant package --base <VM name> --output <Box name>
PS D:\06_Vagrant_Box\Windows10_1909_18363_418(x64)> vagrant package --base "Windows10_1909_18363_418(x64)" --output Windows10_1909.box ==> Windows10_1909_18363_418(x64): Exporting VM...
==> Windows10_1909_18363_418(x64): Compressing package to: D:/06_Vagrant_Box/Windows10_1909_18363_418(x64)/Windows10_1909.box
확인해보면 원래 VM 크기의 절반정도 크기로 만들어진 box 파일을 확인할 수 있습니다. 다음 Vagrant
의 박스에 추가해야 합니다.
vagrant box add <prefix/box> <box image file>
PS D:\06_Vagrant_Box\Windows10_1909_18363_418(x64)> vagrant box add shh0ya/win10k Windows10_1909.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'shh0ya/win10k' (v0) for provider:
box: Unpacking necessary files from: file://D:/06_Vagrant_Box/Windows10_1909_18363_418(x64)/Windows10_1909.box
box:
==> box: Successfully added box 'shh0ya/win10k' (v0) for 'virtualbox'!
PS D:\06_Vagrant_Box\Windows10_1909_18363_418(x64)> vagrant box list
shh0ya/win10k (virtualbox, 0)
PS D:\06_Vagrant_Box\Windows10_1909_18363_418(x64)>
위와 같이 박스가 생성된 것을 확인할 수 있습니다.
[0x02] Vagrant Up
이제 박스가 준비되었습니다. 작성자는 다음과 같은 디렉토리 구조에서 진행하였습니다.
PS D:\> tree D:\06_Vagrant_Box /F
D:\06_VAGRANT_BOX
├─Box_Win10_1909_18363_418(x64)
└─Windows10_1909_18363_418(x64)
│ Windows10_1909.box
│ Windows10_1909_18363_418(x64).vbox
│ Windows10_1909_18363_418(x64).vbox-prev
│ Windows10_1909_18363_418(x64).vdi
│
└─Logs
VBox.log
VBox.log.1
VBox.log.2
VBoxHardening.log
접두어로 Box
가 존재하는 디렉터리에는 실제 Vagrant
에서 박스를 실행할 때 필요한 설정 파일들과 배치파일들을 작성할 것입니다. 위에서 가상 머신을 박스 이미지로 추출하고 Vagrant
에 등록되었으므로 원하는 경로에 설정 파일을 등록하고 실행할 수 있습니다.
원하는 디렉토리에 Vagrantfile
파일을 생성하십시오. 이는 박스를 실행할 때 여러가지 작업을 자동화할 수 있습니다.
# File name : Vagrantfile
Vagrant.configure("2") do |config|
config.vm.guest = :windows
config.vm.communicator = "winrm"
config.winrm.password = "vagrant"
config.winrm.username = "vagrant"
config.vm.define "win10" do |win10|
win10.vm.box = "shh0ya/win10k"
win10.vm.provision "shell", path: "guest/kdbg.bat"
win10.vm.network :forwarded_port, guest: 49152, host: 49152
end
end
각 설정에 대해 설명하겠습니다.
- vm.guest = :windows ; 기반의 게스트 OS임을 Vagrant에 명시합니다.
- vm.communicator = “winrm” ; 게스트OS를 winrm을 통해 관리합니다.
- winrm.password = “vagrant” ; 초기 설정한 계정명과 패스워드입니다.
- winrm.username = “vagrant”
- vm.box = “shh0ya/win10k” ; Vagrant에 추가되어 있는 박스 이름입니다.
- vm.provision “shell”, path: “guest/kdbg.bat” ; 박스가 실행되고 실행 될 스크립트 파일입니다.
- vm.network :forward_port, guest: 49152, host: 49152 ; 호스트에게 커널 디버깅 포트를 노출합니다.
프로비저닝 옵션에 명시한 것과 같이 해당 디렉토리 내 guest
디렉토리 안에 배치파일을 생성할 것입니다.
해당 배치파일에는 디버그 모드 활성화를 하는 내용으로 작성됩니다.
# File name : kdbg.bat
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.56.1 port:49152 key:1.1.1.1
shutdown /r /t 0
이제 모든 준비가 되었습니다. 직접 만든 박스 이미지를 실행하고 커널 디버깅을 할 수 있습니다. 작성자의 디렉토리 구조입니다.
D:\06_VAGRANT_BOX
├─Box_Win10_1909_18363_418(x64)
│ │ Vagrantfile
│ │
│ └─guest
│ kdbg.bat
│
└─Windows10_1909_18363_418(x64)
│ Windows10_1909.box
│ Windows10_1909_18363_418(x64).vbox
│ Windows10_1909_18363_418(x64).vbox-prev
│ Windows10_1909_18363_418(x64).vdi
│
└─Logs
VBox.log
VBox.log.1
VBox.log.2
VBoxHardening.log
현재 설정은 네트워크 디버깅입니다. 다만 좀 더 깊은 분석을 원한다면 직렬포트를 이용하여 디버깅 하길 바랍니다. 자동으로 windbg를 실행하고 디버깅 연결까지 아래와 같은 스크립트로 가능합니다.
start vagrant up
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=49152,key=1.1.1.1
수 초내에 가능할 것 같았지만 조금은 더 걸리는 것 같습니다. 그래도 원클릭으로 디버깅이 가능합니다
[-] Using Serial port
직렬포트를 이용하기 위해서는 기존에 가상머신 템플릿에 직렬포트 설정을 해줘야 합니다. 그리고 박스 이미지를 만들면 같은 설정이 됩니다.
이에 따른 프로비저닝 스크립트와 자동실행 스크립트는 아래와 같습니다.
# File name : kdbg.bat
bcdedit /debug on
bcdedit /dbgsettings serial debugport:1 baudrate:115200
shutdown /r /t 0
# File name : VagrantStart.bat
start vagrant up
timeout 15
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k com:port=\\.\pipe\debugmode,baud=115200,pipe,reconnect,resets=0
간혹 디버거가 붙지 않는 현상이 있어 위와 같이 타임아웃을 정해주는 것으로 해결하였습니다. 위의 스크립트에서는 파이프 이름과 디버그 포트만 맞게 설정해주면 됩니다.(부팅 도중 Vagrant
에서 간섭하는 부분과 충돌이 있는 듯 합니다.)
[0x03] Conclusion
여기까지 Vagrant
설치 및 활용에 관한 내용이었습니다. 사실 박스 이미지를 구축하는 내용이 대부분이지만, 그 외 Vagrant Cloud에서 찾을 수 있기 때문입니다.
도커와 같이 사용자들이 제작하여 업로드합니다. 활용에 맞는 이미지를 골라 vagrant init <box name>
명령을 통해 간단히 내려받고 실행할 수 있습니다.