BLOG main image
분류 전체보기 (222)
Reversing (13)
Pwnable (4)
Linux Kernel (3)
Crypto (2)
Wargames (66)
Programming (18)
Write Up (32)
Project (22)
Web (2)
My Life (52)
Memo (3)
etc (2)
발표자료 (1)
36,063 Visitors up to today!
Today 12 hit, Yesterday 30 hit
daisy rss
tistory 티스토리 가입하기!
'Linux Kernel'에 해당되는 글 3건
2018.03.27 14:38


x86-64 리눅스 커널을 디버깅 하는 방법입니다.

커널을 디버깅하기 위해 host-machine, target-machine이 필요한데 host-machine은 우분투 x86-64라고 가정하고 target-machine은 커널을 qemu에 올려서 vm으로 진행하겠습니다.

먼저 디버깅할 버전의 커널을 컴파일 해야합니다.

커널을 다운로드 받고 (http://kernel.org) 압축을 풀어서 커널 디렉토리로 이동한 후

make defconfig
make kvmconfig
make menuconfig

를 순서대로 실행해줍니다.

make menuconfig를 하면 옵션을 선택할 수 있는 창들이 뜰 텐데 여기서 

Kernel Hacking
    compile-time checks and compiler options
        compile the kernel with debug info 체크

를 해준 후 exit하면 make를 하기 위한 설정이 다 되었습니다.

이제 make를 해서 빌드를 시작하고 기다리면 됩니다.

빌드가 다 되었다면 arch/x86/boot/bzImage에 커널 파일이 생기고 최상위 디렉토리에 vmlinux파일이 정상적으로 생겼을 것입니다. 이 파일을 vm을 구성할 디렉토리로 옮겨줍니다.

다음은 파일시스템을 구성해야합니다.

sudo apt-get install debootstrap를 해서 다운로드 해주고 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/bash
# Copyright 2016 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
 
# create-image.sh creates a minimal Debian-wheezy Linux image suitable for syzkaller.
 
set -eux
 
# Create a minimal Debian-wheezy distributive as a directory.
RELEASE=wheezy
DIR=wheezy
sudo rm -rf $DIR
mkdir -p $DIR
sudo debootstrap --include=openssh-server,curl,tar,gcc,libc6-dev,time,strace,ltrace,sudo,less,psmisc,selinux-utils,policycoreutils,checkpolicy,selinux-policy-default,gdb $RELEASE $DIR
 
# Set some defaults and enable promtless ssh to the machine for root.
sudo sed -'/^root/ { s/:x:/::/ }' $DIR/etc/passwd
echo 'T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100' | sudo tee -a $DIR/etc/inittab
printf '\nauto eth0\niface eth0 inet dhcp\n' | sudo tee -a $DIR/etc/network/interfaces
echo 'debugfs /sys/kernel/debug debugfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'SELINUX=disabled' | sudo tee $DIR/etc/selinux/config
echo "kernel.printk = 7 4 1 3" | sudo tee -a $DIR/etc/sysctl.conf
echo 'debug.exception-trace = 0' | sudo tee -a $DIR/etc/sysctl.conf
echo "net.core.bpf_jit_enable = 1" | sudo tee -a $DIR/etc/sysctl.conf
echo "net.core.bpf_jit_harden = 2" | sudo tee -a $DIR/etc/sysctl.conf
echo "net.ipv4.ping_group_range = 0 65535" | sudo tee -a $DIR/etc/sysctl.conf
echo -en "127.0.0.1\tlocalhost\n" | sudo tee $DIR/etc/hosts
echo "nameserver 8.8.8.8" | sudo tee -a $DIR/etc/resolve.conf
echo "debugging-machine" | sudo tee $DIR/etc/hostname
ssh-keygen -f $RELEASE.id_rsa -t rsa -''
sudo mkdir -p $DIR/root/.ssh/
cat $RELEASE.id_rsa.pub | sudo tee $DIR/root/.ssh/authorized_keys
 
# Build a disk image
dd if=/dev/zero of=$RELEASE.img bs=1M seek=2047 count=1
sudo mkfs.ext4 -F $RELEASE.img
sudo mkdir -/mnt/$DIR
sudo mount -o loop $RELEASE.img /mnt/$DIR
sudo cp -a $DIR//mnt/$DIR/.
sudo umount /mnt/$DIR
cs

위 코드를 create-image.sh로 붙여넣고 실행해주면 됩니다.
(google syzkaller에서 가져옴 (https://github.com/google/syzkaller/blob/master/tools/create-image.sh))

wheezy.img까지 정상적으로 생성되었다면 이제 부팅을 할 시간입니다.

qemu가 없다면 sudo apt-get install qemu kvm qemu-kvm을 해서 설치해주고

qemu-system-x86_64 -kernel bzImage -append "console=ttyS0 root=/dev/sda debug" -hda wheezy.img -net user,hostfwd=tcp::10021-:22 -net nic -nographic -m 4G -smp 2 -s

을 이용해서 부팅할 수 있습니다.

kernel은 아까 컴파일한 bzImage파일을 사용하겠다는 의미이고 이미지는 wheezy.img, 22번 포트를 10021포트로 리다이렉팅 했기 때문에 ssh -p 10021 -i wheezy.id_rsa root@localhost를 이용해서 ssh접속을 할 수 있습니다.

그리고 메모리 4G, cpu코어 두개를 할당하고 1234번 포트로 gdb 디버깅이 들어오도록 listen하고 있겠다는 의미입니다.

만약 본인의 cpu가 가상화를 지원한다면 -enable-kvm 옵션을 추가해서 좀 더 빠르게 qemu를 돌릴 수 있습니다.

이제 정상적으로 부팅이 됐다면 디버깅을 해볼 차례입니다. qemu를 종료하고 gdb -q ./vmlinux를 해서 심볼을 로드합니다.

그 후 b*start_kernel을 해서 커널의 첫 시작 함수인 start_kernel에 브레이크 포인트를 걸고 target remote localhost:1234를 해서 1234번 포트로 디버기가 붙기를 기다립니다.

그 후 다시 qemu를 부팅한다면 gdb로 커널이 붙게됩니다. 그 상태에서 c를 입력해주면 start_kernel에 브레이크 포인트가 걸립니다.

심볼까지 로드했기 때문에 현재 어셈의 위치와 소스를 비교하면서 디버깅 할 수 있습니다.

이제 원하는 함수 / 드라이버에 브레이크 포인트를 걸고 디버깅하면 됩니다.

Name
Password
Homepage
Secret
2018.03.27 11:37


낮은 버전의 커널을 최신 혹은 높은 버전의 gcc로 컴파일 할 때 
cc1: error: code model kernel does not support PIC mode
라는 에러를 볼 수 있다. gcc특정 버전 이상에서는 pie적용이 기본으로 설정되어있어서 나는 오류이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
diff --git a/Makefile b/Makefile
index dda982c..f96b174 100644
--- a/Makefile
+++ b/Makefile
@@ -608,6 +608,12 @@ endif # $(dot-config)
 # Defaults to vmlinux, but the arch makefile usually adds further targets
 all: vmlinux
 
+# force no-pie for distro compilers that enable pie by default
+KBUILD_CFLAGS += $(call cc-option, -fno-pie)
+KBUILD_CFLAGS += $(call cc-option, -no-pie)
+KBUILD_AFLAGS += $(call cc-option, -fno-pie)
+KBUILD_CPPFLAGS += $(call cc-option, -fno-pie)
+
 # The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default
 # values of the respective KBUILD_* variables
 ARCH_CPPFLAGS :=
cs

이 코드를 pie.patch로 만들고 커널 소스 최상위 디렉토리에서 

patch -p1 < pie.patch 해주면 된다. (Makefile을 다 설정해준 후에 해줘야한다.)

Name
Password
Homepage
Secret
2018.03.20 15:57

가끔씩 옛날 버전 컴파일 하다가 ____ilog2_nan 이거 없다고 에러날 때가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
diff --git a/include/linux/log2.h b/include/linux/log2.h
index ef3d4f67118c..07ef24eedf83 100644
--- a/include/linux/log2.h
+++ b/include/linux/log2.h
@@ -16,12 +16,6 @@ 
 #include <linux/bitops.h>
 
 /*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
  * non-constant log of base 2 calculators
  * - the arch may override these in asm/bitops.h if they can be implemented
  *   more efficiently than using fls() and fls64()
@@ -85,7 +79,7 @@  unsigned long __rounddown_pow_of_two(unsigned long n)
 #define ilog2(n)                \
 (                        \
     __builtin_constant_p(n) ? (        \
-        (n) < 1 ? ____ilog2_NaN() :    \
+        (n) < 1 ? 0 :            \
         (n) & (1ULL << 63) ? 63 :    \
         (n) & (1ULL << 62) ? 62 :    \
         (n) & (1ULL << 61) ? 61 :    \
@@ -149,9 +143,7 @@  unsigned long __rounddown_pow_of_two(unsigned long n)
         (n) & (1ULL <<  3) ?  3 :    \
         (n) & (1ULL <<  2) ?  2 :    \
         (n) & (1ULL <<  1) ?  1 :    \
-        (n) & (1ULL <<  0) ?  0 :    \
-        ____ilog2_NaN()            \
-                   ) :        \
+        0           ) :        \
     (sizeof(n) <= 4) ?            \
     __ilog2_u32(n) :            \
     __ilog2_u64(n)                \
cs

이거 커널 소스 디렉토리에서 이거 붙여넣어서 ilog.patch로 저장하고 patch -p1 < ilog.patch 하면 지가 알아서 패치해준다.

그리고 다시 make ㄲㄲ

Name
Password
Homepage
Secret
prev"" #1 next