-
-
[原创] Linux ptrace详细分析系列(二)
-
发表于: 2021-2-6 11:10 10749
-
承接上文 Linux ptrace详细分析系列(一),本次主要介绍2个不同版本下的源码分析和2个关键参数解析。
首先看一下linux-2.6.0的sys_ptrace
的处理流程(以/arch/i386/kernel/ptrace.c
为例):
整体来看较为简单,经过简单的验证后根据不同的request
参数进入不同的处理流程。
根据源码分析结果,梳理函数的整体处理流程如下(为保证图片清晰,将图片进行了切割):
上述流程图基本描述清晰了Linux-2.6版本下的sys_ptrace
函数的执行流程。其中可以看参数为到PTRACE_TRACEME, PTRACE_ATTACH
时进行了特殊处理,其他情况下,流程基本相同,根据不同的request
的值调用对应的handler函数即可。
在Linux-2.6版本中,针对不同platform设计了不同的函数实现,总体流程上没有改变,只是根据不同的platform特点在某些位置坐了不同的处理方式。各platform对应的函数实现文件如下:
在kernel/ptrace.c
中实现对公共函数的实现,此处不做过多介绍,感兴趣的师傅可自行研究:
Linux-5.9版本的源码分析:
梳理上述源码,可以得到函数流程图如下:
Linux-5.9中使用了宏的方式,在进行函数调用时先进行函数替换解析出完整的函数体再进行具体执行(详细替换可参考系列(一)中的函数定义部分内容)。而且与Linux-2.6不同的是,kernel/ptrace.c
负责总体调度,使用arch_ptrace
进行不同架构的处理的选择:
Linux-5.9版本的这种改动相比Linux-2.6的设计,更为清晰也更为安全(个人十分喜欢这种设计,由衷佩服这些优秀的开发者)。
ptrace
总计有4个参数,其中比较重要的是第一个参数--request
,该参数决定了具体执行的系统调用功能。可取值如下(部分):
备注:上述参数中,PTRACE_GETREGS, PTRACE_SETREGS, PTRACE_GETFPREGS, PTRACE_SETFPREGS
参数为Interl386特有。
各参数所代表的值由/usr/include/sys/ptrace.h
文件指定:
下面将对request
中几个常见、重要的参数进行详细解析:
描述
本进程被其父进程跟踪,如果子进程没有被其父进程跟踪,不能使用该选项。PTRACE_TRACEME
只被tracee
使用。
定义
分析
通过分析源码我们可以明确看到,PTRACE_TRACEME
并没有真正使子进程停止。它内部完成的操作只有对父进程是否能对子进程进行trace的合法性检查,然后将子进程链接到父进程的饿ptrace链表中。真正导致子进程停止的是exec
系统调用。
在系统调用成功后,kernel会判断该进程是否被ptrace跟踪。如果处于跟踪状态,kernel将会向该进程发送SIGTRAP
信号,正是该信号导致了当前进程的停止。
在exec.c
中对该函数的调用如下:
SIGTRAP
信号的值为5,专门为调试设计。当kernel发生int 3
时,触发回掉函数do_trap()
,其代码如下:
父进程唤醒wait
对子进程进行监控,wait
有3种退出情况(子进程正常退出、收到信号退出、收到信号暂停),对于PTRACE_TRACEME
来说,对应的是第三种情况--收到信号后暂停。
PTRACE_TRACEME
只是表明了子进程可以被trace,如果进程调用了PTRACE_TRACEME
,那么该进程处理信号的方式会发生改变。例如一个进程正在运行,此时输入ctrl+c(SIGINT)
,则进程会直接退出;如果进程中有ptrace (PTRACE_TRACEME,0,NULL,NULL)
,当输入CTRL+C
时,该进程将会处于stopped的状态。
在sys_ptrace
函数中,该部分的处理流程如下:
在5.9版中,单独写成了ptrace_traceme()
函数,而在2.6版本中,直接在sys_ptrace
的逻辑中进行实现:
虽然2个版本的核心功能相同,但是5.9版本的处理逻辑和情况考量相比2.6版本上升了很大高度。
描述
attach到pid指定的进程,使其成为调用进程的tracee
。tracer
会向tracee
发送一个SIGSTOP
信号,但不一定已通过此调用完成而停止;tracer
使用waitpid()
等待tracee
停止。
定义
分析
代码上可以看出,PTRACE_ATTACH
处理的方式与PTRACE_TRACEME
处理的方式不同。PTRACE_ATTACH
会使父进程直接向子进程发送SIGSTOP
信号,如果子进程停止,那么父进程的wait
操作被唤醒,从而成功attach。一个进程不能attach多次。
在2.6版本中的实现如下(kernel/ptrace.c
):
(未完待续)
/
*
*
Note that this implementation of ptrace behaves differently
from
vanilla
*
ptrace. Contrary to what the man page says,
in
the PTRACE_PEEKTEXT,
*
PTRACE_PEEKDATA,
and
PTRACE_PEEKUSER requests the data variable
is
not
*
ignored. Instead, the data variable
is
expected to point at a location
*
(
in
user space) where the result of the ptrace call
is
written (instead of
*
being returned).
*
/
asmlinkage
int
sys_ptrace(
long
request,
long
pid,
long
addr,
long
data)
{
struct task_struct
*
child;
struct user
*
dummy
=
NULL;
int
i, ret;
lock_kernel();
ret
=
-
EPERM;
if
(request
=
=
PTRACE_TRACEME) {
/
/
请求为PTRACE_TRACEME
/
*
检查是否做好被跟踪的准备
*
/
if
(current
-
>ptrace & PT_PTRACED)
goto out;
ret
=
security_ptrace(current
-
>parent, current);
if
(ret)
goto out;
/
*
检查通过,在process flags中设置ptrace位
*
/
current
-
>ptrace |
=
PT_PTRACED;
ret
=
0
;
goto out;
}
/
*
非PTRACE_TRACEME的请求
*
/
ret
=
-
ESRCH;
/
/
首先设置返回值为ESRCH,表明没有该进程,宏定义在errno
-
base.h头文件中
read_lock(&tasklist_lock);
child
=
find_task_by_pid(pid);
/
/
查找task结构
if
(child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if
(!child)
/
/
没有找到task结构,指明所给pid错误
goto out;
ret
=
-
EPERM;
/
/
返回操作未授权
if
(pid
=
=
1
)
/
/
init进程不允许被调试
goto out_tsk;
/
*
请求为 PTRACE_ATTACH 时
*
/
if
(request
=
=
PTRACE_ATTACH) {
ret
=
ptrace_attach(child);
/
/
进行attach
goto out_tsk;
}
/
*
检查进程是否被跟踪,没有的话不能执行其他功能;
*
当不是PTRACE_KILL时,要求进程状态为TASK_STOPPED;
*
被跟踪进程必须为当前进程的子进程
*
在之前是直接在该代码处实现以上逻辑,现在重新将以上功能封装成了ptrace_check_attach函数
*
/
ret
=
ptrace_check_attach(child, request
=
=
PTRACE_KILL);
if
(ret <
0
)
goto out_tsk;
/
*
以下就为根据不同的request参数进行对应的处理了,用一个switch来总括,流程比较简单。
*
/
switch (request) {
/
*
when I
and
D space are separate, these will need to be fixed. 这算预告吗?
23333
*
/
case PTRACE_PEEKTEXT:
/
*
read word at location addr.
*
/
case PTRACE_PEEKDATA: {
unsigned
long
tmp;
int
copied;
copied
=
access_process_vm(child, addr, &tmp, sizeof(tmp),
0
);
ret
=
-
EIO;
/
/
返回I
/
O错误
if
(copied !
=
sizeof(tmp))
break
;
ret
=
put_user(tmp,(unsigned
long
*
) data);
break
;
}
/
*
read the word at location addr
in
the USER area.
*
/
case PTRACE_PEEKUSR: {
unsigned
long
tmp;
ret
=
-
EIO;
if
((addr &
3
) || addr <
0
||
addr > sizeof(struct user)
-
3
)
break
;
tmp
=
0
;
/
*
Default
return
condition
*
/
if
(addr < FRAME_SIZE
*
sizeof(
long
))
tmp
=
getreg(child, addr);
if
(addr >
=
(
long
) &dummy
-
>u_debugreg[
0
] &&
addr <
=
(
long
) &dummy
-
>u_debugreg[
7
]){
addr
-
=
(
long
) &dummy
-
>u_debugreg[
0
];
addr
=
addr >>
2
;
tmp
=
child
-
>thread.debugreg[addr];
}
ret
=
put_user(tmp,(unsigned
long
*
) data);
break
;
}
/
*
when I
and
D space are separate, this will have to be fixed.
*
/
case PTRACE_POKETEXT:
/
*
write the word at location addr.
*
/
case PTRACE_POKEDATA:
ret
=
0
;
if
(access_process_vm(child, addr, &data, sizeof(data),
1
)
=
=
sizeof(data))
break
;
ret
=
-
EIO;
break
;
case PTRACE_POKEUSR:
/
*
write the word at location addr
in
the USER area
*
/
ret
=
-
EIO;
if
((addr &
3
) || addr <
0
||
addr > sizeof(struct user)
-
3
)
break
;
if
(addr < FRAME_SIZE
*
sizeof(
long
)) {
ret
=
putreg(child, addr, data);
break
;
}
/
*
We need to be very careful here. We implicitly
want to modify a portion of the task_struct,
and
we
have to be selective about what portions we allow someone
to modify.
*
/
ret
=
-
EIO;
if
(addr >
=
(
long
) &dummy
-
>u_debugreg[
0
] &&
addr <
=
(
long
) &dummy
-
>u_debugreg[
7
]){
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
4
])
break
;
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
5
])
break
;
if
(addr < (
long
) &dummy
-
>u_debugreg[
4
] &&
((unsigned
long
) data) >
=
TASK_SIZE
-
3
)
break
;
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
7
]) {
data &
=
~DR_CONTROL_RESERVED;
for
(i
=
0
; i<
4
; i
+
+
)
if
((
0x5f54
>> ((data >> (
16
+
4
*
i)) &
0xf
)) &
1
)
goto out_tsk;
}
addr
-
=
(
long
) &dummy
-
>u_debugreg;
addr
=
addr >>
2
;
child
-
>thread.debugreg[addr]
=
data;
ret
=
0
;
}
break
;
case PTRACE_SYSCALL:
/
*
continue
and
stop at
next
(
return
from
) syscall
*
/
case PTRACE_CONT: {
/
*
restart after signal.
*
/
long
tmp;
ret
=
-
EIO;
if
((unsigned
long
) data > _NSIG)
break
;
if
(request
=
=
PTRACE_SYSCALL) {
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
else
{
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
child
-
>exit_code
=
data;
/
*
make sure the single step bit
is
not
set
.
*
/
tmp
=
get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
put_stack_long(child, EFL_OFFSET,tmp);
wake_up_process(child);
ret
=
0
;
break
;
}
/
*
*
make the child exit. Best I can do
is
send it a sigkill.
*
perhaps it should be put
in
the status that it wants to
*
exit.
*
/
case PTRACE_KILL: {
long
tmp;
ret
=
0
;
if
(child
-
>state
=
=
TASK_ZOMBIE)
/
*
already dead
*
/
break
;
child
-
>exit_code
=
SIGKILL;
/
*
make sure the single step bit
is
not
set
.
*
/
tmp
=
get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
put_stack_long(child, EFL_OFFSET, tmp);
wake_up_process(child);
break
;
}
case PTRACE_SINGLESTEP: {
/
*
set
the trap flag.
*
/
long
tmp;
ret
=
-
EIO;
if
((unsigned
long
) data > _NSIG)
break
;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
if
((child
-
>ptrace & PT_DTRACE)
=
=
0
) {
/
*
Spurious delayed TF traps may occur
*
/
child
-
>ptrace |
=
PT_DTRACE;
}
tmp
=
get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
put_stack_long(child, EFL_OFFSET, tmp);
child
-
>exit_code
=
data;
/
*
give it a chance to run.
*
/
wake_up_process(child);
ret
=
0
;
break
;
}
case PTRACE_DETACH:
/
*
detach a process that was attached.
*
/
ret
=
ptrace_detach(child, data);
break
;
case PTRACE_GETREGS: {
/
*
Get
all
gp regs
from
the child.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data, FRAME_SIZE
*
sizeof(
long
))) {
ret
=
-
EIO;
break
;
}
for
( i
=
0
; i < FRAME_SIZE
*
sizeof(
long
); i
+
=
sizeof(
long
) ) {
__put_user(getreg(child, i),(unsigned
long
*
) data);
data
+
=
sizeof(
long
);
}
ret
=
0
;
break
;
}
case PTRACE_SETREGS: {
/
*
Set
all
gp regs
in
the child.
*
/
unsigned
long
tmp;
if
(!access_ok(VERIFY_READ, (unsigned
*
)data, FRAME_SIZE
*
sizeof(
long
))) {
ret
=
-
EIO;
break
;
}
for
( i
=
0
; i < FRAME_SIZE
*
sizeof(
long
); i
+
=
sizeof(
long
) ) {
__get_user(tmp, (unsigned
long
*
) data);
putreg(child, i, tmp);
data
+
=
sizeof(
long
);
}
ret
=
0
;
break
;
}
case PTRACE_GETFPREGS: {
/
*
Get the child FPU state.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data,
sizeof(struct user_i387_struct))) {
ret
=
-
EIO;
break
;
}
ret
=
0
;
if
(!child
-
>used_math)
init_fpu(child);
get_fpregs((struct user_i387_struct __user
*
)data, child);
break
;
}
case PTRACE_SETFPREGS: {
/
*
Set
the child FPU state.
*
/
if
(!access_ok(VERIFY_READ, (unsigned
*
)data,
sizeof(struct user_i387_struct))) {
ret
=
-
EIO;
break
;
}
child
-
>used_math
=
1
;
set_fpregs(child, (struct user_i387_struct __user
*
)data);
ret
=
0
;
break
;
}
case PTRACE_GETFPXREGS: {
/
*
Get the child extended FPU state.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data,
sizeof(struct user_fxsr_struct))) {
ret
=
-
EIO;
break
;
}
if
(!child
-
>used_math)
init_fpu(child);
ret
=
get_fpxregs((struct user_fxsr_struct __user
*
)data, child);
break
;
}
case PTRACE_SETFPXREGS: {
/
*
Set
the child extended FPU state.
*
/
if
(!access_ok(VERIFY_READ, (unsigned
*
)data,
sizeof(struct user_fxsr_struct))) {
ret
=
-
EIO;
break
;
}
child
-
>used_math
=
1
;
ret
=
set_fpxregs(child, (struct user_fxsr_struct __user
*
)data);
break
;
}
case PTRACE_GET_THREAD_AREA:
ret
=
ptrace_get_thread_area(child,
addr, (struct user_desc __user
*
) data);
break
;
case PTRACE_SET_THREAD_AREA:
ret
=
ptrace_set_thread_area(child,
addr, (struct user_desc __user
*
) data);
break
;
default:
ret
=
ptrace_request(child, request, addr, data);
break
;
}
out_tsk:
put_task_struct(child);
out:
unlock_kernel();
return
ret;
}
/
*
*
Note that this implementation of ptrace behaves differently
from
vanilla
*
ptrace. Contrary to what the man page says,
in
the PTRACE_PEEKTEXT,
*
PTRACE_PEEKDATA,
and
PTRACE_PEEKUSER requests the data variable
is
not
*
ignored. Instead, the data variable
is
expected to point at a location
*
(
in
user space) where the result of the ptrace call
is
written (instead of
*
being returned).
*
/
asmlinkage
int
sys_ptrace(
long
request,
long
pid,
long
addr,
long
data)
{
struct task_struct
*
child;
struct user
*
dummy
=
NULL;
int
i, ret;
lock_kernel();
ret
=
-
EPERM;
if
(request
=
=
PTRACE_TRACEME) {
/
/
请求为PTRACE_TRACEME
/
*
检查是否做好被跟踪的准备
*
/
if
(current
-
>ptrace & PT_PTRACED)
goto out;
ret
=
security_ptrace(current
-
>parent, current);
if
(ret)
goto out;
/
*
检查通过,在process flags中设置ptrace位
*
/
current
-
>ptrace |
=
PT_PTRACED;
ret
=
0
;
goto out;
}
/
*
非PTRACE_TRACEME的请求
*
/
ret
=
-
ESRCH;
/
/
首先设置返回值为ESRCH,表明没有该进程,宏定义在errno
-
base.h头文件中
read_lock(&tasklist_lock);
child
=
find_task_by_pid(pid);
/
/
查找task结构
if
(child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if
(!child)
/
/
没有找到task结构,指明所给pid错误
goto out;
ret
=
-
EPERM;
/
/
返回操作未授权
if
(pid
=
=
1
)
/
/
init进程不允许被调试
goto out_tsk;
/
*
请求为 PTRACE_ATTACH 时
*
/
if
(request
=
=
PTRACE_ATTACH) {
ret
=
ptrace_attach(child);
/
/
进行attach
goto out_tsk;
}
/
*
检查进程是否被跟踪,没有的话不能执行其他功能;
*
当不是PTRACE_KILL时,要求进程状态为TASK_STOPPED;
*
被跟踪进程必须为当前进程的子进程
*
在之前是直接在该代码处实现以上逻辑,现在重新将以上功能封装成了ptrace_check_attach函数
*
/
ret
=
ptrace_check_attach(child, request
=
=
PTRACE_KILL);
if
(ret <
0
)
goto out_tsk;
/
*
以下就为根据不同的request参数进行对应的处理了,用一个switch来总括,流程比较简单。
*
/
switch (request) {
/
*
when I
and
D space are separate, these will need to be fixed. 这算预告吗?
23333
*
/
case PTRACE_PEEKTEXT:
/
*
read word at location addr.
*
/
case PTRACE_PEEKDATA: {
unsigned
long
tmp;
int
copied;
copied
=
access_process_vm(child, addr, &tmp, sizeof(tmp),
0
);
ret
=
-
EIO;
/
/
返回I
/
O错误
if
(copied !
=
sizeof(tmp))
break
;
ret
=
put_user(tmp,(unsigned
long
*
) data);
break
;
}
/
*
read the word at location addr
in
the USER area.
*
/
case PTRACE_PEEKUSR: {
unsigned
long
tmp;
ret
=
-
EIO;
if
((addr &
3
) || addr <
0
||
addr > sizeof(struct user)
-
3
)
break
;
tmp
=
0
;
/
*
Default
return
condition
*
/
if
(addr < FRAME_SIZE
*
sizeof(
long
))
tmp
=
getreg(child, addr);
if
(addr >
=
(
long
) &dummy
-
>u_debugreg[
0
] &&
addr <
=
(
long
) &dummy
-
>u_debugreg[
7
]){
addr
-
=
(
long
) &dummy
-
>u_debugreg[
0
];
addr
=
addr >>
2
;
tmp
=
child
-
>thread.debugreg[addr];
}
ret
=
put_user(tmp,(unsigned
long
*
) data);
break
;
}
/
*
when I
and
D space are separate, this will have to be fixed.
*
/
case PTRACE_POKETEXT:
/
*
write the word at location addr.
*
/
case PTRACE_POKEDATA:
ret
=
0
;
if
(access_process_vm(child, addr, &data, sizeof(data),
1
)
=
=
sizeof(data))
break
;
ret
=
-
EIO;
break
;
case PTRACE_POKEUSR:
/
*
write the word at location addr
in
the USER area
*
/
ret
=
-
EIO;
if
((addr &
3
) || addr <
0
||
addr > sizeof(struct user)
-
3
)
break
;
if
(addr < FRAME_SIZE
*
sizeof(
long
)) {
ret
=
putreg(child, addr, data);
break
;
}
/
*
We need to be very careful here. We implicitly
want to modify a portion of the task_struct,
and
we
have to be selective about what portions we allow someone
to modify.
*
/
ret
=
-
EIO;
if
(addr >
=
(
long
) &dummy
-
>u_debugreg[
0
] &&
addr <
=
(
long
) &dummy
-
>u_debugreg[
7
]){
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
4
])
break
;
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
5
])
break
;
if
(addr < (
long
) &dummy
-
>u_debugreg[
4
] &&
((unsigned
long
) data) >
=
TASK_SIZE
-
3
)
break
;
if
(addr
=
=
(
long
) &dummy
-
>u_debugreg[
7
]) {
data &
=
~DR_CONTROL_RESERVED;
for
(i
=
0
; i<
4
; i
+
+
)
if
((
0x5f54
>> ((data >> (
16
+
4
*
i)) &
0xf
)) &
1
)
goto out_tsk;
}
addr
-
=
(
long
) &dummy
-
>u_debugreg;
addr
=
addr >>
2
;
child
-
>thread.debugreg[addr]
=
data;
ret
=
0
;
}
break
;
case PTRACE_SYSCALL:
/
*
continue
and
stop at
next
(
return
from
) syscall
*
/
case PTRACE_CONT: {
/
*
restart after signal.
*
/
long
tmp;
ret
=
-
EIO;
if
((unsigned
long
) data > _NSIG)
break
;
if
(request
=
=
PTRACE_SYSCALL) {
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
else
{
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
child
-
>exit_code
=
data;
/
*
make sure the single step bit
is
not
set
.
*
/
tmp
=
get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
put_stack_long(child, EFL_OFFSET,tmp);
wake_up_process(child);
ret
=
0
;
break
;
}
/
*
*
make the child exit. Best I can do
is
send it a sigkill.
*
perhaps it should be put
in
the status that it wants to
*
exit.
*
/
case PTRACE_KILL: {
long
tmp;
ret
=
0
;
if
(child
-
>state
=
=
TASK_ZOMBIE)
/
*
already dead
*
/
break
;
child
-
>exit_code
=
SIGKILL;
/
*
make sure the single step bit
is
not
set
.
*
/
tmp
=
get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
put_stack_long(child, EFL_OFFSET, tmp);
wake_up_process(child);
break
;
}
case PTRACE_SINGLESTEP: {
/
*
set
the trap flag.
*
/
long
tmp;
ret
=
-
EIO;
if
((unsigned
long
) data > _NSIG)
break
;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
if
((child
-
>ptrace & PT_DTRACE)
=
=
0
) {
/
*
Spurious delayed TF traps may occur
*
/
child
-
>ptrace |
=
PT_DTRACE;
}
tmp
=
get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
put_stack_long(child, EFL_OFFSET, tmp);
child
-
>exit_code
=
data;
/
*
give it a chance to run.
*
/
wake_up_process(child);
ret
=
0
;
break
;
}
case PTRACE_DETACH:
/
*
detach a process that was attached.
*
/
ret
=
ptrace_detach(child, data);
break
;
case PTRACE_GETREGS: {
/
*
Get
all
gp regs
from
the child.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data, FRAME_SIZE
*
sizeof(
long
))) {
ret
=
-
EIO;
break
;
}
for
( i
=
0
; i < FRAME_SIZE
*
sizeof(
long
); i
+
=
sizeof(
long
) ) {
__put_user(getreg(child, i),(unsigned
long
*
) data);
data
+
=
sizeof(
long
);
}
ret
=
0
;
break
;
}
case PTRACE_SETREGS: {
/
*
Set
all
gp regs
in
the child.
*
/
unsigned
long
tmp;
if
(!access_ok(VERIFY_READ, (unsigned
*
)data, FRAME_SIZE
*
sizeof(
long
))) {
ret
=
-
EIO;
break
;
}
for
( i
=
0
; i < FRAME_SIZE
*
sizeof(
long
); i
+
=
sizeof(
long
) ) {
__get_user(tmp, (unsigned
long
*
) data);
putreg(child, i, tmp);
data
+
=
sizeof(
long
);
}
ret
=
0
;
break
;
}
case PTRACE_GETFPREGS: {
/
*
Get the child FPU state.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data,
sizeof(struct user_i387_struct))) {
ret
=
-
EIO;
break
;
}
ret
=
0
;
if
(!child
-
>used_math)
init_fpu(child);
get_fpregs((struct user_i387_struct __user
*
)data, child);
break
;
}
case PTRACE_SETFPREGS: {
/
*
Set
the child FPU state.
*
/
if
(!access_ok(VERIFY_READ, (unsigned
*
)data,
sizeof(struct user_i387_struct))) {
ret
=
-
EIO;
break
;
}
child
-
>used_math
=
1
;
set_fpregs(child, (struct user_i387_struct __user
*
)data);
ret
=
0
;
break
;
}
case PTRACE_GETFPXREGS: {
/
*
Get the child extended FPU state.
*
/
if
(!access_ok(VERIFY_WRITE, (unsigned
*
)data,
sizeof(struct user_fxsr_struct))) {
ret
=
-
EIO;
break
;
}
if
(!child
-
>used_math)
init_fpu(child);
ret
=
get_fpxregs((struct user_fxsr_struct __user
*
)data, child);
break
;
}
case PTRACE_SETFPXREGS: {
/
*
Set
the child extended FPU state.
*
/
if
(!access_ok(VERIFY_READ, (unsigned
*
)data,
sizeof(struct user_fxsr_struct))) {
ret
=
-
EIO;
break
;
}
child
-
>used_math
=
1
;
ret
=
set_fpxregs(child, (struct user_fxsr_struct __user
*
)data);
break
;
}
case PTRACE_GET_THREAD_AREA:
ret
=
ptrace_get_thread_area(child,
addr, (struct user_desc __user
*
) data);
break
;
case PTRACE_SET_THREAD_AREA:
ret
=
ptrace_set_thread_area(child,
addr, (struct user_desc __user
*
) data);
break
;
default:
ret
=
ptrace_request(child, request, addr, data);
break
;
}
out_tsk:
put_task_struct(child);
out:
unlock_kernel();
return
ret;
}
/
*
*
linux
/
kernel
/
ptrace.c
*
*
(C) Copyright
1999
Linus Torvalds
*
*
Common interfaces
for
"ptrace()"
which we do
not
want
*
to continually duplicate across every architecture.
*
/
... ...
/
*
*
ptrace a task: make the debugger its new parent
and
*
move it to the ptrace
list
.
*
*
Must be called with the tasklist lock write
-
held.
*
/
void __ptrace_link(task_t
*
child, task_t
*
new_parent)
{
... ...
}
/
*
*
unptrace a task: move it back to its original parent
and
*
remove it
from
the ptrace
list
.
*
*
Must be called with the tasklist lock write
-
held.
*
/
void __ptrace_unlink(task_t
*
child)
{
... ...
}
/
*
*
Check that we have indeed attached to the thing..
*
/
int
ptrace_check_attach(struct task_struct
*
child,
int
kill)
{
if
(!(child
-
>ptrace & PT_PTRACED))
return
-
ESRCH;
if
(child
-
>parent !
=
current)
return
-
ESRCH;
if
(!kill) {
if
(child
-
>state !
=
TASK_STOPPED)
return
-
ESRCH;
wait_task_inactive(child);
}
/
*
All
systems go..
*
/
return
0
;
}
int
ptrace_attach(struct task_struct
*
task)
{
... ...
}
int
ptrace_detach(struct task_struct
*
child, unsigned
int
data)
{
... ...
}
/
*
*
Access another process' address space.
*
Source
/
target
buffer
must be kernel space,
*
Do
not
walk the page table directly, use get_user_pages
*
/
int
access_process_vm(struct task_struct
*
tsk, unsigned
long
addr, void
*
buf,
int
len
,
int
write)
{
... ...
}
int
ptrace_readdata(struct task_struct
*
tsk, unsigned
long
src, char __user
*
dst,
int
len
)
{
... ...
}
int
ptrace_writedata(struct task_struct
*
tsk, char __user
*
src, unsigned
long
dst,
int
len
)
{
... ...
}
static
int
ptrace_setoptions(struct task_struct
*
child,
long
data)
{
... ...
}
static
int
ptrace_getsiginfo(struct task_struct
*
child, siginfo_t __user
*
data)
{
... ...
}
static
int
ptrace_setsiginfo(struct task_struct
*
child, siginfo_t __user
*
data)
{
... ...
}
int
ptrace_request(struct task_struct
*
child,
long
request,
long
addr,
long
data)
{
int
ret
=
-
EIO;
switch (request) {
#ifdef PTRACE_OLDSETOPTIONS
case PTRACE_OLDSETOPTIONS:
#endif
case PTRACE_SETOPTIONS:
ret
=
ptrace_setoptions(child, data);
break
;
case PTRACE_GETEVENTMSG:
ret
=
put_user(child
-
>ptrace_message, (unsigned
long
__user
*
) data);
break
;
case PTRACE_GETSIGINFO:
ret
=
ptrace_getsiginfo(child, (siginfo_t __user
*
) data);
break
;
case PTRACE_SETSIGINFO:
ret
=
ptrace_setsiginfo(child, (siginfo_t __user
*
) data);
break
;
default:
break
;
}
return
ret;
}
void ptrace_notify(
int
exit_code)
{
BUG_ON (!(current
-
>ptrace & PT_PTRACED));
/
*
Let the debugger run.
*
/
current
-
>exit_code
=
exit_code;
set_current_state(TASK_STOPPED);
notify_parent(current, SIGCHLD);
schedule();
/
*
*
Signals sent
while
we were stopped might
set
TIF_SIGPENDING.
*
/
spin_lock_irq(¤t
-
>sighand
-
>siglock);
recalc_sigpending();
spin_unlock_irq(¤t
-
>sighand
-
>siglock);
}
EXPORT_SYMBOL(ptrace_notify);
/
*
*
linux
/
kernel
/
ptrace.c
*
*
(C) Copyright
1999
Linus Torvalds
*
*
Common interfaces
for
"ptrace()"
which we do
not
want
*
to continually duplicate across every architecture.
*
/
... ...
/
*
*
ptrace a task: make the debugger its new parent
and
*
move it to the ptrace
list
.
*
*
Must be called with the tasklist lock write
-
held.
*
/
void __ptrace_link(task_t
*
child, task_t
*
new_parent)
{
... ...
}
/
*
*
unptrace a task: move it back to its original parent
and
*
remove it
from
the ptrace
list
.
*
*
Must be called with the tasklist lock write
-
held.
*
/
void __ptrace_unlink(task_t
*
child)
{
... ...
}
/
*
*
Check that we have indeed attached to the thing..
*
/
int
ptrace_check_attach(struct task_struct
*
child,
int
kill)
{
if
(!(child
-
>ptrace & PT_PTRACED))
return
-
ESRCH;
if
(child
-
>parent !
=
current)
return
-
ESRCH;
if
(!kill) {
if
(child
-
>state !
=
TASK_STOPPED)
return
-
ESRCH;
wait_task_inactive(child);
}
/
*
All
systems go..
*
/
return
0
;
}
int
ptrace_attach(struct task_struct
*
task)
{
... ...
}
int
ptrace_detach(struct task_struct
*
child, unsigned
int
data)
{
... ...
}
/
*
*
Access another process' address space.
*
Source
/
target
buffer
must be kernel space,
*
Do
not
walk the page table directly, use get_user_pages
*
/
int
access_process_vm(struct task_struct
*
tsk, unsigned
long
addr, void
*
buf,
int
len
,
int
write)
{
... ...
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [分享]出一点自己暂时不用的书 4839
- 玩ChatGPT有感 15825
- [注意] 2022看雪二进制漏洞小组成立通告 33644
- [分享]我和看雪的故事 -- by 有毒 31651
- [原创]多版本gcc/g++共存方案 23203