1. Giải thích mục đích script extract-vmlinux
2. Chạy extract-vmlinux ở chế độ DEBUG trên Ubuntu Server 22.04
extract-vmlinux-new1
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
#
# (c) 2011 Corentin Chary <corentin.chary@gmail.com>
#
# ----------------------------------------------------------------------
set -ux
check_vmlinux()
{
# Use readelf to check if it's a valid ELF
# TODO: find a better to way to check that it's really vmlinux
# and not just an elf
readelf -h $1 > /dev/null 2>&1 || return 1
cat $1
exit 0
}
try_decompress()
{
# The obscure use of the "tr" filter is to work around older versions of
# "grep" that report the byte offset of the line instead of the pattern.
# Try to find the header ($1) and decompress from here
for pos in `tr "$1n$2" "n$2=" < "$img" | grep -abo "^$2"`
do
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
check_vmlinux $tmp
done
}
# Check invocation:
me=${0##*/}
me1=$0
img=$1
total=$#
if [ $# -ne 1 -o ! -s "$img" ]
then
echo "Usage: $me <kernel-image>" >&2
exit 2
fi
# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap "rm -f $tmp" 0
# That didn't work, so retry after decompression.
try_decompress '3721310' xy gunzip
try_decompress '3757zXZ00' abcde unxz
try_decompress 'BZh' xy bunzip2
try_decompress '135' xxx unlzma
try_decompress '211114132' xy 'lzop -d'
try_decompress '02!L30' xxx 'lz4 -d'
try_decompress '(265/375' xxx unzstd
# Finally check for uncompressed images or objects:
check_vmlinux $img
# Bail out:
echo "$me: Cannot find vmlinux." >&2
Kết quả chạy
root@DevSecOps.Edu.VN:~# ./extract-vmlinux-new1 /boot/vmlinuz-`uname -r` > vmlinux-`uname -r`
+ me=extract-vmlinux-new1
+ me1=./extract-vmlinux-new1
+ img=/boot/vmlinuz-6.2.0-39-generic
+ total=1
+ [ 1 -ne 1 -o ! -s /boot/vmlinuz-6.2.0-39-generic ]
+ mktemp /tmp/vmlinux-XXX
+ tmp=/tmp/vmlinux-Wgn
+ trap rm -f /tmp/vmlinux-Wgn 0
+ try_decompress 3721310 xy gunzip
+ tr 3721310nxy nxy=
+ grep -abo ^xy
+ pos=10743949
+ tail -c+10743949 /boot/vmlinuz-6.2.0-39-generic
+ gunzip
+ check_vmlinux /tmp/vmlinux-Wgn
+ readelf -h /tmp/vmlinux-Wgn
+ return 1
+ try_decompress 3757zXZ00 abcde unxz
+ tr 3757zXZ00nabcde nabcde=
+ grep -abo ^abcde
+ try_decompress BZh xy bunzip2
+ tr BZhnxy nxy=
+ grep -abo ^xy
+ try_decompress 135 xxx unlzma
+ tr 135nxxx nxxx=
+ grep -abo ^xxx
+ try_decompress 211114132 xy lzop -d
+ tr 211114132nxy nxy=
+ grep -abo ^xy
+ try_decompress 02!L30 xxx lz4 -d
+ tr 02!L30nxxx nxxx=
+ grep -abo ^xxx
+ try_decompress (265/375 xxx unzstd
+ tr (265/375nxxx nxxx=
+ grep -abo ^xxx
+ pos=17321
+ tail -c+17321 /boot/vmlinuz-6.2.0-39-generic
+ unzstd
+ check_vmlinux /tmp/vmlinux-Wgn
+ readelf -h /tmp/vmlinux-Wgn
+ cat /tmp/vmlinux-Wgn
+ exit 0
+ rm -f /tmp/vmlinux-Wgn
root@DevSecOps.Edu.VN:~# file vmlinux-`uname -r` | tr ',' 'n'
vmlinux-6.2.0-39-generic: ELF 64-bit LSB executable
x86-64
version 1 (SYSV)
statically linked
BuildID[sha1]=2a3defeb6f65ab82b04220896fb7f396de6a54e5
stripped
root@DevSecOps.Edu.VN:~# readelf -h vmlinux-`uname -r`
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000000
Start of program headers: 64 (bytes into file)
Start of section headers: 73400752 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 5
Size of section headers: 64 (bytes)
Number of section headers: 39
Section header string table index: 38
3. Giải thích dòng code 39 – 45
me=${0##*/}
img=$1
# ! là ngược lại
if [ $# -ne 1 -o ! -s "$img" ]
then
echo "Usage: $me <kernel-image>" >&2
exit 2
fi
# Tạo 1 file rỗng
# --> nếu file đã có thì update lại thời gian tạo file là lúc chạy lệnh
touch vmlinuz
# Dung lượng (size) của file là 0
ll vmlinuz
# Giải nén với file size = 0 sẽ báo lỗi
./extract-vmlinux vmlinuz
me=${0##*/} → chỉ lấy tên script → bỏ ./ hoặc /root/ hoặc */
Nếu chạy script không nhập vào tên file để giải nén ($# -ne 1)
hoặc nhập vào tên file nhưng dung lượng = 0 thì cũng báo lỗi (-o ! -s “$img”).
https://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions
Regular Expression - RegEx
* là xuất hiện 0 hoặc nhiều lần
#######################################################################
# ${parameter##word} --> word có thể là mẫu (pattern) --> xóa chuỗi dài nhất có thể
# --> xóa chuỗi "(0 hoặc nhiều kí tự bất kì)/" dài nhất
t11='/usr/share/man/man1/' ; echo ${t1##*/}
t12='/usr/share/man/man1/zstd.1.gz' ; echo ${t2##*/}
zstd.1.gz
#######################################################################
# ${parameter#word} --> word có thể là mẫu (pattern) --> xóa chuỗi ngắn nhất có thể
# --> xóa chuỗi "(0 hoặc nhiều kí tự bất kì)/" ngắn nhất
t21='/usr/share/man/man1/' ; echo ${t3#*/}
usr/share/man/man1/
t22='/usr/share/man/man1/zstd.1.gz' ; echo ${t4#*/}
usr/share/man/man1/zstd.1.gz
https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions
Giải thích tham số vị trí (Positional Parameters)
pos_params.sh
cat pos_params.sh
#!/bin/bash
echo "$0 = $0"
echo "$1 = $1"
echo "$2 = $2"
echo "$3 = $3"
echo "$4 = $4"
echo "$5 = $5"
echo "$6 = $6"
echo "$7 = $7"
echo "$8 = $8"
echo "$9 = $9"
echo "$10 = $10"
echo "${10} = ${10}"
echo "$11 = $11"
echo "${11} = ${11}"
echo "$12 = $12"
echo "${12} = ${12}"
echo "$13 = $13"
echo "${13} = ${13}"
echo "$14 = $14"
echo "${14} = ${14}"
echo "$15 = $15"
echo "${15} = ${15}"
echo "$16 = $16"
echo "${16} = ${16}"
echo "$* = $*"
echo "$@ = $@"
echo "$# = $#"
# END
./pos_params.sh D e v S e c O p s . E d u . V N
$0 = ./pos_params.sh
$1 = D
$2 = e
$3 = v
$4 = S
$5 = e
$6 = c
$7 = O
$8 = p
$9 = s
$10 = D0
${10} = .
$11 = D1
${11} = E
$12 = D2
${12} = d
$13 = D3
${13} = u
$14 = D4
${14} = .
$15 = D5
${15} = V
$16 = D6
${16} = N
$* = D e v S e c O p s . E d u . V N
$@ = D e v S e c O p s . E d u . V N
$# = 16
cat -n pos_params.sh
/pos_params.sh D e v S e c O p s . E d u . V N
4. Giải thích dòng code 48 – 49
# Tạo ra file trong /tmp với XXX là 3 kí tự tạo ra ngẫu nhiên
# --> mục đích tạo ra 1 file trong /tmp có tên ngẫu nhiên để k trùng với file nào đang có trong /tmp
# Do tên tạo ra ngẫu nhiên nên phải lưu vào biến tmp để xóa được
tmp=$(mktemp /tmp/vmlinux-XXX)
# Bắt sự kiện khi script chạy xong có trạng thái thoát là gì thì sẽ làm công việc tương ứng
# Trong trường hợp này nếu script thoát ở trạng thái thành công thì sẽ xóa file vừa tạo trong /tmp
trap "rm -f $tmp" 0
ll /tmp/vmlinux*
tmp=$(mktemp /tmp/vmlinux-XXX)
ll /tmp/vmlinux*
echo $tmp
https://manpages.ubuntu.com/manpages/noble/en/man1/mktemp.1.html
# Liệt kê các tín hiệu trap nhận biết được
trap -l
# Nhấn Ctrl + C
trap "echo Nhấn Ctrl + C sẽ thấy em nó" 2
# Nhấn Ctrl + Z sau đó nhấn Enter
trap "echo Nhấn Ctrl + Z sẽ thấy em nó" 20
5. Giải thích dòng code 52 – 58
# Chạy hàm try_decompress
# '3721310' là $1 --> 3721310 là file signature (magic number) của gzip
# xy là $2
# gunzip là $3
try_decompress '3721310' xy gunzip
try_decompress '3757zXZ00' abcde unxz
try_decompress 'BZh' xy bunzip2
try_decompress '135' xxx unlzma
try_decompress '211114132' xy 'lzop -d'
try_decompress '02!L30' xxx 'lz4 -d'
try_decompress '(265/375' xxx unzstd
####################################################################################
try_decompress()
{
# The obscure use of the "tr" filter is to work around older versions of
# "grep" that report the byte offset of the line instead of the pattern.
# Try to find the header ($1) and decompress from here
for pos in `tr "$1n$2" "n$2=" < "$img" | grep -abo "^$2"`
do
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
check_vmlinux $tmp
done
}
####################################################################################
check_vmlinux()
{
# Use readelf to check if it's a valid ELF
# TODO: find a better to way to check that it's really vmlinux
# and not just an elf
readelf -h $1 > /dev/null 2>&1 || return 1
cat $1
exit 0
}
Giải thích về File signature = Magic number = Header
File signature hay Magic number là các số hexa ở đầu file để xác định file đó là loại gì
→ bạn tạo ra 1 loại file mới phải đăng kí số Magic number để người đời biết
→ trên Linux lệnh file xác định loại file dựa vào magic number
gzip magic number
gzip có Magic number ở dạng hexa là 1f 8b 08
→ coi trong RFC 1952
https://datatracker.ietf.org/doc/html/rfc1952
# Coi magic number bằng lịnh xxd
xxd -g1 -l8 /usr/share/info/gzip.info.gz
# Coi magic number trực tiếp từ standard input
echo "DevSecOps.Edu.VN" | gzip -c | xxd -l4 -g1
# Coi loại file = lịnh file
file /usr/share/info/gzip.info.gz
# Đổi từ hexa sang octal = bc
for i in 1F 8B 08; do echo "obase=8; ibase=16; $i" | bc; done
# Đổi từ hexa sang octal = printf
printf "%o %o %on" 0x1f 0x8b 0x08
# Tác giả chuyển magic number của gzip từ hệ 16 (hexa) sang hệ 8 (octal)
# --> với các kí tự không in được trên màn hình sẽ chuyển sang hệ 8
# --> hoặc các số có giá trị nằm ngoài 127 giá trị của bảng mã ASCII
# Trường hợp này đổi cả 3 số hexa ra octal (1f 8b 08)
try_decompress '3721310' xy gunzip
→ hệ 8 (octal) trong tr phải có trước 3 chữ số ; n sẽ chuyển thành xuống hàng
https://manpages.ubuntu.com/manpages/noble/en/man1/tr.1.html
# Đổi các số magic number của gzip từ hexa sang hệ 10 (decimal)
for i in 1F 8B 08; do echo "obase=10; ibase=16; $i" | bc; done
# Cài gói ascii
apt update && apt install ascii -y
# Lệnh ascii để xem bảng mã ascii
ascii
Các kí tự không in ra được trong bảng mã ASCII
Các kí tự in ra được trong bảng mã ASCII
Bảng mã ASCII mở rộng từ 127 lên 255 kí tự
xz magic number
xz có Magic number ở dạng hexa là FD 37 7A 58 5A 00 hoặc 0xFD, ‘7’, ‘z’, ‘X’, ‘Z’, 0x00
https://tukaani.org/xz/xz-file-format.txt
# Coi magic number của xz = lịnh xxd
xxd -g1 -l8 /usr/share/fwupd/uefi-capsule-ux.tar.xz
# Coi magic number trực tiếp từ standard input
echo "DevSecOps.Edu.VN" | xz -z | xxd -l8 -g1
# Coi loại file = lịnh file
file /usr/share/fwupd/uefi-capsule-ux.tar.xz
# Đổi từ hexa sang octal = bc
echo "obase=8; ibase=16; FD" | bc
# Đổi từ hexa sang octal = printf
printf "%on" 0xFD
# Trường hợp này chỉ đổi FD (hexa) ra 375 (octal)
try_decompress '3757zXZ00' abcde unxz
bzip2 magic number
Làm y hệt – Coi như bài tập dành cho bạn
lzma magic number
Làm y bong – Coi như bài tập dành cho bạn
lzop magic number
Làm i xì – Coi như bài tập dành cho bạn
lz4 magic number
Làm y chang – Coi như bài tập dành cho bạn
zstd magic number
zstd có Magic number ở dạng hexa là FD 2F B5 28 (Little Endian)
https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#zstandard-frames
# Coi magic number trực tiếp từ standard input
echo "DevSecOps.Edu.VN" | zstd -z | xxd -l8 -g1
# Coi magic number theo group 4 byte
echo "DevSecOps.Edu.VN" | zstd -z | xxd -l8 -g4
# -e là chuyển sang dạng Little Endian
echo "DevSecOps.Edu.VN" | zstd -z | xxd -e -l8 -g4
printf "%o %on" 0xb5 0xfd
ascii
→ xxd có tùy chọn -e để chuyển sang dạng Little Endian.
https://manpages.ubuntu.com/manpages/noble/en/man1/xxd.1.html
# Trường hợp này đổi 2 số hexa b5, fd ra 265, 375 (octal)
try_decompress '(265/375' xxx unzstd
Giải thích hàm try_decompress
Do grep version cũ không lấy đúng vị trí/độ dời (offset) của mẫu tìm kiếm (pattern) nếu không nằm ở đầu dòng.
→ phải xài tr nối Magic number với 1 chuỗi nào đó sau đó thay thế Magic number bằng chuỗi đó + xuống đầu dòng để grep tìm đúng vị trí → lưu vào biến pos (viết tắt của position?)
→ từ đó lấy vị trí sau Magic number bằng tail để giải nén → để trong /tmp lưu vào biến tmp
Giải thích lệnh grep -abo
Vị trí Magic number của vmlinuz* trên Ubuntu Server 22.04
grep -abo -m1 -e $'x28xb5x2fxfd' /boot/vmlinuz-`uname -r`
pos=$(grep -abo -m1 -e $'x28xb5x2fxfd' /boot/vmlinuz-`uname -r`)
echo $pos
pos=${pos%%:*}
echo $pos
Vị trí Magic number của vmlinuz* trên AlmaLinux 9
grep -abo -e $'x1fx8bx08' /boot/vmlinuz-`uname -r`
pos=$(grep -abo -e $'x1fx8bx08' /boot/vmlinuz-`uname -r`)
echo $pos
pos=${pos%%:*}
echo $pos
Vị trí Magic number của vmlinuz* trên Debian 12
grep -abo -m1 -e $'xFDx37x7Ax58x5Ax00' /boot/vmlinuz-`uname -r`
pos=$(grep -abo -m1 -e $'xFDx37x7Ax58x5Ax00' /boot/vmlinuz-`uname -r`)
echo $pos
pos=${pos%%:*}
echo $pos
extract-vmlinux-new để in ra pos = vị trí giải nén = vị trí Magic number + 1
cat extract-vmlinux-new
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
#
# (c) 2011 Corentin Chary <corentin.chary@gmail.com>
#
# ----------------------------------------------------------------------
check_vmlinux()
{
# Use readelf to check if it's a valid ELF
# TODO: find a better to way to check that it's really vmlinux
# and not just an elf
readelf -h $1 > /dev/null 2>&1 || return 1
#cat $1
exit 0
}
try_decompress()
{
# The obscure use of the "tr" filter is to work around older versions of
# "grep" that report the byte offset of the line instead of the pattern.
# Try to find the header ($1) and decompress from here
for pos in `tr "$1n$2" "n$2=" < "$img" | grep -abo "^$2"`
do
echo $pos
pos=${pos%%:*}
echo $pos
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
check_vmlinux $tmp
done
}
# Check invocation:
me=${0##*/}
img=$1
if [ $# -ne 1 -o ! -s "$img" ]
then
echo "Usage: $me <kernel-image>" >&2
exit 2
fi
# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXXXXX)
trap "rm -f $tmp" 0
# That didn't work, so retry after decompression.
try_decompress '3721310' xy gunzip
try_decompress '3757zXZ00' abcde unxz
try_decompress 'BZh' xy bunzip2
try_decompress '135' xxx unlzma
try_decompress '211114132' xy 'lzop -d'
try_decompress '02!L30' xxx 'lz4 -d'
try_decompress '(265/375' xxx unzstd
# Finally check for uncompressed images or objects:
# check_vmlinux $img
# Bail out:
# echo "$me: Cannot find vmlinux." >&1
Giải thích hàm check_vmlinux
Hàm try_decompress gọi tới hàm check_vmlinux → đọc header ELF đã của file đã giải nén trong /tmp
→ nếu đúng đọc tiếp file = lệnh cat → thoát
→ nếu sai thoát khỏi hàm
→ chạy tiếp vòng lặp for với biến pos ở 1 vị trí khác tìm thấy gọi hàm check_vmlinux chạy tiếp
6. Giải thích dòng code 61 – 64
check_vmlinux $img
# Bail out:
echo "$me: Cannot find vmlinux." >&2
Kiểm tra file nhập vào có định dạng ELF hay không → không cần kiểm tra nén
→ nếu đúng thì đọc file = lệnh cat → thoát
→ có dòng này để file nào nhập vào là ELF format thì đọc luôn
Nếu không phải thì báo lỗi cuối cùng là không thể tìm thấy vmlinux → thoát khỏi script