@windmelon
2019-03-19T16:04:42.000000Z
字数 3652
阅读 2262
Linux系统分析实验
原创作品转载请注明出处https://github.com/mengning/linuxkernel/
sa18225465
ubuntu 18.04 虚拟机
VMware workstation 14 Player
下载linux3.9.4内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.tar.xz
解压并编译
cd linux-5.0
make menuconfig #选择compile the kernel with debug info
make
下载menuOS
git clone https://github.com/mengning/menu.git
Makefile内容如下
CC_PTHREAD_FLAGS = -lpthread
CC_FLAGS = -c
CC_OUTPUT_FLAGS = -o
CC = gcc
RM = rm
RM_FLAGS = -f
TARGET = test
OBJS = linktable.o menu.o test.o
all: $(OBJS)
$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
rootfs:
gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
gcc -o hello hello.c -m32 -static
find init hello | cpio -o -Hnewc |gzip -9 > ../rootfs.img
qemu -kernel ../linux-5.0/arch/x86/boot/bzImage -initrd ../rootfs.img
.c.o:
$(CC) $(CC_FLAGS) $<
clean:
$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
编译
cd menu-master
make
cd ..
启动menuOS
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img
这里选择65号系统调用,65号系统调用glibc中接口为int getpgrp(void)
,作用是获得当前进程组识别码
在menu-master/main.c中增加两个函数
int Gprp(int argc, char *argv[])
{
int prp;
prp = getpgrp();
printf("pgrp:%d\n",prp);
return 0;
}
int GprpAsm(int argc, char *argv[])
{
int prp;
asm volatile(
"mov $0x41,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (prp)
);
printf("pgrp:%d\n",prp);
return 0;
}
并在main函数中注册
MenuConfig("getpgrp","Get process group id",Gprp);
MenuConfig("getpgrp-asm","Get process group id(asm)",GprpAsm);
测试效果
打开两个终端,其中一个输入命令
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img -s -S -append nokaslr
注意:这里必须加-append nokaslr,因为kaslr是linux的防御机制,会对运行的代码进行随机地址映射,使用这个命令禁用随机地址映射,否则和符号表对不上,导致断点设置失效
另一个使用gdb,并设置断点
gdb
(gdb)file linux-5.0/vmlinux
(gdb)target remote:1234
(gdb)b sys_getpgrp
(gdb)c
此时在QEMU窗口中
getpgrp
发现此时已经进入到内核态,运行内核函数,获取grp了,我们需要知道的是我们运行API函数getpgrp()
或者通过汇编代码调用65号中断到进入内核态到返回用户态的过程中发生了什么
linux5.0内核好像更改了系统调用函数的名字,内核源代码和网上资料的都不太一样,这里参考的是
系统调用system_call的处理过程
在使用int 0x80中断之后,CPU会运行arch/x86/entry/entry_32.S中的指令
//这段代码就是系统调用处理的过程,其它的中断过程也是与此类似
//系统调用就是一个特殊的中断,也存在保护现场和回复现场
ENTRY(system_call)//这是0x80之后的下一条指令
RING0_INT_FRAME # can't unwind into user space anyway
ASM_CLAC
pushl_cfi %eax # save orig_eax
SAVE_ALL//保护现场
GET_THREAD_INFO(%ebp)
# system call tracing in operation / emulation
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(NR_syscalls), %eax
jae syscall_badsys
syscall_call:
// 调用了系统调用处理函数,实际的系统调用服务程序
call *sys_call_table(,%eax,4)//定义的系统调用的表,eax传递过来的就是系统调用号,在例子中就是调用的systime
syscall_after_call:
movl %eax,PT_EAX(%esp) # store the return value
syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx # current->work
jne syscall_exit_work//退出之前,syscall_exit_work
//进入到syscall_exit_work里边有一个进程调度时机
restore_all:
TRACE_IRQS_IRET
restore_all_notrace://返回到用户态
#ifdef CONFIG_X86_ESPFIX32
movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS
# Warning: PT_OLDSS(%esp) contains the wrong/random values if we
# are returning to the kernel.
# See comments in process.c:copy_thread() for details.
movb PT_OLDSS(%esp), %ah
movb PT_CS(%esp), %al
andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
CFI_REMEMBER_STATE
je ldt_ss # returning to user-space with LDT SS
#endif
restore_nocheck:
RESTORE_REGS 4 # skip orig_eax/error_code
irq_return:
INTERRUPT_RETURN//iret(宏),系统调用过程到这里结束