
Every Linux enthusiast has a favorite operating system. Some even have several, each tailored for specific tasks. One such special case is a portable system that you can boot on someone else’s machine or on your own if your main OS suddenly stops functioning properly.
There are many ready-made solutions designed for these tasks. However, we want to create a fully customized environment—one that’s familiar and equipped with the necessary programs!
There are two options here: create a bootable ISO image or simply install everything on a flash drive. The first option will result in a much more compact system, while the second option provides the important capability to install and remove programs and change settings while the system is running.
Yes, USB drives will wear out over time, but they are inexpensive these days, and being able to run a full-fledged operating system, in my opinion, is far more important.
Preparation
I will conduct all operations on Arch Linux for x86-64 and will use the original archiso, which is used to create official ISO images.
Therefore, the system must have the following packages installed: archiso, arch-install-scripts, and gptfdisk.
You can either install directly onto a USB drive or create a disk image that can later be written to a flash drive. I’ll now describe the process of creating a disk image.
Creating an Empty File Sized for the USB Drive
Navigate to the directory where we will create our image.
cd ~
The following actions will be performed with superuser privileges.
su
To help you navigate more easily, I’ll display the output of certain commands.
You can find out the exact size of the device using the command
lsblk -bo NAME,SIZE
lsblk -bo NAME,SIZE
NAME SIZE
sda4005527552
├─sda1 10485760
├─sda2 268435456
└─sda3 3725540864
mmcblk0 62226694144
├─mmcblk0p1 209715200
└─mmcblk0p2 62015930368
It will display all devices, and you’ll need to identify your USB drive among them.
Once you have the device number, you can create a command and specify the desired file size. Divide the size by 4096 to get the required number of blocks.
Examples:
16 GB: 16034824192 / 4096 = 3914752
4 GB: 4005527552 / 4096 = 977912
4096 is the standard sector size for a file system.
The command to create a file named usb.
with a size of 4 GB is:
dd if=/dev/zero of=usb.img bs=4096 count=977912 status=progress
$ dd if=/dev/zero of=usb.img bs=4096 count=977912 status=progress
Using /dev/zero to create a USB image...
3,651,747,840 bytes (3.7 GB, 3.4 GiB) copied, 8 seconds, 456 MB/s
977,912 records in977,912 records out
4,005,527,552 bytes (4.0 GB, 3.7 GiB) copied, 8.78 seconds, 456 MB/s
Let’s check:
ls -la usb.img
Here’s the command used to list the detailed information of the file usb.
in a directory. The output shows:
- File permissions:
-rw-rw-r--
indicating it’s a regular file with read and write permissions for the owner and group, and read permission for others. - Number of links:
1
- Owner:
root
- Group:
root
- File size:
4005527552
bytes - Last modified date and time: April 25 at 19:51
- File name:
usb.
img
If the file turns out to be larger than needed, you can trim it to the desired length using truncate
. For example, to reduce it to 16 GB:
truncate -s 16034824192 usb.img
Creating Disk Partitioning Layout
To allow our system to boot on computers using both BIOS and EFI, we need to partition the disk into three sections:
- 10 MB without a filesystem, required for installing the GRUB bootloader in BIOS mode.
- 256 MB, will be formatted to FAT32, here we will create the
boot
partition and install GRUB in EFI mode. - The remaining space will be allocated to the root filesystem, which will be formatted as Ext4.
Creating a GPT Layout and Partitioning the Disk into Three Parts:
sgdisk -o -n 1:0:+10M -t 1:EF02 -n 2:0:+256M -t 2:EF00 -n 3:0:0 -t 3:8300 usb.img
$ sgdisk -o -n 1:0:+10M -t 1:EF02 -n 2:0:+256M -t 2:EF00 -n 3:0:0 -t 3:8300 usb.img
Creating new GPT entries in memory.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.
Mounting the Created Image into the System
We establish a connection between the image file usb.
and a loopback device. This allows us to interact with the image file as if it were a physical device, like a USB drive.
losetup --show -Pf usb.img
$ losetup –show -Pf usb.img
/dev/loop0
The command will display the name of the created device, which in this case is /
.
Checking.
ls /dev/loop0*
$ ls /dev/loop0*
/dev/loop0
/dev/loop0p1
/dev/loop0p2
/dev/loop0p3
Our image should have three partitions: loop0p1
, loop0p2
, loop0p3
.
Formatting Partitions
According to the standard, the EFI partition should be formatted in FAT32.
mkfs.fat -F32 /dev/loop0p2
$ mkfs.fat -F32 /dev/loop0p2
mkfs.fat 4.2 (2021-01-31)
The third section will be the main one, and you can choose any file system for it that is supported by both the bootloader and the kernel.
Formatting to Ext4:
mkfs.ext4 /dev/loop0p3
$ mkfs.ext4 /dev/loop0p3
mke2fs 1.46.5 (30-Dec-2021)
…
…
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
Mount Partitions on /mnt/usb
First, we’ll mount the partition /
, which will serve as our main partition for installing the system. Then, within this partition, we’ll create a directory /
, where we’ll mount the partition /
.
mkdir -p /mnt/usb
mount /dev/loop0p3 /mnt/usb
mkdir /mnt/usb/boot
mount /dev/loop0p2 /mnt/usb/boot
Copying airootfs
A few words about Archiso. This is the tool used to create official Arch Linux images. It consists of two parts: the mkarchiso
script, which assembles the image, and a directory with the configuration settings for the upcoming image. We will need the configuration directory, which contains:
- A list of packages that need to be installed, stored in the
packages.
file.x86_64 - The
airootfs
directory. Its structure is identical to that of Arch Linux and contains the system configuration files.
We will need to specify the path to the directory with Archiso settings three times. To make this easier, let’s assign the path to a variable.
export ARCHISO=/usr/share/archiso/configs/releng
We replicate the settings from the official image before installing packages. If a configuration file for a package already exists during its installation, the file is not overwritten; instead, a new file with the extension .pacnew is created. You will see warnings about this during the package installation process.
cp -afv $ARCHISO/airootfs/* /mnt/usb/
Translating Certain Files into Russian (Optional)
You can customize the system’s greeting message to whatever you like. The greeting file is located at /
. I’ve (okay, Yandex did) provided a simple translation of the original file, and below is the command to replace it.
wget -O /mnt/usb/etc/motd https://gitflic.ru/project/ksandr/archi/blob/raw?file=airootfs/etc/motd
In the installation image, there’s a command called Installation_guide
that opens the system installation wiki page in a text-based console browser.
Replace the link so that it points to a Russian-language equivalent of the page.
sed -i 's/https:\/\/wiki.archlinux.org\/title\/Installation_guide/https:\/\/wiki.archlinux.org\/title\/Installation_guide\(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)/' /mnt/usb/usr/local/bin/Installation_guide
Installing Packages
The list of packages is taken from the official image settings.
Installation Command:
pacstrap -c -G -M -C $ARCHISO/pacman.conf -- /mnt/usb
To parse the list of packages, you can use the following command:
grep -v '^#' $ARCHISO/packages.x86_64 | tr '\n' ' ' | xargs pacstrap -c -G -M -C $ARCHISO/pacman.conf -- /mnt/usb
$ grep -v ‘^#’ packages.x86_64 | tr ‘\n’ ‘ ‘ | xargs pacstrap -c -G -M -C pacman.conf — /mnt/usb
…
…
…
(14/16) Reloading system bus configuration…
Skipped: Running in chroot.
(15/16) Warn about old perl modules
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_MESSAGES = “ru_RU.UTF-8”,
LANG = “ru_RU.UTF-8”
are supported and installed on your system.
perl: warning: Falling back to the standard locale (“C”).
(16/16) Updating the info directory file…
If the process concludes with these lines, you can proceed to the next step. If not, double-check to ensure you completed each action correctly.
Fine-Tuning the Setup
The main part of the installation aligns with what is written in the official wiki.
For convenience, I’ll repeat some of the steps from the official guide in this article.
We are generating the /
file for our image, which is a list of file systems that are mounted during system startup.
genfstab -U /mnt/usb > /mnt/usb/etc/fstab
We’ll switch to a chroot environment. This will isolate the process from the rest of the system, creating a virtual environment where the process can operate as if it were on a separate system.
After executing this command, the current process and all its child processes will operate in an isolated environment with /
as the root directory.
arch-chroot /mnt/usb
We need to edit the /
file.
If the system you’re creating an image from uses a swap partition, then the entry for mounting this partition will be added to the /
file when it’s generated.
You need to disable the swap partition in the fstab
file if it exists. Remove or comment out the line that corresponds to it.
vim /etc/fstab
I also recommend changing the mount options from realtime
to noatime
. This reduces the number of write operations to the disk.
Below is an example of a file with modifications; the UUID values in your file will be different.
/dev/loop0p3
UUID=d6ef477c-8db7-4b1b-9e55-a28fefbf03c1 / ext4 rw,noatime 0 1
$ /dev/loop0p2
UUID=C963-38B9/boot vfat rw,noatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro0 2
$ /dev/sda1
#UUID=8ac5fbc3-058e-4a18-a084-e68b52c1af46 none swap defaults0 0
Set your time zone.
ln -sf /usr/share/zoneinfo/Europe/Moscow /etc/localtime
The file /
includes a list of available system localizations. It is recommended to keep these two:
en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8
Generate localization files.
locale-gen
Set a system-wide value for the LANG
variable.
echo LANG=ru_RU.UTF-8 > /etc/locale.conf
Set the font and layout switch parameters.
In the file /
, enter:
KEYMAP=ru
FONT=cyr-sun16
info
Some of the manipulations described in this section can be performed directly with the archiso directory, reducing the number of steps required during installation.
Setting up Bootloaders
Start by creating an initial system boot disk.
Edit the configuration file located at /
.
We will use the standard mkinitcpio
configuration file, so make sure to modify the line that points to the config.
vim /etc/mkinitcpio.d/linux.preset
$ mkinitcpio preset file for the 'linux' package on archiso
PRESETS=('archiso')
ALL_kver='/boot/vmlinuz-linux'
archiso_config='/etc/mkinitcpio.conf'
archiso_image="/boot/initramfs-linux.img"
Edit the mkinitcpio
configuration file. In the HOOKS
parameter, you need to remove autodetect
.
vim /etc/mkinitcpio.conf
...
HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block filesystems fsck)
...
Regenerate the initcpio image.
info
The mkinitcpio image in Arch Linux is generated by the mkinitcpio script and includes all the necessary files and scripts for booting and initializing the system. This image is used during the boot process after the Linux kernel is loaded, but before the system is fully initialized and control is transferred to user space.
mkinitcpio -P
$ mkinitcpio -P
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: ‘archiso’
…
==> Initcpio image generation successful
Install the bootloader for systems with BIOS. It is placed at the beginning of the disk, which is why we created the first partition.
Note: The command specifies the disk, not the disk partition!
grub-install --target=i386-pc --recheck /dev/loop0
$ grub-install –target=i386-pc –recheck /dev/loop0
Installation for i386-pc platform is in progress.
Installation completed successfully. No errors.
Installing the bootloader for EFI systems.
grub-install --target=x86_64-efi --efi-directory /boot --recheck --removable
$ grub-install –target=x86_64-efi –efi-directory /boot –recheck –removable
Installing for x86_64-efi platform.
Installation completed. No errors.
Create a configuration file for the bootloader.
grub-mkconfig -o /boot/grub/grub.cfg
$ grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file…
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/intel-ucode.img /boot/amd-ucode.img /boot/initramfs-linux.img
Warning: os-prober will not be executed to detect other boot partitions.
Their systems will not be added to the GRUB boot configuration.
Refer to the documentation for the GRUB_DISABLE_OS_PROBER option.
Adding boot menu entry for UEFI firmware settings…
Found memtest86+ image: /boot/memtest86+/memtest.bin
/usr/bin/grub-probe: warning: unknown device type loop0.
Found memtest86+ EFI image: /boot/memtest86+/memtest.efi
/usr/bin/grub-probe: warning: unknown device type loop0.
done
As you might have understood, we have installed two bootloaders, and both of them refer to a single configuration file.
The official ISO image of Arch Linux uses the Syslinux bootloader. You could use it too; it’s a matter of preference. I opted for a simpler configuration option.
Configuring Logging
To reduce the number of disk accesses, configure logging to use RAM instead of the disk.
Creating a Directory:
mkdir -p /etc/systemd/journald.conf.d
Record the following settings in the file /
:
[Journal]Storage=volatileSystemMaxUse=16MRuntimeMaxUse=32M
Additional Setup Steps
When creating an ISO disk image using mkarchiso, some configurations are handled using “hooks” (command-line scripts) in the pacman package manager. These hooks are removed during the ISO creation process.
Since we are building the image ourselves, we need to manually remove certain hooks.
rm -v -- $(grep -Frl 'remove from airootfs' /etc/pacman.d/hooks/)
Setting the Root Password (Optional)
By default, there is no password set for the root user, allowing you to establish your own. During startup, the system automatically logs in as root.
SSH access for the root user is also enabled by default.
passwd
$ passwd
New password:
Retype new password:
passwd: password successfully updated
You can also create a regular user.
Image Ready: Unmount Devices
Exiting chroot.
exit
Unmount the partitions in reverse order.
umount /mnt/usb/boot
umount /mnt/usb
We will deactivate the loop device mapping to the usb.
file.
losetup -D
Writing the Image to a USB Drive
Attention! If you haven’t broken your system yet, this might be a great opportunity to do so. So be extremely cautious.
www
A detailed description of how to write the image is available in the official Arch Linux wiki.
Connect the USB drive to the system and enter the command:
lsblk -piaf
Find the USB drive in the list and note down its number.
And write the image to it. In the command below, replace the of=/
parameter (substitute the letter of your drive for X).
dd if=usb.img of=/dev/sdX bs=4096 status=progress && sync
Exit the root environment:
exit
Testing the Image with QEMU (Optional)
To test how our image operates, we can run it in an emulator.
This section assumes that you have these packages installed:
qemu-base tigervnc openbsd-netcat
You can launch a virtual machine from the created image using the following command:
qemu-system-x86_64 -drive file=usb.img,format=raw -m 1024 -enable-kvm -monitor unix:/tmp/monitor.sock,server,nowait
VNC server running on 127.0.0.1:5901
You can connect to the virtual machine using any VNC client.
For TigerVNC, the command to connect would be as follows:
vncviewer 127.0.0.1:5901
TigerVNC Viewer v1.13.1
Build date: 2024-01-20 02:29
Copyright (C) 1999-2022, TigerVNC Team and many others (see README.rst)
More information about TigerVNC can be found at https://www.tigervnc.org
…
To connect to the virtual machine’s management console, you can use tools like nc
(netcat).
nc -U /tmp/monitor.sock
QEMU 9.0.0 monitor – type ‘help’ for more information
(qemu)

Summary
We have a system installation image file that can be written to any media. The key feature is that once you boot from it, you can install additional packages and configure the system to meet your specific needs.
I used a 4 GB flash drive, and after performing all the necessary actions, 1.2 GB of free space remained on it.
[Bonus Tips
This disk imaging method is versatile and can be used with various Archiso configurations. For example, it can be applied with Ctlos, one of the locally developed Arch Linux distributions.

The author, Сtlos, uses a custom repository for certain packages. You can find the instructions for connecting it on the project page.
Alternatively, you can edit the pacman.
file (located in the configuration directory) by adding an additional repository and disabling package signature verification.
Here is an example of a modified pacman.
file:
...[ctlos_repo]SigLevel = Optional TrustAllServer = https://github.com/ctlos/$repo/raw/master/$archServer = https://osdn.net/projects/ctlos/storage/$repo/$archServer = https://cvc.keybase.pub/$repo#Include = /etc/pacman.d/ctlos-mirrorlist[ctlos-aur]SigLevel = Optional TrustAllServer = https://github.com/ctlos/$repo/raw/master/$archServer = https://osdn.net/projects/ctlos/storage/$repo/$archServer = https://cvc.keybase.pub/$repo#Include = /etc/pacman.d/ctlos-mirrorlist
Important point: It’s better to clone a remote repository as root because all files in the configuration directory should belong to the root user.

2023.02.13 — First Contact: Attacks on Google Pay, Samsung Pay, and Apple Pay
Electronic wallets, such as Google Pay, Samsung Pay, and Apple Pay, are considered the most advanced and secure payment tools. However, these systems are also…
Full article →
2022.06.01 — F#ck AMSI! How to bypass Antimalware Scan Interface and infect Windows
Is the phrase "This script contains malicious content and has been blocked by your antivirus software" familiar to you? It's generated by Antimalware Scan Interface…
Full article →
2023.02.21 — Herpaderping and Ghosting. Two new ways to hide processes from antiviruses
The primary objective of virus writers (as well as pentesters and Red Team members) is to hide their payloads from antiviruses and avoid their detection. Various…
Full article →
2022.02.15 — Reverse shell of 237 bytes. How to reduce the executable file using Linux hacks
Once I was asked: is it possible to write a reverse shell some 200 bytes in size? This shell should perform the following functions: change its name…
Full article →
2022.06.02 — Blindfold game. Manage your Android smartphone via ABD
One day I encountered a technical issue: I had to put a phone connected to a single-board Raspberry Pi computer into the USB-tethering mode on boot. To do this,…
Full article →
2022.06.01 — WinAFL in practice. Using fuzzer to identify security holes in software
WinAFL is a fork of the renowned AFL fuzzer developed to fuzz closed-source programs on Windows systems. All aspects of WinAFL operation are described in the official documentation,…
Full article →
2023.07.07 — Evil Ethernet. BadUSB-ETH attack in detail
If you have a chance to plug a specially crafted device to a USB port of the target computer, you can completely intercept its traffic, collect cookies…
Full article →
2023.03.03 — Infiltration and exfiltration. Data transmission techniques used in pentesting
Imagine a situation: you managed to penetrate the network perimeter and gained access to a server. This server is part of the company's internal network, and, in theory, you could…
Full article →
2022.02.09 — Kernel exploitation for newbies: from compilation to privilege escalation
Theory is nothing without practice. Today, I will explain the nature of Linux kernel vulnerabilities and will shown how to exploit them. Get ready for an exciting journey:…
Full article →
2022.06.01 — Routing nightmare. How to pentest OSPF and EIGRP dynamic routing protocols
The magic and charm of dynamic routing protocols can be deceptive: admins trust them implicitly and often forget to properly configure security systems embedded in these protocols. In this…
Full article →