操作系统内核加载随机化记录器
目标
记录随机化加载地址,信息存放到日志文件。
实现方法:分析内核源码中地址随机化的位置与原理,对随机化后的地址进行记录。
实验环境:
基于Linux ubuntu 4.4.0-31-generic #50~14.04.1-Ubuntu的Ubuntu7.0运行环境
Linux4.20+qemu开发环境
参考文档:
https://www.cnblogs.com/wangaohui/p/7122653.html
实验方法
抓住进程地址随机化两个关键点:一是记录进程加载地址信息的关键结构体,二是进程执行关键函数。
有关进程的关键结构体为vm_area_struct,具体如下,其中即记录了相应的vm_start地址。
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address
within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next, *vm_prev;
struct rb_node vm_rb;
/*
* Largest free memory gap in bytes to the left of this VMA.
* Either between this VMA and vma->vm_prev, or between one of the
* VMAs below us in the VMA rbtree and its ->vm_prev. This helps
* get_unmapped_area find a free area of the right size.
*/
unsigned long rb_subtree_gap;
/* Second cache line starts here. */
struct mm_struct *vm_mm; /* The address space we belong to. */
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, see mm.h. */
/*
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap interval tree.
*/
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
/*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
* list, after a COW of one of the file pages. A MAP_SHARED vma
* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
* or brk vma (with NULL file) can only be in an anon_vma list.
*/
struct list_head anon_vma_chain; /* Serialized by mmap_sem &
* page_table_lock */
struct anon_vma *anon_vma; /* Serialized by page_table_lock */
/* Function pointers to deal with this struct. */
const struct vm_operations_struct *vm_ops;
/* Information about our backing store: */
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
units, *not* PAGE_CACHE_SIZE */
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */
#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
};
进程执行关键函数为do_execve,其中使用了关键结构体linux_binprm ,如下所示
struct linux_binprm {
char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
unsigned long vma_pages;
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
unsigned long p; /* current top of mem */
unsigned int
cred_prepared:1,/* true if creds already prepared (multiple
* preps happen for interpreters) */
cap_effective:1;/* true if has elevated effective capabilities,
* false if not; except for init which inherits
* its parent's caps anyway */
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth; /* only for search_binary_handler() */
struct file * file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
unsigned int per_clear; /* bits to clear in current->personality */
int argc, envc;
const char * filename; /* Name of binary as seen by procps */
const char * interp; /* Name of the binary really executed. Most
of the time same as filename, but could be
different for binfmt_{misc,script} */
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
};
分析其调用顺序,
linux-4.2\fs\exec.c __do_execve_file()
linux-4.2\fs\exec.c exec_binprm()
linux-4.2\fs\exec.c search_binary_handler()
linux-4.2\fs\binfmt_elf.c load_elf_binary()
linux-4.2\fs\exec.c setup_new_exec(bprm)
linux-4.2\arch\x86\mm\mmap.c arch_pick_mmap_layout(current->mm)
可逐步找到地址随机化的关键函数:arch_pick_mmap_layout。
void arch_pick_mmap_layout(struct mm_struct *mm)
{
unsigned long random_factor = 0UL;
if (current->flags & PF_RANDOMIZE)
random_factor = arch_mmap_rnd();
mm->mmap_legacy_base = mmap_legacy_base(random_factor);
if (mmap_is_legacy()) {
mm->mmap_base = mm->mmap_legacy_base;
mm->get_unmapped_area = arch_get_unmapped_area;
} else {
mm->mmap_base = mmap_base(random_factor);
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
}
}
堆栈随机化函数为
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), executable_stack);
通过GDB调试可查看其随机化的结果,如下图所示。
可通过命令行获取随机化后的地址,如图所示,
与实验获取结果进行对比,地址相同,实验成功。
源码
static int load_elf_binary(struct linux_binprm *bprm)
{
/*
原始代码
*/
struct file *fp;
mm_segment_t fs;
loff_t my_pos;
int filename_size;
char * file_str = NULL;
struct timex txc;
struct rtc_time tm;
/*
**原始代码
#ifdef ELF_PLAT_INIT
ELF_PLAT_INIT(regs, reloc_func_desc);
#endif
**/
filename_size = 1024;
file_str = kmalloc(filename_size , GFP_KERNEL);
if(file_str == NULL)
{
goto end;
}
memset(file_str, 0, filename_size );
do_gettimeofday(&(txc.time));
rtc_time_to_tm(txc.time.tv_sec,&tm);
sprintf(file_str,"UTC time :%d-%02d-%02d %02d:%02d:%02d;filename: %s ;vm_start :0x%lx \n",tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,bprm->filename,bprm->vma->vm_start);
fp = filp_open("/address_file",O_RDWR |O_CREAT| O_APPEND ,0777);
if (IS_ERR(fp)){
goto end;
}
my_pos = 0;
fs = get_fs();
set_fs(KERNEL_DS);
vfs_write(fp,file_str, strlen(file_str),&my_pos);
filp_close(fp,NULL);
set_fs(fs);
end:
if(file_str != NULL)
{
kfree(file_str);
}
/**原始代码*/
}