1、刚工作时做Linux 流控;后来做安全操作系统;再后来做操作系统加固;现在做TCP 加速。唉!没离开过类Unix!!!但是水平有限。。
全部博文(353)
分类: LINUX
2015-06-05 17:21:55
原文地址:load_elf_binary阅读(1) 作者:ch122633
typedef struct elf32_hdr{unsigned char e_ident[EI_NIDENT];Elf32_Half e_type; ELF文件类型,1表示此文件是重定位文件,2表示可执行文件,3表示动态连接库Elf32_Half e_machine; CPU类型,它指出了此文件使用何种指令集。如果是Intel 0x386 CPU此值为3Elf32_Word e_version; ELF文件版本,为1。Elf32_Addr e_entry; /* Entry point */映像的程序入口Elf32_Off e_phoff; 这个是Program Header offset 程序头位移量Elf32_Off e_shoff; 这个是Section Header offset 节头偏移量Elf32_Word e_flags; 处理器特定标志Elf32_Half e_ehsize; ELF头部长度Elf32_Half e_phentsize; 数组元素(表项)的大小Elf32_Half e_phnum; Program Header numberElf32_Half e_shentsize; 数组元素(表项)的大小Elf32_Half e_shnum; Section Header numberElf32_Half e_shstrndx; 节头部字符表索引} Elf32_Ehdr;//此结构体一共52个字节
/*
* This structure is used to hold the arguments that are used when loading binaries.
*/这种结构是用来装是用来装载二进制时的参数。
该函数用到了一个类型为linux_binprm的结构体来保存要要执行的文件相关的信息
struct linux_binprm{
char buf[BINPRM_BUF_SIZE]; 保存可执行文件的头128字节
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
#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,
cap_effective:1;
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth;
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; 命令行参数和环境变量数目
char * filename; /* Name of binary as seen by procps */要执行的文件的名称
char * interp; 要执行的文件的真实名称,通常和filename相同
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
};
int memcmp(const void *buf1, const void *buf2, unsigned int count);
loc->elf_ex.e_ident 确认是否为"\177ELF"的ELF格式文件 SELFMAG是4
这里\177为8进制,十六进制为0x7f,后面的为'E','L','F'
#define elf_check_arch(x) \EF_CRIS_VARIANT_MASK 为0x0000000e 1110
((x)->e_machine == EM_CRIS \ 确认处理器
&& ((((x)->e_flags & EF_CRIS_VARIANT_MASK) == EF_CRIS_VARIANT_ANY_V0_V10 \
|| (((x)->e_flags & EF_CRIS_VARIANT_MASK) == EF_CRIS_VARIANT_COMMON_V10_V32))))
int kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count)
{
mm_segment_t old_fs;
loff_t pos = offset;
int result;
old_fs = get_fs();
set_fs(get_ds());
/* The cast to a user pointer is valid due to the set_fs() */
result = vfs_read(file, (void __user *)addr, count, &pos);
set_fs(old_fs);
return result;
}
kernel_read就是把那个loc映像文件的程序表头读入了。
然后就是get_fs了
#define get_fs() (current_thread_info()->addr_limit) 看名字就是获取当前的地址访问限制值。
然后是set_fs
set_fs(get_ds()); #define get_ds() (KERNEL_DS) #define KERNEL_DS ((mm_segment_t){0})看不懂
后面vfs_read() 应该是内核读取到用户空间吧,放到addr的字符串指针? 返回的读取长度?
typedef struct elf32_phdr {elf_ppnt = elf_phdata;
Elf32_Word p_type; 部的类型
Elf32_Off p_offset; 该段在文件中的偏移。这个偏移是相对于整个文件的。
Elf32_Addr p_vaddr; 该段加载后在进程空间中占用的内存起始地址。
Elf32_Addr p_paddr; 该段的物理地地址。这个字段被忽略,因为在多数现代操作系统下物理地址是进程无法触及的。
Elf32_Word p_filesz; 该段在文件中占用的字节大小,有些段可能在文件中不存在但却占内存空间,此时这个字段为0。
Elf32_Word p_memsz;该段在内存中占用的字节大小,有些段可能仅存在于文件中而不被加载到内存,此时这个字段为0。
Elf32_Word p_flags;段的属性。它用每一个二进制位表示一种属,相应位为1表示含有相应的属性,为0表示不含那种属性。其中最低位是可执行位,次低位是可写位,第三低位是可读位。
Elf32_Word p_align; 对齐。
} Elf32_Phdr;
struct file *open_exec(const char *name)
{
struct file *file;
int err;
这个就是最主要的函数吧
file = do_filp_open(AT_FDCWD, name, AT_FDCWD -100 当前路径吧?相对路径名
O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0, O_LARGEFILE 大文件?O_RDONLY 可读写FMODE_EXEC文件被打开
MAY_EXEC | MAY_OPEN); MAY_EXEC允许本地用户绕过限制执行文件 MAY_OPEN.......
if (IS_ERR(file))
goto out;
err = -EACCES; #define EACCES 13 Permission denied!!!
if (!S_ISREG(file->f_path.dentry->d_inode->i_mode))
goto exit;
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit;
fsnotify_open(file->f_path.dentry);
err = deny_write_access(file);
if (err)
goto exit;
out:
return file;
exit:
fput(file);
return ERR_PTR(err);
}
struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode)
这个函数再说吧http://blog.csdn.net/f413933206/article/details/5701913
do_filp_open先是把函数中的open_flag确认各种flag保证文件的权限,防止被破坏。然后link_path_walk()将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构。path_init为查找作准备工作,path_walk真正上路查找,这两个函数联合起来根据一段路径名找到对应的dentry
好像不搞懂dentry不行啊
struct dentry {
atomic_t d_count; 目录项对象使用计数器
unsigned int d_flags; /* protected by d_lock */ 目录项标志
spinlock_t d_lock; /* per dentry lock */
int d_mounted;
struct inode *d_inode; /* Where the name belongs to - NULL is 与文件名关联的索引节点
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */ 散列表表项的指针
struct dentry *d_parent; /* parent directory */ 父目录的目录项对象
struct qstr d_name;
struct list_head d_lru; /* LRU list */ 未使用链表的指针
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */ 父目录中目录项对象的链表的指针
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */ 对目录而言,表示子目录目录项对象的链表
struct list_head d_alias; /* inode alias list */ 相关索引节点(别名)的链表
unsigned long d_time; /* used by d_revalidate */
const struct dentry_operations *d_op; 目录项方法
struct super_block *d_sb; /* The root of the dentry tree */ 文件的超级块对象
void *d_fsdata; /* fs-specific data */ 与文件系统相关的数据
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ 存放短文件名
};
d_count有三种状态,回收内存的时候会考虑它
/*
struct inode {
struct hlist_node i_hash; 哈希表
struct list_head i_list; /* backing dev IO list */索引节点链表
struct list_head i_sb_list;
struct list_head i_dentry;,怎么两边都有目录项链表
unsigned long i_ino; 节点号
atomic_t i_count; 引用记数
unsigned int i_nlink;
uid_t i_uid; 使用者id
gid_t i_gid; 使用者id组
dev_t i_rdev; 实设备标识符
unsigned int i_blkbits; 以位为单位的块大小
u64 i_version; 版本号
loff_t i_size; 以字节为单位的文件大小
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime; 最后访问时间
struct timespec i_mtime; 最后修改(modify)时间
struct timespec i_ctime; 最后改变(change)时间
blkcnt_t i_blocks; 文件的块数
unsigned short i_bytes; 使用的字节数
umode_t i_mode;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */自旋锁
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem; 索引节点信号量
const struct inode_operations *i_op; 索引节点操作表
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */默认的索引节点操作
struct super_block *i_sb; 相关的超级块
struct file_lock *i_flock; 文件锁链表
struct address_space *i_mapping; 相关的地址映射
struct address_space i_data; 设备地址映射
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS]; 节点的磁盘限额
#endif
struct list_head i_devices; 块设备链表
union {
struct pipe_inode_info *i_pipe; 这个是网络设备?管道信息
struct block_device *i_bdev; 块设备驱动
struct cdev *i_cdev; 当inode指向一个字符设备文件时
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
int inode_permission(struct inode *inode, int mask)然后又是kernel_read到底是读到哪里啊,读文件还是读内存啊。读到bprm->buf里吧。
{
int retval;
if (mask & MAY_WRITE) {
umode_t mode = inode->i_mode;
/*
* Nobody gets write access to a read-only fs.
*/
if (IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/*
* Nobody gets write access to an immutable file.
*/
if (IS_IMMUTABLE(inode))
return -EACCES;
}
if (inode->i_op->permission)
retval = inode->i_op->permission(inode, mask);
else
retval = generic_permission(inode, mask, inode->i_op->check_acl);
if (retval)
return retval;
retval = devcgroup_inode_permission(inode, mask);
if (retval)
return retval;
return security_inode_permission(inode,
mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
}好像在确认文件的读写执行的权限,具体的也不是很看得懂了
这个函数完全不能理解
void setup_new_exec(struct linux_binprm * bprm)
{
int i, ch;
char * name;
char tcomm[sizeof(current->comm)];
arch_pick_mmap_layout(current->mm); 好像在判断栈的格式
/* This is the point of no return */
current->sas_ss_sp = current->sas_ss_size = 0;
if (current_euid() == current_uid() && current_egid() == current_gid())
set_dumpable(current->mm, 1);
else
set_dumpable(current->mm, suid_dumpable);
name = bprm->filename;
/* Copies the binary name from after last slash */
for (i=0; (ch = *(name++)) != '\0';) {
if (ch == '/')
i = 0; /* overwrite what we wrote */
else
if (i < (sizeof(tcomm) - 1))
tcomm[i++] = ch;
}
tcomm[i] = '\0';
set_task_comm(current, tcomm);
/* Set the new mm task size. We have to do that late because it may
* depend on TIF_32BIT which is only updated in flush_thread() on
* some architectures like powerpc
*/
current->mm->task_size = TASK_SIZE;
/* install the new credentials */
if (bprm->cred->uid != current_euid() ||
bprm->cred->gid != current_egid()) {
current->pdeath_signal = 0;
} else if (file_permission(bprm->file, MAY_READ) ||
bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
set_dumpable(current->mm, suid_dumpable);
}
/*
* Flush performance counters when crossing a
* security domain:
*/
if (!get_dumpable(current->mm))
perf_event_exit_task(current);
/* An exec changes our domain. We are no longer part of the thread
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
flush_old_files(current->files);
}