导读:
标题 分析应用程序加载时堆栈中的参数结构
作者 opera (enthusiast)
时间 04/13/01 09:17 PM
内核在运行应用程序之前要在用户堆栈最顶部建立一参数块,缺省配置下参数块最大为32个页面.
用户堆栈最顶部(0xBFFFFFFC)的字保留为0,接下来依次为执行文件名字符串,环境字符串表,命令行参数字符串表,处理器名称字符串,然后是一段辅助信息表,环境字符串指针表,命令行字符串指针表,最后是命令行参数数量argc.
对于标准的Linux程序来说,命令行字符串表指针argv和环境字符串表指针envp并不压入argc之上,就是说ELF的入口函数并不是以main(argc,argv,envp)参数形式调用,而是用main(argc,argv[0],argv[1],...,NULL,envp[0],envp[1],...,NULL)这样的形式,我觉得这有点莫名其妙,这样就使得Linux必须用汇编写一启动代码作一次转换才能调用main()函数,Linux采用这种方案的妙处何在?
struct linux_binprm{
char buf[BINPRM_BUF_SIZE];
struct page *page[MAX_ARG_PAGES]; 用户堆栈参数页面的内核映象
unsigned long p; /* current top of mem */
int sh_bang;
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
};
; fs/exec.c
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;
filename = getname((char *) regs.ebx);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ?s);
if (error == 0)
current->ptrace &= ~PT_DTRACE;
putname(filename);
out:
return error;
}
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
struct file *file;
int retval;
int i;
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
return retval;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
;保留堆栈最顶部的一个字,
;bprm.p是bprm.page上的相对堆栈指针,同时反映了参数页可用内存的大小
memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
; 清除参数页面指针表
bprm.file = file;
bprm.filename = filename;
bprm.sh_bang = 0;
bprm.loader = 0;
bprm.exec = 0;
if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
; 扫描用户参数数组,计算参数个数
allow_write_access(file);
fput(file);
return bprm.argc;
}
if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
; 扫描用户环境数组,计算环境变量个数
allow_write_access(file);
fput(file);
return bprm.envc;
}
retval = prepare_binprm(&bprm);
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm.filename, &bprm);
if (retval < 0) 在参数堆栈顶部首先压入可执行文件名
goto out;
bprm.exec = bprm.p;
retval = copy_strings(bprm.envc, envp, &bprm);
if (retval < 0) 接着压入环境字符串表
goto out;
retval = copy_strings(bprm.argc, argv, &bprm);
if (retval < 0) 再压入命令行参数字符串表
goto out;
retval = search_binary_handler(&bprm,regs);
if (retval >= 0)
/* execve success */
return retval;
out:
/* Something went wrong, return the inode and free the argument pages*/
allow_write_access(bprm.file);
if (bprm.file)
fput(bprm.file);
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page * page = bprm.page[ i ];
if (page)
__free_page(page);
}
return retval;
}
; fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
...
setup_arg_pages(bprm); 将内核的参数页映射到用户堆栈区域
...
bprm->p = (unsigned long)
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
load_addr, load_bias,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
...
}
static elf_addr_t *
create_elf_tables(char *p, int argc, int envc,
struct elfhdr * exec,
unsigned long load_addr,
unsigned long load_bias,
unsigned long interp_load_addr, int ibcs)
{
elf_caddr_t *argv;
elf_caddr_t *envp;
elf_addr_t *sp, *csp;
char *k_platform, *u_platform;
long hwcap;
size_t platform_len = 0;
; 这时p指向第1个命令行参数字符串
/*
* Get hold of platform and hardware capabilities masks for
* the machine we are running on. In some cases (Sparc),
* this info is impossible to get, in others (i386) it is
* merely difficult.
*/
hwcap = ELF_HWCAP; CPU特性描述字
k_platform = ELF_PLATFORM; CPU类型名
if (k_platform) {
platform_len = strlen(k_platform) + 1;
u_platform = p - platform_len;
__copy_to_user(u_platform, k_platform, platform_len);
} else
u_platform = p;
/*
* Force 16 byte _final_ alignment here for generality.
* Leave an extra 16 bytes free so that on the PowerPC we
* can move the aux table up to start on a 16-byte boundary.
*/
sp = (elf_addr_t *)((~15UL & (unsigned long)(u_platform)) - 16UL);
; 16字节边界上对齐
csp = sp;
csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0));
csp -= envc+1; 环境字符串表
csp -= argc+1; 命令行参数字符串表
csp -= (!ibcs ? 3 : 1); 是否建立argv与envp指针
if ((unsigned long)csp & 15UL)
sp -= ((unsigned long)csp & 15UL) / sizeof(*sp);
; 预偏置一下,使得最后的sp能够在16字节边界上对齐
/*
* Put the ELF interpreter info on the stack
*/
#define NEW_AUX_ENT(nr, id, val) / 由两个字组成的表项
__put_user ((id), sp+(nr*2)); /
__put_user ((val), sp+(nr*2+1)); /
sp -= 2; 分配两个字的空间
NEW_AUX_ENT(0, AT_NULL, 0); 写入顶部两个零指针作为补充参数表的结束
if (k_platform) {
sp -= 2;
; 填写CPU类型名称
NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
}
sp -= 3*2;
NEW_AUX_ENT(0, AT_HWCAP, hwcap); 填写CPU特性字
NEW_AUX_ENT(1, AT_PAGESZ, ELF_EXEC_PAGESIZE); 填写可执行文件页长
NEW_AUX_ENT(2, AT_CLKTCK, CLOCKS_PER_SEC);
if (exec) {
; 填写动态链接程序的补充参数表
sp -= 10*2;
NEW_AUX_ENT(0, AT_PHDR, load_addr + exec->e_phoff); 程序段表
NEW_AUX_ENT(1, AT_PHENT, sizeof (struct elf_phdr)); 程序段表项长度
NEW_AUX_ENT(2, AT_PHNUM, exec->e_phnum); 程序段表项数目
NEW_AUX_ENT(3, AT_BASE, interp_load_addr); 动态连接器的加载地址
NEW_AUX_ENT(4, AT_FLAGS, 0);
NEW_AUX_ENT(5, AT_ENTRY, load_bias + exec->e_entry); 主程序的入口
NEW_AUX_ENT(6, AT_UID, (elf_addr_t) current->uid);
NEW_AUX_ENT(7, AT_EUID, (elf_addr_t) current->euid);
NEW_AUX_ENT(8, AT_GID, (elf_addr_t) current->gid);
NEW_AUX_ENT(9, AT_EGID, (elf_addr_t) current->egid);
}
#undef NEW_AUX_ENT
sp -= envc+1; 分配环境指针表
envp = (elf_caddr_t *) sp;
sp -= argc+1; 分配参数指针表
argv = (elf_caddr_t *) sp;
if (!ibcs) {
__put_user((elf_addr_t)(unsigned long) envp,--sp); 填写环境指针表地址
__put_user((elf_addr_t)(unsigned long) argv,--sp); 填写命令行参数指针表地址
}
__put_user((elf_addr_t)argc,--sp); 填写命令行参数数目
current->mm->arg_start = (unsigned long) p; 第1个命令行参数字符串地址
while (argc-->0) {
; 填写命令行参数指针表
__put_user((elf_caddr_t)(unsigned long)p,argv++);
p += strlen_user(p);
}
__put_user(NULL, argv); 命令行参数指针表终止指针
current->mm->arg_end = current->mm->env_start = (unsigned long) p; 第1个参数字符串地址
while (envc-->0) {
; 填写环境指针表
__put_user((elf_caddr_t)(unsigned long)p,envp++);
p += strlen_user(p);
}
__put_user(NULL, envp); 环境指针表终止指针
current->mm->env_end = (unsigned long) p; 环境字符串表终止地址
return sp; 返回指向命令行参数数目的地址
}
int setup_arg_pages(struct linux_binprm *bprm)
{
unsigned long stack_base;
struct vm_area_struct *mpnt;
int i;
stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
bprm->p += stack_base; 这时bprm->p重定位为用户参数块的堆栈指针
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base; 指向用户堆栈最顶部的可执行文件名
mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
if (!mpnt)
return -ENOMEM;
; 建立初始的vm_area_struct结构
down(¤t->mm->mmap_sem);
{
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; 虚存起始于参数块的堆栈指针
mpnt->vm_end = STACK_TOP; 虚存终止于STACK_TOP
mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0;
mpnt->vm_file = NULL;
mpnt->vm_private_data = (void *) 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
; 目前虚存的总量
}
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page *page = bprm->page[ i ];
if (page) {
bprm->page[ i ] = NULL;
current->mm->rss++;
put_dirty_page(current,page,stack_base);
; 将page所指向的物理页面映射到用户参数地址上
}
stack_base += PAGE_SIZE;
}
up(¤t->mm->mmap_sem);
return 0;
}
; fs/exec.c
; copy_strings_kernel(argc,argv,bprm)将字符串指针数组argv中argc个字符串
; 从上向下压入内核参数页bprm->page之中
int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
{
while (argc-- > 0) {
char *str;
int len;
unsigned long pos;
if (get_user(str, argv+argc) || !str || !(len = strnlen_user(str, bprm->p)))
return -EFAULT;
if (bprm->p < len)
return -E2BIG;
bprm->p -= len;
/* XXX: add architecture specific overflow check here. */
pos = bprm->p;
while (len > 0) { 在拷贝中动态地分配参数页
char *kaddr;
int i, new, err;
struct page *page;
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
i = pos/PAGE_SIZE;
page = bprm->page[ i ];
new = 0;
if (!page) {
page = alloc_page(GFP_HIGHUSER);
bprm->page[ i ] = page;
if (!page)
return -ENOMEM;
new = 1;
}
kaddr = kmap(page);
if (new && offset)
memset(kaddr, 0, offset);
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len) {
bytes_to_copy = len;
if (new)
memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len);
}
err = copy_from_user(kaddr + offset, str, bytes_to_copy);
kunmap(page);
if (err)
return -EFAULT;
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
}
}
return 0;
}
Edited by lucian_yao on 04/16/01 01:08 PM.
标题 Re: 分析应用程序加载时堆栈中的参数结构 [re: opera]
作者 lucian_yao (addict)
时间 04/16/01 01:10 PM
(argc,argv,envp)参数形式调用
--------------不知道是不是这样会有安全漏洞?
本文转自
http://www.patching.net/bbs/viewgooddoc_2097_4.html
标题 分析应用程序加载时堆栈中的参数结构
作者 opera (enthusiast)
时间 04/13/01 09:17 PM
内核在运行应用程序之前要在用户堆栈最顶部建立一参数块,缺省配置下参数块最大为32个页面.
用户堆栈最顶部(0xBFFFFFFC)的字保留为0,接下来依次为执行文件名字符串,环境字符串表,命令行参数字符串表,处理器名称字符串,然后是一段辅助信息表,环境字符串指针表,命令行字符串指针表,最后是命令行参数数量argc.
对于标准的Linux程序来说,命令行字符串表指针argv和环境字符串表指针envp并不压入argc之上,就是说ELF的入口函数并不是以main(argc,argv,envp)参数形式调用,而是用main(argc,argv[0],argv[1],...,NULL,envp[0],envp[1],...,NULL)这样的形式,我觉得这有点莫名其妙,这样就使得Linux必须用汇编写一启动代码作一次转换才能调用main()函数,Linux采用这种方案的妙处何在?
struct linux_binprm{
char buf[BINPRM_BUF_SIZE];
struct page *page[MAX_ARG_PAGES]; 用户堆栈参数页面的内核映象
unsigned long p; /* current top of mem */
int sh_bang;
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
};
; fs/exec.c
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;
filename = getname((char *) regs.ebx);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ?s);
if (error == 0)
current->ptrace &= ~PT_DTRACE;
putname(filename);
out:
return error;
}
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
struct file *file;
int retval;
int i;
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
return retval;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
;保留堆栈最顶部的一个字,
;bprm.p是bprm.page上的相对堆栈指针,同时反映了参数页可用内存的大小
memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
; 清除参数页面指针表
bprm.file = file;
bprm.filename = filename;
bprm.sh_bang = 0;
bprm.loader = 0;
bprm.exec = 0;
if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
; 扫描用户参数数组,计算参数个数
allow_write_access(file);
fput(file);
return bprm.argc;
}
if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
; 扫描用户环境数组,计算环境变量个数
allow_write_access(file);
fput(file);
return bprm.envc;
}
retval = prepare_binprm(&bprm);
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm.filename, &bprm);
if (retval < 0) 在参数堆栈顶部首先压入可执行文件名
goto out;
bprm.exec = bprm.p;
retval = copy_strings(bprm.envc, envp, &bprm);
if (retval < 0) 接着压入环境字符串表
goto out;
retval = copy_strings(bprm.argc, argv, &bprm);
if (retval < 0) 再压入命令行参数字符串表
goto out;
retval = search_binary_handler(&bprm,regs);
if (retval >= 0)
/* execve success */
return retval;
out:
/* Something went wrong, return the inode and free the argument pages*/
allow_write_access(bprm.file);
if (bprm.file)
fput(bprm.file);
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page * page = bprm.page[ i ];
if (page)
__free_page(page);
}
return retval;
}
; fs/binfmt_elf.c
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
...
setup_arg_pages(bprm); 将内核的参数页映射到用户堆栈区域
...
bprm->p = (unsigned long)
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
load_addr, load_bias,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
...
}
static elf_addr_t *
create_elf_tables(char *p, int argc, int envc,
struct elfhdr * exec,
unsigned long load_addr,
unsigned long load_bias,
unsigned long interp_load_addr, int ibcs)
{
elf_caddr_t *argv;
elf_caddr_t *envp;
elf_addr_t *sp, *csp;
char *k_platform, *u_platform;
long hwcap;
size_t platform_len = 0;
; 这时p指向第1个命令行参数字符串
/*
* Get hold of platform and hardware capabilities masks for
* the machine we are running on. In some cases (Sparc),
* this info is impossible to get, in others (i386) it is
* merely difficult.
*/
hwcap = ELF_HWCAP; CPU特性描述字
k_platform = ELF_PLATFORM; CPU类型名
if (k_platform) {
platform_len = strlen(k_platform) + 1;
u_platform = p - platform_len;
__copy_to_user(u_platform, k_platform, platform_len);
} else
u_platform = p;
/*
* Force 16 byte _final_ alignment here for generality.
* Leave an extra 16 bytes free so that on the PowerPC we
* can move the aux table up to start on a 16-byte boundary.
*/
sp = (elf_addr_t *)((~15UL & (unsigned long)(u_platform)) - 16UL);
; 16字节边界上对齐
csp = sp;
csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0));
csp -= envc+1; 环境字符串表
csp -= argc+1; 命令行参数字符串表
csp -= (!ibcs ? 3 : 1); 是否建立argv与envp指针
if ((unsigned long)csp & 15UL)
sp -= ((unsigned long)csp & 15UL) / sizeof(*sp);
; 预偏置一下,使得最后的sp能够在16字节边界上对齐
/*
* Put the ELF interpreter info on the stack
*/
#define NEW_AUX_ENT(nr, id, val) / 由两个字组成的表项
__put_user ((id), sp+(nr*2)); /
__put_user ((val), sp+(nr*2+1)); /
sp -= 2; 分配两个字的空间
NEW_AUX_ENT(0, AT_NULL, 0); 写入顶部两个零指针作为补充参数表的结束
if (k_platform) {
sp -= 2;
; 填写CPU类型名称
NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
}
sp -= 3*2;
NEW_AUX_ENT(0, AT_HWCAP, hwcap); 填写CPU特性字
NEW_AUX_ENT(1, AT_PAGESZ, ELF_EXEC_PAGESIZE); 填写可执行文件页长
NEW_AUX_ENT(2, AT_CLKTCK, CLOCKS_PER_SEC);
if (exec) {
; 填写动态链接程序的补充参数表
sp -= 10*2;
NEW_AUX_ENT(0, AT_PHDR, load_addr + exec->e_phoff); 程序段表
NEW_AUX_ENT(1, AT_PHENT, sizeof (struct elf_phdr)); 程序段表项长度
NEW_AUX_ENT(2, AT_PHNUM, exec->e_phnum); 程序段表项数目
NEW_AUX_ENT(3, AT_BASE, interp_load_addr); 动态连接器的加载地址
NEW_AUX_ENT(4, AT_FLAGS, 0);
NEW_AUX_ENT(5, AT_ENTRY, load_bias + exec->e_entry); 主程序的入口
NEW_AUX_ENT(6, AT_UID, (elf_addr_t) current->uid);
NEW_AUX_ENT(7, AT_EUID, (elf_addr_t) current->euid);
NEW_AUX_ENT(8, AT_GID, (elf_addr_t) current->gid);
NEW_AUX_ENT(9, AT_EGID, (elf_addr_t) current->egid);
}
#undef NEW_AUX_ENT
sp -= envc+1; 分配环境指针表
envp = (elf_caddr_t *) sp;
sp -= argc+1; 分配参数指针表
argv = (elf_caddr_t *) sp;
if (!ibcs) {
__put_user((elf_addr_t)(unsigned long) envp,--sp); 填写环境指针表地址
__put_user((elf_addr_t)(unsigned long) argv,--sp); 填写命令行参数指针表地址
}
__put_user((elf_addr_t)argc,--sp); 填写命令行参数数目
current->mm->arg_start = (unsigned long) p; 第1个命令行参数字符串地址
while (argc-->0) {
; 填写命令行参数指针表
__put_user((elf_caddr_t)(unsigned long)p,argv++);
p += strlen_user(p);
}
__put_user(NULL, argv); 命令行参数指针表终止指针
current->mm->arg_end = current->mm->env_start = (unsigned long) p; 第1个参数字符串地址
while (envc-->0) {
; 填写环境指针表
__put_user((elf_caddr_t)(unsigned long)p,envp++);
p += strlen_user(p);
}
__put_user(NULL, envp); 环境指针表终止指针
current->mm->env_end = (unsigned long) p; 环境字符串表终止地址
return sp; 返回指向命令行参数数目的地址
}
int setup_arg_pages(struct linux_binprm *bprm)
{
unsigned long stack_base;
struct vm_area_struct *mpnt;
int i;
stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
bprm->p += stack_base; 这时bprm->p重定位为用户参数块的堆栈指针
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base; 指向用户堆栈最顶部的可执行文件名
mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
if (!mpnt)
return -ENOMEM;
; 建立初始的vm_area_struct结构
down(¤t->mm->mmap_sem);
{
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; 虚存起始于参数块的堆栈指针
mpnt->vm_end = STACK_TOP; 虚存终止于STACK_TOP
mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0;
mpnt->vm_file = NULL;
mpnt->vm_private_data = (void *) 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
; 目前虚存的总量
}
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page *page = bprm->page[ i ];
if (page) {
bprm->page[ i ] = NULL;
current->mm->rss++;
put_dirty_page(current,page,stack_base);
; 将page所指向的物理页面映射到用户参数地址上
}
stack_base += PAGE_SIZE;
}
up(¤t->mm->mmap_sem);
return 0;
}
; fs/exec.c
; copy_strings_kernel(argc,argv,bprm)将字符串指针数组argv中argc个字符串
; 从上向下压入内核参数页bprm->page之中
int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
{
while (argc-- > 0) {
char *str;
int len;
unsigned long pos;
if (get_user(str, argv+argc) || !str || !(len = strnlen_user(str, bprm->p)))
return -EFAULT;
if (bprm->p < len)
return -E2BIG;
bprm->p -= len;
/* XXX: add architecture specific overflow check here. */
pos = bprm->p;
while (len > 0) { 在拷贝中动态地分配参数页
char *kaddr;
int i, new, err;
struct page *page;
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
i = pos/PAGE_SIZE;
page = bprm->page[ i ];
new = 0;
if (!page) {
page = alloc_page(GFP_HIGHUSER);
bprm->page[ i ] = page;
if (!page)
return -ENOMEM;
new = 1;
}
kaddr = kmap(page);
if (new && offset)
memset(kaddr, 0, offset);
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len) {
bytes_to_copy = len;
if (new)
memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len);
}
err = copy_from_user(kaddr + offset, str, bytes_to_copy);
kunmap(page);
if (err)
return -EFAULT;
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
}
}
return 0;
}
Edited by lucian_yao on 04/16/01 01:08 PM.
标题 Re: 分析应用程序加载时堆栈中的参数结构 [re: opera]
作者 lucian_yao (addict)
时间 04/16/01 01:10 PM
(argc,argv,envp)参数形式调用
--------------不知道是不是这样会有安全漏洞?
本文转自
http://www.patching.net/bbs/viewgooddoc_2097_4.html