标签归档:kernel

centos/redhat kernel 支持rbd模块(el7)

el内核3.10.0-123.el7.x86_64默认不支持rbd模块,需要将rbd模块编译进去。

1 需要将config的配置项打开。如下:

2 编译过程可以选择只编译模块,也可以将kernel重新编译。模块编译见本博客相关内容,这里采用kernel重新编译,编译过程中出现以下问题:

经分析后,给内核该文件打了个patch

LXC,Linux containers

1 LXC安装

2 LXC的介绍

Lxc 全称,Linux containers,是一种基于容器的操作系统层级的虚拟化技术。LXC可以在操作系统层级上提供虚拟的执行环境,一个虚拟机的执行环境就是一个容器,可以为该容器绑定特定的CPU、Memory节点,并可以分配特定比例的CPU时间,IO时间,限制可以使用的内存大小(包括内存和swap空间),提供device访问控制,提供独立的namespace(网络,pid,ipc,mnt,uts);

LXC依靠Linux内核相关特性,基于容器的虚拟化技术起源于所谓的资源容器和安全容器,LXC在资源管理方面依赖与Linux内核的Cgroups子系统,Cgroups子系统是Linux内核提供的一个基于进程组的资源管理框架,可以为特定的进程组限定可以使用资源,LXC在资源隔离控制方面依赖与Linux内核的namespace特性,具体而言就是在clone时加入相应的flag(NEWNS NEWPID)等。

LXC和传统的HAL(硬件抽象层)层次的虚拟化技术相比,具有以下优势:

1 更小的虚拟化开销(LXC的诸多特性有Kernel提供,Kernel实现这些特性只有极少的消耗)

2       快速部署,不需要创建虚拟机,只需要安装LXC,不需要单独为内核打补丁。

1 Cgroups

Cgroups子系统是(control groups的缩写),是Linux内核提供的一种可以限制、记录、隔离进程组(process)所使用的物理资源(cpu,memory,io.etc)机制。

Cgroups最初的目标为资源管理器提供统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口,现在的cgroups适用于多种应有场景,从单独的进程资源控制,到实现操作系统层次的虚拟化。

1 限制进程组可以使用资源的数量,Resource Limiting,如,memory子系统可以为进程设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发OOM(Out of memory).

2 进程组的优先级控制,比如,可以使用CPU子系统为某个进程组分配特定的cpu share.

3 记录进程组使用的资源数量,比如可以使用cpuacct子系统记录某个进程使用cpu的时间。

4 进程组隔离,比如使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。

5 进程组控制,比如使用freezer子系统将进程组挂起和恢复。

Cgroups的相关概念:

1 任务:(task),在cgroups中,任务就是系统的一个进程

2 控制族群,(control groups)。控制族群就是一组按照某种标准划分的进程,Cgroups

中的资源控制是一控制族群为单位实现,一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个进程组群。一个进程组的进程可以使用cgroups以控制族群为单位分配资源,同时也受到cgroups以控制族群为单位设定的限制。

3 层级,(hierarchy).控制族群可以组织成hierarchical的形式,既一颗控制族群树,控制族群树上的子节点控制族群是父节点控制族群的孩子。继承父控制族群的特定的属性。

4 子系统,(subsystem)。一个子系统就是一个资源控制器, 比如cpu子系统就是控制cpu时间分配器的一个控制器,子系统必须附加attach到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

Cgroups的系统相互关系

1 每次在系统中创建新层级时, 该系统中的所有任务都是那个层次的默认cgroup.(root cgroup,此 cgroup在创建层级时,自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。

2 一个子系统最多只能附加到一个层级。

3 一个层级可以附加多个子系统

4 一个任务可以是多个cgroup的成员,但这些cgroup必须在不同的层级。

5 系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在的cgroup的成员。 然后可以根据需要将该子任务移动到不同的cgroup中,但开始时,总是继承其父任务的cgroups

Cgroups子系统介绍

blkio–这个子系统为块设备设定输入/输出限制,比如物理设备(硬盘,固态硬盘,usb等)

cpu–这个子系统使用调度程序提供对CPU的cgroup 任务访问

cpuacct– 这个子系统自动生成cgroup中任务所使用的cpu报告

cpuset –这个子系统为cgroup中的任务分配独立CPU和内存节点

devices–这个子系统允许或者拒绝cgroup中的任务访问设备

freezer– 这个子系统刮起或者恢复cgroup中的任务

memory — 这个子系统设定cgroup中任务内存的限制,并自动生成由那些任务使用的内存资源报告。

Net_cls–这个子系统使用等级识别符(classid)标记网络数据包,可以允许linux流量控制程序tc,识别从具体cgroup中生成的数据包

Ns — 命名空间子系统。

 

2 Namespaces机制

Linux Namespaces机制提供了一种资源隔离方案,PID、IPC、NetWork等系统资源不再是全局性的,而是属于特定的Namespace.每个Namespaces里面的资源对其他的Namespace都是透明的。要创建新的Namespace,只需要调用clone 时指定相应的flag。当调用clone时,设定为CLONE_NEWPID.就会创建一个新的pid Namespaces。clone出来的新进程将成为NameSpace里的第一个进程,一个PID Namespace 为进程提供了一个独立的PID环境,PID namespace内的PID将从1开始,在Namespace内调用fork,vfork,或clone都将产生一个在该Namespace内部独立的PID。新创建的Namespace里的第一个进程在该Namespace内的PID为1.PID namespace 是层次性的,新建的Namespace将会创建该Namespace的进程属于Namespace的子namespace。子namespace中的进程对于父namespace是可见的,一个进程将拥有不止一个PID。而是在所在的Namespace中的进程对所有直系祖先Namespace中都将有有一个PID。

当调用Clone,设定了Clone_NEWIPC.将会创建一个新的IPCNamespace,clone出来的进程将成为Namespace里的第一个进程,一个IPC Namespace 有一组System V IPC objects标识符构成,这个标识符有IPC相关的系统调用创建, 在一个IPC Namespace里面创建IPC object 对该Namespace内的所有进程可见,但对其他的Namespace不可见,这样就使的不同的Namespace 之间的进程不能直接通信。PID namespace和IPCnamespace可以组合起来一起使用,只需要在调用clone时,同时指定CLONE_NEWPID|CLONE_NEWIPC.

当调用Clone时,设定了CLONE_NEWNS,就会创建一个新的mount Namespace,每个进程都存在于一个mount namespace里面,mount namespace为进程提供一个文件层次视图,如果不设定这个flag,子进程和父进程共享一个mount namespace,其后子进程调用mount或umount将会影响到所有该Namespace内的进程,如果子进程在一个独立的mount namespace里面,就可以调用mount或umount建立一份新的文件层次视图。该flag配合pivot_root系统调用,可以为进程创建一个独立的目录空间。

当调用clone,设定CLONE_NEWNET,就会创建一个新的NetWork Namespace.一个network namespace为进程提供了一个完全独力的网络协议栈的视图,包括网络设备接口,IPV4/IPV6协议栈,IP路由表,防火墙规则,sockets等。一个Network namespace提供了一个独立的网络环境,就跟一个独立的系统一样。一个物理设备只能存在一个Network namespace中,可以从一个namespace 移动到另一个Namespace中,虚拟网络设备提供了一种类似管道的抽象,可以在不同的Namespace之间建立隧道,利用虚拟化网络设备,可以建立到其他的namespace中的物理设备桥接,当一个Namespace被销毁时,物理设备会自动移回init network namespace,即系统最开始的namespace.

当调用clone,设定CLONE_NEWUTS,就会创建一个新的UTS Namespace,一个UTSnamespcae 就是一组被uname返回的标识符,新的UTS namespace 中的标识符通过复制调用进程所属的namespace的标识符来初始化,clone出来的进程可以通过相关系统调用改变这些标识符,比如sethostname来改变namespace的hostname。这一改变,对namespace内所有进程可见。CLONE_NEWUTS和CLONE_NEWNET可以一起使用。

以上所有clone flag 都可以一起使用,为进程提供了一个独立的运行环境,LXC正是通过在clone时设定flag,为进程创建对立PID,IPC,FS,NETWORK,UTS的空间容器,一个容器就是一个虚拟的运行环境,对容器的进程是透明的,每个进程都以为自己直接在系统上运行。

chromium在sandbox里将设置namespace的代码:

 

关于内核系统调用的hooks问题

关于内核系统调用的hooks问题
转载请注明出处 [ By selinux.com]SELinux+
代码地址:https://github.com/qfong/mkm
截获系统调用的常用的方法通过sys_call_table的方法,如:
注册hooks函数:

unhooks函数

注册hooks函数,但系统必须导出sys_call_table内核符合,但在2.6内核和部分2.4的内核系统中,sys_call_table不再导出,但可以在内存中找到它。

下面给出的方法是基于通过搜索代码块指针为SYS_CLOSE,寻找一个特定的内存模式,用于发现sys_call_table的指针地址,然后依靠的地址表的前6个字到该地址。这是在x86系统以及一招,但一向被认为是不稳定和不太可靠。如下面的代码:

但也有一些依靠sys_call的中断处理程序地址中断描述符表,中断陷入点为0×80。然后一个搜索sys_call_table指针地址的处理程序,用于查找需要跳转到的确切sys_call中断处理。

ARM Linux上的系统,没有IDT(Interrupt Descriptor Table),而是软件中断(SWI)处理程序所在LDR指向的地址0×00000008,或在0xFFFF0008或高向量实现(high-vector implementations)如Android。按照地址,查找并加载其系统调用表。

下面是一些其他获得调用表的方法:
X86平台上

X86_64平台上

ia32平台

ARM

关于写保护
由于内核的页标记为只读,尝试用函数去写这个区域的内存,会产生一个内核oops。这种保护可以很简单的被规避,但通过设置cr0寄存器的WP位为0,禁止写保护CPU上。控制寄存器维基百科的文章 证实了这一点属性:

位 名称 全名 说明
16 WP 写保护 确定CPU是否可以写入页面标记为只读
WP位将需要在代码中的多个点的设置和重置,所以它使抽象的操作纲领性意义。下面的代码来源于PAX项目(http://pax.grsecurity.net/),专门从native_pax_open_kernel()和native_pax_close_kernel()例程。采取格外谨慎,以防止潜在的竞争条件引起由倒霉的调度在SMP系统,由丹•罗森伯格在一篇博客文章中(http://vulnfactory.org/blog/2011/08/12/wp-safe-or-not/)解释:
As described in the Intel Manuals (Volume 3A, Section 2.5):

Code如下:

也可以通过以下代码来实现

在一些ARM中,WP位的概念不存在,必须采取特殊照顾,而在ARM中引入了指令缓存架构来处理数据。虽然数据和指令高速缓存的概念也存在x86和x86_64硬件架构的,这样的功能没有在发展过程中构成障碍。在Android里面,可以不需要对内存保护机制进行考虑。

实现代码地址:https://github.com/qfong/mkm
附文档: 丹•罗森伯格在一篇博客文章
WP: Safe or Not?

During the course of kernel exploitation (or some other form of runtime kernel modification), it is frequently desirable to be able to modify the contents of read-only memory. On x86, a classic trick is to leverage the WP (write-protect) bit in the CR0 register.

As described in the Intel Manuals (Volume 3A, Section 2.5):

WP Write Protect (bit 16 of CR0) – When set, inhibits supervisor-level proce-
dures from writing into read-only pages; when clear, allows supervisor-level
procedures to write into read-only pages (regardless of the U/S bit setting;
see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the
copy-on-write method of creating a new process (forking) used by operating
systems such as UNIX.
In an exploit where code execution has been achieved and the attacker wishes to, for example, install hooks in a read-only data structure, a simple solution is to toggle this bit to 0, perform the write, and toggle it back. This technique has been well-known for years, and is not only used in rootkits but also in commercial anti-virus products (is there a difference?).

In practice, this approach works nearly all of the time. But there are some caveats to be aware of when using this trick in exploit development.

Scheduling Race

On SMP systems, there is a scheduling race that must be dealt with. In extremely unlucky circumstances, it’s possible that the current thread disables the WP bit, is scheduled out at a precise moment, is re-scheduled onto a CPU that still has the WP bit enabled, and faults when attempting to perform a write to read-only memory. Even though I’ve never seen this happen in practice, it’s easy enough to contend with. If this is being done via some mechanism where you have the capability to compile against the current kernel (e.g. a module), one correct way of addressing this is to disable preemption of the current thread while performing writes to read-only pages, as the PaX project does:

static inline unsigned long native_pax_open_kernel(void)
{
unsigned long cr0;

preempt_disable();
barrier();
cr0 = read_cr0() ^ X86_CR0_WP;
BUG_ON(unlikely(cr0 & X86_CR0_WP));
write_cr0(cr0);
return cr0 ^ X86_CR0_WP;
}

static inline unsigned long native_pax_close_kernel(void)
{
unsigned long cr0;

cr0 = read_cr0() ^ X86_CR0_WP;
BUG_ON(unlikely(!(cr0 & X86_CR0_WP)));
write_cr0(cr0);
barrier();
preempt_enable_no_resched();
return cr0 ^ X86_CR0_WP;
}
These two functions, pax_open_kernel() and pax_close_kernel(), are used to modify structures such as the IDT when the PAX_KERNEXEC feature is enabled.

If you’re performing these modifications in an exploit, a simpler solution is to leverage the cli (clear interrupt flag) and sti (set interrupt flag) instructions to disable interrupts entirely during the course of the writes, which prevents re-scheduling as a side effect:

.macro disable_wp
cli
mov eax,cr0
and eax,0xfffeffff
mov cr0,eax
.endm

.macro enable_wp
mov eax,cr0
or eax,0×10000
mov cr0,eax
sti
.endm
Xen

The scheduling issue can easily be worked around, but twiz mentioned to me that there may be problems doing this on Xen. When not using HAP (hardware-assisted paging), Xen handles paging by creating shadow page tables, mapping the guest’s page tables read-only, and relying on WP to cause writes to guest page tables to trap and be handled properly by the hypervisor. As a result, CR0.WP was forcibly enabled in the past. This did not apply when using HAP, where the guest has always been able to freely access CR0.

However, in 2007, Xen added support for emulating the behavior of CR0.WP in order to support software that relies on WP modification (e.g. anti-virus). When the guest faults on a non-user write to a resident page while CR0.WP=0, the faulting instruction is then emulated to allow the write to succeed. This feature is limited by the completeness of the x86 emulator, but everything except the most esoteric instructions should be emulated properly. As a result, leveraging the WP bit to write to read-only memory on Xen should not pose any problems for exploit developers.

Thanks

Thanks to twiz for suggesting looking at Xen, and Keir Fraser for helpful information.

linux kernel定制(五)

5 获得驱动模块的脚本
转载请注明出处[By SELinux+]